On Wednesday, 18 October 2017 at 03:48:01 UTC, Ky-Anh Huynh wrote:
Hi,

I'm using Bash heavily in my systems. Things become slow and slow when I have tons of scripts :) And sometimes it's not easy to manipulate data.

You may have heard of recutils [1] which has a C extension to be loaded by Bash. Is it possible to write similar things in D, for Bash? I am not good at C; it's great if I explore this field:)

Some examples in C are in [2].

My experience: Dlang has `pipe` support however the syntax is not as clean as Bash :) Most of the times I see short (<1k loc) Bash scripts are easy to maintain than Ruby (and now D things) scripts.

[2]: http://git.savannah.gnu.org/cgit/bash.git/tree/examples/loadables/cat.c

(Not a linux pro or even bash user here, don't know anything about bash API or internals, not even a C user, proceed on your own risk)

Need to investigate how bash actually handles loading

From the look of it there is configuration struct of type 'builtin' that has basically and entry point function that serves as main('cat_builtin'), documentation function ('cat_doc') and other required stuff.

But yes, in theory nothing crazy and seems doable.


Now according to the readme in examples folder
"""
Loadable builtins are loaded into a running shell with

        enable -f filename builtin-name
"""


This give a hint to look at what this 'enable' implementation does, my guess it can do a simple dlopen(filename) and then dlsym(builtin-name) that most likely expected to be one of those struct of type 'builtin' or that *_builtin() function, and the rest is implemenetation details.

And... yes (can search for dlsym) - http://git.savannah.gnu.org/cgit/bash.git/tree/builtins/enable.def#n365


Now with that knowledge it should be possible to our test plugin.

Some (pseudo) code that could serve as starting point...
-------------------------------------------------

// first we need 'builtin' struct in D

// (from http://git.savannah.gnu.org/cgit/bash.git/tree/builtins.h)
struct builtin {
  char* name;                   /* The name that the user types. */
sh_builtin_func_t function; /* The address of the invoked function. */
  int flags;                    /* One of the #defines above. */
const(char)* const* long_doc; /* NULL terminated array of strings. */
  const(char)* short_doc;       /* Short version of documentation. */
  char* handle;                 /* for future use */
}

// add some declarations

alias sh_builtin_func_t = extern(C) int sh_builtin_func_t (WORD_LIST *);
enum BUILTIN_ENABLED = 0x01; // builtins.h
// TODO: find WORD_LIST declaration (sorry)

extern(C) static builtin plugtest_struct = {
        "testcommand", // function will be acessible by this name?
        test_builtin,
        BUILTIN_ENABLED,
        test_doc.ptr, // will need to convert string[] to char**
        // the
        "testcommand [-] [file ...]",
        0
};

string[] test_doc = [
        "Out test function."
];


// http://git.savannah.gnu.org/cgit/bash.git/tree/examples/loadables/cat.c#n91 // seems like default int return, if you got a segfault then it is something else
extern(C) static int test_builtin(WORD_LIST* list)
{
import core.runtime; // don't remember the exact names, sorry, but this one is required
        import std.stdio : writeln;

Runtime.initialize(); // you would probably need to track this one because command can be called multiple times during plugin lifetime

writeln("it works!"); // if you see this in terminal you are lucky, otherwise find out what is 'write' and use it instead
        return 0;
}
---------------------------------------------------------------

This isn't the actual code but should give you a hint, the rest is up to you.

Reply via email to