2018-01-12 15:41:27 +00:00
|
|
|
/*
|
2020-01-12 16:50:37 +00:00
|
|
|
* Copyright (c) 2020 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.
|
|
|
|
*/
|
|
|
|
|
2019-01-24 05:15:58 +00:00
|
|
|
#ifndef JANET_AMALG
|
2019-12-31 00:06:15 +00:00
|
|
|
#include "features.h"
|
2019-02-19 01:13:35 +00:00
|
|
|
#include <janet.h>
|
2019-01-06 06:49:56 +00:00
|
|
|
#include "util.h"
|
2019-01-24 05:15:58 +00:00
|
|
|
#endif
|
2018-01-06 16:09:15 +00:00
|
|
|
|
2019-12-15 21:41:58 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2019-05-30 22:50:52 +00:00
|
|
|
#ifndef JANET_WINDOWS
|
2020-05-05 23:14:42 +00:00
|
|
|
#include <fcntl.h>
|
2019-05-30 22:50:52 +00:00
|
|
|
#include <sys/wait.h>
|
2020-05-06 23:33:25 +00:00
|
|
|
#include <unistd.h>
|
2019-05-30 22:50:52 +00:00
|
|
|
#endif
|
|
|
|
|
2019-01-24 05:15:58 +00:00
|
|
|
static int cfun_io_gc(void *p, size_t len);
|
2019-12-09 03:50:33 +00:00
|
|
|
static int io_file_get(void *p, Janet key, Janet *out);
|
2020-07-03 21:19:05 +00:00
|
|
|
static void io_file_marshal(void *p, JanetMarshalContext *ctx);
|
|
|
|
static void *io_file_unmarshal(JanetMarshalContext *ctx);
|
2018-01-16 01:14:21 +00:00
|
|
|
|
2020-03-14 15:12:47 +00:00
|
|
|
const JanetAbstractType janet_file_type = {
|
2019-01-03 00:41:07 +00:00
|
|
|
"core/file",
|
2019-01-24 05:15:58 +00:00
|
|
|
cfun_io_gc,
|
2019-02-05 16:14:13 +00:00
|
|
|
NULL,
|
2019-02-06 22:58:27 +00:00
|
|
|
io_file_get,
|
2020-07-03 21:19:05 +00:00
|
|
|
NULL,
|
|
|
|
io_file_marshal,
|
|
|
|
io_file_unmarshal,
|
|
|
|
JANET_ATEND_UNMARSHAL
|
2018-01-06 16:09:15 +00:00
|
|
|
};
|
|
|
|
|
2019-01-06 08:23:03 +00:00
|
|
|
/* Check arguments to fopen */
|
2020-07-03 21:19:05 +00:00
|
|
|
static int32_t checkflags(const uint8_t *str) {
|
|
|
|
int32_t flags = 0;
|
2018-01-16 01:14:21 +00:00
|
|
|
int32_t i;
|
2019-01-06 01:09:03 +00:00
|
|
|
int32_t len = janet_string_length(str);
|
2020-08-27 12:46:00 +00:00
|
|
|
if (!len || len > 10)
|
|
|
|
janet_panic("file mode must have a length between 1 and 10");
|
2018-01-16 01:14:21 +00:00
|
|
|
switch (*str) {
|
|
|
|
default:
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panicf("invalid flag %c, expected w, a, or r", *str);
|
|
|
|
break;
|
2018-01-16 01:14:21 +00:00
|
|
|
case 'w':
|
2019-12-14 15:03:56 +00:00
|
|
|
flags |= JANET_FILE_WRITE;
|
2018-01-16 01:14:21 +00:00
|
|
|
break;
|
|
|
|
case 'a':
|
2019-12-14 15:03:56 +00:00
|
|
|
flags |= JANET_FILE_APPEND;
|
2018-01-16 01:14:21 +00:00
|
|
|
break;
|
|
|
|
case 'r':
|
2019-12-14 15:03:56 +00:00
|
|
|
flags |= JANET_FILE_READ;
|
2018-01-16 01:14:21 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (i = 1; i < len; i++) {
|
|
|
|
switch (str[i]) {
|
|
|
|
default:
|
2020-08-27 12:46:00 +00:00
|
|
|
janet_panicf("invalid flag %c, expected +, b, or n", str[i]);
|
2019-01-06 01:09:03 +00:00
|
|
|
break;
|
2018-01-16 01:14:21 +00:00
|
|
|
case '+':
|
2019-12-14 15:03:56 +00:00
|
|
|
if (flags & JANET_FILE_UPDATE) return -1;
|
|
|
|
flags |= JANET_FILE_UPDATE;
|
2018-01-16 01:14:21 +00:00
|
|
|
break;
|
|
|
|
case 'b':
|
2019-12-14 15:03:56 +00:00
|
|
|
if (flags & JANET_FILE_BINARY) return -1;
|
|
|
|
flags |= JANET_FILE_BINARY;
|
2018-01-16 01:14:21 +00:00
|
|
|
break;
|
2020-08-27 12:46:00 +00:00
|
|
|
case 'n':
|
|
|
|
if (flags & JANET_FILE_NONIL) return -1;
|
|
|
|
flags |= JANET_FILE_NONIL;
|
|
|
|
break;
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2020-07-03 21:19:05 +00:00
|
|
|
static void *makef(FILE *f, int32_t flags) {
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
|
2018-01-16 01:14:21 +00:00
|
|
|
iof->file = f;
|
|
|
|
iof->flags = flags;
|
2020-05-06 23:33:25 +00:00
|
|
|
#ifndef JANET_WINDOWS
|
|
|
|
/* While we would like fopen to set cloexec by default (like O_CLOEXEC) with the e flag, that is
|
|
|
|
* not standard. */
|
2020-05-09 10:26:50 +00:00
|
|
|
if (!(flags & JANET_FILE_NOT_CLOSEABLE))
|
2020-05-09 17:00:01 +00:00
|
|
|
fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
|
2020-05-06 23:33:25 +00:00
|
|
|
#endif
|
2020-07-03 21:19:05 +00:00
|
|
|
return iof;
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
|
2018-05-11 12:13:26 +00:00
|
|
|
/* Open a process */
|
2020-05-09 17:00:01 +00:00
|
|
|
#ifndef JANET_NO_PROCESSES
|
2019-01-24 05:15:58 +00:00
|
|
|
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_arity(argc, 1, 2);
|
|
|
|
const uint8_t *fname = janet_getstring(argv, 0);
|
|
|
|
const uint8_t *fmode = NULL;
|
2020-07-03 21:19:05 +00:00
|
|
|
int32_t flags;
|
2019-01-06 01:09:03 +00:00
|
|
|
if (argc == 2) {
|
|
|
|
fmode = janet_getkeyword(argv, 1);
|
2020-08-27 12:46:00 +00:00
|
|
|
flags = JANET_FILE_PIPED | checkflags(fmode);
|
|
|
|
if (flags & (JANET_FILE_UPDATE | JANET_FILE_BINARY | JANET_FILE_APPEND)) {
|
|
|
|
janet_panicf("invalid popen file mode :%S, expected :r or :w", fmode);
|
2019-01-06 01:09:03 +00:00
|
|
|
}
|
2020-08-27 12:46:00 +00:00
|
|
|
fmode = (const uint8_t *)((fmode[0] == 'r') ? "r" : "w");
|
2019-01-09 02:42:16 +00:00
|
|
|
} else {
|
|
|
|
fmode = (const uint8_t *)"r";
|
2019-12-14 15:03:56 +00:00
|
|
|
flags = JANET_FILE_PIPED | JANET_FILE_READ;
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
2018-07-11 00:01:39 +00:00
|
|
|
#define popen _popen
|
2018-05-11 12:13:26 +00:00
|
|
|
#endif
|
2019-01-06 01:09:03 +00:00
|
|
|
FILE *f = popen((const char *)fname, (const char *)fmode);
|
2018-05-11 12:13:26 +00:00
|
|
|
if (!f) {
|
2020-08-27 12:46:00 +00:00
|
|
|
if (flags & JANET_FILE_NONIL)
|
|
|
|
janet_panicf("failed to popen %s: %s", fname, strerror(errno));
|
2019-01-06 01:09:03 +00:00
|
|
|
return janet_wrap_nil();
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
2020-07-03 21:19:05 +00:00
|
|
|
return janet_makefile(f, flags);
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
2019-01-06 01:52:32 +00:00
|
|
|
#endif
|
2018-05-11 12:13:26 +00:00
|
|
|
|
2019-12-29 22:57:34 +00:00
|
|
|
static Janet cfun_io_temp(int32_t argc, Janet *argv) {
|
|
|
|
(void)argv;
|
|
|
|
janet_fixarity(argc, 0);
|
2020-05-09 10:26:50 +00:00
|
|
|
// XXX use mkostemp when we can to avoid CLOEXEC race.
|
2019-12-29 22:57:34 +00:00
|
|
|
FILE *tmp = tmpfile();
|
|
|
|
if (!tmp)
|
2019-12-31 01:24:40 +00:00
|
|
|
janet_panicf("unable to create temporary file - %s", strerror(errno));
|
|
|
|
return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
|
2019-12-29 22:57:34 +00:00
|
|
|
}
|
|
|
|
|
2019-01-24 05:15:58 +00:00
|
|
|
static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_arity(argc, 1, 2);
|
|
|
|
const uint8_t *fname = janet_getstring(argv, 0);
|
|
|
|
const uint8_t *fmode;
|
2020-07-03 21:19:05 +00:00
|
|
|
int32_t flags;
|
2019-01-06 01:09:03 +00:00
|
|
|
if (argc == 2) {
|
|
|
|
fmode = janet_getkeyword(argv, 1);
|
|
|
|
flags = checkflags(fmode);
|
2018-01-16 01:14:21 +00:00
|
|
|
} else {
|
|
|
|
fmode = (const uint8_t *)"r";
|
2019-12-14 15:03:56 +00:00
|
|
|
flags = JANET_FILE_READ;
|
2018-05-13 00:31:28 +00:00
|
|
|
}
|
2019-01-06 01:09:03 +00:00
|
|
|
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
2020-08-27 12:46:00 +00:00
|
|
|
return f ? janet_makefile(f, flags)
|
2020-09-02 01:06:35 +00:00
|
|
|
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
|
|
|
|
: janet_wrap_nil();
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|
|
|
|
|
2019-05-14 15:04:17 +00:00
|
|
|
/* Read up to n bytes into buffer. */
|
2020-03-14 15:12:47 +00:00
|
|
|
static void read_chunk(JanetFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
|
2019-12-14 15:03:56 +00:00
|
|
|
if (!(iof->flags & (JANET_FILE_READ | JANET_FILE_UPDATE)))
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("file is not readable");
|
|
|
|
janet_buffer_extra(buffer, nBytesMax);
|
2018-06-26 13:37:34 +00:00
|
|
|
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))
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("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
|
|
|
}
|
|
|
|
|
2018-01-06 16:09:15 +00:00
|
|
|
/* Read a certain number of bytes into memory */
|
2019-01-24 05:15:58 +00:00
|
|
|
static Janet cfun_io_fread(int32_t argc, Janet *argv) {
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_arity(argc, 2, 3);
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
2019-12-14 15:03:56 +00:00
|
|
|
if (iof->flags & JANET_FILE_CLOSED) janet_panic("file is closed");
|
2019-01-06 01:09:03 +00:00
|
|
|
JanetBuffer *buffer;
|
|
|
|
if (argc == 2) {
|
|
|
|
buffer = janet_buffer(0);
|
|
|
|
} else {
|
|
|
|
buffer = janet_getbuffer(argv, 2);
|
|
|
|
}
|
2019-05-14 15:04:17 +00:00
|
|
|
int32_t bufstart = buffer->count;
|
2019-01-06 01:09:03 +00:00
|
|
|
if (janet_checktype(argv[1], JANET_KEYWORD)) {
|
|
|
|
const uint8_t *sym = janet_unwrap_keyword(argv[1]);
|
2019-01-03 00:41:07 +00:00
|
|
|
if (!janet_cstrcmp(sym, "all")) {
|
2019-11-12 01:05:00 +00:00
|
|
|
int32_t sizeBefore;
|
|
|
|
do {
|
|
|
|
sizeBefore = buffer->count;
|
|
|
|
read_chunk(iof, buffer, 4096);
|
|
|
|
} while (sizeBefore < buffer->count);
|
2019-05-14 15:04:17 +00:00
|
|
|
/* Never return nil for :all */
|
|
|
|
return janet_wrap_buffer(buffer);
|
2019-01-03 00:41:07 +00:00
|
|
|
} else if (!janet_cstrcmp(sym, "line")) {
|
2018-02-04 05:54:38 +00:00
|
|
|
for (;;) {
|
|
|
|
int x = fgetc(iof->file);
|
2019-01-06 01:09:03 +00:00
|
|
|
if (x != EOF) janet_buffer_push_u8(buffer, (uint8_t)x);
|
2018-02-06 15:31:42 +00:00
|
|
|
if (x == EOF || x == '\n') break;
|
2018-02-04 05:54:38 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panicf("expected one of :all, :line, got %v", argv[1]);
|
2018-05-20 01:16:00 +00:00
|
|
|
}
|
2018-02-04 05:54:38 +00:00
|
|
|
} else {
|
2019-01-06 01:09:03 +00:00
|
|
|
int32_t len = janet_getinteger(argv, 1);
|
|
|
|
if (len < 0) janet_panic("expected positive integer");
|
|
|
|
read_chunk(iof, buffer, len);
|
2018-02-04 05:54:38 +00:00
|
|
|
}
|
2019-05-14 15:04:17 +00:00
|
|
|
if (bufstart == buffer->count) return janet_wrap_nil();
|
2019-01-06 01:09:03 +00:00
|
|
|
return janet_wrap_buffer(buffer);
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write bytes to a file */
|
2019-01-24 05:15:58 +00:00
|
|
|
static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_arity(argc, 1, -1);
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
2019-12-14 15:03:56 +00:00
|
|
|
if (iof->flags & JANET_FILE_CLOSED)
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("file is closed");
|
2019-12-14 15:03:56 +00:00
|
|
|
if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("file is not writeable");
|
|
|
|
int32_t i;
|
|
|
|
/* Verify all arguments before writing to file */
|
|
|
|
for (i = 1; i < argc; i++)
|
|
|
|
janet_getbytes(argv, i);
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
JanetByteView view = janet_getbytes(argv, i);
|
|
|
|
if (view.len) {
|
|
|
|
if (!fwrite(view.bytes, view.len, 1, iof->file)) {
|
|
|
|
janet_panic("error writing to file");
|
|
|
|
}
|
2018-05-07 03:25:59 +00:00
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
2019-01-06 01:09:03 +00:00
|
|
|
return argv[0];
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Flush the bytes in the file */
|
2019-01-24 05:15:58 +00:00
|
|
|
static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
|
2019-01-06 01:45:24 +00:00
|
|
|
janet_fixarity(argc, 1);
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
2019-12-14 15:03:56 +00:00
|
|
|
if (iof->flags & JANET_FILE_CLOSED)
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("file is closed");
|
2019-12-14 15:03:56 +00:00
|
|
|
if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("file is not writeable");
|
|
|
|
if (fflush(iof->file))
|
|
|
|
janet_panic("could not flush file");
|
|
|
|
return argv[0];
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
|
|
|
|
2020-05-06 23:33:25 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
#define pclose _pclose
|
|
|
|
#define WEXITSTATUS(x) x
|
|
|
|
#endif
|
|
|
|
|
2018-01-16 01:14:21 +00:00
|
|
|
/* Cleanup a file */
|
2019-01-24 05:15:58 +00:00
|
|
|
static int cfun_io_gc(void *p, size_t len) {
|
2018-01-16 01:14:21 +00:00
|
|
|
(void) len;
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = (JanetFile *)p;
|
2019-12-14 15:03:56 +00:00
|
|
|
if (!(iof->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
|
2020-05-06 23:33:25 +00:00
|
|
|
/* We can't panic inside a gc, so just ignore bad statuses here */
|
|
|
|
if (iof->flags & JANET_FILE_PIPED) {
|
2020-05-11 02:06:52 +00:00
|
|
|
#ifndef JANET_NO_PROCESSES
|
2020-05-06 23:33:25 +00:00
|
|
|
pclose(iof->file);
|
2020-05-11 02:06:52 +00:00
|
|
|
#endif
|
2020-05-06 23:33:25 +00:00
|
|
|
} else {
|
|
|
|
fclose(iof->file);
|
|
|
|
}
|
2018-01-12 15:41:27 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Close a file */
|
2019-01-24 05:15:58 +00:00
|
|
|
static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
|
2019-01-06 01:45:24 +00:00
|
|
|
janet_fixarity(argc, 1);
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
2019-12-14 15:03:56 +00:00
|
|
|
if (iof->flags & JANET_FILE_CLOSED)
|
2020-01-21 09:03:57 +00:00
|
|
|
return janet_wrap_nil();
|
2019-12-14 15:03:56 +00:00
|
|
|
if (iof->flags & (JANET_FILE_NOT_CLOSEABLE))
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("file not closable");
|
2019-12-14 15:03:56 +00:00
|
|
|
if (iof->flags & JANET_FILE_PIPED) {
|
2020-05-11 02:06:52 +00:00
|
|
|
#ifndef JANET_NO_PROCESSES
|
2019-05-29 15:40:07 +00:00
|
|
|
int status = pclose(iof->file);
|
2019-12-14 15:03:56 +00:00
|
|
|
iof->flags |= JANET_FILE_CLOSED;
|
2019-05-29 15:50:46 +00:00
|
|
|
if (status == -1) janet_panic("could not close file");
|
|
|
|
return janet_wrap_integer(WEXITSTATUS(status));
|
2020-08-08 00:48:06 +00:00
|
|
|
#else
|
|
|
|
return janet_wrap_nil();
|
2020-05-11 02:06:52 +00:00
|
|
|
#endif
|
2018-05-11 12:13:26 +00:00
|
|
|
} else {
|
2020-05-28 19:35:09 +00:00
|
|
|
if (fclose(iof->file)) {
|
|
|
|
iof->flags |= JANET_FILE_NOT_CLOSEABLE;
|
|
|
|
janet_panic("could not close file");
|
|
|
|
}
|
2019-12-14 15:03:56 +00:00
|
|
|
iof->flags |= JANET_FILE_CLOSED;
|
2019-05-29 15:40:07 +00:00
|
|
|
return janet_wrap_nil();
|
2018-05-11 12:13:26 +00:00
|
|
|
}
|
2018-01-16 01:14:21 +00:00
|
|
|
}
|
|
|
|
|
2018-02-04 17:58:40 +00:00
|
|
|
/* Seek a file */
|
2019-01-24 05:15:58 +00:00
|
|
|
static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_arity(argc, 2, 3);
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
2019-12-14 15:03:56 +00:00
|
|
|
if (iof->flags & JANET_FILE_CLOSED)
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panic("file is closed");
|
2018-02-04 17:58:40 +00:00
|
|
|
long int offset = 0;
|
|
|
|
int whence = SEEK_CUR;
|
2019-01-06 01:09:03 +00:00
|
|
|
if (argc >= 2) {
|
|
|
|
const uint8_t *whence_sym = janet_getkeyword(argv, 1);
|
2019-01-03 00:41:07 +00:00
|
|
|
if (!janet_cstrcmp(whence_sym, "cur")) {
|
2018-02-04 17:58:40 +00:00
|
|
|
whence = SEEK_CUR;
|
2019-01-03 00:41:07 +00:00
|
|
|
} else if (!janet_cstrcmp(whence_sym, "set")) {
|
2018-02-04 17:58:40 +00:00
|
|
|
whence = SEEK_SET;
|
2019-01-03 00:41:07 +00:00
|
|
|
} else if (!janet_cstrcmp(whence_sym, "end")) {
|
2018-02-04 17:58:40 +00:00
|
|
|
whence = SEEK_END;
|
|
|
|
} else {
|
2019-01-06 01:09:03 +00:00
|
|
|
janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]);
|
2018-02-04 17:58:40 +00:00
|
|
|
}
|
2019-01-06 01:09:03 +00:00
|
|
|
if (argc == 3) {
|
2019-01-06 02:58:39 +00:00
|
|
|
offset = (long) janet_getinteger64(argv, 2);
|
2018-02-04 17:58:40 +00:00
|
|
|
}
|
|
|
|
}
|
2019-01-06 01:09:03 +00:00
|
|
|
if (fseek(iof->file, offset, whence)) janet_panic("error seeking file");
|
|
|
|
return argv[0];
|
2018-02-04 17:58:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-06 22:58:27 +00:00
|
|
|
static JanetMethod io_file_methods[] = {
|
|
|
|
{"close", cfun_io_fclose},
|
|
|
|
{"flush", cfun_io_fflush},
|
2019-11-28 01:43:46 +00:00
|
|
|
{"read", cfun_io_fread},
|
2019-02-06 22:58:27 +00:00
|
|
|
{"seek", cfun_io_fseek},
|
2019-11-28 01:43:46 +00:00
|
|
|
{"write", cfun_io_fwrite},
|
2019-02-06 22:58:27 +00:00
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
2019-12-09 03:50:33 +00:00
|
|
|
static int io_file_get(void *p, Janet key, Janet *out) {
|
2019-02-06 22:58:27 +00:00
|
|
|
(void) p;
|
|
|
|
if (!janet_checktype(key, JANET_KEYWORD))
|
2019-12-09 03:50:33 +00:00
|
|
|
return 0;
|
2019-12-10 00:45:05 +00:00
|
|
|
return janet_getmethod(janet_unwrap_keyword(key), io_file_methods, out);
|
2019-02-06 22:58:27 +00:00
|
|
|
}
|
|
|
|
|
2020-07-03 21:19:05 +00:00
|
|
|
static void io_file_marshal(void *p, JanetMarshalContext *ctx) {
|
|
|
|
JanetFile *iof = (JanetFile *)p;
|
|
|
|
if (ctx->flags & JANET_MARSHAL_UNSAFE) {
|
2020-07-25 13:09:22 +00:00
|
|
|
janet_marshal_abstract(ctx, p);
|
2020-07-04 01:13:49 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
janet_marshal_int(ctx, _fileno(iof->file));
|
|
|
|
#else
|
2020-07-03 21:19:05 +00:00
|
|
|
janet_marshal_int(ctx, fileno(iof->file));
|
2020-07-04 01:13:49 +00:00
|
|
|
#endif
|
2020-07-03 21:19:05 +00:00
|
|
|
janet_marshal_int(ctx, iof->flags);
|
|
|
|
} else {
|
|
|
|
janet_panic("cannot marshal file in safe mode");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *io_file_unmarshal(JanetMarshalContext *ctx) {
|
|
|
|
if (ctx->flags & JANET_MARSHAL_UNSAFE) {
|
|
|
|
JanetFile *iof = janet_unmarshal_abstract(ctx, sizeof(JanetFile));
|
|
|
|
int32_t fd = janet_unmarshal_int(ctx);
|
|
|
|
int32_t flags = janet_unmarshal_int(ctx);
|
|
|
|
char fmt[4] = {0};
|
|
|
|
int index = 0;
|
|
|
|
if (flags & JANET_FILE_READ) fmt[index++] = 'r';
|
|
|
|
if (flags & JANET_FILE_APPEND) {
|
|
|
|
fmt[index++] = 'a';
|
|
|
|
} else if (flags & JANET_FILE_WRITE) {
|
|
|
|
fmt[index++] = 'w';
|
|
|
|
}
|
2020-07-04 01:13:49 +00:00
|
|
|
#ifdef JANET_WINDOWS
|
|
|
|
iof->file = _fdopen(fd, fmt);
|
|
|
|
#else
|
2020-07-03 21:19:05 +00:00
|
|
|
iof->file = fdopen(fd, fmt);
|
2020-07-04 01:13:49 +00:00
|
|
|
#endif
|
2020-07-03 21:19:05 +00:00
|
|
|
if (iof->file == NULL) {
|
|
|
|
iof->flags = JANET_FILE_CLOSED;
|
|
|
|
} else {
|
|
|
|
iof->flags = flags;
|
|
|
|
}
|
|
|
|
return iof;
|
|
|
|
} else {
|
|
|
|
janet_panic("cannot unmarshal file in safe mode");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 01:44:19 +00:00
|
|
|
FILE *janet_dynfile(const char *name, FILE *def) {
|
|
|
|
Janet x = janet_dyn(name);
|
|
|
|
if (!janet_checktype(x, JANET_ABSTRACT)) return def;
|
2019-04-16 23:10:01 +00:00
|
|
|
void *abstract = janet_unwrap_abstract(x);
|
2020-03-14 15:12:47 +00:00
|
|
|
if (janet_abstract_type(abstract) != &janet_file_type) return def;
|
|
|
|
JanetFile *iofile = abstract;
|
2019-04-17 01:44:19 +00:00
|
|
|
return iofile->file;
|
2019-04-16 23:10:01 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 14:30:58 +00:00
|
|
|
static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
|
|
|
|
FILE *dflt_file, int32_t offset, Janet x) {
|
2019-10-19 14:44:27 +00:00
|
|
|
FILE *f;
|
|
|
|
switch (janet_type(x)) {
|
|
|
|
default:
|
2020-08-09 14:30:58 +00:00
|
|
|
janet_panicf("cannot print to %v", x);
|
2019-10-19 14:44:27 +00:00
|
|
|
case JANET_BUFFER: {
|
|
|
|
/* Special case buffer */
|
|
|
|
JanetBuffer *buf = janet_unwrap_buffer(x);
|
2020-08-09 14:30:58 +00:00
|
|
|
for (int32_t i = offset; i < argc; ++i) {
|
2019-10-19 15:30:29 +00:00
|
|
|
janet_to_string_b(buf, argv[i]);
|
2019-10-19 14:44:27 +00:00
|
|
|
}
|
|
|
|
if (newline)
|
|
|
|
janet_buffer_push_u8(buf, '\n');
|
|
|
|
return janet_wrap_nil();
|
|
|
|
}
|
|
|
|
case JANET_NIL:
|
|
|
|
f = dflt_file;
|
2020-08-09 14:30:58 +00:00
|
|
|
if (f == NULL) janet_panic("cannot print to nil");
|
2019-10-19 14:44:27 +00:00
|
|
|
break;
|
|
|
|
case JANET_ABSTRACT: {
|
|
|
|
void *abstract = janet_unwrap_abstract(x);
|
2020-03-14 15:12:47 +00:00
|
|
|
if (janet_abstract_type(abstract) != &janet_file_type)
|
2019-10-19 15:30:29 +00:00
|
|
|
return janet_wrap_nil();
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iofile = abstract;
|
2019-10-19 14:44:27 +00:00
|
|
|
f = iofile->file;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-08-09 14:30:58 +00:00
|
|
|
for (int32_t i = offset; i < argc; ++i) {
|
2019-10-19 15:30:29 +00:00
|
|
|
int32_t len;
|
|
|
|
const uint8_t *vstr;
|
|
|
|
if (janet_checktype(argv[i], JANET_BUFFER)) {
|
|
|
|
JanetBuffer *b = janet_unwrap_buffer(argv[i]);
|
|
|
|
vstr = b->data;
|
|
|
|
len = b->count;
|
|
|
|
} else {
|
|
|
|
vstr = janet_to_string(argv[i]);
|
|
|
|
len = janet_string_length(vstr);
|
|
|
|
}
|
2019-10-19 15:51:11 +00:00
|
|
|
if (len) {
|
|
|
|
if (1 != fwrite(vstr, len, 1, f)) {
|
2020-08-09 14:30:58 +00:00
|
|
|
if (f == dflt_file) {
|
|
|
|
janet_panicf("cannot print %d bytes", len);
|
|
|
|
} else {
|
|
|
|
janet_panicf("cannot print %d bytes to %v", len, x);
|
|
|
|
}
|
2019-10-19 15:51:11 +00:00
|
|
|
}
|
2019-04-16 23:10:01 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-19 14:44:27 +00:00
|
|
|
if (newline)
|
|
|
|
putc('\n', f);
|
2019-04-16 23:10:01 +00:00
|
|
|
return janet_wrap_nil();
|
2019-10-19 14:44:27 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 14:30:58 +00:00
|
|
|
|
|
|
|
static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
|
|
|
|
int newline, const char *name, FILE *dflt_file) {
|
|
|
|
Janet x = janet_dyn(name);
|
|
|
|
return cfun_io_print_impl_x(argc, argv, newline, dflt_file, 0, x);
|
|
|
|
}
|
|
|
|
|
2019-10-19 14:44:27 +00:00
|
|
|
static Janet cfun_io_print(int32_t argc, Janet *argv) {
|
|
|
|
return cfun_io_print_impl(argc, argv, 1, "out", stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_prin(int32_t argc, Janet *argv) {
|
|
|
|
return cfun_io_print_impl(argc, argv, 0, "out", stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_eprint(int32_t argc, Janet *argv) {
|
|
|
|
return cfun_io_print_impl(argc, argv, 1, "err", stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_eprin(int32_t argc, Janet *argv) {
|
|
|
|
return cfun_io_print_impl(argc, argv, 0, "err", stderr);
|
|
|
|
}
|
|
|
|
|
2020-08-09 14:30:58 +00:00
|
|
|
static Janet cfun_io_xprint(int32_t argc, Janet *argv) {
|
2019-10-19 15:30:29 +00:00
|
|
|
janet_arity(argc, 1, -1);
|
2020-08-09 14:30:58 +00:00
|
|
|
return cfun_io_print_impl_x(argc, argv, 1, NULL, 1, argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_xprin(int32_t argc, Janet *argv) {
|
|
|
|
janet_arity(argc, 1, -1);
|
|
|
|
return cfun_io_print_impl_x(argc, argv, 0, NULL, 1, argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
|
|
|
|
FILE *dflt_file, int32_t offset, Janet x) {
|
|
|
|
FILE *f;
|
|
|
|
const char *fmt = janet_getcstring(argv, offset);
|
2019-10-19 15:30:29 +00:00
|
|
|
switch (janet_type(x)) {
|
|
|
|
default:
|
2020-08-09 14:30:58 +00:00
|
|
|
janet_panicf("cannot print to %v", x);
|
2019-10-19 15:30:29 +00:00
|
|
|
case JANET_BUFFER: {
|
|
|
|
/* Special case buffer */
|
|
|
|
JanetBuffer *buf = janet_unwrap_buffer(x);
|
2020-08-09 14:30:58 +00:00
|
|
|
janet_buffer_format(buf, fmt, offset, argc, argv);
|
2019-12-02 10:45:03 +00:00
|
|
|
if (newline) janet_buffer_push_u8(buf, '\n');
|
2019-10-19 15:30:29 +00:00
|
|
|
return janet_wrap_nil();
|
|
|
|
}
|
|
|
|
case JANET_NIL:
|
|
|
|
f = dflt_file;
|
2020-08-09 14:30:58 +00:00
|
|
|
if (f == NULL) janet_panic("cannot print to nil");
|
2019-10-19 15:30:29 +00:00
|
|
|
break;
|
|
|
|
case JANET_ABSTRACT: {
|
|
|
|
void *abstract = janet_unwrap_abstract(x);
|
2020-03-14 15:12:47 +00:00
|
|
|
if (janet_abstract_type(abstract) != &janet_file_type)
|
2019-10-19 15:30:29 +00:00
|
|
|
return janet_wrap_nil();
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iofile = abstract;
|
2019-10-19 15:30:29 +00:00
|
|
|
f = iofile->file;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
JanetBuffer *buf = janet_buffer(10);
|
2020-08-09 14:30:58 +00:00
|
|
|
janet_buffer_format(buf, fmt, offset, argc, argv);
|
2019-12-02 10:45:03 +00:00
|
|
|
if (newline) janet_buffer_push_u8(buf, '\n');
|
2019-10-19 15:51:11 +00:00
|
|
|
if (buf->count) {
|
|
|
|
if (1 != fwrite(buf->data, buf->count, 1, f)) {
|
2020-08-09 14:30:58 +00:00
|
|
|
janet_panicf("could not print %d bytes to file", buf->count);
|
2019-10-19 15:51:11 +00:00
|
|
|
}
|
2019-10-19 15:30:29 +00:00
|
|
|
}
|
|
|
|
/* Clear buffer to make things easier for GC */
|
|
|
|
buf->count = 0;
|
|
|
|
buf->capacity = 0;
|
|
|
|
free(buf->data);
|
|
|
|
buf->data = NULL;
|
|
|
|
return janet_wrap_nil();
|
|
|
|
}
|
|
|
|
|
2020-08-09 14:30:58 +00:00
|
|
|
static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
|
|
|
|
const char *name, FILE *dflt_file) {
|
|
|
|
janet_arity(argc, 1, -1);
|
|
|
|
Janet x = janet_dyn(name);
|
|
|
|
return cfun_io_printf_impl_x(argc, argv, newline, dflt_file, 0, x);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-10-19 15:30:29 +00:00
|
|
|
static Janet cfun_io_printf(int32_t argc, Janet *argv) {
|
2019-12-02 10:45:03 +00:00
|
|
|
return cfun_io_printf_impl(argc, argv, 1, "out", stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_prinf(int32_t argc, Janet *argv) {
|
|
|
|
return cfun_io_printf_impl(argc, argv, 0, "out", stdout);
|
2019-10-19 15:30:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_eprintf(int32_t argc, Janet *argv) {
|
2019-12-02 10:45:03 +00:00
|
|
|
return cfun_io_printf_impl(argc, argv, 1, "err", stderr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_eprinf(int32_t argc, Janet *argv) {
|
|
|
|
return cfun_io_printf_impl(argc, argv, 0, "err", stderr);
|
2019-10-19 15:30:29 +00:00
|
|
|
}
|
|
|
|
|
2020-08-09 14:30:58 +00:00
|
|
|
static Janet cfun_io_xprintf(int32_t argc, Janet *argv) {
|
|
|
|
janet_arity(argc, 2, -1);
|
|
|
|
return cfun_io_printf_impl_x(argc, argv, 1, NULL, 1, argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_xprinf(int32_t argc, Janet *argv) {
|
|
|
|
janet_arity(argc, 2, -1);
|
|
|
|
return cfun_io_printf_impl_x(argc, argv, 0, NULL, 1, argv[0]);
|
|
|
|
}
|
|
|
|
|
2020-02-04 00:14:32 +00:00
|
|
|
static void janet_flusher(const char *name, FILE *dflt_file) {
|
|
|
|
Janet x = janet_dyn(name);
|
|
|
|
switch (janet_type(x)) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case JANET_NIL:
|
|
|
|
fflush(dflt_file);
|
|
|
|
break;
|
|
|
|
case JANET_ABSTRACT: {
|
|
|
|
void *abstract = janet_unwrap_abstract(x);
|
2020-03-14 15:12:47 +00:00
|
|
|
if (janet_abstract_type(abstract) != &janet_file_type) break;
|
|
|
|
JanetFile *iofile = abstract;
|
2020-02-04 00:14:32 +00:00
|
|
|
fflush(iofile->file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_flush(int32_t argc, Janet *argv) {
|
|
|
|
janet_fixarity(argc, 0);
|
|
|
|
(void) argv;
|
|
|
|
janet_flusher("out", stdout);
|
|
|
|
return janet_wrap_nil();
|
|
|
|
}
|
|
|
|
|
|
|
|
static Janet cfun_io_eflush(int32_t argc, Janet *argv) {
|
|
|
|
janet_fixarity(argc, 0);
|
|
|
|
(void) argv;
|
|
|
|
janet_flusher("err", stderr);
|
|
|
|
return janet_wrap_nil();
|
|
|
|
}
|
|
|
|
|
2019-10-19 14:44:27 +00:00
|
|
|
void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...) {
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
Janet x = janet_dyn(name);
|
|
|
|
JanetType xtype = janet_type(x);
|
|
|
|
switch (xtype) {
|
|
|
|
default:
|
2019-10-19 15:30:29 +00:00
|
|
|
/* Other values simply do nothing */
|
|
|
|
break;
|
|
|
|
case JANET_NIL:
|
2019-10-19 14:44:27 +00:00
|
|
|
case JANET_ABSTRACT: {
|
|
|
|
FILE *f = dflt_file;
|
|
|
|
JanetBuffer buffer;
|
|
|
|
int32_t len = 0;
|
|
|
|
while (format[len]) len++;
|
|
|
|
janet_buffer_init(&buffer, len);
|
2020-04-14 12:33:33 +00:00
|
|
|
janet_formatbv(&buffer, format, args);
|
2019-10-19 14:44:27 +00:00
|
|
|
if (xtype == JANET_ABSTRACT) {
|
|
|
|
void *abstract = janet_unwrap_abstract(x);
|
2020-03-14 15:12:47 +00:00
|
|
|
if (janet_abstract_type(abstract) != &janet_file_type)
|
2019-10-19 15:30:29 +00:00
|
|
|
break;
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iofile = abstract;
|
2019-10-19 15:30:29 +00:00
|
|
|
f = iofile->file;
|
2019-10-19 14:44:27 +00:00
|
|
|
}
|
|
|
|
fwrite(buffer.data, buffer.count, 1, f);
|
|
|
|
janet_buffer_deinit(&buffer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case JANET_BUFFER:
|
2020-04-14 12:33:33 +00:00
|
|
|
janet_formatbv(janet_unwrap_buffer(x), format, args);
|
2019-10-19 14:44:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
return;
|
2019-04-16 23:10:01 +00:00
|
|
|
}
|
|
|
|
|
2019-01-24 05:15:58 +00:00
|
|
|
static const JanetReg io_cfuns[] = {
|
2019-04-16 23:10:01 +00:00
|
|
|
{
|
|
|
|
"print", cfun_io_print,
|
|
|
|
JDOC("(print & xs)\n\n"
|
|
|
|
"Print values to the console (standard out). Value are converted "
|
|
|
|
"to strings if they are not already. After printing all values, a "
|
2019-10-19 14:44:27 +00:00
|
|
|
"newline character is printed. Use the value of (dyn :out stdout) to determine "
|
|
|
|
"what to push characters to. Expects (dyn :out stdout) to be either a core/file or "
|
|
|
|
"a buffer. Returns nil.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"prin", cfun_io_prin,
|
|
|
|
JDOC("(prin & xs)\n\n"
|
|
|
|
"Same as print, but does not add trailing newline.")
|
|
|
|
},
|
2019-10-19 15:30:29 +00:00
|
|
|
{
|
|
|
|
"printf", cfun_io_printf,
|
|
|
|
JDOC("(printf fmt & xs)\n\n"
|
2019-12-02 10:45:03 +00:00
|
|
|
"Prints output formatted as if with (string/format fmt ;xs) to (dyn :out stdout) with a trailing newline.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"prinf", cfun_io_prinf,
|
|
|
|
JDOC("(prinf fmt & xs)\n\n"
|
|
|
|
"Like printf but with no trailing newline.")
|
2019-10-19 15:30:29 +00:00
|
|
|
},
|
2019-10-19 14:44:27 +00:00
|
|
|
{
|
|
|
|
"eprin", cfun_io_eprin,
|
|
|
|
JDOC("(eprin & xs)\n\n"
|
|
|
|
"Same as prin, but uses (dyn :err stderr) instead of (dyn :out stdout).")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"eprint", cfun_io_eprint,
|
|
|
|
JDOC("(eprint & xs)\n\n"
|
|
|
|
"Same as print, but uses (dyn :err stderr) instead of (dyn :out stdout).")
|
2019-04-16 23:10:01 +00:00
|
|
|
},
|
2019-10-19 15:30:29 +00:00
|
|
|
{
|
|
|
|
"eprintf", cfun_io_eprintf,
|
|
|
|
JDOC("(eprintf fmt & xs)\n\n"
|
2019-12-02 10:45:03 +00:00
|
|
|
"Prints output formatted as if with (string/format fmt ;xs) to (dyn :err stderr) with a trailing newline.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"eprinf", cfun_io_eprinf,
|
|
|
|
JDOC("(eprinf fmt & xs)\n\n"
|
|
|
|
"Like eprintf but with no trailing newline.")
|
2019-12-29 22:57:34 +00:00
|
|
|
},
|
2020-08-09 14:30:58 +00:00
|
|
|
{
|
|
|
|
"xprint", cfun_io_xprint,
|
|
|
|
JDOC("(xprint to & xs)\n\n"
|
|
|
|
"Print to a file or other value explicitly (no dynamic bindings) with a trailing "
|
|
|
|
"newline character. The value to print "
|
|
|
|
"to is the first argument, and is otherwise the same as print. Returns nil.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"xprin", cfun_io_xprin,
|
|
|
|
JDOC("(xprin to & xs)\n\n"
|
|
|
|
"Print to a file or other value explicitly (no dynamic bindings). The value to print "
|
|
|
|
"to is the first argument, and is otherwise the same as prin. Returns nil.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"xprintf", cfun_io_xprintf,
|
|
|
|
JDOC("(xprint to fmt & xs)\n\n"
|
|
|
|
"Like printf but prints to an explicit file or value to. Returns nil.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"xprinf", cfun_io_xprinf,
|
|
|
|
JDOC("(xprin to fmt & xs)\n\n"
|
|
|
|
"Like prinf but prints to an explicit file or value to. Returns nil.")
|
|
|
|
},
|
2020-02-04 00:14:32 +00:00
|
|
|
{
|
|
|
|
"flush", cfun_io_flush,
|
|
|
|
JDOC("(flush)\n\n"
|
|
|
|
"Flush (dyn :out stdout) if it is a file, otherwise do nothing.")
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"eflush", cfun_io_eflush,
|
|
|
|
JDOC("(eflush)\n\n"
|
|
|
|
"Flush (dyn :err stderr) if it is a file, otherwise do nothing.")
|
|
|
|
},
|
2019-12-31 01:24:40 +00:00
|
|
|
{
|
2019-12-29 22:57:34 +00:00
|
|
|
"file/temp", cfun_io_temp,
|
|
|
|
JDOC("(file/temp)\n\n"
|
|
|
|
"Open an anonymous temporary file that is removed on close."
|
|
|
|
"Raises an error on failure.")
|
2019-10-19 15:30:29 +00:00
|
|
|
},
|
2019-01-06 06:49:56 +00:00
|
|
|
{
|
2019-01-24 05:15:58 +00:00
|
|
|
"file/open", cfun_io_fopen,
|
2019-06-08 14:30:30 +00:00
|
|
|
JDOC("(file/open path &opt mode)\n\n"
|
2019-02-20 01:51:34 +00:00
|
|
|
"Open a file. path is an 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 writing to the file\n"
|
|
|
|
"\ta - append to the file\n"
|
|
|
|
"\tb - open the file in binary mode (rather than text mode)\n"
|
2020-08-27 12:46:00 +00:00
|
|
|
"\t+ - append to the file instead of overwriting it\n"
|
|
|
|
"\tn - error if the file cannot be opened instead of returning nil")
|
2018-11-16 07:09:38 +00:00
|
|
|
},
|
2019-01-06 06:49:56 +00:00
|
|
|
{
|
2019-01-24 05:15:58 +00:00
|
|
|
"file/close", cfun_io_fclose,
|
2019-01-06 06:49:56 +00:00
|
|
|
JDOC("(file/close f)\n\n"
|
2019-02-20 01:51:34 +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 "
|
2019-12-28 02:21:48 +00:00
|
|
|
"other processes read the file. If the file is the result of a file/popen "
|
|
|
|
"call, close waits for and returns the process exit status.")
|
2018-11-16 07:09:38 +00:00
|
|
|
},
|
2019-01-06 06:49:56 +00:00
|
|
|
{
|
2019-01-24 05:15:58 +00:00
|
|
|
"file/read", cfun_io_fread,
|
2019-06-08 14:30:30 +00:00
|
|
|
JDOC("(file/read f what &opt buf)\n\n"
|
2019-02-20 01:51:34 +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-11-16 07:09:38 +00:00
|
|
|
},
|
2019-01-06 06:49:56 +00:00
|
|
|
{
|
2019-01-24 05:15:58 +00:00
|
|
|
"file/write", cfun_io_fwrite,
|
2019-01-06 06:49:56 +00:00
|
|
|
JDOC("(file/write f bytes)\n\n"
|
2019-02-20 01:51:34 +00:00
|
|
|
"Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
|
|
|
|
"file.")
|
2018-11-16 07:09:38 +00:00
|
|
|
},
|
2019-01-06 06:49:56 +00:00
|
|
|
{
|
2019-01-24 05:15:58 +00:00
|
|
|
"file/flush", cfun_io_fflush,
|
2019-01-06 06:49:56 +00:00
|
|
|
JDOC("(file/flush f)\n\n"
|
2019-02-20 01:51:34 +00:00
|
|
|
"Flush any buffered bytes to the file system. In most files, writes are "
|
|
|
|
"buffered for efficiency reasons. Returns the file handle.")
|
2018-11-16 07:09:38 +00:00
|
|
|
},
|
2019-01-06 06:49:56 +00:00
|
|
|
{
|
2019-01-24 05:15:58 +00:00
|
|
|
"file/seek", cfun_io_fseek,
|
2019-06-08 14:30:30 +00:00
|
|
|
JDOC("(file/seek f &opt whence n)\n\n"
|
2019-02-20 01:51:34 +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-11-16 07:09:38 +00:00
|
|
|
},
|
2020-05-09 17:00:01 +00:00
|
|
|
#ifndef JANET_NO_PROCESSES
|
2019-01-06 06:49:56 +00:00
|
|
|
{
|
2019-01-24 05:15:58 +00:00
|
|
|
"file/popen", cfun_io_popen,
|
2019-06-08 14:30:30 +00:00
|
|
|
JDOC("(file/popen path &opt mode)\n\n"
|
2019-02-20 01:51:34 +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-16 07:09:38 +00:00
|
|
|
},
|
2020-05-09 17:00:01 +00:00
|
|
|
#endif
|
2018-11-15 20:45:41 +00:00
|
|
|
{NULL, NULL, NULL}
|
2018-01-19 21:43:19 +00:00
|
|
|
};
|
|
|
|
|
2019-03-02 14:36:34 +00:00
|
|
|
/* C API */
|
|
|
|
|
2020-08-29 22:36:14 +00:00
|
|
|
JanetFile *janet_getjfile(const Janet *argv, int32_t n) {
|
|
|
|
return janet_getabstract(argv, n, &janet_file_type);
|
|
|
|
}
|
|
|
|
|
2019-03-02 14:36:34 +00:00
|
|
|
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = janet_getabstract(argv, n, &janet_file_type);
|
2019-03-02 16:46:31 +00:00
|
|
|
if (NULL != flags) *flags = iof->flags;
|
2019-03-02 14:36:34 +00:00
|
|
|
return iof->file;
|
|
|
|
}
|
|
|
|
|
2019-12-29 14:49:05 +00:00
|
|
|
Janet janet_makefile(FILE *f, int flags) {
|
2020-07-03 21:19:05 +00:00
|
|
|
return janet_wrap_abstract(makef(f, flags));
|
2019-12-29 14:49:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
JanetAbstract janet_checkfile(Janet j) {
|
2020-03-14 15:12:47 +00:00
|
|
|
return janet_checkabstract(j, &janet_file_type);
|
2019-12-29 14:49:05 +00:00
|
|
|
}
|
|
|
|
|
2019-12-30 00:53:35 +00:00
|
|
|
FILE *janet_unwrapfile(Janet j, int *flags) {
|
2020-03-14 15:12:47 +00:00
|
|
|
JanetFile *iof = janet_unwrap_abstract(j);
|
2019-12-29 14:49:05 +00:00
|
|
|
if (NULL != flags) *flags = iof->flags;
|
|
|
|
return iof->file;
|
|
|
|
}
|
|
|
|
|
2018-01-16 01:14:21 +00:00
|
|
|
/* Module entry point */
|
2019-01-06 01:09:03 +00:00
|
|
|
void janet_lib_io(JanetTable *env) {
|
2019-02-08 05:44:30 +00:00
|
|
|
janet_core_cfuns(env, NULL, io_cfuns);
|
2020-07-03 21:19:05 +00:00
|
|
|
janet_register_abstract_type(&janet_file_type);
|
2020-08-29 22:36:14 +00:00
|
|
|
int default_flags = JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE;
|
2018-01-16 01:14:21 +00:00
|
|
|
/* stdout */
|
2019-02-08 05:44:30 +00:00
|
|
|
janet_core_def(env, "stdout",
|
2020-08-29 22:36:14 +00:00
|
|
|
janet_makefile(stdout, JANET_FILE_APPEND | default_flags),
|
2019-02-20 01:51:34 +00:00
|
|
|
JDOC("The standard output file."));
|
2018-01-16 01:14:21 +00:00
|
|
|
/* stderr */
|
2019-02-08 05:44:30 +00:00
|
|
|
janet_core_def(env, "stderr",
|
2020-08-29 22:36:14 +00:00
|
|
|
janet_makefile(stderr, JANET_FILE_APPEND | default_flags),
|
2019-02-20 01:51:34 +00:00
|
|
|
JDOC("The standard error file."));
|
2018-01-16 01:14:21 +00:00
|
|
|
/* stdin */
|
2019-02-08 05:44:30 +00:00
|
|
|
janet_core_def(env, "stdin",
|
2020-08-29 22:36:14 +00:00
|
|
|
janet_makefile(stdin, JANET_FILE_READ | default_flags),
|
2019-02-20 01:51:34 +00:00
|
|
|
JDOC("The standard input file."));
|
2019-02-27 09:54:10 +00:00
|
|
|
|
2018-01-06 16:09:15 +00:00
|
|
|
}
|