2018-01-12 15:41:27 +00:00
|
|
|
/*
|
2018-05-18 03:41:20 +00:00
|
|
|
* Copyright (c) 2018 Calvin Rose
|
2018-01-12 15:41:27 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2018-09-10 18:54:12 +00:00
|
|
|
/* Compiler feature test macros for things */
|
2018-07-04 03:07:35 +00:00
|
|
|
#define _DEFAULT_SOURCE
|
2018-09-10 18:54:12 +00:00
|
|
|
#define _BSD_SOURCE
|
|
|
|
|
2018-07-04 03:07:35 +00:00
|
|
|
#include <stdio.h>
|
2018-09-06 02:18:42 +00:00
|
|
|
#include <janet/janet.h>
|
2018-05-11 12:13:26 +00:00
|
|
|
#include <errno.h>
|
2018-01-06 16:09:15 +00:00
|
|
|
|
2018-01-16 01:14:21 +00:00
|
|
|
#define IO_WRITE 1
|
|
|
|
#define IO_READ 2
|
|
|
|
#define IO_APPEND 4
|
|
|
|
#define IO_UPDATE 8
|
|
|
|
#define IO_NOT_CLOSEABLE 16
|
|
|
|
#define IO_CLOSED 32
|
|
|
|
#define IO_BINARY 64
|
|
|
|
#define IO_SERIALIZABLE 128
|
2018-05-11 12:13:26 +00:00
|
|
|
#define IO_PIPED 256
|
2018-01-16 01:14:21 +00:00
|
|
|
|
|
|
|
typedef struct IOFile IOFile;
|
|
|
|
struct IOFile {
|
|
|
|
FILE *file;
|
|
|
|
int flags;
|
|
|
|
};
|
|
|
|
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_gc(void *p, size_t len);
|
2018-01-16 01:14:21 +00:00
|
|
|
|
2018-09-06 02:18:42 +00:00
|
|
|
JanetAbstractType janet_io_filetype = {
|
2018-02-03 22:22:04 +00:00
|
|
|
":core.file",
|
2018-09-06 02:18:42 +00:00
|
|
|
janet_io_gc,
|
2018-01-17 04:18:45 +00:00
|
|
|
NULL
|
2018-01-06 16:09:15 +00:00
|
|
|
};
|
|
|
|
|
2018-01-16 01:14:21 +00:00
|
|
|
/* Check argupments to fopen */
|
|
|
|
static int checkflags(const uint8_t *str, int32_t len) {
|
|
|
|
int flags = 0;
|
|
|
|
int32_t i;
|
|
|
|
if (!len || len > 3) return -1;
|
|
|
|
switch (*str) {
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
case 'w':
|
|
|
|
flags |= IO_WRITE;
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
flags |= IO_APPEND;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
flags |= IO_READ;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (i = 1; i < len; i++) {
|
|
|
|
switch (str[i]) {
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
case '+':
|
|
|
|
if (flags & IO_UPDATE) return -1;
|
|
|
|
flags |= IO_UPDATE;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
if (flags & IO_BINARY) return -1;
|
|
|
|
flags |= IO_BINARY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2018-01-06 18:25:45 +00:00
|
|
|
/* Check file argument */
|
2018-09-06 02:18:42 +00:00
|
|
|
static IOFile *checkfile(JanetArgs args, int32_t n) {
|
2018-01-16 01:14:21 +00:00
|
|
|
IOFile *iof;
|
|
|
|
if (n >= args.n) {
|
2018-09-06 02:18:42 +00:00
|
|
|
*args.ret = janet_cstringv("expected core.file");
|
2018-01-06 18:25:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
if (!janet_checktype(args.v[n], JANET_ABSTRACT)) {
|
|
|
|
*args.ret = janet_cstringv("expected core.file");
|
2018-01-06 18:25:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
iof = (IOFile *) janet_unwrap_abstract(args.v[n]);
|
|
|
|
if (janet_abstract_type(iof) != &janet_io_filetype) {
|
|
|
|
*args.ret = janet_cstringv("expected core.file");
|
2018-01-06 18:25:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
return iof;
|
2018-01-06 18:25:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check buffer argument */
|
2018-09-06 02:18:42 +00:00
|
|
|
static JanetBuffer *checkbuffer(JanetArgs args, int32_t n, int optional) {
|
2018-01-16 01:14:21 +00:00
|
|
|
if (optional && n == args.n) {
|
2018-09-06 02:18:42 +00:00
|
|
|
return janet_buffer(0);
|
2018-01-06 18:25:45 +00:00
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
if (n >= args.n) {
|
2018-09-06 02:18:42 +00:00
|
|
|
*args.ret = janet_cstringv("expected buffer");
|
2018-01-06 18:25:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
if (!janet_checktype(args.v[n], JANET_BUFFER)) {
|
|
|
|
*args.ret = janet_cstringv("expected buffer");
|
2018-01-06 18:25:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
return janet_unwrap_abstract(args.v[n]);
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 02:18:42 +00:00
|
|
|
static Janet makef(FILE *f, int flags) {
|
|
|
|
IOFile *iof = (IOFile *) janet_abstract(&janet_io_filetype, sizeof(IOFile));
|
2018-01-16 01:14:21 +00:00
|
|
|
iof->file = f;
|
|
|
|
iof->flags = flags;
|
2018-09-06 02:18:42 +00:00
|
|
|
return janet_wrap_abstract(iof);
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 12:13:26 +00:00
|
|
|
/* Open a process */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_popen(JanetArgs args) {
|
2018-05-11 12:13:26 +00:00
|
|
|
const uint8_t *fname, *fmode;
|
|
|
|
int32_t modelen;
|
|
|
|
FILE *f;
|
|
|
|
int flags;
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_MINARITY(args, 1);
|
|
|
|
JANET_MAXARITY(args, 2);
|
|
|
|
JANET_ARG_STRING(fname, args, 0);
|
2018-05-11 12:13:26 +00:00
|
|
|
if (args.n == 2) {
|
2018-09-06 02:18:42 +00:00
|
|
|
if (!janet_checktype(args.v[1], JANET_STRING) &&
|
|
|
|
!janet_checktype(args.v[1], JANET_SYMBOL))
|
|
|
|
JANET_THROW(args, "expected string mode");
|
|
|
|
fmode = janet_unwrap_string(args.v[1]);
|
|
|
|
modelen = janet_string_length(fmode);
|
2018-05-11 12:13:26 +00:00
|
|
|
} else {
|
|
|
|
fmode = (const uint8_t *)"r";
|
|
|
|
modelen = 1;
|
|
|
|
}
|
|
|
|
if (fmode[0] == ':') {
|
|
|
|
fmode++;
|
|
|
|
modelen--;
|
|
|
|
}
|
|
|
|
if (modelen != 1 || !(fmode[0] == 'r' || fmode[0] == 'w')) {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "invalid file mode");
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
|
|
|
flags = (fmode[0] == 'r') ? IO_PIPED | IO_READ : IO_PIPED | IO_WRITE;
|
2018-09-06 02:18:42 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
2018-07-11 00:01:39 +00:00
|
|
|
#define popen _popen
|
2018-10-17 03:08:26 +00:00
|
|
|
#endif
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
|
|
#define popen(A, B) (errno = 0, NULL)
|
2018-05-11 12:13:26 +00:00
|
|
|
#endif
|
2018-07-11 00:01:39 +00:00
|
|
|
f = popen((const char *)fname, (const char *)fmode);
|
2018-05-11 12:13:26 +00:00
|
|
|
if (!f) {
|
|
|
|
if (errno == EMFILE) {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "too many streams are open");
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "could not open file");
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_RETURN(args, makef(f, flags));
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
|
|
|
|
2018-01-16 01:14:21 +00:00
|
|
|
/* Open a a file and return a userdata wrapper around the C file API. */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_fopen(JanetArgs args) {
|
2018-01-16 01:14:21 +00:00
|
|
|
const uint8_t *fname, *fmode;
|
|
|
|
int32_t modelen;
|
2018-01-06 16:09:15 +00:00
|
|
|
FILE *f;
|
2018-01-16 01:14:21 +00:00
|
|
|
int flags;
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_MINARITY(args, 1);
|
|
|
|
JANET_MAXARITY(args, 2);
|
|
|
|
JANET_ARG_STRING(fname, args, 0);
|
2018-01-16 01:14:21 +00:00
|
|
|
if (args.n == 2) {
|
2018-09-06 02:18:42 +00:00
|
|
|
if (!janet_checktype(args.v[1], JANET_STRING) &&
|
|
|
|
!janet_checktype(args.v[1], JANET_SYMBOL))
|
|
|
|
JANET_THROW(args, "expected string mode");
|
|
|
|
fmode = janet_unwrap_string(args.v[1]);
|
|
|
|
modelen = janet_string_length(fmode);
|
2018-01-16 01:14:21 +00:00
|
|
|
} else {
|
|
|
|
fmode = (const uint8_t *)"r";
|
|
|
|
modelen = 1;
|
|
|
|
}
|
2018-02-04 17:58:40 +00:00
|
|
|
if (fmode[0] == ':') {
|
|
|
|
fmode++;
|
|
|
|
modelen--;
|
|
|
|
}
|
2018-05-13 00:31:28 +00:00
|
|
|
if ((flags = checkflags(fmode, modelen)) < 0) {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "invalid file mode");
|
2018-05-13 00:31:28 +00:00
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
f = fopen((const char *)fname, (const char *)fmode);
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_RETURN(args, f ? makef(f, flags) : janet_wrap_nil());
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 13:37:34 +00:00
|
|
|
/* Read up to n bytes into buffer. Return error string if error. */
|
2018-09-06 02:18:42 +00:00
|
|
|
static const char *read_chunk(IOFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
|
2018-06-26 13:37:34 +00:00
|
|
|
if (!(iof->flags & (IO_READ | IO_UPDATE)))
|
|
|
|
return "file is not readable";
|
|
|
|
/* Ensure buffer size */
|
2018-11-16 21:24:10 +00:00
|
|
|
if (janet_buffer_extra(buffer, nBytesMax))
|
2018-06-26 13:37:34 +00:00
|
|
|
return "buffer overflow";
|
|
|
|
size_t ntoread = nBytesMax;
|
|
|
|
size_t nread = fread((char *)(buffer->data + buffer->count), 1, ntoread, iof->file);
|
2018-11-16 21:24:10 +00:00
|
|
|
if (nread != ntoread && ferror(iof->file))
|
2018-06-26 13:37:34 +00:00
|
|
|
return "could not read file";
|
2018-08-06 01:32:32 +00:00
|
|
|
buffer->count += (int32_t) nread;
|
2018-06-26 13:37:34 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-06 16:09:15 +00:00
|
|
|
/* Read a certain number of bytes into memory */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_fread(JanetArgs args) {
|
|
|
|
JanetBuffer *b;
|
2018-01-16 01:14:21 +00:00
|
|
|
IOFile *iof = checkfile(args, 0);
|
|
|
|
if (!iof) return 1;
|
2018-05-11 12:13:26 +00:00
|
|
|
if (iof->flags & IO_CLOSED)
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file is closed");
|
2018-01-16 01:14:21 +00:00
|
|
|
b = checkbuffer(args, 2, 1);
|
2018-01-06 18:25:45 +00:00
|
|
|
if (!b) return 1;
|
2018-09-06 02:18:42 +00:00
|
|
|
if (janet_checktype(args.v[1], JANET_SYMBOL)) {
|
|
|
|
const uint8_t *sym = janet_unwrap_symbol(args.v[1]);
|
|
|
|
if (!janet_cstrcmp(sym, ":all")) {
|
2018-02-04 05:54:38 +00:00
|
|
|
/* Read whole file */
|
2018-06-26 13:37:34 +00:00
|
|
|
int status = fseek(iof->file, 0, SEEK_SET);
|
|
|
|
if (status) {
|
|
|
|
/* backwards fseek did not work (stream like popen) */
|
|
|
|
int32_t sizeBefore;
|
|
|
|
do {
|
|
|
|
sizeBefore = b->count;
|
|
|
|
const char *maybeErr = read_chunk(iof, b, 1024);
|
2018-09-06 02:18:42 +00:00
|
|
|
if (maybeErr) JANET_THROW(args, maybeErr);
|
2018-06-26 13:37:34 +00:00
|
|
|
} while (sizeBefore < b->count);
|
|
|
|
} else {
|
|
|
|
fseek(iof->file, 0, SEEK_END);
|
|
|
|
long fsize = ftell(iof->file);
|
|
|
|
fseek(iof->file, 0, SEEK_SET);
|
2018-09-06 02:18:42 +00:00
|
|
|
if (fsize > INT32_MAX) JANET_THROW(args, "buffer overflow");
|
2018-06-26 13:37:34 +00:00
|
|
|
const char *maybeErr = read_chunk(iof, b, (int32_t) fsize);;
|
2018-09-06 02:18:42 +00:00
|
|
|
if (maybeErr) JANET_THROW(args, maybeErr);
|
2018-06-26 13:37:34 +00:00
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
} else if (!janet_cstrcmp(sym, ":line")) {
|
2018-02-04 05:54:38 +00:00
|
|
|
for (;;) {
|
|
|
|
int x = fgetc(iof->file);
|
2018-09-06 02:18:42 +00:00
|
|
|
if (x != EOF && janet_buffer_push_u8(b, (uint8_t)x))
|
|
|
|
JANET_THROW(args, "buffer overflow");
|
2018-02-06 15:31:42 +00:00
|
|
|
if (x == EOF || x == '\n') break;
|
2018-02-04 05:54:38 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "expected one of :all, :line");
|
2018-05-20 01:16:00 +00:00
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
} else if (!janet_checktype(args.v[1], JANET_INTEGER)) {
|
|
|
|
JANET_THROW(args, "expected positive integer");
|
2018-02-04 05:54:38 +00:00
|
|
|
} else {
|
2018-09-06 02:18:42 +00:00
|
|
|
int32_t len = janet_unwrap_integer(args.v[1]);
|
|
|
|
if (len < 0) JANET_THROW(args, "expected positive integer");
|
2018-06-26 13:37:34 +00:00
|
|
|
const char *maybeErr = read_chunk(iof, b, len);
|
2018-09-06 02:18:42 +00:00
|
|
|
if (maybeErr) JANET_THROW(args, maybeErr);
|
2018-02-04 05:54:38 +00:00
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_RETURN(args, janet_wrap_buffer(b));
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write bytes to a file */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_fwrite(JanetArgs args) {
|
2018-01-12 15:41:27 +00:00
|
|
|
int32_t len, i;
|
|
|
|
const uint8_t *str;
|
2018-01-16 01:14:21 +00:00
|
|
|
IOFile *iof = checkfile(args, 0);
|
|
|
|
if (!iof) return 1;
|
2018-05-11 12:13:26 +00:00
|
|
|
if (iof->flags & IO_CLOSED)
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file is closed");
|
2018-01-16 01:14:21 +00:00
|
|
|
if (!(iof->flags & (IO_WRITE | IO_APPEND | IO_UPDATE)))
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file is not writeable");
|
2018-05-13 00:31:28 +00:00
|
|
|
for (i = 1; i < args.n; i++) {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_CHECKMANY(args, i, JANET_TFLAG_BYTES);
|
2018-05-13 00:31:28 +00:00
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
for (i = 1; i < args.n; i++) {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_ARG_BYTES(str, len, args, i);
|
2018-05-07 03:25:59 +00:00
|
|
|
if (len) {
|
2018-09-06 02:18:42 +00:00
|
|
|
if (!fwrite(str, len, 1, iof->file)) JANET_THROW(args, "error writing to file");
|
2018-05-07 03:25:59 +00:00
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_RETURN(args, janet_wrap_abstract(iof));
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Flush the bytes in the file */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_fflush(JanetArgs args) {
|
2018-01-16 01:14:21 +00:00
|
|
|
IOFile *iof = checkfile(args, 0);
|
|
|
|
if (!iof) return 1;
|
2018-05-11 12:13:26 +00:00
|
|
|
if (iof->flags & IO_CLOSED)
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file is closed");
|
2018-01-16 01:14:21 +00:00
|
|
|
if (!(iof->flags & (IO_WRITE | IO_APPEND | IO_UPDATE)))
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file is not flushable");
|
|
|
|
if (fflush(iof->file)) JANET_THROW(args, "could not flush file");
|
|
|
|
JANET_RETURN(args, janet_wrap_abstract(iof));
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup a file */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_gc(void *p, size_t len) {
|
2018-01-16 01:14:21 +00:00
|
|
|
(void) len;
|
|
|
|
IOFile *iof = (IOFile *)p;
|
|
|
|
if (!(iof->flags & (IO_NOT_CLOSEABLE | IO_CLOSED))) {
|
|
|
|
return fclose(iof->file);
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Close a file */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_fclose(JanetArgs args) {
|
2018-01-16 01:14:21 +00:00
|
|
|
IOFile *iof = checkfile(args, 0);
|
|
|
|
if (!iof) return 1;
|
2018-03-11 19:35:23 +00:00
|
|
|
if (iof->flags & (IO_CLOSED))
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file already closed");
|
2018-03-11 19:35:23 +00:00
|
|
|
if (iof->flags & (IO_NOT_CLOSEABLE))
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file not closable");
|
2018-05-11 12:13:26 +00:00
|
|
|
if (iof->flags & IO_PIPED) {
|
2018-09-06 02:18:42 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
2018-07-11 00:01:39 +00:00
|
|
|
#define pclose _pclose
|
2018-05-11 12:13:26 +00:00
|
|
|
#endif
|
2018-09-06 02:18:42 +00:00
|
|
|
if (pclose(iof->file)) JANET_THROW(args, "could not close file");
|
2018-05-11 12:13:26 +00:00
|
|
|
} else {
|
2018-09-06 02:18:42 +00:00
|
|
|
if (fclose(iof->file)) JANET_THROW(args, "could not close file");
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
iof->flags |= IO_CLOSED;
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_RETURN(args, janet_wrap_abstract(iof));
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
|
|
|
|
2018-02-04 17:58:40 +00:00
|
|
|
/* Seek a file */
|
2018-09-06 02:18:42 +00:00
|
|
|
static int janet_io_fseek(JanetArgs args) {
|
2018-02-04 17:58:40 +00:00
|
|
|
long int offset = 0;
|
|
|
|
int whence = SEEK_CUR;
|
|
|
|
IOFile *iof = checkfile(args, 0);
|
|
|
|
if (!iof) return 1;
|
2018-05-11 12:13:26 +00:00
|
|
|
if (iof->flags & IO_CLOSED)
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "file is closed");
|
2018-02-04 17:58:40 +00:00
|
|
|
if (args.n >= 2) {
|
|
|
|
const uint8_t *whence_sym;
|
2018-09-06 02:18:42 +00:00
|
|
|
if (!janet_checktype(args.v[1], JANET_SYMBOL))
|
|
|
|
JANET_THROW(args, "expected symbol");
|
|
|
|
whence_sym = janet_unwrap_symbol(args.v[1]);
|
|
|
|
if (!janet_cstrcmp(whence_sym, ":cur")) {
|
2018-02-04 17:58:40 +00:00
|
|
|
whence = SEEK_CUR;
|
2018-09-06 02:18:42 +00:00
|
|
|
} else if (!janet_cstrcmp(whence_sym, ":set")) {
|
2018-02-04 17:58:40 +00:00
|
|
|
whence = SEEK_SET;
|
2018-09-06 02:18:42 +00:00
|
|
|
} else if (!janet_cstrcmp(whence_sym, ":end")) {
|
2018-02-04 17:58:40 +00:00
|
|
|
whence = SEEK_END;
|
|
|
|
} else {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "expected one of :cur, :set, :end");
|
2018-02-04 17:58:40 +00:00
|
|
|
}
|
|
|
|
if (args.n >= 3) {
|
2018-11-16 07:09:38 +00:00
|
|
|
double doffset;
|
|
|
|
JANET_ARG_NUMBER(doffset, args, 2);
|
|
|
|
offset = (long int)doffset;
|
2018-02-04 17:58:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fseek(iof->file, offset, whence))
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_THROW(args, "error seeking file");
|
|
|
|
JANET_RETURN(args, args.v[0]);
|
2018-02-04 17:58:40 +00:00
|
|
|
}
|
|
|
|
|
2018-09-06 02:18:42 +00:00
|
|
|
static const JanetReg cfuns[] = {
|
2018-12-01 03:49:21 +00:00
|
|
|
{"file/open", janet_io_fopen,
|
|
|
|
"(file/open path [,mode])\n\n"
|
2018-11-16 07:09:38 +00:00
|
|
|
"Open a file. path is files absolute or relative path, and "
|
|
|
|
"mode is a set of flags indicating the mode to open the file in. "
|
|
|
|
"mode is a keyword where each character represents a flag. If the file "
|
|
|
|
"cannot be opened, returns nil, otherwise returns the new file handle. "
|
|
|
|
"Mode flags:\n\n"
|
|
|
|
"\tr - allow reading from the file\n"
|
|
|
|
"\tw - allow witing to the file\n"
|
|
|
|
"\ta - append to the file\n"
|
|
|
|
"\tb - open the file in binary mode (rather than text mode)\n"
|
|
|
|
"\t+ - append to the file instead of overwriting it"
|
|
|
|
},
|
2018-12-01 03:49:21 +00:00
|
|
|
{"file/close", janet_io_fclose,
|
|
|
|
"(file/close f)\n\n"
|
2018-11-16 07:09:38 +00:00
|
|
|
"Close a file and release all related resources. When you are "
|
|
|
|
"done reading a file, close it to prevent a resource leak and let "
|
|
|
|
"other processes read the file."
|
|
|
|
},
|
2018-12-01 03:49:21 +00:00
|
|
|
{"file/read", janet_io_fread,
|
|
|
|
"(file/read f what [,buf])\n\n"
|
2018-11-16 07:09:38 +00:00
|
|
|
"Read a number of bytes from a file into a buffer. A buffer can "
|
|
|
|
"be provided as an optional fourth argument. otherwise a new buffer "
|
|
|
|
"is created. 'what' can either be an integer or a keyword. Returns the "
|
|
|
|
"buffer with file contents. "
|
|
|
|
"Values for 'what':\n\n"
|
|
|
|
"\t:all - read the whole file\n"
|
|
|
|
"\t:line - read up to and including the next newline character\n"
|
|
|
|
"\tn (integer) - read up to n bytes from the file"
|
|
|
|
},
|
2018-12-01 03:49:21 +00:00
|
|
|
{"file/write", janet_io_fwrite,
|
|
|
|
"(file/write f bytes)\n\n"
|
2018-11-16 07:09:38 +00:00
|
|
|
"Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
|
|
|
|
"file"
|
|
|
|
},
|
2018-12-01 03:49:21 +00:00
|
|
|
{"file/flush", janet_io_fflush,
|
|
|
|
"(file/flush f)\n\n"
|
2018-11-16 07:09:38 +00:00
|
|
|
"Flush any buffered bytes to the filesystem. In most files, writes are "
|
|
|
|
"buffered for efficiency reasons. Returns the file handle."
|
|
|
|
},
|
2018-12-01 03:49:21 +00:00
|
|
|
{"file/seek", janet_io_fseek,
|
|
|
|
"(file/seek f [,whence [,n]])\n\n"
|
2018-11-16 07:09:38 +00:00
|
|
|
"Jump to a relative location in the file. 'whence' must be one of\n\n"
|
|
|
|
"\t:cur - jump relative to the current file location\n"
|
|
|
|
"\t:set - jump relative to the beginning of the file\n"
|
|
|
|
"\t:end - jump relative to the end of the file\n\n"
|
|
|
|
"By default, 'whence' is :cur. Optionally a value n may be passed "
|
|
|
|
"for the relative number of bytes to seek in the file. n may be a real "
|
|
|
|
"number to handle large files of more the 4GB. Returns the file handle."
|
|
|
|
},
|
2018-12-01 03:49:21 +00:00
|
|
|
{"file/popen", janet_io_popen,
|
|
|
|
"(file/popen path [,mode])\n\n"
|
2018-11-16 07:09:38 +00:00
|
|
|
"Open a file that is backed by a process. The file must be opened in either "
|
|
|
|
"the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
|
|
|
|
"process can be read from the file. In :w mode, the stdin of the process "
|
|
|
|
"can be written to. Returns the new file."
|
|
|
|
},
|
2018-11-15 20:45:41 +00:00
|
|
|
{NULL, NULL, NULL}
|
2018-01-19 21:43:19 +00:00
|
|
|
};
|
|
|
|
|
2018-01-16 01:14:21 +00:00
|
|
|
/* Module entry point */
|
2018-09-06 02:18:42 +00:00
|
|
|
int janet_lib_io(JanetArgs args) {
|
|
|
|
JanetTable *env = janet_env(args);
|
|
|
|
janet_cfuns(env, NULL, cfuns);
|
2018-01-16 01:14:21 +00:00
|
|
|
|
|
|
|
/* stdout */
|
2018-10-21 05:35:07 +00:00
|
|
|
janet_def(env, "stdout",
|
2018-11-16 21:24:10 +00:00
|
|
|
makef(stdout, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
2018-11-16 07:09:38 +00:00
|
|
|
"The standard output file.");
|
|
|
|
|
2018-01-16 01:14:21 +00:00
|
|
|
|
|
|
|
/* stderr */
|
2018-10-21 05:35:07 +00:00
|
|
|
janet_def(env, "stderr",
|
2018-11-16 07:09:38 +00:00
|
|
|
makef(stderr, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
|
|
|
"The standard error file.");
|
2018-01-16 01:14:21 +00:00
|
|
|
|
|
|
|
/* stdin */
|
2018-10-21 05:35:07 +00:00
|
|
|
janet_def(env, "stdin",
|
2018-11-16 07:09:38 +00:00
|
|
|
makef(stdin, IO_READ | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
|
|
|
"The standard input file.");
|
2018-01-16 01:14:21 +00:00
|
|
|
|
2018-01-12 15:41:27 +00:00
|
|
|
return 0;
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|