summaryrefslogtreecommitdiffstats
path: root/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'stream.c')
-rw-r--r--stream.c1988
1 files changed, 1353 insertions, 635 deletions
diff --git a/stream.c b/stream.c
index 915ed7e5..1e9019fb 100644
--- a/stream.c
+++ b/stream.c
@@ -1,4 +1,4 @@
-/* Copyright 2009-2020
+/* Copyright 2009-2024
* Kaz Kylheku <kaz@kylheku.com>
* Vancouver, Canada
* All rights reserved.
@@ -6,25 +6,27 @@
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
+#define UTF8_DECL_OPENDIR
#include <stdio.h>
#include <string.h>
#include <stddef.h>
@@ -53,12 +55,12 @@
#if HAVE_WINDOWS_H
#include <windows.h>
#endif
-#if HAVE_SOCKETS
-#include <sys/socket.h>
-#endif
#if HAVE_WSPAWN || HAVE_SPAWN
#include <process.h>
#endif
+#if HAVE_ZLIB
+#include <zlib.h>
+#endif
#include "alloca.h"
#include "lib.h"
#include "gc.h"
@@ -71,8 +73,13 @@
#include "eval.h"
#include "regex.h"
#include "txr.h"
-#include "arith.h"
#include "buf.h"
+#if HAVE_ZLIB
+#include "gzio.h"
+#endif
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
/* Adhere to ISO C rules about direction switching on update streams. */
#ifndef __gnu_linux__
@@ -88,9 +95,11 @@ val get_error_s, get_error_str_s, clear_error_s, get_fd_s;
val print_flo_precision_s, print_flo_digits_s, print_flo_format_s;
val pprint_flo_format_s, print_base_s, print_circle_s;
+val print_json_format_s;
val from_start_k, from_current_k, from_end_k;
val real_time_k, name_k, addr_k, fd_k, byte_oriented_k;
+val standard_k;
val format_s;
val stdio_stream_s;
@@ -99,13 +108,18 @@ val stdio_stream_s;
val socket_error_s;
#endif
+struct cobj_class *stream_cls, *stdio_stream_cls;
+
const wchli_t *path_sep_chars = wli("/");
+wchar_t path_var_sep_char = ':';
+
+val top_stderr;
-val shell, shell_arg;
+static val shell, shell_arg;
void strm_base_init(struct strm_base *s)
{
- static struct strm_base init = { indent_off, 60, 10, 0, 0, 0, 0, 0, 0 };
+ static struct strm_base init = { indent_off, 60, 10, 0, 0, 0, 0, 0, nil, 0 };
*s = init;
}
@@ -116,7 +130,8 @@ void strm_base_cleanup(struct strm_base *s)
void strm_base_mark(struct strm_base *s)
{
- (void) s;
+ if (s->close_result)
+ gc_mark(s->close_result);
}
void stream_print_op(val stream, val out, val pretty, struct strm_ctx *ctx)
@@ -140,140 +155,165 @@ void stream_mark_op(val stream)
strm_base_mark(s);
}
-static noreturn void unimpl(val stream, val op)
+static NORETURN void unimpl(val stream, val op)
{
uw_throwf(file_error_s, lit("~a: not supported by stream ~s"),
op, stream, nao);
abort();
}
-static noreturn val unimpl_put_string(val stream, val str)
+static NORETURN val unimpl_put_string(val stream, val str)
{
+ (void) str;
unimpl(stream, lit("put-string"));
}
-static noreturn val unimpl_put_char(val stream, val ch)
+static NORETURN val unimpl_put_char(val stream, val ch)
{
+ (void) ch;
unimpl(stream, lit("put-char"));
}
-static noreturn val unimpl_put_byte(val stream, int byte)
+static NORETURN val unimpl_put_byte(val stream, int byte)
{
+ (void) byte;
unimpl(stream, lit("put-byte"));
}
-static noreturn val unimpl_get_line(val stream)
+static NORETURN val unimpl_get_line(val stream)
{
unimpl(stream, lit("get-line"));
}
-static noreturn val unimpl_get_char(val stream)
+static NORETURN val unimpl_get_char(val stream)
{
unimpl(stream, lit("get-char"));
}
-static noreturn val unimpl_get_byte(val stream)
+static NORETURN val unimpl_get_byte(val stream)
{
unimpl(stream, lit("get-byte"));
}
-static noreturn val unimpl_unget_char(val stream, val ch)
+static NORETURN val unimpl_unget_char(val stream, val ch)
{
+ (void) ch;
unimpl(stream, lit("unget-char"));
}
-static noreturn val unimpl_unget_byte(val stream, int byte)
+static NORETURN val unimpl_unget_byte(val stream, int byte)
{
+ (void) byte;
unimpl(stream, lit("unget-byte"));
}
-static noreturn val unimpl_put_buf(val stream, val buf, cnum pos)
+static NORETURN ucnum unimpl_put_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
+ (void) ptr;
+ (void) len;
+ (void) pos;
unimpl(stream, lit("put-buf"));
}
-static noreturn val unimpl_fill_buf(val stream, val buf, cnum pos)
+static NORETURN ucnum unimpl_fill_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
+ (void) ptr;
+ (void) len;
+ (void) pos;
unimpl(stream, lit("fill-buf"));
}
-static noreturn val unimpl_seek(val stream, val off, enum strm_whence whence)
+static NORETURN val unimpl_seek(val stream, val off, enum strm_whence whence)
{
+ (void) off;
+ (void) whence;
unimpl(stream, lit("seek-stream"));
}
-static noreturn val unimpl_truncate(val stream, val len)
+static NORETURN val unimpl_truncate(val stream, val len)
{
+ (void) len;
unimpl(stream, lit("truncate-stream"));
}
-static noreturn val unimpl_get_fd(val stream)
-{
- unimpl(stream, lit("fileno"));
-}
-
-static noreturn val unimpl_get_sock_family(val stream)
+static NORETURN val unimpl_get_sock_family(val stream)
{
unimpl(stream, lit("sock-family"));
}
-static noreturn val unimpl_get_sock_type(val stream)
+static NORETURN val unimpl_get_sock_type(val stream)
{
unimpl(stream, lit("sock-type"));
}
-static noreturn val unimpl_get_sock_peer(val stream)
+static NORETURN val unimpl_get_sock_peer(val stream)
{
unimpl(stream, lit("sock-peer"));
}
-static noreturn val unimpl_set_sock_peer(val stream, val peer)
+static NORETURN val unimpl_set_sock_peer(val stream, val peer)
{
+ (void) peer;
unimpl(stream, lit("sock-set-peer"));
}
static val null_put_string(val stream, val str)
{
+ (void) stream;
+ (void) str;
return nil;
}
static val null_put_char(val stream, val ch)
{
+ (void) stream;
+ (void) ch;
return nil;
}
static val null_put_byte(val stream, int byte)
{
+ (void) stream;
+ (void) byte;
return nil;
}
static val null_get_line(val stream)
{
+ (void) stream;
return nil;
}
static val null_get_char(val stream)
{
+ (void) stream;
return nil;
}
static val null_get_byte(val stream)
{
+ (void) stream;
return nil;
}
static val null_close(val stream, val throw_on_error)
{
+ (void) stream;
+ (void) throw_on_error;
return nil;
}
static val null_flush(val stream)
{
+ (void) stream;
return nil;
}
static val null_seek(val stream, val off, enum strm_whence whence)
{
+ (void) stream;
+ (void) off;
+ (void) whence;
return nil;
}
@@ -289,38 +329,43 @@ static val null_get_prop(val stream, val ind)
static val null_set_prop(val stream, val ind, val value)
{
+ (void) stream;
+ (void) ind;
+ (void) value;
return nil;
}
static val null_get_error(val stream)
{
+ (void) stream;
return nil;
}
static val null_get_error_str(val stream)
{
+ (void) stream;
return nil;
}
static val null_clear_error(val stream)
{
+ (void) stream;
return nil;
}
static val null_get_fd(val stream)
{
+ (void) stream;
return nil;
}
-static val generic_put_buf(val stream, val buf, cnum pos)
+static ucnum generic_put_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
- val self = lit("put-buf");
struct strm_ops *ops = coerce(struct strm_ops *, stream->co.ops);
- cnum len = c_num(length_buf(buf)), i;
- mem_t *ptr = buf_get(buf, self);
+ ucnum i;
if (pos >= len)
- return num(len);
+ return len;
for (i = pos; i < len; i++)
ops->put_byte(stream, *ptr++);
@@ -328,27 +373,26 @@ static val generic_put_buf(val stream, val buf, cnum pos)
if (i > len)
i = len;
- return num(i);
+ return i;
}
-static val generic_fill_buf(val stream, val buf, cnum pos)
+static ucnum generic_fill_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
val self = lit("fill-buf");
struct strm_ops *ops = coerce(struct strm_ops *, stream->co.ops);
- cnum len = c_num(length_buf(buf)), i;
- mem_t *ptr = buf_get(buf, self);
+ ucnum i;
for (i = pos; i < len; i++) {
val byte = ops->get_byte(stream);
if (!byte)
break;
- *ptr++ = c_num(byte);
+ *ptr++ = c_num(byte, self);
}
if (i > len)
i = len;
- return num(i);
+ return i;
}
void fill_stream_ops(struct strm_ops *ops)
@@ -392,7 +436,7 @@ void fill_stream_ops(struct strm_ops *ops)
if (!ops->clear_error)
ops->clear_error = null_clear_error;
if (!ops->get_fd)
- ops->get_fd = unimpl_get_fd;
+ ops->get_fd = null_get_fd;
if (!ops->get_sock_family)
ops->get_sock_family = unimpl_get_sock_family;
if (!ops->get_sock_type)
@@ -403,6 +447,45 @@ void fill_stream_ops(struct strm_ops *ops)
ops->set_sock_peer = unimpl_set_sock_peer;
}
+struct dev_null {
+ struct strm_base a;
+ int fd;
+};
+
+static val dev_null_close(val stream, val throw_on_error)
+{
+ struct dev_null *n = coerce(struct dev_null *, stream->co.handle);
+ (void) throw_on_error;
+ if (n->fd != -1) {
+ close(n->fd);
+ n->fd = -1;
+ return t;
+ }
+ return nil;
+}
+
+static val dev_null_get_fd(val stream)
+{
+ struct dev_null *n = coerce(struct dev_null *, stream->co.handle);
+ if (n->fd == -1 && (n->fd = open("/dev/null", O_RDWR)) == -1) {
+ int eno = errno;
+ uw_ethrowf(errno_to_file_error(eno), lit("error opening /dev/null: ~d/~s"),
+ num(eno), errno_to_str(eno), nao);
+ }
+ return num(n->fd);
+}
+
+static val dev_null_get_prop(val stream, val ind)
+{
+ if (ind == name_k)
+ return null_get_prop(stream, ind);
+
+ if (ind == fd_k)
+ return dev_null_get_fd(stream);
+
+ return nil;
+}
+
static struct strm_ops null_ops =
strm_ops_init(cobj_ops_init(eq,
stream_print_op,
@@ -414,16 +497,17 @@ static struct strm_ops null_ops =
null_get_char, null_get_byte,
unimpl_unget_char, unimpl_unget_byte,
unimpl_put_buf, unimpl_fill_buf,
- null_close, null_flush, null_seek, unimpl_truncate,
- null_get_prop, null_set_prop,
+ dev_null_close, null_flush, null_seek, unimpl_truncate,
+ dev_null_get_prop, null_set_prop,
null_get_error, null_get_error_str, null_clear_error,
- null_get_fd);
+ dev_null_get_fd);
val make_null_stream(void)
{
- struct strm_base *s = coerce(struct strm_base *, chk_malloc(sizeof *s));
- strm_base_init(s);
- return cobj(coerce(mem_t *, s), stream_s, &null_ops.cobj_ops);
+ struct dev_null *n = coerce(struct dev_null *, chk_malloc(sizeof *n));
+ strm_base_init(&n->a);
+ n->fd = -1;
+ return cobj(coerce(mem_t *, n), stream_cls, &null_ops.cobj_ops);
}
#if CONFIG_STDIO_STRICT
@@ -440,8 +524,6 @@ struct stdio_handle {
char *buf;
#if HAVE_FORK_STUFF
pid_t pid;
-#else
- int pid;
#endif
val mode; /* used by tail */
unsigned is_rotated : 8; /* used by tail */
@@ -501,10 +583,9 @@ static void stdio_stream_mark(val stream)
val errno_to_string(val err)
{
- if (err == zero)
- return lit("unspecified error");
- else if (is_num(err))
- return string_utf8(strerror(c_num(err)));
+ val self = lit("get-error-str");
+ if (is_num(err))
+ return errno_to_str(c_num(err, self));
else if (!err)
return lit("no error");
else if (err == t)
@@ -519,14 +600,15 @@ static val stdio_maybe_read_error(val stream)
if (h->f == 0)
uw_throwf(file_error_s, lit("error reading ~s: file closed"), stream, nao);
if (ferror(h->f)) {
- val err = num(errno);
+ int eno = errno;
+ val err = num(eno);
h->err = err;
#ifdef EAGAIN
- if (errno == EAGAIN)
- uw_throwf(timeout_error_s, lit("timed out reading ~s"), stream, nao);
+ if (eno == EAGAIN)
+ uw_ethrowf(timeout_error_s, lit("timed out reading ~s"), stream, nao);
#endif
- uw_throwf(file_error_s, lit("error reading ~s: ~d/~s"),
- stream, err, errno_to_string(err), nao);
+ uw_ethrowf(file_error_s, lit("error reading ~s: ~d/~s"),
+ stream, err, errno_to_string(err), nao);
}
if (feof(h->f))
h->err = t;
@@ -538,28 +620,21 @@ static val stdio_maybe_error(val stream, val action)
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
val err = num(errno);
if (h->f == 0)
- uw_throwf(file_error_s, lit("error ~a ~s: file closed"), stream, action, nao);
+ uw_ethrowf(file_error_s, lit("error ~a ~s: file closed"), action, stream, nao);
h->err = err;
#ifdef EAGAIN
if (errno == EAGAIN)
- uw_throwf(timeout_error_s, lit("timed out on ~s"), stream, nao);
+ uw_ethrowf(timeout_error_s, lit("timed out on ~s"), stream, nao);
#endif
- uw_throwf(file_error_s, lit("error ~a ~s: ~d/~s"),
- stream, action, err, errno_to_string(err), nao);
+ uw_ethrowf(file_error_s, lit("error ~a ~s: ~d/~s"),
+ action, stream, err, errno_to_string(err), nao);
}
static int se_putc(int ch, FILE *f)
{
int ret;
sig_save_enable;
-#ifdef __CYGWIN__
- {
- char out[2] = { ch, 0 };
- ret = fputs(out, f) == EOF ? EOF : ch;
- }
-#else
ret = putc(ch, f);
-#endif
sig_restore_enable;
return ret;
}
@@ -619,12 +694,13 @@ static int stdio_get_char_callback(mem_t *f)
static val stdio_put_string(val stream, val str)
{
+ val self = lit("put-string");
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
errno = 0;
if (h->f != 0) {
- const wchar_t *s = c_str(str);
+ const wchar_t *s = c_str(str, self);
stdio_switch(h, stdio_write);
@@ -697,8 +773,6 @@ static val stdio_get_prop(val stream, val ind)
return h->is_real_time ? t : nil;
} else if (ind == name_k) {
return h->descr;
- } else if (ind == fd_k) {
- return h->f ? num(fileno(h->f)) : nil;
} else if (ind == byte_oriented_k) {
return h->is_byte_oriented ? t : nil;
}
@@ -715,6 +789,9 @@ static val stdio_set_prop(val stream, val ind, val prop)
} else if (ind == byte_oriented_k) {
h->is_byte_oriented = prop ? 1 : 0;
return t;
+ } else if (ind == name_k) {
+ h->descr = prop;
+ return t;
}
return nil;
}
@@ -751,7 +828,7 @@ static val stdio_get_fd(val stream)
{
val self = lit("stream-fd");
struct stdio_handle *h = coerce(struct stdio_handle *,
- cobj_handle(self, stream, stdio_stream_s));
+ cobj_handle(self, stream, stdio_stream_cls));
return h->f ? num(fileno(h->f)) : nil;
}
@@ -768,7 +845,7 @@ val generic_get_line(val stream)
for (;;) {
val chr = ops->get_char(stream);
- wint_t ch = chr ? c_chr(chr) : WEOF;
+ wint_t ch = chr ? convert(wint_t, c_chr(chr)) : WEOF;
if (ch == WEOF && buf == 0)
break;
@@ -863,57 +940,53 @@ static val stdio_unget_byte(val stream, int byte)
: stdio_maybe_error(stream, lit("writing"));
}
-static val stdio_put_buf(val stream, val buf, cnum pos)
+static ucnum stdio_put_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
val self = lit("put-buf");
- ucnum len = c_unum(length_buf(buf));
- mem_t *ptr = buf_get(buf, self);
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
if (convert(size_t, len) != len || len > INT_PTR_MAX)
uw_throwf(error_s, lit("~a: buffer too large"), self, nao);
- if (convert(ucnum, pos) >= len)
- return num(len);
+ if (pos >= len)
+ return len;
errno = 0;
if (h->f != 0) {
cnum nwrit = fwrite(ptr + pos, 1, len - pos, h->f);
if (nwrit > 0)
- return num(pos + nwrit);
+ return pos + nwrit;
}
stdio_maybe_error(stream, lit("writing"));
- return zero;
+ return 0;
}
-static val stdio_fill_buf(val stream, val buf, cnum pos)
+static ucnum stdio_fill_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
val self = lit("fill-buf");
- ucnum len = c_unum(length_buf(buf));
- mem_t *ptr = buf_get(buf, self);
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
if (convert(size_t, len) != len || len > INT_PTR_MAX)
uw_throwf(error_s, lit("~a: buffer too large"), self, nao);
- if (convert(ucnum, pos) >= len)
- return num(len);
+ if (pos >= len)
+ return len;
errno = 0;
if (h->f != 0) {
cnum nread = fread(ptr + pos, 1, len - pos, h->f);
if (nread > 0)
- return num(pos + nread);
+ return pos + nread;
}
stdio_maybe_read_error(stream);
- return num(pos);
+ return pos;
}
static val stdio_close(val stream, val throw_on_error)
{
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
- if (h->f != 0 && h->f != stdin && h->f != stdout) {
+ if (h->f != 0 && h->f != stdin && h->f != stdout && h->f != stderr) {
int result = fclose(h->f);
h->f = 0;
- if (result == EOF && throw_on_error) {
+ if (result == EOF && default_null_arg(throw_on_error)) {
h->err = num(errno);
- uw_throwf(file_error_s, lit("error closing ~s: ~d/~s"),
- stream, num(errno), string_utf8(strerror(errno)), nao);
+ uw_ethrowf(file_error_s, lit("error closing ~s: ~d/~s"),
+ stream, num(errno), errno_to_str(errno), nao);
}
return result != EOF ? t : nil;
}
@@ -923,8 +996,9 @@ static val stdio_close(val stream, val throw_on_error)
#if HAVE_FTRUNCATE || HAVE_CHSIZE
static val stdio_truncate(val stream, val len)
{
+ val self = lit("truncate-stream");
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
- cnum l = c_num(len);
+ cnum l = c_num(len, self);
#if HAVE_FTRUNCATE
typedef off_t trunc_off_t;
int (*truncfun)(int, off_t) = ftruncate;
@@ -1047,25 +1121,49 @@ static struct strm_ops stdio_ops =
static struct strm_ops stdio_sock_ops;
#endif
+#if HAVE_FCNTL
+int w_open_mode(const wchar_t *wname, const struct stdio_mode m)
+{
+ char *name = utf8_dup_to(wname);
+ size_t nsiz = strlen(name) + 1;
+ int flags = (if3(m.read && m.write, O_RDWR, 0) |
+ if3(m.read && !m.write, O_RDONLY, 0) |
+ if3(!m.read && m.write, O_WRONLY, 0) |
+ if3(m.create, if3(!m.notrunc, O_TRUNC, 0) | O_CREAT, 0) |
+ if3(m.append, O_APPEND, 0) |
+ if3(m.excl, O_EXCL, 0) |
+#if O_TMPFILE
+ if3(m.tmpfile, O_TMPFILE, 0) |
+#endif
+ if3(m.nonblock, O_NONBLOCK, 0));
+ char *stkname = coerce(char *, alloca(nsiz));
+ int fd;
+
+ memcpy(stkname, name, nsiz);
+ free(name);
+
+ sig_save_enable;
+ fd = open(stkname, flags, 0666);
+ sig_restore_enable;
+
+ return fd;
+}
+#endif
+
static FILE *w_fopen_mode(const wchar_t *wname, const wchar_t *mode,
const struct stdio_mode m)
{
#if HAVE_FCNTL
- if (m.notrunc) {
- char *name = utf8_dup_to(wname);
- int flags = (m.read ? O_RDWR : O_WRONLY) | O_CREAT;
- int fd = open(name, flags, 0777);
- free(name);
- if (fd < 0)
- return NULL;
- return (fd < 0) ? NULL : w_fdopen(fd, mode);
- }
+ int fd = w_open_mode(wname, m);
+ return (fd < 0) ? NULL : w_fdopen(fd, mode);
#else
- if (m.notrunc)
+ /* TODO: detect if fopen supports "x" in mode */
+ if (m.notrunc || m.excl || m.nonblock)
uw_throwf(file_error_s,
- lit("open-file: \"m\" mode not supported on this system"), nao);
-#endif
+ lit("open-file: specified mode not supported on this system"),
+ nao);
return w_fopen(wname, mode);
+#endif
}
@@ -1082,6 +1180,7 @@ static void tail_calc(unsigned long *state, int *usec, int *mod)
static void tail_strategy(val stream, unsigned long *state)
{
+ val self = lit("open-tail");
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
int usec = 0, mod = 0;
val mode = nil;
@@ -1117,11 +1216,11 @@ static void tail_strategy(val stream, unsigned long *state)
FILE *newf;
if (!mode)
- mode = normalize_mode(&m, h->mode, m_r);
+ mode = normalize_mode(&m, h->mode, m_r, self);
/* Try to open the file.
*/
- if (!(newf = w_fopen_mode(c_str(h->descr), c_str(mode), m))) {
+ if (!(newf = w_fopen_mode(c_str(h->descr, self), c_str(mode, self), m))) {
/* If already have the file open previously, and the name
* does not open any more, then the file has rotated.
* Have the caller try to read the last bit of data
@@ -1267,61 +1366,45 @@ static int pipevp_close(FILE *f, pid_t pid)
sig_restore_enable;
return status;
}
-#endif
-static int se_pclose(FILE *f)
+val pipe_close_status_helper(val stream, val throw_on_error,
+ int status, val self)
{
- int ret;
- sig_save_enable;
- ret = pclose(f);
- sig_restore_enable;
- return ret;
+ if (status < 0) {
+ if (throw_on_error)
+ uw_ethrowf(process_error_s,
+ lit("~a: stream ~s: unable to obtain status of process: ~d/~s"),
+ self, stream, num(errno), errno_to_str(errno), nao);
+ return nil;
+ } else {
+#if HAVE_SYS_WAIT
+ if (WIFEXITED(status)) {
+ int exitstatus = WEXITSTATUS(status);
+ return num(exitstatus);
+ } else if (default_null_arg(throw_on_error)) {
+ if (WIFSIGNALED(status)) {
+ int termsig = WTERMSIG(status);
+ uw_throwf(process_error_s, lit("~a: stream ~s: process terminated by signal ~a"),
+ self, stream, num(termsig), nao);
+ }
+ }
+#endif
+ return status == 0 ? zero : nil;
+ }
}
static val pipe_close(val stream, val throw_on_error)
{
+ val self = lit("close-stream");
struct stdio_handle *h = coerce(struct stdio_handle *, stream->co.handle);
if (h->f != 0) {
-#if HAVE_FORK_STUFF
- int status = h->pid != 0 ? pipevp_close(h->f, h->pid) : se_pclose(h->f);
-#else
- int status = se_pclose(h->f);
-#endif
+ int status = pipevp_close(h->f, h->pid);
h->f = 0;
- if (status < 0) {
- if (throw_on_error)
- uw_throwf(process_error_s,
- lit("unable to obtain status of command ~s: ~d/~s"),
- stream, num(errno), string_utf8(strerror(errno)), nao);
- } else {
-#if HAVE_SYS_WAIT
- if (throw_on_error) {
- if (WIFSIGNALED(status)) {
- int termsig = WTERMSIG(status);
- uw_throwf(process_error_s, lit("pipe ~s terminated by signal ~a"),
- stream, num(termsig), nao);
-#ifndef WIFCONTINUED
-#define WIFCONTINUED(X) 0
-#endif
- } else if (WIFSTOPPED(status) || WIFCONTINUED(status)) {
- uw_throwf(process_error_s,
- lit("processes of closed pipe ~s still running"),
- stream, nao);
- }
- }
- if (WIFEXITED(status)) {
- int exitstatus = WEXITSTATUS(status);
- return num(exitstatus);
- }
-#else
- if (status != 0 && throw_on_error)
- uw_throwf(process_error_s, lit("closing pipe ~s failed"), stream, nao);
-#endif
- return status == 0 ? zero : nil;
- }
+ return pipe_close_status_helper(stream, throw_on_error, status, self);
}
+
return nil;
}
@@ -1352,11 +1435,13 @@ static struct strm_ops pipe_ops =
stdio_get_error_str,
stdio_clear_error,
stdio_get_fd);
+#endif
-static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
+static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl,
+ val self)
{
struct stdio_mode m = stdio_mode_init_blank;
- const wchar_t *ms = c_str(default_arg(mode_str, lit("")));
+ const wchar_t *ms = c_str(default_arg_strict(mode_str, null_string), self);
int nredir = 0;
switch (*ms) {
@@ -1372,7 +1457,9 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
case 'a':
ms++;
m.write = 1;
+ m.create = 1;
m.append = 1;
+ m.notrunc = 1;
break;
case 'm':
ms++;
@@ -1380,15 +1467,24 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
m.create = 1;
m.notrunc = 1;
break;
+ case 'T':
+ ms++;
+#if O_TMPFILE
+ m.read = 1;
+ m.write = 1;
+ m.tmpfile = 1;
+#else
+ m.malformed = 1;
+#endif
+ break;
default:
break;
}
if (*ms == '+') {
ms++;
- if (m.read)
- m.write = 1;
m.read = 1;
+ m.write = 1;
}
if (!m.read && !m.write)
@@ -1399,6 +1495,14 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
case 'b':
m.binary = 1;
break;
+ case 'x':
+ /* Ensure only "w" and "w+" can have the "x" option. */
+ if (!m.write || !m.create || m.notrunc) {
+ m.malformed = 1;
+ return m;
+ }
+ m.excl = 1;
+ break;
case 'i':
m.interactive = 1;
break;
@@ -1416,6 +1520,9 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
}
m.unbuf = 1;
break;
+ case 'n':
+ m.nonblock = 1;
+ break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (m.unbuf) {
@@ -1431,13 +1538,13 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
}
if (ms[1] != '(') {
- if (!isdigit((unsigned char) ms[1]) || !ms[2]) {
+ if (!isdigit(convert(unsigned char, ms[1])) || !ms[2]) {
m.malformed = 1;
return m;
}
m.redir[nredir][0] = ms[1] - '0';
- if (isdigit((unsigned char) ms[2])) {
+ if (isdigit(convert(unsigned char, ms[2]))) {
m.redir[nredir][1] = ms[2] - '0';
} else switch (ms[2]) {
case 'n': case 'x':
@@ -1473,6 +1580,13 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
nredir++;
break;
}
+ case 'z':
+ m.gzip = 1;
+ if (isdigit(convert(unsigned char, ms[1]))) {
+ m.gzlevel = *++ms - '0';
+ break;
+ }
+ break;
default:
m.malformed = 1;
return m;
@@ -1485,11 +1599,12 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl)
return m;
}
-struct stdio_mode parse_mode(val mode_str, struct stdio_mode m_dfl)
+struct stdio_mode parse_mode(val mode_str, struct stdio_mode m_dfl, val self)
{
- struct stdio_mode m = do_parse_mode(mode_str, m_dfl);
+ struct stdio_mode m = do_parse_mode(mode_str, m_dfl, self);
if (m.malformed)
- uw_throwf(file_error_s, lit("invalid mode string ~s"), mode_str, nao);
+ uw_throwf(file_error_s, lit("~a: invalid mode string ~s"), self,
+ mode_str, nao);
return m;
}
@@ -1514,11 +1629,14 @@ static val format_mode(const struct stdio_mode m)
*ptr++ = '+';
}
- if (m.binary)
+ if (m.binary && !m.gzip)
*ptr++ = 'b';
+ if (m.gzip && m.gzlevel)
+ *ptr++ = '0' + m.gzlevel;
+
#ifdef __CYGWIN__
- if (!m.binary && (opt_compat == 144 || opt_compat == 145))
+ if (!m.gzip && !m.binary && (opt_compat == 144 || opt_compat == 145))
*ptr++ = 't';
#endif
@@ -1526,25 +1644,29 @@ static val format_mode(const struct stdio_mode m)
return string(buf);
}
-val normalize_mode(struct stdio_mode *m, val mode_str, struct stdio_mode m_dfl)
+val normalize_mode(struct stdio_mode *m, val mode_str, struct stdio_mode m_dfl,
+ val self)
{
- *m = do_parse_mode(mode_str, m_dfl);
+ *m = do_parse_mode(mode_str, m_dfl, self);
if (m->malformed)
- uw_throwf(file_error_s, lit("invalid file open mode ~s"), mode_str, nao);
+ uw_throwf(file_error_s, lit("~a: invalid file open mode ~s"),
+ self, mode_str, nao);
return format_mode(*m);
}
-val normalize_mode_no_bin(struct stdio_mode *m, val mode_str, struct stdio_mode m_dfl)
+val normalize_mode_no_bin(struct stdio_mode *m, val mode_str,
+ struct stdio_mode m_dfl, val self)
{
#ifdef __CYGWIN__
- return normalize_mode(m, mode_str, m_dfl);
+ return normalize_mode(m, mode_str, m_dfl, self);
#else
- *m = do_parse_mode(mode_str, m_dfl);
+ *m = do_parse_mode(mode_str, m_dfl, self);
if (m->malformed)
- uw_throwf(file_error_s, lit("invalid file open mode ~s"), mode_str, nao);
+ uw_throwf(file_error_s, lit("~a: invalid file open mode ~s"),
+ self, mode_str, nao);
m->binary = 0;
@@ -1585,7 +1707,7 @@ val set_mode_props(const struct stdio_mode m, val stream)
static val make_stdio_stream_common(FILE *f, val descr, struct cobj_ops *ops)
{
struct stdio_handle *h = coerce(struct stdio_handle *, chk_malloc(sizeof *h));
- val stream = cobj(coerce(mem_t *, h), stdio_stream_s, ops);
+ val stream = cobj(coerce(mem_t *, h), stdio_stream_cls, ops);
strm_base_init(&h->a);
h->f = f;
h->descr = descr;
@@ -1627,11 +1749,6 @@ val make_tail_stream(FILE *f, val descr)
return stream;
}
-val make_pipe_stream(FILE *f, val descr)
-{
- return make_stdio_stream_common(f, descr, &pipe_ops.cobj_ops);
-}
-
#if HAVE_SOCKETS
val make_sock_stream(FILE *f, val family, val type)
{
@@ -1647,7 +1764,7 @@ val stream_fd(val stream)
{
val self = lit("fileno");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_fd(stream);
}
@@ -1656,7 +1773,7 @@ val sock_family(val stream)
{
val self = lit("sock-family");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_sock_family(stream);
}
@@ -1664,7 +1781,7 @@ val sock_type(val stream)
{
val self = lit("sock-type");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_sock_type(stream);
}
@@ -1672,7 +1789,7 @@ val sock_peer(val stream)
{
val self = lit("sock-peer");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_sock_peer(stream);
}
@@ -1680,7 +1797,7 @@ val sock_set_peer(val stream, val peer)
{
val self = lit("sock-set-peer");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->set_sock_peer(stream, peer);
}
#endif
@@ -1739,10 +1856,12 @@ static val dir_get_line(val stream)
static val dir_close(val stream, val throw_on_error)
{
struct dir_handle *h = coerce(struct dir_handle *, stream->co.handle);
+ (void) throw_on_error;
if (h->d != 0) {
closedir(coerce(DIR *, h->d));
h->d = 0;
+ return t;
}
return nil;
@@ -1791,7 +1910,7 @@ static val make_dir_stream(DIR *dir)
strm_base_init(&h->a);
h->d = dir;
h->err = nil;
- return cobj(coerce(mem_t *, h), stream_s, &dir_ops.cobj_ops);
+ return cobj(coerce(mem_t *, h), stream_cls, &dir_ops.cobj_ops);
}
struct string_in {
@@ -1914,7 +2033,7 @@ val make_string_input_stream(val string)
strm_base_init(&s->a);
s->string = string;
s->pos = zero;
- return cobj(coerce(mem_t *, s), stream_s, &string_in_ops.cobj_ops);
+ return cobj(coerce(mem_t *, s), stream_cls, &string_in_ops.cobj_ops);
}
struct byte_input {
@@ -1984,15 +2103,16 @@ static struct strm_ops byte_in_ops =
val make_string_byte_input_stream(val string)
{
- type_assert (stringp(string), (lit("~a is not a string"), string, nao));
+ val self = lit("make-string-byte-input-stream");
+ type_assert (stringp(string), (lit("~a: ~s is not a string"), self, string, nao));
{
- const wchar_t *wstring = c_str(string);
+ const wchar_t *wstring = c_str(string, self);
struct byte_input *bi = coerce(struct byte_input *, chk_malloc(sizeof *bi));
strm_base_init(&bi->a);
bi->buf = utf8_dup_to_buf(wstring, &bi->size, 0);
bi->index = 0;
- return cobj(coerce(mem_t *, bi), stream_s, &byte_in_ops.cobj_ops);
+ return cobj(coerce(mem_t *, bi), stream_cls, &byte_in_ops.cobj_ops);
}
}
@@ -2123,12 +2243,13 @@ static struct strm_ops strlist_in_ops =
val make_strlist_input_stream(val list)
{
- struct strlist_in *s = coerce(struct strlist_in *, chk_malloc(sizeof *s));
+ struct strlist_in *s = coerce(struct strlist_in *, chk_calloc(sizeof *s, 1));
+ val stream = cobj(coerce(mem_t *, s), stream_cls, &strlist_in_ops.cobj_ops);
strm_base_init(&s->a);
s->string = car(list);
s->pos = zero;
s->list = cdr(list);
- return cobj(coerce(mem_t *, s), stream_s, &strlist_in_ops.cobj_ops);
+ return stream;
}
struct string_out {
@@ -2189,6 +2310,7 @@ static val string_out_byte_flush(struct string_out *so, val stream)
static val string_out_put_string(val stream, val str)
{
+ val self = lit("put-string");
struct string_out *so = coerce(struct string_out *, stream->co.handle);
if (so->buf == 0)
@@ -2198,8 +2320,8 @@ static val string_out_put_string(val stream, val str)
string_out_byte_flush(so, stream);
{
- const wchar_t *s = c_str(str);
- size_t len = c_num(length_str(str));
+ const wchar_t *s = c_str(str, self);
+ size_t len = c_num(length_str(str), self);
size_t old_size = so->size;
size_t required_size = len + so->fill + 1;
@@ -2220,7 +2342,7 @@ static val string_out_put_string(val stream, val str)
so->fill += len;
return t;
oflow:
- uw_throw(error_s, lit("string output stream overflow"));
+ uw_throwf(error_s, lit("~a: string output stream overflow"), self, nao);
}
}
@@ -2272,17 +2394,18 @@ val make_string_output_stream(void)
so->buf[0] = 0;
utf8_decoder_init(&so->ud);
so->head = so->tail = 0;
- return cobj(coerce(mem_t *, so), stream_s, &string_out_ops.cobj_ops);
+ return cobj(coerce(mem_t *, so), stream_cls, &string_out_ops.cobj_ops);
}
val get_string_from_stream(val stream)
{
val self = lit("get-string-from-stream");
struct string_out *so = coerce(struct string_out *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
if (stream->co.ops == &string_out_ops.cobj_ops) {
val out = nil;
+ wchar_t *buf;
if (!so->buf)
return out;
@@ -2291,14 +2414,14 @@ val get_string_from_stream(val stream)
out = string_out_byte_flush(so, stream);
/* Trim to actual size */
- so->buf = coerce(wchar_t *, chk_realloc(coerce(mem_t *, so->buf),
- (so->fill + 1) * sizeof *so->buf));
- out = string_own(so->buf);
+ buf = coerce(wchar_t *, chk_realloc(coerce(mem_t *, so->buf),
+ (so->fill + 1) * sizeof *so->buf));
so->buf = 0;
+ out = string_own(buf);
return out;
} else {
type_assert (stream->co.ops == &string_in_ops.cobj_ops,
- (lit("~a is not a string stream"), stream, nao));
+ (lit("~a: ~s is not a string stream"), self, stream, nao));
{
struct string_in *si = coerce(struct string_in *, stream->co.handle);
return si->string;
@@ -2388,7 +2511,7 @@ val make_strlist_output_stream(void)
strm_base_init(&s->a);
s->lines = nil;
s->strstream = nil;
- stream = cobj(coerce(mem_t *, s), stream_s, &strlist_out_ops.cobj_ops);
+ stream = cobj(coerce(mem_t *, s), stream_cls, &strlist_out_ops.cobj_ops);
s->strstream = strstream;
return stream;
}
@@ -2397,7 +2520,7 @@ val get_list_from_stream(val stream)
{
val self = lit("get-list-from-stream");
struct strlist_out *s = coerce(struct strlist_out *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
if (stream->co.ops == &strlist_out_ops.cobj_ops) {
val stray = get_string_from_stream(s->strstream);
@@ -2407,7 +2530,7 @@ val get_list_from_stream(val stream)
return nreverse(lines);
}
- type_mismatch(lit("~s is not a string list stream"), stream);
+ type_mismatch(lit("~a: ~s is not a string list stream"), self, stream, nao);
}
struct cat_strm {
@@ -2578,12 +2701,12 @@ val make_catenated_stream(val stream_list)
val catstrm = nil;
strm_base_init(&s->a);
s->streams = nil;
- catstrm = cobj(coerce(mem_t *, s), stream_s, &cat_stream_ops.cobj_ops);
+ catstrm = cobj(coerce(mem_t *, s), stream_cls, &cat_stream_ops.cobj_ops);
s->streams = stream_list;
return catstrm;
}
-val make_catenated_stream_v(struct args *streams)
+val make_catenated_stream_v(varg streams)
{
return make_catenated_stream(args_get_list(streams));
}
@@ -2595,10 +2718,12 @@ val catenated_stream_p(val obj)
val catenated_stream_push(val new_stream, val cat_stream)
{
+ val self = lit("catenated-stream-push");
+
type_assert (streamp(new_stream),
- (lit("~a is not a stream"), new_stream, nao));
+ (lit("~a: ~s is not a stream"), self, new_stream, nao));
type_assert (catenated_stream_p(cat_stream),
- (lit("~a is not a stream"), cat_stream, nao));
+ (lit("~a: ~s is not a stream"), self, cat_stream, nao));
{
struct cat_strm *s = coerce(struct cat_strm *, cat_stream->co.handle);
@@ -2661,16 +2786,16 @@ static val delegate_unget_byte(val stream, int byte)
return s->target_ops->unget_byte(s->target_stream, byte);
}
-static val delegate_put_buf(val stream, val buf, cnum pos)
+static ucnum delegate_put_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
struct delegate_base *s = coerce(struct delegate_base *, stream->co.handle);
- return s->target_ops->put_buf(s->target_stream, buf, pos);
+ return s->target_ops->put_buf(s->target_stream, ptr, len, pos);
}
-static val delegate_fill_buf(val stream, val buf, cnum pos)
+static ucnum delegate_fill_buf(val stream, mem_t *ptr, ucnum len, ucnum pos)
{
struct delegate_base *s = coerce(struct delegate_base *, stream->co.handle);
- return s->target_ops->fill_buf(s->target_stream, buf, pos);
+ return s->target_ops->fill_buf(s->target_stream, ptr, len, pos);
}
static val delegate_close(val stream, val throw_on_error)
@@ -2765,7 +2890,7 @@ static val make_delegate_stream(val self, val orig_stream, size_t handle_size,
struct cobj_ops *ops)
{
struct strm_ops *orig_ops = coerce(struct strm_ops *,
- cobj_ops(self, orig_stream, stream_s));
+ cobj_ops(self, orig_stream, stream_cls));
struct delegate_base *db = coerce(struct delegate_base *,
chk_calloc(1, handle_size));
val delegate_stream;
@@ -2774,7 +2899,7 @@ static val make_delegate_stream(val self, val orig_stream, size_t handle_size,
db->target_stream = nil;
db->target_ops = orig_ops;
- delegate_stream = cobj(coerce(mem_t *, db), stream_s, ops);
+ delegate_stream = cobj(coerce(mem_t *, db), stream_cls, ops);
db->target_stream = orig_stream;
@@ -2826,7 +2951,8 @@ static struct strm_ops record_adapter_ops =
val record_adapter(val regex, val stream, val include_match)
{
val self = lit("record-adapter");
- val rec_adapter = make_delegate_stream(self, default_arg(stream, std_input),
+ val rec_adapter = make_delegate_stream(self,
+ default_arg_strict(stream, std_input),
sizeof (struct record_adapter_base),
&record_adapter_ops.cobj_ops);
struct record_adapter_base *rb = coerce(struct record_adapter_base *,
@@ -2846,7 +2972,7 @@ val stream_set_prop(val stream, val ind, val prop)
{
val self = lit("stream-set-prop");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->set_prop(stream, ind, prop);
}
@@ -2854,7 +2980,11 @@ val stream_get_prop(val stream, val ind)
{
val self = lit("stream-get-prop");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
+
+ if (ind == fd_k && ops->get_fd != null_get_fd)
+ return ops->get_fd(stream);
+
return ops->get_prop(stream, ind);
}
@@ -2871,16 +3001,28 @@ val real_time_stream_p(val obj)
val close_stream(val stream, val throw_on_error)
{
val self = lit("close-stream");
- struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
- return ops->close(stream, throw_on_error);
+ struct strm_base *s = coerce(struct strm_base *,
+ cobj_handle(self, stream, stream_cls));
+ struct strm_ops *ops = coerce(struct strm_ops *, stream->co.ops);
+ val res = s->close_result;
+
+ if (!res) {
+ res = ops->close(stream, throw_on_error);
+
+ if (res == colon_k)
+ res = t;
+ else if (res)
+ s->close_result = res;
+ }
+
+ return res;
}
val get_error(val stream)
{
val self = lit("get-error");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_error(stream);
}
@@ -2888,7 +3030,7 @@ val get_error_str(val stream)
{
val self = lit("get-error-str");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_error_str(stream);
}
@@ -2896,43 +3038,51 @@ val clear_error(val stream)
{
val self = lit("clear-error");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->clear_error(stream);
}
val get_line(val stream_in)
{
val self = lit("get-line");
- val stream = default_arg(stream_in, std_input);
+ val stream = default_arg_strict(stream_in, std_input);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_line(stream);
}
val get_char(val stream_in)
{
val self = lit("get-char");
- val stream = default_arg(stream_in, std_input);
+ val stream = default_arg_strict(stream_in, std_input);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_char(stream);
}
val get_byte(val stream_in)
{
val self = lit("get-byte");
- val stream = default_arg(stream_in, std_input);
+ val stream = default_arg_strict(stream_in, std_input);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->get_byte(stream);
}
+val get_bytes(val self, val stream_in, mem_t *ptr, ucnum len)
+{
+ val stream = default_arg_strict(stream_in, std_input);
+ struct strm_ops *ops = coerce(struct strm_ops *,
+ cobj_ops(self, stream, stream_cls));
+ return unum(ops->fill_buf(stream, ptr, len, 0));
+}
+
val unget_char(val ch, val stream_in)
{
val self = lit("unget-char");
- val stream = default_arg(stream_in, std_input);
+ val stream = default_arg_strict(stream_in, std_input);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
if (!is_chr(ch))
type_mismatch(lit("~a: ~s is not a character"), self, ch, nao);
return ops->unget_char(stream, ch);
@@ -2941,10 +3091,10 @@ val unget_char(val ch, val stream_in)
val unget_byte(val byte, val stream_in)
{
val self = lit("unget-byte");
- cnum b = c_num(byte);
- val stream = default_arg(stream_in, std_input);
+ cnum b = c_num(byte, self);
+ val stream = default_arg_strict(stream_in, std_input);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
if (b < 0 || b > 255)
uw_throwf(file_error_s, lit("~a: stream ~s: byte value ~a out of range"),
@@ -2956,39 +3106,76 @@ val unget_byte(val byte, val stream_in)
val put_buf(val buf, val pos_in, val stream_in)
{
val self = lit("put-buf");
- val stream = default_arg(stream_in, std_output);
- cnum pos = c_num(default_arg(pos_in, zero));
+ val stream = default_arg_strict(stream_in, std_output);
+ ucnum pos = c_unum(default_arg_strict(pos_in, zero), self);
+ ucnum len = c_unum(length_buf(buf), self);
+ mem_t *ptr = buf_get(buf, self);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
- return ops->put_buf(stream, buf, pos);
+ cobj_ops(self, stream, stream_cls));
+
+ return unum(ops->put_buf(stream, ptr, len, pos));
}
val fill_buf(val buf, val pos_in, val stream_in)
{
val self = lit("fill-buf");
- val stream = default_arg(stream_in, std_input);
- cnum pos = c_num(default_arg(pos_in, zero));
+ val stream = default_arg_strict(stream_in, std_input);
+ ucnum pos = c_unum(default_arg_strict(pos_in, zero), self);
+ ucnum len = c_unum(length_buf(buf), self);
+ mem_t *ptr = buf_get(buf, self);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
- return ops->fill_buf(stream, buf, pos);
+ cobj_ops(self, stream, stream_cls));
+ return unum(ops->fill_buf(stream, ptr, len, pos));
}
val fill_buf_adjust(val buf, val pos_in, val stream_in)
{
val self = lit("fill-buf-adjust");
- val stream = default_arg(stream_in, std_input);
- cnum pos = c_num(default_arg(pos_in, zero));
+ val stream = default_arg_strict(stream_in, std_input);
+ ucnum pos = c_unum(default_arg_strict(pos_in, zero), self);
+ val alloc_size = buf_alloc_size(buf);
+ ucnum len = c_unum(alloc_size, self);
+ mem_t *ptr = buf_get(buf, self);
val readpos;
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
- buf_set_length(buf, buf_alloc_size(buf), zero);
- readpos = ops->fill_buf(stream, buf, pos);
+ cobj_ops(self, stream, stream_cls));
+ buf_set_length(buf, alloc_size, zero);
+ readpos = unum(ops->fill_buf(stream, ptr, len, pos));
buf_set_length(buf, readpos, zero);
return readpos;
}
+val get_line_as_buf(val stream_in)
+{
+ val self = lit("get-line-as-buf");
+ val stream = default_arg_strict(stream_in, std_input);
+ struct strm_ops *ops = coerce(struct strm_ops *,
+ cobj_ops(self, stream, stream_cls));
+ val buf = make_buf(zero, nil, num_fast(128));
+ unsigned char bytes[128];
+ size_t count = 0;
+
+ for (;;) {
+ val b = ops->get_byte(stream);
+ if (b == nil || b == num('\n'))
+ break;
+ bytes[count++] = c_num(b, self);
+
+ if (count == sizeof bytes) {
+ buf_put_bytes(buf, length_buf(buf), bytes, count, self);
+ count = 0;
+ }
+ }
+
+ if (count > 0)
+ buf_put_bytes(buf, length_buf(buf), bytes, count, self);
+
+ buf_trim(buf);
+ return buf;
+}
+
struct fmt {
- size_t minsize;
+ const char *type;
const char *dec;
const char *oct;
const char *hex;
@@ -2996,13 +3183,14 @@ struct fmt {
};
static struct fmt fmt_tab[] = {
- { sizeof(short),"%hd", "%ho", "%hx", "%hX" },
- { sizeof(int), "%d", "%o", "%x", "%X" },
- { sizeof(long), "%ld", "%lo", "%lx", "%llX" },
- { sizeof(cnum), "%lld", "%llo", "%llx", "%llX" },
- { sizeof(cnum), "%Ld", "%Lo", "%Lx", "%llX" },
- { sizeof(cnum), "%qd", "%qo", "%qx", "%qX", },
- { sizeof(cnum), "%I64d", "%I64o", "%I64x", "%I64X" },
+ { "short", "%hd", "%ho", "%hx", "%hX" },
+ { "int", "%d", "%o", "%x", "%X" },
+ { "long", "%ld", "%lo", "%lx", "%lX" },
+ { "long long", "%lld", "%llo", "%llx", "%llX" },
+ { "long long", "%Ld", "%Lo", "%Lx", "%LX" },
+ { "long long", "%qd", "%qo", "%qx", "%qX", },
+ { "int64", "%I64d", "%I64o", "%I64x", "%I64X" },
+ { "__int64", "%I64d", "%I64o", "%I64x", "%I64X" },
{ 0, 0, 0, 0, 0 }
};
@@ -3013,16 +3201,29 @@ static void detect_format_string(void)
struct fmt *f;
char buf[64];
cnum num = 1234;
+ const char *cnum_type = if3(strcmp(INTPTR_TYPE, "longlong_t") == 0,
+ LONGLONG_TYPE, INTPTR_TYPE);
- for (f = fmt_tab; f->minsize != 0; f++) {
+ for (f = fmt_tab; f->type != 0; f++) {
+ if (strcmp(cnum_type, f->type) != 0)
+ continue;
memset(buf, 0, sizeof buf);
- if (f->minsize != sizeof num)
+ if (sprintf(buf, f->dec, num) != 4 || strcmp(buf, "1234") != 0)
continue;
- if (sprintf(buf, f->dec, num) == 4 && strcmp(buf, "1234") == 0) {
- num_fmt = f;
- break;
- }
+ memset(buf, 0, sizeof buf);
+ if (sprintf(buf, f->oct, num) != 4 || strcmp(buf, "2322") != 0)
+ continue;
+ memset(buf, 0, sizeof buf);
+ if (sprintf(buf, f->hex, num) != 3 || strcmp(buf, "4d2") != 0)
+ continue;
+ memset(buf, 0, sizeof buf);
+ if (sprintf(buf, f->HEX, num) != 3 || strcmp(buf, "4D2") != 0)
+ continue;
+ num_fmt = f;
+ break;
}
+
+ bug_unless (num_fmt != 0);
}
enum align { al_left, al_center, al_right };
@@ -3065,12 +3266,18 @@ static void vformat_align_post(val stream, enum align align, int slack)
static void vformat_num(val stream, const char *str,
int width, enum align align, int zeropad,
- int precision, int sign)
+ int precision, int add_sign)
{
int sign_char = (str[0] == '-' || str[0] == '+') ? str[0] : 0;
+ int have_sign = sign_char == '-';
+ int mandatory_sign = have_sign || add_sign == '+';
int digit_len = strlen(str) - (sign_char != 0);
- int padlen = precision > digit_len ? precision - digit_len : 0;
- int total_len = digit_len + padlen + (sign_char || sign);
+ int overflow = digit_len > precision;
+ int padlen = overflow ? 0 : precision - digit_len;
+ int tentative_len = digit_len + padlen + (have_sign || add_sign);
+ int total_len = (tentative_len > width &&
+ (have_sign || add_sign)
+ && !mandatory_sign) ? tentative_len - 1 : tentative_len;
int slack = (total_len < width) ? width - total_len : 0;
int i;
@@ -3080,11 +3287,13 @@ static void vformat_num(val stream, const char *str,
for (i = 0; i < padlen; i++)
put_char(chr(' '), stream);
- if (sign_char) {
- put_char(chr(sign_char), stream);
+ if (sign_char)
str++;
- } else if (sign) {
- put_char(chr(sign), stream);
+
+ if (mandatory_sign) {
+ put_char(chr(have_sign ? sign_char : add_sign), stream);
+ } else if (add_sign && tentative_len <= width) {
+ put_char(chr(add_sign), stream);
}
if (zeropad)
@@ -3123,7 +3332,8 @@ static cnum calc_fitlen(const wchar_t *cstr, int precision, int width)
static void vformat_str(val stream, val str, int width, enum align align,
int precision)
{
- const wchar_t *cstr = c_str(str);
+ val self = lit("format");
+ const wchar_t *cstr = c_str(str, self);
cnum fitlen = calc_fitlen(cstr, precision, width);
cnum slack = (fitlen < width) ? width - fitlen : 0;
cnum i, w;
@@ -3146,7 +3356,7 @@ static void vformat_str(val stream, val str, int width, enum align align,
gc_hint(str);
}
-val formatv(val stream_in, val fmtstr, struct args *al)
+val formatv(val stream_in, val fmtstr, varg al)
{
uses_or2;
val stream = if3(stream_in == t,
@@ -3154,21 +3364,23 @@ val formatv(val stream_in, val fmtstr, struct args *al)
or2(stream_in, make_string_output_stream()));
val save_indent = get_indent(stream);
val save_mode = nil;
- val name = lit("format");
+ val self = lit("format");
uw_simple_catch_begin;
{
- const wchar_t *fmt = c_str(fmtstr);
+ const wchar_t *fmt = c_str(fmtstr, self);
enum {
vf_init, vf_width, vf_digits, vf_star, vf_precision, vf_spec
} state = vf_init, saved_state = vf_init;
- int width = 0, precision = 0, precision_p = 0, digits = 0, lt = 0, neg = 0;
- enum align align = al_right;
- int sign = 0, zeropad = 0, dfl_precision = 0;
- int dfl_digits = 0, print_base = 0;
cnum value;
cnum arg_ix = 0;
+ /* conversion variables that are reset before for each conversion */
+ int width = 0, precision = 0, precision_p = 0, digits = 0, lt = 0, neg = 0;
+ int sign = 0, zeropad = 0;
+ enum align align = al_right;
+ /* conversion variables that persist across conversions */
+ int dfl_precision = 0, dfl_digits = 0, print_base = 0;
for (;;) {
val obj;
@@ -3183,14 +3395,9 @@ val formatv(val stream_in, val fmtstr, struct args *al)
break;
case '~':
state = vf_width;
- width = 0;
+ width = precision = precision_p = 0;
+ digits = lt = neg = sign = zeropad = 0;
align = al_right;
- zeropad = 0;
- precision = 0;
- precision_p = 0;
- digits = 0;
- lt = 0;
- neg = 0;
continue;
default:
put_char(chr(ch), stream);
@@ -3235,8 +3442,11 @@ val formatv(val stream_in, val fmtstr, struct args *al)
case vf_precision:
switch (ch) {
case '0':
- zeropad = 1;
- continue;
+ if (!zeropad) {
+ zeropad = 1;
+ continue;
+ }
+ /* fallthrough */
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
saved_state = state;
@@ -3246,6 +3456,9 @@ val formatv(val stream_in, val fmtstr, struct args *al)
case '+': case ' ':
sign = ch;
continue;
+ case '-':
+ sign = '0';
+ continue;
case '*':
saved_state = state;
state = vf_star;
@@ -3263,7 +3476,7 @@ val formatv(val stream_in, val fmtstr, struct args *al)
digits = (digits * 10) + (ch - '0');
if (digits > 999999)
uw_throwf(assert_s, lit("~a: ridiculous precision or field"),
- name, nao);
+ self, nao);
continue;
default:
do_digits:
@@ -3296,35 +3509,61 @@ val formatv(val stream_in, val fmtstr, struct args *al)
}
break;
case vf_star:
- obj = args_get_checked(name, al, &arg_ix);
- digits = c_num(obj);
+ obj = args_get_checked(self, al, &arg_ix);
+ digits = c_num(obj, self);
goto do_digits;
break;
case vf_spec:
state = vf_init;
+ if (zeropad && !precision_p) {
+ zeropad = precision = 0;
+ precision_p = 1;
+ }
switch (ch) {
case 'x': case 'X':
- obj = args_get_checked(name, al, &arg_ix);
+ obj = args_get_checked(self, al, &arg_ix);
typ = type(obj);
hex:
- if (typ == BGNUM) {
- int nchars = mp_radix_size(mp(obj), 16);
- if (nchars >= convert(int, sizeof (num_buf)))
- pnum = coerce(char *, chk_malloc(nchars + 1));
- mp_toradix_case(mp(obj), coerce(unsigned char *, pnum), 16, ch == 'x');
- } else {
- const char *fmt = ch == 'x' ? num_fmt->hex : num_fmt->HEX;
- value = c_num(obj);
- if (value < 0) {
- num_buf[0] = '-';
- sprintf(num_buf + 1, fmt, -value);
- } else {
- sprintf(num_buf, fmt, value);
+ switch (typ) {
+ case BGNUM:
+ {
+ int nchars = mp_radix_size(mp(obj), 16);
+ if (nchars >= convert(int, sizeof (num_buf)))
+ pnum = coerce(char *, chk_malloc(nchars + 1));
+ mp_toradix_case(mp(obj), coerce(unsigned char *, pnum), 16, ch == 'x');
+ }
+ break;
+ case BUF:
+ {
+ ucnum len = c_unum(length_buf(obj), self);
+ ucnum nchars = 2 * len + 1;
+
+ if (len >= INT_PTR_MAX)
+ uw_throwf(error_s, lit("~a: ~~~a conversion given "
+ "too large a buf argument"),
+ self, chr(ch), nao);
+
+ pnum = coerce(char *, chk_malloc(nchars));
+ buf_hex(obj, pnum, nchars, ch == 'X');
+ }
+ break;
+ case NUM:
+ case CHR:
+ default:
+ {
+ const char *fmt = ch == 'x' ? num_fmt->hex : num_fmt->HEX;
+ value = c_num(obj, self);
+ if (value < 0) {
+ num_buf[0] = '-';
+ sprintf(num_buf + 1, fmt, -value);
+ } else {
+ sprintf(num_buf, fmt, value);
+ }
}
}
goto output_num;
case 'o': case 'b':
- obj = args_get_checked(name, al, &arg_ix);
+ obj = args_get_checked(self, al, &arg_ix);
typ = type(obj);
oct:
if (typ == BGNUM) {
@@ -3334,10 +3573,10 @@ val formatv(val stream_in, val fmtstr, struct args *al)
pnum = coerce(char *, chk_malloc(nchars + 1));
mp_toradix(mp(obj), coerce(unsigned char *, pnum), rad);
} else if (ch == 'o') {
- cnum value = c_num(obj);
+ cnum value = c_num(obj, self);
sprintf(num_buf, num_fmt->oct, value);
} else {
- cnum val = c_num(obj);
+ cnum val = c_num(obj, self);
int s = (val < 0);
int i = sizeof num_buf;
@@ -3359,7 +3598,7 @@ val formatv(val stream_in, val fmtstr, struct args *al)
}
goto output_num;
case 'f': case 'e':
- obj = args_get_checked(name, al, &arg_ix);
+ obj = args_get_checked(self, al, &arg_ix);
{
double n;
@@ -3372,29 +3611,33 @@ val formatv(val stream_in, val fmtstr, struct args *al)
n = c_flo(obj, lit("format"));
break;
case NUM:
- n = convert(double, c_num(obj));
+ n = convert(double, c_num(obj, self));
break;
default:
- uw_throwf(error_s, lit("format: ~~~a conversion requires "
+ uw_throwf(error_s, lit("~a: ~~~a conversion requires "
"numeric arg: ~s given"),
- chr(ch), obj, nao);
+ self, chr(ch), obj, nao);
}
if (!precision_p) {
if (!dfl_digits)
- dfl_digits = c_num(cdr(lookup_var(nil, print_flo_digits_s)));
+ dfl_digits = c_num(cdr(lookup_var(nil, print_flo_digits_s)), self);
precision = dfl_digits;
}
/* guard against num_buf overflow */
if (precision > 128)
- uw_throwf(error_s, lit("excessive precision in format: ~s"),
- num(precision), nao);
+ uw_throwf(error_s, lit("~a: excessive precision: ~s"),
+ self, num(precision), nao);
if (ch == 'e') {
sprintf(num_buf, "%.*e", precision, n);
{
+#if CONFIG_LOCALE_TOLERANCE
+ char *dec = strchr(num_buf, dec_point);
+#else
char *dec = strchr(num_buf, '.');
+#endif
char *exp = strchr(dec ? dec : num_buf, 'e');
if (exp) {
@@ -3425,19 +3668,26 @@ val formatv(val stream_in, val fmtstr, struct args *al)
continue;
}
precision = (width ? width - 1 : 0);
+#if CONFIG_LOCALE_TOLERANCE
+ if (dec_point != '.') {
+ char *dot = num_buf;
+ while ((dot = strchr(dot, dec_point)) != 0)
+ *dot++ = '.';
+ }
+#endif
goto output_num;
}
case 'd':
- obj = args_get_checked(name, al, &arg_ix);
+ obj = args_get_checked(self, al, &arg_ix);
typ = type(obj);
goto dec;
case 'a': case 's':
- obj = args_get_checked(name, al, &arg_ix);
+ obj = args_get_checked(self, al, &arg_ix);
typ = type(obj);
if (typ == NUM || typ == BGNUM) {
if (!print_base)
- print_base = c_num(cdr(lookup_var(nil, print_base_s)));
+ print_base = c_num(cdr(lookup_var(nil, print_base_s)), self);
switch (print_base) {
case 0:
case 2:
@@ -3458,8 +3708,10 @@ val formatv(val stream_in, val fmtstr, struct args *al)
dec:
switch (typ) {
case NUM:
- value = c_num(obj);
+ value = c_num(obj, self);
sprintf(num_buf, num_fmt->dec, value);
+ if (width)
+ precision = min(precision, width - 1);
goto output_num;
case BGNUM:
{
@@ -3468,20 +3720,31 @@ val formatv(val stream_in, val fmtstr, struct args *al)
pnum = coerce(char *, chk_malloc(nchars + 1));
mp_toradix(mp(obj), coerce(unsigned char *, pnum), 10);
}
+ if (width)
+ precision = min(precision, width - 1);
goto output_num;
case FLNUM:
if (!precision_p) {
if (!dfl_precision)
dfl_precision = c_num(cdr(lookup_var(nil,
- print_flo_precision_s)));
+ print_flo_precision_s)),
+ self);
precision = dfl_precision;
}
if (precision > 500)
- uw_throwf(error_s, lit("excessive precision in format: ~s"),
- num(precision), nao);
+ uw_throwf(error_s, lit("~a: excessive precision: ~s"),
+ self, num(precision), nao);
- sprintf(num_buf, "%.*g", precision, obj->fl.n);
+ sprintf(num_buf, "%.*g", precision, c_f(obj));
+
+#if CONFIG_LOCALE_TOLERANCE
+ if (dec_point != '.') {
+ char *dot = num_buf;
+ while ((dot = strchr(dot, dec_point)) != 0)
+ *dot++ = '.';
+ }
+#endif
{
char *dec = strchr(num_buf, '.');
@@ -3505,7 +3768,7 @@ val formatv(val stream_in, val fmtstr, struct args *al)
}
if (ch == 's' && (!precision_p || precision > 0) && !dec && !exp)
- strcat(num_buf, ".0");
+ strcat(num_buf, ".0");
}
if (!isdigit(num_buf[0]) && !isdigit(num_buf[1])) {
@@ -3513,7 +3776,8 @@ val formatv(val stream_in, val fmtstr, struct args *al)
continue;
}
- precision = (width ? width - 1 : 0);
+ precision = max(0, min(precision, width - 1));
+
goto output_num;
default:
if (width != 0 || precision_p) {
@@ -3527,7 +3791,7 @@ val formatv(val stream_in, val fmtstr, struct args *al)
continue;
case 'p':
{
- val ptr = args_get_checked(name, al, &arg_ix);
+ val ptr = args_get_checked(self, al, &arg_ix);
value = coerce(cnum, ptr);
sprintf(num_buf, num_fmt->hex, value);
}
@@ -3561,7 +3825,7 @@ val formatv(val stream_in, val fmtstr, struct args *al)
}
if (args_more(al, arg_ix))
- uw_throwf(assert_s, lit("~a: excess arguments"), name, nao);
+ uw_throwf(assert_s, lit("~a: excess arguments"), self, nao);
}
@@ -3581,7 +3845,7 @@ val formatv(val stream_in, val fmtstr, struct args *al)
val vformat(val stream, val fmtstr, va_list vl)
{
val arg;
- args_decl(args, ARGS_MAX);
+ args_decl_constsize(args, ARGS_MAX);
while ((arg = va_arg(vl, val)) != nao)
args_add_checked(lit("format"), args, arg);
@@ -3603,7 +3867,7 @@ val format(val stream, val str, ...)
val st = if3(stream == t,
std_output,
or2(stream, make_string_output_stream()));
- class_check(self, st, stream_s);
+ class_check(self, st, stream_cls);
{
va_list vl;
@@ -3615,7 +3879,7 @@ val format(val stream, val str, ...)
}
}
-val fmt(val string, struct args *args)
+val fmt(val string, varg args)
{
return formatv(nil, string, args);
}
@@ -3631,17 +3895,18 @@ static val put_indent(val stream, struct strm_ops *ops, cnum chars)
val put_string(val string, val stream_in)
{
val self = lit("put-string");
- val stream = default_arg(stream_in, std_output);
- struct strm_base *s = coerce(struct strm_base *, stream->co.handle);
+ val stream = default_arg_strict(stream_in, std_output);
+ struct strm_base *s = coerce(struct strm_base *,
+ cobj_handle(self, stream, stream_cls));
if (lazy_stringp(string)) {
return lazy_str_put(string, stream_in, s);
} else {
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
cnum col = s->column;
- const wchar_t *str = c_str(string), *p = str;
+ const wchar_t *str = c_str(string, self), *p = str;
if (s->indent_mode != indent_off && s->indent_mode != indent_foff) {
while (*str)
@@ -3674,10 +3939,11 @@ val put_string(val string, val stream_in)
val put_char(val ch, val stream_in)
{
val self = lit("put-char");
- val stream = default_arg(stream_in, std_output);
+ val stream = default_arg_strict(stream_in, std_output);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
- struct strm_base *s = coerce(struct strm_base *, stream->co.handle);
+ cobj_ops(self, stream, stream_cls));
+ struct strm_base *s = coerce(struct strm_base *,
+ cobj_handle(self, stream, stream_cls));
wint_t cch = c_chr(ch);
switch (cch) {
@@ -3716,10 +3982,10 @@ val put_char(val ch, val stream_in)
val put_byte(val byte, val stream_in)
{
val self = lit("put-byte");
- val stream = default_arg(stream_in, std_output);
+ val stream = default_arg_strict(stream_in, std_output);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
- cnum b = c_num(byte);
+ cobj_ops(self, stream, stream_cls));
+ cnum b = c_num(byte, self);
if (b < 0 || b > 255)
uw_throwf(file_error_s, lit("~a: stream ~s: byte value ~a out of range"),
@@ -3728,9 +3994,11 @@ val put_byte(val byte, val stream_in)
return ops->put_byte(stream, b);
}
-val put_line(val string, val stream)
+val put_line(val string, val stream_in)
{
- return (put_string(default_arg(string, null_string), stream), put_char(chr('\n'), stream));
+ val stream = default_arg_strict(stream_in, std_output);
+ return (put_string(default_arg_strict(string, null_string), stream),
+ put_char(chr('\n'), stream));
}
val put_strings(val strings, val stream)
@@ -3754,9 +4022,9 @@ val put_lines(val lines, val stream)
val flush_stream(val stream_in)
{
val self = lit("flush-stream");
- val stream = default_arg(stream_in, std_output);
+ val stream = default_arg_strict(stream_in, std_output);
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
return ops->flush(stream);
}
@@ -3764,7 +4032,7 @@ val seek_stream(val stream, val offset, val whence)
{
val self = lit("seek-stream");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
enum strm_whence w;
if (whence == from_start_k)
@@ -3784,7 +4052,7 @@ val truncate_stream(val stream, val len)
{
val self = lit("truncate-stream");
struct strm_ops *ops = coerce(struct strm_ops *,
- cobj_ops(self, stream, stream_s));
+ cobj_ops(self, stream, stream_cls));
if (missingp(len))
len = ops->seek(stream, zero, strm_cur);
return ops->truncate(stream, len);
@@ -3794,7 +4062,7 @@ val get_indent_mode(val stream)
{
val self = lit("get-indent-mode");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
return num_fast(s->indent_mode);
}
@@ -3802,10 +4070,10 @@ val test_set_indent_mode(val stream, val compare, val mode)
{
val self = lit("test-set-indent-mode");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
val oldval = num_fast(s->indent_mode);
if (oldval == compare)
- s->indent_mode = convert(enum indent_mode, c_num(mode));
+ s->indent_mode = convert(enum indent_mode, c_num(mode, self));
return oldval;
}
@@ -3813,10 +4081,10 @@ val test_neq_set_indent_mode(val stream, val compare, val mode)
{
val self = lit("test-neq-set-indent-mode");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
val oldval = num_fast(s->indent_mode);
if (oldval != compare)
- s->indent_mode = convert(enum indent_mode, c_num(mode));
+ s->indent_mode = convert(enum indent_mode, c_num(mode, self));
return oldval;
}
@@ -3824,9 +4092,9 @@ val set_indent_mode(val stream, val mode)
{
val self = lit("set-indent-mode");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
val oldval = num_fast(s->indent_mode);
- s->indent_mode = convert(enum indent_mode, c_num(mode));
+ s->indent_mode = convert(enum indent_mode, c_num(mode, self));
return oldval;
}
@@ -3834,7 +4102,7 @@ val get_indent(val stream)
{
val self = lit("get-indent");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
return num(s->indent_chars);
}
@@ -3842,9 +4110,9 @@ val set_indent(val stream, val indent)
{
val self = lit("set-indent");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
val oldval = num(s->indent_chars);
- s->indent_chars = c_num(indent);
+ s->indent_chars = c_num(indent, self);
if (s->indent_chars < 0)
s->indent_chars = 0;
return oldval;
@@ -3854,10 +4122,22 @@ val inc_indent(val stream, val delta)
{
val self = lit("inc-indent");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
val oldval = num(s->indent_chars);
val col = num(s->column);
- s->indent_chars = c_num(plus(delta, col));
+ s->indent_chars = c_num(plus(delta, col), self);
+ if (s->indent_chars < 0)
+ s->indent_chars = 0;
+ return oldval;
+}
+
+val inc_indent_abs(val stream, val delta)
+{
+ val self = lit("inc-indent-abs");
+ struct strm_base *s = coerce(struct strm_base *,
+ cobj_handle(self, stream, stream_cls));
+ val oldval = num(s->indent_chars);
+ s->indent_chars = c_num(plus(delta, oldval), self);
if (s->indent_chars < 0)
s->indent_chars = 0;
return oldval;
@@ -3867,7 +4147,7 @@ val width_check(val stream, val alt)
{
val self = lit("width-check");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
if ((s->indent_mode == indent_code &&
s->column >= s->indent_chars + s->code_width) ||
@@ -3890,7 +4170,7 @@ val force_break(val stream)
{
val self = lit("force-break");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
s->force_break = 1;
return stream;
}
@@ -3899,9 +4179,9 @@ val set_max_length(val stream, val length)
{
val self = lit("set-max-length");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
cnum old_max = s->max_length;
- s->max_length = c_num(length);
+ s->max_length = c_num(length, self);
return num(old_max);
}
@@ -3909,9 +4189,9 @@ val set_max_depth(val stream, val depth)
{
val self = lit("set-max-depth");
struct strm_base *s = coerce(struct strm_base *,
- cobj_handle(self, stream, stream_s));
+ cobj_handle(self, stream, stream_cls));
cnum old_max = s->max_depth;
- s->max_depth = c_num(depth);
+ s->max_depth = c_num(depth, self);
return num(old_max);
}
@@ -3929,11 +4209,11 @@ struct strm_ctx *get_ctx(val stream)
return s->ctx;
}
-val get_string(val stream_in, val nchars, val close_after_p)
+val get_string(val stream_in, val nchars_in, val close_after_p)
{
- val stream = default_arg(stream_in, std_input);
+ val stream = default_arg_strict(stream_in, std_input);
val strstream = make_string_output_stream();
- nchars = default_null_arg(nchars);
+ val nchars = default_null_arg(nchars_in);
val ch;
if (nchars) {
@@ -3952,24 +4232,16 @@ val get_string(val stream_in, val nchars, val close_after_p)
return get_string_from_stream(strstream);
}
-static DIR *w_opendir(const wchar_t *wname)
-{
- char *name = utf8_dup_to(wname);
- DIR *d = opendir(name);
- free(name);
- return d;
-}
-
-
val open_directory(val path)
{
- DIR *d = w_opendir(c_str(path));
+ val self = lit("open-directory");
+ DIR *d = w_opendir(c_str(path, self));
if (!d) {
int eno = errno;
- uw_throwf(errno_to_file_error(eno),
- lit("error opening directory ~s: ~d/~s"),
- path, num(eno), string_utf8(strerror(eno)), nao);
+ uw_ethrowf(errno_to_file_error(eno),
+ lit("error opening directory ~s: ~d/~s"),
+ path, num(eno), errno_to_str(eno), nao);
}
return make_dir_stream(d);
@@ -3977,49 +4249,109 @@ val open_directory(val path)
val open_file(val path, val mode_str)
{
+ val self = lit("open-file");
struct stdio_mode m, m_r = stdio_mode_init_r;
- val norm_mode = normalize_mode(&m, mode_str, m_r);
- FILE *f = w_fopen_mode(c_str(path), c_str(norm_mode), m);
+ val norm_mode = normalize_mode(&m, mode_str, m_r, self);
- if (!f) {
- int eno = errno;
- uw_throwf(errno_to_file_error(eno), lit("error opening ~s: ~d/~s"),
- path, num(eno), string_utf8(strerror(eno)), nao);
+#if HAVE_ZLIB
+again:
+#endif
+ if (!m.gzip) {
+ FILE *f = w_fopen_mode(c_str(path, self), c_str(norm_mode, self), m);
+
+ if (!f)
+ goto error;
+
+ return set_mode_props(m, make_stdio_stream(f, path));
+ } else {
+#if HAVE_ZLIB
+ gzFile f = w_gzopen_mode(c_str(path, self), c_str(norm_mode, self),
+ m, self);
+
+ if (!f)
+ goto error;
+
+ if (m.read && gzdirect(f)) {
+ gzclose(f);
+ m.gzip = 0;
+ goto again;
+ }
+
+ return make_gzio_stream(f, -1, path, m.write);
+#else
+ uw_ethrowf(file_error_s, lit("~a: not built with zlib support"),
+ self, nao);
+#endif
}
- return set_mode_props(m, make_stdio_stream(f, path));
+error:
+ {
+ int eno = errno;
+ uw_ethrowf(errno_to_file_error(eno), lit("error opening ~s: ~d/~s"),
+ path, num(eno), errno_to_str(eno), nao);
+ }
}
-val open_fileno(val fd, val mode_str)
+val open_fileno(val fd, val mode_str, val pid_opt)
{
+ val self = lit("open-fileno");
struct stdio_mode m, m_r = stdio_mode_init_r;
- FILE *f = (errno = 0, w_fdopen(c_num(fd), c_str(normalize_mode(&m, mode_str, m_r))));
+ val norm_mode = normalize_mode(&m, mode_str, m_r, self);
+ val pid = default_arg(pid_opt, nil);
- if (!f) {
- int eno = errno;
- close(c_num(fd));
- uw_throwf(errno_to_file_error(eno), lit("error opening descriptor ~a: ~d/~s"),
- fd, num(eno), string_utf8(strerror(eno)), nao);
- }
+ if (!m.gzip) {
+ FILE *f = (errno = 0, w_fdopen(c_num(fd, self),
+ c_str(norm_mode, self)));
+
+ if (!f)
+ {
+ int eno = errno;
+ close(c_num(fd, self));
+ uw_ethrowf(errno_to_file_error(eno),
+ lit("error opening descriptor ~a: ~d/~s"),
+ fd, num(eno), errno_to_str(eno), nao);
+ }
- return set_mode_props(m, make_stdio_stream(f, format(nil,
- lit("fd ~d"),
- fd, nao)));
+ {
+ val descr = format(nil, lit("fd ~d"), fd, nao);
+
+#if HAVE_FORK_STUFF
+ return set_mode_props(m, if3(pid,
+ make_pipevp_stream(f, descr,
+ c_num(pid, self)),
+ make_stdio_stream(f, descr)));
+#else
+ return set_mode_props(m, make_stdio_stream(f, descr));
+#endif
+ }
+
+ } else {
+#if HAVE_ZLIB
+ cnum fdn = c_num(fd, self);
+ gzFile f = w_gzdopen_mode(fdn, c_str(norm_mode, self), m, self);
+ return make_gzio_stream(f, fdn, format(nil, lit("fd ~d"), fd, nao),
+ m.write);
+#else
+ uw_ethrowf(file_error_s, lit("~a: not built with zlib support"),
+ self, nao);
+#endif
+ }
}
val open_tail(val path, val mode_str, val seek_end_p)
{
+ val self = lit("open-tail");
struct stdio_mode m, m_r = stdio_mode_init_r;
- val mode = normalize_mode(&m, mode_str, m_r);
- FILE *f = w_fopen_mode(c_str(path), c_str(mode), m);
+ val mode = normalize_mode(&m, mode_str, m_r, self);
+ FILE *f = w_fopen_mode(c_str(path, self), c_str(mode, self), m);
struct stdio_handle *h;
val stream;
unsigned long state = 0;
if (f && default_null_arg(seek_end_p))
if (fseek(f, 0, SEEK_END) < 0)
- uw_throwf(file_error_s, lit("error seeking to end of ~s: ~d/~s"),
- path, num(errno), string_utf8(strerror(errno)), nao);
+ uw_ethrowf(file_error_s, lit("error seeking to end of ~s: ~d/~s"),
+ path, num(errno), errno_to_str(errno), nao);
stream = make_tail_stream(f, path);
h = coerce(struct stdio_handle *, stream->co.handle);
@@ -4033,6 +4365,9 @@ struct save_fds {
volatile int in;
volatile int out;
volatile int err;
+ volatile int subin;
+ volatile int subout;
+ volatile int suberr;
};
#define FDS_IN 1
@@ -4041,39 +4376,72 @@ struct save_fds {
static void fds_init(struct save_fds *fds)
{
- fds->in = fds->out = fds->err = -1;
+ fds->in = fds->out = fds->err = fds->subin = fds->subin = fds->subin = -1;
}
-static int fds_subst(val stream, int fd_std)
+static int fds_getfd(val stream, val self)
{
- int fd_orig = c_num(stream_fd(stream));
+ val sfd = stream_fd(stream);
+ int fd_sub = if3(integerp(sfd), c_num(sfd, self), INT_MIN);
+
- if (fd_orig == fd_std)
+ if (fd_sub == INT_MIN)
+ uw_throwf(file_error_s, lit("~a: (fileno ~s) is ~s, which is unusable"),
+ self, stream, sfd, nao);
+
+ return fd_sub;
+}
+
+#if HAVE_WSPAWN || HAVE_SPAWN
+static int fds_subst(int fd_sub, int fd_std, val self)
+{
+ if (fd_sub == fd_std)
return -1;
{
int fd_dup = dup(fd_std);
if (fd_dup != -1) {
- dup2(fd_orig, fd_std);
+ dup2(fd_sub, fd_std);
return fd_dup;
}
- uw_throwf(file_error_s, lit("failed to duplicate file descriptor: ~d/~s"),
- num(errno), string_utf8(strerror(errno)), nao);
+ uw_ethrowf(file_error_s, lit("~a: failed to duplicate file descriptor: ~d/~s"),
+ self, num(errno), errno_to_str(errno), nao);
}
}
+#endif
+
+static void fds_subst_nosave(int fd_sub, int fd_std)
+{
+ if (fd_sub == fd_std)
+ return;
+ dup2(fd_sub, fd_std);
+}
-static void fds_swizzle(struct save_fds *fds, int flags)
+static void fds_prepare(struct save_fds *fds, int flags, val self)
{
if ((flags & FDS_IN) != 0)
- fds->in = fds_subst(std_input, STDIN_FILENO);
+ fds->subin = fds_getfd(std_input, self);
if ((flags & FDS_OUT) != 0)
- fds->out = fds_subst(std_output, STDOUT_FILENO);
+ fds->subout = fds_getfd(std_output, self);
if ((flags & FDS_ERR) != 0)
- fds->err = fds_subst(std_error, STDERR_FILENO);
+ fds->suberr = fds_getfd(std_error, self);
+}
+
+#if HAVE_WSPAWN || HAVE_SPAWN
+static void fds_swizzle(struct save_fds *fds, int flags, val self)
+{
+ if ((flags & FDS_IN) != 0)
+ fds->in = fds_subst(fds->subin, STDIN_FILENO, self);
+
+ if ((flags & FDS_OUT) != 0)
+ fds->out = fds_subst(fds->subout, STDOUT_FILENO, self);
+
+ if ((flags & FDS_ERR) != 0)
+ fds->err = fds_subst(fds->suberr, STDERR_FILENO, self);
}
static void fds_restore(struct save_fds *fds)
@@ -4093,37 +4461,43 @@ static void fds_restore(struct save_fds *fds)
close(fds->err);
}
}
+#endif
-
-val open_command(val path, val mode_str)
+static void fds_clobber(struct save_fds *fds, int flags)
{
- struct stdio_mode m, m_r = stdio_mode_init_r;
- val mode = normalize_mode_no_bin(&m, mode_str, m_r);
- int input = m.read != 0;
- struct save_fds sfds;
- FILE *f = 0;
-
- fds_init(&sfds);
-
- uw_simple_catch_begin;
+ if ((flags & FDS_IN) != 0)
+ fds_subst_nosave(fds->subin, STDIN_FILENO);
- fds_swizzle(&sfds, (input ? FDS_IN : FDS_OUT) | FDS_ERR);
+ if ((flags & FDS_OUT) != 0)
+ fds_subst_nosave(fds->subout, STDOUT_FILENO);
- f = w_popen(c_str(path), c_str(mode));
+ if ((flags & FDS_ERR) != 0)
+ fds_subst_nosave(fds->suberr, STDERR_FILENO);
+}
- if (!f) {
- int eno = errno;
- uw_throwf(errno_to_file_error(eno), lit("error opening pipe ~s: ~d/~s"),
- path, num(eno), string_utf8(strerror(eno)), nao);
- }
+val path_search(val name, val path_in)
+{
+ val self = lit("path-search");
+ val ps = static_str(path_sep_chars);
- uw_unwind {
- fds_restore(&sfds);
+ if (empty(name) || equal(name, lit(".")) || equal(name, lit(".."))) {
+ return nil;
+ } else if (break_str(name, ps)) {
+ return name;
+ } else {
+ val path = default_arg_strict(path_in, getenv_wrap(lit("PATH")));
+ val spath = if3(listp(path), path, split_str(path, chr(path_var_sep_char)));
+ for (; spath; spath = cdr(spath)) {
+ val dir = car(spath);
+ val full = path_cat(dir, name);
+ char *full8 = utf8_dup_to(c_str(full, self));
+ int res = access(full8, F_OK);
+ free(full8);
+ if (res == 0)
+ return full;
+ }
+ return nil;
}
-
- uw_catch_end;
-
- return set_mode_props(m, make_pipe_stream(f, path));
}
#if HAVE_FORK_STUFF
@@ -4131,8 +4505,7 @@ static val open_subprocess(val name, val mode_str, val args, val fun)
{
val self = lit("open-subprocess");
struct stdio_mode m, m_r = stdio_mode_init_r;
- val mode = normalize_mode(&m, mode_str, m_r);
- int input = m.read != 0;
+ int input = (normalize_mode(&m, mode_str, m_r, self), m.read != 0);
int fd[2];
pid_t pid;
char **argv = 0;
@@ -4140,19 +4513,21 @@ static val open_subprocess(val name, val mode_str, val args, val fun)
int i, nargs;
struct save_fds sfds;
val ret = nil;
+ int fds_flags = (input ? FDS_IN : FDS_OUT) | FDS_ERR;
args = default_null_arg(args);
fun = default_null_arg(fun);
- nargs = c_num(length(args)) + 1;
+ nargs = c_num(length(args), self) + 1;
if (!name && !fun)
uw_throwf(error_s, lit("~a: program name and/or function required"), self, nao);
- fds_init(&sfds);
+ if (!input)
+ flush_stream(std_output);
- uw_simple_catch_begin;
+ fds_init(&sfds);
- fds_swizzle(&sfds, (input ? FDS_IN : FDS_OUT) | FDS_ERR);
+ fds_prepare(&sfds, fds_flags, self);
if (nargs < 0 || nargs == INT_MAX)
uw_throwf(error_s, lit("~a: argument list overflow"), self, nao);
@@ -4163,15 +4538,15 @@ static val open_subprocess(val name, val mode_str, val args, val fun)
if (pipe(fd) == -1) {
int eno = errno;
free(argv);
- uw_throwf(errno_to_file_error(eno),
- lit("opening pipe ~s, pipe syscall failed: ~d/~s"),
- name, num(eno), string_utf8(strerror(eno)), nao);
+ uw_ethrowf(errno_to_file_error(eno),
+ lit("opening pipe ~s, pipe syscall failed: ~d/~s"),
+ name, num(eno), errno_to_str(eno), nao);
}
if (argv) {
for (i = 0, iter = cons(name, args); iter; i++, iter = cdr(iter)) {
val arg = car(iter);
- argv[i] = utf8_dup_to(c_str(arg));
+ argv[i] = utf8_dup_to(c_str(arg, self));
}
argv[i] = 0;
}
@@ -4184,11 +4559,13 @@ static val open_subprocess(val name, val mode_str, val args, val fun)
free(argv[i]);
free(argv);
}
- uw_throwf(process_error_s, lit("opening pipe ~s, fork syscall failed: ~d/~s"),
- name, num(errno), string_utf8(strerror(errno)), nao);
+ uw_ethrowf(process_error_s, lit("opening pipe ~s, fork syscall failed: ~d/~s"),
+ name, num(errno), errno_to_str(errno), nao);
}
if (pid == 0) {
+ fds_clobber(&sfds, fds_flags);
+
if (input) {
dup2(fd[1], STDOUT_FILENO);
if (fd[1] != STDOUT_FILENO) /* You never know */
@@ -4232,13 +4609,15 @@ static val open_subprocess(val name, val mode_str, val args, val fun)
if (fun)
funcall(fun);
- if (argv)
+ if (argv) {
+ val ch_env = child_env;
+ if (ch_env != t)
+ replace_env(ch_env);
execvp(argv[0], argv);
+ }
_exit(errno);
} else {
int whichfd;
- char *utf8mode = utf8_dup_to(c_str(mode));
- FILE *f;
if (input) {
close(fd[1]);
@@ -4258,28 +4637,26 @@ static val open_subprocess(val name, val mode_str, val args, val fun)
fcntl(whichfd, F_SETFD, FD_CLOEXEC);
#endif
- if ((f = fdopen(whichfd, utf8mode)) == 0) {
+ uw_simple_catch_begin;
+
+ ret = open_fileno(num(whichfd), mode_str, num(pid));
+
+ uw_unwind {
int status;
- kill(pid, SIGINT);
- kill(pid, SIGTERM);
- while (waitpid(pid, &status, 0) == -1 && errno == EINTR)
- ;
- free(utf8mode);
- uw_throwf(file_error_s, lit("opening pipe ~s, fdopen failed: ~d/~s"),
- name, num(errno), string_utf8(strerror(errno)), nao);
+ if (ret == nil) {
+ int eno = errno;
+ kill(pid, SIGINT);
+ kill(pid, SIGTERM);
+ while (waitpid(pid, &status, 0) == -1 && errno == EINTR)
+ ;
+ uw_ethrowf(file_error_s, lit("opening pipe ~s: ~d/~s"),
+ name, num(eno), errno_to_str(eno), nao);
+ }
}
- free(utf8mode);
- /* TODO: catch potential OOM exception here and kill process. */
- ret = set_mode_props(m, make_pipevp_stream(f, name, pid));
+ uw_catch_end;
}
- uw_unwind {
- fds_restore(&sfds);
- }
-
- uw_catch_end;
-
return ret;
}
@@ -4287,99 +4664,24 @@ val open_process(val name, val mode_str, val args)
{
return open_subprocess(name, mode_str, args, nil);
}
-#else
-
-static void string_extend_count(int count, val out, val tail)
-{
- int i;
- for (i = 0; i < count; i++)
- string_extend(out, tail);
-}
-
-static val win_escape_cmd(val str)
-{
- const wchar_t *s;
- val out = string(L"");
-
- for (s = c_str(str); *s; s++) {
- switch (*s) {
- case ' ': case '\t':
- string_extend(out, lit("\""));
- string_extend(out, chr(*s));
- string_extend(out, lit("\""));
- break;
- default:
- string_extend(out, chr(*s));
- }
- }
-
- return out;
-}
-static val win_escape_arg(val str)
+val open_command(val command, val mode_str)
{
- int bscount = 0, i;
- const wchar_t *s;
- val out = string(L"");
-
- for (s = c_str(str); *s; s++) {
- switch (*s) {
- case '"':
- string_extend_count(bscount, out, lit("\\\\"));
- string_extend(out, lit("\\^\""));
- bscount = 0;
- break;
- case '\\':
- bscount++;
- break;
- case '^': case '%': case '!':
- case '\n': case '&': case '|':
- case '<': case '>':
- case '(': case ')':
- for (i = 0; i < bscount; i++)
- string_extend_count(bscount, out, lit("\\"));
- string_extend(out, chr('^'));
- string_extend(out, chr(*s));
- break;
- default:
- for (i = 0; i < bscount; i++)
- string_extend_count(bscount, out, lit("\\"));
- string_extend(out, chr(*s));
- bscount = 0;
- break;
- }
- }
-
- for (i = 0; i < bscount; i++)
- string_extend(out, lit("\\"));
-
- return out;
-}
-
-static val win_make_cmdline(val args)
-{
- val out = string(L"");
-
- string_extend(out, win_escape_cmd(pop(&args)));
- string_extend(out, chr(' '));
-
- while (args) {
- string_extend(out, lit("^\""));
- string_extend(out, win_escape_arg(pop(&args)));
- if (args)
- string_extend(out, lit("^\" "));
- else
- string_extend(out, lit("^\""));
- }
-
- return out;
+#ifdef __CYGWIN__
+ uses_or2;
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ val interp = if3(psc[0] == '\\',
+ or2(getenv_wrap(lit("COMSPEC")),
+ lit("C:\\WINDOWS\\system32\\cmd.exe")),
+ lit("/bin/sh"));
+ val opt = if3(psc[0] == '\\', lit("/c"), lit("-c"));
+#else
+ val interp = lit("/bin/sh");
+ val opt = lit("-c");
+#endif
+ return open_process(interp, mode_str, list(opt, command, nao));
}
-val open_process(val name, val mode_str, val args)
-{
- val win_cmdline = win_make_cmdline(cons(name, default_null_arg(args)));
- return open_command(win_cmdline, mode_str);
-}
#endif
#if HAVE_WSPAWN || HAVE_SPAWN
@@ -4415,36 +4717,60 @@ static val run(val command, val args)
val iter;
int i, nargs, status = 0;
struct save_fds sfds;
+ volatile val save_env, ch_env = child_env;
args = default_null_arg(args);
- nargs = c_num(length(args)) + 1;
+ nargs = c_num(length(args), self) + 1;
+
+ flush_stream(std_output);
fds_init(&sfds);
uw_simple_catch_begin;
- fds_swizzle(&sfds, FDS_IN | FDS_OUT | FDS_ERR);
+ fds_prepare(&sfds, FDS_IN | FDS_OUT | FDS_ERR, self);
+
+ fds_swizzle(&sfds, FDS_IN | FDS_OUT | FDS_ERR, self);
if (nargs < 0 || nargs == INT_MAX)
uw_throwf(error_s, lit("~a: argument list overflow"), self, nao);
+ if (ch_env != t) {
+ save_env = env();
+ replace_env(ch_env);
+ }
+
wargv = coerce(const wchar_t **, chk_xalloc(nargs + 1, sizeof *wargv, self));
for (i = 0, iter = cons(command, args); iter; i++, iter = cdr(iter))
- wargv[i] = c_str(car(iter));
+ wargv[i] = c_str(car(iter), self);
wargv[i] = 0;
+ if (status == 0) {
#if HAVE_WSPAWN
- status = _wspawnvp(_P_WAIT, c_str(command), wargv);
+ status = _wspawnvp(_P_WAIT, c_str(command, self), wargv);
#else
- status = w_spawnvp(_P_WAIT, c_str(command), nargs, wargv);
+ status = w_spawnvp(_P_WAIT, c_str(command, self), nargs, wargv);
#endif
+#ifdef __CYGWIN__
+ /* Cygwin spawnvp reports regular termination status in upper 8 bits, and
+ * termination signal in lower 8 bits. Let's massage it so that we produce
+ * the same behavior as on Linux.
+ */
+ if (status && status < 0x100)
+ status = -1; /* ensure nil return */
+ else
+ status >>= 8;
+#endif
+ }
free(strip_qual(wchar_t **, wargv));
gc_hint(args);
uw_unwind {
+ if (ch_env != t)
+ replace_env(save_env);
fds_restore(&sfds);
}
@@ -4453,11 +4779,6 @@ static val run(val command, val args)
return (status < 0) ? nil : num(status);
}
-static val sh(val command)
-{
- return run(lit("cmd.exe"), list(lit("/C"), command, nao));
-}
-
#elif HAVE_FORK_STUFF
static val run(val name, val args)
@@ -4471,7 +4792,7 @@ static val run(val name, val args)
val ret = nil;
args = default_null_arg(args);
- nargs = c_num(length(args)) + 1;
+ nargs = c_num(length(args), self) + 1;
if (nargs < 0 || nargs == INT_MAX)
uw_throwf(error_s, lit("~a: argument list overflow"), self, nao);
@@ -4480,15 +4801,15 @@ static val run(val name, val args)
for (i = 0, iter = cons(name, args); iter; i++, iter = cdr(iter)) {
val arg = car(iter);
- argv[i] = utf8_dup_to(c_str(arg));
+ argv[i] = utf8_dup_to(c_str(arg, self));
}
argv[i] = 0;
- fds_init(&sfds);
+ flush_stream(std_output);
- uw_simple_catch_begin;
+ fds_init(&sfds);
- fds_swizzle(&sfds, FDS_IN | FDS_OUT | FDS_ERR);
+ fds_prepare(&sfds, FDS_IN | FDS_OUT | FDS_ERR, self);
pid = fork();
@@ -4496,11 +4817,15 @@ static val run(val name, val args)
for (i = 0; i < nargs; i++)
free(argv[i]);
free(argv);
- uw_throwf(process_error_s, lit("opening process ~s, fork syscall failed: ~d/~s"),
- name, num(errno), string_utf8(strerror(errno)), nao);
+ uw_ethrowf(process_error_s, lit("opening process ~s, fork syscall failed: ~d/~s"),
+ name, num(errno), errno_to_str(errno), nao);
}
if (pid == 0) {
+ val ch_env = child_env;
+ fds_clobber(&sfds, FDS_IN | FDS_OUT | FDS_ERR);
+ if (ch_env != t)
+ replace_env(ch_env);
execvp(argv[0], argv);
_exit(errno);
} else {
@@ -4523,31 +4848,80 @@ static val run(val name, val args)
}
out:
- uw_unwind {
- fds_restore(&sfds);
- }
-
- uw_catch_end;
-
return ret;
}
+#endif
+
+#if HAVE_WSPAWN || HAVE_SPAWN || HAVE_FORK_STUFF
+
static val sh(val command)
{
return run(shell, list(shell_arg, command, nao));
}
-#else
-#error port me!
#endif
+static val sh_esc_dq(val string)
+{
+ return str_esc(lit("$`\\\""), chr('\\'), string);
+}
+
+static val sh_esc_sq(val string)
+{
+ return str_esc(lit("'"), lit("'\\'"), string);
+}
+
+static val sh_esc_common(val string, int all, val self)
+{
+ const wchar_t *s, *str = c_str(string, self);
+ int sq = 0, dq = 0, es = 0;
+
+ for (s = str; *s; s++)
+ {
+ wchar_t ch = *s;
+
+ if (ch == '\'')
+ es = sq = 1;
+ else if (wcschr(L"$`\\\"", ch))
+ es = dq = 1;
+ else if (wcschr(L"|&;<>() \t\n*?[#~", ch))
+ es = 1;
+ else if (all && wcschr(L"=%", ch))
+ es = 1;
+ }
+
+ if (!es)
+ return string;
+
+ if (!dq)
+ return scat3(chr('"'), string, chr('"'));
+
+ if (!sq)
+ return scat3(chr('\''), string, chr('\''));
+
+ return scat3(chr('\''), sh_esc_sq(string), chr('\''));
+}
+
+static val sh_esc(val string)
+{
+ return sh_esc_common(string, 0, lit("sh-esc"));
+}
+
+static val sh_esc_all(val string)
+{
+ return sh_esc_common(string, 1, lit("sh-esc-all"));
+}
+
val remove_path(val path, val throw_on_error)
{
- if (w_remove(c_str(path)) < 0) {
+ val self = lit("remove-path");
+
+ if (w_remove(c_str(path, self)) < 0) {
if (default_null_arg(throw_on_error) || errno != ENOENT) {
int eno = errno;
- uw_throwf(errno_to_file_error(eno), lit("trying to remove ~s: ~d/~s"),
- path, num(eno), string_utf8(strerror(eno)), nao);
+ uw_ethrowf(errno_to_file_error(eno), lit("trying to remove ~s: ~d/~s"),
+ path, num(eno), errno_to_str(eno), nao);
}
return nil;
}
@@ -4557,11 +4931,13 @@ val remove_path(val path, val throw_on_error)
val rename_path(val from, val to)
{
- if (w_rename(c_str(from), c_str(to)) < 0) {
+ val self = lit("rename-path");
+
+ if (w_rename(c_str(from, self), c_str(to, self)) < 0) {
int eno = errno;
- uw_throwf(errno_to_file_error(eno),
- lit("trying to rename ~s to ~s: ~d/~s"),
- from, to, num(eno), string_utf8(strerror(eno)), nao);
+ uw_ethrowf(errno_to_file_error(eno),
+ lit("trying to rename ~s to ~s: ~d/~s"),
+ from, to, num(eno), errno_to_str(eno), nao);
}
return t;
@@ -4597,50 +4973,91 @@ static val open_files_star(val file_list, val substitute_stream, val mode)
}
}
-static val ap_regex;
+static val volume_prefix_p(const wchar_t *str)
+{
+ enum { init, slash } state;
-val abs_path_p(val path)
+ for (state = init; *str; str++) {
+ wchar_t ch = *str;
+ switch (state) {
+ case init:
+ if (iswalnum(ch))
+ continue;
+ if (ch == ':') {
+ state = slash;
+ continue;
+ }
+ return nil;
+ case slash:
+ if (ch == '/' || ch == '\\')
+ return t;
+ return nil;
+ }
+ }
+
+ return nil;
+}
+
+static val volume_name_p(const wchar_t *str)
{
- val ch;
+ for (; *str; str++) {
+ if (iswalnum(*str))
+ continue;
+ if (*str == ':')
+ return t;
+ break;
+ }
+
+ return nil;
+}
- if (length(path) == zero)
+val portable_abs_path_p(val path)
+{
+ val self = lit("portable-abs-path-p");
+ const wchar_t *str = c_str(path, self);
+
+ if (*str == 0)
return nil;
- if ((ch = chr_str(path, zero)) == chr('/') || ch == chr('\\'))
+ if (str[0] == '/' || str[0] == '\\')
return t;
+ return volume_prefix_p(str);
+}
- if (!ap_regex)
- ap_regex = regex_compile(lit("[A-Za-z0-9]+:[/\\\\]"), nil);
+val abs_path_p(val path)
+{
+ val self = lit("abs-path-p");
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ const wchar_t *str = c_str(path, self);
- if (match_regex(path, ap_regex, zero))
+ if (*str == 0)
+ return nil;
+ if (wcschr(psc, str[0]))
return t;
- return nil;
-}
+ if (psc[0] != '\\')
+ return nil;
-static val plp_regex;
+ return volume_prefix_p(str);
+}
val pure_rel_path_p(val path)
{
- val ch;
- val len = length_str(path);
+ val self = lit("pure-rel-path-p");
+ const wchar_t *str = c_str(path, self);
- if (len == zero)
+ if (str[0] == 0)
return t;
- if ((ch = chr_str(path, zero)) == chr('/') || ch == chr('\\'))
+ if (str[0] == '/' || str[0] == '\\')
return nil;
- if (len == one)
- return ch == chr('.') ? nil : t;
+ if (str[1] == 0)
+ return str[0] == '.' ? nil : t;
- if (ch == chr('.') &&
- ((ch = chr_str(path, one)) == chr('/') || ch == chr('\\')))
+ if (str[0] == '.' && (str[1] == '/' || str[1] == '\\'))
return nil;
- if (!plp_regex)
- plp_regex = regex_compile(lit("[A-Za-z0-9]+:"), nil);
-
- if (match_regex(path, plp_regex, zero))
+ if (volume_name_p(str))
return nil;
return t;
@@ -4652,19 +5069,21 @@ static void detect_path_separators(void)
struct utsname un;
if (uname(&un) >= 0) {
- if (strncmp(un.sysname, "CYGNAL", 6) == 0)
+ if (strncmp(un.sysname, "CYGNAL", 6) == 0) {
path_sep_chars = wli("\\/");
- return;
+ path_var_sep_char = ';';
+ }
}
#endif
}
val base_name(val path, val suff)
{
- const wchar_t *wpath = c_str(path);
- const wchar_t *end = wpath + c_num(length_str(path));
+ val self = lit("base-name");
+ const wchar_t *wpath = c_str(path, self);
+ const wchar_t *end = wpath + c_num(length_str(path), self);
const wchar_t *rsep;
- const wchar_t *psc = wref(coerce(const wchar_t *, path_sep_chars));
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
if (end == wpath)
return null_string;
@@ -4683,9 +5102,9 @@ val base_name(val path, val suff)
{
val base = mkustring(num_fast(end - rsep));
- init_str(base, rsep);
+ init_str(base, rsep, self);
return if3(!null_or_missing_p(suff) && ends_with(suff, base, nil, nil) &&
- neql(length(suff), length(base)),
+ plusp(length(suff)) && neql(length(suff), length(base)),
sub(base, zero, neg(length(suff))),
base);
}
@@ -4693,9 +5112,10 @@ val base_name(val path, val suff)
val dir_name(val path)
{
- const wchar_t *wpath = c_str(path);
- const wchar_t *rsep = wpath + c_num(length_str(path));
- const wchar_t *psc = wref(coerce(const wchar_t *, path_sep_chars));
+ val self = lit("dir-name");
+ const wchar_t *wpath = c_str(path, self);
+ const wchar_t *rsep = wpath + c_num(length_str(path), self);
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
if (rsep == wpath)
return lit(".");
@@ -4722,14 +5142,187 @@ val dir_name(val path)
{
val base = mkustring(num_fast(rsep - wpath - 1));
- return init_str(base, wpath);
+ return init_str(base, wpath, self);
+ }
+}
+
+val short_suffix(val name, val alt_in)
+{
+ val self = lit("short-suffix");
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ const wchar_t *str = c_str(name, self);
+ const wchar_t *dot = wcsrchr(str, '.');
+ const wchar_t *sl = if3(dot, wcspbrk(dot + 1, psc), 0);
+ int sl_trail = if3(sl, sl[wcsspn(sl, psc)] == 0, 0);
+
+ if (!dot || (sl && sl[1] && !sl_trail) || dot == str || wcschr(psc, dot[-1])) {
+ return default_null_arg(alt_in);
+ } else {
+ wchar_t *suff = chk_strdup(dot);
+ if (sl)
+ suff[sl - dot] = 0;
+ return string_own(suff);
+ }
+}
+
+val long_suffix(val name, val alt_in)
+{
+ val self = lit("long-suffix");
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ const wchar_t *str = c_str(name, self);
+ const wchar_t *dot = wcschr(str, '.');
+
+ {
+ const wchar_t *sl;
+
+ while (dot && (sl = wcspbrk(dot, psc)) && sl[1] && sl[wcsspn(sl, psc)] != 0)
+ dot = wcschr(sl + 1, '.');
+
+ if (dot && (dot == str || wcschr(psc, dot[-1])))
+ dot = wcschr(dot + 1, '.');
+
+ if (!dot || dot == str) {
+ return default_null_arg(alt_in);
+ } else {
+ wchar_t *suff = chk_strdup(dot);
+ if (sl)
+ suff[sl - dot] = 0;
+ return string_own(suff);
+ }
+ }
+}
+
+val trim_short_suffix(val name)
+{
+ val self = lit("trim-short-suffix");
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ const wchar_t *str = c_str(name, self);
+ const wchar_t *dot = wcsrchr(str, '.');
+ const wchar_t *sl = if3(dot, wcspbrk(dot + 1, psc), 0);
+ int sl_trail = if3(sl, sl[wcsspn(sl, psc)] == 0, 0);
+
+ if (!dot || (sl && sl[1] && !sl_trail) || dot == str || wcschr(psc, dot[-1])) {
+ return name;
+ } else {
+ size_t off = dot - str;
+ if (sl) {
+ size_t slsz = wcslen(sl) + 1;
+ size_t nchar = off + slsz;
+ wchar_t *out = chk_wmalloc(nchar);
+ wmemcpy(out, str, off);
+ wmemcpy(out + off, sl, slsz);
+ return string_own(out);
+ } else {
+ wchar_t *pref = chk_substrdup(str, 0, dot - str);
+ return string_own(pref);
+ }
+ }
+}
+
+val trim_long_suffix(val name)
+{
+ val self = lit("trim-long-suffix");
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ const wchar_t *str = c_str(name, self);
+ const wchar_t *dot = wcschr(str, '.');
+
+ {
+ const wchar_t *sl;
+
+ while (dot && (sl = wcspbrk(dot, psc)) && sl[1] && sl[wcsspn(sl, psc)] != 0)
+ dot = wcschr(sl + 1, '.');
+
+ if (dot && (dot == str || wcschr(psc, dot[-1])))
+ dot = wcschr(dot + 1, '.');
+
+ if (!dot || dot == str) {
+ return name;
+ } else {
+ size_t off = dot - str;
+ if (sl) {
+ size_t slsz = wcslen(sl) + 1;
+ size_t nchar = off + slsz;
+ wchar_t *out = chk_wmalloc(nchar);
+ wmemcpy(out, str, off);
+ wmemcpy(out + off, sl, slsz);
+ return string_own(out);
+ } else {
+ wchar_t *pref = chk_substrdup(str, 0, dot - str);
+ return string_own(pref);
+ }
+ }
+ }
+}
+
+val trim_path_seps(val name)
+{
+ val self = lit("trim-path-seps");
+ const wchar_t *str = c_str(name, self);
+ const wchar_t *psc = L"/\\";
+ const wchar_t *fsl = 0;
+ cnum len = c_num(length_str(name), self);
+
+ if (portable_abs_path_p(name))
+ fsl = wcspbrk(str, psc);
+
+ while (len-- > 0)
+ if (!wcschr(psc, str[len]) || str + len == fsl)
+ break;
+
+ return string_own(chk_substrdup(str, 0, len + 1));
+}
+
+val add_suffix(val name, val suffix)
+{
+ val self = lit("add-suffix");
+ size_t len_n = c_unum(length_str(name), self);
+ size_t len_s = c_unum(length_str(suffix), self);
+ const wchar_t *psc = coerce(const wchar_t *, path_sep_chars);
+ const wchar_t *nam = c_str(name, self);
+ const wchar_t *suf = c_str(suffix, self);
+ const wchar_t *sl = wcspbrk(nam, psc);
+
+ if (psc[0] == '\\' || 1) {
+ const wchar_t *set = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ L"abcdefghijklmnopqrstuvwxyz"
+ L"0123456789";
+ const wchar_t *drv = nam + wcsspn(nam, set);
+ if (drv[0] == ':' && sl == drv + 1) {
+ if (drv - nam > 1) {
+ if (wcschr(psc, drv[2]))
+ sl = wcspbrk(drv + 3, psc);
+ } else if (drv > nam) {
+ sl = wcspbrk(drv + 2, psc);
+ }
+ }
+ }
+
+ if (sl == nam)
+ sl = wcspbrk(nam + 1, psc);
+
+ while (sl) {
+ const wchar_t *nsl = sl + 1 + wcsspn(sl + 1, psc);
+
+ if (*nsl == 0) {
+ size_t nchar = len_n + len_s + 1;
+ size_t offs = sl - nam;
+ wchar_t *out = chk_wmalloc(nchar);
+ wmemcpy(out, nam, offs);
+ wmemcpy(out + offs, suf, len_s);
+ wcscpy(out + offs + len_s, sl);
+ return string_own(out);
+ }
+
+ sl = wcspbrk(nsl, psc);
}
+
+ return scat2(name, suffix);
}
val path_cat(val dir_name, val base_name)
{
- val dl = length(dir_name);
- val bl = length(base_name);
+ val dl = length_str(dir_name);
+ val bl = length_str(base_name);
val ps = static_str(path_sep_chars);
if (dl == zero)
@@ -4765,6 +5358,25 @@ val path_cat(val dir_name, val base_name)
return scat(lit("/"), dir_name, base_name, nao);
}
+static val path_vcat(varg args)
+{
+ cnum ix = 0;
+
+ if (!args_more(args, ix)) {
+ return lit(".");
+ } else {
+ val accum = args_get(args, &ix);
+
+ if (!stringp(accum))
+ uw_throwf(file_error_s, lit("path-cat: ~s isn't a string"), accum, nao);
+
+ while (args_more(args, ix))
+ accum = path_cat(accum, args_get(args, &ix));
+
+ return accum;
+ }
+}
+
val make_byte_input_stream(val obj)
{
val self = lit("make-byte-input-stream");
@@ -4782,6 +5394,72 @@ val make_byte_input_stream(val obj)
}
}
+val tmpfile_wrap(void)
+{
+ val self = lit("tmpfile");
+ struct stdio_mode m_blank = stdio_mode_init_blank;
+ struct stdio_mode m = do_parse_mode(lit("w+b"), m_blank, self);
+ FILE *tf = tmpfile();
+ if (tf != 0)
+ return set_mode_props(m, make_stdio_stream(tf, self));
+ uw_ethrowf(file_error_s, lit("~a failed: ~d/~s"),
+ self, num(errno), errno_to_str(errno), nao);
+}
+
+#if HAVE_MKDTEMP
+
+val mkdtemp_wrap(val prefix)
+{
+ val self = lit("mkdtemp");
+ char *tmpl = utf8_dup_to(c_str(scat2(prefix, lit("XXXXXX")), self));
+
+ if (mkdtemp(tmpl) != 0) {
+ val ret = string_utf8(tmpl);
+ free(tmpl);
+ return ret;
+ }
+
+ free(tmpl);
+ uw_ethrowf(file_error_s, lit("mkdtemp failed: ~d/~s"),
+ num(errno), errno_to_str(errno), nao);
+}
+
+#endif
+
+#if HAVE_MKSTEMP
+
+val mkstemp_wrap(val prefix, val suffix)
+{
+ val self = lit("mkstemp");
+ val suff = default_arg_strict(suffix, null_string);
+ val templ = scat3(prefix, lit("XXXXXX"), suff);
+ cnum slen = c_num(length(suff), self);
+ char *tmpl = utf8_dup_to(c_str(templ, self));
+ val name;
+ int fd;
+
+#if HAVE_MKSTEMPS
+ fd = mkstemps(tmpl, slen);
+#else
+ if (slen > 0) {
+ free(tmpl);
+ uw_throwf(system_error_s, lit("~a: suffix not supported"), self, nao);
+ }
+ fd = mkstemp(tmpl);
+#endif
+ name = string_utf8(tmpl);
+ free(tmpl);
+ if (fd != -1) {
+ val stream = open_fileno(num(fd), lit("w+b"), nil);
+ stream_set_prop(stream, name_k, name);
+ return stream;
+ }
+ uw_ethrowf(file_error_s, lit("~a failed: ~d/~s"),
+ self, num(errno), errno_to_str(errno), nao);
+}
+
+#endif
+
static val iobuf_free_list;
val iobuf_get(void)
@@ -4811,8 +5489,7 @@ void iobuf_list_empty(void)
void stream_init(void)
{
- prot1(&ap_regex);
- prot1(&plp_regex);
+ prot1(&top_stderr);
detect_format_string();
detect_path_separators();
@@ -4825,6 +5502,7 @@ void stream_init(void)
addr_k = intern(lit("addr"), keyword_package);
fd_k = intern(lit("fd"), keyword_package);
byte_oriented_k = intern(lit("byte-oriented"), keyword_package);
+ standard_k = intern(lit("standard"), keyword_package);
format_s = intern(lit("format"), user_package);
stdio_stream_s = intern(lit("stdio-stream"), user_package);
#if HAVE_SOCKETS
@@ -4851,6 +5529,9 @@ void stream_init(void)
clear_error_s = intern(lit("clear-error"), user_package);
get_fd_s = intern(lit("get-fd"), user_package);
+ stream_cls = cobj_register(stream_s);
+ stdio_stream_cls = cobj_register_super(stdio_stream_s, stream_cls);
+
reg_var(stdin_s = intern(lit("*stdin*"), user_package),
make_stdio_stream(stdin, lit("stdin")));
reg_var(stdout_s = intern(lit("*stdout*"), user_package),
@@ -4858,7 +5539,7 @@ void stream_init(void)
reg_var(stddebug_s = intern(lit("*stddebug*"), user_package),
make_stdio_stream(stdout, lit("debug")));
reg_var(stderr_s = intern(lit("*stderr*"), user_package),
- make_stdio_stream(stderr, lit("stderr")));
+ top_stderr = make_stdio_stream(stderr, lit("stderr")));
reg_var(stdnull_s = intern(lit("*stdnull*"), user_package),
make_null_stream());
@@ -4877,6 +5558,7 @@ void stream_init(void)
reg_var(print_base_s = intern(lit("*print-base*"), user_package),
num_fast(10));
reg_var(print_circle_s = intern(lit("*print-circle*"), user_package), nil);
+ reg_var(print_json_format_s = intern(lit("*print-json-format*"), user_package), nil);
#if HAVE_ISATTY
if (isatty(fileno(stdin)) == 1) {
@@ -4913,6 +5595,7 @@ void stream_init(void)
reg_fun(unget_byte_s, func_n2o(unget_byte, 1));
reg_fun(put_buf_s, func_n3o(put_buf, 1));
reg_fun(fill_buf_s, func_n3o(fill_buf, 1));
+ reg_fun(intern(lit("get-line-as-buf"), user_package), func_n1o(get_line_as_buf, 0));
reg_fun(intern(lit("fill-buf-adjust"), user_package), func_n3o(fill_buf_adjust, 1));
reg_fun(intern(lit("flush-stream"), user_package), func_n1o(flush_stream, 0));
reg_fun(intern(lit("seek-stream"), user_package), func_n3(seek_stream));
@@ -4935,25 +5618,39 @@ void stream_init(void)
reg_fun(intern(lit("record-adapter"), user_package), func_n3o(record_adapter, 1));
reg_fun(intern(lit("open-directory"), user_package), func_n1(open_directory));
reg_fun(intern(lit("open-file"), user_package), func_n2o(open_file, 1));
- reg_fun(intern(lit("open-fileno"), user_package), func_n2o(open_fileno, 1));
+ reg_fun(intern(lit("open-fileno"), user_package), func_n3o(open_fileno, 1));
reg_fun(intern(lit("open-tail"), user_package), func_n3o(open_tail, 1));
+ reg_fun(intern(lit("path-search"), user_package), func_n2o(path_search, 1));
reg_fun(intern(lit("open-command"), user_package), func_n2o(open_command, 1));
reg_fun(intern(lit("open-pipe"), user_package), func_n2(open_command));
reg_fun(intern(lit("open-process"), user_package), func_n3o(open_process, 2));
#if HAVE_FORK_STUFF
reg_fun(intern(lit("open-subprocess"), user_package), func_n4o(open_subprocess, 2));
#endif
+#if HAVE_WSPAWN || HAVE_SPAWN || HAVE_FORK_STUFF
reg_fun(intern(lit("sh"), user_package), func_n1(sh));
reg_fun(intern(lit("run"), user_package), func_n2o(run, 1));
+#endif
+ reg_fun(intern(lit("sh-esc"), user_package), func_n1(sh_esc));
+ reg_fun(intern(lit("sh-esc-all"), user_package), func_n1(sh_esc_all));
+ reg_fun(intern(lit("sh-esc-dq"), user_package), func_n1(sh_esc_dq));
+ reg_fun(intern(lit("sh-esc-sq"), user_package), func_n1(sh_esc_sq));
reg_fun(intern(lit("remove-path"), user_package), func_n2o(remove_path, 1));
reg_fun(intern(lit("rename-path"), user_package), func_n2(rename_path));
reg_fun(intern(lit("open-files"), user_package), func_n3o(open_files, 1));
reg_fun(intern(lit("open-files*"), user_package), func_n3o(open_files_star, 1));
+ reg_fun(intern(lit("portable-abs-path-p"), user_package), func_n1(portable_abs_path_p));
reg_fun(intern(lit("abs-path-p"), user_package), func_n1(abs_path_p));
reg_fun(intern(lit("pure-rel-path-p"), user_package), func_n1(pure_rel_path_p));
reg_fun(intern(lit("base-name"), user_package), func_n2o(base_name, 1));
reg_fun(intern(lit("dir-name"), user_package), func_n1(dir_name));
- reg_fun(intern(lit("path-cat"), user_package), func_n2(path_cat));
+ reg_fun(intern(lit("short-suffix"), user_package), func_n2o(short_suffix, 1));
+ reg_fun(intern(lit("long-suffix"), user_package), func_n2o(long_suffix, 1));
+ reg_fun(intern(lit("trim-short-suffix"), user_package), func_n1(trim_short_suffix));
+ reg_fun(intern(lit("trim-long-suffix"), user_package), func_n1(trim_long_suffix));
+ reg_fun(intern(lit("trim-path-seps"), user_package), func_n1(trim_path_seps));
+ reg_fun(intern(lit("path-cat"), user_package), func_n0v(path_vcat));
+ reg_fun(intern(lit("add-suffix"), user_package), func_n2(add_suffix));
reg_varl(intern(lit("path-sep-chars"), user_package), static_str(path_sep_chars));
reg_fun(intern(lit("get-indent-mode"), user_package), func_n1(get_indent_mode));
reg_fun(intern(lit("test-set-indent-mode"), user_package), func_n3(test_set_indent_mode));
@@ -4962,6 +5659,7 @@ void stream_init(void)
reg_fun(intern(lit("get-indent"), user_package), func_n1(get_indent));
reg_fun(intern(lit("set-indent"), user_package), func_n2(set_indent));
reg_fun(intern(lit("inc-indent"), user_package), func_n2(inc_indent));
+ reg_fun(intern(lit("inc-indent-abs"), user_package), func_n2(inc_indent_abs));
reg_fun(intern(lit("width-check"), user_package), func_n2(width_check));
reg_fun(intern(lit("force-break"), user_package), func_n1(force_break));
reg_fun(intern(lit("set-max-length"), user_package), func_n2(set_max_length));
@@ -4970,6 +5668,13 @@ void stream_init(void)
reg_varl(intern(lit("indent-data"), user_package), num_fast(indent_data));
reg_varl(intern(lit("indent-code"), user_package), num_fast(indent_code));
reg_varl(intern(lit("indent-foff"), user_package), num_fast(indent_foff));
+ reg_fun(intern(lit("tmpfile"), user_package), func_n0(tmpfile_wrap));
+#if HAVE_MKDTEMP
+ reg_fun(intern(lit("mkdtemp"), user_package), func_n1(mkdtemp_wrap));
+#endif
+#if HAVE_MKSTEMP
+ reg_fun(intern(lit("mkstemp"), user_package), func_n2o(mkstemp_wrap, 1));
+#endif
#if HAVE_SOCKETS
uw_register_subtype(socket_error_s, error_s);
@@ -4978,7 +5683,9 @@ void stream_init(void)
fill_stream_ops(&null_ops);
fill_stream_ops(&stdio_ops);
fill_stream_ops(&tail_ops);
+#if HAVE_FORK_STUFF
fill_stream_ops(&pipe_ops);
+#endif
fill_stream_ops(&string_in_ops);
fill_stream_ops(&byte_in_ops);
fill_stream_ops(&strlist_in_ops);
@@ -5017,4 +5724,15 @@ void stream_init(void)
}
}
#endif
+
+#if HAVE_ZLIB
+ gzio_init();
+#endif
+}
+
+void stream_compat_fixup(int compat_ver)
+{
+ if (compat_ver <= 258)
+ reg_fun(intern(lit("abs-path-p"), user_package),
+ func_n1(portable_abs_path_p));
}