Okay guys, I guess I have a working integration for ash subshell support (tested with BusyBox ash/sh on mipsel) now. I am offering it to you developers for inspection and adaption, so it may soon become a part of the mainstream MC code base. I am aware of a few shortcomings and have mentioned them as TODO items in code comments.
The first file 040-ash_as_subshell.patch contains the functional part of the patch, while 040-ash_as_subshell_additional.patch contains some minor additions to documentation and test code which are not necessary to run mc + ash. Feel free and encouraged to criticise and improve the code - as I said, I am not a C programmer and rather got my results by continuous testing with strace as a debugging tool because I do not know how to handle gdb. What I have so far: - Ash mode will be used if the login shell is /bin/sh (on my platform it is safe to assume this, for general use this needs to be adapted to really recognise if /bin/sh is an ash or dash. I know how to do this via shell script, but not cleanly via C, sorry. - Ash as a subshell currently displays a fixed PS1 prompt like "user@host:/my/path/$ " ("user@host:/my/path/# " for root). It should be easy enough to change this, it just served my purpose like this. It is just a detail. - Chdir via subshell "cd" and via MC panel works as expected. - Analogous to Bash, it is possible to use an init file ~/.local/share/mc/ashrc (with auto-fallback to ~/.profile). This is not even implemented upstream for Zsh and Tcsh at the moment. - Plese refer to code comments for how and why I implemented the precmd via PS1 with two-fold indirection. This was necessary to enable the start of a sub-subshell (user command "ash" from subshell) without the sub-shell freezing (kill -STOP) or throwing an error. This is the trickiest part and I am somewhat proud I got it resolved with my rudimentary knowledge because it was a pain in the a**. Maybe there is a much simpler way. - I added some minimal documentation for Ash subshell mode to the help file, but only in English (needs translation), also extending Bash mode by mentioning ~/.bashrc (upstream just mentions ~/.local/share/mc/ashrc). Remark: In order to get ENV into the environment for the init file, I had to uncomment "g_free (putenv_str)". Initially I had used g_free because Bash mode also uses it for INPUTRC. I wonder if the g_free also needs to be removed in that case. Some MC professional might want to check this. I hope you are interested in this feature even though until now nobody has answered my earlier inquiries. I want to thank Harald (ralda) from the BusyxBox list though for sharing his thoughts and insights with me. e.g. he gave me the important hint with (not to use) g_free. Kind regards -- Alexander Kriegisch Alexander Kriegisch, 03.03.2012 18:39: > Never mind, I am one step further: > >> case ASH: >> /* ash does not support precmd, we need to read cwd from the prompt >> */ >> g_snprintf (precmd, sizeof (precmd), >> "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n", >> subshell_pipe[WRITE]); >> break; > > This works for BusyBox ash (tested on mipsel platform). The command will > be contained in the shell editing history, but for me this is acceptable. > > The next thing I want to do is set ENV so as to enable loading my shell > profile without having to use a login shell. This would be the > equivalent to Bash's "-rcfile" parameter. I did this: > >> case ASH: >> /* Do we have a custom init file ~/.local/share/mc/ashrc? */ >> init_file = mc_config_get_full_path ("ashrc"); >> >> /* Otherwise use ~/.profile */ >> if (access (init_file, R_OK) == -1) >> { >> g_free (init_file); >> init_file = g_strdup (".profile"); >> } >> >> /* Put init file to ENV variable used by ash */ >> char *putenv_str = g_strconcat ("ENV=", init_file, NULL); >> putenv (putenv_str); >> g_free (putenv_str); >> >> break; > > And later this: > >> /* TODO for upstream patch: Execute correct ash/dash/busybox shell (not >> necessary for Freetz) */ >> case ASH: >> execl (shell, "sh", (char *) NULL); >> break; > > The problem is that ENV seems to be unset in my subshell's environment, > i.e. that ~ /.profile (whiche exists) is not evaluated. Do I need to > somehow export the ENV variable to make it available to the subshell? > BTW, I am compiling against uClibc 0.9.29, if this is relevant. > > > Alexander Kriegisch, 03.03.2012 14:41: >> I am on an embedded environment. Bash is available as a separate >> package, but huge in comparison with the built-in ash of BusyBox. >> >> Disclaimer before I continue: I am *not* a C programmer. I can read >> a bit of C, but not develop anything meaningful other than copy, >> paste and modify existing code. So bear with me anyway, if you >> please. >> >> I looked into src/subshell.c and tried to figure out what happens >> there. I have started to add ash support, but MC seems to depend on >> PROMPT_COMMAND/precmd/fish_prompt, i.e. on a function or alias which >> is executed each time just before a prompt is printed. Ash does not >> have anything like that, AFAIK. If I understand correctly, using >> those pre-commands are MC's way of determining the cwd. I wonder why >> you do not just use something like pwd or $PWD, but OTOH I do not >> understand the difficulties of corresponding with the subshell. >> >> Anyway, in my case it would be absolutely fine to set a fixed value >> of PS1='\w\$ ' for the subshell and read the cwd from the prompt. In >> any case it would be better than not having the option of using ash >> as a subshell at all. >> >> Could anyone maybe provide me with a code snippet or a patch which >> shows me how to extract the cwd from the prompt assuming that PS1 has >> the value '\w\$ '? (BTW, '\$' will be evaluated to '$' or '#' in >> ash, depending on whether you are root or not.) >> >> Thank you so much. If I succeed in adding ash as a subshell, I will >> provide a (raw) patch for you to inspect and maybe refine and >> include upstream.
--- src/subshell.c 2012-03-02 13:55:52.018954847 +0100 +++ src/subshell.c 2012-03-04 05:10:53.750406898 +0100 @@ -126,6 +126,7 @@ static enum { BASH, + ASH, TCSH, ZSH, FISH @@ -269,8 +270,10 @@ switch (subshell_type) { case BASH: + /* Do we have a custom init file ~/.local/share/mc/bashrc? */ init_file = mc_config_get_full_path ("bashrc"); + /* Otherwise use ~/.bashrc */ if (access (init_file, R_OK) == -1) { g_free (init_file); @@ -294,6 +297,24 @@ break; + case ASH: + /* Do we have a custom init file ~/.local/share/mc/ashrc? */ + init_file = mc_config_get_full_path ("ashrc"); + + /* Otherwise use ~/.profile */ + if (access (init_file, R_OK) == -1) + { + g_free (init_file); + init_file = g_strdup (".profile"); + } + + /* Put init file to ENV variable used by ash */ + char *putenv_str = g_strconcat ("ENV=", init_file, NULL); + putenv (putenv_str); + // Do not use "g_free (putenv_str)", otherwise ENV will be undefined! + + break; + /* TODO: Find a way to pass initfile to TCSH and ZSH */ case TCSH: case ZSH: @@ -332,6 +353,11 @@ execl (shell, "bash", "-rcfile", init_file, (char *) NULL); break; + /* TODO for upstream patch: Execute correct ash/dash/busybox shell (not necessary for Freetz) */ + case ASH: + execl (shell, "sh", (char *) NULL); + break; + case TCSH: execl (shell, "tcsh", (char *) NULL); break; @@ -796,6 +822,9 @@ subshell_type = BASH; else if (strstr (shell, "/fish")) subshell_type = FISH; + /* TODO for upstream patch: Check if "sh" really points to ash/dash/busybox (not necessary for Freetz) */ + else if (strstr (shell, "/ash") || strstr (shell, "/dash") || strstr (shell, "/sh")) + subshell_type = ASH; else { mc_global.tty.use_subshell = FALSE; @@ -846,7 +875,7 @@ return; } } - else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe)) + else /* subshell_type is BASH, ASH or ZSH */ if (pipe (subshell_pipe)) { perror (__FILE__ ": couldn't create pipe"); mc_global.tty.use_subshell = FALSE; @@ -883,6 +912,24 @@ " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]); break; + case ASH: + /* Ash needs a somewhat complicated precmd emulation via PS1. + BUF_SMALL (defined in lib/global.h) is the length limit for precmd. */ + g_snprintf (precmd, sizeof (precmd), + + // A: This leads to a stopped subshell (=frozen mc) if user calls "sh" command + //"PS1='$(pwd>&%d; kill -STOP $$)\\\\u@\\\\h:\\\\w\\\\$ '\n", + + // B: This leads to "sh: precmd: not found" in sub-subshell if user calls "sh" command + //"precmd(){ pwd>&%d;kill -STOP $$; }; PS1='$(precmd)\\\\u@\\\\h:\\\\w\\\\$ '\n", + + // C: This works if user calls "sh" command because in sub-subshell + // PRECMD is unfedined, thus evaluated to empty string - no damage done + "precmd(){ pwd>&%d;kill -STOP $$; }; PRECMD=precmd; PS1='$(eval $PRECMD)\\\\u@\\\\h:\\\\w\\\\$ '\n", + + subshell_pipe[WRITE]); + break; + case ZSH: g_snprintf (precmd, sizeof (precmd), " precmd(){ pwd>&%d;kill -STOP $$ }\n", subshell_pipe[WRITE]); @@ -1103,6 +1150,13 @@ quote_cmd_start = "(printf \"%b\" '"; quote_cmd_end = "')"; } + /* TODO: When BusyBox printf is fixed, get rid of this "else if", see + http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */ + else if (subshell_type == ASH) + { + quote_cmd_start = "\"`echo -en '"; + quote_cmd_end = "'`\""; + } else { quote_cmd_start = "\"`printf \"%b\" '";
--- lib/mcconfig/paths.c 2012-03-04 04:28:07.000000000 +0100 +++ lib/mcconfig/paths.c 2012-03-04 04:28:43.000000000 +0100 @@ -82,6 +82,7 @@ /* data */ { "skins", &mc_data_str, MC_SKINS_SUBDIR}, { "fish", &mc_data_str, FISH_PREFIX}, + { "ashrc", &mc_data_str, "ashrc"}, { "bashrc", &mc_data_str, "bashrc"}, { "inputrc", &mc_data_str, "inputrc"}, { "extfs.d", &mc_data_str, MC_EXTFS_DIR}, --- tests/lib/mcconfig/user_configs_path.c 2012-03-04 04:27:47.000000000 +0100 +++ tests/lib/mcconfig/user_configs_path.c 2012-03-04 05:33:48.418447747 +0100 @@ -96,6 +96,7 @@ path_fail_unless (CONF_DATA, MC_SKINS_SUBDIR); path_fail_unless (CONF_DATA, FISH_PREFIX); + path_fail_unless (CONF_DATA, "ashrc"); path_fail_unless (CONF_DATA, "bashrc"); path_fail_unless (CONF_DATA, "inputrc"); path_fail_unless (CONF_DATA, MC_EXTFS_DIR); --- doc/man/mc.1.in 2012-03-04 05:18:35.970419532 +0100 +++ doc/man/mc.1.in 2012-03-04 05:35:58.262451703 +0100 @@ -2408,7 +2408,7 @@ .\"NODE " The subshell support" .SH " The subshell support" The subshell support is a compile time option, that works with the -shells: bash, tcsh and zsh. +shells: bash, ash, tcsh and zsh. .PP When the subshell code is activated the Midnight Commander will spawn a concurrent copy of your shell (the one defined in the @@ -2423,8 +2423,10 @@ If you are using .B bash you can specify startup -commands for the subshell in your ~/.local/share/mc/bashrc file and +commands for the subshell in your ~/.local/share/mc/bashrc file (fallback ~/.bashrc) and special keyboard maps in the ~/.local/share/mc/inputrc file. +.B ash +users may specify startup commands in ~/.local/share/mc/ashrc (fallback ~/.profile). .B tcsh users may specify startup commands in the ~/.local/share/mc/tcshrc file. .PP
_______________________________________________ mc-devel mailing list http://mail.gnome.org/mailman/listinfo/mc-devel