compatdb_core/runtime/
process.rs1use crate::EnvEntry;
5
6#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12pub enum Arch {
13 X86_64,
16 I386,
18 Aarch64,
20}
21
22impl Arch {
23 #[must_use]
28 pub const fn pe_dir(self) -> &'static str {
29 match self {
30 Self::I386 => "i386-windows",
31 Self::X86_64 | Self::Aarch64 => "aarch64-windows",
32 }
33 }
34
35 #[must_use]
47 pub const fn of_process(wow64: bool, machine: Option<u16>) -> Self {
48 match (wow64, machine) {
49 (true, _) => Self::I386,
50 (false, Some(IMAGE_FILE_MACHINE_ARM64)) => Self::Aarch64,
51 _ => Self::X86_64,
52 }
53 }
54}
55
56pub const IMAGE_FILE_MACHINE_ARM64: u16 = 0xaa64;
58
59#[must_use]
62pub fn basename_of(image_path: &[u16]) -> String {
63 let start = image_path
64 .iter()
65 .rposition(|&u| u == u16::from(b'\\') || u == u16::from(b'/'))
66 .map_or(0, |i| i.saturating_add(1));
67 String::from_utf16_lossy(image_path.get(start..).unwrap_or(&[]))
68}
69
70#[must_use]
76pub fn append_cmdline(cmdline: &[u16], appends: &[impl AsRef<str>]) -> Option<Vec<u16>> {
77 append_cmdline_each(cmdline, appends).0
78}
79
80#[must_use]
83pub fn append_cmdline_each(
84 cmdline: &[u16],
85 appends: &[impl AsRef<str>],
86) -> (Option<Vec<u16>>, Vec<bool>) {
87 let mut cur = cmdline.to_vec();
88 let mut appended = Vec::with_capacity(appends.len());
89 for text in appends {
90 let text = text.as_ref();
91 if text.is_empty() || contains_cs(&cur, text) {
92 appended.push(false);
93 continue;
94 }
95 cur.push(u16::from(b' '));
96 cur.extend(text.encode_utf16());
97 appended.push(true);
98 }
99 (appended.contains(&true).then_some(cur), appended)
100}
101
102#[must_use]
106pub fn env_value(block: &[u16], name: &str) -> Option<String> {
107 let seg = split_env(block)
108 .into_iter()
109 .find(|seg| env_name_matches(seg, name))?;
110 let value = seg
111 .iter()
112 .position(|&u| u == u16::from(b'='))
113 .and_then(|i| seg.get(i.saturating_add(1)..))
114 .unwrap_or(&[]);
115 Some(String::from_utf16_lossy(value))
116}
117
118#[must_use]
124pub fn merge_env(block: &[u16], entries: &[EnvEntry]) -> Option<Vec<u16>> {
125 let mut segs = split_env(block);
126 let mut changed = false;
127 for entry in entries {
128 let name = entry.name().as_str();
129 let found = segs.iter().position(|seg| env_name_matches(seg, name));
130 let Some(value) = entry.value() else {
131 if let Some(i) = found {
132 segs.remove(i);
133 changed = true;
134 }
135 continue;
136 };
137 let mut new_seg: Vec<u16> = name.encode_utf16().collect();
138 new_seg.push(u16::from(b'='));
139 new_seg.extend(value.encode_utf16());
140 match found {
141 Some(i) if segs.get(i) == Some(&new_seg) => {}
142 Some(i) => {
143 if let Some(slot) = segs.get_mut(i) {
144 *slot = new_seg;
145 changed = true;
146 }
147 }
148 None => {
149 segs.push(new_seg);
150 changed = true;
151 }
152 }
153 }
154 if !changed {
155 return None;
156 }
157 let mut out = Vec::new();
158 for seg in &segs {
159 out.extend_from_slice(seg);
160 out.push(0);
161 }
162 out.push(0);
163 Some(out)
164}
165
166pub const fn to_lower_u16(u: u16) -> u16 {
168 if u >= b'A' as u16 && u <= b'Z' as u16 {
169 u | 0x20
170 } else {
171 u
172 }
173}
174
175fn contains_cs(haystack: &[u16], needle: &str) -> bool {
177 let n: Vec<u16> = needle.encode_utf16().collect();
178 if n.is_empty() {
179 return true;
180 }
181 if haystack.len() < n.len() {
182 return false;
183 }
184 haystack.windows(n.len()).any(|w| w == n.as_slice())
185}
186
187fn split_env(block: &[u16]) -> Vec<Vec<u16>> {
190 let mut segs = Vec::new();
191 let mut cur = Vec::new();
192 for &u in block {
193 if u == 0 {
194 if cur.is_empty() {
195 break;
196 }
197 segs.push(std::mem::take(&mut cur));
198 } else {
199 cur.push(u);
200 }
201 }
202 segs
203}
204
205fn env_name_matches(seg: &[u16], name: &str) -> bool {
208 if seg.first() == Some(&u16::from(b'=')) {
209 return false;
210 }
211 let seg_name = seg.split(|&u| u == u16::from(b'=')).next().unwrap_or(seg);
212 let want: Vec<u16> = name.encode_utf16().map(to_lower_u16).collect();
213 seg_name.len() == want.len()
214 && seg_name
215 .iter()
216 .map(|&u| to_lower_u16(u))
217 .eq(want.iter().copied())
218}
219
220#[cfg(test)]
221mod tests;