| 1 | package Psh::Builtins::Cd; |
| 2 | |
| 3 | require Psh::Support::Dirs; |
| 4 | require Psh::Completion; |
| 5 | require Psh::OS; |
| 6 | require Psh::Util; |
| 7 | |
| 8 | =item * C<cd DIR> |
| 9 | |
| 10 | Change the working directory to DIR or home if DIR is not specified. |
| 11 | The special DIR "-" is interpreted as "return to the previous |
| 12 | directory". |
| 13 | |
| 14 | C<cd %num> will jump to a certain directory in the stack (See also |
| 15 | builtin C<dirs>). |
| 16 | |
| 17 | C<cd +num> and C<cd -num> will go forward/backward in the directory |
| 18 | stack. |
| 19 | |
| 20 | =cut |
| 21 | |
| 22 | sub bi_cd |
| 23 | { |
| 24 | my $in_dir = shift || Psh::OS::get_home_dir(); |
| 25 | my $explicit=0; |
| 26 | unless (@Psh::Support::Dirs::stack) { |
| 27 | push @Psh::Support::Dirs::stack, $ENV{PWD}; |
| 28 | } |
| 29 | |
| 30 | if ($in_dir=~/^[+-](\d+)$/) { |
| 31 | my $tmp_pos=$Psh::Support::Dirs::stack_pos-int($in_dir); |
| 32 | if ($tmp_pos<0) { |
| 33 | # TODO: Error handling |
| 34 | } elsif ($tmp_pos>$#Psh::Support::Dirs::stack) { |
| 35 | # TODO: Error handling |
| 36 | } else { |
| 37 | $in_dir=$Psh::Support::Dirs::stack[$tmp_pos]; |
| 38 | $Psh::Support::Dirs::stack_pos=$tmp_pos; |
| 39 | } |
| 40 | } elsif ($in_dir eq '-') { |
| 41 | if (@Psh::Support::Dirs::stack>1) { |
| 42 | if ($Psh::Support::Dirs::stack_pos==0) { |
| 43 | $in_dir=$Psh::Support::Dirs::stack[1]; |
| 44 | $Psh::Support::Dirs::stack_pos=1; |
| 45 | } else { |
| 46 | $in_dir=$Psh::Support::Dirs::stack[0]; |
| 47 | $Psh::Support::Dirs::stack_pos=0; |
| 48 | } |
| 49 | } |
| 50 | } elsif ($in_dir=~ /^\%(\d+)$/) { |
| 51 | my $tmp_pos=$1; |
| 52 | if ($tmp_pos>$#Psh::Support::Dirs::stack) { |
| 53 | # TODO: Error handling |
| 54 | } else { |
| 55 | $in_dir=$Psh::Support::Dirs::stack[$tmp_pos]; |
| 56 | $Psh::Support::Dirs::stack_pos=$tmp_pos; |
| 57 | } |
| 58 | } else { |
| 59 | $explicit=1 unless $in_dir eq $Psh::Support::Dirs::stack[0]; |
| 60 | # Don't push the same value again |
| 61 | $Psh::Support::Dirs::stack_pos=0; |
| 62 | } |
| 63 | my $dirpath='.'; |
| 64 | |
| 65 | if ($ENV{CDPATH} && !Psh::OS::file_name_is_absolute($in_dir)) { |
| 66 | $dirpath.=$ENV{CDPATH}; |
| 67 | } |
| 68 | |
| 69 | foreach my $cdbase (split $Psh::OS::PATH_SEPARATOR,$dirpath) { |
| 70 | my $dir= $in_dir; |
| 71 | if( $cdbase eq '.') { |
| 72 | $dir = Psh::Util::abs_path($dir); |
| 73 | } else { |
| 74 | $dir = Psh::Util::abs_path(Psh::OS::catdir($cdbase,$dir)); |
| 75 | } |
| 76 | |
| 77 | if ($dir and (-e $dir) and (-d _)) { |
| 78 | if (-x _) { |
| 79 | $ENV{OLDPWD}= $ENV{PWD}; |
| 80 | unshift @Psh::Support::Dirs::stack, $dir if $explicit; |
| 81 | CORE::chdir $dir; |
| 82 | $ENV{PWD}=$dir; |
| 83 | return (1,undef); |
| 84 | } else { |
| 85 | Psh::Util::print_error_i18n('perm_denied',$in_dir,$Psh::bin); |
| 86 | return (0,undef); |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | Psh::Util::print_error_i18n('no_such_dir',$in_dir,$Psh::bin); |
| 91 | return (0,undef); |
| 92 | } |
| 93 | |
| 94 | sub cmpl_cd { |
| 95 | my( $text, $pre, $start, $line, $startchar) = @_; |
| 96 | return 1,Psh::Completion::cmpl_directories($pre.$text); |
| 97 | } |
| 98 | |
| 99 | 1; |