pub fn parse_command(
    command: &OsStr,
    argument: &OsStr,
    icon: Option<&OsStr>,
    desktop_entry_path: Option<&Path>,
) -> Vec<OsString> {
    log::debug!(
        "Parsing XDG Exec command {:?}, with argument {:?}",
        command,
        argument
    );

    // let command_name_re = byte_regex!(r#"^[^ \t"]+|"[^ \t]+""#);
    let mut escape_char = false;
    let mut reading_quoted = false;
    let mut reading_singlequoted = false;

    let mut parsed_command_parts = Vec::new();
    let mut text_atom = Vec::new();

    // I think doing it like this, although a bit big, is the clearest way to follow the scheme described on the
    // specification:
    // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
    // We even need to escape backslash TWICE when we're inside quotes, as it is written:
    // "Likewise, a literal dollar sign in a quoted argument in a desktop entry file is unambiguously represented with ("\\$")."
    //
    // The idea is to separate and unquote the arguments first, then do some regex replacements on the arguments individually.
    // The spec itself says "Implementations must undo quoting before expanding field codes..."
    for &c in command.as_bytes() {
        // If we are escaping something we will just let it pass
        if escape_char {
            text_atom.push(c);
            escape_char = false;
        // Otherwise, we have to pay special attention to backslash
        } else if c == b'\\' {
            // If we see a backslash and are not escaping anything we will not "read" the
            // backslash, and instead escape the next char.
            escape_char = true;
        // If we're reading a quoted argument ("like this")
        } else if reading_quoted {
            if c != b'"' {
                text_atom.push(c);
            } else {
                // When we find another ", we collected a text atom
                // If there is text we store it
                if text_atom.len() > 0 {
                    let text_atom_string = parse_quoted_text(
                        OsStr::from_bytes(&text_atom),
                        argument,
                        icon,
                        desktop_entry_path,
                    );
                    parsed_command_parts.push(text_atom_string);
                    text_atom.clear();
                }
                // And the quoted ended
                reading_quoted = false;
            }
        // If we're reading a singly quoted argument ('like this')
        } else if reading_singlequoted {
            // Same thing but for '
            if c != b'\'' {
                text_atom.push(c);
            } else {
                // When we find another ', we collected a text atom
                // If there is text we store it
                if text_atom.len() > 0 {
                    let text_atom_string = parse_quoted_text(
                        OsStr::from_bytes(&text_atom),
                        argument,
                        icon,
                        desktop_entry_path,
                    );
                    parsed_command_parts.push(text_atom_string);
                    text_atom.clear();
                }
                // And the quoting ended
                reading_singlequoted = false;
            }
        // If not quoting, or scaping, then space is a text atom separator
        } else if [b' ', b'\t', b'\n'].contains(&c) {
            // If there is text we store it
            if text_atom.len() > 0 {
                let text_atom_string = parse_unquoted_text(
                    OsStr::from_bytes(&text_atom),
                    argument,
                    icon,
                    desktop_entry_path,
                );
                parsed_command_parts.push(text_atom_string);
                text_atom.clear();
            }
        // If a non whitespace, nor backslash character, when we're neither escaping nor in quotes, then...
        } else {
            match c {
                b'"' => reading_quoted = true,
                b'\'' => reading_singlequoted = true,
                anything_else => text_atom.push(anything_else),
            }
        }
    } // End of iteration over the command's bytes

    // At the end of the loop we flush whatever was being accumulated to the command parts
    if text_atom.len() > 0 {
        // If the value was well formed, quoted strings end on a quote character, and
        // not on EOF, so this should be unquoted.
        let text_atom_string = parse_unquoted_text(
            OsStr::from_bytes(&text_atom),
            argument,
            icon,
            desktop_entry_path,
        );
        parsed_command_parts.push(text_atom_string);
        text_atom.clear();
    }

    log::debug!(
        "XDG parsed command {:?} to {:?}",
        command,
        parsed_command_parts
    );
    parsed_command_parts
}
// The exec field of the FreeDesktop entry may contain some flags that need to
// be replaced by parameters or even other stuff. I am trying to implement it
// all this time.
//
// This function kind of became a monster
The exec field of the FreeDesktop entry may contain some flags that need to
be replaced by parameters or even other stuff. I am trying to implement it
all this time.

This function kind of became a monster
The exec field of the FreeDesktop entry may contain some flags that need to be replaced by parameters or even other stuff . I am trying to implement it all this time . This function kind of became a monster
pub fn parse_command(
    command: &OsStr,
    argument: &OsStr,
    icon: Option<&OsStr>,
    desktop_entry_path: Option<&Path>,
) -> Vec<OsString> {
    log::debug!(
        "Parsing XDG Exec command {:?}, with argument {:?}",
        command,
        argument
    );
    // let command_name_re = byte_regex!(r#"^[^ \t"]+|"[^ \t]+""#);
    let mut escape_char = false;
    let mut reading_quoted = false;
    let mut reading_singlequoted = false;
    let mut parsed_command_parts = Vec::new();
    let mut text_atom = Vec::new();
    // I think doing it like this, although a bit big, is the clearest way to follow the scheme described on the
    // specification:
    // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
    // We even need to escape backslash TWICE when we're inside quotes, as it is written:
    // "Likewise, a literal dollar sign in a quoted argument in a desktop entry file is unambiguously represented with ("\\$")."
    //
    // The idea is to separate and unquote the arguments first, then do some regex replacements on the arguments individually.
    // The spec itself says "Implementations must undo quoting before expanding field codes..."
    for &c in command.as_bytes() {
        // If we are escaping something we will just let it pass
        if escape_char {
            text_atom.push(c);
            escape_char = false;
        // Otherwise, we have to pay special attention to backslash
        } else if c == b'\\' {
            // If we see a backslash and are not escaping anything we will not "read" the
            // backslash, and instead escape the next char.
            escape_char = true;
        // If we're reading a quoted argument ("like this")
        } else if reading_quoted {
            if c != b'"' {
                text_atom.push(c);
            } else {
                // When we find another ", we collected a text atom
                // If there is text we store it
                if text_atom.len() > 0 {
                    let text_atom_string = parse_quoted_text(
                        OsStr::from_bytes(&text_atom),
                        argument,
                        icon,
                        desktop_entry_path,
                    );
                    parsed_command_parts.push(text_atom_string);
                    text_atom.clear();
                }
                // And the quoted ended
                reading_quoted = false;
            }
        // If we're reading a singly quoted argument ('like this')
        } else if reading_singlequoted {
            // Same thing but for '
            if c != b'\'' {
                text_atom.push(c);
            } else {
                // When we find another ', we collected a text atom
                // If there is text we store it
                if text_atom.len() > 0 {
                    let text_atom_string = parse_quoted_text(
                        OsStr::from_bytes(&text_atom),
                        argument,
                        icon,
                        desktop_entry_path,
                    );
                    parsed_command_parts.push(text_atom_string);
                    text_atom.clear();
                }
                // And the quoting ended
                reading_singlequoted = false;
            }
        // If not quoting, or scaping, then space is a text atom separator
        } else if [b' ', b'\t', b'\n'].contains(&c) {
            // If there is text we store it
            if text_atom.len() > 0 {
                let text_atom_string = parse_unquoted_text(
                    OsStr::from_bytes(&text_atom),
                    argument,
                    icon,
                    desktop_entry_path,
                );
                parsed_command_parts.push(text_atom_string);
                text_atom.clear();
            }
        // If a non whitespace, nor backslash character, when we're neither escaping nor in quotes, then...
        } else {
            match c {
                b'"' => reading_quoted = true,
                b'\'' => reading_singlequoted = true,
                anything_else => text_atom.push(anything_else),
            }
        }
    } // End of iteration over the command's bytes
    // At the end of the loop we flush whatever was being accumulated to the command parts
    if text_atom.len() > 0 {
        // If the value was well formed, quoted strings end on a quote character, and
        // not on EOF, so this should be unquoted.
        let text_atom_string = parse_unquoted_text(
            OsStr::from_bytes(&text_atom),
            argument,
            icon,
            desktop_entry_path,
        );
        parsed_command_parts.push(text_atom_string);
        text_atom.clear();
    }
    log::debug!(
        "XDG parsed command {:?} to {:?}",
        command,
        parsed_command_parts
    );
    parsed_command_parts
}
pub fn parse_command ( command : & OsStr , argument : & OsStr , icon : Option < & OsStr > , desktop_entry_path : Option < & Path > , ) -> Vec < OsString > { log :: debug ! ( " " command argument ) ; // let command_name_re = byte_regex!(r#"^[^ \t"]+|"[^ \t]+""#); let mut escape_char = false ; let mut reading_quoted = false ; let mut reading_singlequoted = false ; let mut parsed_command_parts = Vec :: new ( ) ; let mut text_atom = Vec :: new ( ) ; // I think doing it like this, although a bit big, is the clearest way to follow the scheme described on the // specification: // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables // We even need to escape backslash TWICE when we're inside quotes, as it is written: // "Likewise, a literal dollar sign in a quoted argument in a desktop entry file is unambiguously represented with ("\\$")." // // The idea is to separate and unquote the arguments first, then do some regex replacements on the arguments individually. // The spec itself says "Implementations must undo quoting before expanding field codes..." for & c in command . as_bytes ( ) { // If we are escaping something we will just let it pass if escape_char { text_atom . push ( c ) ; escape_char = false ; // Otherwise, we have to pay special attention to backslash } else if c == b'\\' { // If we see a backslash and are not escaping anything we will not "read" the // backslash, and instead escape the next char. escape_char = true ; // If we're reading a quoted argument ("like this") } else if reading_quoted { if c != b'"' { text_atom . push ( c ) ; } else { // When we find another ", we collected a text atom // If there is text we store it if text_atom . len ( ) > 0 { let text_atom_string = parse_quoted_text ( OsStr :: from_bytes ( & text_atom ) , argument , icon , desktop_entry_path , ) ; parsed_command_parts . push ( text_atom_string ) ; text_atom . clear ( ) ; } // And the quoted ended reading_quoted = false ; } // If we're reading a singly quoted argument ('like this') } else if reading_singlequoted { // Same thing but for ' if c != b'\'' { text_atom . push ( c ) ; } else { // When we find another ', we collected a text atom // If there is text we store it if text_atom . len ( ) > 0 { let text_atom_string = parse_quoted_text ( OsStr :: from_bytes ( & text_atom ) , argument , icon , desktop_entry_path , ) ; parsed_command_parts . push ( text_atom_string ) ; text_atom . clear ( ) ; } // And the quoting ended reading_singlequoted = false ; } // If not quoting, or scaping, then space is a text atom separator } else if [ b' ' , b'\t' , b'\n' ] . contains ( & c ) { // If there is text we store it if text_atom . len ( ) > 0 { let text_atom_string = parse_unquoted_text ( OsStr :: from_bytes ( & text_atom ) , argument , icon , desktop_entry_path , ) ; parsed_command_parts . push ( text_atom_string ) ; text_atom . clear ( ) ; } // If a non whitespace, nor backslash character, when we're neither escaping nor in quotes, then... } else { match c { b'"' => reading_quoted = true , b'\'' => reading_singlequoted = true , anything_else => text_atom . push ( anything_else ) , } } } // End of iteration over the command's bytes // At the end of the loop we flush whatever was being accumulated to the command parts if text_atom . len ( ) > 0 { // If the value was well formed, quoted strings end on a quote character, and // not on EOF, so this should be unquoted. let text_atom_string = parse_unquoted_text ( OsStr :: from_bytes ( & text_atom ) , argument , icon , desktop_entry_path , ) ; parsed_command_parts . push ( text_atom_string ) ; text_atom . clear ( ) ; } log :: debug ! ( " " command parsed_command_parts ) ; parsed_command_parts }
The exec field of the FreeDesktop entry may contain some flags that need to
be replaced by parameters or even other stuff.
The exec field of the FreeDesktop entry may contain some flags that need to be replaced by parameters or even other stuff .
command
&OsStr
argument
&OsStr
icon
Option<&OsStr>
desktop_entry_path
Option<&Path>