File Coverage

blib/lib/Psh/OS/Unix.pm
Criterion Covered Total %
statement 3 372 0.8
branch 0 186 0.0
condition 0 92 0.0
subroutine 1 46 2.1
pod 0 31 0.0
total 4 727 0.5


line stmt bran cond sub pod time code
1             package Psh::OS::Unix;
2              
3 1     1   15 use strict;
  1         2  
  1         4682  
4             require POSIX;
5             require Psh::Locale;
6              
7             $Psh::OS::PATH_SEPARATOR=':';
8             $Psh::OS::FILE_SEPARATOR='/';
9              
10             $Psh::history_file = ".psh_history";
11              
12             # Sets the title of the current window
13             sub set_window_title {
14 0     0 0   my $title= shift;
15 0           my $term= $ENV{TERM};
16 0 0         if( $term=~ /^(rxvt.*)|(xterm.*)|(.*xterm)|(kterm)|(aixterm)|(dtterm)/) {
17 0           print "\017\033]2;$title\007";
18             }
19             }
20              
21             #
22             # Returns the hostname of the machine psh is running on, preferrably
23             # the full version
24             #
25              
26             sub get_hostname {
27 0     0 0   require Sys::Hostname;
28 0           return Sys::Hostname::hostname();
29             }
30              
31             sub getcwd_psh {
32 0     0 0   my $cwd;
33 0           chomp($cwd = `pwd`);
34 0           $cwd;
35             }
36              
37             #
38             # Returns a list of well-known hosts (from /etc/hosts)
39             #
40             sub get_known_hosts {
41 0     0 0   my $hosts_file = "/etc/hosts"; # TODO: shouldn't be hard-coded?
42 0           my @result=();
43 0           local *F_KNOWNHOST;
44 0 0         if (open(F_KNOWNHOST,"< $hosts_file")) {
45 0           my $hosts_text = join ('', );
46 0           close(F_KNOWNHOST);
47 0           push @result,Psh::Util::parse_hosts_file($hosts_text);
48             }
49 0           my $tmp= catfile(Psh::OS::get_home_dir(),
50             '.ssh','known_hosts');
51 0 0         if (-r $tmp) {
52 0 0         if (open(F_KNOWNHOST, "< $tmp")) {
53 0           while () {
54 0           chomp;
55 0 0         next unless $_;
56 0 0         if (/^([a-zA-Z].*?)\,/) {
57 0           push @result, $1;
58             }
59             }
60             }
61             }
62 0 0         if (!@result) {
63 0           push @result,'localhost';
64             }
65 0           return @result;
66             }
67              
68             #
69             # Returns a list of all users on the system, prepended with ~
70             #
71             {
72             my @user_cache;
73             sub get_all_users {
74 0 0   0 0   unless (@user_cache) {
75 0           CORE::setpwent;
76 0           while (my ($name) = CORE::getpwent) {
77 0           push(@user_cache,'~'.$name);
78             }
79 0           CORE::endpwent;
80             }
81 0           return @user_cache;
82             }
83             }
84              
85             #
86             # void display_pod(text)
87             #
88             sub display_pod {
89 0     0 0   my $tmp= Psh::OS::tmpnam();
90 0           my $text= shift;
91              
92 0           local *TMP;
93 0           open( TMP,">$tmp");
94 0           print TMP $text;
95 0           close(TMP);
96              
97 0           eval {
98 0           require Pod::Text;
99 0           Pod::Text::pod2text($tmp,*STDOUT);
100             };
101 0 0         Psh::Util::print_debug_class('e',"Error: $@") if $@;
102 0 0         print $text if $@;
103              
104 0           unlink($tmp);
105             }
106              
107             sub get_home_dir {
108 0   0 0 0   my $user = shift || $ENV{USER};
109 0 0 0       return $ENV{HOME} if ((! $user) && (-d $ENV{HOME}));
110 0   0       return (CORE::getpwnam($user))[7]||'';
111             }
112              
113             sub get_rc_files {
114 0     0 0   my @rc=();
115              
116 0 0         if (-r '/etc/pshrc') {
117 0           push @rc, '/etc/pshrc';
118             }
119 0           my $home= Psh::OS::get_home_dir();
120 0 0         if ($home) { push @rc, catfile($home,'.pshrc') };
  0            
121 0           return @rc;
122             }
123              
124 0     0 0   sub get_path_extension { return (''); }
125              
126             #
127             # int inc_shlvl ()
128             #
129             # Increments $ENV{SHLVL}. Also checks for login shell status and does
130             # appropriate OS-specific tasks depending on it.
131             #
132             sub inc_shlvl {
133 0     0 0   my @pwent = CORE::getpwuid($<);
134 0 0 0       if ((! $ENV{SHLVL}) && ($pwent[8] eq $0)) { # would use $Psh::bin, but login shells are guaranteed full paths
135 0           $Psh::login_shell = 1;
136 0           $ENV{SHLVL} = 1;
137             } else {
138 0           $Psh::login_shell = 0;
139 0           $ENV{SHLVL}++;
140             }
141             }
142              
143              
144             ###################################################################
145             # JOB CONTROL
146             ###################################################################
147              
148              
149             #
150             # void _give_terminal_to (int PID)
151             #
152             # Make pid the foreground process of the terminal controlling STDIN.
153             #
154              
155             {
156             my $terminal_owner=0;
157              
158             sub _give_terminal_to
159             {
160             # If a fork of a psh fork tries to call this then exit
161             # as it would probably mess up the shell
162             # This hack is necessary as e.g.
163             # alias ls=/bin/ls
164             # ls &
165             # call fork_process from within a fork
166              
167 0 0   0     return if $Psh::OS::Unix::forked_already;
168 0 0         return if $terminal_owner==$_[0];
169 0           $terminal_owner=$_[0];
170              
171 0           local $SIG{TSTP} = 'IGNORE';
172 0           local $SIG{TTIN} = 'IGNORE';
173 0           local $SIG{TTOU} = 'IGNORE';
174 0           local $SIG{CHLD} = 'IGNORE';
175              
176 0           my ($pkg,$file,$line,$sub)= caller(1);
177 0           my $status= POSIX::tcsetpgrp(fileno STDIN,$_[0]);
178             }
179              
180             sub _get_terminal_owner
181             {
182 0     0     return $terminal_owner;
183             }
184             }
185              
186              
187              
188             #
189             # void _wait_for_system(int PID, [bool QUIET_EXIT], [bool NO_TERMINAL])
190             #
191             # Waits for a program to be stopped/ended, prints no message on normal
192             # termination if QUIET_EXIT is specified and true.
193             #
194             # If NO_TERMINAL is specified and true it won't try to transfer
195             # terminal ownership
196             #
197              
198             sub _wait_for_system
199             {
200 0     0     my($pid, $quiet) = @_;
201 0 0         if (!defined($quiet)) { $quiet = 0; }
  0            
202              
203 0           my $psh_pgrp = CORE::getpgrp();
204              
205 0           my $pid_status = -1;
206              
207 0           my $job= Psh::Joblist::get_job($pid);
208              
209 0 0         return if ! $job;
210              
211 0   0       my $term_pid= $job->{pgrp_leader}||$pid;
212              
213 0           _give_terminal_to($term_pid);
214              
215 0           my $output='';
216 0           my $status=1;
217 0           my $returnpid;
218 0           while (1) {
219 0 0         if (!$job->{running}) { $job->continue; }
  0            
220             {
221 0           local $Psh::currently_active = $pid;
  0            
222 0           $returnpid = CORE::waitpid($pid,POSIX::WUNTRACED());
223 0           $pid_status = $?;
224             }
225 0 0         last if $returnpid<1;
226              
227             # Very ugly work around for the problem that
228             # processes occasionally get SIGTTOUed without reason
229             # We can do this here because we know the process has
230             # to run and could not have been stopped by TTOU
231 0 0 0       if ($returnpid== $pid &&
      0        
232             POSIX::WIFSTOPPED($pid_status) &&
233             Psh::OS::signal_name(POSIX::WSTOPSIG($pid_status)) eq 'TTOU') {
234 0           $job->continue;
235 0           next;
236             }
237             # Collect output here - we cannot print it while another
238             # process might possibly be in the foreground;
239 0           $output.=_handle_wait_status($returnpid, $pid_status, $quiet, 1);
240 0 0         if ($returnpid == $pid) {
241 0           $status=POSIX::WEXITSTATUS($pid_status);
242 0           last;
243             }
244             }
245 0           _give_terminal_to($psh_pgrp);
246 0 0         Psh::Util::print_out($output) if length($output);
247 0           return $status==0;
248             }
249              
250             #
251             # void _handle_wait_status(int PID, int STATUS, bool QUIET_EXIT)
252             #
253             # Take the appropriate action given that waiting on PID returned
254             # STATUS. Normal termination is not reported if QUIET_EXIT is true.
255             #
256              
257             sub _handle_wait_status {
258 0     0     my ($pid, $pid_status, $quiet, $collect) = @_;
259             # Have to obtain these before we potentially delete the job
260 0           my $job= Psh::Joblist::get_job($pid);
261 0           my $command = $job->{call};
262 0           my $visindex= Psh::Joblist::get_job_number($pid);
263 0           my $verb='';
264              
265 0 0         if (POSIX::WIFEXITED($pid_status)) {
    0          
    0          
266 0           my $status= POSIX::WEXITSTATUS($pid_status);
267 0 0         if ($status==0) {
268 0 0         $verb= ucfirst(Psh::Locale::get_text('done')) unless $quiet;
269             } else {
270 0           $verb= ucfirst(Psh::Locale::get_text('error'));
271             }
272 0           Psh::Joblist::delete_job($pid);
273             } elsif (POSIX::WIFSIGNALED($pid_status)) {
274 0           my $tmp= Psh::Locale::get_text('terminated');
275 0           $verb = "\u$tmp (" .
276             Psh::OS::signal_description(POSIX::WTERMSIG($pid_status)) . ')';
277 0           Psh::Joblist::delete_job($pid);
278             } elsif (POSIX::WIFSTOPPED($pid_status)) {
279 0           my $tmp= Psh::Locale::get_text('stopped');
280 0           $verb = "\u$tmp (" .
281             Psh::OS::signal_description(POSIX::WSTOPSIG($pid_status)) . ')';
282 0           $job->{running}= 0;
283             }
284 0 0 0       if ($verb && $visindex>0) {
285 0           my $line="[$visindex] $verb $pid $command\n";
286 0 0         return $line if $collect;
287              
288 0           Psh::Util::print_out($line );
289             }
290 0           return '';
291             }
292              
293              
294             #
295             # void reap_children()
296             #
297             # Checks wether any children we spawned died
298             #
299              
300             sub reap_children
301             {
302 0     0 0   my $returnpid=0;
303 0           while (($returnpid = CORE::waitpid(-1, POSIX::WNOHANG() |
304             POSIX::WUNTRACED())) > 0) {
305 0           _handle_wait_status($returnpid, $?);
306             }
307             }
308              
309             sub execute_complex_command {
310 0     0 0   my @array= @{shift()};
  0            
311 0           my $fgflag= shift @array;
312 0           my @return_val;
313 0           my $success= 0;
314 0           my $eval_thingie;
315 0           my $pgrp_leader= 0;
316 0           my $pid;
317 0           my $string='';
318 0           my @tmp;
319              
320 0           my ($read,$chainout,$chainin);
321              
322 0           for( my $i=0; $i<@array; $i++) {
323             # ([ $strat, $how, \@options, \@words, $line]);
324 0           my ($strategy, $how, $options, $words, $text, $opt)= @{$array[$i]};
  0            
325 0           local $Psh::current_options= $opt;
326 0   0       $text||='';
327              
328 0           my $line= join(' ',@$words);
329 0           my $forcefork;
330 0           ($success, $eval_thingie,$words,$forcefork, @return_val)= $strategy->execute( \$line, $words, $how, $i>0);
331              
332 0   0       $forcefork||=$i<$#array;
333              
334 0 0         if( defined($eval_thingie)) {
335 0 0         if( $#array) {
336 0           ($read,$chainout)= POSIX::pipe();
337             }
338 0           foreach (@$options) {
339 0 0 0       if ($_->[0]==Psh::Parser::T_REDIRECT() and
      0        
340             ($_->[1] eq '<&' or $_->[1] eq '>&')) {
341 0 0         if ($_->[3] eq 'chainin') {
    0          
342 0           $_->[3]= $chainin;
343             } elsif ($_->[3] eq 'chainout') {
344 0           $_->[3]= $chainout;
345             }
346             }
347             }
348 0           my $termflag=!($i==$#array);
349              
350 0           ($pid,$success,@tmp)= _fork_process($eval_thingie,$words,
351             $fgflag,$text,$options,
352             $pgrp_leader,$termflag,
353             $forcefork);
354              
355 0 0 0       if( !$i && !$pgrp_leader) {
356 0           $pgrp_leader=$pid;
357             }
358              
359 0 0 0       if( $i<$#array && $#array) {
360 0           POSIX::close($chainout);
361 0           $chainin= $read;
362             }
363 0 0 0       if( @return_val < 1 ||
364             !defined($return_val[0])) {
365 0           @return_val= @tmp;
366             }
367             }
368 0 0         $string.='|' if $i>0;
369 0           $string.=$text;
370             }
371              
372 0 0         if( $pid) {
373 0           my $job= Psh::Joblist::create_job($pid,$string);
374 0           $job->{pgrp_leader}=$pgrp_leader;
375 0 0         if( $fgflag) {
376 0           $success=_wait_for_system($pid, 1);
377             } else {
378 0           my $visindex= Psh::Joblist::get_job_number($job->{pid});
379 0           Psh::Util::print_out("[$visindex] Background $pgrp_leader $string\n");
380             }
381             }
382 0           return ($success,\@return_val);
383             }
384              
385             sub _setup_redirects {
386 0     0     my $options= shift;
387 0           my $save= shift;
388              
389 0 0         return [] if ref $options ne 'ARRAY';
390              
391 0           my @cache=();
392 0           foreach my $option (@$options) {
393 0 0         if( $option->[0] == Psh::Parser::T_REDIRECT()) {
394 0           my $type= $option->[2];
395 0           my $cachefileno;
396              
397 0 0         if ($option->[1] eq '<&') {
    0          
    0          
    0          
    0          
398 0           POSIX::dup2($option->[3], $type);
399             } elsif ($option->[1] eq '>&') {
400 0           POSIX::dup2($option->[3], $type);
401             } elsif ($option->[1] eq '<') {
402 0           my $tmpfd= POSIX::open( $option->[3], &POSIX::O_RDONLY);
403 0           POSIX::dup2($tmpfd, $type);
404 0           POSIX::close($tmpfd);
405             } elsif ($option->[1] eq '>') {
406 0           my $tmpfd= POSIX::open( $option->[3], &POSIX::O_WRONLY |
407             &POSIX::O_TRUNC | &POSIX::O_CREAT );
408 0           POSIX::dup2($tmpfd, $type);
409 0           POSIX::close($tmpfd);
410             } elsif ($option->[1] eq '>>') {
411 0           my $tmpfd= POSIX::open( $option->[3], &POSIX::O_WRONLY |
412             &POSIX::O_CREAT);
413 0           POSIX::lseek($tmpfd,0, &POSIX::SEEK_END);
414 0           POSIX::dup2($tmpfd, $type);
415 0           POSIX::close($tmpfd);
416             }
417 0 0         if ($^F<$type) { # preserve filedescriptors higher than 2
418 0           $^F=$type;
419             }
420             }
421             }
422 0           select(STDOUT);
423 0           return \@cache;
424             }
425              
426             sub _has_redirects {
427 0     0     my $options= shift;
428 0 0         return 0 if ref $options ne 'ARRAY';
429              
430 0           foreach my $option (@$options) {
431 0 0         return 1 if( $option->[0] == Psh::Parser::T_REDIRECT());
432             }
433 0           return 0;
434             }
435              
436             #
437             # void fork_process( code|program, words,
438             # int fgflag, text to display in jobs,
439             # redirection options,
440             # pid of pgroupleader, do not set terminal flag,
441             # force a fork?)
442             #
443              
444             sub _fork_process {
445 0     0     my( $code, $words, $fgflag, $string, $options,
446             $pgrp_leader, $termflag, $forcefork) = @_;
447 0           my($pid);
448              
449             # HACK - if it's foreground code AND perl code AND
450             # there are no redirects
451             # we do not fork, otherwise we'll never get
452             # the result value, changed variables etc.
453 0 0 0       if( $fgflag and !$forcefork and ref($code) eq 'CODE'
      0        
      0        
454             and !_has_redirects($options)
455             ) {
456 0           my @result= eval { &$code };
  0            
457 0 0 0       Psh::Util::print_error($@) if $@ && $@ !~/^SECRET/;
458 0           return (0,@result);
459             }
460              
461 0 0         unless ($pid = fork) { #child
462 0 0         unless (defined $pid) {
463 0           Psh::Util::print_error_i18n('fork_failed');
464 0           return (-1,0,undef);
465             }
466              
467 0           $Psh::OS::Unix::forked_already=1;
468 0 0         close(READ) if( $pgrp_leader);
469 0           _setup_redirects($options,0);
470 0   0       POSIX::setpgid(0,$pgrp_leader||$$);
471 0 0 0       _give_terminal_to($pgrp_leader||$$) if $fgflag && !$termflag;
      0        
472 0           remove_signal_handlers();
473              
474 0 0         if( ref($code) eq 'CODE') {
475 0           my @tmp=&{$code};
  0            
476 0 0 0       if (!@tmp or $tmp[0]) {
477 0           CORE::exit(0);
478             }
479 0           CORE::exit(1);
480             } else {
481             {
482 0 0         if( ! ref $options) {
  0            
483 0           exec $code;
484             } else {
485 0           $code= shift @$words;
486 0           exec { $code } @$words;
  0            
487             }
488             } # Avoid unreachable warning
489 0           Psh::Util::print_error_i18n('exec_failed',$code);
490 0           CORE::exit(-1);
491             }
492             }
493 0   0       POSIX::setpgid($pid,$pgrp_leader||$pid);
494 0 0 0       _give_terminal_to($pgrp_leader||$pid) if $fgflag && !$termflag;
      0        
495 0           return ($pid,0,undef);
496             }
497              
498             sub fork_process {
499 0     0 0   my( $code, $fgflag, $string, $options) = @_;
500 0           my ($pid,$sucess,@result)= _fork_process($code,undef,$fgflag,$string,$options);
501 0 0         return @result if !$pid;
502 0           my $job= Psh::Joblist::create_job($pid,$string);
503 0 0         if( !$fgflag) {
504 0           my $visindex= Psh::Joblist::get_job_number($job->{pid});
505 0           Psh::Util::print_out("[$visindex] Background $pid $string\n");
506             }
507 0 0         _wait_for_system($pid, 1) if $fgflag;
508 0           return undef;
509             }
510              
511             #
512             # Returns true if the system has job_control abilities
513             #
514 0     0 0   sub has_job_control { return 1; }
515              
516             #
517             # void restart_job(bool FOREGROUND, int JOB_INDEX)
518             #
519             sub restart_job
520             {
521 0     0 0   my ($fg_flag, $job_to_start) = @_;
522              
523 0           my $job= Psh::Joblist::find_job($job_to_start);
524              
525 0 0         if(defined($job)) {
526 0           my $pid = $job->{pid};
527 0           my $command = $job->{call};
528              
529 0 0         if ($command) {
530 0           my $verb;
531 0           my $qRunning = $job->{running};
532              
533 0 0         if ($fg_flag) {
    0          
534 0           $verb= ucfirst(Psh::Locale::get_text('foreground'));
535             } elsif ($qRunning) {
536             # bg request, and it's already running:
537 0           return;
538             } else {
539 0           $verb= ucfirst(Psh::Locale::get_text('restart'));
540             }
541 0           my $visindex = Psh::Joblist::get_job_number($pid);
542 0           Psh::Util::print_out("[$visindex] $verb $pid $command\n");
543              
544 0 0         if($fg_flag) {
    0          
545 0           eval { _wait_for_system($pid, 0); };
  0            
546 0 0         Psh::Util::print_debug_class('e',"Error: $@") if $@;
547             } elsif( !$qRunning) {
548 0           $job->continue;
549             }
550             }
551             }
552             }
553              
554             sub resume_job {
555 0     0 0   my $job= shift;
556              
557 0           kill 'CONT', -$job->{pid};
558 0 0         kill 'CONT', -$job->{pgrp_leader} if $job->{pgrp_leader};
559             }
560              
561             # Simply doing backtick eval - mainly for Prompt evaluation
562             sub backtick {
563 0     0 0   my $com=join ' ',@_;
564 0           local $^F=50;
565 0           my ($read,$write)= POSIX::pipe();
566              
567 0 0         unless(my $pid=fork) {
568 0           POSIX::close($read);
569 0           POSIX::dup2($write,fileno(*STDOUT));
570 0 0         $^F=$write if ($write>$^F);
571 0           my ($success)= Psh::evl($com);
572 0           CORE::exit(!$success);
573             }
574 0           POSIX::close($write);
575 0           my $result='';
576 0           local(*READ);
577 0           open(READ,"<&=$read");
578 0           while() {
579 0           $result.=$_;
580             }
581 0           close(READ);
582 0           return $result;
583             }
584              
585             ###################################################################
586             # SIGNALS
587             ###################################################################
588              
589             # Setup special treatment of certain signals
590             # Having a value of 0 means to ignore the signal completely in
591             # the loops while a code ref installs a different default
592             # handler. Note that calling _ignore_handler is different than
593             # setting the signal action to ignore - if you set the signal
594             # action to ignore, the signal might be passed on to parent processes
595             # which could decide to handle them for us
596              
597             my %special_handlers= (
598             'CHLD' => \&_ignore_handler,
599             'CLD' => \&_ignore_handler,
600             'TTOU' => \&_ttou_handler,
601             'TTIN' => \&_ttou_handler,
602             'TERM' => \&Psh::OS::fb_exit_psh,
603             'HUP' => \&Psh::OS::fb_exit_psh,
604             'SEGV' => 0,
605             'WINCH'=> 0,
606             'ZERO' => 0,
607             );
608              
609             my @signals= grep { substr($_,0,1) ne '_' } keys %SIG;
610              
611             #
612             # void remove_signal_handlers()
613             #
614             # This used to manually set INT, QUIT, CONT, STOP, TSTP, TTIN,
615             # TTOU, and CHLD.
616             #
617             # The new technique changes the settings of *all* signals. It is
618             # from Recipe 16.13 of The Perl Cookbook (Page 582). It should be
619             # compatible with Perl 5.004 and later.
620             #
621              
622             sub remove_signal_handlers
623             {
624 0     0 0   foreach my $sig (@signals) {
625 0 0 0       next if exists($special_handlers{$sig}) &&
626             ! ref($special_handlers{$sig});
627 0           $SIG{$sig} = 'DEFAULT';
628             }
629             }
630              
631             #
632             # void setup_signal_handlers
633             #
634             # This used to manually set INT, QUIT, CONT, STOP, TSTP, TTIN,
635             # TTOU, and CHLD.
636             #
637             # See comment for remove_signal_handlers() for more information.
638             #
639              
640             sub setup_signal_handlers
641             {
642 0     0 0   foreach my $sig (@signals) {
643 0 0         if( exists($special_handlers{$sig})) {
644 0 0         if( ref($special_handlers{$sig})) {
645 0           $SIG{$sig}= $special_handlers{$sig};
646             }
647 0           next;
648             }
649 0           $SIG{$sig} = \&_signal_handler;
650             }
651              
652 0           reinstall_resize_handler();
653             }
654              
655              
656             #
657             # Setup the SIGSEGV handler
658             #
659             sub setup_sigsegv_handler
660             {
661 0     0 0   $SIG{SEGV} = \&_error_handler;
662             }
663              
664             #
665             # Setup SIGINT handler for readline
666             #
667             sub setup_readline_handler
668             {
669 0     0 0   $SIG{INT}= \&_readline_handler;
670             }
671              
672             sub remove_readline_handler
673             {
674 0     0 0   $SIG{INT}= \&_signal_handler;
675             }
676              
677             sub reinstall_resize_handler
678             {
679 0     0 0   Psh::OS::fb_reinstall_resize_handler();
680 0           &_resize_handler('WINCH');
681             }
682              
683              
684             #
685             # readline_handler()
686             #
687             # Readline ^C handler.
688             #
689              
690             sub _readline_handler
691             {
692 0     0     my $sig= shift;
693 0           setup_readline_handler();
694 0           print "\n"; # Clean up the display
695 0           die "SECRET $Psh::bin: Signal $sig\n"; # changed to SECRET... just in case
696             }
697              
698             sub _ttou_handler
699             {
700 0     0     _give_terminal_to($$);
701             }
702              
703             #
704             # void _signal_handler( string SIGNAL )
705             #
706              
707             sub _signal_handler
708             {
709 0     0     my ($sig) = @_;
710              
711 0 0         if ($Psh::currently_active > 0) {
    0          
712 0           Psh::Util::print_debug("Received signal SIG$sig, sending to $Psh::currently_active\n");
713              
714 0           kill $sig, -$Psh::currently_active;
715             } elsif ($Psh::currently_active < 0) {
716 0           Psh::Util::print_debug("Received signal SIG$sig, sending to Perl code\n");
717 0           die "SECRET ${Psh::bin}: Signal $sig\n";
718             } else {
719 0           _give_terminal_to($$);
720 0           Psh::Util::print_debug("Received signal SIG$sig, die-ing\n");
721 0 0         die "SECRET ${Psh::bin}: Signal $sig\n" if $sig eq 'INT';
722             }
723              
724 0           $SIG{$sig} = \&_signal_handler;
725             }
726              
727              
728             #
729             # ignore_handler()
730             #
731              
732             sub _ignore_handler
733 0     0     {
734             }
735              
736              
737             sub _error_handler
738             {
739 0     0     my ($sig) = @_;
740 0           Psh::Util::print_error_i18n('unix_received_strange_sig',$sig);
741 0           kill 'INT', $$; # HACK to stop a possible endless loop!
742             }
743              
744             #
745             # _resize_handler()
746             #
747              
748             sub _resize_handler
749             {
750 0     0     my ($sig) = @_;
751              
752 0           Psh::OS::check_terminal_size();
753              
754 0           $SIG{$sig} = \&_resize_handler;
755             }
756              
757             {
758             my $debian=-1;
759             sub _check_debian {
760 0 0   0     if ($debian==-1) {
761 0 0         if (-r '/etc/debian-version') {
762 0           $debian=1;
763             } else {
764 0           $debian=0;
765             }
766             }
767 0           return $debian;
768             }
769             }
770              
771             sub get_editor {
772 0     0 0   my $file= shift;
773 0           my $suggestion= shift;
774 0   0       my $editor= $suggestion||$ENV{VISUAL}||$ENV{EDITOR};
775 0 0         if (_check_debian()) {
776 0   0       $editor ||='editor';
777             } else {
778 0   0       $editor ||='vi';
779             }
780 0           return $editor;
781             }
782              
783             # File::Spec
784              
785             sub canonpath {
786 0     0 0   my ($path) = @_;
787 0 0         $path =~ s|/+|/|g unless($^O eq 'cygwin'); # xx////xx -> xx/xx
788 0           $path =~ s|(/\.)+/|/|g; # xx/././xx -> xx/xx
789 0 0         $path =~ s|^(\./)+||s unless $path eq "./"; # ./xx -> xx
790 0           $path =~ s|^/(\.\./)+|/|s; # /../../xx -> xx
791 0 0         $path =~ s|/\Z(?!\n)|| unless $path eq "/"; # xx/ -> xx
792 0           return $path;
793             }
794              
795             sub catfile {
796 0     0 0   my $file = pop @_;
797 0 0         return $file unless @_;
798 0           my $dir = catdir(@_);
799 0 0         $dir .= "/" unless substr($dir,-1) eq "/";
800 0           return $dir.$file;
801             }
802              
803             sub catdir {
804 0     0 0   my @args = @_;
805 0           foreach (@args) {
806             # append a slash to each argument unless it has one there
807 0 0 0       $_ .= "/" if $_ eq '' || substr($_,-1) ne "/";
808             }
809 0           return canonpath(join('', @args));
810             }
811              
812             sub file_name_is_absolute {
813 0     0 0   my $file= shift;
814 0           return scalar($file =~ m:^/:s);
815             }
816              
817             sub rootdir {
818 0     0 0   '/';
819             }
820              
821             sub splitdir {
822 0     0 0   my ($directories) = @_ ;
823              
824 0 0         if ( $directories !~ m|/\Z(?!\n)| ) {
825 0           return split( m|/|, $directories );
826             }
827             else {
828 0           my( @directories )= split( m|/|, "${directories}dummy" ) ;
829 0           $directories[ $#directories ]= '' ;
830 0           return @directories ;
831             }
832             }
833              
834             sub rel2abs {
835 0     0 0   my ($path,$base ) = @_;
836            
837             # Clean up $path
838 0 0         if ( ! file_name_is_absolute( $path ) ) {
839             # Figure out the effective $base and clean it up.
840 0 0 0       if ( !defined( $base ) || $base eq '' ) {
    0          
841 0           $base = Psh::getcwd_psh() ;
842             }
843             elsif ( ! file_name_is_absolute( $base ) ) {
844 0           $base = rel2abs( $base ) ;
845             }
846             else {
847 0           $base = canonpath( $base ) ;
848             }
849            
850             # Glom them together
851 0           $path = catdir( $base, $path ) ;
852             }
853            
854 0           return canonpath( $path ) ;
855             }
856              
857             1;
858              
859             __END__