1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
use {EXIT_ERROR, EXIT_SUCCESS, POLLED_TWICE};
use clap::{App, AppSettings, Arg};
use env::{AsyncIoEnvironment, FileDescEnvironment, StringWrapper,
          ReportErrorEnvironment, WorkingDirectoryEnvironment};
use io::FileDesc;
use future::{EnvFuture, Poll};
use path::{has_dot_components, NormalizationError, NormalizedPath};
use spawn::ExitResult;
use std::borrow::Borrow;
use std::path::Path;
use void::Void;

impl_generic_builtin_cmd! {
    /// Represents a `pwd` builtin command which will
    /// print out the current working directory.
    pub struct Pwd;

    /// Creates a new `pwd` builtin command with the provided arguments.
    pub fn pwd();

    /// A future representing a fully spawned `pwd` builtin command.
    pub struct SpawnedPwd;

    /// A future representing a fully spawned `pwd` builtin command
    /// which no longer requires an environment to run.
    pub struct PwdFuture;

    where T: StringWrapper,
          E: WorkingDirectoryEnvironment,
}

impl<T, I, E: ?Sized> EnvFuture<E> for SpawnedPwd<I>
    where T: StringWrapper,
          I: Iterator<Item = T>,
          E: AsyncIoEnvironment
              + FileDescEnvironment
              + ReportErrorEnvironment
              + WorkingDirectoryEnvironment,
          E::FileHandle: Borrow<FileDesc>,
{
    type Item = ExitResult<PwdFuture<E::WriteAll>>;
    type Error = Void;

    fn poll(&mut self, env: &mut E) -> Poll<Self::Item, Self::Error> {
        const PWD: &str = "pwd";
        const ARG_LOGICAL: &str = "L";
        const ARG_PHYSICAL: &str = "P";

        let app = App::new(PWD)
            .setting(AppSettings::NoBinaryName)
            .setting(AppSettings::DisableVersion)
            .about("Prints the absolute path name of the current working directory")
            .arg(Arg::with_name(ARG_LOGICAL)
                 .short(ARG_LOGICAL)
                 .multiple(true)
                 .overrides_with(ARG_PHYSICAL)
                 .help("Display the logical current working directory.")
            )
            .arg(Arg::with_name(ARG_PHYSICAL)
                 .short(ARG_PHYSICAL)
                 .multiple(true)
                 .overrides_with(ARG_LOGICAL)
                 .help("Display the physical current working directory (all symbolic links resolved).")
            );

        let app_args = self.args.take()
            .expect(POLLED_TWICE)
            .into_iter()
            .map(StringWrapper::into_owned);

        let matches = try_and_report!(PWD, app.get_matches_from_safe(app_args), env);

        generate_and_print_output!(PWD, env, |env| {
            let mut cwd_bytes = if matches.is_present(ARG_PHYSICAL) {
                physical(env.current_working_dir())
            } else {
                logical(env.current_working_dir())
            };

            if let Ok(ref mut bytes) = cwd_bytes {
                bytes.push(b'\n');
            }

            cwd_bytes
        })
    }

    fn cancel(&mut self, _env: &mut E) {
        self.args.take();
    }
}

fn logical(path: &Path) -> Result<Vec<u8>, NormalizationError> {
    if has_dot_components(path) {
        physical(path)
    } else {
        let bytes = path.to_string_lossy().into_owned().into_bytes();
        Ok(bytes)
    }
}

fn physical(path: &Path) -> Result<Vec<u8>, NormalizationError> {
    let mut normalized_path = NormalizedPath::new();
    normalized_path.join_normalized_physical(path)
        .map(|()| normalized_path.to_string_lossy().into_owned().into_bytes())
}