~sircmpwn/hare-dev

hare: all: Use optional parameters for flags v1 APPLIED

Alexey Yerin: 1
 all: Use optional parameters for flags

 22 files changed, 115 insertions(+), 190 deletions(-)
#1192295 alpine.yml success
#1192296 freebsd.yml success
#1192297 openbsd.yml success
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/hare-dev/patches/50881/mbox | git am -3
Learn more about email & git

[PATCH hare] all: Use optional parameters for flags Export this patch

Signed-off-by: Alexey Yerin <yyp@disroot.org>
---
 fnmatch/+test.ha       |  6 ++++-
 fnmatch/fnmatch.ha     | 12 +++------
 fs/fs.ha               | 34 ++++++++++++------------
 fs/types.ha            |  8 +++---
 glob/glob.ha           |  8 ++----
 hare/lex/+test.ha      |  8 +++---
 hare/lex/lex.ha        | 12 ++++-----
 net/+freebsd.ha        | 11 +++-----
 net/+linux.ha          | 11 +++-----
 net/+openbsd.ha        | 11 +++-----
 net/tcp/listener.ha    |  4 +--
 net/unix/listener.ha   |  4 +--
 net/unix/socketpair.ha | 11 +++-----
 os/+freebsd/dirfdfs.ha | 29 +++++++--------------
 os/+linux/dirfdfs.ha   | 59 +++++++++++++++++-------------------------
 os/+openbsd/dirfdfs.ha | 29 +++++++--------------
 os/os.ha               | 14 ++++------
 unix/+freebsd/pipe.ha  | 10 +++----
 unix/+linux/pipe.ha    | 10 +++----
 unix/+openbsd/pipe.ha  | 10 +++----
 wordexp/+test.ha       |  2 +-
 wordexp/wordexp.ha     |  2 +-
 22 files changed, 115 insertions(+), 190 deletions(-)

diff --git a/fnmatch/+test.ha b/fnmatch/+test.ha
index 440eb93b..0bd219d2 100644
--- a/fnmatch/+test.ha
+++ b/fnmatch/+test.ha
@@ -186,7 +186,11 @@ const testcases: [_]testcase = [

@test fn fnmatch() void = {
	for (let tc .. testcases) {
		assert(fnmatch(tc.0, tc.1, tc.3...) == tc.2);
		let flags = flag::NONE;
		for (let fl .. tc.3) {
			flags |= fl;
		};
		assert(fnmatch(tc.0, tc.1, flags) == tc.2);
	};

};
diff --git a/fnmatch/fnmatch.ha b/fnmatch/fnmatch.ha
index 3f240e71..49cf85ad 100644
--- a/fnmatch/fnmatch.ha
+++ b/fnmatch/fnmatch.ha
@@ -43,15 +43,11 @@ type token = (rune | bracket | star | question | end);
//
// A set of flags that alter the matching behavior may be passed to
// [[fnmatch]]. For an explanation of their meaning, see [[flag]].
export fn fnmatch(pattern: str, string: str, flags: flag...) bool = {
	let fl: flag = 0;
	for (let flag .. flags) {
		fl |= flag;
	};
	let b = if (fl & flag::PATHNAME != 0) {
		yield fnmatch_pathname(pattern, string, fl);
export fn fnmatch(pattern: str, string: str, flags: flag = flag::NONE) bool = {
	let b = if (flags & flag::PATHNAME != 0) {
		yield fnmatch_pathname(pattern, string, flags);
	} else {
		yield fnmatch_internal(pattern, string, fl);
		yield fnmatch_internal(pattern, string, flags);
	};
	return b is bool && b: bool;
};
diff --git a/fs/fs.ha b/fs/fs.ha
index 4b1667bd..3e96c29d 100644
--- a/fs/fs.ha
+++ b/fs/fs.ha
@@ -16,16 +16,18 @@ export fn close(fs: *fs) void = {

// Opens a file.
//
// If no flags are provided, [[flag::RDONLY]] is used when opening the file.
//
// [[flag::CREATE]] isn't very useful with this function, since the new file's
// mode is set to zero. For this use-case, use [[create]] instead.
export fn open(fs: *fs, path: str, flags: flag...) (io::handle | error) = {
export fn open(
	fs: *fs,
	path: str,
	flags: flag = flag::RDONLY,
) (io::handle | error) = {
	match (fs.open) {
	case null =>
		return errors::unsupported;
	case let f: *openfunc =>
		return f(fs, path, flags...);
		return f(fs, path, flags);
	};
};

@@ -33,35 +35,34 @@ export fn open(fs: *fs, path: str, flags: flag...) (io::handle | error) = {
// handle on the host operating system, which may not be possible with all
// filesystem implementations (such cases will return [[io::unsupported]]).
//
// If no flags are provided, [[flag::RDONLY]] is used when opening the file.
//
// [[flag::CREATE]] isn't very useful with this function, since the new file's
// mode is set to zero. For this use-case, use [[create_file]] instead.
export fn open_file(fs: *fs, path: str, flags: flag...) (io::file | error) = {
export fn open_file(
	fs: *fs,
	path: str,
	flags: flag = flag::RDONLY,
) (io::file | error) = {
	match (fs.openfile) {
	case null =>
		return errors::unsupported;
	case let f: *openfilefunc =>
		return f(fs, path, flags...);
		return f(fs, path, flags);
	};
};

// Creates a new file with the given mode if it doesn't already exist, and opens
// it for writing.
//
// If no flags are provided, [[flag::WRONLY]] and [[flag::TRUNC]] are used when
// opening the file.
export fn create(
	fs: *fs,
	path: str,
	mode: mode,
	flags: flag...
	flags: flag = flag::WRONLY | flag::TRUNC,
) (io::handle | error) = {
	match (fs.create) {
	case null =>
		return errors::unsupported;
	case let f: *createfunc =>
		return f(fs, path, mode, flags...);
		return f(fs, path, mode, flags);
	};
};

@@ -69,20 +70,17 @@ export fn create(
// it as an [[io::file]] for writing. This file will be backed by an open file
// handle on the host operating system, which may not be possible with all
// filesystem implementations (such cases will return [[io::unsupported]]).
//
// If no flags are provided, [[flag::WRONLY]] and [[flag::TRUNC]] are used when
// opening the file.
export fn create_file(
	fs: *fs,
	path: str,
	mode: mode,
	flags: flag...
	flags: flag = flag::WRONLY | flag::TRUNC,
) (io::file | error) = {
	match (fs.createfile) {
	case null =>
		return errors::unsupported;
	case let f: *createfilefunc =>
		return f(fs, path, mode, flags...);
		return f(fs, path, mode, flags);
	};
};

diff --git a/fs/types.ha b/fs/types.ha
index 8bf43e44..2f39b803 100644
--- a/fs/types.ha
+++ b/fs/types.ha
@@ -216,27 +216,27 @@ export type symlinkfunc = fn(fs: *fs, target: str, path: str) (void | error);
export type openfunc = fn(
	fs: *fs,
	path: str,
	flags: flag...
	flags: flag,
) (io::handle | error);

export type openfilefunc = fn(
	fs: *fs,
	path: str,
	flags: flag...
	flags: flag,
) (io::file | error);

export type createfunc = fn(
	fs: *fs,
	path: str,
	mode: mode,
	flags: flag...
	flags: flag,
) (io::handle | error);

export type createfilefunc = fn(
	fs: *fs,
	path: str,
	mode: mode,
	flags: flag...
	flags: flag,
) (io::file | error);

// An abstract implementation of a filesystem, which provides common filesystem
diff --git a/glob/glob.ha b/glob/glob.ha
index 1b1cf8fa..c6dfb1d9 100644
--- a/glob/glob.ha
+++ b/glob/glob.ha
@@ -64,17 +64,13 @@ export fn strerror(err: failure) str = {

// Returns a generator of pathnames matching a pattern. The result must be
// freed using [[finish]].
export fn glob(pattern: str, flags: flag...) generator = {
export fn glob(pattern: str, flags: flag = flag::NONE) generator = {
	let ss = strstack_init();
	memio::concat(strstack_push(&ss), pattern)!;
	let bs = flag::NONE;
	for (let flag .. flags) {
		bs |= flag;
	};
	return generator {
		pats = ss,
		matc = 0,
		flgs = bs,
		flgs = flags,
		tmpp = pattern_init(),
	};
};
diff --git a/hare/lex/+test.ha b/hare/lex/+test.ha
index 29abdb29..bbac8a8f 100644
--- a/hare/lex/+test.ha
+++ b/hare/lex/+test.ha
@@ -7,7 +7,7 @@ use io;
use memio;
use strings;

fn initbuf(in: []u8, flags: flag...) lexer = {
fn initbuf(in: []u8, flags: flag = flag::NONE) lexer = {
	static let buf: [256]u8 = [0...];
	static let s = memio::stream {
		stream = null: io::stream,
@@ -21,11 +21,11 @@ fn initbuf(in: []u8, flags: flag...) lexer = {

	s = memio::fixed(in);
	sc = bufio::newscanner_static(&s, buf);
	return init(&sc, "<test>", flags...);
	return init(&sc, "<test>", flags);
};

fn initstr(in: str, flags: flag...) lexer = {
	return initbuf(strings::toutf8(in), flags...);
fn initstr(in: str, flags: flag = flag::NONE) lexer = {
	return initbuf(strings::toutf8(in), flags);
};

@test fn unlex() void = {
diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha
index 9eb39278..85af7aa0 100644
--- a/hare/lex/lex.ha
+++ b/hare/lex/lex.ha
@@ -55,11 +55,11 @@ export fn strerror(err: error) const str = {

// Initializes a new lexer for the given [[bufio::scanner]]. The path is
// borrowed.
export fn init(in: *bufio::scanner, path: str, flags: flag...) lexer = {
	let f = flag::NONE;
	for (let i = 0z; i < len(flags); i += 1) {
		f |= flags[i];
	};
export fn init(
	in: *bufio::scanner,
	path: str,
	flags: flag = flag::NONE,
) lexer = {
	const loc = location { path = path, line = 1, col = 1 };
	return lexer {
		in = in,
@@ -68,7 +68,7 @@ export fn init(in: *bufio::scanner, path: str, flags: flag...) lexer = {
		prevrloc = (1, 1),
		un = (ltok::EOF, void, loc),
		prevunlocs = [((1, 1), (1, 1))...],
		flags = f,
		flags = flags,
		...
	};
};
diff --git a/net/+freebsd.ha b/net/+freebsd.ha
index 4fa2d071..fb3d208d 100644
--- a/net/+freebsd.ha
+++ b/net/+freebsd.ha
@@ -19,14 +19,9 @@ export type sockflag = enum int {
// Accepts the next connection from a socket. Blocks until a new connection is
// available. Optionally accepts NOCLOEXEC and NONBLOCK flags. If flags are
// supplied, the [[io::file]] returned will have the supplied flags set.
export fn accept(sock: socket, flags: sockflag...) (socket | error) = {
	// Apply any supplied flags
	let f = 0i;
	for (let i = 0z; i < len(flags); i += 1) {
		f |= flags[i];
	};
	f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
	const fd = match (rt::accept4(sock, null, null, f)) {
export fn accept(sock: socket, flags: sockflag = 0) (socket | error) = {
	flags ^= rt::SOCK_CLOEXEC: sockflag; // invert CLOEXEC
	const fd = match (rt::accept4(sock, null, null, flags)) {
	case let err: rt::errno =>
		return errors::errno(err);
	case let fd: int =>
diff --git a/net/+linux.ha b/net/+linux.ha
index 6818999c..3c92c43b 100644
--- a/net/+linux.ha
+++ b/net/+linux.ha
@@ -19,14 +19,9 @@ export type sockflag = enum int {
// Accepts the next connection from a socket. Blocks until a new connection is
// available. Optionally accepts NOCLOEXEC and NONBLOCK flags. If flags are
// supplied, the [[io::file]] returned will have the supplied flags set.
export fn accept(sock: socket, flags: sockflag...) (socket | error) = {
	// Apply any supplied flags
	let f = 0i;
	for (let i = 0z; i < len(flags); i += 1) {
		f |= flags[i];
	};
	f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
	const fd = match (rt::accept4(sock, null, null, f: int)) {
export fn accept(sock: socket, flags: sockflag = 0) (socket | error) = {
	flags ^= rt::SOCK_CLOEXEC: sockflag; // invert CLOEXEC
	const fd = match (rt::accept4(sock, null, null, flags)) {
	case let err: rt::errno =>
		return errors::errno(err);
	case let fd: int =>
diff --git a/net/+openbsd.ha b/net/+openbsd.ha
index b1e3c6cd..2a38e27c 100644
--- a/net/+openbsd.ha
+++ b/net/+openbsd.ha
@@ -19,14 +19,9 @@ export type sockflag = enum int {
// Accepts the next connection from a socket. Blocks until a new connection is
// available. Optionally accepts NOCLOEXEC and NONBLOCK flags. If flags are
// supplied, the [[io::file]] returned will have the supplied flags set.
export fn accept(sock: socket, flags: sockflag...) (socket | error) = {
	// Apply any supplied flags
	let f = 0i;
	for (let i = 0z; i < len(flags); i += 1) {
		f |= flags[i];
	};
	f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
	const fd = match (rt::accept4(sock, null, null, f: int)) {
export fn accept(sock: socket, flags: sockflag = 0) (socket | error) = {
	flags ^= rt::SOCK_CLOEXEC: sockflag; // invert CLOEXEC
	const fd = match (rt::accept4(sock, null, null, flags)) {
	case let err: rt::errno =>
		return errors::errno(err);
	case let fd: int =>
diff --git a/net/tcp/listener.ha b/net/tcp/listener.ha
index ef74d84d..4f2816ee 100644
--- a/net/tcp/listener.ha
+++ b/net/tcp/listener.ha
@@ -7,5 +7,5 @@ use net;
// available. This is a convenience wrapper around [[net::accept]].
export fn accept(
	sock: net::socket,
	flags: net::sockflag...
) (net::socket | net::error) = net::accept(sock, flags...);
	flags: net::sockflag,
) (net::socket | net::error) = net::accept(sock, flags);
diff --git a/net/unix/listener.ha b/net/unix/listener.ha
index ef74d84d..4f2816ee 100644
--- a/net/unix/listener.ha
+++ b/net/unix/listener.ha
@@ -7,5 +7,5 @@ use net;
// available. This is a convenience wrapper around [[net::accept]].
export fn accept(
	sock: net::socket,
	flags: net::sockflag...
) (net::socket | net::error) = net::accept(sock, flags...);
	flags: net::sockflag,
) (net::socket | net::error) = net::accept(sock, flags);
diff --git a/net/unix/socketpair.ha b/net/unix/socketpair.ha
index 687976a4..a19ab02a 100644
--- a/net/unix/socketpair.ha
+++ b/net/unix/socketpair.ha
@@ -8,15 +8,10 @@ use rt;

// A thin wrapper around socketpair(2) that presumes [[rt::AF_UNIX]] for the
// domain and returns an unnamed pair of sockets of type [[rt::SOCK_STREAM]].
export fn socketpair(flags: net::sockflag...) ((net::socket, net::socket) | net::error) = {
export fn socketpair(flags: net::sockflag = 0) ((net::socket, net::socket) | net::error) = {
	let sv: [2]int = [0...];
	// Apply any supplied flags
	let f = 0i;
	for (let i = 0z; i < len(flags); i += 1) {
		f |= flags[i];
	};
	f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
	match (rt::socketpair(rt::AF_UNIX: int, (rt::SOCK_STREAM | f): int, 0, &sv)) {
	flags ^= rt::SOCK_CLOEXEC: net::sockflag; // invert CLOEXEC
	match (rt::socketpair(rt::AF_UNIX: int, rt::SOCK_STREAM | flags, 0, &sv)) {
	case let err: rt::errno =>
		return errors::errno(err);
	case =>
diff --git a/os/+freebsd/dirfdfs.ha b/os/+freebsd/dirfdfs.ha
index 692ab443..a0e88b74 100644
--- a/os/+freebsd/dirfdfs.ha
+++ b/os/+freebsd/dirfdfs.ha
@@ -163,45 +163,34 @@ fn fsflags_to_bsd(flags: fs::flag) (int | errors::unsupported) = {
fn fs_open_file(
	fs: *fs::fs,
	path: str,
	flags: fs::flag...
	flags: fs::flag,
) (io::file | fs::error) = {
	let oflags = fs::flag::RDONLY;
	for (let flag .. flags) {
		oflags |= flag;
	};
	return _fs_open(fs, path, fsflags_to_bsd(oflags)?, 0);
	return _fs_open(fs, path, fsflags_to_bsd(flags)?, 0);
};

fn fs_open(
	fs: *fs::fs,
	path: str,
	flags: fs::flag...
) (io::handle | fs::error) = fs_open_file(fs, path, flags...)?;
	flags: fs::flag,
) (io::handle | fs::error) = fs_open_file(fs, path, flags)?;

fn fs_create_file(
	fs: *fs::fs,
	path: str,
	mode: fs::mode,
	flags: fs::flag...
	flags: fs::flag,
) (io::file | fs::error) = {
	let oflags: fs::flag = 0;
	if (len(flags) == 0z) {
		oflags |= fs::flag::WRONLY | fs::flag::TRUNC;
	};
	for (let flag .. flags) {
		oflags |= flag;
	};
	oflags |= fs::flag::CREATE;
	return _fs_open(fs, path, fsflags_to_bsd(oflags)?, mode)?;
	flags |= fs::flag::CREATE;
	return _fs_open(fs, path, fsflags_to_bsd(flags)?, mode)?;
};

fn fs_create(
	fs: *fs::fs,
	path: str,
	mode: fs::mode,
	flags: fs::flag...
	flags: fs::flag,
) (io::handle | fs::error) = {
	return fs_create_file(fs, path, mode, flags...)?;
	return fs_create_file(fs, path, mode, flags)?;
};

fn fs_remove(fs: *fs::fs, path: str) (void | fs::error) = {
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
index 538a4399..b436c25d 100644
--- a/os/+linux/dirfdfs.ha
+++ b/os/+linux/dirfdfs.ha
@@ -49,13 +49,12 @@ type os_filesystem = struct {

// Opens a file descriptor as an [[fs::fs]]. This file descriptor must be a
// directory file. The file will be closed when the fs is closed.
export fn dirfdopen(fd: io::file, resolve_flags: resolve_flag...) *fs::fs = {
	let ofs = alloc(os_filesystem { ... });
export fn dirfdopen(
	fd: io::file,
	resolve_flags: resolve_flag = resolve_flag::NORMAL,
) *fs::fs = {
	let ofs = alloc(os_filesystem { resolve = resolve_flags, ... });
	let fs = static_dirfdopen(fd, ofs);

	for (let flag .. resolve_flags) {
		ofs.resolve |= flag;
	};
	fs.close = &fs_close;
	return fs;
};
@@ -91,13 +90,14 @@ fn static_dirfdopen(fd: io::file, filesystem: *os_filesystem) *fs::fs = {

// Clones a dirfd filesystem, optionally adding additional [[resolve_flag]]
// constraints.
export fn dirfs_clone(fs: *fs::fs, resolve_flags: resolve_flag...) *fs::fs = {
export fn dirfs_clone(
	fs: *fs::fs,
	resolve_flags: resolve_flag = resolve_flag::NORMAL,
) *fs::fs = {
	assert(fs.open == &fs_open);
	let fs = fs: *os_filesystem;
	let new = alloc(*fs);
	for (let flag .. resolve_flags) {
		fs.resolve |= flag;
	};
	fs.resolve |= resolve_flags;
	new.dirfd = rt::fcntl(new.dirfd, rt::F_DUPFD_CLOEXEC, 0) as int;
	return &new.fs;
};
@@ -175,22 +175,18 @@ fn _fs_open(
fn fs_open_file(
	fs: *fs::fs,
	path: str,
	flags: fs::flag...
	flags: fs::flag,
) (io::file | fs::error) = {
	let oflags = fs::flag::RDONLY: int;
	for (let flag .. flags) {
		oflags |= flag: int;
	};
	oflags ^= fs::flag::CTTY | fs::flag::NOCLOEXEC; // invert NOCTTY/CLOEXEC
	flags ^= fs::flag::CTTY | fs::flag::NOCLOEXEC; // invert NOCTTY/CLOEXEC

	if ((oflags: fs::flag & fs::flag::DIRECTORY) == fs::flag::DIRECTORY) {
	if ((flags & fs::flag::DIRECTORY) == fs::flag::DIRECTORY) {
		// This is arch-specific
		oflags &= ~fs::flag::DIRECTORY: int;
		oflags |= rt::O_DIRECTORY: int;
		flags &= ~fs::flag::DIRECTORY;
		flags |= rt::O_DIRECTORY: fs::flag;
	};

	let oh = rt::open_how {
		flags = oflags: u64,
		flags = flags: u64,
		...
	};
	return _fs_open(fs, path, &oh);
@@ -199,27 +195,20 @@ fn fs_open_file(
fn fs_open(
	fs: *fs::fs,
	path: str,
	flags: fs::flag...
) (io::handle | fs::error) = fs_open_file(fs, path, flags...)?;
	flags: fs::flag,
) (io::handle | fs::error) = fs_open_file(fs, path, flags)?;

fn fs_create_file(
	fs: *fs::fs,
	path: str,
	mode: fs::mode,
	flags: fs::flag...
	flags: fs::flag,
) (io::file | fs::error) = {
	let oflags = 0;
	if (len(flags) == 0) {
		oflags |= fs::flag::WRONLY | fs::flag::TRUNC;
	};
	for (let flag .. flags) {
		oflags |= flag: int;
	};
	oflags ^= fs::flag::CTTY | fs::flag::NOCLOEXEC; // invert NOCTTY/CLOEXEC
	oflags |= fs::flag::CREATE: int;
	flags ^= fs::flag::CTTY | fs::flag::NOCLOEXEC; // invert NOCTTY/CLOEXEC
	flags |= fs::flag::CREATE;

	let oh = rt::open_how {
		flags = oflags: u64,
		flags = flags: u64,
		mode = mode: u64,
		...
	};
@@ -230,9 +219,9 @@ fn fs_create(
	fs: *fs::fs,
	path: str,
	mode: fs::mode,
	flags: fs::flag...
	flags: fs::flag,
) (io::handle | fs::error) = {
	return fs_create_file(fs, path, mode, flags...)?;
	return fs_create_file(fs, path, mode, flags)?;
};

fn fs_remove(fs: *fs::fs, path: str) (void | fs::error) = {
diff --git a/os/+openbsd/dirfdfs.ha b/os/+openbsd/dirfdfs.ha
index 836ad040..c7f7c020 100644
--- a/os/+openbsd/dirfdfs.ha
+++ b/os/+openbsd/dirfdfs.ha
@@ -87,20 +87,16 @@ fn _fs_open(
fn fs_open_file(
	fs: *fs::fs,
	path: str,
	flags: fs::flag...
	flags: fs::flag,
) (io::file | fs::error) = {
	let oflags = fs::flag::RDONLY;
	for (let flag .. flags) {
		oflags |= flag;
	};
	return _fs_open(fs, path, fsflags_to_bsd(oflags)?, 0);
	return _fs_open(fs, path, fsflags_to_bsd(flags)?, 0);
};

fn fs_open(
	fs: *fs::fs,
	path: str,
	flags: fs::flag...
) (io::handle | fs::error) = fs_open_file(fs, path, flags...)?;
	flags: fs::flag,
) (io::handle | fs::error) = fs_open_file(fs, path, flags)?;

fn fs_readlink(fs: *fs::fs, path: str) (str | fs::error) = {
	let fs = fs: *os_filesystem;
@@ -123,25 +119,18 @@ fn fs_create_file(
	fs: *fs::fs,
	path: str,
	mode: fs::mode,
	flags: fs::flag...
	flags: fs::flag,
) (io::file | fs::error) = {
	let oflags: fs::flag = 0;
	if (len(flags) == 0z) {
		oflags |= fs::flag::WRONLY | fs::flag::TRUNC;
	};
	for (let flag .. flags) {
		oflags |= flag;
	};
	oflags |= fs::flag::CREATE;
	return _fs_open(fs, path, fsflags_to_bsd(oflags)?, mode)?;
	flags |= fs::flag::CREATE;
	return _fs_open(fs, path, fsflags_to_bsd(flags)?, mode)?;
};

fn fs_create(
	fs: *fs::fs,
	path: str,
	mode: fs::mode,
	flags: fs::flag...
) (io::handle | fs::error) = fs_create_file(fs, path, mode, flags...)?;
	flags: fs::flag,
) (io::handle | fs::error) = fs_create_file(fs, path, mode, flags)?;

fn fs_remove(fs: *fs::fs, path: str) (void | fs::error) = {
	let fs = fs: *os_filesystem;
diff --git a/os/os.ha b/os/os.ha
index ae58c1f8..c8762d7f 100644
--- a/os/os.ha
+++ b/os/os.ha
@@ -84,20 +84,16 @@ export fn symlink(target: str, path: str) (void | fs::error) =

// Opens a file.
//
// If no flags are provided, [[fs::flag::RDONLY]] is used when opening the
// file.
//
// [[fs::flag::CREATE]] isn't very useful with this function, since the new
// file's mode is set to zero. For this use-case, use [[create]] instead.
export fn open(path: str, flags: fs::flag...) (io::file | fs::error) =
	fs::open_file(cwd, path, flags...);
export fn open(
	path: str,
	flags: fs::flag = fs::flag::RDONLY,
) (io::file | fs::error) = fs::open_file(cwd, path, flags);

// Creates a new file with the given mode if it doesn't already exist and opens
// it for writing.
//
// If no flags are provided, [[fs::flag::WRONLY]] and [[fs::flag::TRUNC]] are
// used when opening the file.
//
// Only the permission bits of the mode are used. If other bits are set, they
// are discarded.
//
@@ -105,7 +101,7 @@ export fn open(path: str, flags: fs::flag...) (io::file | fs::error) =
export fn create(
	path: str,
	mode: fs::mode,
	flags: fs::flag...
	flags: fs::flag = fs::flag::WRONLY | fs::flag::TRUNC,
) (io::file | fs::error) = fs::create_file(cwd, path, mode, flags...);

// Canonicalizes a path in this filesystem by resolving all symlinks and
diff --git a/unix/+freebsd/pipe.ha b/unix/+freebsd/pipe.ha
index 2b3d1616..38777fbf 100644
--- a/unix/+freebsd/pipe.ha
+++ b/unix/+freebsd/pipe.ha
@@ -15,14 +15,10 @@ export type pipe_flag = enum {

// Create a pair of two linked [[io::file]]s, such that any data written to the
// second [[io::file]] may be read from the first.
export fn pipe(flags: pipe_flag...) ((io::file, io::file) | errors::error) = {
export fn pipe(flags: pipe_flag = 0) ((io::file, io::file) | errors::error) = {
	let fds: [2]int = [0...];
	let flag: pipe_flag = 0;
	for (let i = 0z; i < len(flags); i += 1) {
		flag |= flags[i];
	};
	flag ^= pipe_flag::NOCLOEXEC; // invert CLOEXEC
	match (rt::pipe2(&fds, flag)) {
	flags ^= pipe_flag::NOCLOEXEC; // invert CLOEXEC
	match (rt::pipe2(&fds, flags)) {
	case void => void;
	case let e: rt::errno =>
		return errors::errno(e);
diff --git a/unix/+linux/pipe.ha b/unix/+linux/pipe.ha
index 2b3d1616..38777fbf 100644
--- a/unix/+linux/pipe.ha
+++ b/unix/+linux/pipe.ha
@@ -15,14 +15,10 @@ export type pipe_flag = enum {

// Create a pair of two linked [[io::file]]s, such that any data written to the
// second [[io::file]] may be read from the first.
export fn pipe(flags: pipe_flag...) ((io::file, io::file) | errors::error) = {
export fn pipe(flags: pipe_flag = 0) ((io::file, io::file) | errors::error) = {
	let fds: [2]int = [0...];
	let flag: pipe_flag = 0;
	for (let i = 0z; i < len(flags); i += 1) {
		flag |= flags[i];
	};
	flag ^= pipe_flag::NOCLOEXEC; // invert CLOEXEC
	match (rt::pipe2(&fds, flag)) {
	flags ^= pipe_flag::NOCLOEXEC; // invert CLOEXEC
	match (rt::pipe2(&fds, flags)) {
	case void => void;
	case let e: rt::errno =>
		return errors::errno(e);
diff --git a/unix/+openbsd/pipe.ha b/unix/+openbsd/pipe.ha
index 4f3780c7..6ca0bf8c 100644
--- a/unix/+openbsd/pipe.ha
+++ b/unix/+openbsd/pipe.ha
@@ -14,14 +14,10 @@ export type pipe_flag = enum {

// Create a pair of two linked [[io::file]]s, such that any data written to the
// second [[io::file]] may be read from the first.
export fn pipe(flags: pipe_flag...) ((io::file, io::file) | errors::error) = {
export fn pipe(flags: pipe_flag = 0) ((io::file, io::file) | errors::error) = {
	let fds: [2]int = [0...];
	let flag: pipe_flag = 0;
	for (let i = 0z; i < len(flags); i += 1) {
		flag |= flags[i];
	};
	flag ^= pipe_flag::NOCLOEXEC; // invert CLOEXEC
	match (rt::pipe2(&fds, flag)) {
	flags ^= pipe_flag::NOCLOEXEC; // invert CLOEXEC
	match (rt::pipe2(&fds, flags)) {
	case void => void;
	case let e: rt::errno =>
		return errors::errno(e);
diff --git a/wordexp/+test.ha b/wordexp/+test.ha
index 5bd86b93..49205ecb 100644
--- a/wordexp/+test.ha
+++ b/wordexp/+test.ha
@@ -32,7 +32,7 @@ fn streq(a: []str, b: []str) bool = {

	for (let i = 0z; i < len(cases); i += 1) {
		const (in, out) = cases[i];
		const words = wordexp(in, flag::NONE)!;
		const words = wordexp(in)!;
		defer strings::freeall(words);
		assert(streq(words, out));
	};
diff --git a/wordexp/wordexp.ha b/wordexp/wordexp.ha
index ce779aab..d458fe5c 100644
--- a/wordexp/wordexp.ha
+++ b/wordexp/wordexp.ha
@@ -26,7 +26,7 @@ export type flag = enum uint {
//
// Pass the return value to [[strings::freeall]] to free resources associated
// with the return value.
export fn wordexp(s: str, flags: flag) ([]str | error) = {
export fn wordexp(s: str, flags: flag = flag::NONE) ([]str | error) = {
	const (rd, wr) = exec::pipe();

	// "x" is added to handle the list of expanded words being empty
-- 
2.44.0
hare/patches: SUCCESS in 1m4s

[all: Use optional parameters for flags][0] from [Alexey Yerin][1]

[0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/50881
[1]: mailto:yyp@disroot.org

✓ #1192296 SUCCESS hare/patches/freebsd.yml https://builds.sr.ht/~sircmpwn/job/1192296
✓ #1192295 SUCCESS hare/patches/alpine.yml  https://builds.sr.ht/~sircmpwn/job/1192295
✓ #1192297 SUCCESS hare/patches/openbsd.yml https://builds.sr.ht/~sircmpwn/job/1192297
reviewed and OK by me

maby mention the breaking change in the commit message?

i also like the change removing variadic arguments, but was this ever
discussed (is everyone fine with this or is there some benifit)?
Thanks!

To git@git.sr.ht:~sircmpwn/hare
   10f02df0..28a63020  master -> master