hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

util.ha (4775B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use io;
      5 use path;
      6 use strings;
      7 
      8 // Converts a mode into a Unix-like mode string (e.g. "-rw-r--r--"). The string
      9 // is statically allocated, use [[strings::dup]] to duplicate it or it will be
     10 // overwritten on subsequent calls.
     11 export fn mode_str(m: mode) const str = {
     12 	static let buf: [10]u8 = [0...];
     13 	buf = [
     14 		(if (m & mode::DIR == mode::DIR) 'd'
     15 			else if (m & mode::FIFO == mode::FIFO) 'p'
     16 			else if (m & mode::SOCK == mode::SOCK) 's'
     17 			else if (m & mode::BLK == mode::BLK) 'b'
     18 			else if (m & mode::LINK == mode::LINK) 'l'
     19 			else if (m & mode::CHR == mode::CHR) 'c'
     20 			else '-'): u8,
     21 		(if (m & mode::USER_R == mode::USER_R) 'r' else '-'): u8,
     22 		(if (m & mode::USER_W == mode::USER_W) 'w' else '-'): u8,
     23 		(if (m & mode::SETUID == mode::SETUID) 's'
     24 			else if (m & mode::USER_X == mode::USER_X) 'x'
     25 			else '-'): u8,
     26 		(if (m & mode::GROUP_R == mode::GROUP_R) 'r' else '-'): u8,
     27 		(if (m & mode::GROUP_W == mode::GROUP_W) 'w' else '-'): u8,
     28 		(if (m & mode::SETGID == mode::SETGID) 's'
     29 			else if (m & mode::GROUP_X == mode::GROUP_X) 'x'
     30 			else '-'): u8,
     31 		(if (m & mode::OTHER_R == mode::OTHER_R) 'r' else '-'): u8,
     32 		(if (m & mode::OTHER_W == mode::OTHER_W) 'w' else '-'): u8,
     33 		(if (m & mode::STICKY == mode::STICKY) 't'
     34 			else if (m & mode::OTHER_X == mode::OTHER_X) 'x'
     35 			else '-'): u8,
     36 	];
     37 	return strings::fromutf8(buf)!;
     38 };
     39 
     40 @test fn mode_str() void = {
     41 	assert(mode_str(0o777: mode) == "-rwxrwxrwx");
     42 	assert(mode_str(mode::DIR | 0o755: mode) == "drwxr-xr-x");
     43 	assert(mode_str(0o755: mode | mode::SETUID) == "-rwsr-xr-x");
     44 	assert(mode_str(0o644: mode) == "-rw-r--r--");
     45 	assert(mode_str(0: mode) == "----------");
     46 };
     47 
     48 // Returns the permission bits of a file mode.
     49 export fn mode_perm(m: mode) mode = (m: uint & 0o777u): mode;
     50 
     51 // Returns the type bits of a file mode.
     52 export fn mode_type(m: mode) mode = (m: uint & ~0o777u): mode;
     53 
     54 // bit mask for the file type bit field
     55 def IFMT: mode = 0o0170000u: mode;
     56 
     57 // Returns true if this item is a regular file.
     58 export fn isfile(mode: mode) bool = mode & IFMT == mode::REG;
     59 
     60 // Returns true if this item is a FIFO (named pipe).
     61 export fn isfifo(mode: mode) bool = mode & IFMT == mode::FIFO;
     62 
     63 // Returns true if this item is a directory.
     64 export fn isdir(mode: mode) bool = mode & IFMT == mode::DIR;
     65 
     66 // Returns true if this item is a character device.
     67 export fn ischdev(mode: mode) bool = mode & IFMT == mode::CHR;
     68 
     69 // Returns true if this item is a block device.
     70 export fn isblockdev(mode: mode) bool = mode & IFMT == mode::BLK;
     71 
     72 // Returns true if this item is a symbolic link.
     73 export fn islink(mode: mode) bool = mode & IFMT == mode::LINK;
     74 
     75 // Returns true if this item is a Unix socket.
     76 export fn issocket(mode: mode) bool = mode & IFMT == mode::SOCK;
     77 
     78 @test fn modes() void = {
     79 	const foo = mode::LINK | 0o755: mode;
     80 	assert(islink(foo));
     81 	assert(!isfile(foo));
     82 };
     83 
     84 // Reads all entries from a directory. The caller must free the return value
     85 // with [[dirents_free]].
     86 export fn readdir(fs: *fs, path: str) ([]dirent | error) = {
     87 	let i = iter(fs, path)?;
     88 	defer finish(i);
     89 	let ents: []dirent = [];
     90 
     91 	for (let d => next(i)?) {
     92 		append(ents, dirent_dup(&d));
     93 	};
     94 	return ents;
     95 };
     96 
     97 // Frees a slice of [[dirent]]s.
     98 export fn dirents_free(dirents: []dirent) void = {
     99 	for (let d &.. dirents) {
    100 		dirent_finish(d);
    101 	};
    102 	free(dirents);
    103 };
    104 
    105 // Removes a directory, and anything in it.
    106 export fn rmdirall(fs: *fs, path: str) (void | error) = {
    107 	let buf = path::init(path)!;
    108 	return rmdirall_path(fs, &buf);
    109 };
    110 
    111 fn rmdirall_path(fs: *fs, buf: *path::buffer) (void | error) = {
    112 	let it = iter(fs, path::string(buf))?;
    113 	defer finish(it);
    114 	for (let ent => next(it)?) {
    115 		path::push(buf, ent.name)!;
    116 
    117 		switch (ent.ftype & mode::DIR) {
    118 		case mode::DIR =>
    119 			rmdirall_path(fs, buf)?;
    120 		case =>
    121 			remove(fs, path::string(buf))?;
    122 		};
    123 		path::pop(buf);
    124 	};
    125 	return rmdir(fs, path::string(buf));
    126 };
    127 
    128 // Canonicalizes a path in this filesystem by resolving all symlinks and
    129 // collapsing any "." or ".." path components. The return value is statically
    130 // allocated and will be overwritten on subsequent calls.
    131 export fn realpath(fs: *fs, path: str) (str | error) = {
    132 	static let res = path::buffer { ... };
    133 	path::set(&res)!;
    134 	static let pathbuf = path::buffer { ... };
    135 	path::set(&pathbuf, resolve(fs, path))!;
    136 	const iter = path::iter(&pathbuf);
    137 
    138 	for (let item => path::nextiter(&iter)) {
    139 		const item = path::push(&res, item)!;
    140 
    141 		const link = match (readlink(fs, item)) {
    142 		case let link: str =>
    143 			yield link;
    144 		case wrongtype =>
    145 			continue;
    146 		case let err: error =>
    147 			return err;
    148 		};
    149 
    150 		if (!path::abs(link)) {
    151 			path::push(&res, "..", link)!;
    152 		} else {
    153 			path::set(&res, link)!;
    154 		};
    155 	};
    156 
    157 	return path::string(&res);
    158 };