diff --git a/gtk_channels/CMakeLists.txt b/gtk_channels/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b2f390d9aca6d06b39548a201640bb6dab194f64
--- /dev/null
+++ b/gtk_channels/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Set the minimum version of cmake required to build this project
+
+cmake_minimum_required(VERSION 2.6)
+project(gtk_channels)
+
+find_package(blc_channel REQUIRED)
+find_package(blc_program REQUIRED)
+find_package(blc_process REQUIRED)
+
+find_package(PkgConfig REQUIRED) 
+pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
+pkg_check_modules(GTK3_SOURCE_VIEW REQUIRED gtksourceview-4)
+pkg_check_modules(GTKMM gtkmm-3.0 REQUIRED)
+pkg_check_modules(VTE3  vte-2.91 REQUIRED) #libvtemm is not yet common
+#pkg_check_modules(GTKMMPLPLOT REQUIRED gtkmm-plplot-2.0)
+
+add_definitions(-std=c++14 ${BL_DEFINITIONS} ${GTK3_CFLAGS_OTHER} )
+include_directories(${BL_INCLUDE_DIRS} ${GTK3_INCLUDE_DIRS} ${GTK3_SOURCE_VIEW_INCLUDE_DIRS} ${VTE_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS} ${VTE3_INCLUDE_DIRS})
+link_directories(${GTK3_LIBRARY_DIRS}  ${VTE_LIBRARY_DIRS} /usr/local/lib)
+add_executable(gtk_channels gtk_channels.cpp Channel.cpp  Process.cpp parse_help.cpp)
+target_link_libraries(gtk_channels ${BL_LIBRARIES} ${GTK3_LIBRARIES} ${GTK3_SOURCE_VIEW_LIBRARIES} ${VTE_LIBRARIES} ${GTKMM_LIBRARIES} ${VTE3_LIBRARIES})
diff --git a/gtk_channels/Channel.cpp b/gtk_channels/Channel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f502fcb26689a680179bcd85406279dd9a53ed1c
--- /dev/null
+++ b/gtk_channels/Channel.cpp
@@ -0,0 +1,224 @@
+//
+//  Channel.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 29/09/2019.
+//
+
+#include "Process.h"
+#include "Channel.hpp"
+#include "blc_channel.h"
+#include "string"
+#include <vector>
+#include <forward_list>
+
+#include <vte/vte.h>
+#include <thread>
+#include <iostream>
+
+using namespace Gtk;
+using namespace Glib; //signal_idle,RefPtr
+
+using  namespace std;
+
+vector <char*> argv_vec;
+
+
+struct ChannelMenu:Gtk::Menu{
+    ChannelMenu(Gtk::TreeView &tree_view);
+
+};
+
+
+
+
+struct ModelColumns : public TreeModel::ColumnRecord{
+    TreeModelColumn<int> id, pid;
+    TreeModelColumn<string> name, type, format, dimensions, icon;
+    TreeModelColumn<Process*> process;
+    
+    
+    ModelColumns(){
+        add(id);
+        add(name);
+        add(type);
+        add(format);
+        add(dimensions);
+        add(icon);
+        add(pid);
+        add(process);
+    }
+    
+    void add_channel(RefPtr<TreeStore>  &tree, blc_channel *channel){
+        uint32_t type_str, format_str;
+        char dims_str[256];
+        TreeModel::Row childrow;
+        
+        auto row = *(tree->append()); //* is because it is an iterator and we want the content of the ref.
+        //row[id] = channel->id;
+        row[name] = channel->name;
+        row[type] = string(UINT32_TO_STRING(type_str, channel->type), 4);
+        row[format] = string(UINT32_TO_STRING(format_str, channel->format), 4);
+        channel->sprint_dims(dims_str, 256);
+        row[dimensions] = dims_str;
+        row[process]=nullptr;
+        row[id]=channel->id;
+        
+        // row[icon] =  Gtk::Stock::YES.id; //dialog-information";
+    }
+}m_Columns;
+
+static void refresh_channels(void* user_data){
+    
+    auto channel_widget = (ChannelWidget *)user_data;
+    signal_idle().connect([channel_widget](){ //important to use an idle otherwise Thread problems
+        channel_widget->refresh_channel_list();
+        return false;
+    });
+}
+
+ChannelMenu::ChannelMenu(TreeView &tree_view){
+    MenuItem *item = new MenuItem("_Remove", true);
+    append(*item);
+    accelerate(*this);
+    show_all();
+    item->signal_activate().connect([&tree_view](){
+        tree_view.get_selection()->selected_foreach_iter([](const TreeModel::iterator& iter){
+            TreeModel::Row row = *iter;
+            string name= row[m_Columns.name];
+            blc_remove_channel_with_name(name.data());
+        });;
+    });
+}
+
+
+void ChannelWidget::add_process( TreeModel::Row &row){
+    TreeModel::Row childrow;
+    string command;
+    
+    Process *process = new Process(blaar_dir+"/bin/", "o_gnuplot");
+    process->add_arg(((string)row[m_Columns.name]).data());
+    process->run();
+    
+    command=blc_program_name;
+    
+    for (auto arg = process->arg_vector.begin()+1; arg!=process->arg_vector.end()-1; arg++){
+        command+=" ";
+        command+=*arg;
+    }
+    
+    childrow = *(tree_store->append(row.children()));
+    childrow[m_Columns.name]=command;
+    childrow[m_Columns.icon]="utilities-terminal";
+    childrow[m_Columns.process]= process;
+    show_all_children();
+}
+
+void ChannelWidget::display_process( TreeModel::Row &row){
+    Process *process=row[m_Columns.process];
+    
+    process->display_terminal();
+}
+
+
+ChannelWidget::ChannelWidget(){
+    CellRendererPixbuf     cellRendererPixBuf;
+    
+    this->set_enable_search();
+   // this->m
+    
+    blaar_dir=getenv("BLAAR_DIR");
+    if (blaar_dir.empty()) EXIT_ON_ERROR("Your environment variable 'BLAAR_DIR' is not defined.");
+    
+    tree_store = TreeStore::create(m_Columns);
+    set_model(tree_store);
+    
+    append_column("Name", m_Columns.name);
+    append_column("Type", m_Columns.type);
+    append_column("Format", m_Columns.format);
+    append_column("Dimensions", m_Columns.dimensions);
+    
+    int numcols = append_column("image ", cellRendererPixBuf);
+    get_column(numcols-1)->add_attribute(cellRendererPixBuf, "icon-name",  m_Columns.icon);
+    
+    refresh_channel_list();
+    blc_channel_check_for_event(refresh_channels, this);
+    get_selection()->set_mode(SELECTION_MULTIPLE);
+    show_all_children();
+    
+   // set_events(Gdk::BUTTON_PRESS_MASK);
+    
+    signal_row_activated().connect([this](const TreeModel::Path& path, TreeViewColumn* column){
+        TreeIter iter=this->tree_store->get_iter(path);
+        TreeModel::Row row = *iter;
+        if (tree_store->iter_depth(iter) == 0) this->add_process(row);
+        else display_process(row);
+    });
+    
+    get_selection()->signal_changed().connect([](){
+        return true;
+    });
+    
+    signal_button_release_event().connect([this](GdkEventButton const *event){
+    
+        if  (event->button == 3){
+            ChannelMenu *m_Menu_Popup = new ChannelMenu(*this);
+            m_Menu_Popup->popup_at_pointer((GdkEvent*)event);
+        
+        }
+        return true;
+    });
+}
+
+/*
+void ChannelWidget::menu_add_program_item(Menu &menu, string name, string program){
+    MenuItem *item = new MenuItem(name);
+    
+    item->signal_activate().connect([this](){
+        auto refSelection = get_selection();
+        if (refSelection) {
+            TreeModel::iterator iter = refSelection->get_selected();
+            TreeModel::Row row = *iter;
+            for (TreeModel::iterator iter = refSelection-> (); ){
+                
+                string name= row[m_Columns.name];
+                blc_remove_channel_with_name(name.data());
+            }
+        }
+    });
+    menu.append(*item);
+};*/
+
+void ChannelWidget::refresh_channel_list(){
+    bool found;
+    int channels_nb;
+    blc_channel *channel_infos=nullptr;
+    channels_nb = blc_channel_get_all_infos(&channel_infos);
+    list<TreeModel::iterator> list_iters_without_channels;
+    
+    /* Generate a list of iter on row to be able to remove the row if the channel does not exist anymore*/
+    tree_store->foreach_iter([&list_iters_without_channels, this](TreeModel::iterator const &iter){
+        if (this->tree_store->iter_depth(iter)==0) list_iters_without_channels.push_back(iter); //copy iter
+        return false;
+    });
+    
+    for(blc_channel *channel=channel_infos;  channel != channel_infos+channels_nb; channel++){
+        found=false;
+        auto list_iter=list_iters_without_channels.begin();
+        while(list_iter != list_iters_without_channels.end()){
+            TreeModel::Row row = *(*list_iter);
+            if (row[m_Columns.id]==channel->id){
+                found=true;
+                list_iter=list_iters_without_channels.erase(list_iter);
+            }
+            else list_iter++;
+        }
+        
+        if (found==false)  m_Columns.add_channel(tree_store, channel);
+    }
+    for(auto list_iter: list_iters_without_channels){
+        tree_store->erase(*list_iter);
+    }
+    this->show_all_children();
+    free(channel_infos);
+}
diff --git a/gtk_channels/Channel.hpp b/gtk_channels/Channel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..098345b5d24f681c1aa144fdae66834a0141c3b6
--- /dev/null
+++ b/gtk_channels/Channel.hpp
@@ -0,0 +1,25 @@
+//
+//  Channel.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 29/09/2019.
+//
+
+#ifndef Channel_hpp
+#define Channel_hpp
+
+#include <gtkmm.h>
+
+
+
+struct ChannelWidget:Gtk::TreeView{
+    std::string blaar_dir;
+    Glib::RefPtr<Gtk::TreeStore>  tree_store;
+    
+    ChannelWidget();
+    void add_process(Gtk::TreeModel::Row &row);
+    void display_process(Gtk::TreeModel::Row &row);
+
+    void refresh_channel_list();
+};
+#endif /* Channel_hpp */
diff --git a/gtk_channels/Graph.cpp b/gtk_channels/Graph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82c96b177a9fc587b1449abc98d62479683f43f5
--- /dev/null
+++ b/gtk_channels/Graph.cpp
@@ -0,0 +1,9 @@
+//
+//  Graph.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 25/08/2017.
+//
+//
+
+#include "Graph.h"
diff --git a/gtk_channels/Link.cpp b/gtk_channels/Link.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9ecf57df018f35e51b4a5d882741e558f0ca6329
--- /dev/null
+++ b/gtk_channels/Link.cpp
@@ -0,0 +1,146 @@
+//
+//  Link.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/08/2017.
+//
+//
+
+#include "common.h"
+#include "Link.h"
+#include "Process_old.h"
+#include <blc_core.h> //APPEND_ITEM
+
+#include <gtk/gtk.h>
+
+Link::Link(Script *script, Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync):sync(sync), script(script), selected(0), input_process(input_process), output_process(output_process){
+    Link *link=this;    
+  //  printf("Input program: %s nodes:%d:%s\n", input_process->program->name, output_process->program->output_channels_nb, output_channel);
+   // printf("Out program: %s nodes:%d:%s\n", output_process->program->name, output_process->program->input_channels_nb, input_channel);
+    
+    asprintf(&name, "link_%d", script->links_nb);
+    
+ //   edge=agedge(current_script->main_graph, input_process->node, output_process->node, name, 1);
+//    agsafeset(edge, (char*)"label", (char*)name, (char*)"");
+    if (sync==0) {
+ //       agsafeset(edge, (char*)"style", (char*)"dashed", (char*)"");
+  //      agsafeset(edge, (char*)"constraint", (char*)"false", (char*)"");
+
+    }
+    
+ /*   agsafeset(edge,(char*)"tailport",output_channel, (char*)"");
+    agsafeset(edge,(char*)"headport",input_channel, (char*)"");*/
+    APPEND_ITEM(&script->links, &script->links_nb, &link);
+    if (input_process->output_links_nb==0 && output_process->input_links_nb==0) output_process->input_process=input_process;
+    APPEND_ITEM(&input_process->output_links, &input_process->output_links_nb, &link);
+    APPEND_ITEM(&output_process->input_links, &output_process->input_links_nb, &link);
+}
+
+Link::~Link(){
+    Link *tmp_link=this;
+    
+    
+    
+    REMOVE_ITEM(&input_process->output_links, &input_process->output_links_nb, &tmp_link);
+    REMOVE_ITEM(&output_process->input_links, &output_process->input_links_nb, &tmp_link);
+    if (input_process->output_links_nb==0 && output_process->input_links_nb==0) output_process->input_process=NULL;
+ 
+
+    if (selected)   REMOVE_ITEM(&script->selected_links,&script->selected_links_nb, &tmp_link);
+    REMOVE_ITEM(&script->links,&script->links_nb, &tmp_link);
+ //   agdelete(script->main_graph, edge);
+    free(name);
+}
+
+Link *Link::dialog(Script *script, Process_old *input_process, Process_old *output_process){
+    char **channel;
+    Link *link;
+    
+    GtkWidget  *dialog, *box, *in_combobox, *out_combobox, *sync_checkbox;
+    GtkWidget  *grid;
+    
+    dialog=gtk_dialog_new_with_buttons("New link", NULL, GTK_DIALOG_MODAL,  "_OK", GTK_RESPONSE_ACCEPT, "_Cancel", GTK_RESPONSE_REJECT, NULL);
+    box=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+    
+    grid=gtk_grid_new();
+    
+    in_combobox=gtk_combo_box_text_new();
+    out_combobox=gtk_combo_box_text_new();
+    sync_checkbox=gtk_check_button_new();
+    
+    gtk_grid_attach(GTK_GRID(grid),  gtk_label_new(input_process->program->name), 0, 0, 1, 1);
+    FOR_EACH(channel, input_process->program->output_channels, input_process->program->output_channels_nb) gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(in_combobox), NULL, *channel);
+    
+    gtk_grid_attach(GTK_GRID(grid),  gtk_label_new(output_process->program->name), 0, 1, 1, 1);
+    FOR_EACH(channel, output_process->program->input_channels, output_process->program->input_channels_nb) gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(out_combobox), NULL, *channel);
+    
+    gtk_grid_attach(GTK_GRID(grid),  gtk_label_new("Synchronize"), 0, 2, 1, 1);
+
+    
+    gtk_combo_box_set_active(GTK_COMBO_BOX(in_combobox), 0);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(out_combobox), 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sync_checkbox), 1);
+    
+    gtk_grid_attach(GTK_GRID(grid), in_combobox, 1, 0, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), out_combobox, 1, 1, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), sync_checkbox, 1, 2, 1, 1);
+
+    
+    gtk_container_add(GTK_CONTAINER(box), grid);
+    gtk_widget_show_all(dialog);
+    
+    switch (gtk_dialog_run(GTK_DIALOG(dialog)))
+    {
+        case GTK_RESPONSE_ACCEPT:
+            link=new Link(script, input_process, gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(in_combobox)),
+                          output_process, gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(out_combobox)),
+                          gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_checkbox)));
+            break;
+        case GTK_RESPONSE_CANCEL:
+            link=NULL;
+            break;
+    }
+    gtk_widget_destroy(dialog);
+    
+    return link;
+}
+
+
+void Link::edit(){
+    
+    dialog(script, input_process, output_process);
+}
+
+void Link::select(){
+    Link *tmp_link;
+    if (selected==0){
+//        agsafeset( edge, (char*) "penwidth", (char*) "3", (char*)"1");
+
+      //  agsafeset( edge, (char*) "color", (char*) "red", "black");
+        tmp_link=this;
+        APPEND_ITEM(&script->selected_links, &script->selected_links_nb, &tmp_link);
+        selected=1;
+    }
+}
+
+void Link::unselect(){
+    Link *tmp_link;
+
+    if (selected){
+    /*    if (sync) agset((void*) edge, (char*) "style", (char*) "dashed");
+        else agset((void*) edge, (char*) "style", (char*) "solid");*/
+  //      agsafeset((void*) edge, (char*) "color", (char*) "black", "black");
+//        agsafeset( edge, (char*) "penwidth", (char*) "1", (char*)"1");
+
+
+        tmp_link=this;
+
+        REMOVE_ITEM(&script->selected_links, &script->selected_links_nb, &tmp_link);
+        selected=0;
+    }
+    
+}
+
+
+
+
diff --git a/gtk_channels/Link.h b/gtk_channels/Link.h
new file mode 100644
index 0000000000000000000000000000000000000000..98cc30db88dc8b8a859eae6fa779cd4e8998c2f1
--- /dev/null
+++ b/gtk_channels/Link.h
@@ -0,0 +1,38 @@
+//
+//  Link.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/08/2017.
+//
+//
+
+#ifndef LINK_H
+#define LINK_H
+
+#include "Process_old.h"
+#include "Script.h"
+
+class Script;
+class Process_old; 
+
+class Link{
+    int sync;
+    Script *script;
+
+public:
+    char *name;
+    int selected;
+    Process_old *input_process, *output_process;
+
+    
+    Link(Script *script, Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync);
+    ~Link();
+    
+    void edit();
+    void select();
+    void unselect();
+    
+    static Link *dialog(Script *script, Process_old *input_process, Process_old *output_process);
+};
+
+#endif /* Link_hpp */
diff --git a/gtk_channels/Menu.cpp b/gtk_channels/Menu.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..930bbab8344594dd414647a4035b39779f76d34d
--- /dev/null
+++ b/gtk_channels/Menu.cpp
@@ -0,0 +1,131 @@
+//
+//  menu.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 23/08/2017.
+//
+//
+
+#include "Script.h" //current_script
+#include "Menu.h"
+#include "common.h"
+#include "blc_program.h"
+#include "parse_help.h"
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <libgen.h> //basename
+
+void action_input_cb(GtkWidget *widget, Program *menu_action)
+{
+    char  *argv[2];
+    char path[PATH_MAX+1];
+    (void)widget;
+    
+  //  SPRINTF(path, "%s/%s", blaar_bin_dir, menu_action->program_name);
+    
+    argv[0]=basename(path);
+    argv[1]=NULL;
+    // execute_process_with_iter(create_command_line(dirname(path), argv));
+}
+
+static void add_process_cb(GtkWidget *widget,  Program *program){
+    (void)widget;
+    
+    current_script->add_process(program, NULL);
+}
+
+static void process_dialog_cb(GtkWidget *widget, Program *program){
+    (void)widget;
+    
+    current_script->add_process(program, program->argv_dialog());
+}
+
+static int possible_default(int positional_arguments_nb, PROGRAM_TYPE  type){
+    return ((positional_arguments_nb==0 && type==INPUT_PROGRAM) || (positional_arguments_nb==1 && type==FUNCTION_PROGRAM));
+}
+
+/**Read the list of programs in directory_str and add it to the menu if the type (INPUT_PROGRAM, FUNCTION_PROGRAM) match.
+ */
+static void add_directory_programs(GtkWidget *menu_shell, char const *directory_str, PROGRAM_TYPE type){
+    
+    GtkWidget *default_item, *action_button, *menu, *menu_item;
+    DIR *directory;
+    struct stat status;
+    struct dirent program_dirent, *result;
+    
+    char const *description;
+    char token[NAME_MAX+1];
+    int positional_arguments_nb, optional_arguments_nb;
+    Program *program;
+    struct blc_optional_argument *optional_arguments;
+    struct blc_positional_argument *positional_arguments;
+    
+    SYSTEM_ERROR_CHECK(directory = opendir(directory_str), NULL, "Opening dir '%s'.", directory_str);
+    do{
+        if (readdir_r(directory, &program_dirent, &result) != 0)  EXIT_ON_ERROR("readdir_r error parsing dir.");
+        else if (result){
+            if (result->d_type==DT_REG){
+                
+                if (((type==INPUT_PROGRAM) && ( strncmp(result->d_name, "i_", 2)==0)) || ((type==FUNCTION_PROGRAM) && (( strncmp(result->d_name, "f_", 2)==0) ||  ( strncmp(result->d_name, "o_", 2)==0)))){
+                    fstat(result->d_reclen, &status);
+                    description=blc_parse_help(directory_str, result->d_name, &optional_arguments, &optional_arguments_nb, &positional_arguments, &positional_arguments_nb, NULL);
+                    
+                    
+                    if (positional_arguments_nb || optional_arguments_nb>1){
+                        program=new Program(result->d_name, type, description, optional_arguments, optional_arguments_nb, positional_arguments, positional_arguments_nb);
+                      
+                        if (possible_default(positional_arguments_nb, type))
+                        {
+                            menu=gtk_menu_new();
+                            menu_item = gtk_menu_item_new_with_label(result->d_name);
+
+                            gtk_container_add(GTK_CONTAINER(menu_shell), menu_item);
+                            gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
+                            default_item = gtk_menu_item_new_with_label("default");
+                            action_button = gtk_menu_item_new_with_label("options ...");
+                            gtk_container_add(GTK_CONTAINER(menu), default_item);
+                            gtk_container_add(GTK_CONTAINER(menu), action_button);
+
+                            g_signal_connect(G_OBJECT(default_item), "activate", G_CALLBACK(add_process_cb), program);
+                        }
+                        else{
+                            SPRINTF(token, "%s ...",result->d_name);
+                            action_button = gtk_menu_item_new_with_label(token);
+                            gtk_container_add(GTK_CONTAINER(menu_shell), action_button);
+                            
+                        }
+                        g_signal_connect(G_OBJECT(action_button), "activate", G_CALLBACK(process_dialog_cb), program);
+                    }
+                    else{
+                        if (possible_default(positional_arguments_nb, type)){
+                            default_item = gtk_menu_item_new_with_label(result->d_name);
+                            program=new Program(result->d_name, type, description);
+                            gtk_container_add(GTK_CONTAINER(menu_shell), default_item);
+                            g_signal_connect(G_OBJECT(default_item), "activate", G_CALLBACK(add_process_cb), program);
+                        }
+                    }
+                }
+            }
+        }
+    }while(result);
+    closedir(directory);
+}
+
+BlaarMenu::BlaarMenu(PROGRAM_TYPE menu_type){
+    
+    widget=gtk_menu_new();
+    
+    if (menu_type==INPUT_PROGRAM){
+        add_directory_programs(widget, blaar_bin_dir, INPUT_PROGRAM);
+        add_directory_programs(widget, blaar_scripts_dir, INPUT_PROGRAM);
+    }
+    else if (menu_type==FUNCTION_PROGRAM){
+        add_directory_programs(widget, blaar_bin_dir, FUNCTION_PROGRAM);
+        add_directory_programs(widget, blaar_scripts_dir, FUNCTION_PROGRAM);
+    }
+    else EXIT_ON_ERROR("Wrong option '%d'", menu_type);
+    gtk_widget_show_all(widget);
+}
+
diff --git a/gtk_channels/Menu.h b/gtk_channels/Menu.h
new file mode 100644
index 0000000000000000000000000000000000000000..b95214dbea5cd92918cf0bc9c7d6f8c3b254c581
--- /dev/null
+++ b/gtk_channels/Menu.h
@@ -0,0 +1,21 @@
+//
+//  Menu.h
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 25/08/2017.
+//
+//
+
+#ifndef MENU_H
+#define MENU_H
+
+#include "common.h"
+#include <gtk/gtk.h>
+
+struct BlaarMenu{
+    
+    BlaarMenu(PROGRAM_TYPE menu_type);
+    GtkWidget *widget;
+};
+
+#endif
diff --git a/gtk_channels/Plot.cpp b/gtk_channels/Plot.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..64975352edded099353005281bc3fea77bec7900
--- /dev/null
+++ b/gtk_channels/Plot.cpp
@@ -0,0 +1,18 @@
+//
+//  Plot.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/09/2019.
+//
+
+#include "Plot.hpp"
+
+
+
+
+
+
+Plot::Plot(){
+    
+    
+}
diff --git a/gtk_channels/Plot.hpp b/gtk_channels/Plot.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1bd8f03a02e9d35cef83a4660d44759a36a0feb7
--- /dev/null
+++ b/gtk_channels/Plot.hpp
@@ -0,0 +1,24 @@
+//
+//  Plot.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/09/2019.
+//
+
+#ifndef Plot_hpp
+#define Plot_hpp
+
+#include <stdio.h>
+#include <gtkmm-plplot.h>
+
+
+struct Plot{
+    Gtk::PLplot::Plot2D plot;
+    Gtk::PLplot::Canvas canvas(plot);
+    
+    Plot();
+ //   plot.
+};
+
+
+#endif /* Plot_hpp */
diff --git a/gtk_channels/Process.cpp b/gtk_channels/Process.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..75d857959eb9fa600f0af5c8726e2e937a1f14f8
--- /dev/null
+++ b/gtk_channels/Process.cpp
@@ -0,0 +1,68 @@
+//
+//  Process.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/08/2017.
+//
+//
+#include "Process.h"
+#include "blc_core.h"
+#include <errno.h>
+#include <unistd.h> //execve
+
+#include <thread>
+
+#include <vte/vte.h>
+#include <sys/pipe.h>
+#include <gtkmm.h>
+
+
+using namespace std;
+using namespace Gtk;
+
+void TerminalSpawnAsyncCallback (VteTerminal *terminal, GPid pid, GError *error, gpointer user_data){
+    
+    char **argv=(char**)user_data;
+    if (error){
+        fprintf(stderr, "Process error: '%s'\n", error->message);
+        for (auto arg=argv; *arg!=nullptr; arg++){
+            fprintf(stderr, "%s ", *arg);
+        }
+        fprintf(stderr, "\n");
+    }
+    else fprintf(stderr, "Success pid %d\n", pid);
+};
+
+Process::Process(string const &directory, string const &program):directory(directory),program_name(program){
+    command_path=directory+program;
+    arg_vector.emplace_back(command_path.data());
+};
+
+Process::~Process(){
+    for (char const *&arg: arg_vector ){
+        free(&arg);
+    }
+};
+
+void Process::add_arg(string const &arg){
+    arg_vector.emplace_back(strdup(arg.data()));
+}
+
+void Process::run(){
+    
+    terminal=VTE_TERMINAL(vte_terminal_new());
+    arg_vector.emplace_back(nullptr);
+    
+    vte_terminal_spawn_async(terminal, VTE_PTY_DEFAULT , nullptr, (char**)arg_vector.data(), nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, 1000, nullptr, TerminalSpawnAsyncCallback, arg_vector.data());
+    
+   
+}
+
+void Process::display_terminal(){
+    
+    Widget *mm_terminal=Glib::wrap(GTK_WIDGET(terminal));
+          
+    terminal_window=new Window();
+    terminal_window->add(*mm_terminal);
+    terminal_window->show_all();
+}
diff --git a/gtk_channels/Process.h b/gtk_channels/Process.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd44a8b0a06ec495ba298f286d4561981391b4fb
--- /dev/null
+++ b/gtk_channels/Process.h
@@ -0,0 +1,40 @@
+//
+//  Process.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 30/08/2017.
+//
+//
+
+#ifndef PROCESS_H
+#define PROCESS_H
+
+#include <vector>
+#include <string>
+#include <thread>
+#include <gtkmm.h>
+#include <vte/vte.h>
+
+
+struct Process{
+    pid_t pid;
+
+    int stdin_fileno, stdout_fileno, stderr_fileno;
+    std::string name;
+    std::string directory;
+    std::string program_name;
+    std::string command_path;
+    std::string channel;
+    std::thread *run_thread;
+    std::vector<const char*> arg_vector;
+    VteTerminal *terminal;
+    Gtk::Window *terminal_window;
+    
+    Process(std::string const &directory, std::string const &program_name);
+    ~Process();
+
+    void add_arg(std::string const &arg);
+    void run();
+    void display_terminal();
+};
+#endif /* Process_hpp */
diff --git a/gtk_channels/Program.cpp b/gtk_channels/Program.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a209e32166c792836f4e44468e1ad73a3de696b
--- /dev/null
+++ b/gtk_channels/Program.cpp
@@ -0,0 +1,294 @@
+//
+//  Process.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 29/08/2017.
+//
+//
+
+#include "Program.h"
+
+#include <blc_core.h>
+#include <string.h>
+
+
+static void toggled_option_cb(GtkToggleButton *button, gpointer widget){
+    gtk_widget_set_sensitive(GTK_WIDGET(widget), gtk_toggle_button_get_active(button));
+}
+
+Program::Program(char const *program_name, PROGRAM_TYPE type, char const *description):  description(description), type(type){
+    this->name=strdup(program_name);
+}
+
+Program::Program(char const*program_name, PROGRAM_TYPE type, char const*description, blc_optional_argument *optional_arguments, int optional_arguments_nb, blc_positional_argument *positional_arguments, int positional_arguments_nb)
+: description(description), optional_arguments(optional_arguments), optional_arguments_nb(optional_arguments_nb), positional_arguments(positional_arguments), positional_arguments_nb(positional_arguments_nb), type(type)
+{
+    blc_positional_argument *positional_argument;
+    blc_optional_argument *optional_argument;
+    char const *port_name;
+    
+    input_channels_nb=0;
+    input_channels=NULL;
+    output_channels_nb=0;
+    output_channels=NULL;
+    
+    this->name=strdup(program_name);
+    
+    FOR_EACH(positional_argument, positional_arguments, positional_arguments_nb){
+        if (strcmp(positional_argument->name, "blc_channel-in")==0){
+            asprintf((char**)&port_name, "%d", input_channels_nb);
+            APPEND_ITEM(&input_channels, &input_channels_nb, &port_name);
+        }
+        else if (strcmp(positional_argument->name, "blc_channel-out")==0){
+            asprintf((char**)&port_name, "%d", output_channels_nb);
+            APPEND_ITEM(&output_channels, &output_channels_nb, &port_name);
+        }
+    }
+    
+    FOR_EACH(optional_argument, optional_arguments, optional_arguments_nb){
+        if (optional_argument->value){
+            if (strcmp(optional_argument->value, "blc_channel-in")==0){
+                if (optional_argument->longname) port_name=optional_argument->longname;
+                else port_name=optional_argument->shortname;
+                APPEND_ITEM(&input_channels, &input_channels_nb, &port_name);
+            }
+            else if (strcmp(optional_argument->value, "blc_channel-out")==0){
+                if (optional_argument->longname) port_name=optional_argument->longname;
+                else port_name=optional_argument->shortname;
+                APPEND_ITEM(&output_channels, &output_channels_nb, &port_name);
+            }
+        }
+    }
+}
+
+static void remove_args(char ***argv, char **arg_pt, int args_to_remove_nb){
+    int  i, argc;
+    
+    for (argc=0; argv[argc]!=NULL; argc++); //We ount current arg_nb
+    
+    for(i=0; arg_pt[i+args_to_remove_nb]!=NULL; i++){
+        arg_pt[i]=arg_pt[i+args_to_remove_nb];
+    }
+    arg_pt[i]=NULL;
+    
+    MANY_REALLOCATIONS(argv, argc-args_to_remove_nb+1);
+}
+
+static char const* find_option(blc_optional_argument *option, char ***argv){
+    
+    char **arg_pt;
+    char const *arg=NULL;
+    
+    for (arg_pt=*argv; *arg_pt!=NULL; arg_pt++){
+        if (strncmp(*arg_pt, "--", 2)== 0){ //It may be a long option
+            if (strncmp((*arg_pt)+2, option->longname, strlen(option->longname))==0) {
+                if (option->value) {
+                    arg=strpbrk(*arg_pt, " =");
+                    while(arg[0]==' ') arg++; //Remove spaces
+                    remove_args(argv, arg_pt, 1);
+                    break;
+                }
+                else {
+                    arg="1"; //It is a flag
+                    remove_args(argv, arg_pt, 1);
+                    break;
+                }
+            }
+        }
+        else if ((*arg_pt)[0]=='-'){ //It may be a short option
+            if ((*arg_pt)[1]==option->shortname[0]){
+                if (option->value){
+                    arg=*arg_pt+2; //We remove first two characters ( -<letter> ) and remove spaces after if there are
+                    while(arg[0]==' ') arg++;
+                    remove_args(argv, arg_pt, 1);
+                    break;
+                }
+                else {
+                    arg="1"; //It is a flag
+                    remove_args(argv, arg_pt, 1);
+                    break;
+                }
+            }
+        }
+    }
+    return arg; //option not found
+}
+
+/**Display a dialog menu with all the possible options and parameters
+ If exisiting_argv is not NULL, the default options are already filled
+ */
+char  **Program::argv_dialog(char **existing_argv)
+{
+    blc_optional_argument *option;
+    char const *pos;
+    char const *value;
+    char *arg;
+    char const *existing_value;
+    char **argv=NULL;
+    int argc, existing_id;
+    int index;
+    
+    char token[NAME_MAX+1];
+    char text[NAME_MAX+1];
+    int line_id;
+    
+    GtkWidget **optional_displays=NULL, **positional_displays;
+    int token_size;
+    GtkWidget  *dialog, *box;
+    GtkWidget  *parameter_label, *description_label, *grid, *help_label;
+    int i;
+    
+  //  dialog=gtk_dialog_new_with_buttons(name, main_window->gobj(), GTK_DIALOG_MODAL,  "_OK", GTK_RESPONSE_ACCEPT, "_Cancel", GTK_RESPONSE_REJECT, NULL);
+    box=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+    description_label=gtk_label_new(description);
+    grid=gtk_grid_new();
+    
+    line_id=0;
+    
+    optional_displays=MANY_ALLOCATIONS(optional_arguments_nb, GtkWidget*);
+    FOR(i, optional_arguments_nb) {
+        option=&optional_arguments[i];
+        
+        if (existing_argv) existing_value=find_option(option, &existing_argv);
+        else existing_value=NULL;
+        
+        if (option->value==NULL) optional_displays[i]=gtk_label_new(""); //This is a flag
+        else {
+            if (strchr(option->value, '|') || existing_value){
+                index=0;
+                pos=option->value;
+                optional_displays[i]=gtk_combo_box_text_new_with_entry();
+                token_size=0;
+                sscanf(pos, "%*[ =]%n", &token_size); //remove spaces or equal
+                pos+=token_size;
+                existing_id=-1;
+                while(sscanf(pos, "%[^| ]%n", token, &token_size)==1) {
+                    pos+=token_size;
+                    token_size=0;
+                    sscanf(pos, "%*[| ]%n", &token_size); //remove spaces or equal
+                    pos+=token_size;
+                    gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(optional_displays[i]), NULL, token);
+                    if ((existing_value) && (strcmp(token, existing_value)==0))  gtk_combo_box_set_active(GTK_COMBO_BOX(optional_displays[i]), index);
+                    index++;
+                }
+                
+                if (existing_value==NULL) gtk_combo_box_set_active(GTK_COMBO_BOX(optional_displays[i]), 0);
+                else {
+                    if (gtk_combo_box_get_active(GTK_COMBO_BOX(optional_displays[i]))==-1){
+                        gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(optional_displays[i]), NULL, existing_value);
+                        gtk_combo_box_set_active(GTK_COMBO_BOX(optional_displays[i]), index);
+                    }
+                }
+            }
+            else {
+                optional_displays[i]=gtk_entry_new();
+                gtk_entry_set_text(GTK_ENTRY(optional_displays[i]), option->value);
+            }
+        }
+        
+        if (option->longname) SPRINTF(text, "--%s", option->longname);
+        else SPRINTF(text, "-%s", option->shortname);
+        
+        parameter_label=gtk_toggle_button_new_with_label(text);
+        gtk_widget_set_hexpand(parameter_label, 1);
+        gtk_widget_set_halign(parameter_label, GTK_ALIGN_START);
+        gtk_grid_attach(GTK_GRID(grid), parameter_label, 0, line_id, 1, 1);
+        help_label=gtk_label_new(option->help);
+        gtk_widget_set_halign(help_label, GTK_ALIGN_START);
+        if (optional_displays[i]) gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(optional_displays[i]), 1, line_id, 1, 1);
+        //    else  optional_displays[i]=help_label;
+        
+        if (existing_value==NULL) gtk_widget_set_sensitive(GTK_WIDGET(optional_displays[i]), FALSE);
+        else gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(parameter_label), TRUE);
+        
+        g_signal_connect(G_OBJECT(parameter_label), "toggled", G_CALLBACK(toggled_option_cb), optional_displays[i]);
+        
+        gtk_grid_attach(GTK_GRID(grid), help_label, 2, line_id, 1, 1);
+        line_id++;
+    }
+    
+    positional_displays=MANY_ALLOCATIONS(positional_arguments_nb, GtkWidget*);
+    FOR(i, positional_arguments_nb) {
+        parameter_label=gtk_label_new(positional_arguments[i].name);
+        positional_displays[i]=gtk_entry_new();
+        
+        if (existing_argv && existing_argv[1]) {
+            gtk_entry_set_text(GTK_ENTRY(positional_displays[i]), existing_argv[1]); //0 is the program name
+            remove_args(&existing_argv, &existing_argv[1], 1);
+        }
+        else if (positional_arguments[i].value) gtk_entry_set_text(GTK_ENTRY(positional_displays[i]), positional_arguments[i].value);
+        
+        gtk_widget_set_hexpand(parameter_label, 0);
+        
+        gtk_widget_set_halign(parameter_label, GTK_ALIGN_START);
+        gtk_grid_attach(GTK_GRID(grid), parameter_label, 0, line_id, 1, 1);
+        gtk_grid_attach(GTK_GRID(grid), positional_displays[i], 1, line_id, 1, 1);
+        line_id++;
+    }
+    
+    gtk_label_set_line_wrap (GTK_LABEL(description_label), 1);
+    gtk_widget_set_halign(description_label, GTK_ALIGN_START);
+    
+    gtk_container_add(GTK_CONTAINER(box), description_label);
+    gtk_container_add(GTK_CONTAINER(box), grid);
+    
+    gtk_widget_show_all(dialog);
+    
+    switch (gtk_dialog_run(GTK_DIALOG(dialog)))
+    {
+        case GTK_RESPONSE_ACCEPT:
+            //       SPRINTF(path, "%s/%s", functions_dir, program_name);
+            argv=NULL;
+            argc=0;
+            value=strdup(name);
+            APPEND_ITEM(&argv, &argc, &value);
+            FOR(i, optional_arguments_nb){
+                if( gtk_widget_get_sensitive(GTK_WIDGET(optional_displays[i]))) {
+                        if (GTK_IS_ENTRY(optional_displays[i])) value=strdup(gtk_entry_get_text(GTK_ENTRY(optional_displays[i])));
+                        else if (GTK_IS_COMBO_BOX_TEXT(optional_displays[i])) value=strdup(gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(optional_displays[i])));
+                        else value=NULL; //It is a flag
+                    
+                    if (optional_arguments[i].shortname){
+                        if (value==NULL) SYSTEM_ERROR_CHECK(asprintf(&arg, "-%s", optional_arguments[i].shortname), -1, NULL);
+                        else{
+                            if (strchr(value, ' ')) SYSTEM_ERROR_CHECK(asprintf(&arg, "-%s\"%s\"", optional_arguments[i].shortname, value), -1, NULL); //protect spaces
+                            else SYSTEM_ERROR_CHECK(asprintf(&arg, "-%s%s", optional_arguments[i].shortname, value), -1, NULL);
+                        }
+                    }
+                    else if (optional_arguments[i].longname){
+                        if (value==NULL) SYSTEM_ERROR_CHECK(asprintf(&arg, "--%s", optional_arguments[i].longname), -1, NULL);
+                        else {
+                            if (strchr(value, ' ')) SYSTEM_ERROR_CHECK(asprintf(&arg, "--%s=\"%s\"", optional_arguments[i].longname,  value), -1, NULL); //protect spaces
+                            else SYSTEM_ERROR_CHECK(asprintf(&arg, "--%s=%s", optional_arguments[i].longname,  value), -1, NULL);
+                        }
+                    }
+                    else EXIT_ON_ERROR("An option must have at least a short or long name");
+                    APPEND_ITEM(&argv, &argc, &arg);
+                }
+            }
+            
+            FOR(i, positional_arguments_nb){
+                value=strdup(gtk_entry_get_text(GTK_ENTRY(positional_displays[i])));
+                APPEND_ITEM(&argv, &argc, &value);
+            }
+            arg=NULL;
+            APPEND_ITEM(&argv, &argc, &arg);
+            //          execute_process_with_iter(create_command_line(dirname(path), argv));
+            
+            break;
+        case GTK_RESPONSE_CANCEL:break;
+    }
+    
+    gtk_widget_destroy (dialog);
+    
+    if (existing_argv){
+        for (i=0; existing_argv[i]!=NULL; i++) free(existing_argv[i]);
+    }
+    FREE(existing_argv);
+    FREE(optional_displays);
+    FREE(positional_displays);
+    return argv;
+}
+
+
+
diff --git a/gtk_channels/Program.h b/gtk_channels/Program.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f8cfede673e9836cb32c6aaa46b6de91d461a6c
--- /dev/null
+++ b/gtk_channels/Program.h
@@ -0,0 +1,38 @@
+//
+//  Process.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 29/08/2017.
+//
+//
+
+#ifndef PROGRAM_H
+#define PROGRAM_H
+
+#include "parse_help.h"
+#include <stdlib.h>
+
+typedef enum {INPUT_PROGRAM, FUNCTION_PROGRAM } PROGRAM_TYPE;
+typedef enum {INPUT_CHANNEL, OUTPUT_CHANNEL} CHANNEL_DIRECTION;
+
+class Program
+{
+public:
+
+    char const *description;
+    blc_optional_argument *optional_arguments;
+    int optional_arguments_nb;
+    blc_positional_argument *positional_arguments;
+    int positional_arguments_nb;
+    
+    PROGRAM_TYPE type;
+    char const *name;
+    char  ** argv_dialog(char **argv=NULL);
+    char **input_channels, **output_channels, **input_channels_help, **output_channels_help;
+    int input_channels_nb, output_channels_nb;
+    
+    Program(char const*program_name, PROGRAM_TYPE type, char const*description);
+    Program(char const*program_name, PROGRAM_TYPE type, char const*description, blc_optional_argument *, int, blc_positional_argument *, int);
+};
+
+#endif /* PROGRAM_H */
diff --git a/gtk_channels/Script.cpp b/gtk_channels/Script.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e53878b40dc5050d0317242f1fc29fbc7259549
--- /dev/null
+++ b/gtk_channels/Script.cpp
@@ -0,0 +1,431 @@
+
+#include "common.h"
+#include "gtk_common.h"
+#include <gtksourceview/gtksource.h>
+#include "Script.h"
+#include "blc_core.h"
+#include <stdarg.h>
+#include "Process_old.h"
+#include "Link.h"
+
+
+Script **Script::array=NULL;
+int Script::nb=0;
+
+void status_push(char const *format, ...){
+    va_list arguments;
+    char *message=NULL;
+    va_start(arguments, format);
+    vasprintf(&message, format, arguments);
+    gtk_statusbar_push(GTK_STATUSBAR(general_statusbar), statusbar_context_id, message);
+    FREE(message);
+    va_end(arguments);
+}
+
+typedef enum {
+    ACTION_NOTHING = 0, ACTION_CREATE_LINK, ACTION_CREATE_LINK_AND_PROCESS, ACTION_SELECT_PROCESS
+} ACTION_TYPE;
+
+ACTION_TYPE action;
+
+static int motion_gtk_cb(GtkWidget *widget, GdkEventButton *event, Script *script){
+    Process_old *process;
+    (void) widget;
+    
+    if (script->initial_process)
+    {
+        process = script->get_process(event->x, event->y);
+        if (process == NULL){
+            STATUS_PUSH("Release button to create a link from '%s' to a new process.", script->initial_process->name);
+            action = ACTION_CREATE_LINK_AND_PROCESS;
+        }
+        else if (process==script->initial_process){
+            if (action == ACTION_CREATE_LINK_AND_PROCESS){
+                STATUS_PUSH("Release button to create a recursive link to '%s'.", script->initial_process->name);
+                action = ACTION_CREATE_LINK;
+            }
+        }
+        else{
+            action = ACTION_CREATE_LINK;
+            STATUS_PUSH("Release button to create a link from '%s' to '%s'.", script->initial_process->name, process->name);
+        }
+    }
+    return TRUE;
+}
+
+static int button_press_gtk_cb(GtkWidget *widget, GdkEventButton *event, Script *script){
+    Process_old *process=NULL;
+    Link *link=NULL;
+    
+    if (event->button == 1){
+        process=script->get_process(event->x, event->y);
+        if (process==NULL) link=script->get_link(event->x, event->y);
+        
+        if (event->type == GDK_BUTTON_PRESS){
+            action = ACTION_NOTHING;
+            
+            script->initial_link=link;
+            if (process){
+                STATUS_PUSH(process->name);
+                script->initial_process=process;
+            }
+            else if (link) STATUS_PUSH(link->name);
+        }
+        else
+            if (event->type == GDK_2BUTTON_PRESS){
+                if (process && process==script->initial_process){
+                    process->edit();
+                }
+                else if (link && link==script->initial_link){
+                    link->edit();
+                }
+                /*           if (initial_link != NULL) initial_link->edit();
+                 else if (initial_group != NULL){
+                 strcpy(previous_group_name, initial_group->no_name);
+                 if (initial_group->edit())
+                 {
+                 agdelete(leto->main_graph, initial_group->ag_node);
+                 initial_group->add_to_graph(leto->graphs[initial_group->ech_temps]);
+                 leto->update_links_with_new_group_name(previous_group_name, initial_group->no_name);
+                 
+                 leto->update_layout();
+                 }
+                 }
+                 }
+                 else if (event->type == GDK_BUTTON_PRESS)
+                 {*/
+            }
+    }
+    
+    return TRUE;
+}
+
+static int button_release_gtk_cb(GtkWidget *widget, GdkEventButton *event,  Script *script){
+    Link *link;
+    Process_old *process;
+    char *output_channel=NULL, *input_channel=NULL;
+    Process_old *input_process, *output_process;
+    
+    int need_redraw = 0;
+    (void) widget;
+    
+    process=script->get_process(event->x, event->y);
+    if (process==NULL) link=script->get_link(event->x, event->y);
+    
+    if (event->button == 1){
+        switch (action){
+            case ACTION_NOTHING:
+                if ((process) && (process == script->initial_process)){
+                    if (event->state & GDK_CONTROL_MASK){
+                        if (process->selected) process->unselect();
+                        else process->select();
+                        need_redraw=1;
+                        
+                    }
+                    else{
+                        script->unselect_all();
+                        process->select();
+                        need_redraw=1;
+                    }
+                }
+                else
+                    if (link && link== script->initial_link){
+                        if (event->state & GDK_CONTROL_MASK){
+                            if (link->selected) link->unselect();
+                            else link->select();
+                            need_redraw=1;
+                            
+                        }
+                        else{
+                            script->unselect_all();
+                            link->select();
+                            need_redraw=1;
+                        }
+                    } else
+                    {
+                        if (script->unselect_all()) need_redraw=1;
+                    }
+                
+                break;
+                
+            case ACTION_CREATE_LINK:
+                input_process=script->initial_process;
+                output_process=process;
+                
+                if (input_process->program->output_channels_nb==0) STATUS_PUSH("'%s' does not have ouput channel", input_process->program->name);
+                else if (input_process->program->output_channels_nb==1) output_channel=input_process->program->output_channels[0];
+                
+                if (output_process->program->input_channels_nb==0) STATUS_PUSH("'%s' does not have input channel", output_process->program->name);
+                else if (output_process->program->input_channels_nb==1) input_channel=output_process->program->input_channels[0];
+                
+                
+                if ((input_channel) && (output_channel)) {
+                    script->add_link(input_process, output_channel,  output_process, input_channel, 1);
+                }
+                else Link::dialog(script, input_process, output_process);
+                
+                action=ACTION_NOTHING;
+                
+                /*
+                 new_link = script->add_link_dialog(initial_group, final_group);
+                 if (new_link != NULL)
+                 {
+                 blc_say("link_created between %s and %s created.", initial_group->no_name, final_group->no_name);
+                 set_selected_link(new_link);
+                 }
+                 else blc_say("Link creation canceled");
+                 set_selected_link(new_link);
+                 type_group::unselect_all();*/
+                break;
+                /*
+                 case LETO_ACTION_CREATE_LINK_AND_GROUP:
+                 blc_say("Creating a new group");
+                 new_group = script->add_group_dialog();
+                 if (new_group != NULL)
+                 {
+                 blc_say("A new group '%s' has been created, creating a new link.", new_group->no_name);
+                 if (initial_group != NULL) new_link = script->add_link_dialog(initial_group, new_group);
+                 else if (final_group != NULL) new_link = script->add_link_dialog(new_group, final_group);
+                 if (new_link != NULL)
+                 {
+                 blc_say("A new link has been created.");
+                 }
+                 else blc_say("Link creation canceled.", new_group->no_name);
+                 }
+                 else blc_say("Group creation canceled");
+                 set_selected_link(new_link);
+                 type_group::unselect_all();
+                 new_group->select();
+                 break;*/
+        }
+    }
+    if (need_redraw)  gtk_widget_queue_draw(script->widget);
+    script->initial_link=NULL;
+    script->initial_process=NULL;
+    
+    return TRUE;
+}
+
+
+void Script::delete_selection(){
+    
+    Link **link_pt;
+    
+ //   gvFreeLayout(gvc, main_graph);
+    while(selected_processes) {
+        
+        //We slect the links associated to the process in order to remove them
+        FOR_EACH(link_pt, links, links_nb) if ((link_pt[0]->input_process==selected_processes[0]) || (link_pt[0]->output_process==selected_processes[0])) link_pt[0]->select();
+        delete selected_processes[0];
+    }
+    while(selected_links) delete selected_links[0];
+    generate_layout();
+}
+
+
+
+static int draw_callback (GtkWidget *widget, cairo_t *cr, Script *script){
+    Process_old **process_pt;
+    
+//    gvRenderContext(script->gvc, script->main_graph, "png:cairo", cr); // FIXME: Trick The export of cairo to png stdout has been desactivated in the graphviz plugin (gvrender_pango.c)
+    return FALSE;
+}
+
+void Script::init(){
+    GtkWidget *drawing_area_scrolled_window, scrolled_window2, *label;
+    GtkTextBuffer text_buffer;
+    GtkSourceLanguage *language;
+    GtkSourceLanguageManager *manager;
+    
+    char const * const*ids;
+    int i;
+    
+    processes=NULL;
+    processes_nb=0;
+    links=NULL;
+    links_nb=0;
+    
+    selected_processes=NULL;
+    selected_processes_nb=0;
+    selected_links=NULL;
+    selected_links_nb=0;
+    
+    Script *script=this; //we cannot use &this
+    last_process=NULL;
+    
+    APPEND_ITEM(&array, &nb, &script);
+    
+    widget=gtk_paned_new(GTK_ORIENTATION_VERTICAL);
+    drawing_area_scrolled_window=gtk_scrolled_window_new(NULL, NULL);
+    source_view=gtk_source_view_new();
+
+    grid=gtk_grid_new();
+    drawing_area=gtk_layout_new(NULL, NULL);
+    gtk_widget_set_hexpand(widget, 1);
+    gtk_widget_set_vexpand(widget, 1);
+    gtk_paned_set_position (GTK_PANED(widget), 400);
+    gtk_widget_set_events(drawing_area, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK );
+    gtk_container_add(GTK_CONTAINER(drawing_area_scrolled_window), drawing_area);
+    gtk_container_add(GTK_CONTAINER(drawing_area), grid);
+    gtk_container_add(GTK_CONTAINER(widget), drawing_area_scrolled_window);
+    gtk_container_add(GTK_CONTAINER(widget), source_view);
+
+    gtk_widget_set_size_request(source_view, -1, 100);
+    
+    g_signal_connect(drawing_area, "draw", G_CALLBACK(draw_callback), this);
+    g_signal_connect(drawing_area, "button-press-event", G_CALLBACK(button_press_gtk_cb), this);
+    g_signal_connect(drawing_area, "button-release-event", G_CALLBACK(button_release_gtk_cb), this);
+    g_signal_connect(drawing_area, "motion-notify-event", G_CALLBACK(motion_gtk_cb), this);
+}
+
+Script::Script(){
+    
+    init();
+  //  main_graph = agopen((char*)"main", Agdirected, NULL);
+    
+    //Graph attribute
+  /*  agattr(main_graph, AGRAPH, (char*)"rankdir", (char*)"LR");
+    agattr(main_graph, AGRAPH, (char*)"bgcolor", (char*)"transparent");
+    agattr(main_graph, AGRAPH, (char*)"resolution", (char*)"72");
+    agattr(main_graph, AGRAPH, (char*)"splines", (char*)"true");
+    agattr(main_graph, AGRAPH, (char*)"overlap",  (char*)"false");*/
+    //    agattr(main_graph, AGRAPH, (char*)"compound", (char*)"TRUE");
+    
+    //Node attributes
+ /*   agattr(main_graph, AGNODE, (char*)"shape", (char*)"none");
+    agattr(main_graph, AGNODE, (char*)"style", (char*)"filled");
+    agattr(main_graph, AGNODE, (char*)"fillcolor", (char*)"white");
+    agattr( main_graph, AGNODE, (char*)"fontsize", (char*)"16");
+    
+    
+    agattr( main_graph, AGEDGE, (char*)"penwidth", (char*)"1");
+    agattr( main_graph, AGEDGE, (char*)"color", (char*)"black");
+    agattr( main_graph, AGEDGE, (char*)"fontsize", (char*)"16");
+    
+    input_subgraph = agsubg(main_graph, (char*)"input", true);
+    agattr(input_subgraph, AGRAPH, (char*)"rank", (char*)"same");
+    
+    gvLayout(gvc, main_graph, "dot");*/
+}
+
+Script::Script(char const *path){
+    
+    FILE *file;
+    
+    init();
+    
+    SYSTEM_ERROR_CHECK(file=fopen(path, "r"), NULL, "Error opening '%s'", path);
+ //   main_graph = agread(file, NULL);
+    SYSTEM_ERROR_CHECK(fclose(file), -1, NULL);
+    generate_layout();
+}
+
+
+void Script::add_process(Program *program, char **argv){
+    new Process_old(this, program, argv);
+    generate_layout();
+}
+
+void Script::add_link( Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync){
+ //   gvFreeLayout(gvc, main_graph);
+    new Link(this, input_process, output_channel, output_process, input_channel, sync);
+    generate_layout();
+}
+
+int Script::unselect_all(){
+    int unselected_items_nb=0;
+    while(selected_processes_nb){
+        selected_processes[0]->unselect();
+        unselected_items_nb++;
+        
+    }
+    while(selected_links_nb){
+        selected_links[0]->unselect();
+        unselected_items_nb++;
+    }
+    return unselected_items_nb;
+}
+
+void Script::save_bash(char const *path){
+    char *command;
+    FILE *file;
+    Process_old **process_pt;
+    
+    SYSTEM_ERROR_CHECK(file=fopen(path, "w"), NULL, "Opening '%s'", path);
+    
+    fprintf(file, "source blaar.sh\n\n");
+    
+    FOR_EACH(process_pt, processes, processes_nb){
+        if (process_pt[0]->program->type==INPUT_PROGRAM){
+            command=blc_create_command_line_from_argv(process_pt[0]->argv);
+            fprintf(file, "%s |\n", command);
+            free(command);
+        }
+    }
+    SYSTEM_ERROR_CHECK(fclose(file), -1, "Closing '%s'", path);
+}
+
+Process_old *Script::get_process(double x, double y){
+    Process_old **process_pt;
+    
+    y=height-y;
+    
+    FOR_EACH(process_pt, processes, processes_nb){
+ /*       node_box = ND_bb(process_pt[0]->node);
+        if (x > node_box.LL.x && x < node_box.UR.x && y > node_box.LL.y && y < node_box.UR.y) return process_pt[0];*/
+    }
+    return NULL;
+}
+
+
+Link *Script::get_link(double x, double y){
+  /*
+    Link **link_pt;
+    pointf pos;
+    
+    y=height-y;
+    
+    FOR_EACH(link_pt, links, links_nb){
+        pos=ED_label(link_pt[0]->edge)->pos;
+        if ((fabs(x - pos.x) < 10) && (fabs(y - pos.y) < 10)) return link_pt[0];
+    }*/
+    return NULL;
+}
+
+
+void Script::generate_layout(){
+    char *command_line;
+
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    Process_old **process_pt, *previous_process=NULL, *process;
+    GtkTextBuffer *buffer;
+    
+    buffer=gtk_text_view_get_buffer (GTK_TEXT_VIEW(source_view));
+    gtk_text_buffer_set_text(buffer, "", -1);
+ //   gvLayout(gvc, main_graph, "dot");
+
+    //Tricks to update node position but it is not used
+    surface=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1000, 1000);
+    cr=cairo_create(surface);
+//    gvRenderContext(gvc, main_graph, "svg:cairo", cr);
+    cairo_destroy(cr);
+    FOR_EACH(process_pt, processes, processes_nb) {
+        process=process_pt[0];
+        if (previous_process){
+            if ( previous_process->output_links && previous_process->output_links[0]->output_process==process) gtk_text_buffer_insert_at_cursor(buffer, " ", -1);
+            else gtk_text_buffer_insert_at_cursor(buffer, "\n", -1);
+        }
+
+        command_line=blc_create_command_line_from_argv(process->argv);
+        gtk_text_buffer_insert_at_cursor(buffer, command_line, -1);
+        FREE(command_line);
+        if (process_pt!=processes+processes_nb-1) gtk_text_buffer_insert_at_cursor(buffer, " |", -1);
+        previous_process=process;
+    }
+}
+ 
+
+
+
+
diff --git a/gtk_channels/Script.h b/gtk_channels/Script.h
new file mode 100644
index 0000000000000000000000000000000000000000..07c90e0ed77e6a3e30508b9d368c055375b533d4
--- /dev/null
+++ b/gtk_channels/Script.h
@@ -0,0 +1,64 @@
+//
+//  Script.hpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 25/08/2017.
+//
+//
+
+#ifndef SCRIPT_H
+#define SCRIPT_H
+
+//#include "common.h"
+#include <gtk/gtk.h>
+#include "Program.h"
+#include "Process.h"
+#include "Link.h"
+
+
+class Process_old;
+
+class Script{
+    int width;
+public:
+    int height;
+    GtkWidget *drawing_area, *source_view, *status_image, *grid;
+    Process_old *initial_process;
+    Link *initial_link;
+    Process_old **processes, **selected_processes;
+    int processes_nb, selected_processes_nb;
+    Link **links, **selected_links;
+    int links_nb, selected_links_nb;
+ /*   Agraph_t *main_graph, *input_subgraph;
+    GVC_t *gvc;*/
+    GtkWidget *widget;
+    Process_old *last_process;
+    static Script **array;
+    static int nb;
+    
+    void init();
+
+    Script();
+    Script(char const *filename);
+
+    void add_process(Program *program, char **argv);
+    void add_link( Process_old *input_process, char *output_channel, Process_old *output_process, char *input_channel, int sync);
+    Process_old *get_process(double x, double y);
+    Link *get_link(double x, double y);
+    void save_bash(char const *filename);
+    void update_display();
+    int unselect_all();
+    
+    void delete_selection();
+    void generate_layout();
+    void run();
+    void pause();
+    void stop();
+    
+
+};
+
+//extern Script *current_script;
+
+
+#endif 
diff --git a/gtk_channels/Window.cpp b/gtk_channels/Window.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d3f4b4e449827e26ee47b79d77562bcf4ec01a5d
--- /dev/null
+++ b/gtk_channels/Window.cpp
@@ -0,0 +1,9 @@
+//
+//  Window.c
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 28/08/2017.
+//
+//
+
+#include "Window.h"
diff --git a/gtk_channels/common.h b/gtk_channels/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..bf5e222ef294370a258482dd257e4390d6cca701
--- /dev/null
+++ b/gtk_channels/common.h
@@ -0,0 +1,22 @@
+//
+//  common.h
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 25/08/2017.
+//
+//
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <sys/syslimits.h> //PATH_MAX
+
+#include "Script.h"
+
+extern char blaar_bin_dir[PATH_MAX];
+extern char blaar_scripts_dir[PATH_MAX];
+
+extern Script *current_script;
+
+
+#endif
diff --git a/gtk_channels/gtk_channels.cpp b/gtk_channels/gtk_channels.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..699fa8a7733b4cf6e77a1e9f0166c193a354ad7f
--- /dev/null
+++ b/gtk_channels/gtk_channels.cpp
@@ -0,0 +1,51 @@
+
+#include "blc_program.h"
+#include "libgen.h"
+#include <gtkmm.h>
+#include "Channel.hpp"
+#include "thread"
+
+using namespace std;
+using namespace Gtk;
+
+struct App:Application{
+   // App(int &argc, char **&argv);
+    ApplicationWindow main_window;
+    GtkScrolledWindow channel_window;
+    ChannelWidget channel_widget;
+    
+    //Overrides of default signal handlers:
+    void on_startup() override; //This is called for the first instance of the app
+    void on_activate() override;
+};
+
+/*App::App(int &argc, char **&argv):Application(argc, argv){
+    Glib::set_application_name("e_bash");
+}*/
+
+void App::on_startup(){
+    Application::on_startup();
+    add_action("quit", [](){
+        exit(0);
+    });
+  }
+
+void App::on_activate(){
+    
+    Application::on_activate();
+    add_window(main_window);
+    main_window.add(channel_widget);
+    main_window.show_all();
+}
+
+int main(int argc, char **argv)
+{
+    App app;//(argc, argv);
+    
+    blc_program_set_description("Graphical interface for blaar programs");
+    blc_program_init(&argc, &argv, blc_quit);
+    
+    return app.run(0, 0);
+}
+
+
diff --git a/gtk_channels/gtk_common.h b/gtk_channels/gtk_common.h
new file mode 100644
index 0000000000000000000000000000000000000000..f480b44870902becf62513b74f9dbdd1a1011aca
--- /dev/null
+++ b/gtk_channels/gtk_common.h
@@ -0,0 +1,25 @@
+//
+//  gtk_common.h
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 24/08/2017.
+//
+//
+
+#ifndef GTK_COMMON_H
+#define GTK_COMMON_H
+
+#include <gtkmm.h>
+#include <gtk/gtk.h>
+
+#define STATUS_PUSH(...) status_push( __VA_ARGS__)
+
+
+extern GtkWidget *general_statusbar, *tool_run_pause, *tool_stop;
+extern guint statusbar_context_id;
+extern GtkWidget *image_start, *image_pause, *image_stop;
+
+void status_push(char const *format, ...);
+
+
+#endif /* gtk_common_h */
diff --git a/gtk_channels/parse_help.cpp b/gtk_channels/parse_help.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7793b57a13521f6061bbb8e06f682b226104eb5a
--- /dev/null
+++ b/gtk_channels/parse_help.cpp
@@ -0,0 +1,220 @@
+//
+//  parse_help.cpp
+//  e_bash
+//
+//  Created by Arnaud Blanchard on 23/08/2017.
+//
+//
+
+#include "parse_help.h"
+#include "blc_core.h"
+#include <unistd.h>
+#include <errno.h> //EINTR
+#include <libgen.h> //basename
+#include <stdio.h> //popen
+
+//This are in blc_program
+#define POSITIONAL_ARGUMENTS_TITLE "\npositional arguments:\n"
+#define OPTIONAL_ARGUMENTS_TITLE "\noptional arguments:\n"
+
+void blc_remove_spaces(char const **text)
+{
+    while ((*text)[0]==' ') (*text)++;
+}
+
+static void free_optional_argument(struct blc_optional_argument *argument)
+{
+    if (argument->shortname) FREE(argument->shortname);
+    if (argument->longname) FREE(argument->longname);
+    if (argument->help) FREE(argument->help);
+}
+
+static void blc_get_help(blc_mem *help, char const *directory, char const *program_name)
+{
+    char buffer[LINE_MAX];
+    int error;
+    ssize_t n;
+    FILE *answer;
+    
+    SPRINTF(buffer,"cd %s;  ./%s -h 2>&1", directory, program_name); // The 'cd <directory>;' is necessary for the scripts to find blaar.sh ( popen use 'sh' and dos not use envirnment variables'
+    SYSTEM_ERROR_CHECK(answer = popen(buffer, "r"), NULL, "Executing '%s'", buffer);
+    
+    do{
+        SYSTEM_ERROR_RETRY_ON_SPECIFIC_ERRNO(n=fread( buffer, 1, sizeof(buffer), answer), -1, EINTR, "Reading pipe of '%s'.", buffer);
+        help->append(buffer, n);
+    }while(n!=0);
+    error=pclose(answer);
+    help->append_text("");//Eof line
+    if (error!=0) {
+        PRINT_WARNING("Error '%d' executing '%s' message '%s'", error, buffer, help->chars);
+        help->allocate(0);
+    }
+}
+
+char *blc_parse_description(char const *str, char const **optional_argument_description, char const **positional_argument_description)
+{
+    char const *pt;
+    char *description;
+    char const *end_description;
+    
+    pt=strstr(str, POSITIONAL_ARGUMENTS_TITLE);
+    if (pt){
+        end_description=pt;
+        if (positional_argument_description) *positional_argument_description=pt+strlen(POSITIONAL_ARGUMENTS_TITLE);
+        pt=strstr(pt, OPTIONAL_ARGUMENTS_TITLE);
+    }
+    else {
+        if (positional_argument_description) *positional_argument_description=NULL;
+        pt=strstr(str, OPTIONAL_ARGUMENTS_TITLE);
+        end_description=pt;
+    }
+    if (pt){
+        if (optional_argument_description) *optional_argument_description=pt+strlen(OPTIONAL_ARGUMENTS_TITLE);
+    }
+    else
+    {
+        if (optional_argument_description) *optional_argument_description=NULL;
+    }
+    
+    
+    if (end_description==NULL) description=strdup(str);
+    else description=strndup(str, end_description-str);
+    return description;
+}
+
+/**Parse text in optional_arguments_description to find optional_arguments.
+It returns the number of arguments and update pos at the end of the optional arguments bloc.*/
+
+int blc_parse_optional_arguments(struct blc_optional_argument **results, char const **pos, char const *optional_arguments_description)
+{
+    char const*help;
+    char token[NAME_MAX+1];
+    char text[NAME_MAX+1];
+    int token_size;
+    int arg=1;
+    int results_nb=0;
+    struct blc_optional_argument argument;
+    
+    *results=NULL;
+    
+    while(arg)
+    {
+        help=NULL;
+        blc_remove_spaces(pos);
+        if (sscanf(*pos, "[--%[^] ]%n", token, &token_size)==1){
+            *pos+=token_size;
+            SPRINTF(text, "--%s", token);
+            argument.shortname=NULL;
+            argument.longname=strdup(token);
+            help=strstr(optional_arguments_description, text);
+        }
+        else if (sscanf(*pos, "[-%[^] ]%n", token, &token_size)==1){
+            *pos+=token_size;
+            SPRINTF(text, "-%s", token);
+            argument.shortname=strdup(token);
+            argument.longname=NULL;
+            if (optional_arguments_description) //Test not useful ?
+            {
+                help=strstr(optional_arguments_description, text);
+                if (help) {
+                    help+=strlen(text);
+                    if (sscanf(help, ", --%s%n", token, &token_size)==1) {
+                        help+=token_size;
+                        argument.longname=strdup(token);
+                    }
+                }
+            }
+            
+        }else break;
+        token_size=0;
+        sscanf(*pos, "%*[= ]%n", &token_size); //remove spaces or equal
+        *pos+=token_size;
+        if (sscanf(*pos, "%[^] ]%n", token, &token_size)==1) {
+            *pos+=token_size;
+            argument.value=strdup(token);  //FIXME: Memory may not be free
+        }
+        else  argument.value=NULL;
+        
+        if (help){
+            help+=strlen(text);
+            blc_remove_spaces(&help);
+            if (argument.value) {
+                help+=strlen(argument.value);
+                blc_remove_spaces(&help);
+            }
+            argument.help=strndup(help, strchr(help, '\n') - help);
+        }
+        else argument.help=NULL;
+        
+        if (argument.longname==NULL || strcmp(argument.longname, "help")!=0) APPEND_ITEM(results, &results_nb, &argument);
+        else free_optional_argument(&argument);
+        
+        token_size=0;
+        sscanf(*pos, "%*[] ]%n", &token_size);
+        *pos+=token_size;
+    }
+    return results_nb;
+}
+
+int blc_parse_positional_arguments(struct blc_positional_argument **results, char const **pos, char const *positional_arguments_description)
+{
+    char const *help;
+    char token[NAME_MAX+1];
+    int token_size;
+    int results_nb=0;
+    struct blc_positional_argument argument;
+    
+    *results=NULL;
+    while (sscanf(*pos, "%[^\n ]%n", token, &token_size)==1)
+    {
+        *pos+=token_size;
+        argument.name=strdup(token);
+        argument.value=NULL;
+        help=strstr(positional_arguments_description, token);
+        if (help)
+        {
+            help+=strlen(token);
+            blc_remove_spaces(&help);
+            argument.help=strndup(help, strchr(help, '\n') - help);
+        }
+        else argument.help=NULL;
+        APPEND_ITEM(results, &results_nb, &argument);
+    }
+    
+    while (sscanf(*pos, "[%s]%n", token, &token_size)==1)
+    {
+        *pos+=token_size;
+        argument.name=strdup(token);
+        argument.value=NULL;
+        help=strstr(positional_arguments_description, token) + strlen(token);
+        while(help[0]==' ') help++;
+        argument.help=strndup(help, strchr(help, '\n') - help);
+        APPEND_ITEM(results, &results_nb, &argument);
+    }
+    return results_nb;
+}
+
+char const *blc_parse_help(char const *directory, char const *program_name, blc_optional_argument **optional_arguments, int *optional_arguments_nb, blc_positional_argument **positional_arguments, int *positional_arguments_nb, char **epilog)
+{
+    char const *str;
+    char token[LINE_MAX];
+    blc_mem help;
+    char const *description;
+    char const *description_start;
+    const char *positional_argument_description;
+    const char *optional_argument_description;
+    (void)epilog;
+    
+    blc_get_help(&help, directory, program_name);
+    
+    if (help.chars==NULL) description=NULL;
+    else {
+        SPRINTF(token, "usage: %s", program_name);
+        str=strstr(help.chars, token)+strlen(token);
+        description_start=strchr(str, '\n')+1;
+        description=blc_parse_description(description_start, &optional_argument_description, &positional_argument_description);
+        *optional_arguments_nb=blc_parse_optional_arguments(optional_arguments, &str, optional_argument_description);
+        *positional_arguments_nb=blc_parse_positional_arguments(positional_arguments, &str, positional_argument_description);
+    }
+    return description;
+}
diff --git a/gtk_channels/parse_help.h b/gtk_channels/parse_help.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b34bfcc6c5d873059ded1f8bcf98951c9fdc2fc
--- /dev/null
+++ b/gtk_channels/parse_help.h
@@ -0,0 +1,23 @@
+#ifndef BLC_PARSE_HELP
+#define BLC_PARSE_HELP
+
+
+typedef struct blc_optional_argument
+{
+    char const *shortname;
+    char const *longname;
+    char const *help;
+    char const *value;
+}blc_optional_argument;
+
+typedef struct blc_positional_argument
+{
+    char const *name;
+    char const *help;
+    char const *value;
+}blc_positional_argument;
+
+
+char const *blc_parse_help(char const *directory, char const *program_name, blc_optional_argument **optional_arguments, int *optional_arguments_nb, blc_positional_argument **positional_arguments, int *positional_arguments_nb, char **epilog);
+
+#endif