diff options
Diffstat (limited to 'contrib/ls-config/src/ls-config.c')
-rw-r--r-- | contrib/ls-config/src/ls-config.c | 1345 |
1 files changed, 1345 insertions, 0 deletions
diff --git a/contrib/ls-config/src/ls-config.c b/contrib/ls-config/src/ls-config.c new file mode 100644 index 0000000..4db8966 --- /dev/null +++ b/contrib/ls-config/src/ls-config.c @@ -0,0 +1,1345 @@ +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> +#include <locale.h> +#include <libintl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <math.h> +#include <libconfig.h> + +#define PACKAGE "LS bash config" +#define VERSION "1.0.3" + +// global flags +struct flags { + int quiet; //quiet output + int names; //set for printout config variables names + int types; //set for printout config variables types + int values; //set for printout config variables values + int indexes; //set for printout config variables indexes + int counter; //set for printout config varibales counting (for grout, list, array. in other cases it return 1) + int unset; //unset valriable + int boolstring; //set for output bool variable (0|1) as test (false|true) + int mode; //1 - for setting variable, 0 - for get hist data + int error; //error status handling +}; + +//take valur from input and comvert it to int +//TODO: Read long too +int getNumber() { + char buf[1000]; + int test,val; + unsigned int inp; + fgets(buf, sizeof buf, stdin); + test = sscanf(buf, "%u", &inp); + val = (int) inp; + if(val < 0) val *= -1; + if(test > 0) return val; + return (int) 0; +} + +//printout help messsage +void printHelp() { + printf(gettext("Configuration file handling\n")); + printf("\n"); + printf(gettext("Usage: ls-config [OPTION]\n")); + printf(gettext("Reading and writening data from configuration files\n")); + printf(gettext("in libconfig9 format.\n")); + printf("\n"); + printf(gettext("CAUTION: using without given config file are cause error!\n")); + printf("\n"); + printf(gettext("Available options:\n")); + printf(gettext(" -f, --file=FILE Configuration file to handle.\n")); + printf("\n"); + printf(gettext(" -s, --set=PATH Set configuration variable of given path.\n")); + printf(gettext(" -d, --data=DATA Configuration variable value (only with -s)\n")); + printf(gettext(" -p, --type=TYPE Configuration value type\n")); + printf("\n"); + printf(gettext(" -g, --get=PATH Get configuration variable of given path.\n")); + printf(gettext(" -n, --names Printout variables names.\n")); + printf(gettext(" -t, --types Printout variables types.\n")); + printf(gettext(" -v, --values Printout variables values.\n")); + printf(gettext(" -i, --indexes Printout variables indexes.\n")); + printf(gettext(" -c, --count Printout elements count (only: array, list, group).\n")); + printf(gettext(" -b, --bool-string Printout boolean variables as text.\n")); + printf("\n"); + printf(gettext(" -q, --quiet Quiet output to use in scripts.\n")); + printf(gettext(" -h, --help Print this help message.\n")); + printf("\n"); + printf(gettext("TYPE: Variable types:\n")); + printf(gettext(" group - variables group,\n")); + printf(gettext(" array - array of variables,\n")); + printf(gettext(" list - list of variables,\n")); + printf(gettext(" int - integer number,\n")); + printf(gettext(" int64 - 64bit integer number,\n")); + printf(gettext(" float - float point number,\n")); + printf(gettext(" bool - boolean value,\n")); + printf(gettext(" string - character string.\n")); + printf("\n"); + printf("(c) 2013 by LucaS web sutio - http://www.lucas.net.pl\n"); + printf("Author: Ćukasz A. Grabowski\n"); + printf(gettext("Licence: ")); + printf("GPL v2.\n"); + exit(0); +}; + +//set configuration int value +int set_config_int(config_setting_t *setting, char *dataString, struct flags optflags) { + long bufl; //int (long) to get from input data string + int buf, scs; //config int, success status + char *erp; //error output + + //convert input data to int + errno = 0; + bufl = strtol(dataString, &erp, 0); + if(((errno == ERANGE && (bufl == LONG_MAX || bufl == LONG_MIN)) || (errno != 0 && bufl == 0)) || (erp == dataString) || bufl > INT_MAX || bufl < INT_MIN) { + if(optflags.quiet == 0) printf(gettext("ERROR! Incorrect data format.\n")); + return 12; + }; + buf = (int)bufl; + + //set configuration variable + scs = config_setting_set_int(setting, buf); + if(scs == CONFIG_FALSE) { + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + return 0; +}; + +//set configuration int64 value +int set_config_int64(config_setting_t *setting, char *dataString, struct flags optflags) { + long bufl; //long to get from input data string + int scs; //success status + char *erp; //error output + + //convert input data to long + errno = 0; + bufl = strtol(dataString, &erp, 0); + if(((errno == ERANGE && (bufl == LONG_MAX || bufl == LONG_MIN)) || (errno != 0 && bufl == 0)) || (erp == dataString)) { + if(optflags.quiet == 0) printf(gettext("ERROR! Incorrect data format.\n")); + return 12; + }; + + //set configuration variable + scs = config_setting_set_int64(setting, bufl); + if(scs == CONFIG_FALSE) { + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + return 0; +}; + +//set configuration float value +int set_config_float(config_setting_t *setting, char *dataString, struct flags optflags) { + double buff; //double (float) to get from input data string + int scs; //success status + char *erp; //error output + + //convert input data to double + errno = 0; + buff = strtod(dataString, &erp); + if(((errno == ERANGE && (buff == HUGE_VALF || buff == HUGE_VALL)) || (errno != 0 && buff == 0)) || (erp == dataString)) { + if(optflags.quiet == 0) printf(gettext("ERROR! Incorrect data format.\n")); + return 12; + } + + //set configuration variable + scs = config_setting_set_float(setting, buff); + if(scs == CONFIG_FALSE) { + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + return 0; +}; + +//set configuration boolean value +int set_config_bool(config_setting_t *setting, char *dataString, struct flags optflags) { + int scs, buf; //success status, input convert burrer + + //convert input data + //chceck both 1/0 and true/false string + buf = -1; + if(!strcmp(dataString, "1") || !strcmp(dataString, "true") || !strcmp(dataString, "TRUE")) buf = 1; + if(!strcmp(dataString, "0") || !strcmp(dataString, "false") || !strcmp(dataString, "FALSE")) buf = 0; + if(buf < 0) { + if(optflags.quiet == 0) printf(gettext("ERROR! Incorrect data format.\n")); + return 12; + } + + //set configuration variable + scs = config_setting_set_bool(setting, buf); + if(scs == CONFIG_FALSE) { + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + return 0; +}; + +//configuratnion variable path look like: foo.bar.car +//this fuction return string giving path to parent element (foo.bar) +char* path_parent(char *dataPath) { + char *str_ptr, *last_ptr, *newpath, *dot="."; //tokenized buffer, last buffer, new parent path, separator + newpath = malloc(1); + memset(newpath, 0, 1); + last_ptr = malloc(1); + memset(last_ptr, 0, 1); + + //tokenize string and save last token + str_ptr = strtok(dataPath, "."); + last_ptr = (char*)realloc(last_ptr, (strlen(str_ptr)+1)*sizeof(char)); + strcpy(last_ptr, str_ptr); + + //loop overt path to build new path without last element + while(str_ptr != NULL) { + str_ptr = strtok(NULL, "."); + if(str_ptr != NULL) { + if(strlen(last_ptr) > 0 ) { + newpath = (char*)realloc(newpath, (strlen(newpath)+strlen(last_ptr)+2)*sizeof(char)); + strcat(newpath, dot); + strcat(newpath, last_ptr); + }; + last_ptr = (char*)realloc(last_ptr, (strlen(str_ptr)+1)*sizeof(char)); + strcpy(last_ptr, str_ptr); + } else { + last_ptr = (char*)realloc(last_ptr, (1)*sizeof(char)); + memset(last_ptr, 0, 1); + }; + }; + free(dataPath); + + //if new path empty thren return null + if(strlen(newpath) == 0) { + free(newpath); + newpath = NULL; + }; + return newpath; +}; + +//get element name from configuration variable path +//e.g.: from foo.bar return bar +char* path_name(char *dataPath) { + char *str_ptr, *name, *tk; //tokenized buffer, element name, copy of dataPath + name = malloc(1); + + //make copy of dataPath + tk = malloc((strlen(dataPath)+1)*sizeof(char)); + memset(name, 0, 1); + strcpy(tk, dataPath); + + //tokenize dataPath + str_ptr = strtok(tk, "."); + + //loop over tokenize pathh to get last element + while(str_ptr != NULL) { + name = (char*)realloc(name, (strlen(str_ptr)+1)*sizeof(char)); + strcpy(name, str_ptr); + str_ptr = strtok(NULL, "."); + }; + free(tk); + + //if no element name then return null + if(strlen(name) == 0) { + free(name); + name = NULL; + }; + return name; +}; + +//set configuration path +//@return int success +//@param configFile - name (with path) of configuration fille +//@param dataPath - path of configuration variable (in config file) +//@param optflags - global options flags +//@param dataString - data to store in configuration variable in string format +//@param dataType - type of variable to save +int set_config(char *configFile, char *dataPath, struct flags optflags, char *dataString, char *dataType) { + config_t cfg; //libcongig configuration handler + config_setting_t *setting, *ss; //libconfig element handrer: mant, and subset (uset for multielement types) + config_init(&cfg); + int scs, dt, dattyp; //sucess statu, data type + char *npath; // new variable configuration path path + + //open and read configuration file + if(!config_read_file(&cfg, configFile)) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Can't read configuration file.\n")); + return 1; + }; + + //if no data path or data string then cause error + if(dataPath == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Conviguration variable path not given.\n")); + return 4; + }; + if(dataString == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Configuration variable value not given.\n")); + return 9; + }; + + //find configuration variable of given path + setting = config_lookup(&cfg, dataPath); + if(setting == NULL) { + //if variable of given path not found get element name and partent path, + //then try to create it + npath = path_name(dataPath); + dataPath = path_parent(dataPath); + if(dataPath == NULL) { + setting = config_root_setting(&cfg); + } else { + setting = config_lookup(&cfg, dataPath); + }; + if(setting == NULL) { + //if parent not exists exit with error + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Inavlid configuration variable path.\n")); + return 16; + }; + //chceck type of parent element (named alement can be added only to group element) + dt = config_setting_type(setting); + if(dt != CONFIG_TYPE_GROUP) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! New named configuration variable can be added only to group element.\n")); + return 17; + }; + //check if new element type are given + if(dataType == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Configuration variable type not given.\n")); + return 13; + }; + //now get type based on his name + if(!strcmp(dataType, "int")) { + dattyp = CONFIG_TYPE_INT; + } else if(!strcmp(dataType, "int64")) { + dattyp = CONFIG_TYPE_INT64; + } else if(!strcmp(dataType, "float")) { + dattyp = CONFIG_TYPE_FLOAT; + } else if(!strcmp(dataType, "string")) { + dattyp = CONFIG_TYPE_STRING; + } else if(!strcmp(dataType, "bool")) { + dattyp = CONFIG_TYPE_BOOL; + } else if(!strcmp(dataType, "array")) { + dattyp = CONFIG_TYPE_ARRAY; + } else if(!strcmp(dataType, "list")) { + dattyp = CONFIG_TYPE_LIST; + } else if(!strcmp(dataType, "group")) { + dattyp = CONFIG_TYPE_GROUP; + } else { + //if given type no mutch eny then cause error and exit + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Inlegal data type.\n")); + return 14; + }; + //add new element to configuration file + ss = config_setting_add(setting, npath, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + scs = 0; + //and based on new type set his value + switch(dattyp) { + case CONFIG_TYPE_INT: + scs = set_config_int(ss, dataString, optflags); + break; + case CONFIG_TYPE_INT64: + scs = set_config_int64(ss, dataString, optflags); + break; + case CONFIG_TYPE_FLOAT: + scs = set_config_float(ss, dataString, optflags); + break; + case CONFIG_TYPE_STRING: + scs = config_setting_set_string(ss, dataString); + if(scs == CONFIG_FALSE) { + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + scs = 11; + } else scs = 0; + break; + case CONFIG_TYPE_BOOL: + scs = set_config_bool(ss, dataString, optflags); + break; + }; + if(scs > 0) { + //if occurs some error wihe setting variable value exit with error + config_destroy(&cfg); + return scs; + }; + } else { + //but if we found element of given path, try to set his value + //first of all determinate type of value + dt = config_setting_type(setting); + switch(dt) { + case CONFIG_TYPE_INT: + if(dataType != NULL && strcmp(dataType, "int")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //then set value + scs = set_config_int(setting, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_INT64: + if(dataType != NULL && strcmp(dataType, "int64")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //then set value + scs = set_config_int64(setting, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_FLOAT: + if(dataType != NULL && strcmp(dataType, "float")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //then set value + scs = set_config_float(setting, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_STRING: + if(dataType != NULL && strcmp(dataType, "string")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //then set value + scs = config_setting_set_string(setting, dataString); + if(scs == CONFIG_FALSE) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + break; + case CONFIG_TYPE_BOOL: + if(dataType != NULL && strcmp(dataType, "bool")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //then set value + scs = set_config_bool(setting, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_ARRAY: + //if array are empty we can set alement of any scalar type + if(config_setting_length(setting) == 0) { + //but we must have his type + if(dataType == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Configuration variable type not given.\n")); + return 13; + }; + if(!strcmp(dataType, "int")) { + dattyp = CONFIG_TYPE_INT; + } else if(!strcmp(dataType, "int64")) { + dattyp = CONFIG_TYPE_INT64; + } else if(!strcmp(dataType, "float")) { + dattyp = CONFIG_TYPE_FLOAT; + } else if(!strcmp(dataType, "string")) { + dattyp = CONFIG_TYPE_STRING; + } else if(!strcmp(dataType, "bool")) { + dattyp = CONFIG_TYPE_BOOL; + } else { + //only scalar type availabe + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Prohibited data type.\n")); + return 18; + }; + //first of all we must add new element to array + ss = config_setting_add(setting, NULL, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //then based on his type set value + switch(dattyp) { + case CONFIG_TYPE_INT: + scs = set_config_int(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_INT64: + scs = set_config_int64(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_FLOAT: + scs = set_config_float(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_STRING: + scs = config_setting_set_string(ss, dataString); + if(scs == CONFIG_FALSE) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + break; + case CONFIG_TYPE_BOOL: + scs = set_config_bool(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + }; + } else { + //but if we have some element in array, we can add only element of same type + //so, because all element in arry must be same type, we get type of first element + //and based on it set new element + dattyp = config_setting_type(config_setting_get_elem(setting, 0)); + switch(dattyp) { + case CONFIG_TYPE_INT: + if(dataType != NULL && strcmp(dataType, "int")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //add new element + ss = config_setting_add(setting, NULL, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //then set his value + scs = set_config_int(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_INT64: + if(dataType != NULL && strcmp(dataType, "int64")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //add new element + ss = config_setting_add(setting, NULL, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //then set his value + scs = set_config_int64(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_FLOAT: + if(dataType != NULL && strcmp(dataType, "float")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //add new element + ss = config_setting_add(setting, NULL, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //then set his value + scs = set_config_float(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + case CONFIG_TYPE_STRING: + if(dataType != NULL && strcmp(dataType, "string")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //add new element + ss = config_setting_add(setting, NULL, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //then set his value + scs = config_setting_set_string(ss, dataString); + if(scs == CONFIG_FALSE) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + break; + case CONFIG_TYPE_BOOL: + if(dataType != NULL && strcmp(dataType, "bool")) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! inconsistent value type.\n")); + return 10; + }; + //add new element + ss = config_setting_add(setting, NULL, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //then set his value + scs = set_config_bool(ss, dataString, optflags); + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + break; + }; + }; + break; + case CONFIG_TYPE_LIST: + //in case adding element to list, we can add any type of element + if(dataType == NULL) { + //but we must konwn his type + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Configuration variable type not given.\n")); + return 13; + }; + if(!strcmp(dataType, "int")) { + dattyp = CONFIG_TYPE_INT; + } else if(!strcmp(dataType, "int64")) { + dattyp = CONFIG_TYPE_INT64; + } else if(!strcmp(dataType, "float")) { + dattyp = CONFIG_TYPE_FLOAT; + } else if(!strcmp(dataType, "string")) { + dattyp = CONFIG_TYPE_STRING; + } else if(!strcmp(dataType, "bool")) { + dattyp = CONFIG_TYPE_BOOL; + } else if(!strcmp(dataType, "array")) { + dattyp = CONFIG_TYPE_ARRAY; + } else if(!strcmp(dataType, "list")) { + dattyp = CONFIG_TYPE_LIST; + } else if(!strcmp(dataType, "group")) { + dattyp = CONFIG_TYPE_GROUP; + } else { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Inlegal data type.\n")); + return 14; + }; + //add new element of given type + ss = config_setting_add(setting, NULL, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //now, based on type, set element value + scs = 0; + switch(dattyp) { + case CONFIG_TYPE_INT: + scs = set_config_int(ss, dataString, optflags); + break; + case CONFIG_TYPE_INT64: + scs = set_config_int64(ss, dataString, optflags); + break; + case CONFIG_TYPE_FLOAT: + scs = set_config_int64(ss, dataString, optflags); + break; + case CONFIG_TYPE_STRING: + scs = config_setting_set_string(ss, dataString); + if(scs == CONFIG_FALSE) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + scs = 0; + break; + case CONFIG_TYPE_BOOL: + scs = set_config_int64(ss, dataString, optflags); + break; + }; + if(scs > 0) { + config_destroy(&cfg); + return scs; + }; + //finaly outpt index of new added element + if(optflags.quiet == 0) { + printf(gettext("Added element index: %d\n"), config_setting_index(ss)); + } else { + printf("%d", config_setting_index(ss)); + }; + break; + case CONFIG_TYPE_GROUP: + //to group we can add any type of element, but we must have his name + if(dataType == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Configuration variable type not given.\n")); + return 13; + }; + if(strlen(dataString) < 1) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Bad name of configuration variable.\n")); + return 15; + }; + //determinate type of new variable + if(!strcmp(dataType, "int")) { + dattyp = CONFIG_TYPE_INT; + } else if(!strcmp(dataType, "int64")) { + dattyp = CONFIG_TYPE_INT64; + } else if(!strcmp(dataType, "float")) { + dattyp = CONFIG_TYPE_FLOAT; + } else if(!strcmp(dataType, "string")) { + dattyp = CONFIG_TYPE_STRING; + } else if(!strcmp(dataType, "bool")) { + dattyp = CONFIG_TYPE_BOOL; + } else if(!strcmp(dataType, "array")) { + dattyp = CONFIG_TYPE_ARRAY; + } else if(!strcmp(dataType, "list")) { + dattyp = CONFIG_TYPE_LIST; + } else if(!strcmp(dataType, "group")) { + dattyp = CONFIG_TYPE_GROUP; + } else { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Inlegal data type.\n")); + return 14; + }; + //then add new alement + ss = config_setting_add(setting, dataString, dattyp); + if(ss == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable set failed.\n")); + return 11; + }; + //in case of adding new element to group we not set his value + //(value field of input are used to get variable name) + //We only output index of new added element + if(optflags.quiet == 0) { + printf(gettext("Added element index: %d\n"), config_setting_index(ss)); + } else { + printf("%d", config_setting_index(ss)); + }; + break; + }; + } + + //Finaly write configuration file + scs = config_write_file(&cfg, configFile); + if(scs == CONFIG_FALSE) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Configuration file write failed.\n")); + return 8; + }; + config_destroy(&cfg); + return 0; +}; + +//unset configuration path +//(remove variable from configuration file) +//@return int success +//@param char* configFile - the name (with path) of configuration file +//@param char* configPath - path to configuration valriable to remove (unset) +//@param struct flags optflags - global flags +int unset_config(char *configFile, char *dataPath, struct flags optflags) { + config_t cfg; //configuration file handler + config_setting_t *setting, *par; //configuration valriale handler, and paren variable handler + int idx, scs; //index of variable, sucess status + //open configuration file + config_init(&cfg); + if(!config_read_file(&cfg, configFile)) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Can't read configuration file.\n")); + return 1; + }; + //chceck if data path given + if(dataPath == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Conviguration variable path not given.\n")); + return 4; + }; + //now find variable of given path + setting = config_lookup(&cfg, dataPath); + if(setting == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Given variable path not found.\n")); + return 3; + }; + //get element index + idx = config_setting_index(setting); + if(idx < 0) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Can't remove root element.\n")); + return 5; + }; + //now find parent element + par = config_setting_parent(setting); + if(par == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Can't find parent element.\n")); + return 6; + }; + //then remove element + scs = config_setting_remove_elem(par, idx); + if(scs == CONFIG_FALSE) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Variable unset failed.\n")); + return 7; + }; + //Finaly write configuration file + scs = config_write_file(&cfg, configFile); + if(scs == CONFIG_FALSE) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Configuration file write failed.\n")); + return 8; + }; + config_destroy(&cfg); + return 0; +}; + +//get configuratioin variable +//(read it from configuration file) +//@return char* variable value +//@param char* configFile - configuration file name (with path) +//@param cher* dataPath - configuration variable path (in file) +//@param struct flags optflags - global flags +int read_config(char *configFile, char *dataPath, struct flags optflags) { + config_t cfg; //configuration file handler + config_setting_t *setting, *ss; //configuration element handler, and helper handler (config element too) + int comaset, varindex, varcounter; //helper flat for buid output strings, varibale index, counter + unsigned int maxel, i; //max elements, and loop index + char buffer[256]; //reading buffer + const char *cbuffer; + const char *coma=";"; //output string variable separator + int ibuffer, ssize; //value int buffer + char *dataName, *dataTypeName, *dataValueString; //name of variable, type of variable, value of variable + int dataType, st; //internale variable type + //initialize values + dataValueString = malloc(1); + dataTypeName = malloc(1); + memset(dataValueString, 0, 1); + memset(dataTypeName, 0, 1); + varindex = 0; + varcounter = 0; + //open and read configuration file + config_init(&cfg); + if(!config_read_file(&cfg, configFile)) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Can't read configuration file.\n")); + return 1; + }; + //now find variable element of given path + if(dataPath == NULL) { + //if path not givne load root element (default) + setting = config_root_setting(&cfg); + } else { + setting = config_lookup(&cfg, dataPath); + }; + if(setting == NULL) { + config_destroy(&cfg); + if(optflags.quiet == 0) printf(gettext("ERROR! Given variable path not found.\n")); + return 3; + }; + //read variable name + dataName = config_setting_name(setting); + if(dataName == NULL) dataName = "NULL"; //in case variable have no name convert to string representation + //read variable type + dataType = config_setting_type(setting); + //next conver type to human readable and read variable value based on his type + //and in cases in type not scalar read index and coutn variables + switch(dataType) { + case CONFIG_TYPE_INT: + dataTypeName = (char*)realloc(dataTypeName, 4*sizeof(char)); + strcpy(dataTypeName, "int"); + sprintf(buffer, "%d", config_setting_get_int(setting)); + dataValueString = (char*)realloc(dataValueString, (strlen(buffer)+1)*sizeof(char)); + strcpy(dataValueString, buffer); + break; + case CONFIG_TYPE_INT64: + dataTypeName = (char*)realloc(dataTypeName, 6*sizeof(char)); + strcpy(dataTypeName, "int64"); + sprintf(buffer, "%lld", config_setting_get_int64(setting)); + dataValueString = (char*)realloc(dataValueString, (strlen(buffer)+1)*sizeof(char)); + strcpy(dataValueString, buffer); + break; + case CONFIG_TYPE_FLOAT: + dataTypeName = (char*)realloc(dataTypeName, 9*sizeof(char)); + strcpy(dataTypeName, "float"); + sprintf(buffer, "%f", config_setting_get_float(setting)); + dataValueString = (char*)realloc(dataValueString, (strlen(buffer)+1)*sizeof(char)); + strcpy(dataValueString, buffer); + break; + case CONFIG_TYPE_STRING: + dataTypeName = (char*)realloc(dataTypeName, 7*sizeof(char)); + strcpy(dataTypeName, "string"); + cbuffer = config_setting_get_string(setting); + dataValueString = (char*)realloc(dataValueString, (strlen(cbuffer)+1)*sizeof(char)); + strcpy(dataValueString, cbuffer); + break; + case CONFIG_TYPE_BOOL: + dataTypeName = (char*)realloc(dataTypeName, 5*sizeof(char)); + strcpy(dataTypeName, "bool"); + if(optflags.boolstring == 1) { + //if expect bool as string, convert it to human readable + ibuffer = config_setting_get_bool(setting); + if(ibuffer == CONFIG_TRUE) { + dataValueString = (char*)realloc(dataValueString, 5*sizeof(char)); + strcpy(dataValueString, "true"); + } else { + dataValueString = (char*)realloc(dataValueString, 6*sizeof(char)); + strcpy(dataValueString, "false"); + } + } else { + //else output as digit + sprintf(buffer, "%d", config_setting_get_bool(setting)); + dataValueString = (char*)realloc(dataValueString, (strlen(buffer)+1)*sizeof(char)); + strcpy(dataValueString, buffer); + }; + break; + case CONFIG_TYPE_ARRAY: + dataTypeName = (char*)realloc(dataTypeName, 6*sizeof(char)); + strcpy(dataTypeName, "array"); + //get element count + maxel = (unsigned int)config_setting_length(setting); + comaset = 0; + //and loop over all elements + for(i = 0; i < maxel; i++) { + //get element + ss = config_setting_get_elem(setting, i); + if(ss != NULL) { + st = config_setting_type(ss); + switch(st) { + case CONFIG_TYPE_INT: + sprintf(buffer, "%d", config_setting_get_int(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + break; + case CONFIG_TYPE_INT64: + sprintf(buffer, "%lld", config_setting_get_int64(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + break; + case CONFIG_TYPE_FLOAT: + sprintf(buffer, "%f", config_setting_get_float(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + break; + case CONFIG_TYPE_STRING: + ssize = (int)strlen(config_setting_get_string(ss)); + + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+ssize+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, config_setting_get_string(ss)); + break; + case CONFIG_TYPE_BOOL: + if(optflags.boolstring == 1) { + ibuffer = config_setting_get_bool(ss); + if(ibuffer == CONFIG_TRUE) { + //if bool must be outputed as humen readable - convert it + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+4+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "true"); + } else { + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+5+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "false"); + } + } else { + //else output as digit + sprintf(buffer, "%d", config_setting_get_bool(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + }; + break; + case CONFIG_TYPE_ARRAY: + //if array contains array output as kwyword ARRAY + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+7)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "ARRAY"); + break; + case CONFIG_TYPE_LIST: + //if array contains list output as keyword LIST + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+6)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "LIST"); + break; + case CONFIG_TYPE_GROUP: + //if array contains group output as keywort GROUP + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+7)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "GROUP"); + break; + }; + comaset = 1; + }; + }; + break; + case CONFIG_TYPE_LIST: + dataTypeName = (char*)realloc(dataTypeName, 5*sizeof(char)); + strcpy(dataTypeName, "list"); + //get element count + maxel = (unsigned int)config_setting_length(setting); + //end loop over all elements + comaset = 0; + for(i = 0; i < maxel; i++) { + ss = config_setting_get_elem(setting, i); + if(ss != NULL) { + st = config_setting_type(ss); + switch(st) { + case CONFIG_TYPE_INT: + sprintf(buffer, "%d", config_setting_get_int(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + break; + case CONFIG_TYPE_INT64: + sprintf(buffer, "%lld", config_setting_get_int64(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + break; + case CONFIG_TYPE_FLOAT: + sprintf(buffer, "%f", config_setting_get_float(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + break; + case CONFIG_TYPE_STRING: + ssize = (int)strlen(config_setting_get_string(ss)); + + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+ssize+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, config_setting_get_string(ss)); + break; + case CONFIG_TYPE_BOOL: + if(optflags.boolstring == 1) { + ibuffer = config_setting_get_bool(ss); + if(ibuffer == CONFIG_TRUE) { + //if bool must be printout as humanreadable - convert it + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+4+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "true"); + } else { + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+5+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "false"); + } + } else { + //else output as int + sprintf(buffer, "%d", config_setting_get_bool(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+strlen(buffer)+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, buffer); + }; + break; + case CONFIG_TYPE_ARRAY: + //if list contain array output as keyword ARRAY + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+7)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "ARRAY"); + break; + case CONFIG_TYPE_LIST: + //if list contain list output as keyword LIST + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+6)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "LIST"); + break; + case CONFIG_TYPE_GROUP: + //if list contain group output as keyword GROUP + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+7)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, "GROUP"); + break; + }; + comaset = 1; + }; + }; + break; + case CONFIG_TYPE_GROUP: + dataTypeName = (char*)realloc(dataTypeName, 6*sizeof(char)); + strcpy(dataTypeName, "group"); + //get elementc count + maxel = (unsigned int)config_setting_length(setting); + //and loop over all elements + //but in group case, we return inside variables names + comaset = 0; + for(i = 0; i < maxel; i++) { + ss = config_setting_get_elem(setting, i); + if(ss != NULL) { + ssize = (int)strlen(config_setting_name(ss)); + dataValueString = (char*)realloc(dataValueString, (strlen(dataValueString)+ssize+2)*sizeof(char)); + if(comaset == 1) strcat(dataValueString, coma); + strcat(dataValueString, config_setting_name(ss)); + comaset = 1; + }; + }; + break; + }; + + //last we get readed variable index, and element count + varindex = config_setting_index(setting); + varcounter = config_setting_length(setting); + + //and finaly output data + if(optflags.names == 1 && optflags.quiet == 0) printf(gettext("Variable name: %s\n"), dataName); + if(optflags.names == 1 && optflags.quiet == 1) printf("%s", dataName); + if((optflags.types == 1 && optflags.quiet == 1) && optflags.names == 1) printf(":"); + if(optflags.types == 1 && optflags.quiet == 0) printf(gettext("Variable type: %s\n"), dataTypeName); + if(optflags.types == 1 && optflags.quiet == 1) printf("%s", dataTypeName); + if((optflags.values == 1 && optflags.quiet == 1) && (optflags.names == 1 || optflags.types == 1)) printf(":"); + if(optflags.values == 1 && optflags.quiet == 0) printf(gettext("Variable value: %s\n"), dataValueString); + if(optflags.values == 1 && optflags.quiet == 1) printf("%s", dataValueString); + if((optflags.indexes == 1 && optflags.quiet == 1) && (optflags.names == 1 || optflags.types == 1 || optflags.values == 1)) printf(":"); + if(optflags.indexes == 1 && optflags.quiet == 0) printf(gettext("Variable index: %d\n"), varindex); + if(optflags.indexes == 1 && optflags.quiet == 1) printf("%d", varindex); + if((optflags.counter == 1 && optflags.quiet == 1) && (optflags.names == 1 || optflags.types == 1 || optflags.values == 1 || optflags.indexes == 1)) printf(":"); + if(optflags.counter == 1 && optflags.quiet == 0) printf(gettext("Variable elements count: %d\n"), varcounter); + if(optflags.counter == 1 && optflags.quiet == 1) printf("%d", varcounter); + if(optflags.quiet == 1) printf("\n"); + + config_destroy(&cfg); + return 0; +} + +int main(int argc, char **argv) { + //firs set locale and domain to work with internationalization + setlocale(LC_ALL, ""); + bindtextdomain("ls-config", "/usr/share/locale"); + textdomain("ls-config"); + + //then declare and init values + int opt,test; //used for read innput: option, and testing + int fd; //file descriptor + char *sinp, *dataPath=NULL, *dataString=NULL, *dataType=NULL; //string input, configuration variable path, input data, variable type + char *configFile=NULL; //config file name (with path) + struct flags optflags = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //global flags initialize + int excode; //program exit code + excode = 0; + + sinp = malloc(sizeof(char) * 256); + + //long options reading + struct option long_options[] = { + /* These options set a flag. */ + {"quiet", no_argument, &optflags.quiet, 1}, + {"names", no_argument, &optflags.names, 1}, + {"types", no_argument, &optflags.types, 1}, + {"values", no_argument, &optflags.values, 1}, + {"indexes", no_argument, &optflags.indexes, 1}, + {"count", no_argument, &optflags.counter, 1}, + {"unset", no_argument, &optflags.unset, 1}, + {"bool-string", no_argument, &optflags.boolstring, 1}, + /* These options don't set a flag. + We distinguish them by their indices. */ + {"help", no_argument, 0, 'h'}, + {"set", required_argument, 0, 's'}, + {"get", optional_argument, 0, 'g'}, + {"data", required_argument, 0, 'd'}, + {"type", required_argument, 0, 'p'}, + {"file", required_argument, 0, 'f'}, + {0, 0, 0, 0} + }; + + //next collect all input (given as options to program) + while(1) { + int option_index = 0; + opt = getopt_long (argc, argv, "qntvicubs:g:d:p:hf:", long_options, &option_index); + + if(opt == -1) break; + + switch (opt) { + case 0: + /* If this option set a flag, do nothing else now. */ + if(long_options[option_index].flag != 0) break; + if(strcmp(long_options[option_index].name, "set") == 0 && optarg) { + test = sscanf(optarg, "%s", sinp); + if(test > 0) { + dataPath = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataPath, sinp); + }; + optflags.mode = 1; + }; + if(strcmp(long_options[option_index].name, "get") == 0 && optarg) { + test = sscanf(optarg, "%s", sinp); + if(test > 0) { + dataPath = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataPath, sinp); + }; + optflags.mode = 0; + }; + if(strcmp(long_options[option_index].name, "data") == 0 && optarg) { + test = sscanf(optarg, "%[^\n]s", sinp); + if(test > 0) { + dataString = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataString, sinp); + }; + }; + if(strcmp(long_options[option_index].name, "type") == 0 && optarg) { + test = sscanf(optarg, "%s", sinp); + if(test > 0) { + dataType = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataType, sinp); + }; + }; + if(strcmp(long_options[option_index].name, "file") == 0 && optarg) { + test = sscanf(optarg, "%[^\n]s", sinp); + if(test > 0) { + configFile = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(configFile, sinp); + }; + }; + break; + case 'q': + optflags.quiet = 1; + break; + case 'n': + optflags.names = 1; + break; + case 't': + optflags.types = 1; + break; + case 'v': + optflags.values = 1; + break; + case 'i': + optflags.indexes = 1; + break; + case 'c': + optflags.counter = 1; + break; + case 'u': + optflags.unset = 1; + break; + case 'b': + optflags.boolstring = 1; + break; + case 's': + if(optarg) { + test = sscanf(optarg, "%s", sinp); + if(test > 0) { + dataPath = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataPath, sinp); + }; + optflags.mode = 1; + }; + break; + case 'g': + if(optarg) { + test = sscanf(optarg, "%s", sinp); + if(test > 0) { + dataPath = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataPath, sinp); + }; + }; + optflags.mode = 0; + break; + case 'd': + if(optarg) { + test = sscanf(optarg, "%[^\n]s", sinp); + if(test > 0) { + dataString = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataString, sinp); + }; + }; + break; + case 'p': + if(optarg) { + test = sscanf(optarg, "%s", sinp); + if(test > 0) { + dataType = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(dataType, sinp); + }; + }; + break; + case 'h': + //free input buffer and printout help message + free(sinp); + printHelp(); //this function contain exit from program + break; + case 'f': + test = sscanf(optarg, "%[^\n]s", sinp); + if(test > 0) { + configFile = (char*)malloc((strlen(sinp)+1)*sizeof(char)); + strcpy(configFile, sinp); + }; + break; + case '?': + break; + default: + break; + } + }; + + //first of all we must ensure, then configuration file are available with right access mode + if(optflags.mode == 0 && access(configFile, R_OK) < 0) optflags.error = 1; + if(optflags.mode == 1) { + fd = open(configFile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if(fd < 0) { + optflags.error = 1; + }; + close(fd); + }; + if(optflags.error > 0) { + if(optflags.quiet == 0) printf(gettext("ERROR! Can't read configuration file.\n")); + free(sinp); + free(configFile); + exit(1); + }; + + //now if we want to set variable, we must have his path + if(optflags.mode == 1 && dataPath == NULL) { + if(optflags.quiet == 0) printf(gettext("ERROR! Conviguration variable path not given.\n")); + free(sinp); + free(configFile); + exit(4); + }; + + //if no output data requested, set to default output + if(optflags.names == 0 && optflags.types == 0 && optflags.values == 0 && optflags.indexes == 0 && optflags.counter == 0) { + optflags.names = 1; + optflags.types = 1; + optflags.values = 1; + }; + + //now we invode main work of this software based on request type (set, unset of get) + if(optflags.mode == 0) excode = read_config(configFile, dataPath, optflags); + if(optflags.mode == 1 && optflags.unset == 1) excode = unset_config(configFile, dataPath, optflags); + if(optflags.mode == 1 && optflags.unset == 0) excode = set_config(configFile, dataPath, optflags, dataString, dataType); + + //then finalize free resources and exit returnig excode + free(sinp); + free(configFile); + exit(excode); +} + |