📄 subshell.c
字号:
/* Make MC's special commands not show up in bash's history */
putenv ("HISTCONTROL=ignorespace");
/* Allow alternative readline settings for MC */
if (access (".mc/inputrc", R_OK) == 0)
putenv ("INPUTRC=.mc/inputrc");
break;
case TCSH:
init_file = ".mc/tcshrc";
if (access (init_file, R_OK) == -1)
init_file += 3;
break;
case ZSH:
break;
default:
fprintf (stderr, __FILE__": unimplemented subshell type %d\n",
subshell_type);
_exit (FORK_FAILURE);
}
/* }}} */
/* {{{ Attach all our standard file descriptors to the pty */
/* This is done just before the fork, because stderr must still */
/* be connected to the real tty during the above error messages; */
/* otherwise the user will never see them. */
dup2 (pty_slave, STDIN_FILENO);
dup2 (pty_slave, STDOUT_FILENO);
dup2 (pty_slave, STDERR_FILENO);
/* }}} */
/* {{{ Execute the subshell at last */
close (subshell_pipe[READ]);
close (pty_slave); /* These may be FD_CLOEXEC, but just in case... */
switch (subshell_type)
{
case BASH:
execl (shell, "bash", "-rcfile", init_file, NULL);
break;
case TCSH:
execl (shell, "tcsh", NULL); /* What's the -rcfile equivalent? */
break;
case ZSH:
execl (shell, "zsh", "+Z", NULL);
break;
}
/* If we get this far, everything failed miserably */
_exit (FORK_FAILURE);
/* }}} */
}
close(pty_slave);
#ifdef SYNC_PTY_SIDES
sigsuspend (&old_mask);
signal (SIGUSR1, SIG_DFL);
sigprocmask (SIG_SETMASK, &old_mask, NULL);
/* ...before installing our handler for SIGCHLD. */
#endif
#if 0
/* {{{ Install our handler for SIGCHLD */
init_sigchld ();
/* We could have received the SIGCHLD signal for the subshell
* before installing the init_sigchld */
pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
if (pid == subshell_pid){
use_subshell = FALSE;
return;
}
/* }}} */
#endif
/* {{{ Set up `precmd' or equivalent for reading the subshell's CWD */
switch (subshell_type)
{
char precmd[80];
case BASH:
sprintf (precmd, " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
subshell_pipe[WRITE]);
goto write_it;
case ZSH:
sprintf (precmd, "precmd(){ pwd>&%d;kill -STOP $$ }\n",
subshell_pipe[WRITE]);
goto write_it;
case TCSH:
sprintf (precmd, "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n", tcsh_fifo);
write_it:
write (subshell_pty, precmd, strlen (precmd));
}
/* }}} */
/* {{{ Wait until the subshell has started up and processed the command */
subshell_state = RUNNING_COMMAND;
enable_interrupt_key ();
if (!feed_subshell (QUIETLY, TRUE)){
use_subshell = FALSE;
}
disable_interrupt_key ();
if (!subshell_alive)
use_subshell = FALSE; /* Subshell died instantly, so don't use it */
/* }}} */
}
/* }}} */
/* {{{ invoke_subshell */
int invoke_subshell (const char *command, int how, char **new_dir)
{
/* {{{ Fiddle with terminal modes */
static struct termios raw_mode = {0};
/* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
/* original settings. However, here we need to make this tty very raw, */
/* so that all keyboard signals, XON/XOFF, etc. will get through to the */
/* pty. So, instead of changing the code for execute(), pre_exec(), */
/* etc, we just set up the modes we need here, before each command. */
if (raw_mode.c_iflag == 0) /* First time: initialise `raw_mode' */
{
tcgetattr (STDOUT_FILENO, &raw_mode);
raw_mode.c_lflag &= ~ICANON; /* Disable line-editing chars, etc. */
raw_mode.c_lflag &= ~ISIG; /* Disable intr, quit & suspend chars */
raw_mode.c_lflag &= ~ECHO; /* Disable input echoing */
raw_mode.c_iflag &= ~IXON; /* Pass ^S/^Q to subshell undisturbed */
raw_mode.c_iflag &= ~ICRNL; /* Don't translate CRs into LFs */
raw_mode.c_oflag &= ~OPOST; /* Don't postprocess output */
raw_mode.c_cc[VTIME] = 0; /* IE: wait forever, and return as */
raw_mode.c_cc[VMIN] = 1; /* soon as a character is available */
}
tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
/* }}} */
/* Make the subshell change to MC's working directory */
if (new_dir)
do_subshell_chdir (cpanel->cwd, TRUE, 1);
if (command == NULL) /* The user has done "C-o" from MC */
{
if (subshell_state == INACTIVE)
{
subshell_state = ACTIVE;
/* FIXME: possibly take out this hack; the user can
re-play it by hitting C-hyphen a few times! */
write (subshell_pty, " \b", 2); /* Hack to make prompt reappear */
}
}
else /* MC has passed us a user command */
{
if (how == QUIETLY)
write (subshell_pty, " ", 1);
write (subshell_pty, command, strlen (command));
write (subshell_pty, "\n", 1);
subshell_state = RUNNING_COMMAND;
subshell_ready = FALSE;
}
feed_subshell (how, FALSE);
if (new_dir && subshell_alive && strcmp (subshell_cwd, cpanel->cwd))
*new_dir = subshell_cwd; /* Make MC change to the subshell's CWD */
/* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
while (!subshell_alive && !quit && use_subshell)
init_subshell ();
prompt_pos = 0;
return quit;
}
/* }}} */
/* {{{ read_subshell_prompt */
int read_subshell_prompt (int how)
{
/* {{{ Local variables */
int clear_now = FALSE;
static int prompt_size = INITIAL_PROMPT_SIZE;
int bytes = 0, i, rc = 0;
struct timeval timeleft = {0, 0};
fd_set tmp;
FD_ZERO (&tmp);
FD_SET (subshell_pty, &tmp);
/* }}} */
if (subshell_prompt == NULL) /* First time through */
{
subshell_prompt = (char *) malloc (prompt_size);
*subshell_prompt = '\0';
prompt_pos = 0;
}
while (subshell_alive &&
(rc = select (FD_SETSIZE, &tmp, NULL, NULL, &timeleft)))
{
/* {{{ Check for `select' errors */
if (rc == -1)
if (errno == EINTR)
continue;
else
{
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
perror ("\n"__FILE__": select (FD_SETSIZE, &tmp...)");
exit (1);
}
/* }}} */
bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
if (how == VISIBLY)
write (STDOUT_FILENO, pty_buffer, bytes);
/* {{{ Extract the prompt from the shell output */
for (i=0; i<bytes; ++i)
if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r'){
prompt_pos = 0;
clear_now = FALSE;
} else {
clear_now = TRUE;
if (!pty_buffer [i])
continue;
subshell_prompt[prompt_pos++] = pty_buffer[i];
if (prompt_pos == prompt_size)
subshell_prompt = (char *) realloc (subshell_prompt,
prompt_size *= 2);
}
/* Sometimes we get an empty new line and then nothing,
* we better just keep the old prompt instead. */
if (clear_now)
subshell_prompt[prompt_pos] = '\0';
/* }}} */
}
if (rc == 0 && bytes == 0)
return FALSE;
return TRUE;
}
/* }}} */
/* {{{ resize_subshell */
void resize_subshell (void)
{
#if defined TIOCSWINSZ && !defined SCO_FLAVOR
struct winsize tty_size;
tty_size.ws_row = LINES;
tty_size.ws_col = COLS;
tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
if (ioctl (subshell_pty, TIOCSWINSZ, &tty_size))
perror (__FILE__": couldn't set pty size");
#endif
}
/* }}} */
/* {{{ exit_subshell */
int exit_subshell (void)
{
int quit = TRUE;
if (subshell_state != INACTIVE && subshell_alive)
quit = !query_dialog (_(" Warning "), _(" The shell is still active. Quit anyway? "),
0, 2, _("&Yes"), _("&No"));
#if AIX_TCSH_CODE_BELOW_IS_IT_FIXED
/* New Test code */
else
{
if (subshell_type == TCSH)
sprintf (pty_buffer, " echo -n Jobs:>/tmp/mc.pipe.%d;jobs>/tmp/"
"mc.pipe.%d;kill -STOP $$\n", getpid (), getpid ());
else
sprintf (pty_buffer, " echo -n Jobs:>&%d;jobs>&%d;kill -STOP $$\n",
subshell_pipe[WRITE], subshell_pipe[WRITE]);
write (subshell_pty, pty_buffer, strlen (pty_buffer));
#ifndef HAVE_GRANTPT /* FIXME */
if (subshell_type == ZSH)
/* Here we have to drain the shell output, because zsh does a */
/* tcsetattr(SHTTY, TCSADRAIN...) which will block if we don't */
read (subshell_pty, pty_buffer, pty_buffer_size);
#endif
/* TCSH + AIX hang here, fix this before removing the ifdef above */
if (read (subshell_pipe[READ], pty_buffer, pty_buffer_size) == 5)
quit = TRUE;
else
quit = !query_dialog (_(" Warning "), _(" There are stopped jobs.")
_(" Quit anyway? "), 0, 2, _("&Yes"), _("&No"));
synchronize ();
subshell_state = RUNNING_COMMAND;
feed_subshell (QUIETLY, FALSE); /* Drain the shell output (again) */
}
#endif
if (quit && subshell_type == TCSH)
{
/* We abuse of pty_buffer here, but it doesn't matter at this stage */
sprintf (pty_buffer, "/tmp/mc.pipe.%d", getpid ());
if (unlink (pty_buffer) == -1)
perror (__FILE__": couldn't remove named pipe /tmp/mc.pipe.NNN");
}
return quit;
}
/* }}} */
/* {{{ do_subshell_chdir */
/* If it actually changed the directory it returns true */
void do_subshell_chdir (char *directory, int do_update, int reset_prompt)
{
char *temp;
if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, cpanel->cwd))){
/* We have to repaint the subshell prompt if we read it from
* the main program. Please note that in the code after this
* if, the cd command that is sent will make the subshell
* repaint the prompt, so we don't have to paint it. */
if (do_update)
do_update_prompt ();
return;
}
/* The initial space keeps this out of the command history (in bash
because we set "HISTCONTROL=ignorespace") */
write (subshell_pty, " cd ", 4);
if (*directory) {
temp = name_quote (directory, 0);
write (subshell_pty, temp, strlen (temp));
free (temp);
} else {
write (subshell_pty, "/", 1);
}
write (subshell_pty, "\n", 1);
subshell_state = RUNNING_COMMAND;
feed_subshell (QUIETLY, FALSE);
if (subshell_alive && strcmp (subshell_cwd, cpanel->cwd) && strcmp (cpanel->cwd, "."))
fprintf (stderr, _("Warning: Couldn't change to %s.\n"), cpanel->cwd);
if (reset_prompt)
prompt_pos = 0;
update_prompt = FALSE;
/* Make sure that MC never stores the CWD in a silly format */
/* like /usr////lib/../bin, or the strcmp() above will fail */
}
/* }}} */
/* {{{ subshell_get_console_attributes */
void subshell_get_console_attributes (void)
{
/* {{{ Get our current terminal modes */
if (tcgetattr (STDOUT_FILENO, &shell_mode))
{
perror (__FILE__": couldn't get terminal settings");
use_subshell = FALSE;
return;
}
/* }}} */
}
/* }}} */
/* {{{ sigchld_handler */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -