~sircmpwn/hare

hare/fs/util.ha -rw-r--r-- 3.7 KiB
5edc06a0Eyal Sawady hare::{ast,parse,unparse}: implement offset() an hour ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use errors;
use io;
use path;
use strings;

// Returns a human-friendly representation of an error.
export fn strerror(err: error) const str = match (err) {
case wrongtype =>
	yield "Wrong entry type for requested operation";
case cannotrename =>
	yield "Unable to perform rename operation (try move instead)";
case errors::noentry =>
	yield "File or directory not found";
case errors::noaccess =>
	yield "Permission denied";
case errors::exists =>
	yield "File or directory exists";
case errors::invalid =>
	yield "Invalid argument";
case err: io::error =>
	yield io::strerror(err);
};

// Converts a mode into a Unix-like mode string (e.g. "-rw-r--r--"). The string
// is statically allocated, use [[strings::dup]] to duplicate it or it will be
// overwritten on subsequent calls.
export fn mode_str(m: mode) const str = {
	static let buf: [10]u8 = [0...];
	buf = [
		(if (m & mode::DIR == mode::DIR) 'd'
			else if (m & mode::FIFO == mode::FIFO) 'p'
			else if (m & mode::SOCK == mode::SOCK) 's'
			else if (m & mode::BLK == mode::BLK) 'b'
			else if (m & mode::LINK == mode::LINK) 'l'
			else if (m & mode::CHR == mode::CHR) 'c'
			else '-'): u32: u8,
		(if (m & mode::USER_R == mode::USER_R) 'r' else '-'): u32: u8,
		(if (m & mode::USER_W == mode::USER_W) 'w' else '-'): u32: u8,
		(if (m & mode::SETUID == mode::SETUID) 's'
			else if (m & mode::USER_X == mode::USER_X) 'x'
			else '-'): u32: u8,
		(if (m & mode::GROUP_R == mode::GROUP_R) 'r' else '-'): u32: u8,
		(if (m & mode::GROUP_W == mode::GROUP_W) 'w' else '-'): u32: u8,
		(if (m & mode::SETGID == mode::SETGID) 's'
			else if (m & mode::GROUP_X == mode::GROUP_X) 'x'
			else '-'): u32: u8,
		(if (m & mode::OTHER_R == mode::OTHER_R) 'r' else '-'): u32: u8,
		(if (m & mode::OTHER_W == mode::OTHER_W) 'w' else '-'): u32: u8,
		(if (m & mode::STICKY == mode::STICKY) 't'
			else if (m & mode::OTHER_X == mode::OTHER_X) 'x'
			else '-'): u32: u8,
	];
	return strings::fromutf8(buf);
};

@test fn mode_str() void = {
	assert(mode_str(0o777: mode) == "-rwxrwxrwx");
	assert(mode_str(mode::DIR | 0o755: mode) == "drwxr-xr-x");
	assert(mode_str(0o755: mode | mode::SETUID) == "-rwsr-xr-x");
	assert(mode_str(0o644: mode) == "-rw-r--r--");
	assert(mode_str(0: mode) == "----------");
};

// Returns the permission bits of a file mode.
export fn mode_perm(m: mode) mode = (m: uint & 0o777u): mode;

// Returns the type bits of a file mode.
export fn mode_type(m: mode) mode = (m: uint & ~0o777u): mode;

// Returns true if this item is a regular file.
export fn isfile(mode: mode) bool = mode & mode::REG == mode::REG;

// Returns true if this item is a FIFO (named pipe).
export fn isfifo(mode: mode) bool = mode & mode::FIFO == mode::FIFO;

// Returns true if this item is a directory.
export fn isdir(mode: mode) bool = mode & mode::DIR == mode::DIR;

// Returns true if this item is a character device.
export fn ischdev(mode: mode) bool = mode & mode::CHR == mode::CHR;

// Returns true if this item is a block device.
export fn isblockdev(mode: mode) bool = mode & mode::BLK == mode::BLK;

// Returns true if this item is a symbolic link.
export fn islink(mode: mode) bool = mode & mode::LINK == mode::LINK;

// Returns true if this item is a Unix socket.
export fn issocket(mode: mode) bool = mode & mode::SOCK == mode::SOCK;

// Reads all entries from a directory. The caller must free the return value
// with [[dirents_free]].
export fn readdir(fs: *fs, path: str) ([]dirent | error) = {
	let i = iter(fs, path)?;
	let ents: []dirent = [];
	for (true) {
		match (next(i)) {
		case d: dirent =>
			append(ents, dirent_dup(&d));
		case void =>
			break;
		};
	};
	return ents;
};

// Frees a slice of [[dirent]]s.
export fn dirents_free(d: []dirent) void = {
	for (let i = 0z; i < len(d); i += 1) {
		dirent_free(&d[i]);
	};
};