hare

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

xdg.ha (4239B)


      1 // License: MPL-2.0
      2 // (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
      3 // (c) 2021 Ember Sawady <ecs@d2evs.net>
      4 // (c) 2022 Sebastian <sebastian@sebsite.pw>
      5 // (c) 2022 Jon Eskin <eskinjp@gmail.com>
      6 // (c) 2022 Simon Ser <contact@emersion.fr>
      7 use errors;
      8 use fmt;
      9 use fs;
     10 use os;
     11 use path;
     12 use io;
     13 use unix;
     14 
     15 fn lookup(prog: str, var: str, default: str) str = {
     16 	static let buf = path::buffer { ... };
     17 	path::set(&buf)!;
     18 	match (os::getenv(var)) {
     19 	case let s: str =>
     20 		const path = path::push(&buf, s, prog)!;
     21 		if (!path::abs(path)) {
     22 			yield;
     23 		};
     24 		match (os::stat(path)) {
     25 		case let err: fs::error =>
     26 			os::mkdirs(path, 0o755)!;
     27 			return path;
     28 		case let st: fs::filestat =>
     29 			if (fs::isdir(st.mode)) {
     30 				return path;
     31 			};
     32 		};
     33 	case void => void;
     34 	};
     35 
     36 	const home = os::getenv("HOME") as str;
     37 	const path = path::set(&buf, home, default, prog)!;
     38 	match (os::mkdirs(path, 0o755)) {
     39 	case void =>
     40 		yield;
     41 	case let err: fs::error =>
     42 		fmt::fatalf("Error creating {}: {}",
     43 			path, fs::strerror(err));
     44 	};
     45 	return path;
     46 };
     47 
     48 // Returns a directory suitable for storing config files. The "prog" parameter
     49 // should be a descriptive name unique to this program. The return value is
     50 // statically allocated and will be overwritten on subsequent calls to any
     51 // function in the dirs module.
     52 export fn config(prog: str) str = lookup(prog, "XDG_CONFIG_HOME", ".config");
     53 
     54 // Returns an [[fs::fs]] for storing config files. The "prog" parameter
     55 // should be a descriptive name unique to this program.
     56 export fn configfs(prog: str) *fs::fs = os::diropen(config(prog)) as *fs::fs;
     57 
     58 // Returns a directory suitable for cache files. The "prog" parameter should be
     59 // a descriptive name unique to this program. The return value is statically
     60 // allocated and will be overwritten on subsequent calls to any function in the
     61 // dirs module.
     62 export fn cache(prog: str) str = lookup(prog, "XDG_CACHE_HOME", ".cache");
     63 
     64 // Returns an [[fs::fs]] for cache files.
     65 export fn cachefs(prog: str) *fs::fs = os::diropen(cache(prog)) as *fs::fs;
     66 
     67 // Returns a directory suitable for persistent data files. The "prog" parameter
     68 // should be a descriptive name unique to this program. The return value is
     69 // statically allocated and will be overwritten on subsequent calls to any
     70 // function in the dirs module.
     71 export fn data(prog: str) str = {
     72 	static let buf = path::buffer { ... };
     73 	const fragment = path::set(&buf, ".local", "share")!;
     74 	return lookup(prog, "XDG_DATA_HOME", fragment);
     75 };
     76 
     77 // Returns an [[fs::fs]] for persistent data files. If "prog" is given, a unique
     78 // path for this program to store data will be returned.
     79 export fn datafs(prog: str) *fs::fs = os::diropen(data(prog)) as *fs::fs;
     80 
     81 // Returns a directory suitable for storing program state data. The "prog"
     82 // parameter should be a descriptive name unique to this program. The return
     83 // value is statically allocated and will be overwritten on subsequent calls to
     84 // any function in the dirs module.
     85 export fn state(prog: str) str = {
     86 	static let buf = path::buffer { ... };
     87 	const fragment = path::set(&buf, ".local", "state")!;
     88 	return lookup(prog, "XDG_STATE_HOME", fragment);
     89 };
     90 
     91 // Returns an [[fs::fs]] for storing program state data.
     92 export fn statefs(prog: str) *fs::fs = os::diropen(state(prog)) as *fs::fs;
     93 
     94 // Returns a directory suitable for storing non-essential runtime files and
     95 // other file objects (such as sockets, named pipes, and so on). Applications
     96 // should use this directory for communication and synchronization purposes and
     97 // should not place larger files in it, since it might reside in runtime memory
     98 // and cannot necessarily be swapped out to disk.
     99 //
    100 // The specification requires the directory to be owned by the current user and
    101 // not be world-readable. No fallback is implemented in case XDG_RUNTIME_DIR is
    102 // unset or incorrectly set up.
    103 export fn runtime() (str | fs::error) = {
    104 	let path = match (os::getenv("XDG_RUNTIME_DIR")) {
    105 	case let path: str =>
    106 		yield path;
    107 	case void =>
    108 		return errors::noentry;
    109 	};
    110 
    111 	const st = os::stat(path)?;
    112 	const uid = unix::getuid();
    113 	if (st.uid != uid || fs::mode_perm(st.mode) != fs::mode::USER_RWX) {
    114 		return errors::noaccess;
    115 	};
    116 	if (!fs::isdir(st.mode)) {
    117 		return fs::wrongtype;
    118 	};
    119 
    120 	return path;
    121 };