Skip to content

Commit

Permalink
Add std.writeFile (#932)
Browse files Browse the repository at this point in the history
std.loadFile already exists and is convenient. std.writeFile replaces
the more onerous std.open + std.write + std.close idiom.
  • Loading branch information
bnoordhuis authored Feb 27, 2025
1 parent 9d6e372 commit 9a902ba
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 6 deletions.
12 changes: 12 additions & 0 deletions docs/docs/stdlib.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,18 @@ encoding. Return `null` in case of I/O error.

If `options.binary` is set to `true` a `Uint8Array` is returned instead.

### `writeFile(filename, data)`

Create the file `filename` and write `data` into it.

`data` can be a string, a typed array or an `ArrayBuffer`. `undefined` is
treated as the empty string.

When `data` is a string, the file is opened in text mode; otherwise it is
opened in binary mode. (This distinction is only relevant on Windows.)

Throws an exception if the file cannot be created or written to.

### `open(filename, flags, errorObj = undefined)`

Open a file (wrapper to the libc `fopen()`). Return the FILE
Expand Down
63 changes: 63 additions & 0 deletions quickjs-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,68 @@ static JSValue js_std_loadFile(JSContext *ctx, JSValue this_val,
return ret;
}

static JSValue js_std_writeFile(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
const char *filename;
const char *mode;
const void *buf;
size_t len, n;
JSValue data, val, ret;
bool release, unref;
FILE *fp;

ret = JS_EXCEPTION;
len = 0;
buf = "";
mode = "w";
data = argv[1];
unref = false;
release = false;
filename = JS_ToCString(ctx, argv[0]);
if (!filename)
return JS_EXCEPTION;
if (JS_IsObject(data)) {
val = JS_GetPropertyStr(ctx, data, "buffer");
if (JS_IsException(val))
goto exception;
if (JS_IsArrayBuffer(val)) {
data = val;
unref = true;
} else {
JS_FreeValue(ctx, val);
}
}
if (JS_IsArrayBuffer(data)) {
buf = JS_GetArrayBuffer(ctx, &len, data);
mode = "wb";
} else if (!JS_IsUndefined(data)) {
buf = JS_ToCStringLen(ctx, &len, data);
release = true;
}
if (!buf)
goto exception;
fp = fopen(filename, mode);
if (!fp) {
JS_ThrowPlainError(ctx, "error opening %s for writing", filename);
goto exception;
}
n = fwrite(buf, len, 1, fp);
fclose(fp);
if (n != 1) {
JS_ThrowPlainError(ctx, "error writing to %s", filename);
goto exception;
}
ret = JS_UNDEFINED;
exception:
JS_FreeCString(ctx, filename);
if (release)
JS_FreeCString(ctx, buf);
if (unref)
JS_FreeValue(ctx, data);
return ret;
}

typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx,
const char *module_name);

Expand Down Expand Up @@ -1674,6 +1736,7 @@ static const JSCFunctionListEntry js_std_funcs[] = {
JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ),
#endif
JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ),
JS_CFUNC_DEF("writeFile", 2, js_std_writeFile ),
JS_CFUNC_DEF("strerror", 1, js_std_strerror ),

/* FILE I/O */
Expand Down
13 changes: 7 additions & 6 deletions tests/test_std.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,15 @@ function test_getline()
function test_popen()
{
var str, f, fname = "tmp_file.txt";
var content = "hello world";
var ta, content = "hello world";
var cmd = isWin ? "type" : "cat";

f = std.open(fname, "w");
f.puts(content);
f.close();

/* test loadFile */
ta = new Uint8Array([...content].map(c => c.charCodeAt(0)));
std.writeFile(fname, ta);
assert(std.loadFile(fname), content);
std.writeFile(fname, ta.buffer);
assert(std.loadFile(fname), content);
std.writeFile(fname, content);
assert(std.loadFile(fname), content);

/* execute shell command */
Expand Down

0 comments on commit 9a902ba

Please sign in to comment.