<commit_before>#[cfg(windows)]
use std::env;
use std::{path::Path,
          result};

#[cfg(windows)]
use crate::{common::cli::{DEFAULT_BINLINK_DIR,
                          FS_ROOT},
            hcore::fs};
use crate::{common::ui::{UIReader,
                         UIWriter,
                         UI},
            hcore::{crypto::SigKeyPair,
                    env as henv,
                    package::ident,
                    Error::InvalidOrigin}};
#[cfg(windows)]
use std::ptr;
use url::Url;
#[cfg(windows)]
use widestring::WideCString;
#[cfg(windows)]
use winapi::shared::minwindef::LPARAM;
#[cfg(windows)]
use winapi::um::winuser::{self,
                          HWND_BROADCAST,
                          SMTO_ABORTIFHUNG,
                          WM_SETTINGCHANGE};
#[cfg(windows)]
use winreg::enums::{HKEY_LOCAL_MACHINE,
                    KEY_ALL_ACCESS,
                    KEY_READ};
#[cfg(windows)]
use winreg::RegKey;

use crate::{command,
            config,
            error::Result,
            AUTH_TOKEN_ENVVAR,
            BLDR_URL_ENVVAR,
            CTL_SECRET_ENVVAR,
            ORIGIN_ENVVAR};

pub fn start(ui: &mut UI, cache_path: &Path) -> Result<()> {
    ui.br()?;
    ui.title("Habitat CLI Setup")?;
    ui.para("Welcome to hab setup. Let's get started.")?;

    ui.heading("Habitat Builder Instance")?;
    ui.para(
        "Habitat packages can be stored in either the public builder instance \
         https://bldr.habitat.sh or in an on-premises builder depot instance. If \
         you do not set a builder URL now, the `hab` CLI will default to using \
         the public builder instance. This can be overridden at any time after setup.",
    )?;
    if ask_default_builder_instance(ui)? {
        ui.br()?;
        ui.para("Enter the url of your builder instance. The default is \
                 https://bldr.habitat.sh. The configured endpoint can be overridden any time \
                 with a `HAB_BLDR_URL` envvar or a --url flag on the cli.")?;
        let mut url = prompt_url(ui)?;

        while valid_url(&url).is_err() {
            ui.br()?;
            ui.fatal(&format!("{}: is invalid, please provide a valid url", url))?;
            ui.br()?;

            url = prompt_url(ui)?;
        }

        write_cli_config_bldr_url(&url)?;
    } else {
        ui.br()?;
        ui.para("No worries, should you need to use a different bldr instance you can set a \
                 `HAB_BLDR_URL` envvar or pass the `--url` flag to the cli!")?;
    }

    ui.heading("Set up a default origin")?;
    ui.para("Every package in Habitat belongs to an origin, which indicates the person or \
             organization responsible for maintaining that package. Each origin also has a key \
             used to cryptographically sign packages in that origin.")?;
    ui.para("Selecting a default origin tells package building operations such as 'hab pkg \
             build' what key should be used to sign the packages produced. If you do not set a \
             default origin now, you will have to tell package building commands each time what \
             origin to use.")?;
    ui.para("For more information on origins and how they are used in building packages, please \
             consult the docs at https://www.habitat.sh/docs/create-packages-build/")?;
    if ask_default_origin(ui)? {
        ui.br()?;
        ui.para("Enter the name of your origin. If you plan to publish your packages publicly, \
                 we recommend that you select one that is not already in use on the Habitat \
                 build service found at https://bldr.habitat.sh/.")?;
        ui.para("Origins must begin with a lowercase letter or number. Allowed characters \
                 include lowercase letters, numbers, _, -. No more than 255 characters.")?;
        let mut origin = prompt_origin(ui)?;

        while !ident::is_valid_origin_name(&origin) {
            ui.br()?;
            ui.fatal(&format!("{}", InvalidOrigin(origin)))?;
            ui.br()?;

            origin = prompt_origin(ui)?;
        }
        write_cli_config_origin(&origin)?;
        ui.br()?;
        if is_origin_in_cache(&origin, cache_path) {
            ui.para(&format!("You already have an origin key for {} created and installed. \
                              Great work!",
                             &origin))?;
        } else {
            ui.heading("Create origin key pair")?;
            ui.para(&format!("It doesn't look like you have a signing key for the origin `{}'. \
                              Without it, you won't be able to build new packages successfully.",
                             &origin))?;
            ui.para("You can either create a new signing key now, or, if you are building \
                     packages for an origin that already exists, ask the owner to give you the \
                     signing key.")?;
            ui.para("For more information on the use of origin keys, please consult the \
                     documentation at https://www.habitat.sh/docs/concepts-keys/#origin-keys")?;
            if ask_create_origin(ui, &origin)? {
                create_origin(ui, &origin, cache_path)?;
            } else {
                ui.para(&format!("You might want to create an origin key later with: `hab \
                                  origin key generate {}'",
                                 &origin))?;
            }
        }
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    ui.heading("Habitat Personal Access Token")?;
    ui.para("While you can perform tasks like building and running Habitat packages without \
             needing to authenticate with Builder, some operations like uploading your packages \
             to Builder, or checking the status of your build jobs from the Habitat client will \
             require you to use an access token.")?;
    ui.para("The Habitat Personal Access Token can be generated via the Builder  Profile page \
             (https://bldr.habitat.sh/#/profile). Once you have generated your token, you can \
             enter it here.")?;
    ui.para("If you would like to save your token for use by the Habitat client, please enter \
             your access token. Otherwise, just enter No.")?;
    ui.para(
        "For more information on using Builder, please read the \
         documentation at https://www.habitat.sh/docs/using-builder/",
    )?;
    if ask_default_auth_token(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Personal Access Token.")?;
        let auth_token = prompt_auth_token(ui)?;
        write_cli_config_auth_token(&auth_token)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    if ask_default_ctl_secret(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Supervisor CtlGateway secret.")?;
        let ctl_secret = prompt_ctl_secret(ui)?;
        write_cli_config_ctl_secret(&ctl_secret)?;
    } else {
        ui.para("Okay, maybe another time.")?;
    }
    #[cfg(windows)]
    {
        let binlink_path =
            Path::new(&*FS_ROOT).join(Path::new(DEFAULT_BINLINK_DIR).strip_prefix("/").unwrap());
        ui.heading("Habitat Binlink Path")?;
        ui.para("The `hab` command-line tool can create binlinks for package binaries in the \
                 'PATH' when using the 'pkg binlink' or 'pkg install --binlink' commands. By \
                 default, Habitat will create these binlinks in the '/hab/bin' directory. This \
                 directory will always be included in your 'PATH' when inside a Studio \
                 environment. However, you will want this directory on your machine's \
                 persistent 'PATH' in order to access binlinks outside of a Studio.")?;
        if ui.prompt_yes_no("Add binlink directory to PATH?", Some(true))? {
            if !fs::am_i_root() {
                ui.fatal("You must be in an elevated console running as an administrator in \
                          order to set the machine's PATH. Please open a new console running as \
                          administrator and run setup again to add the Habitat binlink \
                          directory to your path.")?;
            } else {
                set_binlink_path(&binlink_path)?;
                ui.para(&format!("{} has been added to your path. You will need to open a new \
                                  console window for this added entry to take effect.",
                                 binlink_path.display()))?;
            }
        } else {
            ui.para("Okay, maybe another time.")?;
        }
    }
    ui.heading("CLI Setup Complete")?;
    ui.para("That's all for now. Thanks for using Habitat!")?;
    Ok(())
}

fn ask_default_origin(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default origin?", Some(true))?)
}

fn ask_default_builder_instance(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Connect to an on-premises bldr instance?", Some(true))?)
}

fn ask_create_origin(ui: &mut UI, origin: &str) -> Result<bool> {
    Ok(ui.prompt_yes_no(&format!("Create an origin key for `{}'?", origin),
                        Some(true))?)
}

fn write_cli_config_origin(origin: &str) -> Result<()> {
    let mut config = config::load()?;
    config.origin = Some(origin.to_string());
    config::save(&config)
}

fn write_cli_config_bldr_url(url: &str) -> Result<()> {
    let mut config = config::load()?;
    config.bldr_url = Some(url.to_string());
    config::save(&config)
}

fn write_cli_config_auth_token(auth_token: &str) -> Result<()> {
    let mut config = config::load()?;
    config.auth_token = Some(auth_token.to_string());
    config::save(&config)
}

fn write_cli_config_ctl_secret(value: &str) -> Result<()> {
    let mut config = config::load()?;
    config.ctl_secret = Some(value.to_string());
    config::save(&config)
}

fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
    match SigKeyPair::get_latest_pair_for(origin, cache_path, None) {
        Ok(pair) => {
            match pair.secret() {
                Ok(_) => true,
                _ => false,
            }
        }
        _ => false,
    }
}

fn create_origin(ui: &mut UI, origin: &str, cache_path: &Path) -> Result<()> {
    let result = command::origin::key::generate::start(ui, &origin, cache_path);
    ui.br()?;
    result
}

fn prompt_origin(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.origin {
        Some(o) => {
            ui.para(&format!("You already have a default origin set up as `{}', but feel free \
                              to change it if you wish.",
                             &o))?;
            Some(o)
        }
        None => henv::var(ORIGIN_ENVVAR).or_else(|_| henv::var("USER")).ok(),
    };
    Ok(ui.prompt_ask("Default origin name", default.as_ref().map(|x| &**x))?)
}

fn ask_default_auth_token(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default Habitat personal access token?",
                        Some(true))?)
}

fn ask_default_ctl_secret(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default Habitat Supervisor CtlGateway secret?",
                        Some(true))?)
}

fn prompt_url(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.bldr_url {
        Some(u) => {
            ui.para("You already have a default builder url set up, but feel free to change it \
                     if you wish.")?;
            Some(u)
        }
        None => henv::var(BLDR_URL_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask("Private builder url", default.as_ref().map(|x| &**x))?)
}

fn prompt_auth_token(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.auth_token {
        Some(o) => {
            ui.para("You already have a default auth token set up, but feel free to change it \
                     if you wish.")?;
            Some(o)
        }
        None => henv::var(AUTH_TOKEN_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask("Habitat personal access token",
                     default.as_ref().map(|x| &**x))?)
}

fn prompt_ctl_secret(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.ctl_secret {
        Some(o) => {
            ui.para(
                    "You already have a default CtlGateway secret set up, but feel free to \
                     change it
                if you wish.",
            )?;
            Some(o)
        }
        None => henv::var(CTL_SECRET_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask("Habitat Supervisor CtlGateway secret",
                     default.as_ref().map(|x| &**x))?)
}

fn valid_url(val: &str) -> result::Result<(), String> {
    match Url::parse(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("URL: '{}' is not valid", &val)),
    }
}

#[cfg(windows)]
fn binlink_is_on_path(binlink_path: &Path) -> bool {
    match RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey_with_flags(
        r"System\CurrentControlSet\Control\Session Manager\Environment",
        KEY_READ,
    ) {
        Ok(env) => {
            let path: String = env
                .get_value("path")
                .expect("could not find a machine PATH");
            env::split_paths(&path).any(|p| p == binlink_path)
        }
        _ => false,
    }
}

/// this sets the permanent machine PATH and not
/// the path of this process. These are maintained
/// in the Windows registry
#[cfg(windows)]
fn set_binlink_path(binlink_path: &Path) -> Result<()> {
    if !binlink_is_on_path(binlink_path) {
        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
        let env = hklm.open_subkey_with_flags(
            r"System\CurrentControlSet\Control\Session Manager\Environment",
            KEY_ALL_ACCESS,
        )?;
        let mut paths = vec![binlink_path.to_path_buf()];
        let path: String = env.get_value("path")?;
        paths.append(&mut env::split_paths(&path).collect());
        env.set_value("path", &env::join_paths(paths)?.to_str().unwrap())?;

        // After altering the machine environment, we must broadcast
        // a WM_SETTINGCHANGE message to all windows so the user
        // will not need to sign out/in for the new path to take effect
        unsafe {
            winuser::SendMessageTimeoutW(HWND_BROADCAST,
                                         WM_SETTINGCHANGE,
                                         0,
                                         WideCString::from_str("Environment").unwrap().as_ptr()
                                         as LPARAM,
                                         SMTO_ABORTIFHUNG,
                                         5000,
                                         ptr::null_mut());
        }
    }
    Ok(())
}
<commit_msg>modification to hab setup defaults and language<commit_after>#[cfg(windows)]
use std::env;
use std::{path::Path,
          result};

#[cfg(windows)]
use crate::{common::cli::{DEFAULT_BINLINK_DIR,
                          FS_ROOT},
            hcore::fs};
use crate::{common::ui::{UIReader,
                         UIWriter,
                         UI},
            hcore::{crypto::SigKeyPair,
                    env as henv,
                    package::ident,
                    Error::InvalidOrigin}};
#[cfg(windows)]
use std::ptr;
use url::Url;
#[cfg(windows)]
use widestring::WideCString;
#[cfg(windows)]
use winapi::shared::minwindef::LPARAM;
#[cfg(windows)]
use winapi::um::winuser::{self,
                          HWND_BROADCAST,
                          SMTO_ABORTIFHUNG,
                          WM_SETTINGCHANGE};
#[cfg(windows)]
use winreg::enums::{HKEY_LOCAL_MACHINE,
                    KEY_ALL_ACCESS,
                    KEY_READ};
#[cfg(windows)]
use winreg::RegKey;

use crate::{command,
            config,
            error::Result,
            AUTH_TOKEN_ENVVAR,
            BLDR_URL_ENVVAR,
            CTL_SECRET_ENVVAR,
            ORIGIN_ENVVAR};

pub fn start(ui: &mut UI, cache_path: &Path) -> Result<()> {
    ui.br()?;
    ui.title("Habitat CLI Setup")?;
    ui.para("Welcome to hab setup. Let's get started.")?;

    ui.heading("Habitat Builder Instance")?;
    ui.para(
        "Habitat packages can be stored in either the public builder instance \
         https://bldr.habitat.sh or in an on-premises builder depot instance. If \
         you do not set a builder URL now, the `hab` CLI will default to using \
         the public builder instance. This can be overridden at any time after setup.",
    )?;
    if ask_default_builder_instance(ui)? {
        ui.br()?;
        ui.para("Enter the url of your builder instance. The default is \
                 https://bldr.habitat.sh. The configured endpoint can be overridden any time \
                 with a `HAB_BLDR_URL` environment variable or a --url flag on the cli.")?;
        let mut url = prompt_url(ui)?;

        while valid_url(&url).is_err() {
            ui.br()?;
            ui.fatal(&format!("{}: is invalid, please provide a valid url", url))?;
            ui.br()?;

            url = prompt_url(ui)?;
        }

        write_cli_config_bldr_url(&url)?;
    } else {
        ui.br()?;
        ui.para("Alright, maybe another time. You can also set a `HAB_BLDR_URL` environment \
                 variable or pass the `--url` flag to the cli.")?;
    }

    ui.heading("Set up a default origin")?;
    ui.para("Every package in Habitat belongs to an origin, which indicates the person or \
             organization responsible for maintaining that package. Each origin also has a key \
             used to cryptographically sign packages in that origin.")?;
    ui.para("Selecting a default origin tells package building operations such as 'hab pkg \
             build' what key should be used to sign the packages produced. If you do not set a \
             default origin now, you will have to tell package building commands each time what \
             origin to use.")?;
    ui.para("For more information on origins and how they are used in building packages, please \
             consult the docs at https://www.habitat.sh/docs/create-packages-build/")?;
    if ask_default_origin(ui)? {
        ui.br()?;
        ui.para("Enter the name of your origin. If you plan to publish your packages publicly, \
                 we recommend that you select one that is not already in use on the Habitat \
                 build service found at https://bldr.habitat.sh/.")?;
        ui.para("Origins must begin with a lowercase letter or number. Allowed characters \
                 include lowercase letters, numbers, _, -. No more than 255 characters.")?;
        let mut origin = prompt_origin(ui)?;

        while !ident::is_valid_origin_name(&origin) {
            ui.br()?;
            ui.fatal(&format!("{}", InvalidOrigin(origin)))?;
            ui.br()?;

            origin = prompt_origin(ui)?;
        }
        write_cli_config_origin(&origin)?;
        ui.br()?;
        if is_origin_in_cache(&origin, cache_path) {
            ui.para(&format!("You already have an origin key for {} created and installed. \
                              Great work!",
                             &origin))?;
        } else {
            ui.heading("Create origin key pair")?;
            ui.para(&format!("It doesn't look like you have a signing key for the origin `{}'. \
                              Without it, you won't be able to build new packages successfully.",
                             &origin))?;
            ui.para("You can either create a new signing key now, or, if you are building \
                     packages for an origin that already exists, ask the owner to give you the \
                     signing key.")?;
            ui.para("For more information on the use of origin keys, please consult the \
                     documentation at https://www.habitat.sh/docs/concepts-keys/#origin-keys")?;
            if ask_create_origin(ui, &origin)? {
                create_origin(ui, &origin, cache_path)?;
            } else {
                ui.para(&format!("You might want to create an origin key later with: `hab \
                                  origin key generate {}'",
                                 &origin))?;
            }
        }
    } else {
        ui.para("Alright, maybe another time. You can also set a `HAB_ORIGIN` environment \
                 variable when using the cli.")?;
    }
    ui.heading("Habitat Personal Access Token")?;
    ui.para("While you can perform tasks like building and running Habitat packages without \
             needing to authenticate with Builder, some operations like uploading your packages \
             to Builder, or checking the status of your build jobs from the Habitat client will \
             require you to use an access token.")?;
    ui.para("The Habitat Personal Access Token can be generated via the Builder  Profile page \
             (https://bldr.habitat.sh/#/profile). Once you have generated your token, you can \
             enter it here.")?;
    ui.para("If you would like to save your token for use by the Habitat client, please enter \
             your access token. Otherwise, just enter No.")?;
    ui.para(
        "For more information on using Builder, please read the \
         documentation at https://www.habitat.sh/docs/using-builder/",
    )?;
    if ask_default_auth_token(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Personal Access Token.")?;
        let auth_token = prompt_auth_token(ui)?;
        write_cli_config_auth_token(&auth_token)?;
    } else {
        ui.para("Alright, maybe another time. You can also set a `HAB_AUTH_TOKEN` environment \
                 variable or pass the `--auth` flag to the cli.")?;
    }
    ui.heading("Supervisor Control Gateway Secret")?;
    ui.para("The Supervisor control gateway secret is used to authenticate hab client commands \
             to a Supervisor. When a new Supervisor is created, a unique secret is generated \
             automatically and stored locally in a file: `/hab/sup/default/CTL_SECRET`. When \
             issuing commands to a local Supervisor, the control gateway secret is retrieved \
             from this file. Typically, a default Supervisor control gateway secret would only \
             be needed if you wish to send commands to a remote supervisor, in which case, a \
             default control gateway secret would need to match the one already in use by the \
             remote supervisor.")?;
    if ask_default_ctl_secret(ui)? {
        ui.br()?;
        ui.para("Enter your Habitat Supervisor control gateway secret.")?;
        let ctl_secret = prompt_ctl_secret(ui)?;
        write_cli_config_ctl_secret(&ctl_secret)?;
    } else {
        ui.para("Alright, maybe another time. You can also set a `HAB_CTL_SECRET` environment \
                 variable when issuing commands to a remote Supervisor.")?;
    }
    #[cfg(windows)]
    {
        let binlink_path =
            Path::new(&*FS_ROOT).join(Path::new(DEFAULT_BINLINK_DIR).strip_prefix("/").unwrap());
        ui.heading("Habitat Binlink Path")?;
        ui.para("The `hab` command-line tool can create binlinks for package binaries in the \
                 'PATH' when using the 'pkg binlink' or 'pkg install --binlink' commands. By \
                 default, Habitat will create these binlinks in the '/hab/bin' directory. This \
                 directory will always be included in your 'PATH' when inside a Studio \
                 environment. However, you will want this directory on your machine's \
                 persistent 'PATH' in order to access binlinks outside of a Studio.")?;
        if ui.prompt_yes_no("Add binlink directory to PATH?", Some(true))? {
            if !fs::am_i_root() {
                ui.fatal("You must be in an elevated console running as an administrator in \
                          order to set the machine's PATH. Please open a new console running as \
                          administrator and run setup again to add the Habitat binlink \
                          directory to your path.")?;
            } else {
                set_binlink_path(&binlink_path)?;
                ui.para(&format!("{} has been added to your path. You will need to open a new \
                                  console window for this added entry to take effect.",
                                 binlink_path.display()))?;
            }
        } else {
            ui.para("Alright, maybe another time.")?;
        }
    }
    ui.heading("CLI Setup Complete")?;
    ui.para("That's all for now. Thanks for using Habitat!")?;
    Ok(())
}

fn ask_default_origin(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default origin?", Some(true))?)
}

fn ask_default_builder_instance(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Connect to an on-premises Builder instance?", Some(false))?)
}

fn ask_create_origin(ui: &mut UI, origin: &str) -> Result<bool> {
    Ok(ui.prompt_yes_no(&format!("Create an origin key for `{}'?", origin),
                        Some(true))?)
}

fn write_cli_config_origin(origin: &str) -> Result<()> {
    let mut config = config::load()?;
    config.origin = Some(origin.to_string());
    config::save(&config)
}

fn write_cli_config_bldr_url(url: &str) -> Result<()> {
    let mut config = config::load()?;
    config.bldr_url = Some(url.to_string());
    config::save(&config)
}

fn write_cli_config_auth_token(auth_token: &str) -> Result<()> {
    let mut config = config::load()?;
    config.auth_token = Some(auth_token.to_string());
    config::save(&config)
}

fn write_cli_config_ctl_secret(value: &str) -> Result<()> {
    let mut config = config::load()?;
    config.ctl_secret = Some(value.to_string());
    config::save(&config)
}

fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
    match SigKeyPair::get_latest_pair_for(origin, cache_path, None) {
        Ok(pair) => {
            match pair.secret() {
                Ok(_) => true,
                _ => false,
            }
        }
        _ => false,
    }
}

fn create_origin(ui: &mut UI, origin: &str, cache_path: &Path) -> Result<()> {
    let result = command::origin::key::generate::start(ui, &origin, cache_path);
    ui.br()?;
    result
}

fn prompt_origin(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.origin {
        Some(o) => {
            ui.para(&format!("You already have a default origin set up as `{}', but feel free \
                              to change it if you wish.",
                             &o))?;
            Some(o)
        }
        None => henv::var(ORIGIN_ENVVAR).or_else(|_| henv::var("USER")).ok(),
    };
    Ok(ui.prompt_ask("Default origin name", default.as_ref().map(|x| &**x))?)
}

fn ask_default_auth_token(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default Builder personal access token?",
                        Some(false))?)
}

fn ask_default_ctl_secret(ui: &mut UI) -> Result<bool> {
    Ok(ui.prompt_yes_no("Set up a default Habitat Supervisor control gateway secret?",
                        Some(false))?)
}

fn prompt_url(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.bldr_url {
        Some(u) => {
            ui.para("You already have a default builder url set up, but feel free to change it \
                     if you wish.")?;
            Some(u)
        }
        None => henv::var(BLDR_URL_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask("Private builder url", default.as_ref().map(|x| &**x))?)
}

fn prompt_auth_token(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.auth_token {
        Some(o) => {
            ui.para("You already have a default auth token set up, but feel free to change it \
                     if you wish.")?;
            Some(o)
        }
        None => henv::var(AUTH_TOKEN_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask("Habitat personal access token",
                     default.as_ref().map(|x| &**x))?)
}

fn prompt_ctl_secret(ui: &mut UI) -> Result<String> {
    let config = config::load()?;
    let default = match config.ctl_secret {
        Some(o) => {
            ui.para(
                    "You already have a default control gateway secret set up, but feel free to \
                     change it
                if you wish.",
            )?;
            Some(o)
        }
        None => henv::var(CTL_SECRET_ENVVAR).ok(),
    };
    Ok(ui.prompt_ask("Habitat Supervisor control gateway secret",
                     default.as_ref().map(|x| &**x))?)
}

fn valid_url(val: &str) -> result::Result<(), String> {
    match Url::parse(&val) {
        Ok(_) => Ok(()),
        Err(_) => Err(format!("URL: '{}' is not valid", &val)),
    }
}

#[cfg(windows)]
fn binlink_is_on_path(binlink_path: &Path) -> bool {
    match RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey_with_flags(
        r"System\CurrentControlSet\Control\Session Manager\Environment",
        KEY_READ,
    ) {
        Ok(env) => {
            let path: String = env
                .get_value("path")
                .expect("could not find a machine PATH");
            env::split_paths(&path).any(|p| p == binlink_path)
        }
        _ => false,
    }
}

/// this sets the permanent machine PATH and not
/// the path of this process. These are maintained
/// in the Windows registry
#[cfg(windows)]
fn set_binlink_path(binlink_path: &Path) -> Result<()> {
    if !binlink_is_on_path(binlink_path) {
        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
        let env = hklm.open_subkey_with_flags(
            r"System\CurrentControlSet\Control\Session Manager\Environment",
            KEY_ALL_ACCESS,
        )?;
        let mut paths = vec![binlink_path.to_path_buf()];
        let path: String = env.get_value("path")?;
        paths.append(&mut env::split_paths(&path).collect());
        env.set_value("path", &env::join_paths(paths)?.to_str().unwrap())?;

        // After altering the machine environment, we must broadcast
        // a WM_SETTINGCHANGE message to all windows so the user
        // will not need to sign out/in for the new path to take effect
        unsafe {
            winuser::SendMessageTimeoutW(HWND_BROADCAST,
                                         WM_SETTINGCHANGE,
                                         0,
                                         WideCString::from_str("Environment").unwrap().as_ptr()
                                         as LPARAM,
                                         SMTO_ABORTIFHUNG,
                                         5000,
                                         ptr::null_mut());
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Gecko's media-query device and expression representation.

use crate::custom_properties::CssEnvironment;
use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs;
use crate::media_queries::MediaType;
use crate::properties::ComputedValues;
use crate::string_cache::Atom;
use crate::values::computed::font::FontSize;
use crate::values::{CustomIdent, KeyframesName};
use app_units::Au;
use app_units::AU_PER_PX;
use cssparser::RGBA;
use euclid::Size2D;
use euclid::TypedScale;
use servo_arc::Arc;
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel};

/// The `Device` in Gecko wraps a pres context, has a default values computed,
/// and contains all the viewport rule state.
pub struct Device {
    /// NB: The document owns the styleset, who owns the stylist, and thus the
    /// `Device`, so having a raw document pointer here is fine.
    document: *const structs::Document,
    default_values: Arc<ComputedValues>,
    /// The font size of the root element
    /// This is set when computing the style of the root
    /// element, and used for rem units in other elements.
    ///
    /// When computing the style of the root element, there can't be any
    /// other style being computed at the same time, given we need the style of
    /// the parent to compute everything else. So it is correct to just use
    /// a relaxed atomic here.
    root_font_size: AtomicIsize,
    /// The body text color, stored as an `nscolor`, used for the "tables
    /// inherit from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    body_text_color: AtomicUsize,
    /// Whether any styles computed in the document relied on the root font-size
    /// by using rem units.
    used_root_font_size: AtomicBool,
    /// Whether any styles computed in the document relied on the viewport size
    /// by using vw/vh/vmin/vmax units.
    used_viewport_size: AtomicBool,
    /// The CssEnvironment object responsible of getting CSS environment
    /// variables.
    environment: CssEnvironment,
}

impl fmt::Debug for Device {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use nsstring::nsCString;

        let mut doc_uri = nsCString::new();
        unsafe {
            bindings::Gecko_nsIURI_Debug(
                (*self.document()).mDocumentURI.raw::<structs::nsIURI>(),
                &mut doc_uri,
            )
        };

        f.debug_struct("Device")
            .field("document_url", &doc_uri)
            .finish()
    }
}

unsafe impl Sync for Device {}
unsafe impl Send for Device {}

impl Device {
    /// Trivially constructs a new `Device`.
    pub fn new(document: *const structs::Document) -> Self {
        assert!(!document.is_null());
        let doc = unsafe { &*document };
        let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };
        Device {
            document,
            default_values: ComputedValues::default_values(doc),
            // FIXME(bz): Seems dubious?
            root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
            body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
            used_root_font_size: AtomicBool::new(false),
            used_viewport_size: AtomicBool::new(false),
            environment: CssEnvironment,
        }
    }

    /// Get the relevant environment to resolve `env()` functions.
    #[inline]
    pub fn environment(&self) -> &CssEnvironment {
        &self.environment
    }

    /// Tells the device that a new viewport rule has been found, and stores the
    /// relevant viewport constraints.
    pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
        unreachable!("Gecko doesn't support @viewport");
    }

    /// Whether any animation name may be referenced from the style of any
    /// element.
    pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return false,
        };

        unsafe {
            bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())
        }
    }

    /// Returns the default computed values as a reference, in order to match
    /// Servo.
    pub fn default_computed_values(&self) -> &ComputedValues {
        &self.default_values
    }

    /// Returns the default computed values as an `Arc`.
    pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> {
        &self.default_values
    }

    /// Get the font size of the root element (for rem)
    pub fn root_font_size(&self) -> Au {
        self.used_root_font_size.store(true, Ordering::Relaxed);
        Au::new(self.root_font_size.load(Ordering::Relaxed) as i32)
    }

    /// Set the font size of the root element (for rem)
    pub fn set_root_font_size(&self, size: Au) {
        self.root_font_size
            .store(size.0 as isize, Ordering::Relaxed)
    }

    /// Sets the body text color for the "inherit color from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    pub fn set_body_text_color(&self, color: RGBA) {
        self.body_text_color
            .store(convert_rgba_to_nscolor(&color) as usize, Ordering::Relaxed)
    }

    /// Returns the body text color.
    pub fn body_text_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.body_text_color.load(Ordering::Relaxed) as u32)
    }

    /// Gets the document pointer.
    #[inline]
    pub fn document(&self) -> &structs::Document {
        unsafe { &*self.document }
    }

    /// Gets the pres context associated with this document.
    #[inline]
    pub fn pres_context(&self) -> Option<&structs::nsPresContext> {
        unsafe {
            self.document()
                .mPresShell
                .as_ref()?
                ._base
                .mPresContext
                .mRawPtr
                .as_ref()
        }
    }

    /// Gets the preference stylesheet prefs for our document.
    #[inline]
    pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs {
        unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) }
    }

    /// Recreates the default computed values.
    pub fn reset_computed_values(&mut self) {
        self.default_values = ComputedValues::default_values(self.document());
    }

    /// Rebuild all the cached data.
    pub fn rebuild_cached_data(&mut self) {
        self.reset_computed_values();
        self.used_root_font_size.store(false, Ordering::Relaxed);
        self.used_viewport_size.store(false, Ordering::Relaxed);
    }

    /// Returns whether we ever looked up the root font size of the Device.
    pub fn used_root_font_size(&self) -> bool {
        self.used_root_font_size.load(Ordering::Relaxed)
    }

    /// Recreates all the temporary state that the `Device` stores.
    ///
    /// This includes the viewport override from `@viewport` rules, and also the
    /// default computed values.
    pub fn reset(&mut self) {
        self.reset_computed_values();
    }

    /// Returns the current media type of the device.
    pub fn media_type(&self) -> MediaType {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return MediaType::screen(),
        };

        // Gecko allows emulating random media with mIsEmulatingMedia and
        // mMediaEmulated.
        let medium_to_use = if pc.mIsEmulatingMedia() != 0 {
            pc.mMediaEmulated.mRawPtr
        } else {
            pc.mMedium
        };

        MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
    }

    /// Returns the current viewport size in app units.
    pub fn au_viewport_size(&self) -> Size2D<Au> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Size2D::new(Au(0), Au(0)),
        };
        let area = &pc.mVisibleArea;
        Size2D::new(Au(area.width), Au(area.height))
    }

    /// Returns the current viewport size in app units, recording that it's been
    /// used for viewport unit resolution.
    pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
        self.used_viewport_size.store(true, Ordering::Relaxed);
        self.au_viewport_size()
    }

    /// Returns whether we ever looked up the viewport size of the Device.
    pub fn used_viewport_size(&self) -> bool {
        self.used_viewport_size.load(Ordering::Relaxed)
    }

    /// Returns the device pixel ratio.
    pub fn device_pixel_ratio(&self) -> TypedScale<f32, CSSPixel, DevicePixel> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return TypedScale::new(1.),
        };

        let override_dppx = pc.mOverrideDPPX;
        if override_dppx > 0.0 {
            return TypedScale::new(override_dppx);
        }

        let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;
        let au_per_px = AU_PER_PX as f32;
        TypedScale::new(au_per_px / au_per_dpx)
    }

    /// Returns whether document colors are enabled.
    #[inline]
    pub fn use_document_colors(&self) -> bool {
        let doc = self.document();
        if doc.mIsBeingUsedAsImage() {
            return true;
        }
        let document_color_use =
            unsafe { structs::StaticPrefs_sVarCache_browser_display_document_color_use };
        let prefs = self.pref_sheet_prefs();
        match document_color_use {
            1 => true,
            2 => prefs.mIsChrome,
            _ => !prefs.mUseAccessibilityTheme,
        }
    }

    /// Returns the default background color.
    pub fn default_background_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
    }

    /// Returns the current effective text zoom.
    #[inline]
    fn effective_text_zoom(&self) -> f32 {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return 1.,
        };
        pc.mEffectiveTextZoom
    }

    /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).
    #[inline]
    pub fn zoom_text(&self, size: Au) -> Au {
        size.scale_by(self.effective_text_zoom())
    }

    /// Un-apply text zoom.
    #[inline]
    pub fn unzoom_text(&self, size: Au) -> Au {
        size.scale_by(1. / self.effective_text_zoom())
    }
}
<commit_msg>Move all remaining members of nsIPresShell to mozilla::PresShell<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Gecko's media-query device and expression representation.

use crate::custom_properties::CssEnvironment;
use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs;
use crate::media_queries::MediaType;
use crate::properties::ComputedValues;
use crate::string_cache::Atom;
use crate::values::computed::font::FontSize;
use crate::values::{CustomIdent, KeyframesName};
use app_units::Au;
use app_units::AU_PER_PX;
use cssparser::RGBA;
use euclid::Size2D;
use euclid::TypedScale;
use servo_arc::Arc;
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel};

/// The `Device` in Gecko wraps a pres context, has a default values computed,
/// and contains all the viewport rule state.
pub struct Device {
    /// NB: The document owns the styleset, who owns the stylist, and thus the
    /// `Device`, so having a raw document pointer here is fine.
    document: *const structs::Document,
    default_values: Arc<ComputedValues>,
    /// The font size of the root element
    /// This is set when computing the style of the root
    /// element, and used for rem units in other elements.
    ///
    /// When computing the style of the root element, there can't be any
    /// other style being computed at the same time, given we need the style of
    /// the parent to compute everything else. So it is correct to just use
    /// a relaxed atomic here.
    root_font_size: AtomicIsize,
    /// The body text color, stored as an `nscolor`, used for the "tables
    /// inherit from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    body_text_color: AtomicUsize,
    /// Whether any styles computed in the document relied on the root font-size
    /// by using rem units.
    used_root_font_size: AtomicBool,
    /// Whether any styles computed in the document relied on the viewport size
    /// by using vw/vh/vmin/vmax units.
    used_viewport_size: AtomicBool,
    /// The CssEnvironment object responsible of getting CSS environment
    /// variables.
    environment: CssEnvironment,
}

impl fmt::Debug for Device {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use nsstring::nsCString;

        let mut doc_uri = nsCString::new();
        unsafe {
            bindings::Gecko_nsIURI_Debug(
                (*self.document()).mDocumentURI.raw::<structs::nsIURI>(),
                &mut doc_uri,
            )
        };

        f.debug_struct("Device")
            .field("document_url", &doc_uri)
            .finish()
    }
}

unsafe impl Sync for Device {}
unsafe impl Send for Device {}

impl Device {
    /// Trivially constructs a new `Device`.
    pub fn new(document: *const structs::Document) -> Self {
        assert!(!document.is_null());
        let doc = unsafe { &*document };
        let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };
        Device {
            document,
            default_values: ComputedValues::default_values(doc),
            // FIXME(bz): Seems dubious?
            root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
            body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
            used_root_font_size: AtomicBool::new(false),
            used_viewport_size: AtomicBool::new(false),
            environment: CssEnvironment,
        }
    }

    /// Get the relevant environment to resolve `env()` functions.
    #[inline]
    pub fn environment(&self) -> &CssEnvironment {
        &self.environment
    }

    /// Tells the device that a new viewport rule has been found, and stores the
    /// relevant viewport constraints.
    pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
        unreachable!("Gecko doesn't support @viewport");
    }

    /// Whether any animation name may be referenced from the style of any
    /// element.
    pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return false,
        };

        unsafe {
            bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())
        }
    }

    /// Returns the default computed values as a reference, in order to match
    /// Servo.
    pub fn default_computed_values(&self) -> &ComputedValues {
        &self.default_values
    }

    /// Returns the default computed values as an `Arc`.
    pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> {
        &self.default_values
    }

    /// Get the font size of the root element (for rem)
    pub fn root_font_size(&self) -> Au {
        self.used_root_font_size.store(true, Ordering::Relaxed);
        Au::new(self.root_font_size.load(Ordering::Relaxed) as i32)
    }

    /// Set the font size of the root element (for rem)
    pub fn set_root_font_size(&self, size: Au) {
        self.root_font_size
            .store(size.0 as isize, Ordering::Relaxed)
    }

    /// Sets the body text color for the "inherit color from body" quirk.
    ///
    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
    pub fn set_body_text_color(&self, color: RGBA) {
        self.body_text_color
            .store(convert_rgba_to_nscolor(&color) as usize, Ordering::Relaxed)
    }

    /// Returns the body text color.
    pub fn body_text_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.body_text_color.load(Ordering::Relaxed) as u32)
    }

    /// Gets the document pointer.
    #[inline]
    pub fn document(&self) -> &structs::Document {
        unsafe { &*self.document }
    }

    /// Gets the pres context associated with this document.
    #[inline]
    pub fn pres_context(&self) -> Option<&structs::nsPresContext> {
        unsafe {
            self.document()
                .mPresShell
                .as_ref()?
                .mPresContext
                .mRawPtr
                .as_ref()
        }
    }

    /// Gets the preference stylesheet prefs for our document.
    #[inline]
    pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs {
        unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) }
    }

    /// Recreates the default computed values.
    pub fn reset_computed_values(&mut self) {
        self.default_values = ComputedValues::default_values(self.document());
    }

    /// Rebuild all the cached data.
    pub fn rebuild_cached_data(&mut self) {
        self.reset_computed_values();
        self.used_root_font_size.store(false, Ordering::Relaxed);
        self.used_viewport_size.store(false, Ordering::Relaxed);
    }

    /// Returns whether we ever looked up the root font size of the Device.
    pub fn used_root_font_size(&self) -> bool {
        self.used_root_font_size.load(Ordering::Relaxed)
    }

    /// Recreates all the temporary state that the `Device` stores.
    ///
    /// This includes the viewport override from `@viewport` rules, and also the
    /// default computed values.
    pub fn reset(&mut self) {
        self.reset_computed_values();
    }

    /// Returns the current media type of the device.
    pub fn media_type(&self) -> MediaType {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return MediaType::screen(),
        };

        // Gecko allows emulating random media with mIsEmulatingMedia and
        // mMediaEmulated.
        let medium_to_use = if pc.mIsEmulatingMedia() != 0 {
            pc.mMediaEmulated.mRawPtr
        } else {
            pc.mMedium
        };

        MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
    }

    /// Returns the current viewport size in app units.
    pub fn au_viewport_size(&self) -> Size2D<Au> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return Size2D::new(Au(0), Au(0)),
        };
        let area = &pc.mVisibleArea;
        Size2D::new(Au(area.width), Au(area.height))
    }

    /// Returns the current viewport size in app units, recording that it's been
    /// used for viewport unit resolution.
    pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
        self.used_viewport_size.store(true, Ordering::Relaxed);
        self.au_viewport_size()
    }

    /// Returns whether we ever looked up the viewport size of the Device.
    pub fn used_viewport_size(&self) -> bool {
        self.used_viewport_size.load(Ordering::Relaxed)
    }

    /// Returns the device pixel ratio.
    pub fn device_pixel_ratio(&self) -> TypedScale<f32, CSSPixel, DevicePixel> {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return TypedScale::new(1.),
        };

        let override_dppx = pc.mOverrideDPPX;
        if override_dppx > 0.0 {
            return TypedScale::new(override_dppx);
        }

        let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;
        let au_per_px = AU_PER_PX as f32;
        TypedScale::new(au_per_px / au_per_dpx)
    }

    /// Returns whether document colors are enabled.
    #[inline]
    pub fn use_document_colors(&self) -> bool {
        let doc = self.document();
        if doc.mIsBeingUsedAsImage() {
            return true;
        }
        let document_color_use =
            unsafe { structs::StaticPrefs_sVarCache_browser_display_document_color_use };
        let prefs = self.pref_sheet_prefs();
        match document_color_use {
            1 => true,
            2 => prefs.mIsChrome,
            _ => !prefs.mUseAccessibilityTheme,
        }
    }

    /// Returns the default background color.
    pub fn default_background_color(&self) -> RGBA {
        convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
    }

    /// Returns the current effective text zoom.
    #[inline]
    fn effective_text_zoom(&self) -> f32 {
        let pc = match self.pres_context() {
            Some(pc) => pc,
            None => return 1.,
        };
        pc.mEffectiveTextZoom
    }

    /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).
    #[inline]
    pub fn zoom_text(&self, size: Au) -> Au {
        size.scale_by(self.effective_text_zoom())
    }

    /// Un-apply text zoom.
    #[inline]
    pub fn unzoom_text(&self, size: Au) -> Au {
        size.scale_by(1. / self.effective_text_zoom())
    }
}
<|endoftext|>


<commit_before>use std::io::{stdin, stdout, Write};
use std::process::Command;

fn main() {
    loop {
        print!("redox login: ");
        stdout().flush();

        let mut buffer = String::new();
        stdin().read_line(&mut buffer);

        let path = "ion";
        match Command::new(path).spawn() {
            Ok(mut child) => {
                if let Err(err) = child.wait() {
                    println!("{}: Failed to wait: {}", path, err)
                }
            }
            Err(err) => println!("{}: Failed to execute: {}", path, err),
        }
    }
}
<commit_msg>Init will spawn /bin/sh in /home<commit_after>use std::env;
use std::io::{stdin, stdout, Write};
use std::process::Command;

fn main() {
    loop {
        print!("redox login: ");
        stdout().flush().unwrap();

        let mut buffer = String::new();
        stdin().read_line(&mut buffer).unwrap();

        env::set_current_dir("/home/").unwrap();

        let mut child = Command::new("/bin/sh").spawn().unwrap();
        child.wait().unwrap();
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;
use git2::{Reference as GitReference, Repository, Error as Git2Error};

use libimagstore::storeid::StoreId;
use libimagstore::hook::Hook;
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::error::CustomData as HECD;
use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagerror::trace::trace_error;
use libimagerror::into::IntoError;
use libimagutil::debug_result::*;

use vcs::git::result::Result;
use vcs::git::error::MapErrInto;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::GitHookError as GHE;
use vcs::git::runtime::Runtime as GRuntime;

pub struct CreateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl CreateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> CreateHook {
        CreateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for CreateHook {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "CreateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for CreateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_create"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for CreateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for CreateHook {

    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;

        debug!("[GIT CREATE HOOK]: {:?}", id);

        debug!("[GIT CREATE HOOK]: Ensuring branch checkout");
        try!(self
             .runtime
             .ensure_cfg_branch_is_checked_out()
             .map_err(Box::new)
             .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)));
        debug!("[GIT CREATE HOOK]: Branch checked out");

        self.runtime
            .config_value_or_err()
            .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't get Value object from config")
            .and_then(|cfg| {
                debug!("[GIT CREATE HOOK]: Getting repository");
                self.runtime
                    .repository()
                    .map(|r| (r, cfg))
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't fetch Repository")
                    .map_err_into(GHEK::RepositoryError)
                    .map_err(|e| e.into())
            })
            .and_then(|(repo, cfg)| {
                repo.signature()
                    .map(|s| (repo, cfg, s))
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't fetch Signature")
                    .map_err_into(GHEK::RepositorySignatureFetchingError)
                    .map_err_into(GHEK::RepositoryError)
                    .map_err(|e| e.into())
            })
            .and_then(|(repo, cfg, sig)| {
                repo.index()
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't fetch Index")
                    .and_then(|mut idx| idx.write_tree().map(|t| (idx, t)))
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't write Tree")
                    .and_then(|(idx, tree_id)| repo.find_tree(tree_id).map(|t| (idx, t)))
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't find Tree")
                    .map(|(idx, tree)| (repo, cfg, sig, idx, tree))
                    .map_err_into(GHEK::RepositoryIndexFetchingError)
                    .map_err_into(GHEK::RepositoryError)
                    .map_err(|e| e.into())
            })
            .and_then(|(repo, cfg, sig, mut idx, tree)| {
                idx.add_path(id)
                    .map(|_| (repo, cfg, sig, idx, tree))
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't add Path")
                    .map_dbg_err(|_| format!("\tpath = {:?}", id))
                    .map_dbg_err(|e| format!("\terr  = {:?}", e))
                    .map_err_into(GHEK::RepositoryPathAddingError)
                    .map_err_into(GHEK::RepositoryError)
                    .map_err(|e| e.into())
            })
            .and_then(|(repo, cfg, sig, idx, tree)| {
                repo.head()
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't fetch HEAD")
                    .map_err_into(GHEK::RepositoryHeadFetchingError)
                    .map(|h| h.target())
                    .and_then(|oid| {
                        match oid {
                            Some(oid) => {
                                repo.find_commit(oid)
                                    .map(|c| Some(c))
                                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't find commit")
                                    .map_dbg_err(|_| format!("\toid = {:?}", oid))
                                    .map_err_into(GHEK::RepositoryCommitFindingError)
                            },
                            None => Ok(None),
                        }
                    })
                    .map_err_into(GHEK::RepositoryError)
                    .map_err(|e| e.into())
                    .map(|parent| (repo, cfg, sig, idx, tree, parent))
            })
            .and_then(|(repo, cfg, sig, idx, tree, opt_parent)| {
                let (msg, parents) = match opt_parent {
                    None    => (String::from("Initial commit"), vec![]),
                    Some(p) => (commit_message(&cfg, StoreAction::Create), vec![p]),
                };

                let parents = parents.iter().collect::<Vec<_>>();
                repo.commit(Some("HEAD"), &sig, &sig, &msg[..], &tree, &parents)
                    .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't commit")
                    .map_err_into(GHEK::RepositoryCommittingError)
                    .map_err_into(GHEK::RepositoryError)
                    .map_err(|e| e.into())
            })
            .map(|_| ())
    }

}

<commit_msg>Rewrite hook implementation<commit_after>use std::path::PathBuf;
use std::path::Path;
use std::fmt::{Debug, Formatter, Error as FmtError};
use std::result::Result as RResult;

use toml::Value;
use git2::{Reference as GitReference, Repository, Error as Git2Error};
use git2::{ADD_DEFAULT, STATUS_WT_NEW, STATUS_WT_MODIFIED, IndexMatchedPath};

use libimagstore::storeid::StoreId;
use libimagstore::hook::Hook;
use libimagstore::hook::error::HookError as HE;
use libimagstore::hook::error::HookErrorKind as HEK;
use libimagstore::hook::error::CustomData as HECD;
use libimagstore::hook::result::HookResult;
use libimagstore::hook::position::HookPosition;
use libimagstore::hook::accessor::{HookDataAccessor, HookDataAccessorProvider};
use libimagstore::hook::accessor::StoreIdAccessor;
use libimagerror::trace::trace_error;
use libimagerror::into::IntoError;
use libimagutil::debug_result::*;

use vcs::git::result::Result;
use vcs::git::error::MapErrInto;
use vcs::git::error::GitHookErrorKind as GHEK;
use vcs::git::error::GitHookError as GHE;
use vcs::git::runtime::Runtime as GRuntime;

pub struct CreateHook {
    storepath: PathBuf,

    runtime: GRuntime,

    position: HookPosition,
}

impl CreateHook {

    pub fn new(storepath: PathBuf, p: HookPosition) -> CreateHook {
        CreateHook {
            runtime: GRuntime::new(&storepath),
            storepath: storepath,
            position: p,
        }
    }

}

impl Debug for CreateHook {

    fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
        write!(fmt, "CreateHook(storepath={:?}, repository={}, pos={:?}, cfg={:?}",
               self.storepath,
               (if self.runtime.has_repository() { "Some(_)" } else { "None" }),
               self.position,
               self.runtime.has_config())
    }
}

impl Hook for CreateHook {

    fn name(&self) -> &'static str {
        "stdhook_git_create"
    }

    fn set_config(&mut self, config: &Value) {
        if let Err(e) = self.runtime.set_config(config) {
            trace_error(&e);
        }
    }

}

impl HookDataAccessorProvider for CreateHook {

    fn accessor(&self) -> HookDataAccessor {
        HookDataAccessor::StoreIdAccess(self)
    }
}

impl StoreIdAccessor for CreateHook {

    /// The implementation of the CreateHook
    ///
    /// # Scope
    ///
    /// What this function has to do is _adding_ the new entry to the git index.
    /// After that, the UpdateHook will take care of committing the changes or new file.
    ///
    fn access(&self, id: &StoreId) -> HookResult<()> {
        use vcs::git::action::StoreAction;
        use vcs::git::config::commit_message;

        debug!("[GIT CREATE HOOK]: {:?}", id);

        let path = try!(id.into_pathbuf().map_err_into(GHEK::StoreIdHandlingError));

        let cfg = try!(
            self.runtime
                .config_value_or_err()
                .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't get Value object from config")
        );

        debug!("[GIT CREATE HOOK]: Ensuring branch checkout");
        try!(self
             .runtime
             .ensure_cfg_branch_is_checked_out()
             .map_err(Box::new)
             .map_err(|e| HEK::HookExecutionError.into_error_with_cause(e)));
        debug!("[GIT CREATE HOOK]: Branch checked out");

        debug!("[GIT CREATE HOOK]: Getting repository");
        let repo = try!(
            self.runtime
                .repository()
                .map_dbg_err_str("[GIT CREATE HOOK]: Couldn't fetch Repository")
                .map_err_into(GHEK::RepositoryError)
                .map_err(|e| e.into())
        );
        debug!("[GIT CREATE HOOK]: Repository object fetched");

        let index = try!(repo.index().map_err_into(GHEK::RepositoryIndexFetchingError));

        let file_status = try!(
            repo.status_file(&path).map_err_into(GHEK::RepositoryFileStatusError)
        );

        let cb = &mut |path: &Path, _matched_spec: &[u8]| -> i32 {
            if file_status.contains(STATUS_WT_MODIFIED) ||
                file_status.contains(STATUS_WT_NEW) {

                debug!("[GIT CREATE HOOK]: File is new or modified: {}", path.display());
                0
            } else {
                debug!("[GIT CREATE HOOK]: Ignoring file: {}", path.display());
                1
            }
        };

        try!(
            index.add_all(&[path], ADD_DEFAULT, Some(cb as &mut IndexMatchedPath))
                .map_err_into(GHEK::RepositoryPathAddingError)
        );

        index.write().map_err_into(GHEK::RepositoryIndexWritingError).map_err(|e| e.into())
    }

}

<|endoftext|>