xdg.ha (3403B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use errors; 5 use fmt; 6 use fs; 7 use os; 8 use path; 9 use unix; 10 11 fn lookup(prog: str, var: str, default: str) str = { 12 static let buf = path::buffer { ... }; 13 path::set(&buf)!; 14 match (os::getenv(var)) { 15 case let s: str => 16 const path = path::push(&buf, s, prog)!; 17 if (!path::abs(path)) { 18 yield; 19 }; 20 match (os::stat(path)) { 21 case let err: fs::error => 22 os::mkdirs(path, 0o755)!; 23 return path; 24 case let st: fs::filestat => 25 if (fs::isdir(st.mode)) { 26 return path; 27 }; 28 }; 29 case void => void; 30 }; 31 32 const home = os::getenv("HOME") as str; 33 const path = path::set(&buf, home, default, prog)!; 34 match (os::mkdirs(path, 0o755)) { 35 case let err: fs::error => 36 fmt::fatalf("Error creating {}: {}", 37 path, fs::strerror(err)); 38 case void => void; 39 }; 40 return path; 41 }; 42 43 // Returns a directory suitable for storing config files. The "prog" parameter 44 // should be a descriptive name unique to this program. The return value is 45 // statically allocated and will be overwritten on subsequent calls to any 46 // function in the dirs module. 47 export fn config(prog: str) str = lookup(prog, "XDG_CONFIG_HOME", ".config"); 48 49 // Returns a directory suitable for cache files. The "prog" parameter should be 50 // a descriptive name unique to this program. The return value is statically 51 // allocated and will be overwritten on subsequent calls to any function in the 52 // dirs module. 53 export fn cache(prog: str) str = lookup(prog, "XDG_CACHE_HOME", ".cache"); 54 55 // Returns a directory suitable for persistent data files. The "prog" parameter 56 // should be a descriptive name unique to this program. The return value is 57 // statically allocated and will be overwritten on subsequent calls to any 58 // function in the dirs module. 59 export fn data(prog: str) str = { 60 static let buf = path::buffer { ... }; 61 const fragment = path::set(&buf, ".local", "share")!; 62 return lookup(prog, "XDG_DATA_HOME", fragment); 63 }; 64 65 // Returns a directory suitable for storing program state data. The "prog" 66 // parameter should be a descriptive name unique to this program. The return 67 // value is statically allocated and will be overwritten on subsequent calls to 68 // any function in the dirs module. 69 export fn state(prog: str) str = { 70 static let buf = path::buffer { ... }; 71 const fragment = path::set(&buf, ".local", "state")!; 72 return lookup(prog, "XDG_STATE_HOME", fragment); 73 }; 74 75 // Returns a directory suitable for storing non-essential runtime files and 76 // other file objects (such as sockets, named pipes, and so on). Applications 77 // should use this directory for communication and synchronization purposes and 78 // should not place larger files in it, since it might reside in runtime memory 79 // and cannot necessarily be swapped out to disk. 80 // 81 // The specification requires the directory to be owned by the current user and 82 // not be world-readable. No fallback is implemented in case XDG_RUNTIME_DIR is 83 // unset or incorrectly set up. 84 export fn runtime() (str | fs::error) = { 85 let path = match (os::getenv("XDG_RUNTIME_DIR")) { 86 case let path: str => 87 yield path; 88 case void => 89 return errors::noentry; 90 }; 91 92 const st = os::stat(path)?; 93 const uid = unix::getuid(): uint; 94 if (st.uid != uid || fs::mode_perm(st.mode) != fs::mode::USER_RWX) { 95 return errors::noaccess; 96 }; 97 if (!fs::isdir(st.mode)) { 98 return fs::wrongtype; 99 }; 100 101 return path; 102 };