1use crate::{DesktopEnv, Platform};
2
3use std::ffi::c_void;
4use std::mem;
5use std::process::Command;
6use std::process::Stdio;
7
8#[repr(C)]
9struct PassWd {
10 pw_name: *const c_void,
11 pw_passwd: *const c_void,
12 pw_uid: u32,
13 pw_gid: u32,
14 #[cfg(target_os = "macos")]
15 pw_change: isize,
16 #[cfg(target_os = "macos")]
17 pw_class: *const c_void,
18 pw_gecos: *const c_void,
19 pw_dir: *const c_void,
20 pw_shell: *const c_void,
21 #[cfg(target_os = "macos")]
22 pw_expire: isize,
23 #[cfg(target_os = "macos")]
24 pw_fields: i32,
25}
26
27extern "system" {
28 fn getpwuid_r(
29 uid: u32,
30 pwd: *mut PassWd,
31 buf: *mut c_void,
32 buflen: usize,
33 result: *mut *mut PassWd,
34 ) -> i32;
35 fn geteuid() -> u32;
36 fn strlen(cs: *const c_void) -> usize;
37 fn gethostname(name: *mut c_void, len: usize) -> i32;
38}
39
40fn string_from_cstring(string: *const c_void) -> String {
41 if string.is_null() {
42 return "".to_string();
43 }
44
45 let slice = unsafe {
47 let length = strlen(string);
48 std::slice::from_raw_parts(string as *const u8, length)
49 };
50
51 String::from_utf8_lossy(slice).to_string()
53}
54
55#[inline(always)]
58fn getpwuid() -> (String, String) {
59 const BUF_SIZE: usize = 16_384; let mut buffer = mem::MaybeUninit::<[u8; BUF_SIZE]>::uninit();
61 let mut passwd = mem::MaybeUninit::<PassWd>::uninit();
62 let mut _passwd = mem::MaybeUninit::<*mut PassWd>::uninit();
63
64 let passwd = unsafe {
66 getpwuid_r(
67 geteuid(),
68 passwd.as_mut_ptr(),
69 buffer.as_mut_ptr() as *mut c_void,
70 BUF_SIZE,
71 _passwd.as_mut_ptr(),
72 );
73
74 passwd.assume_init()
75 };
76
77 let a = string_from_cstring(passwd.pw_name);
79 let b = string_from_cstring(passwd.pw_gecos);
80
81 (a, b)
82}
83
84pub fn username() -> String {
85 let pwent = getpwuid();
86
87 pwent.0
88}
89
90fn fancy_fallback(mut computer: String, fallback_fn: fn() -> String) -> String {
91 let mut cap = true;
92
93 if computer.is_empty() {
94 let fallback = fallback_fn();
95
96 for c in fallback.chars() {
97 match c {
98 '.' | '-' | '_' => {
99 computer.push(' ');
100 cap = true;
101 }
102 a => {
103 if cap {
104 cap = false;
105 for i in a.to_uppercase() {
106 computer.push(i);
107 }
108 } else {
109 computer.push(a);
110 }
111 }
112 }
113 }
114 }
115
116 computer
117}
118
119pub fn realname() -> String {
120 let pwent = getpwuid();
121 let realname = pwent.1;
122
123 fancy_fallback(realname, username)
125}
126
127pub fn computer() -> String {
128 let mut computer = String::new();
129
130 let program = if cfg!(not(target_os = "macos")) {
131 Command::new("hostnamectl")
132 .arg("--pretty")
133 .stdout(Stdio::piped())
134 .output()
135 .expect("Couldn't Find `hostnamectl`")
136 } else {
137 Command::new("scutil")
138 .arg("--get")
139 .arg("ComputerName")
140 .output()
141 .expect("Couldn't find `scutil`")
142 };
143
144 computer.push_str(String::from_utf8(program.stdout).unwrap().as_str());
145
146 computer.pop();
151
152 fancy_fallback(computer, hostname)
153}
154
155pub fn hostname() -> String {
156 let mut string = mem::MaybeUninit::<[u8; 256]>::uninit();
158 let string = unsafe {
159 gethostname(string.as_mut_ptr() as *mut c_void, 255);
160 &string.assume_init()[..strlen(string.as_ptr() as *const c_void)]
161 };
162
163 String::from_utf8_lossy(string).to_string()
164}
165
166#[cfg(target_os = "macos")]
167pub fn os() -> String {
168 let mut distro = String::new();
169
170 let name = Command::new("sw_vers")
171 .arg("-productName")
172 .output()
173 .expect("Couldn't find `sw_vers`");
174
175 let version = Command::new("sw_vers")
176 .arg("-productVersion")
177 .output()
178 .expect("Couldn't find `sw_vers`");
179
180 let build = Command::new("sw_vers")
181 .arg("-buildVersion")
182 .output()
183 .expect("Couldn't find `sw_vers`");
184
185 distro.push_str(String::from_utf8(name.stdout).unwrap().as_str());
186 distro.pop();
187 distro.push(' ');
188 distro.push_str(String::from_utf8(version.stdout).unwrap().as_str());
189 distro.pop();
190 distro.push(' ');
191 distro.push_str(String::from_utf8(build.stdout).unwrap().as_str());
192 distro.pop();
193
194 distro
195}
196
197#[cfg(not(target_os = "macos"))]
198pub fn os() -> String {
199 let mut distro = String::new();
200
201 let program = std::fs::read_to_string("/etc/os-release")
202 .expect("Couldn't read file /etc/os-release")
203 .into_bytes();
204
205 distro.push_str(String::from_utf8(program).unwrap().as_str());
206
207 let mut fallback = None;
208
209 for i in distro.split('\n') {
210 let mut j = i.split('=');
211
212 match j.next().unwrap() {
213 "PRETTY_NAME" => {
214 return j.next().unwrap().trim_matches('"').to_string()
215 }
216 "NAME" => {
217 fallback = Some(j.next().unwrap().trim_matches('"').to_string())
218 }
219 _ => {}
220 }
221 }
222
223 if let Some(x) = fallback {
224 x
225 } else {
226 "unknown".to_string()
227 }
228}
229
230#[cfg(target_os = "macos")]
231#[inline(always)]
232pub const fn env() -> DesktopEnv {
233 DesktopEnv::Mac
234}
235
236#[cfg(not(target_os = "macos"))]
237#[inline(always)]
238pub fn env() -> DesktopEnv {
239 match std::env::var_os("DESKTOP_SESSION") {
240 Some(env) => {
241 let env = env.to_str().unwrap().to_uppercase();
242
243 if env.contains("GNOME") {
244 DesktopEnv::Gnome
245 } else if env.contains("LXDE") {
246 DesktopEnv::Lxde
247 } else if env.contains("OPENBOX") {
248 DesktopEnv::Openbox
249 } else if env.contains("I3") {
250 DesktopEnv::I3
251 } else if env.contains("UBUNTU") {
252 DesktopEnv::Ubuntu
253 } else {
254 DesktopEnv::Unknown(env)
255 }
256 }
257 None => DesktopEnv::Unknown("Unknown".to_string()),
259 }
260}
261
262#[cfg(target_os = "macos")]
263#[inline(always)]
264pub const fn platform() -> Platform {
265 Platform::MacOS
266}
267
268#[cfg(not(target_os = "macos"))]
269#[inline(always)]
270pub const fn platform() -> Platform {
271 Platform::Linux
272}