//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>

#include <regex.h>

#include "parse.h"
#include "var.h"
#include "utils.h"
#include "mode.h"
#include "conf.h"
#include "security.h"

#define REGEX_KEYVAL   "^[[:space:]]*(\\[[[:alpha:]]+\\])([^=]*)$|^[[:space:]]*([^=%]*[^[:space:]]{1})[[:space:]]*=[[:space:]]*(.*[^[:space:]]{1})[[:space:]]*$"
#define REGEX_CMDBYCMD "[[:space:]]*(Exec|ExecAndSet|Make|Get|Set|Send|ExecAndSend|Timer|Macro|Load|Include|SendCKPD|Emulate|Dbus)[[:space:]]*(\\()|[[:space:]]*(Exit)[[:space:]]*;{0,1}"

#define REGEX_SET    "[[:space:]]*(bg|caption|editfield|filemanager|fg|font|fullscreen|icons|hints|list|iconlist|menu|parameter|skin|layout|status|text|title|volume|vibrate|image|popup)[[:space:]]*,[[:space:]]*(.*)|[[:space:]]*(repaint|disconnect)[[:space:]]*|[[:space:]]*(cover)[[:space:]]*,[[:space:]]*(by_name|noname)[[:space:]]*,[[:space:]]*(.*)|[[:space:]]*(cover)[[:space:]]*,[[:space:]]*(clear).*"
#define REGEX_GET    "[[:space:]]*(screen_size|cover_size|icon_size|icon_padding|model|version|cursor|ping|password)[[:space:]]*|[[:space:]]*(is_exists)[[:space:]]*,[[:space:]]*(.*)[[:space:]]*,[[:space:]]*(.*)[[:space:]]*|[[:space:]]*(ping)[[:space:]]*,[[:space:]]*(.*)[[:space:]]*"
#define REGEX_TIMER  "[[:space:]]*([^[:space:]]+)[[:space:]]*,[[:space:]]*([[:digit:]]*|\\$\\(.*\\))[[:space:]]*,[[:space:]]*([[:digit:]]*)[[:space:]]*$|[[:space:]]*([^[:space:]]+)[[:space:]]*,[[:space:]]*(cancel|pause|reset|restart|continue)"
#define REGEX_MAKE   "[[:space:]]*(remote|mode|var|alarm)[[:space:]]*,[[:space:]]*(.*)[[:space:]]*|[[:space:]]*(exit|flush|stop|disconnect|none|\\$\\(.*\\))[[:space:]]*"

#define REGEX_SET_TL "[[:space:]]*(bg|caption|fg|font|select|icon)[[:space:]]*,(.*)|[[:space:]]*(add|replace|file|dir)[[:space:]]*,[[:space:]]*([^[:space:]]{1}[^,]*[^[:space:]]{1})[[:space:]]*,[[:space:]]*(.*)|[[:space:]]*(close)[[:space:]]*,[[:space:]]*(clear)[[:space:]]*|[[:space:]]*(clear|close|show|\\$\\(.*\\))[[:space:]]*"
#define REGEX_SET_MN "[[:space:]]*(add|replace)[[:space:]]*,(.*)|[[:space:]]*(clear)[[:space:]]*"
#define REGEX_SET_WM "[[:space:]]*(icon|window|cover)[[:space:]]*,(.*)|[[:space:]]*(remove_all|clear_cache|show|close|cursor|nocursor|dynamic_cursor|\\$\\(.*\\))[[:space:]]*|[[:space:]]*(remove|set_cursor),[[:space:]]*(.*)[[:space:]]*"
#define REGEX_SET_FM "[[:space:]]*(add|replace|select)[[:space:]]*,[[:space:]]*(left|right)[[:space:]]*,[[:space:]]*(.*)|[[:space:]]*(close|show)[[:space:]]*"
#define REGEX_SET_VR "^[[:space:]]*([^,]*)[[:space:]]*,[[:space:]]*(.*)[[:space:]]*"

#define SEPARATOR     '/'

extern char tmp[MAXMAXLEN];

regex_t *keyVal   = NULL;
regex_t *cmdByCmd = NULL;

CONF conf = 
{ MODEL_DEFAULT, // Model
  -1,            // Front-end port
  0,             // set uid
  0              // set gid
};        

static void setCfgDir(char *d)
{
    //printf("CFG DIR set TO >%s<\n",(d?d:"NULL"));

    if (d != NULL) {    // search ../Utils directory

        char* try2 = (char*) malloc(strlen(d) + 10); // + "/../Utils"
        strcpy(try2,d);
        strcat(try2,"/../Utils");

        struct stat buf;

        if(stat(try2, &buf) == 0) {
            try2[strlen(try2)-6] = '\0';
            setVarSimple(VAR_CFGDIR, try2);
            printf("CFG DIR >%s<\n",(try2?try2:"NULL"));
            free(try2);
            return;
        }
        free(try2);
    }

    // last resort: use {prefix}/anyremote/cfg-data directory
    #ifdef DATADIR
    char* cfgDir = (char*) malloc(strlen(DATADIR)+20);    // + "/anyremote/cfg-data"
    strcpy(cfgDir,DATADIR);
    strcat(cfgDir,"/anyremote/cfg-data");
    #else
    cfgDir = (char*) malloc(2);
    strcpy(cfgDir,".");
    #endif
    
    setVarSimple(VAR_CFGDIR, cfgDir);

    //printf("CFG DIR >%s<\n",(cfgDir?cfgDir:"NULL"));
    free(cfgDir);
    
}

/* Parses command line options and set flags. */
int parse_opts(int argc, char *argv[])
{
    int i;
    for(i=1; i<argc; i++) {
        if (strcmp(argv[i],"-log")==0 || strcmp(argv[i],"-l")==0) {
            setVarSimple("Logging", "debug");
        } else if (strcmp(argv[i],"--autoconnect")==0 || strcmp(argv[i],"-a")==0) {
            setVarSimple(VAR_AUTOCONN, "true");
        } else if ((strcmp(argv[i],"--serial")==0 || strcmp(argv[i],"-s")==0) && i+1<argc) {
            setVarSimple("Device", argv[++i]);
        } else if (strcmp(argv[i],"-name")==0) {
            setVarSimple("ServiceName", argv[++i]);
        } else if (strcmp(argv[i],"-fe")==0 && i+1<argc) {
            conf.frontEnd=atoi(argv[++i]);
        } else if (strcmp(argv[i],"--user")==0 || strcmp(argv[i],"-u")==0) {
            if(getUidGid(argv[++i], &conf.uid, &conf.gid)!=EXIT_OK) {
                printf("WARNING: bad username %s\n", argv[i]);
            }
        } else if (strcmp(argv[i],"-password")==0) {
            setUsePassword(BOOL_YES);
        } else if (strcmp(argv[i],"-cfgdir")==0 && i+1<argc) {
            setVarSimple("CfgDir", argv[++i]);
        } else if (strcmp(argv[i],"-tmpdir")==0 && i+1<argc) {
            setVarSimple("TmpDir", argv[++i]);
        } else if (strcmp(argv[i],"-f")==0) {
            ++i;
            continue;   // already processed this parameter
            // -h and -v handled in main.c
        } else {
            printf("ERROR: Unknown input parameter %s\n", argv[i]);
        }
    }
    return 1;
}

static void regexpPrepare(regex_t *r, const char *p)
{
    int err_no=0;

    if((err_no=regcomp(r, p, REG_EXTENDED))!=0) {
        size_t length;
        char *buffer;
        length = regerror (err_no, r, NULL, 0);
        buffer = (char*) malloc(length);
        regerror (err_no, r, buffer, length);
        printf("regexpPrepare(): regcomp() error %s", buffer);

        free(buffer);
        regfree(r);
        free(r);

        exit(1);    // exit since we can't parse cfg
    }

    return;
}

static void deleteSpaces(char **str, int afterCommas)
{
    char *n = (*str);
    char *r = (*str);
    int  c  = 0;

    //printf("INFO : deleteSpaces in: >%s<\n", *str);

    while (*r != '\0') {
        if (isspace(*r) && c >= afterCommas) {
            r++;
        } else {
            if (*r == ',') {
                c++;
            }
            *n = *r;
            r++;
            n++;
        }
    }
    *n = '\0';

    //printf("INFO : deleteSpaces out: >%s<\n", *str);
}

static void normalizeSequence(char **str)
{
    int isSpace = 0;

    char *n = (*str);
    char *r = (*str);

    //printf("INFO : normalizeSequence in: >%s<\n", *str);

    while (*r != '\0') {
        if (isspace(*r)) {
            // Skip spaces
            if(!isSpace) {
                isSpace = 1;
            }
            r++;
        } else if (*r == '(' && r > *str && strstr(*str,"$$")) {  // This is parametrized command Command($$), we have to delete all spaces from brace
            r--;
            deleteSpaces(&r, 0);
            return;
        } else {
            if(isSpace) {
                *n = ' ';
                n++;
            }
            // Copy all symbols
            *n = *r;
            r++;
            n++;
            isSpace = 0;
        }
    }
    *n = '\0';

    //printf("INFO : normalizeSequence out: >%s<\n", *str);
}

static regmatch_t* allocRegmatch(int no_sub)
{
    regmatch_t* result;
    if ((result = (regmatch_t *) malloc(sizeof(regmatch_t) * no_sub))==0) {
        printf("allocRegmatch(): No more memory");
        exit(1);
    }
    return result;
}

static int parseKeyValue(char *in, char ** tag, char ** value)
{
    //printf("INFO: parseKeyValue() %s (%d)\n", in, (int)keyVal->re_nsub+1);

    size_t no_sub = keyVal->re_nsub+1;
    regmatch_t* result = allocRegmatch(no_sub);

    if (strlen(in) > 0 && in[strlen(in) - 1] == '\n') {
        in[strlen(in) - 1] = '\0';
    }

    *value =  NULL;
    *tag   =  NULL;
    if (regexec(keyVal, in, no_sub, result, 0)==0) {
        int use1 = 1;
        int use2 = 2;
        if (result[1].rm_so == -1) {
            if (result[3].rm_so == -1) {
                printf("parseKeyValue(): Incorrectly formed command (1) %s\n", in);
                use1 = -1;
            }
            use1 = 3;
        }
        if (result[2].rm_so == -1) {
            if (result[4].rm_so == -1) {
                printf("parseKeyValue(): Incorrectly formed command (2) %s\n", in);
                use2 = -1;
            }
            use2 = 4;
        }

        if (use1 > 0) {
            *tag   = in + result[use1].rm_so;
            *(in + result[use1].rm_eo) = '\0';
        }

        if (use2 > 0) {
            if (result[use2].rm_so == -1 || result[use2].rm_eo == -1) {
                *value =  NULL;
            } else {
                *value = in + result[use2].rm_so;
                *(in + result[use2].rm_eo) = '\0';
            }
        }

        /*if(*tag != NULL) {
             printf("INFO : Got tag   : >%s<\n", *tag);
        } else {
            printf("INFO : Got tag : >NULL<\n");
        }
        if(*value != NULL) {
             printf("INFO : Got value : >%s<\n", *value);
        }else {
             printf("INFO : Got value : >NULL<\n");
        //} else {
        //    printf("INFO : Not matched\n");
        }*/

    }

    free(result);
    return EXIT_OK;
}

static struct { 
    const char* name; 
    int         id;
} 
ids[] = {
    { CMD_MACRO,    ID_MACRO    },   
    { CMD_MAKE,     ID_MAKE     },   
    { CMD_EXEC,     ID_EXEC     },   
    { CMD_SET,      ID_SET      },   
    { CMD_TIMER,    ID_TIMER    },   
    { CMD_EXECSET,  ID_EXECSET  },
    { CMD_GET,      ID_GET      },   
    { CMD_EXIT,     ID_EXIT     },   
    { CMD_EMU,      ID_EMU      },   
    { CMD_DBUS,     ID_DBUS     },   
    { CMD_INCLUDE,  ID_INCLUDE  },   
    { CMD_LOAD,     ID_LOAD     },   
    { CMD_SENDCKPD, ID_SENDCKPD },   
    { CMD_EXECSEND, ID_EXECSEND },
    { CMD_SEND,     ID_SEND     },   
    { NULL,         ID_UNKNOWN  }  
}; 

const char* id2Cmd (int cmdId)
{
    size_t idx;
    for (idx = 0; ids[idx].name; ++idx) {
        if (ids[idx].id == cmdId) {
            break;
        }
    }
    return (ids[idx].name ? ids[idx].name : "Unknown");
}

static int cmd2id(char *name)
{
    if (name) {
        size_t idx;
        for (idx = 0; ids[idx].name; ++idx) {
            if (strlen(name) == strlen(ids[idx].name) && 
                strcmp(name, ids[idx].name) == 0) {
                return ids[idx].id;
            }
        }
    }
    return ID_UNKNOWN;
}

int cmdSet2id(const char *name)
{
    if (name == NULL) {
        return ID_SET_MAX;
    }
    
    static struct { 
        const char* name; 
        int         id;
    } 
    sids[] = {
        { SET_BG,      ID_SET_BG     },   
        { SET_CAPTION, ID_SET_CAPTION},   
        { SET_EFIELD,  ID_SET_EFIELD },
        { SET_FG,      ID_SET_FG     },  
        { SET_FMGR,    ID_SET_FMGR   },  
        { SET_FONT,    ID_SET_FONT   },  
        { SET_FSCREEN, ID_SET_FSCREEN},  
        { SET_ICONS,   ID_SET_ICONS  },  
        { SET_HINTS,   ID_SET_HINTS  },  
        { SET_LIST,    ID_SET_LIST   },  
        { SET_ILIST,   ID_SET_ILIST  }, 
        { SET_MENU,    ID_SET_MENU   },   
        { SET_PARAM,   ID_SET_PARAM  },
        { SET_REPAINT, ID_SET_REPAINT},   
        { SET_LAYOUT,  ID_SET_LAYOUT },
        { SET_SKIN,    ID_SET_LAYOUT },  // obsolete   
        { SET_STATUS,  ID_SET_STATUS },   
        { SET_TEXT,    ID_SET_TEXT   },   
        { SET_TITLE,   ID_SET_TITLE  },   
        { SET_VIBRATE, ID_SET_VIBRATE},   
        { SET_VOLUME,  ID_SET_VOLUME },   
        { SET_IMAGE,   ID_SET_IMAGE  },   
        { SET_COVER,   ID_SET_COVER  },   
        { SET_POPUP,   ID_SET_POPUP  },   
        { SET_DISCONN, ID_SET_DISCONN},   
        { NULL,        ID_SET_MAX    }
    };
    
    size_t idx;
    for (idx = 0; sids[idx].name; ++idx) {
        if (strncmp(name, sids[idx].name, strlen(sids[idx].name)) == 0) {
            return sids[idx].id;
        }
    }

    return ID_SET_MAX;
}

int cmdGet2id(const char *name)
{
    if (name == NULL) {
        return ID_GET_MAX;
    }
    
    static struct 
        { 
        const char* name; 
        int         id;
    } 
    gids[] = {
        { GET_SCREENSIZE, ID_GET_SCREENSIZE },
        { GET_COVERSIZE,  ID_GET_COVERSIZE  },  
        { GET_ICONSIZE,   ID_GET_ICONSIZE   },  
        { GET_ICONPADDING,ID_GET_ICONPADDING},
        { GET_MODEL,      ID_GET_MODEL      },      
        { GET_VERSION,    ID_GET_VERSION    },    
        { GET_CURSOR,     ID_GET_CURSOR     },     
        { GET_PING,       ID_GET_PING       },       
        { GET_PASSWORD,   ID_GET_PASSWORD   },   
        { GET_ISEXISTS,   ID_GET_ISEXISTS   },
        { NULL,           ID_GET_MAX        }
    };
    
    size_t idx;
    for (idx = 0; gids[idx].name; ++idx) {
        if (strncmp(name, gids[idx].name, strlen(gids[idx].name)) == 0) {
        return gids[idx].id;
        }
    }

    return ID_GET_MAX;
}

static int cmdTimer2id(const char *name)
{
    if (name == NULL) {
        return ID_TIMER_MAX;
    }
    
    static struct 
    { 
        const char* name; 
        int         id;
    } 
    tids[] = {
        { TIMER_CANCEL,   ID_TIMER_CANCEL  },
        { TIMER_PAUSE,    ID_TIMER_PAUSE   },
        { TIMER_RESET,    ID_TIMER_RESET   },
        { TIMER_RESTART,  ID_TIMER_RESTART },
        { TIMER_CONTINUE, ID_TIMER_CONTINUE},
        { NULL,           ID_TIMER_MAX     }
    };
    
    size_t idx;
    for (idx = 0; tids[idx].name; ++idx) {
        if (strncmp(name, tids[idx].name, strlen(tids[idx].name)) == 0) {
            return tids[idx].id;
        }
    }

    // else
    return ID_TIMER_CREATE;
}

static int cmdMake2id(const char *name, int sz)
{
    if (name == NULL) {
        return ID_MAKE_MAX;
    }
    
    static struct 
    { 
        const char* name; 
        int         id;
    } 
    mids[] = {
	{ MAKE_DISCONN, ID_MAKE_DISCONN},
	{ MAKE_MODE,    ID_MAKE_MODE   },  
	{ MAKE_ALARM,   ID_MAKE_ALARM  },  
	{ MAKE_FLUSH,   ID_MAKE_FLUSH  }, 
	{ MAKE_STOP,    ID_MAKE_STOP   },  
	{ MAKE_REMOTE,  ID_MAKE_REMOTE }, 
	{ MAKE_VAR,     ID_MAKE_VAR    },  
	{ MAKE_EXIT,    ID_MAKE_EXIT   },   
	{ MAKE_NONE,    ID_MAKE_NONE   },
	{ NULL,         ID_MAKE_MAX    }
    };
    
    size_t idx;
    for (idx = 0; mids[idx].name; ++idx) {
        if (strlen(mids[idx].name) == sz && 
	    strncmp(name, mids[idx].name, sz) == 0) {
            return mids[idx].id;
        }
    }

    return ID_MAKE_MAX;
}

static int storeGetCmd(cmdItem* ci, char *cmd)
{
    int parseFail = 0;

    regex_t* regex  = (regex_t *) malloc(sizeof(regex_t));
    memset(regex, 0, sizeof(regex_t));

    regexpPrepare(regex, REGEX_GET);

    size_t no_sub = regex->re_nsub+1;

    regmatch_t* result = allocRegmatch(no_sub);

    if (regexec(regex, cmd, no_sub, result, 0) == 0) {    // screen_size, cover_size, icon_size, icon_padding, model, keepalive
        
    //printf("storeGetCmd(): %s %d %d %d\n", cmd, result[1].rm_so, result[2].rm_so, result[5].rm_so);
    
    if (result[1].rm_so >= 0) {
        
        int l = result[1].rm_eo - result[1].rm_so;
    
        char* subCmd = (char*) malloc(l+1);
        strncpy(subCmd,cmd+result[1].rm_so,l);
        subCmd[l] = '\0';
        
        ci->subtype = cmdGet2id(subCmd);
        free(subCmd);
        
        ci->descr = (char*) calloc(1, l + 1);
            strncpy(ci->descr, cmd + result[1].rm_so, l);
        
    } else if (result[2].rm_so >= 0) {        // is_exists

            int l2 = result[2].rm_eo - result[2].rm_so;
            int l3 = result[3].rm_eo - result[3].rm_so;
            int l4 = result[4].rm_eo - result[4].rm_so;
    
        char* subCmd = (char*) malloc(l2+1);
        strncpy(subCmd,cmd+result[2].rm_so,l2);
        subCmd[l2] = '\0';
        
        ci->subtype = cmdGet2id(subCmd);
        free(subCmd);

            ci->descr = (char*) calloc(1, l2 + l3 + l4 + 3);
            strncpy(ci->descr,cmd + result[2].rm_so, l2);
            strcat (ci->descr,",");
            strncat(ci->descr,cmd + result[3].rm_so, l3);

            strcat (ci->descr,",");
            strncat(ci->descr,cmd + result[4].rm_so, l4);
      
        } else if (result[5].rm_so >= 0) {        // ping,timeout
        
            int l2 = result[5].rm_eo - result[5].rm_so;
            int l3 = result[6].rm_eo - result[6].rm_so;
        
        char* subCmd = (char*) malloc(l2+1);
        strncpy(subCmd,cmd+result[5].rm_so,l2);
        subCmd[l2] = '\0';
        
        ci->subtype = cmdGet2id(subCmd);
        free(subCmd);
            
        ci->descr = (char*) calloc(1, l2 + l3 + 2);
            strncpy(ci->descr,cmd + result[5].rm_so, l2);
            strcat (ci->descr,",");
            strncat(ci->descr,cmd + result[6].rm_so, l3);
     }
    } else {
        printf("storeGetCmd(): parse error\n");
        parseFail = 1;
    }

    if (parseFail) {
        printf("storeGetCmd(): command Get( %s ) is formed incorrectly\n", cmd);
    }

    free(result);
    result = NULL;
    regfree(regex);
    free(regex);
    regex = NULL;

    return (parseFail == 0 ? EXIT_OK : EXIT_NOK);
}

static int storeMakeCmd(cmdItem* ci, char *cmd)
{
    int parseFail = 0;
    //printf ("storeMakeCmd: got >%s<\n",cmd);

    regex_t* regex  = (regex_t *) malloc(sizeof(regex_t));
    memset(regex, 0, sizeof(regex_t));

    regexpPrepare(regex, REGEX_MAKE);

    size_t no_sub = regex->re_nsub+1;

    regmatch_t* result = allocRegmatch(no_sub);

    if (regexec(regex, cmd, no_sub, result, 0) == 0) {

        if (result[1].rm_so >= 0 && result[2].rm_so > 0) {

            int l = result[1].rm_eo - result[1].rm_so;
 
            int stype = cmdMake2id(cmd+result[1].rm_so,l);
            ci->subtype = stype;
        
            if (stype == ID_MAKE_VAR) {            // var

                regex_t* regex2  = (regex_t *) malloc(sizeof(regex_t));
                memset(regex2, 0, sizeof(regex_t));

                regexpPrepare(regex2, REGEX_SET_VR);
                size_t no_sub2 = regex2->re_nsub+1;

                regmatch_t* result2 = allocRegmatch(no_sub2);
                char* start2 = cmd + result[2].rm_so;

                int l2 = 0;

                if (regexec(regex2, cmd + result[2].rm_so, no_sub2, result2, 0) == 0) {

                    if (result2[1].rm_so >= 0 && result2[2].rm_so >= 0) {

                        l2 = result2[1].rm_eo - result2[1].rm_so;
                        int le = result2[2].rm_eo - result2[2].rm_so;

                        ci->descr = (char*) calloc(1, l + l2 + 2);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);

                        strcat (ci->descr,",");

                        strncat(ci->descr,start2 + result2[1].rm_so, l2);

                        ci->exec = (char*) calloc(1, le + 1);
                        strncpy(ci->exec,start2 + result2[2].rm_so, le);
                    } else {
                        printf("storeMakeCmd(): parse error (M V1)\n");
                        parseFail = 1;
                    }
                } else {
                    printf("storeMakeCmd(): parse error (M V2)\n");
                    parseFail = 1;
                }
                regfree(regex2);
                free(regex2);
                regex2 = NULL;
                free(result2);
                result2 = NULL;

            } else if (stype == ID_MAKE_MODE  ||    // mode
                       stype == ID_MAKE_ALARM) {    // alarm
               
                ci->descr = (char*) calloc(1, result[1].rm_eo - result[1].rm_so + 1);
                ci->exec  = (char*) calloc(1, result[2].rm_eo - result[2].rm_so + 1);
                strncpy(ci->descr,cmd + result[1].rm_so, result[1].rm_eo - result[1].rm_so);
                strncpy(ci->exec, cmd + result[2].rm_so, result[2].rm_eo - result[2].rm_so);

                //printf ("storeMakeCmd: (2) %s %s \n",ci->descr,ci->exec);

            } else if (stype == ID_MAKE_REMOTE) {       // remote

                if (strncmp(cmd + result[2].rm_so, "on",  2) == 0 ||
                    strncmp(cmd + result[2].rm_so, "off", 3) == 0 ||
                   *(cmd + result[2].rm_so) == '$') {        // Make(remote,$(variable))
                    ci->descr = (char*) calloc(1, result[1].rm_eo - result[1].rm_so + 1);
                    ci->exec  = (char*) calloc(1, result[2].rm_eo - result[2].rm_so + 1);
                    strncpy(ci->descr,cmd + result[1].rm_so, result[1].rm_eo - result[1].rm_so);
                    strncpy(ci->exec, cmd + result[2].rm_so, result[2].rm_eo - result[2].rm_so);

                    //printf ("storeMakeCmd: (3) %s %s \n",ci->descr,ci->exec);

                } else {
                    printf("storeMakeCmd(): parse error: invalid parameter in Make(remote,...)\n");
                    parseFail = 1;
                }
            } else {
                parseFail = 1;
                printf("storeMakeCmd(): parse error: unknown subcommand (1)\n");
            }
        } else if (result[3].rm_so >= 0) {                        // disconnect, flush

            int l = result[3].rm_eo - result[3].rm_so;

            int stype = cmdMake2id(cmd+result[3].rm_so,l);
            ci->subtype = stype;

            ci->descr = (char*) calloc(1, l + 1);
            strncpy(ci->descr,cmd + result[3].rm_so, l);

        } else {
            parseFail = 1;
            printf("storeMakeCmd(): parse error: unknown subcommand (2)\n");
        }
    } else {
        printf("storeMakeCmd(): parse error\n");
        parseFail = 1;
    }

    if (parseFail) {
        printf("storeMakeCmd(): command Make( %s ) is formed incorrectly\n", cmd);
    }

    free(result);
    result = NULL;
    regfree(regex);
    free(regex);
    regex = NULL;

    return (parseFail == 0 ? EXIT_OK : EXIT_NOK);
}

static int storeMacroCmd(cmdItem* ci, char *cmd)
{
    //printf ("storeMacroCmd: got >%s<\n",cmd);

    char *comma = index(cmd,',');
    if (comma) {
        *comma = '\0';
        comma++;
        while (comma && (*comma == ' ' || *comma == '\t')) {
            comma++;
        }
    }
    
    // Why ?
    //deleteSpaces(&cmd,0);

    char *dsc = (char*) calloc(1, strlen(cmd) + 1);
    strcpy(dsc, cmd);
    ci->descr = dsc;

    if (comma && *comma != '\0') {
        char *ex = (char*) calloc(1, strlen(comma) + 1);
        strcpy(ex, comma);
        ci->exec = ex;
    }
    return EXIT_OK;
}

static int storeExecCmd(cmdItem* ci, char *cmd)
{
    ci->exec = (char*) calloc(1, strlen(cmd) + 1);
    strcpy(ci->exec, cmd);
    
    return EXIT_OK;
}

static int storeExecAndSendCmd(cmdItem* ci, char *cmd)
{
    char* comma = index(cmd,',');
    if (comma == NULL) {
        return EXIT_NOK;
    } 

    char *dsc = (char*) calloc(1, comma - cmd + 1);
    char *exc = (char*) calloc(1, strlen(comma));
    strncpy(dsc, cmd, comma - cmd);
    strcpy(exc, comma + 1);
    
    ci->descr = dsc;
    ci->exec  = exc;
    
    return EXIT_OK;
}

static int storeExecAndSetCmdEx(cmdItem* ci, char *cmd, int execFlag)
{
    int parseFail = 0;

    regex_t* regex  = (regex_t *) malloc(sizeof(regex_t));
    memset(regex, 0, sizeof(regex_t));

    regexpPrepare(regex, REGEX_SET);

    size_t no_sub = regex->re_nsub+1;

    //printf("storeExecAndSetCmdEx() %d %s (%d)\n", ci->type, cmd, (int)no_sub);

    regmatch_t* result = allocRegmatch(no_sub);

    if (regexec(regex, cmd, no_sub, result, 0) == 0) {    // Match it

        //printf("storeExecAndSetCmdEx() matched %d %d\n", result[1].rm_so, result[1].rm_eo);

        if (result[1].rm_so >= 0 && result[2].rm_so > 0) {  // subcommand , params matched

            int l = result[1].rm_eo - result[1].rm_so;
        
            char* subCmd = (char*) malloc(l+1);
            strncpy(subCmd,cmd+result[1].rm_so,l);
            subCmd[l] = '\0';

            int stype = cmdSet2id(subCmd);
            //printf("storeExecAndSetCmdEx() subtype %s -> %d \n", subCmd, stype);

            ci->subtype = stype;
            free(subCmd);

            if (stype == ID_SET_BG      ||
                stype == ID_SET_CAPTION ||
                stype == ID_SET_PARAM   ||
                stype == ID_SET_EFIELD  ||
                stype == ID_SET_FG      ||
                stype == ID_SET_FONT    ||
                stype == ID_SET_FSCREEN ||
                stype == ID_SET_ICONS   ||
                stype == ID_SET_HINTS   ||
                stype == ID_SET_LAYOUT  ||
                stype == ID_SET_STATUS  ||
                stype == ID_SET_TITLE   ||
                stype == ID_SET_VOLUME  ||
                stype == ID_SET_VIBRATE ||
                //stype == ID_SET_COVER   ||
                stype == ID_SET_POPUP) {
        
                int le = result[2].rm_eo - result[2].rm_so;
                int le2 = 0;

                if (!execFlag) {
                    le2 = le + 1;
                }
                ci->descr = (char*) calloc(1, l + le2 + 1);
                strncpy(ci->descr,cmd + result[1].rm_so, l);

                if (execFlag) {
                    ci->exec = (char*) calloc(1, le + 1);
                    strncpy(ci->exec,cmd + result[2].rm_so, le);
                } else {
                    strcat (ci->descr,",");
                    strncat(ci->descr,cmd + result[2].rm_so, le);
                }
        
            } else if (stype == ID_SET_FMGR) {

                regex_t* regex2  = (regex_t *) malloc(sizeof(regex_t));
                memset(regex2, 0, sizeof(regex_t));
                regexpPrepare(regex2, REGEX_SET_FM);
                size_t no_sub2 = regex2->re_nsub+1;

                regmatch_t* result2 = allocRegmatch(no_sub2);
                char* start2 = cmd + result[2].rm_so;

                //printf("parse %s\n", cmd + result[2].rm_so);

                if (regexec(regex2, cmd + result[2].rm_so, no_sub2, result2, 0) == 0) {


                    int l2 = 0;

                    if (result2[1].rm_so >= 0 && result2[2].rm_so >= 0) {          // add|replace|select,left|right,_data_

                        l2 = result2[1].rm_eo - result2[1].rm_so;
                        int l3 = result2[2].rm_eo - result2[2].rm_so;

                        int le = result2[3].rm_eo - result2[3].rm_so;
                        int le2 = 0;

                        if (!execFlag) {
                            le2 = le + 1;
                        }

                        ci->descr = (char*) calloc(1, l + l2 + l3 + le2 + 3);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);

                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[1].rm_so, l2);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[2].rm_so, l3);

                        if (execFlag) {
                            ci->exec = (char*) calloc(1, le + 1);
                            strncpy(ci->exec,start2 + result2[3].rm_so, le);
                        } else {
                            strcat (ci->descr,",");
                            strncat(ci->descr,start2 + result2[3].rm_so, le);
                        }

                    } else if (result2[4].rm_so >= 0) {                // close|show

                        l2 = result2[4].rm_eo - result2[4].rm_so;
                        ci->descr = (char*) calloc(1, l + l2 + 2);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[4].rm_so, l2);

                    } else {
                        printf("storeExecAndSetCmdEx(): parse error (F1)\n");
                        parseFail = 1;
                    }
                } else {
                    printf("storeExecAndSetCmdEx(): parse error (F2)\n");
                    parseFail = 1;
                }
                regfree(regex2);
                free(regex2);
                regex2 = NULL;
                free(result2);
                result2 = NULL;

            } else if (stype == ID_SET_LIST  ||
                       stype == ID_SET_ILIST ||
                       stype == ID_SET_TEXT) {

                //printf("storeExecAndSetCmdEx() matched2 %d %d\n", result[2].rm_so, result[2].rm_eo);

                regex_t* regex2  = (regex_t *) malloc(sizeof(regex_t));
                memset(regex2, 0, sizeof(regex_t));
                regexpPrepare(regex2, REGEX_SET_TL);
                size_t no_sub2 = regex2->re_nsub+1;

                regmatch_t* result2 = allocRegmatch(no_sub2);
                char* start2 = cmd + result[2].rm_so;

                //printf("parse %s\n", cmd + result[2].rm_so);

                if (regexec(regex2, cmd + result[2].rm_so, no_sub2, result2, 0) == 0) {

                    int l2 = 0;

                    if (result2[1].rm_so >= 0) {          // bg|caption|fg|font|select

                        l2 = result2[1].rm_eo - result2[1].rm_so;
                        int le = result2[2].rm_eo - result2[2].rm_so;
                        int le2 = 0;

                        if (!execFlag) {
                            le2 = le + 1;
                        }

                        ci->descr = (char*) calloc(1, l + l2 + le2 + 2);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);

                        strcat (ci->descr,",");

                        strncat(ci->descr,start2 + result2[1].rm_so, l2);

                        if (execFlag) {
                            ci->exec = (char*) calloc(1, le + 1);
                            strncpy(ci->exec,start2 + result2[2].rm_so, le);
                        } else {
                            strcat (ci->descr,",");
                            strncat(ci->descr,start2 + result2[2].rm_so, le);
                        }

                    } else if (result2[3].rm_so >= 0) {    // add|replace|file|dir,_title_

                        l2 = result2[3].rm_eo - result2[3].rm_so;
                        int l3 = result2[4].rm_eo - result2[4].rm_so;
                        int le = result2[5].rm_eo - result2[5].rm_so;
                        int le2 = 0;

                        if (!execFlag) {
                            le2 = le + 1;
                        }

                        ci->descr = (char*) calloc(1, l + l2 + l3 + le2 + 3);
                        strncpy(ci->descr,cmd + result[1].rm_so,  l);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[3].rm_so, l2);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[4].rm_so, l3);

                        if (execFlag) {
                            ci->exec = (char*) calloc(1, le + 1);
                            strncpy(ci->exec,start2 + result2[5].rm_so, le);
                        } else {
                            strcat (ci->descr,",");
                            strncat(ci->descr,start2 + result2[5].rm_so, le);
                        }

                    } else if (result2[6].rm_so >= 0 && result2[7].rm_so >= 0) {    // close,clear

                        l2 = result2[6].rm_eo - result2[6].rm_so;
                        int l3 = result2[7].rm_eo - result2[7].rm_so;
                        ci->descr = (char*) calloc(1, l + l2 + l3 + 3);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[6].rm_so, l2);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[7].rm_so, l3);

                    } else if (result2[8].rm_so >= 0) {    // close|clear|show

                        l2 = result2[8].rm_eo - result2[8].rm_so;
                        ci->descr = (char*) calloc(1, l + l2 + 2);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[8].rm_so, l2);

                    } else {
                        printf("storeExecAndSetCmdEx(): parse error (T1)\n");
                        parseFail = 1;
                    }
                } else {
                    printf("storeExecAndSetCmdEx(): parse error (T2)\n");
                    parseFail = 1;
                }
                regfree(regex2);
                free(regex2);
                regex2 = NULL;
                free(result2);
                result2 = NULL;

            } else if (stype == ID_SET_MENU ||
                       stype == ID_SET_IMAGE) {
                //printf("storeExecAndSetCmdEx() matched menu/image\n");
		
                regex_t* regex2  = (regex_t *) malloc(sizeof(regex_t));
                memset(regex2, 0, sizeof(regex_t));

                if (stype == ID_SET_MENU) {
                    regexpPrepare(regex2, REGEX_SET_MN);
                } else {
                    regexpPrepare(regex2, REGEX_SET_WM);
                }
                size_t no_sub2 = regex2->re_nsub+1;

                regmatch_t* result2 = allocRegmatch(no_sub2);
                char* start2 = cmd + result[2].rm_so;

                int l2 = 0;

                if (regexec(regex2, cmd + result[2].rm_so, no_sub2, result2, 0) == 0) {

                    if (result2[1].rm_so >= 0) {          // add|replace or icon|window

                        l2 = result2[1].rm_eo - result2[1].rm_so;
                        int le = result2[2].rm_eo - result2[2].rm_so;
                        int le2 = 0;

                        if (!execFlag) {
                            le2 = le + 1;
                        }

                        ci->descr = (char*) calloc(1, l + l2 + le2 + 2);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);

                        strcat (ci->descr,",");

                        strncat(ci->descr,start2 + result2[1].rm_so, l2);

                        if (execFlag) {
                            ci->exec = (char*) calloc(1, le + 1);
                            strncpy(ci->exec,start2 + result2[2].rm_so, le);
                        } else {
                            strcat (ci->descr,",");
                            strncat(ci->descr,start2 + result2[2].rm_so, le);
                        }

                    } else if (result2[3].rm_so >= 0) {    // clear or show|remove_all|clear_cache|close|cursor|nocursor|dynamic_cursor

                        l2 = result2[3].rm_eo - result2[3].rm_so;
                        ci->descr = (char*) calloc(1, l + l2 + 2);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[3].rm_so, l2);

                    } else if (result2[4].rm_so >= 0) {    // set_cursor

                        int le = result2[5].rm_eo - result2[5].rm_so;

                        l2 = result2[4].rm_eo - result2[4].rm_so;

                        int le2 = 0;
                        if (!execFlag) {
                            le2 = le + 1;
                        }

                        ci->descr = (char*) calloc(1, l + l2 + le2 + 2);
                        strncpy(ci->descr,cmd + result[1].rm_so, l);
                        strcat (ci->descr,",");
                        strncat(ci->descr,start2 + result2[4].rm_so, l2);

                        if (execFlag) {
                            ci->exec = (char*) calloc(1, le + 1);
                            strncpy(ci->exec,start2 + result2[5].rm_so, le);
                        } else {
                            strcat (ci->descr,",");
                            strncat(ci->descr,start2 + result2[5].rm_so, le);
                        }
                    } else {
                        printf("storeExecAndSetCmdEx(): parse error (M1)\n");
                        parseFail = 1;
                    }
                }
		//printf("storeExecAndSetCmdEx(): parsed\n");
		
                regfree(regex2);
                free(regex2);
                regex2 = NULL;
                free(result2);
                result2 = NULL;

            } else {
                printf("storeExecAndSetCmdEx(): parse error (M3)\n");
                parseFail = 1;
            }
        } else if (result[3].rm_so >= 0) {   // disconnect,repaint matched


            int l = result[3].rm_eo - result[3].rm_so;
        
            char* subCmd = (char*) malloc(l+1);
            strncpy(subCmd,cmd+result[3].rm_so,l);
            subCmd[l] = '\0';

            int stype = cmdSet2id(subCmd);
            ci->subtype = stype;
            free(subCmd);

            ci->descr = (char*) calloc(1, l + 1);
            strncpy(ci->descr, cmd + result[3].rm_so, l);

        } else if (result[4].rm_so >= 0) {   // cover,by_name|noname,<file>
            
	    
            int l4 = result[4].rm_eo - result[4].rm_so;
            int l5 = result[5].rm_eo - result[5].rm_so;
            int l6 = result[6].rm_eo - result[6].rm_so;
            //printf("storeExecAndSetCmdEx(): cover %d %d %d\n",l4,l5,l6);

            ci->subtype = ID_SET_COVER;
            
            if (execFlag) {
                ci->descr = (char*) calloc(1,l4+l5+2);
                strncpy(ci->descr,cmd+result[4].rm_so,l4);
                strcat(ci->descr,",");
                strncat(ci->descr,cmd+result[5].rm_so,l5);
                 
                ci->exec = (char*) calloc(1,l6+1);
                strncpy(ci->exec,cmd+result[6].rm_so,l6);
                 
            } else {
                ci->descr = (char*) calloc(1,l4+l5+l6+3);
                strncpy(ci->descr,cmd+result[4].rm_so,l4);
                strcat(ci->descr,",");
                strncat(ci->descr,cmd+result[5].rm_so,l5);
                strcat(ci->descr,",");
                strncat(ci->descr,cmd+result[6].rm_so,l6);
             }
        } else if (result[7].rm_so >= 0) {   // cover,clear
            
            int l7 = result[7].rm_eo - result[7].rm_so;
            int l8 = result[8].rm_eo - result[8].rm_so;
            ci->subtype = ID_SET_COVER;
            
            //printf("storeExecAndSetCmdEx(): coverCLEAR %d %d \n",l7,l8);
	     
            ci->descr = (char*) calloc(1,l7+l8+2);
            strncpy(ci->descr,cmd+result[7].rm_so,l7);
            strcat(ci->descr,",");
            strncat(ci->descr,cmd+result[8].rm_so,l8);

        } else {
            printf("storeExecAndSetCmdEx(): parse error (1)\n");
            parseFail = 1;
        }
        //printf("storeExecAndSetCmdEx(): parsed.\n");
    } else {
        // ExecAndSet(_shell_commands_) goes here ????
        printf("storeExecAndSetCmdEx(): parse error (2)\n");
        parseFail = 1;
    }

    if (parseFail) {
        printf("storeExecAndSetCmdEx(): command %s( %s ) is formed incorrectly\n", (execFlag ? "ExecAndSet" : "Set"), cmd);
    }
    
    //printf("storeExecAndSetCmdEx(): %s -> %s\n", ci->descr, ci->exec);

    free(result);
    result = NULL;
    regfree(regex);
    free(regex);
    regex = NULL;
    //printf("storeExecAndSetCmdEx(): DONE\n");

    return (parseFail == 1 ? EXIT_NOK : EXIT_OK);
}

static int storeSetCmd(cmdItem* ci, char *cmd)
{
    return storeExecAndSetCmdEx(ci, cmd, 0);
}

static int storeExecAndSetCmd(cmdItem* ci, char *cmd)
{
    return storeExecAndSetCmdEx(ci, cmd, 1);
}

static int storeTimerCmd(cmdItem* ci, char *cmd)
{
    //printf("storeTimerCmd(): %s\n",cmd);
    regex_t* regex  = (regex_t *) malloc(sizeof(regex_t));
    memset(regex, 0, sizeof(regex_t));
    regexpPrepare(regex, REGEX_TIMER);

    size_t no_sub = regex->re_nsub+1;

    regmatch_t* result = allocRegmatch(no_sub);

    int cmdIsOk = EXIT_OK;
    if (regexec(regex, cmd, no_sub, result, 0) == 0) {    // Match it

        if (result[1].rm_so >= 0 &&    // Timer(key,1,5)
            result[2].rm_so >  0 &&
            result[3].rm_so >  0) {

            int l1 = result[1].rm_eo - result[1].rm_so;
            int l2 = result[3].rm_eo - result[2].rm_so;  // rest of string
            
            ci->subtype = ID_TIMER_CREATE;
            ci->descr = (char*) calloc(1, l1+1);
            strncpy(ci->descr, cmd + result[1].rm_so, l1);

            ci->exec = (char*) calloc(1, l2+1);
            strncpy(ci->exec, cmd + result[2].rm_so, l2);

        } else if (result[4].rm_so >= 0 &&     // Timer(key,cancel|pause|continue)
                   result[5].rm_so >= 0) {

            int l1 = result[4].rm_eo - result[4].rm_so;
            int l2 = result[5].rm_eo - result[5].rm_so;
         
            char* subCmd = (char*) malloc(l2+1);
            strncpy(subCmd,cmd+result[5].rm_so,l2);
            subCmd[l2] = '\0';

            ci->subtype = cmdTimer2id(subCmd);
            //printf("storeTimerCmd(): command %s -> %d\n", subCmd,ci->subtype);
            free(subCmd);

            ci->descr = (char*) calloc(1, l1+1);
            strncpy(ci->descr, cmd + result[4].rm_so, l1);

            ci->exec = (char*) calloc(1, l2+1);
            strncpy(ci->exec, cmd + result[5].rm_so, l2);
        }
    } else {
        printf("storeTimerCmd(): command %s is formed incorrectly (2)\n", cmd);
        cmdIsOk = EXIT_NOK;
    }

    free(result);
    result = NULL;
    regfree(regex);
    free(regex);
    regex = NULL;

    return cmdIsOk;
}

static int storeDescription(cmdItem* ci, char *cmd)
{
    char *newDescr = (char*) calloc(1, strlen(cmd) + 1);
    strcpy(newDescr, cmd);
    ci->descr = newDescr;
    
    return EXIT_OK;
}

static int storeSendCkpdCmd(cmdItem* ci, char *cmd)
{
    normalizeSequence(&cmd);
    return storeDescription(ci, cmd);
}

static int storeOtherCmd(cmdItem* ci, char *cmd)
{
    deleteSpaces(&cmd,0);
    return storeDescription(ci, cmd);
}

int storeCmds(SingleList** pcommands, const char *inval)
{
    int cmdId;

    size_t no_sub = cmdByCmd->re_nsub+1;
    
    char *value = strdup(inval);

    //printf("storeCmds() %s (%d)\n", value, (int)no_sub);

    regmatch_t* result = allocRegmatch(no_sub);

    int start = 0;
    while(regexec(cmdByCmd, value+start, no_sub, result, 0)==0) {

        //printf("storeCmds() next %s\n", value+start);

        int  step  = 0;
        char *name = NULL;
        char *cmds = NULL;

        int u1 = 1;
        if (result[1].rm_so >= 0 && result[2].rm_so) {  // <name>(<params>) matched
            cmds = value + start + result[2].rm_so + 1;

            // Search ) which is appropriate to matched (
            int braces = 1;     // cmd_name(
            char *ptr = cmds;
            while (*ptr != '\0' && braces > 0) {
                if (*ptr == '(') {
                    braces++;
                }
                if (*ptr == ')') {
                    braces--;
                }
                ptr++;
            }
            step = ptr - value - start + 1;
            if (*(ptr-1) == ')') {
                *(ptr-1) = '\0';
            }
        } else if (result[3].rm_so >= 0) {  // Exit matched
            u1 = 3;
            step = result->rm_eo;
        } else {
            printf("storeCmds(): Strange match\n");
            start += result->rm_eo;
            continue;
        }

        // strip spaces from tail
        if (cmds) {
            char *p = cmds + strlen(cmds) - 1;
            while (isspace(*p)) {
                *p = '\0';
                p--;
            }
        }

        name   = value + start + result[u1].rm_so;
        *(value + start + result[u1].rm_eo) = '\0';

        if (name == NULL) {
            //printf("Got name >NULL<\n");
            start +=result->rm_eo;
            continue;
            //} else {
            //    printf("Got name >%s<\n",name);
        }

        cmdId = cmd2id(name);

        if (cmdId == ID_UNKNOWN) {
            printf("storeCmds(): Unknown command name %s\n", name);
            //here could be params like log=... also
            start +=result->rm_eo;
            continue;
        }

        /*if (cmds != NULL) {
            printf("Got command body >%s<\n",cmds);
        } else {
            printf("Got command body >NULL<\n");
        }*/

        //printf("the rest of cmd >%s<\n",value+start+step);

        int cmdIsOk = EXIT_OK;
    
        // Insert into command list
        cmdItem* newCmd = (cmdItem*) calloc(1, sizeof(cmdItem));

        newCmd->type = cmdId;

        if (cmds == NULL) {
            newCmd->descr = NULL;
        } else {
        
            static struct 
            { 
                int id; 
                int (*hook)(cmdItem* ci, char *cmd);
            } 
            cmdHooks[] = {
                { ID_EXIT,      storeDescription   },
                { ID_EXEC,      storeExecCmd       },
                { ID_SENDCKPD,  storeSendCkpdCmd   },
                { ID_SET,       storeSetCmd        },
                { ID_EXECSET,   storeExecAndSetCmd },
                { ID_TIMER,     storeTimerCmd      },
                { ID_SEND,      storeDescription   },
                { ID_EXECSEND,  storeExecAndSendCmd},
                { ID_MACRO,     storeMacroCmd      },
                { ID_LOAD,      storeDescription   },  // file name can contain spaces inside
                { ID_INCLUDE,   storeDescription   },  // file name can contain spaces inside
                { ID_GET,       storeGetCmd        },
                { ID_MAKE,      storeMakeCmd       },
                { ID_EMU,       storeOtherCmd      },
                { ID_DBUS,      storeOtherCmd      },
                { ID_CMD_MAX,   0                  }
            };

                // Suppose the right order
            //size_t idx;
            //for (idx = 0; cmdHooks[idx].id < ID_CMD_MAX; ++idx) {
                if (cmdId < ID_CMD_MAX /*== cmdHooks[idx].id*/) {
                    if (cmdHooks[cmdId /*(idx*/].hook != NULL) {
                    cmdIsOk = (*(cmdHooks[cmdId /*(idx*/].hook))(newCmd, cmds);
                }
                //break;
                }
            //}
        }
    
        if (cmdIsOk == EXIT_OK) {
            (*pcommands) = listSingleAppend((*pcommands), newCmd);
        } else {
            free(newCmd);
            if (cmds == NULL) {
                printf("storeCmds(): command %s was not stored\n", name);
            } else {
                printf("storeCmds(): command %s ( %s ) was not stored\n", name,cmds);
            }
        }
        start +=step;
        //printf("REST OF CMDS %s\n", value+start);
    }   // while
    
    free(result);
    free(value);
    
    result = NULL;

    return EXIT_OK;
}

static int storeKey(mode* cMode, const char *inTag, const char *value)
{
    type_key* It = NULL;
    int ret = EXIT_OK;

    //printf("storeKey %s %s\n", cMode->name->str,tag);
    
    type_key **head = &(cMode->keys);
    
    char* tag = strdup(inTag);

    normalizeSequence(&tag);

    if(cMode && findExact(cMode, tag)) {
        free(tag);
        return EXIT_OK;  // do not overwrite existing items
    }

    It = (type_key*) calloc(1, sizeof(type_key));

    // Insert in head
    if ((*head) == NULL) {   // first elem
        It->next = NULL;

        // insert to the end
        *head = It;
    } else {
        // insert to the end
        type_key* last = (*head);
        while (last->next) {
            last = last->next;
        }

        last->next = It;
        It->next = NULL;

        // insert to the top version
        //It->next = (type_key*) (*head);
    }
    // insert to the top version
    // *head = It;

    It->key = (char*) calloc(1, strlen(tag)+1);
    strcpy(It->key,tag);

    if (value!= NULL) {
        ret = storeCmds(&(It->commands), value);
    } else {
        It->commands = NULL;
    }
    free(tag);
    
    return ret;
}

static string_t* loadLine(FILE *fp)
{
    char aLine[MAXCMDLEN];
    
    string_t* load_buf = NULL;

    while (1) {
        if (fgets(aLine, MAXCMDLEN, fp)) {
            //printf("LLLLL >%s/<n",aLine);
            int n = strlen(aLine); 
            boolean_t stop = BOOL_NO;
            
            if (n == 1 && aLine[0] == '\n') {   // empty line,got till \n
            
                aLine[0] = '\0';
                stop = BOOL_YES;
            
            } else if (n > 1 && aLine[n-1] == '\n') {   // got till \n

                aLine[n-1] = '\0';
                
                if (aLine[n-2] == '\\') {
                    aLine[n-2] = '\0';
                } else {
                    stop = BOOL_YES;
                }
                
            }   
            if (n > 0) { 
                if (!load_buf) {
                    load_buf = stringNew("");
                }
                if (aLine[0] != '\0') {
                    load_buf = stringAppend(load_buf,aLine);
                }
            } else {
                stop = BOOL_YES;
            }
            if (stop) {
                break;    
            }
        } else {
             break;    
        }
    }
    //printf("+++++ %s +++++\n",(load_buf ? load_buf->str:"NULL"));
    return load_buf;
}

static int loadKeys(FILE *fp)
{
    string_t *tmptext;
    char *tag   = NULL;
    char *value = NULL;
    
    mode* def   = getDefaultMode();  // will create it if absent
    mode* store = getCurrentMode();
    setCurrentMode(def);

    do {
        //printf("loadKeys -------------------------------------------------------------------------\n");
        tmptext=loadLine(fp);

        if (tmptext == NULL) {
            return EXIT_NOK;
        }

        tag   = NULL;
        value = NULL;
        parseKeyValue(tmptext->str, &tag, &value);

        if (tag == NULL) {   // comments and empty lines goes here
            
            if (*(tmptext->str) != '%') {
                boolean_t ok = BOOL_YES;
                int i = 0;
                for (;i<tmptext->len;i++) {
                    if (!isspace(*(tmptext->str + i))) {
                        ok = BOOL_NO;
                        break;
                    }
                }
                if (!ok) {
                    printf("ERROR : incorrectly formed command >%s<\n", tmptext->str);
                    ERROR2("[PARSER]: incorrectly formed command >%s<", tmptext->str);
                }
            }
            stringFree(tmptext, BOOL_YES);
            continue;
        } else if(strcmp(tag,SECTION_END_STR) == 0) {
            stringFree(tmptext, BOOL_YES);
            break;
        } else if(strcmp(tag,MODE_STR) == 0) { // mode definition

            //printf("MODE_STR >%s<\n",value);

            // split --  mode name : parent mode name
            char *split = index(value,':');
            if (split) {
                *split = '\0';
                split++;

                while (split && (*split == ' ' || *split == '\t')) {
                    split++;
                }
            }

            deleteSpaces(&value,0);
            if (split) {
                deleteSpaces(&split,0);
            }

            //printf("MODE_STR >%s< >%s<\n",value,(split?split:"NULL"));

            mode *nm = findMode(value);
            if (!nm) {
                nm = addMode(value, split);
            }
            setCurrentMode(nm);

        } else if(strcmp(tag,MODE_END_STR) == 0) {     // mode definition end
            setCurrentMode(getDefaultMode());
        } else {        // Line with key->command definition
            storeKey(getCurrentMode(), tag, value);
        }
        
        stringFree(tmptext, BOOL_YES);
        
    } while (1);
    
    // restore after load process
    setCurrentMode(store);
    
    return EXIT_OK;
}

static struct 
{ 
    const char* key;
    const char* value;
} 
keyTable[] = {
  {"_GET_ICON_($$)",  
   "ExecAndSet(image,icon,I=`find $(Home)/.anyRemote/Icons/$(Index) $(CfgDir)/Icons/$(Index) -name $(Param).png 2> /dev/null|grep -v svn`;echo \"$(Param),$I\");"},
  {"_GET_COVER_($$)", 
   "ExecAndSet(image,cover,I=`find $(Home)/.anyRemote/Covers $(CfgDir)/Icons/common -name \"$(Param).*\" 2> /dev/null|grep -v svn`;convert -resize $(Index)x$(Index) -depth 8 -background transparent $I \"$(TmpDir)\"/$(Param)_$(Index).png 2> /dev/null;echo \"$(Param),$(TmpDir)/$(Param)_$(Index).png\");"},
  
  {"_MM_($$)",        "Emulate(mousermove,$(Index),$(Param));"},
  {"_MB_($$)",        "Emulate(mouse,$(Index));"},
  {"_KB_($$)",        "Emulate(key,$(Param));"},
  {"_KP_($$)",        "Emulate(keydown,$(Param));"},
  {"_KR_($$)",        "Emulate(keyup,$(Param));"},
  {"_KM_($$)",        "Emulate(modifier,$(Index),$(Param));"},
  
  {NULL,              NULL}
};

static void loadInternal()
{
    mode* im = getInternalMode();

    int i=0;
    while (keyTable[i].key) {
        storeKey(im, keyTable[i].key, keyTable[i].value);
        i++;
    }
}

int load_cfg(const char *mfile, int isInit)
{
    FILE *fp;
    char *tag, *value;
    string_t *tmptext;
    
    //printf("LOAD:%s\n",mfile);

    fp=fopen(mfile,"r");
    if (fp == NULL) {
        return EXIT_NOK;
    }

    if (keyVal == NULL) {        // init it once
        keyVal  = (regex_t *) malloc(sizeof(regex_t));
        memset(keyVal,   0, sizeof(regex_t));
        regexpPrepare(keyVal,   REGEX_KEYVAL);
    }

    if (cmdByCmd == NULL) {     // init it once
        cmdByCmd = (regex_t *) malloc(sizeof(regex_t));
        memset(cmdByCmd, 0, sizeof(regex_t));
        regexpPrepare(cmdByCmd, REGEX_CMDBYCMD);
    }
    //printf("LOAD LINES\n");

    // Go through the conf file
    while(1) {

        tmptext=loadLine(fp);

        if (tmptext == NULL) {
            break;
        }
        //printf("LOAD LINE:%s\n",tmptext->str);

        parseKeyValue(tmptext->str, &tag, &value);

        if (tag == NULL) {
            stringFree(tmptext, BOOL_YES);
            continue;
        } else if (strcmp(tag,KEYS_SECTION_STR) == 0) {
            printf("WARNING: [Keys] tag is deprecated. Please update configuration files !\n");
            if (loadKeys(fp) != EXIT_OK) {
                stringFree(tmptext, BOOL_YES);
                fclose(fp);
                return EXIT_NOK;
            }
            if (!isInit) {        // If we goes here from Include() command, we did all we need
                stringFree(tmptext, BOOL_YES);
                fclose(fp);
                return EXIT_OK;
            }
        } else if (strcmp(tag,PROTOCOL_SECTION_STR) == 0) {
        
            //printf("DO LOAD:%s\n",mfile);
            
            if (loadKeys(fp) != EXIT_OK) {
                stringFree(tmptext, BOOL_YES);
                fclose(fp);
                return EXIT_NOK;
            }
            if (!isInit) {        // If we goes here from Include() command, we did all we need
                stringFree(tmptext, BOOL_YES);
                fclose(fp);
                return EXIT_OK;
            }
        } else if (isInit) {
            if (strncmp("GuiApp",tag,6) != 0) {
                printf("WARNING: Unknown tag in cfg file >%s<\n",tag);
            }
        }
        stringFree(tmptext, BOOL_YES);
    }

    fclose(fp);

    loadInternal();

    //printf("LOAD:%s OK\n",mfile);
    return EXIT_OK;
}

void freeRegexps()
{
    if (cmdByCmd) {
        regfree(cmdByCmd);
        free(cmdByCmd);
        cmdByCmd = NULL;
    }
    if (keyVal) {
        regfree(keyVal);
        free(keyVal);
        keyVal = NULL;
    }
}

void init_cfg_dir(char *path)
{
    int sz = 0;
    const char* v = getVarValue("CfgDir", &sz);
    
    if (!v) {
        if (path != NULL) {
            char *d = strdup(path);
            char *p = rindex(d,SEPARATOR);
            if (p != NULL) {
                *p = '\0';
            } else {
                *d = '\0';
            }
            setCfgDir(d);
            free(d);
        } else {
            setCfgDir(NULL);
        }
    }
}

int init_cfg(char *path)
{
    //printf("init_cfg %s\n",path);
    int ret = EXIT_OK;

    if (path != NULL) {

        printf("Use configuration file %s\n",path);
        ret = load_cfg(path,1);
   
    } else {

        char cfgfile[MAXLEN];
        char *t = getenv("HOME");
        if (t) {
            strcpy(cfgfile, t);
        } else {
            strcpy(cfgfile, ".");
        }
        strcat(cfgfile, CFGFILE);

        printf("Search configuration file %s\n", cfgfile);
        if(load_cfg(cfgfile,1) != EXIT_OK) {
            // Try again in current dir
            strcpy(cfgfile, ".");
            strcat(cfgfile, CFGFILE);

            printf("Search configuration file %s\n", cfgfile);
            ret = load_cfg(cfgfile,1);
        }
    }
    if (ret != EXIT_OK) {
        printf("Can not find configuration file to use or incorrect content of configuration file detected.\n");
    }
    return ret;
}

