Re: How to add subshell support for ash



Oswald Buddenhagen, 05.03.2012 09:27:
> On Sun, Mar 04, 2012 at 03:19:52PM +0100, Alexander Kriegisch wrote:
>> I know how to do this via shell script, but not cleanly via C, sorry.
>>
> you could start by posting the sh code as text or a comment ...

I do not really know what the point would be to produce something which
would never be used, but I am thinking about it and might post an update
later.

>>   - Plese refer to code comments for how and why I implemented the
>>     precmd via PS1 with two-fold indirection.
>>
> as ash is as close to pure posix as it gets, this pretty much qualifies
> as a generic solution. one could even remove the paths for the other
> bourne-compatible shells (except that they are less hacky) ...

I am providing my code as-is, so as I said, every core MC developer or
maintainer is welcome to optimise and streamline my patch as well as the
upstream code base. So if my PS1 precmd trick works for other shells,
too, feel free to use it there, too. Maybe if I have time available I
can test that, but my main concern still is ash.

>> Remark: In order to get ENV into the environment for the init file, I
>> had to uncomment "g_free (putenv_str)".
>>
> that sounds like adding a memory leak. you need to move, not remove the
> free.  but then, i don't know that code, maybe there is already a second
> cleanup path.

I took the chance to just test the INPUTRC case and found out that as I
suspected, it actually does *not* work because the variable is g_free'd
before the subshell is called. The effect is that even though I created
a test file ~/.local/share/mc/inputrc, it was not used by bash and
INPUTRC was undefined in the subshell.

So I updated my patch: putenv_str is now created before the first switch
statement and g_free(putenv_str) is called at the same place as
g_free(init_file). No matter whether my ash subshell patch is accepted
into the code base or not, the INPUTRC case needs to be fixed upstream.
Probably it never worked unless the g_free'd memory area was not
overwritten yet by the time it was used by the bash subshell. After my
fix, INPUTRC is available and used by the bash subshell. I have tested
it on my mipsel target platform.

>> +                    // A: This leads to a stopped subshell (=frozen mc) if user calls "sh" command
>>
> please fix the indentation and avoid c99/c++ comments.

I must say, I am rather happy that indentation and commenting style is
your major concern about my code and not anything functional. My updated
patch does not use c99 style comments anymore. The second patch file
remains unchanged because I did not add any comments there. As for
indentation, I did not find anything wrong there. I am using four spaces
instead of tabs just like you MC developers, even though I find that
rather strange. The indentation level is in line with the rest of the
switch-case statement, even though I personally do not indent that way,
but indentation is largely a matter of taste anyway and I am merely
trying to adapt to the style I found in your code base.

--- src/subshell.c	2012-03-02 13:55:52.018954847 +0100
+++ src/subshell.c	2012-03-05 12:53:34.000000000 +0100
@@ -126,6 +126,7 @@
 static enum
 {
     BASH,
+    ASH,
     TCSH,
     ZSH,
     FISH
@@ -266,11 +267,14 @@
         putenv (g_strdup (sid_str));
     }
 
+    char *putenv_str;
     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);
@@ -285,15 +289,33 @@
             char *input_file = mc_config_get_full_path ("inputrc");
             if (access (input_file, R_OK) == 0)
             {
-                char *putenv_str = g_strconcat ("INPUTRC=", input_file, NULL);
+                putenv_str = g_strconcat ("INPUTRC=", input_file, NULL);
                 putenv (putenv_str);
-                g_free (putenv_str);
+                /* Do not use "g_free (putenv_str)" here, otherwise INPUTRC will be undefined! */
             }
             g_free (input_file);
         }
 
         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 */
+        putenv_str = g_strconcat ("ENV=", init_file, NULL);
+        putenv (putenv_str);
+        /* Do not use "g_free (putenv_str)" here, otherwise ENV will be undefined! */
+
+        break;
+
         /* TODO: Find a way to pass initfile to TCSH and ZSH */
     case TCSH:
     case ZSH:
@@ -332,6 +354,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, "ash", (char *) NULL);
+        break;
+
     case TCSH:
         execl (shell, "tcsh", (char *) NULL);
         break;
@@ -350,6 +377,7 @@
 
     /* If we get this far, everything failed miserably */
     g_free (init_file);
+    g_free (putenv_str);
     _exit (FORK_FAILURE);
 }
 
@@ -796,6 +824,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 +877,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 +914,25 @@
                     " 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 "ash" command
+                     * "PS1='$(pwd>&%d; kill -STOP $$)\\\\u@\\\\h:\\\\w\\\\$ '\n",
+                     *
+                     * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command
+                     * "precmd(){ pwd>&%d;kill -STOP $$; }; PS1='$(precmd)\\\\u@\\\\h:\\\\w\\\\$ '\n",
+                     *
+                     * C: This works if user calls "ash" 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 +1153,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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]