diff --git a/CMakeLists.txt b/CMakeLists.txt index b5b3461..83079b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,7 @@ add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\") # -l -target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES}) +target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm) set(PCFILE "${CMAKE_BINARY_DIR}/${PROJ}.pc") configure_file("${PROJ}.pc.in" ${PCFILE} @ONLY) @@ -154,7 +154,7 @@ if(NOT DEFINED NOGETTEXT) # we need this to prevent ru.po & .mo from deleting by make clean add_custom_target( RU_FILE - COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} + COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -UiF ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} DEPENDS ${PO_FILE} ${SOURCES} ) add_custom_target( diff --git a/Changelog b/Changelog index 1775ef8..5132dfc 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,12 @@ +Wed Sep 10 14:19:24 MSK 2025 +(still version 0.3.3: I forgot to add changelog last commits) +- fixed minor bugs and memory leaks +- use common way for parsing commandline arguments and content of configuration files, so now you can use arrays in configurations (several parameters with same name) +- add functions: +- - int sl_remove_quotes(char *string) - remove outern quotes (" and '), return amount of pairs found +- - void sl_conf_showhelp(int idx, sl_option_t *options) - show help for config file parameters (only long options, without '--' and without exit(1) at the end of function) +- - void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*)) - now this funtion available for external using, parsing of arguments with user help function + Dec 16 2024 VERSION 0.2.1: diff --git a/config.c b/config.c index 74e2958..1b2d13e 100644 --- a/config.c +++ b/config.c @@ -24,6 +24,28 @@ #include "usefull_macros.h" +/** + * @brief sl_remove_quotes - remove all outern quotes - starting and trailng " and ' + * @param (io) string to modify (if quotes found rest of string will be moved to head, tail will be zeroed) + * @return amount of quotation pair found + */ +int sl_remove_quotes(char *string){ + if(!string) return 0; + int l = strlen(string); + if(l < 2) return 0; + int nq = 0, half = l/2; + for(; nq < half; ++nq){ + char _1st = string[nq]; + if(_1st != string[l-1-nq]) break; + if(_1st != '\'' && _1st != '"') break; + } + if(nq == 0) return 0; + l -= 2 * nq; + memmove(string, string + nq, l); + string[l] = 0; + return nq; +} + /** * @brief sl_get_keyval - get key name and its value from string pair * @param pair - empty string, `key = value` or just `key` @@ -66,6 +88,7 @@ int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN] for(; kend < eq && !isspace(*kend); ++kend); size_t l = SL_KEY_LEN - 1; if(l > (size_t)(kend - kstart)) l = kend - kstart; + else if(l < (size_t)(kend - kstart)) WARNX(_("sl_get_keyval(): key would be trunkated to %d symbols"), l); //DBG("kend=%c, kstart=%c, l=%zd", *kend, *kstart, l); strncpy(key, kstart, l); key[l] = 0; @@ -90,13 +113,74 @@ static int read_key(FILE *file, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){ return r; } -// search `opt` record for given `key` -static sl_option_t *opt_search(const char *key, sl_option_t *options){ - while(options->name){ - if(0 == strcmp(key, options->name)) return options; - ++options; +// print option value +static size_t pr_val(sl_argtype_e type, void *argptr, char **buffer, size_t *buflen, size_t pos){ + size_t maxlen = *buflen - pos - 1; + char *buf = *buffer + pos; + switch(type){ + case arg_none: + case arg_int: + DBG("int %d", *(int*) argptr); + return snprintf(buf, maxlen, "%d", *(int*) argptr); + case arg_longlong: + DBG("long long %lld", *(long long*) argptr); + return snprintf(buf, maxlen, "%lld", *(long long*)argptr); + case arg_float: + DBG("float %g", *(float*) argptr); + return snprintf(buf, maxlen, "%g", *(float*) argptr); + case arg_double: + DBG("double %g", *(double*) argptr); + return snprintf(buf, maxlen, "%g", *(double*) argptr); + case arg_string: + if(!argptr || !(*(char**)argptr)){ + return snprintf(buf, maxlen, "(null)"); + }else if(!(**(char**)argptr)){ + return snprintf(buf, maxlen, "(empty)"); + } + char *str = *(char**) argptr; + DBG("string %s", str); + size_t z = strlen(str); + while(pos + z > *buflen + 5){ + *buflen += BUFSIZ; + *buffer = realloc(*buffer, *buflen); + if(!*buffer) ERRX("realloc()"); + maxlen += BUFSIZ; + buf = *buffer + pos; + } + return snprintf(buf, maxlen, "\"%s\"", str); + default: + DBG("function"); + return snprintf(buf, maxlen, "\"(unsupported)\""); } - return NULL; + return 0; +} + +// print one option +static size_t print_opt(sl_option_t *opt, char **buffer, size_t *buflen, size_t pos){ + if((ssize_t)*buflen - pos < 3 *(SL_KEY_LEN + SL_VAL_LEN)){ + *buflen += BUFSIZ; + *buffer = realloc(*buffer, *buflen); + if(!*buffer) ERR("realloc()"); + } + char *buf = *buffer + pos; + size_t l = 0, maxlen = *buflen - pos - 1; + size_t got = snprintf(buf, maxlen, "%s = ", opt->name); + l = got; maxlen -= got; buf += got; + if(opt->flag){ + DBG("got flag '%d'", *opt->flag); + l += snprintf(buf, maxlen, "%d\n", *opt->flag); + return l; + } + if(!opt->argptr){ // ERR! + l += snprintf(buf, maxlen, "\"(no argptr)\"\n"); + WARNX("Parameter \"%s\" have no argptr!", opt->name); + return l; + } + DBG("type: %d", opt->type); + got = pr_val(opt->type, opt->argptr, buffer, buflen, pos + l); + l += got; maxlen -= got; buf += got; + l += snprintf(buf, maxlen, "\n"); + return l; } /** @@ -108,156 +192,44 @@ static sl_option_t *opt_search(const char *key, sl_option_t *options){ char *sl_print_opts(sl_option_t *opt, int showall){ char *buf = MALLOC(char, BUFSIZ); size_t L = BUFSIZ, l = 0; - for(; opt->name; ++opt){ + for(; opt->help; ++opt){ + if(!opt->name) continue; // only show help - not config option! DBG("check %s", opt->name); if(!showall && opt->has_arg == NO_ARGS) continue; // show NO_ARGS only when `showall==TRUE` if(!showall && opt->type == arg_string && !opt->argptr) continue; // empty string - if((ssize_t)L - l < SL_KEY_LEN + SL_VAL_LEN + 5){ - L += BUFSIZ; - buf = realloc(buf, L); - if(!buf) ERR("realloc()"); - } - l += sprintf(buf + l, "%s=", opt->name); - if(opt->flag){ - DBG("got flag '%d'", *opt->flag); - l += sprintf(buf + l, "%d\n", *opt->flag); - continue; - } - if(!opt->argptr){ // ERR! - l += sprintf(buf + l, "\"(no argptr)\"\n"); - WARNX("Parameter \"%s\" have no argptr!", opt->name); - continue; - } - int z = 0; - DBG("type: %d", opt->type); - switch(opt->type){ - case arg_none: - case arg_int: - DBG("int %d", *(int*) opt->argptr); - l += sprintf(buf + l, "%d", *(int*) opt->argptr); - break; - case arg_longlong: - DBG("long long %lld", *(long long*) opt->argptr); - l += sprintf(buf + l, "%lld", *(long long*) opt->argptr); - break; - case arg_float: - DBG("float %g", *(float*) opt->argptr); - l += sprintf(buf + l, "%g", *(float*) opt->argptr); - break; - case arg_double: - DBG("double %g", *(double*) opt->argptr); - l += sprintf(buf + l, "%g", *(double*) opt->argptr); - break; - case arg_string: - if(!opt->argptr || !(*(char*)opt->argptr)){ - l += sprintf(buf + l, "(null)"); - break; - }else if(!(**(char**)opt->argptr)){ - l += sprintf(buf + l, "(empty)"); - break; - } - DBG("string %s", *(char**) opt->argptr); - z = strlen(*(char**) opt->argptr); - while(l + z > L + 3){ - L += BUFSIZ; - buf = realloc(buf, L); - } - l += sprintf(buf + l, "%s", *(char**) opt->argptr); - break; - default: - DBG("function"); - l += sprintf(buf + l, "\"(unsupported)\""); - break; - } - l += sprintf(buf + l, "\n"); + if(opt->has_arg == MULT_PAR){ + sl_option_t tmpopt = *opt; + DBG("type: %d", tmpopt.type); + if(!opt->argptr){ DBG("No pointer to array!"); continue; } +#if 0 + void ***pp = (void***)opt->argptr; + if(!*(char***)pp){ DBG("Array is empty"); continue; } + while(**pp){ + if(opt->type == arg_string){ + DBG("str"); + tmpopt.argptr = *pp; // string is pointer to pointer! + }else tmpopt.argptr = **pp; + if(!tmpopt.argptr){ DBG("null"); break; } + l += print_opt(&tmpopt, &buf, &L, l); + ++(*pp); + } +#endif + void **pp = *(void***)opt->argptr; + if(!(char**)pp){ DBG("Array is empty"); continue; } + while(*pp){ + if(opt->type == arg_string){ + DBG("str"); + tmpopt.argptr = pp; // string is pointer to pointer! + }else tmpopt.argptr = *pp; + if(!tmpopt.argptr){ DBG("null"); break; } + l += print_opt(&tmpopt, &buf, &L, l); + ++(pp); + } + }else l += print_opt(opt, &buf, &L, l); } return buf; } -/** - * @brief sl_set_optval - convert `val` to `oval` parameter according to argument type - * @param oval (o) - value in int/long long/double/float - * @param opt (i) - record with options - * @param val (i) - new value - * @return FALSE if failed (or wrong data range) - */ -int sl_set_optval(sl_optval *oval, sl_option_t *opt, const char *val){ - if(!oval || !opt || !val) return FALSE; - long long ll; - double d; - switch(opt->type){ - case arg_none: - case arg_int: - case arg_longlong: - do{ - if(!sl_str2ll(&ll, val)){ - break; - } - if(opt->type != arg_longlong){ - if(ll > INT_MAX || ll < INT_MIN){ - break; - } - oval->ival = (int)ll; - }else oval->llval = ll; - return TRUE; - }while(0); - break; - case arg_double: - case arg_float: - do{ - if(!sl_str2d(&d, val)){ - break; - } - if(opt->type == arg_double){ - oval->dval = d; - }else{ - if(d > FLT_MAX || d < FLT_MIN){ - break; - } - oval->fval = (float)d; - } - return TRUE; - }while(0); - break; - case arg_string: - return TRUE; - break; - default: - WARNX(_("Unsupported option type")); - return FALSE; - } - WARNX(_("Wrong number format '%s'"), val); - return FALSE; -} - -// increment opt->argptr or set it to val -static void setoa(sl_option_t *opt, const char *val){ - if(!opt || !opt->argptr || opt->type == arg_function) return; - sl_optval O; - if(!sl_set_optval(&O, opt, val)) return; - switch(opt->type){ - case arg_none: // increment integer - *(int*) opt->argptr += O.ival; - break; - case arg_int: - *(int*) opt->argptr = O.ival; - break; - case arg_longlong: - *(long long*) opt->argptr = O.llval; - break; - case arg_double: - *(double*) opt->argptr = O.dval; - break; - case arg_float: - *(float*) opt->argptr = O.fval; - break; - case arg_string: - *(char**)opt->argptr = strdup(val); - break; - default: - break; - } -} /** * @brief sl_conf_readopts - simplest configuration: @@ -273,29 +245,85 @@ int sl_conf_readopts(const char *filename, sl_option_t *options){ WARN(_("Can't open %s"), filename); return 0; } - int N = 0; - char key[SL_KEY_LEN], val[SL_VAL_LEN]; + int argc = 1; +#define BUFSZ (SL_KEY_LEN+SL_VAL_LEN+8) + char key[SL_KEY_LEN], val[SL_VAL_LEN], obuf[BUFSZ]; + int argvsize = 0; + char **argv = NULL; do{ int r = read_key(f, key, val); if(r < 0) break; if(r == 0) continue; - sl_option_t *opt = opt_search(key, options); - if(!opt){ - WARNX(_("Wrong key: '%s'"), key); - continue; + DBG("key='%s', val='%s'", key, (r == 2) ? val : "(absent)"); + ++argc; + if(argvsize <= argc){ + argvsize += 256; + argv = realloc(argv, sizeof(char*) * argvsize); + if(!argv) ERRX("sl_conf_readopts: realloc() error"); } - if(opt->flag) *opt->flag = opt->val; - if(r == 1){ // only key - if(opt->has_arg != NO_ARGS && opt->has_arg != OPT_ARG){ - WARNX(_("Key '%s' need value"), opt->name); - continue; - } - if(opt->argptr) setoa(opt, "1"); - }else{ // key + value - if(opt->argptr) setoa(opt, val); - else WARNX(_("Key '%s' have no argptr!"), opt->name); - } - ++N; + if(argc == 2) argv[0] = strdup(__progname); // all as should be + if(r == 2){ + // remove trailing/ending quotes + sl_remove_quotes(val); + snprintf(obuf, BUFSZ-1, "--%s=%s", key, val); + }else snprintf(obuf, BUFSZ-1, "--%s", key); + DBG("next argv: '%s'", obuf); + argv[argc-1] = strdup(obuf); }while(1); - return N; + if(!argc) return 0; + int N = argc; char **a = argv; + sl_parseargs_hf(&argc, &a, options, sl_conf_showhelp); + for(int n = 0; n < N; ++n) free(argv[n]); + free(argv); + return N - argc; // amount of recognized options +} + +// sort only by long options +static int confsort(const void *a1, const void *a2){ + const sl_option_t *o1 = (sl_option_t*)a1, *o2 = (sl_option_t*)a2; + const char *l1 = o1->name, *l2 = o2->name; + // move empty options to end of list + if(!l1 && !l2) return 1; + if(!l1) return 1; + if(!l2) return -1; + return strcmp(l1, l2); +} + +static void pr_helpstring(sl_option_t *opt){ + if(!opt->name || !opt->help) return; + printf(" %s", opt->name); + if(opt->has_arg == NEED_ARG || opt->has_arg == MULT_PAR) // required argument + printf(" = arg"); + else if(opt->has_arg == OPT_ARG) // optional argument + printf(" [= arg]"); + printf(" -- %s", opt->help); + if(opt->has_arg == MULT_PAR) printf(" (can occur multiple times)"); + printf("\n"); +} + +/** + * @brief sl_conf_showhelp - show help for config file + * (the same as `sl_showhelp`, but without "--", short opts and exit() + * @param options - config options (only long opts used) + */ +void sl_conf_showhelp(int idx, sl_option_t *options){ + if(!options || !(*options).help) return; + // count amount of options + sl_option_t *opts = options; + int N; for(N = 0; opts->help; ++N, ++opts); + if(N == 0) exit(-2); + if(idx > -1){ + if(idx >=N) WARNX(_("sl_conf_showhelp(): wrong index")); + else pr_helpstring(&options[idx]); + return; + } + sl_option_t *tmpopts = MALLOC(sl_option_t, N); + memcpy(tmpopts, options, N*sizeof(sl_option_t)); + printf(_("Configuration file options (format: key=value):\n")); + qsort(tmpopts, N, sizeof(sl_option_t), confsort); + for(int _ = 0; _ < N; ++_){ + if(!tmpopts[_].name) continue; + pr_helpstring(&tmpopts[_]); + } + free(tmpopts); } diff --git a/examples/cmdlnopts.c b/examples/cmdlnopts.c index 3593503..0d519cd 100644 --- a/examples/cmdlnopts.c +++ b/examples/cmdlnopts.c @@ -50,22 +50,28 @@ static glob_pars const Gdefault = { /* * Define command line options by filling structure: * name has_arg flag val type argptr help + * BE carefull! The `help` field is mandatory! Omitting it equivalent of 'end_option' */ static sl_option_t cmdlnopts[] = { - {"lo0", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo0), _("only long arg 0")}, + // short option in only-long options should be zeroed, or you can add flag to set it to given value + {"lo0", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo0), _("only long arg 0 (int)")}, + // for short-only options long option can be NULL + {NULL, NEED_ARG, NULL, '0', arg_string, APTR(&G.so1), _("only short arg 1 (string)")}, + // if you change `arg_int` to `arg_none`, value will be incremented each `-h` {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, -// {"dup", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name")}, - {"lo2", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo2), _("only long arg 2")}, + // for short-only options long option can also be an empty string + {"", NEED_ARG, NULL, '1', arg_string, APTR(&G.so2), _("only short arg 2 (string)")}, + {"lo2", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo2), _("only long arg 2 (int)")}, {"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("serial device speed (default: 9600)")}, {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs")}, {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, {"exclusive",NO_ARGS, NULL, 'e', arg_int, APTR(&G.exclusive), _("open serial device exclusively")}, // example of multiple options - {"Int", MULT_PAR, NULL, 'I', arg_int, APTR(&G.intarr), _("integer multiplying parameter")}, - {"Dbl", MULT_PAR, NULL, 'D', arg_double, APTR(&G.dblarr), _("double multiplying parameter")}, - {"Str", MULT_PAR, NULL, 'S', arg_string, APTR(&G.strarr), _("string multiplying parameter")}, - {"lo1", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo1), _("only long arg 1")}, + {"Int", MULT_PAR, NULL, 'I', arg_int, APTR(&G.intarr), _("integer parameter")}, + {"Dbl", MULT_PAR, NULL, 'D', arg_double, APTR(&G.dblarr), _("double parameter")}, + {"Str", MULT_PAR, NULL, 'S', arg_string, APTR(&G.strarr), _("string parameter")}, + {"lo1", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo1), _("only long arg 1 (int)")}, end_option }; diff --git a/examples/cmdlnopts.h b/examples/cmdlnopts.h index 7e105f7..03ff099 100644 --- a/examples/cmdlnopts.h +++ b/examples/cmdlnopts.h @@ -38,6 +38,8 @@ typedef struct{ int lo0; // only long options int lo1; int lo2; + char *so1; // only short options + char *so2; int rest_pars_num; // number of rest parameters char** rest_pars; // the rest parameters: array of char* } glob_pars; diff --git a/examples/conffile.c b/examples/conffile.c index 5be72fd..7571446 100644 --- a/examples/conffile.c +++ b/examples/conffile.c @@ -23,12 +23,10 @@ #include "usefull_macros.h" typedef struct{ - char *sp1; - char *sp2; + char **sp; int ip1; int ip2; - double dp1; - double dp2; + double **dp; float fp1; float fp2; int help; @@ -36,66 +34,94 @@ typedef struct{ char *confname; } parameters; -static parameters G = { +static parameters G, parini = { .ip1 = INT_MIN, .ip2 = INT_MIN, - .dp1 = NAN, - .dp2 = NAN, .fp1 = NAN, .fp2 = NAN }; +#define CONFOPTS \ + {"string", MULT_PAR, NULL, 's', arg_string, APTR(&G.sp), "string array"}, \ + {"int1", NEED_ARG, NULL, 'i', arg_int, APTR(&G.ip1), "integer one"}, \ + {"int2", NEED_ARG, NULL, 'u', arg_int, APTR(&G.ip2), "integer two"}, \ + {"double", MULT_PAR, NULL, 'd', arg_double, APTR(&G.dp), "double array"}, \ + {"float1", NEED_ARG, NULL, 'f', arg_float, APTR(&G.fp1), "float one"}, \ + {"float2", NEED_ARG, NULL, 'l', arg_float, APTR(&G.fp2), "float two"}, \ + {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose),"verbose level (each -v adds 1)"}, + static sl_option_t cmdlnopts[] = { {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"}, - {"string1", NEED_ARG, NULL, 's', arg_string, APTR(&G.sp1), "string one"}, - {"string2", NEED_ARG, NULL, 'c', arg_string, APTR(&G.sp2), "string two"}, - {"int1", NEED_ARG, NULL, 'i', arg_int, APTR(&G.ip1), "integer one"}, - {"int2", NEED_ARG, NULL, 'u', arg_int, APTR(&G.ip2), "integer two"}, - {"double1", NEED_ARG, NULL, 'd', arg_double, APTR(&G.dp1), "double one"}, - {"double2", NEED_ARG, NULL, 'o', arg_double, APTR(&G.dp2), "double two"}, - {"float1", NEED_ARG, NULL, 'f', arg_float, APTR(&G.fp1), "float one"}, - {"float2", NEED_ARG, NULL, 'l', arg_float, APTR(&G.fp2), "float two"}, + CONFOPTS {"config", NEED_ARG, NULL, 'C', arg_string, APTR(&G.confname),"name of configuration file"}, - {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose),"verbose level (each -v adds 1)"}, + end_option +}; +// config options - without some unneed +static sl_option_t confopts[] = { + CONFOPTS end_option }; - int main(int argc, char **argv){ sl_init(); + G = parini; sl_parseargs(&argc, &argv, cmdlnopts); if(G.help) sl_showhelp(-1, cmdlnopts); + // if you will end main options with '--', you can write some additional options after and again run sl_parseargs with other sl_option_t array + if(argc) for(int i = 0; i < argc; ++i){ + red("Extra arg: `%s`\n", argv[i]); + } sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR; if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1; printf("verbose level: %d\n", lvl); - if(G.sp1){ - printf("Parsing of string1: "); - char key[SL_KEY_LEN], val[SL_VAL_LEN]; - int k = sl_get_keyval(G.sp1, key, val); - switch(k){ - case 0: - red("key not found\n"); - break; - case 1: - green("got key='%s'\n", key); - break; - default: - green("got key='%s', value='%s'\n", key, val); + if(G.sp){ + char **s = G.sp; + while(*s){ + printf("Parsing of string: "); + char key[SL_KEY_LEN], val[SL_VAL_LEN]; + int k = sl_get_keyval(*s, key, val); + switch(k){ + case 0: + red("key not found\n"); + break; + case 1: + green("got key='%s'\n", key); + break; + default: + green("got key='%s', value='%s'\n", key, val); + } + ++s; } } green("Starting parameters values:\n"); char *buf = sl_print_opts(cmdlnopts, TRUE); printf("%s\n", buf); - FREE(buf); + FREE(buf); // don't forget to `free` this buffer if(G.confname){ - int o = sl_conf_readopts(G.confname, cmdlnopts); + const char *confname = G.confname; + G = parini; + printf("now v=%d\n", G.verbose); + int o = sl_conf_readopts(confname, confopts); if(o > 0){ - printf("got %d options in '%s'\n", o, G.confname); + printf("got %d options in '%s'\n", o, confname); green("And after reading of conffile:\n"); - buf = sl_print_opts(cmdlnopts, TRUE); + buf = sl_print_opts(confopts, TRUE); printf("%s\n", buf); FREE(buf); } + // if we want to re-read conffile many times over program runs, don't forget to `free` old arrays like this: + if(G.dp){ + DBG("Clear double array"); + double **p = G.dp; + while(*p){ FREE(*p); ++p; } + FREE(G.dp); + } + if(G.sp){ + DBG("Clear string array %s", *G.sp); + char **s = G.sp; + while(*s){ FREE(*s); ++s; } + FREE(G.sp); + } } return 0; } diff --git a/examples/options.c b/examples/options.c index 630477d..bf355b7 100644 --- a/examples/options.c +++ b/examples/options.c @@ -67,12 +67,6 @@ int main(int argc, char *argv[]){ } sl_check4running((char*)__progname, GP->pidfile); red("%s started, snippets library version is %s\n", __progname, sl_libversion()); - sl_setup_con(); - signal(SIGTERM, signals); // kill (-15) - quit - signal(SIGHUP, SIG_IGN); // hup - ignore - signal(SIGINT, signals); // ctrl+C - quit - signal(SIGQUIT, signals); // ctrl+\ - quit - signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z if(GP->logfile) OPENLOG(GP->logfile, LOGLEVEL_ANY, 1); LOGMSG("Start application..."); if(GP->rest_pars_num){ @@ -89,11 +83,22 @@ int main(int argc, char *argv[]){ } if(GP->strarr){ char **p = GP->strarr; - for(int i = 0; *p; ++i) printf("String[%d]: \"%s\"\n", i, *p++); + for(int i = 0; *p; ++i){ + sl_remove_quotes(*p); + printf("String[%d]: \"%s\"\n", i, *p++); + } } if(GP->lo0 != INT_MIN) printf("You set lo0 to %d\n", GP->lo0); if(GP->lo1 != INT_MIN) printf("You set lo1 to %d\n", GP->lo1); if(GP->lo2 != INT_MIN) printf("You set lo2 to %d\n", GP->lo2); + if(GP->so1){ + sl_remove_quotes(GP->so1); + printf("String so1=%s\n", GP->so1); + } + if(GP->so2){ + sl_remove_quotes(GP->so2); + printf("String so2=%s\n", GP->so2); + } if(GP->device){ LOGDBG("Try to open serial %s", GP->device); dev = sl_tty_new(GP->device, GP->speed, 4096); @@ -103,7 +108,13 @@ int main(int argc, char *argv[]){ signals(0); } } - + if(!dev) return 0; + sl_setup_con(); + signal(SIGTERM, signals); // kill (-15) - quit + signal(SIGHUP, SIG_IGN); // hup - ignore + signal(SIGINT, signals); // ctrl+C - quit + signal(SIGQUIT, signals); // ctrl+\ - quit + signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z // main stuff goes here long seed = sl_random_seed(); green("Now I will sleep for 10 seconds after your last input.\n Do whatever you want. Random seed: %ld\n", seed); diff --git a/locale/ru/LC_MESSAGES/usefull_macros.mo b/locale/ru/LC_MESSAGES/usefull_macros.mo index 4ca8b88..b302ff2 100644 Binary files a/locale/ru/LC_MESSAGES/usefull_macros.mo and b/locale/ru/LC_MESSAGES/usefull_macros.mo differ diff --git a/locale/ru/messages.po b/locale/ru/messages.po index 8246731..5701fe6 100644 --- a/locale/ru/messages.po +++ b/locale/ru/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-06-27 11:51+0300\n" +"POT-Creation-Date: 2025-09-10 12:08+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,33 +17,23 @@ msgstr "" "Content-Type: text/plain; charset=koi8-r\n" "Content-Transfer-Encoding: 8bit\n" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226 -msgid "Unsupported option type" -msgstr "" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:91 #, c-format -msgid "Wrong number format '%s'" +msgid "sl_get_keyval(): key would be trunkated to %d symbols" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:245 #, c-format msgid "Can't open %s" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284 -#, c-format -msgid "Wrong key: '%s'" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:316 +msgid "sl_conf_showhelp(): wrong index" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322 #, c-format -msgid "Key '%s' need value" -msgstr "" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296 -#, c-format -msgid "Key '%s' have no argptr!" +msgid "Configuration file options (format: key=value):\n" msgstr "" #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69 @@ -71,7 +61,19 @@ msgstr "" msgid "Integer out of range" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166 +#. `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked +#. not found +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:129 +#, c-format +msgid "No such parameter: `%s`\n" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:138 +#, c-format +msgid "Parameter `%s` needs value\n" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:163 msgid "Can't use multiple args with arg_none!" msgstr "" @@ -85,22 +87,32 @@ msgstr "" msgid "double short arguments: -%c" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:343 #, c-format msgid "Need argument with %s type\n" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486 +#. print only one message +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:435 +msgid "sl_showhelp(): option index out of range" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:441 +#, c-format +msgid "Usage: %s [arguments]\n" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:528 #, c-format msgid "Wrong parameter: %s" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:532 #, c-format msgid "%s: need argument!" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:536 #, c-format msgid "Wrong argument \"%s\" of parameter \"%s\"" msgstr "" @@ -109,35 +121,35 @@ msgstr "" msgid "Server disconnected" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:385 msgid "Can't start server handlers thread" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:454 msgid "Limit of connections reached" msgstr "" #. check for RB overflow #. -1 - buffer empty (can't be), -2 - buffer overflow -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503 -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:498 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:499 #, c-format msgid "Server thread: ring buffer overflow for fd=%d" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:513 #, c-format msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d" msgstr "" #. buffer overflow -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:525 #, c-format msgid "Server thread: buffer overflow from fd=%d" msgstr "" #. never reached -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:615 #, c-format msgid "Unsupported socket type %d" msgstr "" @@ -215,18 +227,3 @@ msgstr "" #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347 msgid "Can't setup console" msgstr "" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403 -#, c-format -msgid "Wrong double number format '%s'" -msgstr "" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416 -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428 -#, c-format -msgid "Wrong integer number format '%s'" -msgstr "" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432 -msgid "Number out of integer limits" -msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po index 8565b7a..fbadb83 100644 --- a/locale/ru/ru.po +++ b/locale/ru/ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2025-06-27 11:49+0300\n" + "POT-Creation-Date: 2025-09-10 12:08+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,6 +16,25 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n" "Content-Type: text/plain; charset=koi8-r\n" "Content-Transfer-Encoding: 8bit\n" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:91 +#, c-format +msgid "sl_get_keyval(): key would be trunkated to %d symbols" +msgstr "sl_get_keyval(): ключ будет обрезан до %d символов" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:245 +#, c-format +msgid "Can't open %s" +msgstr "Не могу открыть %s" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:316 +msgid "sl_conf_showhelp(): wrong index" +msgstr "sl_conf_showhelp(): неверный индекс" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322 +#, c-format +msgid "Configuration file options (format: key=value):\n" +msgstr "" + #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69 #, c-format msgid "\n" @@ -23,159 +42,114 @@ msgid "\n" msgstr "\n" "Обнаружен одноименный процесс (pid=%d), выход.\n" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490 -#, c-format -msgid "%s: need argument!" -msgstr "%s: необходим аргумент!" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119 -msgid "Can't apply new TTY settings" -msgstr "Не могу сменить настройки порта" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266 -msgid "Can't close mmap'ed file" -msgstr "Не могу закрыть mmap'нутый файл" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130 -msgid "Can't do exclusive open" -msgstr "Не могу перевести порт в эксклюзивный режим" - -#. Get settings -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106 -msgid "Can't get old TTY settings" -msgstr "Не могу получить действующие настройки порта" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279 -msgid "Can't munmap" -msgstr "Не могу munmap" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273 -#, c-format -msgid "Can't open %s" -msgstr "Не могу открыть %s" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252 -#, c-format -msgid "Can't open %s for reading" -msgstr "Не могу открыть %s для чтения" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194 -msgid "Can't open /dev/random" -msgstr "Не могу открыть /dev/random" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141 -msgid "Can't open PID file" -msgstr "Не могу открыть PID файл" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198 -msgid "Can't read /dev/random" -msgstr "Не могу прочесть /dev/random" - #. error reading self name #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:117 msgid "Can't read self name" msgstr "Не могу прочесть имя процесса" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124 -#, c-format -msgid "Can't set speed %d, got ispeed=%d, ospeed=%d" -msgstr "Не могу сменить скорость на %d; ispeed=%d, ospeed=%d" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141 +msgid "Can't open PID file" +msgstr "Не могу открыть PID файл" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347 -msgid "Can't setup console" -msgstr "Не могу настроить консоль" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390 -msgid "Can't start server handlers thread" -msgstr "Не могу запустить поток-обработчик сервера" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256 -#, c-format -msgid "Can't stat %s" -msgstr "Не могу выполнить stat %s" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166 -msgid "Can't use multiple args with arg_none!" -msgstr "Массив параметров не может иметь тип arg_none!" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102 -#, c-format -msgid "Can't use port %s" -msgstr "Не могу использовать порт %s" +#. amount of pcount and/or scount wrong +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53 +msgid "Wrong helpstring!" +msgstr "Неправильный формат строки помощи" #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:82 msgid "Integer out of range" msgstr "Целое вне допустимого диапазона" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296 +#. `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked +#. not found +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:129 #, c-format -msgid "Key '%s' have no argptr!" -msgstr "У ключа '%s' нет указателя на переменную!" +msgid "No such parameter: `%s`\n" +msgstr "Нет такого параметра: %s\n" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:138 #, c-format -msgid "Key '%s' need value" -msgstr "Ключ '%s' требует значения" +msgid "Parameter `%s` needs value\n" +msgstr "Ключ '%s' требует значения\n" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459 -msgid "Limit of connections reached" -msgstr "Достигнут предел соединений" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:163 +msgid "Can't use multiple args with arg_none!" +msgstr "Массив параметров не может иметь тип arg_none!" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262 -msgid "Mmap error for input" -msgstr "Ошибка mmap" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267 +#, c-format +msgid "double long arguments: --%s" +msgstr "дублирующийся длинный параметр: --%s" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273 +#, c-format +msgid "double short arguments: -%c" +msgstr "дублирующийся короткий параметр: -%c" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:343 #, c-format msgid "Need argument with %s type\n" msgstr "Необходим аргумент с типом %s\n" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168 -msgid "Need non-zero buffer for TTY device" -msgstr "Для последовательного устройства требуется буфер ненулевого размера" +#. print only one message +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:435 +msgid "sl_showhelp(): option index out of range" +msgstr "sl_showhelp(): индекс опции вне допустимого диапазона" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248 -msgid "No filename given!" -msgstr "Не задано имя файла!" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:441 +#, c-format +msgid "Usage: %s [arguments]\n" +msgstr "Использование: %s [аргументы]\n" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432 -msgid "Number out of integer limits" -msgstr "Число за пределами int" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:528 +#, c-format +msgid "Wrong parameter: %s" +msgstr "Неправильный параметр: %s" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162 -msgid "Port name is missing" -msgstr "Отсутствует имя порта" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:532 +#, c-format +msgid "%s: need argument!" +msgstr "%s: необходим аргумент!" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:536 +#, c-format +msgid "Wrong argument \"%s\" of parameter \"%s\"" +msgstr "Неправильный аргумент \"%s\" параметра \"%s\"" #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147 msgid "Server disconnected" msgstr "Сервер отключен" -#. buffer overflow -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530 -#, c-format -msgid "Server thread: buffer overflow from fd=%d" -msgstr "Поток сервера: переполнение буфера от fd=%d" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:385 +msgid "Can't start server handlers thread" +msgstr "Не могу запустить поток-обработчик сервера" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518 -#, fuzzy, c-format -msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d" -msgstr "Поток сервера: не могу сохранить данные в кольцевом буфере, " - "переполнение от fd=%d" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:454 +msgid "Limit of connections reached" +msgstr "Достигнут предел соединений" #. check for RB overflow #. -1 - buffer empty (can't be), -2 - buffer overflow -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503 -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:498 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:499 #, c-format msgid "Server thread: ring buffer overflow for fd=%d" msgstr "Поток сервера: переполнение буфера от fd=%d" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226 -msgid "Unsupported option type" -msgstr "Неподдерживаемый тип опции" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:513 +#, c-format +msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d" +msgstr "Поток сервера: не могу сохранить данные в кольцевом буфере, " + "переполнение от fd=%d" + +#. buffer overflow +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:525 +#, c-format +msgid "Server thread: buffer overflow from fd=%d" +msgstr "Поток сервера: переполнение буфера от fd=%d" #. never reached -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:615 #, c-format msgid "Unsupported socket type %d" msgstr "Неподдерживаемый тип сокета %d" @@ -187,48 +161,71 @@ msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: " msgstr "Неправильный формат USART \"%s\"; нужен NPS, где N: 5..8; P: N/E/O/" "1/0, S: 1/2" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102 #, c-format -msgid "Wrong argument \"%s\" of parameter \"%s\"" -msgstr "Неправильный аргумент \"%s\" параметра \"%s\"" +msgid "Can't use port %s" +msgstr "Не могу использовать порт %s" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403 +#. Get settings +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106 +msgid "Can't get old TTY settings" +msgstr "Не могу получить действующие настройки порта" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119 +msgid "Can't apply new TTY settings" +msgstr "Не могу сменить настройки порта" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124 #, c-format -msgid "Wrong double number format '%s'" -msgstr "Неправильный формат double: %s" +msgid "Can't set speed %d, got ispeed=%d, ospeed=%d" +msgstr "Не могу сменить скорость на %d; ispeed=%d, ospeed=%d" -#. amount of pcount and/or scount wrong -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53 -msgid "Wrong helpstring!" -msgstr "Неправильный формат строки помощи" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130 +msgid "Can't do exclusive open" +msgstr "Не могу перевести порт в эксклюзивный режим" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416 -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162 +msgid "Port name is missing" +msgstr "Отсутствует имя порта" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168 +msgid "Need non-zero buffer for TTY device" +msgstr "Для последовательного устройства требуется буфер ненулевого размера" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194 +msgid "Can't open /dev/random" +msgstr "Не могу открыть /dev/random" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198 +msgid "Can't read /dev/random" +msgstr "Не могу прочесть /dev/random" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248 +msgid "No filename given!" +msgstr "Не задано имя файла!" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252 #, c-format -msgid "Wrong integer number format '%s'" -msgstr "Неправильный формат целого: %s" +msgid "Can't open %s for reading" +msgstr "Не могу открыть %s для чтения" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256 #, c-format -msgid "Wrong key: '%s'" -msgstr "Неправильный ключ: %s" +msgid "Can't stat %s" +msgstr "Не могу выполнить stat %s" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229 -#, c-format -msgid "Wrong number format '%s'" -msgstr "Неправильный формат числа: %s" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262 +msgid "Mmap error for input" +msgstr "Ошибка mmap" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486 -#, c-format -msgid "Wrong parameter: %s" -msgstr "Неправильный параметр: %s" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266 +msgid "Can't close mmap'ed file" +msgstr "Не могу закрыть mmap'нутый файл" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267 -#, c-format -msgid "double long arguments: --%s" -msgstr "дублирующийся длинный параметр: --%s" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279 +msgid "Can't munmap" +msgstr "Не могу munmap" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273 -#, c-format -msgid "double short arguments: -%c" -msgstr "дублирующийся короткий параметр: -%c" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347 +msgid "Can't setup console" +msgstr "Не могу настроить консоль" diff --git a/parseargs.c b/parseargs.c index 263e8e8..8e4b7f7 100644 --- a/parseargs.c +++ b/parseargs.c @@ -30,7 +30,7 @@ #include // isalpha #include "usefull_macros.h" -char *helpstring = "%s\n"; +const char *helpstring = NULL; // will be inited later, can't init with gettext on this stage /** * @brief sl_helpstring - change standard help header @@ -115,36 +115,33 @@ static int myatod(void *num, const char *str, sl_argtype_e t){ /** * @brief get_optind - get index of current option in array options + * @param key (i) - original key (short or long) * @param opt (i) - returning val of getopt_long * @param options (i) - array of options * @return index in array */ -static int get_optind(int opt, sl_option_t *options){ - int oind; +static int get_optind(const char *key, int opt, sl_option_t *options, void (*helpfun)(int, sl_option_t*)){ + int oind = 0, theopt = opt; sl_option_t *opts = options; assert(opts); - for(oind = 0; opts->name && opts->val != opt; oind++, opts++){ - DBG("cmp %c and %c", opt, opts->val); + // `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked + if(opt == '?'){ // not found + fprintf(stderr, _("No such parameter: `%s`\n"), key); + helpfun(-1, options); + return -1; // never reached until `helpfun` changed + }else if(opt == ':') theopt = optopt; // search to show helpstring "need parameter" + for(oind = 0; opts->help && opts->val != theopt; oind++, opts++){ + DBG("cmp %c and %c", theopt, opts->val); + } + if(!opts->help) return -1; + if(opt == ':'){ + fprintf(stderr, _("Parameter `%s` needs value\n"), key); + helpfun(oind, options); + return -1; // never reached until `helpfun` changed } - if(!opts->name || opts->val != opt) // no such parameter - sl_showhelp(-1, options); return oind; } -#if 0 -static int get_optindl(struct option *opt, sl_option_t *options){ - int oind; - sl_option_t *opts = options; - assert(opts); - for(oind = 0; opts->name; ){ - DBG("cmp '%s' and '%s'", opt->name, opts->name); - if(0 == strcmp(opt->name, opts->name)) break; - oind++, opts++; - } - if(!opts->name || strcmp(opt->name, opts->name)) // no such parameter - sl_showhelp(-1, options); - return oind; -} -#endif + /** * @brief get_aptr - reallocate new value in array of multiple repeating arguments @@ -194,41 +191,38 @@ void *get_aptr(void *paptr, sl_argtype_e type){ return aptr[i - 1]; } - /** - * @brief sl_parseargs - parse command line arguments - * ! If arg is string, then value will be strdup'ed! - * - * @param argc (io) - address of argc of main(), return value of argc stay after `getopt` - * @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt` - * BE CAREFUL! if you wanna use full argc & argv, save their original values before - * calling this function - * @param options (i) - array of `myoption` for arguments parcing - * - * @exit: in case of error this function show help & make `exit(-1)` - */ -void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ + * @brief sl_parseargs_hf - parse arguments with user help funtion + * @param argc - amount of arguments + * @param argv - arguments + * @param options - array with opts + * @param helpfun - function called in case of wrong arg + */ +void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*)){ char *short_options, *soptr; struct option *long_options, *loptr; - size_t optsize, i; + int optsize = 0; sl_option_t *opts = options; // check whether there is at least one options assert(opts); - assert(opts[0].name); + //assert(opts[0].name); // first we count how much values are in opts - for(optsize = 0; opts->name; optsize++, opts++); + for(optsize = 0; opts->help; optsize++, opts++); // now we can allocate memory - short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts + // TODO: FREE all unneeded memory at end of function + short_options = calloc(optsize * 3 + 2, 1); // multiply by three for '::' in case of args in opts, add 1 for starting ':' long_options = calloc(optsize + 1, sizeof(struct option)); opts = options; loptr = long_options; soptr = short_options; + *soptr++ = ':'; // check the parameters are not repeated char **longlist = MALLOC(char*, optsize); char *shortlist = MALLOC(char, optsize); // fill short/long parameters and make a simple checking - for(i = 0; i < optsize; i++, loptr++, opts++){ + for(int i = 0; i < optsize; i++, loptr++, opts++){ // check - assert(opts->name); // check name - longlist[i] = strdup(opts->name); + //assert(opts->name); // check name + DBG("opts: val=%c, name=%s", opts->val, opts->name); + longlist[i] = opts->name ? strdup(opts->name) : NULL; if(opts->has_arg){ assert(opts->type != arg_none); // check error with arg type assert(opts->argptr); // check pointer @@ -237,7 +231,7 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ assert(opts->argptr); // fill long_options // don't do memcmp: what if there would be different alignment? - loptr->name = opts->name; + loptr->name = opts->name ? opts->name : ""; loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1; loptr->flag = opts->flag; loptr->val = opts->val; @@ -253,7 +247,12 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ } // sort all lists & check for repeating int cmpstringp(const void *p1, const void *p2){ - return strcmp(* (char * const *) p1, * (char * const *) p2); + if(!p1 || !p2) return 0; + const char *str1 = * (char * const *) p1, *str2 = * (char * const *) p2; + if(!str1 && !str2) return 0; + else if(!str1) return 1; + else if(!str2) return -1; + return strcmp(str1, str2); } int cmpcharp(const void *p1, const void *p2){ return (int)(*(char * const)p1 - *(char *const)p2); @@ -261,8 +260,9 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ qsort(longlist, optsize, sizeof(char *), cmpstringp); qsort(shortlist,optsize, sizeof(char), cmpcharp); char *prevl = longlist[0], prevshrt = shortlist[0]; - for(i = 1; i < optsize; ++i){ - if(longlist[i]){ + // check for repeated args + for(int i = 1; i < optsize; ++i){ + if(longlist[i] && *longlist[i]){ if(prevl){ if(strcmp(prevl, longlist[i]) == 0) ERRX(_("double long arguments: --%s"), prevl); } @@ -275,21 +275,32 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ prevshrt = shortlist[i]; } } + FREE(longlist); FREE(shortlist); +#ifdef EBUG + DBG("Argc=%d, argv[0]=%s, argv[1]=%s, short=%s", *argc, (*argv)[0], (*argv)[1], short_options); + for(int _ = 0; _ <= optsize; ++_) fprintf(stderr, "\tlo[%d]='%s'\n", _, long_options[_].name); + DBG("AND argv:"); + for(int _ = 0; _ < *argc; ++_) fprintf(stderr, "\t[%d]='%s'\n", _ ,(*argv)[_]); +#endif + // reinit global `optind` for ability of sequentional run + optind = 1; // now we have both long_options & short_options and can parse `getopt_long` while(1){ int opt; - int /*oindex = -1,*/ optind = -1; // oindex - number of option in long_options, optind - number in options[] - if((opt = getopt_long(*argc, *argv, short_options, long_options, &optind)) == -1) break; - DBG("%c(%d) = getopt_long(argc, argv, %s, long_options, &%d); optopt=%c(%d), errno=%d", opt, opt, short_options, optind, optopt, optopt, errno); - if(optind < 0 ) optind = get_optind(opt, options); // find short option -> need to know index of long + int /*oindex = -1,*/ loptind = -1; // oindex - number of option in long_options, optind - number in options[] + DBG("optind=%d", optind); + const char *curopt = (*argv)[optind]; + if((opt = getopt_long(*argc, *argv, short_options, long_options, &loptind)) == -1) break; + DBG("search `%s`, %c(%d) = getopt_long(argc, argv, %s, long_options, &%d); optopt=%c(%d), errno=%d", curopt, opt, opt, short_options, loptind, optopt, optopt, errno); + if(loptind < 0 ) loptind = get_optind(curopt, opt, options, helpfun); // find short option -> need to know index of long + if(loptind < 0 || loptind >= optsize) continue; // be careful with "-?" flag: all wrong or ambiguous flags will be interpreted as this! - DBG("index=%d", optind); - opts = &options[optind]; - DBG("Got option %s", opts->name); - + DBG("index=%d", loptind); + opts = &options[loptind]; + DBG("Got option %s (%c)", opts->name, opts->val); // now check option if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR) - if(!optarg) sl_showhelp(optind, options); // need argument + if(!optarg) helpfun(loptind, options); // need argument void *aptr; if(opts->has_arg == MULT_PAR){ aptr = get_aptr(opts->argptr, opts->type); @@ -303,39 +314,55 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ default: case arg_none: if(opts->argptr) *((int*)aptr) += 1; // increment value - break; + break; case arg_int: result = myatoll(aptr, optarg, arg_int); type = "integer"; - break; + break; case arg_longlong: result = myatoll(aptr, optarg, arg_longlong); type = "long long"; - break; + break; case arg_double: result = myatod(aptr, optarg, arg_double); type = "double"; - break; + break; case arg_float: result = myatod(aptr, optarg, arg_float); type = "float"; - break; + break; case arg_string: result = (*((void**)aptr) = (void*)strdup(optarg)) != NULL; type = "string"; - break; + break; case arg_function: result = ((sl_argfn_t)aptr)(optarg); - break; + break; } if(!result){ if(type) fprintf(stderr, _("Need argument with %s type\n"), type); - sl_showhelp(optind, options); + helpfun(loptind, options); } } + FREE(short_options); FREE(long_options); *argc -= optind; *argv += optind; } +/** + * @brief sl_parseargs - parse command line arguments + * ! If arg is string, then value will be strdup'ed! + * + * @param argc (io) - address of argc of main(), return value of argc stay after `getopt` + * @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt` + * BE CAREFUL! if you wanna use full argc & argv, save their original values before + * calling this function + * @param options (i) - array of `myoption` for arguments parcing + * + * @exit: in case of error this function show help & make `exit(-1)` + */ +void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ + sl_parseargs_hf(argc, argv, options, sl_showhelp); +} /** * @brief argsort - compare function for qsort @@ -350,6 +377,7 @@ static int argsort(const void *a1, const void *a2){ if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg return (s1 - s2); }else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long + assert(l1); assert(l2); // no way to omit long option if short is absent return strcmp(l1, l2); }else{ // only one have short arg -- return it if(f2 || !s2) return -1; // a1 have short - it is 'lesser' @@ -357,6 +385,34 @@ static int argsort(const void *a1, const void *a2){ } } +// print one string of help +static void pr_helpstring(sl_option_t *opt, char *buf, int indent, size_t bufsz){ + size_t p = sprintf(buf, " "); // a little indent + int havelongopt = opt->name && *opt->name; + if(!opt->flag && opt->val){ // .val is short argument + p += snprintf(buf+p, bufsz-p, "-%c", opt->val); + if(havelongopt) p += snprintf(buf+p, bufsz-p, ", "); // show comma only it there's shor arg + } + if(havelongopt){ + p += snprintf(buf+p, bufsz-p, "--%s", opt->name); + if(opt->has_arg == NEED_ARG || opt->has_arg == MULT_PAR) // required argument + p += snprintf(buf+p, bufsz-p, "=arg"); + else if(opt->has_arg == OPT_ARG) // optional argument + p += snprintf(buf+p, bufsz-p, "[=arg]"); + }else{ + if(opt->has_arg == NEED_ARG || opt->has_arg == MULT_PAR) + p += snprintf(buf+p, bufsz-p, " arg"); + else if(opt->has_arg == OPT_ARG) + p += snprintf(buf+p, bufsz-p, " [arg]"); + } + if(indent > 0){ + assert(p < (size_t)indent); + printf("%-*s%s", indent+1, buf, _(opt->help)); // write options & at least 2 spaces after + }else printf("%s %s", buf, _(opt->help)); + if(opt->has_arg == MULT_PAR) printf(" (can occur multiple times)"); + printf("\n"); +} + /** * @brief sl_showhelp - show help information based on sl_option_t->help values * @param oindex (i) - if non-negative, show only help by sl_option_t[oindex].help @@ -370,49 +426,35 @@ void sl_showhelp(int oindex, sl_option_t *options){ char buf[bufsz+1]; sl_option_t *opts = options; assert(opts); - assert(opts[0].name); // check whether there is at least one options + assert(opts[0].help); // check whether there is at least one options + // count amount of options + int N; for(N = 0; opts->help; ++N, ++opts); + if(N == 0) exit(-2); + DBG("got %d options, oindex=%d", N, oindex); if(oindex > -1){ // print only one message - opts = &options[oindex]; - printf(" "); - if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val); - printf("--%s", opts->name); - if(opts->has_arg == 1) printf("=arg"); - else if(opts->has_arg == 2) printf("[=arg]"); - printf(" %s\n", _(opts->help)); + if(oindex >= N || oindex < 0) ERRX(_("sl_showhelp(): option index out of range")); + pr_helpstring(&options[oindex], buf, 0, bufsz); exit(-1); } // header, by default is just "progname\n" printf("\n"); + if(!helpstring) helpstring = _("Usage: %s [arguments]\n"); if(strstr(helpstring, "%s")) // print progname printf(helpstring, __progname); else // only text printf("%s", helpstring); printf("\n"); // count max_opt_len - do{ - int L = strlen(opts->name); + for(int _ = 0; _ < N; ++_){ + if(!options[_].name) continue; + int L = strlen(options[_].name); if(max_opt_len < L) max_opt_len = L; - }while((++opts)->name); - max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols - opts = options; - // count amount of options - int N; for(N = 0; opts->name; ++N, ++opts); - if(N == 0) exit(-2); + } + max_opt_len += 14; // format: '-S, --long[=arg]' - get addition 13 symbols // Now print all help (sorted) - opts = options; - qsort(opts, N, sizeof(sl_option_t), argsort); + qsort(options, N, sizeof(sl_option_t), argsort); do{ - int p = sprintf(buf, " "); // a little indent - if(!opts->flag && opts->val) // .val is short argument - p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val); - p += snprintf(buf+p, bufsz-p, "--%s", opts->name); - if(opts->has_arg == 1) // required argument - p += snprintf(buf+p, bufsz-p, "=arg"); - else if(opts->has_arg == 2) // optional argument - p += snprintf(buf+p, bufsz-p, "[=arg]"); - assert(p < max_opt_len); // there would be magic if p >= max_opt_len - printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after - ++opts; + pr_helpstring(options++, buf, max_opt_len, bufsz); }while(--N); printf("\n\n"); exit(-1); diff --git a/usefull_macros.h b/usefull_macros.h index 15c578a..3a5ecd8 100644 --- a/usefull_macros.h +++ b/usefull_macros.h @@ -326,6 +326,7 @@ typedef struct{ extern const char *__progname; void sl_showhelp(int oindex, sl_option_t *options); +void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*)); void sl_parseargs(int *argc, char ***argv, sl_option_t *options); /** * @brief sl_helpstring - change standard help header @@ -374,16 +375,18 @@ void *sl_list_pop(sl_list_t **lst); #define SL_COMMENT_CHAR '#' // option or simple configuration value (don't work for functions) -typedef struct{ +/*typedef struct{ union{ int ival; long long llval; double dval; float fval; }; sl_argtype_e type; -} sl_optval; +} sl_optval;*/ int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]); char *sl_print_opts(sl_option_t *opt, int showall); int sl_conf_readopts(const char *filename, sl_option_t *options); +void sl_conf_showhelp(int idx, sl_option_t *options); +int sl_remove_quotes(char *string); /******************************************************************************\ The original ringbuffer.h