Skip to content

Commit

Permalink
windows: move away from ShellExecuteEx to AssocQueryStringW
Browse files Browse the repository at this point in the history
  • Loading branch information
amodm committed Dec 31, 2022
1 parent f617dbd commit ff7b51e
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 145 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -2,4 +2,5 @@
/Cargo.lock
/.idea/
/*.code-workspace
/screenshot.png
/screenshot.png
**/.DS_Store
3 changes: 1 addition & 2 deletions Cargo.toml
Expand Up @@ -33,8 +33,7 @@ dirs = "4.0"
core-foundation = "0.9"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["combaseapi", "impl-default", "objbase", "shellapi", "winerror"] }
widestring = { version = ">= 0.5, <=1.0" }
windows = { version = "0.43.0", features = ["Win32_UI_Shell"] }

[target.'cfg(target_os = "android")'.dependencies]
jni = "0.20"
Expand Down
90 changes: 90 additions & 0 deletions src/common.rs
@@ -0,0 +1,90 @@
use super::{BrowserOptions, Error, ErrorKind, Result};
use log::debug;
use std::process::{Command, Stdio};

/// Parses `line` to find tokens (including quoted strings), and invokes `op`
/// on each token
pub(crate) fn for_each_token<F>(line: &str, mut op: F)
where
F: FnMut(&str),
{
let mut start: Option<usize> = None;
let mut in_quotes = false;
let mut idx = 0;
for ch in line.chars() {
idx += 1;
match ch {
'"' => {
if let Some(start_idx) = start {
op(&line[start_idx..idx - 1]);
start = None;
in_quotes = false;
} else {
start = Some(idx);
in_quotes = true;
}
}
' ' => {
if !in_quotes {
if let Some(start_idx) = start {
op(&line[start_idx..idx - 1]);
start = None;
}
}
}
_ => {
if start.is_none() {
start = Some(idx - 1);
}
}
}
}
if let Some(start_idx) = start {
op(&line[start_idx..idx]);
}
}

/// Run the specified command in foreground/background
pub(crate) fn run_command(
cmd: &mut Command,
background: bool,
options: &BrowserOptions,
) -> Result<()> {
// if dry_run, we return a true, as executable existence check has
// already been done
if options.dry_run {
debug!("dry-run enabled, so not running: {:?}", &cmd);
return Ok(());
}

if background {
debug!("background spawn: {:?}", &cmd);
// if we're in background, set stdin/stdout to null and spawn a child, as we're
// not supposed to have any interaction.
if options.suppress_output {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
} else {
cmd
}
.spawn()
.map(|_| ())
} else {
debug!("foreground exec: {:?}", &cmd);
// if we're in foreground, use status() instead of spawn(), as we'd like to wait
// till completion.
// We also specifically don't suppress anything here, because we're running here
// most likely because of a text browser
cmd.status().and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"command present but exited unsuccessfully",
))
}
})
}
}
43 changes: 30 additions & 13 deletions src/lib.rs
Expand Up @@ -72,6 +72,16 @@ compile_error!(
"Only Windows, Mac OS, iOS, Linux, *BSD and Haiku and Wasm32 are currently supported"
);

#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "haiku",
target_os = "windows"
))]
pub(crate) mod common;

use std::convert::TryFrom;
use std::default::Default;
use std::fmt::Display;
Expand Down Expand Up @@ -337,6 +347,19 @@ impl TargetType {
Err(Error::new(ErrorKind::InvalidInput, "not an http url"))
}
}

#[cfg(not(target_family = "wasm"))]
fn from_file_path(value: &str) -> Result<Self> {
let pb = std::path::PathBuf::from(value);
let url = url::Url::from_file_path(if pb.is_relative() {
std::env::current_dir()?.join(pb)
} else {
pb
})
.map_err(|_| Error::new(ErrorKind::InvalidInput, "failed to convert path to url"))?;

Ok(Self(url))
}
}

impl Deref for TargetType {
Expand Down Expand Up @@ -366,21 +389,15 @@ impl TryFrom<&str> for TargetType {
#[cfg(not(target_family = "wasm"))]
fn try_from(value: &str) -> Result<Self> {
match url::Url::parse(value) {
Ok(u) => Ok(Self(u)),
Err(_) => {
// assume it to be a path if url parsing failed
let pb = std::path::PathBuf::from(value);
let url = url::Url::from_file_path(if pb.is_relative() {
std::env::current_dir()?.join(pb)
Ok(u) => {
if u.scheme().len() == 1 && cfg!(windows) {
// this can happen in windows that C:\abc.html gets parsed as scheme "C"
Self::from_file_path(value)
} else {
pb
})
.map_err(|_| {
Error::new(ErrorKind::InvalidInput, "failed to convert path to url")
})?;

Ok(Self(url))
Ok(Self(u))
}
}
Err(_) => Self::from_file_path(value),
}
}
}
Expand Down
87 changes: 3 additions & 84 deletions src/unix.rs
@@ -1,5 +1,6 @@
use crate::common::run_command;
use crate::{Browser, BrowserOptions, Error, ErrorKind, Result, TargetType};
use log::{debug, trace};
use log::trace;
use std::io::{BufRead, BufReader};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
Expand Down Expand Up @@ -402,47 +403,6 @@ where
err
}

/// Run the specified command in foreground/background
fn run_command(cmd: &mut Command, background: bool, options: &BrowserOptions) -> Result<()> {
// if dry_run, we return a true, as executable existence check has
// already been done
if options.dry_run {
debug!("dry-run enabled, so not running: {:?}", &cmd);
return Ok(());
}

if background {
debug!("background spawn: {:?}", &cmd);
// if we're in background, set stdin/stdout to null and spawn a child, as we're
// not supposed to have any interaction.
if options.suppress_output {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
} else {
cmd
}
.spawn()
.map(|_| ())
} else {
debug!("foreground exec: {:?}", &cmd);
// if we're in foreground, use status() instead of spawn(), as we'd like to wait
// till completion.
// We also specifically don't suppress anything here, because we're running here
// most likely because of a text browser
cmd.status().and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"command present but exited unsuccessfully",
))
}
})
}
}

static TEXT_BROWSERS: [&str; 9] = [
"lynx", "links", "links2", "elinks", "w3m", "eww", "netrik", "retawq", "curl",
];
Expand Down Expand Up @@ -564,6 +524,7 @@ Exec=/bin/ls
not(feature = "disable-wsl")
))]
mod wsl {
use crate::common::for_each_token;
use crate::{Result, TargetType};
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -699,48 +660,6 @@ mod wsl {
}
}

/// Parses `line` to find tokens (including quoted strings), and invokes `op`
/// on each token
fn for_each_token<F>(line: &str, mut op: F)
where
F: FnMut(&str),
{
let mut start: Option<usize> = None;
let mut in_quotes = false;
let mut idx = 0;
for ch in line.chars() {
idx += 1;
match ch {
'"' => {
if let Some(start_idx) = start {
op(&line[start_idx..idx - 1]);
start = None;
in_quotes = false;
} else {
start = Some(idx);
in_quotes = true;
}
}
' ' => {
if !in_quotes {
if let Some(start_idx) = start {
op(&line[start_idx..idx - 1]);
start = None;
}
}
}
_ => {
if start.is_none() {
start = Some(idx - 1);
}
}
}
}
if let Some(start_idx) = start {
op(&line[start_idx..idx]);
}
}

fn wsl_get_filepath_from_url(wc: &WindowsConfig, target: &TargetType) -> Result<String> {
let url = &target.0;
if url.scheme() == "file" {
Expand Down

0 comments on commit ff7b51e

Please sign in to comment.