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 };