Skip to content

How to write extensions

In this tutorial we're going to write a custom function to filter found files by their file extension.

// pseudocode
function check_extension(string: extension, integer: icase)

The first parameter (extension) is a string that specifies the extension we're looking for. The second one (icase) is a number which allows us to enable case insensitive string comparison.

Writing extensions in C

To make all required types and function prototypes available include the extension-interface header in your source file. In this tutorial we create a file named "c-example.c":

$ mkdir c-example && cd ./c-example
$ wget
$ echo '#include "extension-interface.h"' > ./c-example.c

At first you should implement the registration() function to make the extension available. Please specify name, version and a brief description by calling the registration callback fn:

registration(RegistrationCtx *ctx, RegisterExtension fn)
    fn(ctx, "example extension", "0.1.0", "An example extension written in C.");

The discover() function exports the filter functions you want to provide in your extension. Please specify name, number of parameters and parameter types. Parameters can be strings or integers.

discover(RegistrationCtx *ctx, RegisterCallback fn)
    fn(ctx, "c_check_extension", 2, CALLBACK_ARG_TYPE_STRING, CALLBACK_ARG_TYPE_INTEGER);

In the example above a function named c_check_extension() is exported. It has two parameters: a string and an integer.

Now we implement the function:

c_check_extension(const char *filename, int argc, void *argv[])
    const char *offset;
    int result = 0;

    /* find extension */
    if((offset = strrchr(filename, '.')))
        /* first argument: extension to test */
        char *extension = argv[0];

        /* second argument: compare mode */
        int icase = *((int *)argv[1]);

        if(extension && strlen(offset) == strlen(extension))
            result = 1;

            while(*offset && result)
                    result = tolower(*offset) == tolower(*extension);
                    result = *offset == *extension;

                ++offset, ++extension;

    return result;

The first parameter is always the name of the found file. argc specifies the number of function arguments provided by the array argv. Please keep in mind that you receive a pointer to an integer and not the integer value.

Build the source file into a shared library and copy it to your local extension directory:

$ gcc -Wall -O2 -fPIC -nostartfiles -shared ./c-example.c -o ./
$ mkdir -p ~/.efind/extensions
$ cp ./ ~/.efind/extensions/

Now you can use the function in efind:

$ efind . 'c_check_extension(".c", 1)'

You find this example in the examples folder.

Writing extensions in Python 3

efind can load custom functions from Python scripts. Please consider that your script is imported by its module name and not by filename. As a consequence the import may fail if there's a shared library having the same name as your script in one of the extension folders.

Your script has to define the strings EXTENSION_NAME, EXTENSION_VERSION and EXTENSION_DESCRIPTION to export name, version and a brief description:

EXTENSION_NAME="example extension"
EXTENSION_DESCRIPTION="An example extension written in Python."

In our example we want to write a function to test the extension of a filename. It could be implemented the following way:

def py_check_extension(filename: str, extension: str, icase: int):
    result = 0

    _, ext = os.path.splitext(filename)

    if len(ext) > 0:
        if icase == 0:
                result = ext == extension
                result = ext.lower() == extension.lower()

    return result

The first parameter is always the name of the found file. Our example function has two additional arguments: extension and icase. To register the function properly you have to use type-hints.

The EXTENSION_EXPORT array exports your custom function(s) to efind:


After copying the script into your local extension folder (~/.efind/extensions) the custom filter function is available in efind:

$ efind . 'py_check_extension(".py", 1)'

You find this example in the examples folder.


Since version 0.2.1 efind supports logging. There are 6 different log levels available:

Level Description
1 log only fatal errors
2 log all errors
3 log errors and warnings
4 log error, warnings and messages
5 enable debugging
6 enable tracing (very detailed information)

A log message consists of the following fields:

  • timestamp
  • domain
  • source file
  • function name
  • line in the source file
  • message

When developing extensions you can use efind's logging capabilities to debug issues. Let's assume you forgot to declare the EXTENSION_EXPORT variable in a Python extension. This problem can easily be detected by enabling logging and filtering the output with grep:

$ efind --print-extensions --log-level=5 | grep -E "<python>|<extension>"
$ DEBUG Thu Jun 29 19:41:12 2017 <extension> ./extension.c, _extension_manager_extension_registered(), line 262 => Extension registered: name=example extension, version=0.1.0, description=An example extension written in Python.
$ DEBUG Thu Jun  19:41:12 2017 <python> ./py-ext-backend.c, _py_ext_discover(), line 424 => Couldn't find sequence `EXTENSION_EXPORT'.

As you may notice, the TRACE log level produces a lot of messages. Usually the DEBUG log level is sufficient for debugging purposes.