use std::collections::HashSet;
use crate::{
error::Error,
records::{item::ItemBase, windows::Bitness},
util::{encoding::read_setup_string, read::Reader},
version::Version,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum RunWait {
UntilTerminated,
NoWait,
UntilIdle,
}
stable_name_enum!(RunWait, {
Self::UntilTerminated => "until_terminated",
Self::NoWait => "no_wait",
Self::UntilIdle => "until_idle",
});
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum RunFlag {
ShellExec,
SkipIfDoesntExist,
PostInstall,
Unchecked,
SkipIfSilent,
SkipIfNotSilent,
HideWizard,
Bits32,
Bits64,
RunAsOriginalUser,
DontLogParameters,
LogOutput,
}
stable_flag_enum!(RunFlag, {
ShellExec => "shell_exec",
SkipIfDoesntExist => "skip_if_doesnt_exist",
PostInstall => "post_install",
Unchecked => "unchecked",
SkipIfSilent => "skip_if_silent",
SkipIfNotSilent => "skip_if_not_silent",
HideWizard => "hide_wizard",
Bits32 => "bits32",
Bits64 => "bits64",
RunAsOriginalUser => "run_as_original_user",
DontLogParameters => "dont_log_parameters",
LogOutput => "log_output",
});
#[derive(Clone, Debug)]
pub struct RunEntry {
pub name: String,
pub parameters: String,
pub working_dir: String,
pub run_once_id: String,
pub status_message: String,
pub verb: String,
pub description: String,
pub on_log: String,
pub item: ItemBase,
pub show_command: i32,
pub wait: Option<RunWait>,
pub wait_raw: u8,
pub bitness: Option<Bitness>,
pub bitness_raw: u8,
pub flags: HashSet<RunFlag>,
pub options_raw: Vec<u8>,
}
impl RunEntry {
pub(crate) fn read(reader: &mut Reader<'_>, version: &Version) -> Result<Self, Error> {
let name = read_setup_string(reader, version, "Run.Name")?;
let parameters = read_setup_string(reader, version, "Run.Parameters")?;
let working_dir = read_setup_string(reader, version, "Run.WorkingDir")?;
let run_once_id = if version.at_least(1, 3, 9) {
read_setup_string(reader, version, "Run.RunOnceID")?
} else {
String::new()
};
let status_message = if version.at_least(2, 0, 2) {
read_setup_string(reader, version, "Run.StatusMsg")?
} else {
String::new()
};
let verb = if version.at_least(5, 1, 13) {
read_setup_string(reader, version, "Run.Verb")?
} else {
String::new()
};
let description = if version.at_least(2, 0, 0) || version.is_isx() {
read_setup_string(reader, version, "Run.Description")?
} else {
String::new()
};
let on_log = if version.at_least(7, 0, 0) {
read_setup_string(reader, version, "Run.OnLog")?
} else {
String::new()
};
let item = ItemBase::read(reader, version)?;
let show_command = if version.at_least(1, 3, 24) {
reader.i32_le("Run.ShowCmd")?
} else {
0
};
let wait_raw = reader.u8("Run.Wait")?;
let wait = decode_wait(wait_raw);
let (bitness, bitness_raw) = if version.at_least_4(7, 0, 0, 3) {
let raw = reader.u8("Run.Bitness")?;
(Bitness::decode(raw), raw)
} else {
(None, 0)
};
let table = run_flag_table(version);
let raw = reader.set_bytes(table.len(), true, "Run.Options")?;
let flags = super::decode_packed_flags(&raw, &table);
Ok(Self {
name,
parameters,
working_dir,
run_once_id,
status_message,
verb,
description,
on_log,
item,
show_command,
wait,
wait_raw,
bitness,
bitness_raw,
flags,
options_raw: raw,
})
}
}
fn decode_wait(b: u8) -> Option<RunWait> {
match b {
0 => Some(RunWait::UntilTerminated),
1 => Some(RunWait::NoWait),
2 => Some(RunWait::UntilIdle),
_ => None,
}
}
fn run_flag_table(version: &Version) -> Vec<RunFlag> {
let mut t = Vec::new();
if version.at_least(1, 2, 3) {
t.push(RunFlag::ShellExec);
}
if version.at_least(1, 3, 9) || (version.is_isx() && version.at_least(1, 3, 8)) {
t.push(RunFlag::SkipIfDoesntExist);
}
if version.at_least(2, 0, 0) {
t.push(RunFlag::PostInstall);
t.push(RunFlag::Unchecked);
t.push(RunFlag::SkipIfSilent);
t.push(RunFlag::SkipIfNotSilent);
}
if version.at_least(2, 0, 8) {
t.push(RunFlag::HideWizard);
}
if version.at_least(5, 1, 10) && !version.at_least_4(7, 0, 0, 3) {
t.push(RunFlag::Bits32);
t.push(RunFlag::Bits64);
}
if version.at_least(5, 2, 0) {
t.push(RunFlag::RunAsOriginalUser);
}
if version.at_least(6, 1, 0) {
t.push(RunFlag::DontLogParameters);
}
if version.at_least(6, 3, 0) {
t.push(RunFlag::LogOutput);
}
t
}