From e77fddc3b862cbfa4856db39326514c6f2379c91 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Fri, 15 Nov 2024 11:42:29 +0300 Subject: [PATCH] version 0.2.1 --- CMake.readme | 2 + CMakeLists.txt | 14 +- Changelog | 49 ++++ TODO | 1 - config.c | 300 ++++++++++++++++++++++++ daemon.c | 22 +- examples/CMakeLists.txt | 1 + examples/cmdlnopts.c | 21 +- examples/cmdlnopts.h | 8 +- examples/conffile.c | 101 ++++++++ examples/fifo.c | 12 +- examples/helloworld.c | 8 +- examples/options.c | 67 +++--- fifo_lifo.c | 72 +++--- locale/ru/LC_MESSAGES/usefull_macros.mo | Bin 2478 -> 4190 bytes locale/ru/messages.po | 135 ++++++----- locale/ru/ru.po | 155 ++++++------ parseargs.c | 124 ++++++---- term.c | 50 ++-- term2.c | 264 +++++++++++++++++++++ usefull_macros.c | 224 +++++++++--------- usefull_macros.h | 202 +++++++++------- 22 files changed, 1337 insertions(+), 495 deletions(-) create mode 100644 config.c create mode 100644 examples/conffile.c create mode 100644 term2.c diff --git a/CMake.readme b/CMake.readme index e7c8ccd..6d4bbf7 100644 --- a/CMake.readme +++ b/CMake.readme @@ -2,3 +2,5 @@ cmake defines: -DDEBUG=1 - debug mode -DNOGETTEXT=1 - don't run xgettext -DEXAMPLES=1 - to compile examples +-DSL_USE_OLD_TTY - use old termios instead of termios2 (you should use this flag always if library works so) + diff --git a/CMakeLists.txt b/CMakeLists.txt index f5f859e..31c9332 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.9) set(PROJ usefull_macros) set(MINOR_VERSION "1") -set(MID_VERSION "1") +set(MID_VERSION "2") set(MAJOR_VERSION "0") set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") @@ -51,6 +51,16 @@ message("Build type: ${CMAKE_BUILD_TYPE}") # here is one of two variants: all .c in directory or .c files in list aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) +get_filename_component(term_c ${CMAKE_CURRENT_SOURCE_DIR}/term.c ABSOLUTE) +if(DEFINED USE_OLD_TTY OR DEFINED SL_USE_OLD_TTY) + get_filename_component(term_c ${CMAKE_CURRENT_SOURCE_DIR}/term2.c ABSOLUTE) + add_definitions(-DSL_USE_OLD_TTY) +endif() + +message("remove ${term_c}") +list(REMOVE_ITEM SOURCES ${term_c}) + + # directory should contain dir locale/ru for gettext translations set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru) if(NOT DEFINED LOCALEDIR) @@ -136,7 +146,7 @@ if(NOT DEFINED NOGETTEXT) add_custom_command( OUTPUT ${PO_FILE} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=utf-8 ${SOURCES} -c -k_ -kN_ -o ${PO_FILE} + COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=koi8-r ${SOURCES} -c -k_ -kN_ -o ${PO_FILE} COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE} COMMAND enconv ${PO_FILE} DEPENDS ${SOURCES} diff --git a/Changelog b/Changelog index 8b3dfa6..7d851e9 100644 --- a/Changelog +++ b/Changelog @@ -5,3 +5,52 @@ add tty_timeout() fix read_tty() for disconnect add logging add sl_libversion() + +VERSION 0.2.1: +RENAME: + initial_setup -> sl_init + dtime -> sl_dtime + my_alloc -> sl_alloc + mmapbuf -> sl_mmapuf_t + My_mmap -> sl_mmap + My_munmap -> sl_munmap + restore_console -> sl_restore_con + setup_con -> sl_setup_con + read_console -> sl_read_con + mygetchar -> sl_getchar + throw_random_seed -> sl_random_seed + get_available_mem -> sl_mem_avail + TTY_descr -> sl_tty_t + close_tty -> sl_tty_close + new_tty -> sl_tty_new + tty_open -> sl_tty_open + read_tty -> sl_tty_read + tty_timeout -> sl_tty_tmout + write_tty -> sl_tty_write + conv_spd -> sl_tty_convspd + str2double -> sl_str2d + globlog -> sl_globlog + sl_loglevel -> sl_loglevel_e + sl_log -> sl_log_t + argfn -> sl_argfn_t + argtype -> sl_argtype_e + hasarg -> sl_hasarg_e + myoption -> sl_option_t + mysuboption -> sl_suboption_t + showhelp -> sl_showhelp + parseargs -> sl_parseargs + change_helpstring -> sl_helpstring + get_suboption -> sl_get_suboption + iffound_default -> sl_iffound_deflt + check4running -> sl_check4running + readPSname -> sl_getPSname + struct buff_node -> struct sl_buff_node + List -> sl_list_t + list_push_tail -> sl_list_push_tail + list_push -> sl_list_push + list_pop -> sl_list_pop +Remove deprecated code (openlogfile, putlog) +Remove "bool" type (change to int) +FIXED bug with several only long options with same shortopt mark (like 0) +Add simple config files, so you can move some of your parameters settings to it + diff --git a/TODO b/TODO index 0fdfeb5..9469638 100644 --- a/TODO +++ b/TODO @@ -1,2 +1 @@ BUG in commandline options: '?' don't work! -Add prefixes sl_ to every function for solving further problems diff --git a/config.c b/config.c new file mode 100644 index 0000000..b0d13ea --- /dev/null +++ b/config.c @@ -0,0 +1,300 @@ +/* + * This file is part of the Snippets project. + * Copyright 2024 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include // FLT_max/min +#include +#include +#include + +#include "usefull_macros.h" + +/** + * @brief sl_get_keyval - get key name and its value from string pair + * @param pair - empty string, `key = value` or just `key` + * @param key - substring with `key` + * @param value - its value or empty line + * @return 0 if no key found (or line is a comment with '#' first); 1 if only key found and 2 if found both key and value + * this function removes all leading and trailing spaces in `key` and `value`; + * also if `key` have several words only first saved, `value` can be long string with or without quotations + */ +int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){ + DBG("got pair: '%s'", pair); + if(!pair || !*pair) return 0; // empty line + char *kstart = sl_omitspaces(pair); + if(!*kstart || *kstart == SL_COMMENT_CHAR) return 0; // only spaces + int ret = 1; + char *eq = strchr(kstart, '='), *kend = kstart + 1; + if(eq == kstart) return 0; // only = (and maybe val) + char *cmnt = strchr(kstart, SL_COMMENT_CHAR); + if(eq && cmnt && cmnt < eq) eq = NULL; // comment starting before equal sign + if(eq){ do{ + DBG("got equal symbol: '%s'", eq); + char *vstart = sl_omitspaces(eq + 1); + if(!*vstart) break; // empty line or only spaces after `=` + char *vend = sl_omitspacesr(vstart); + size_t l = SL_VAL_LEN - 1; // truncate value to given length + if(l > (size_t)(vend - vstart)) l = vend - vstart; + DBG("l=%zd", l); + strncpy(value, vstart, l); + value[l] = 0; + cmnt = strchr(value, SL_COMMENT_CHAR); + if(cmnt){ // remove trailing spaces before comment + *cmnt = 0; + *sl_omitspacesr(value) = 0; + } + if(!*value) break; + DBG("Got value: '%s'", value); + ret = 2; + }while(0); + }else eq = kstart + strlen(kstart) - 1; + for(; kend < eq && !isspace(*kend); ++kend); + size_t l = SL_KEY_LEN - 1; + if(l > (size_t)(kend - kstart)) l = kend - kstart; + strncpy(key, kstart, l); + key[l] = 0; + cmnt = strchr(key, SL_COMMENT_CHAR); + if(cmnt) *cmnt = 0; + DBG("Got key: '%s'", key); + return ret; +} + +// Read key/value from file; return -1 when file is over +static int read_key(FILE *file, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){ + char *line = NULL; + size_t n = 0; + ssize_t got = getline(&line, &n, file); + if(!line) return 0; + if(got < 0){ // EOF + free(line); + return -1; + } + int r = sl_get_keyval(line, key, value); + free(line); + 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; + } + return NULL; +} + +/** + * @brief sl_print_opts - fills string buffer with information from opt + * @param opt - options buffer + * @param showall - TRUE to show even uninitialized options with NO_ARGS + * @return allocated string (should be free'd) + */ +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){ + 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"); + } + 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: + * read file with lines "par" or "par = val" and set variables like they are options + * @param filename - configuration file name + * @param options - array with options (could be the same like for sl_parseargs) + * @return amount of data read + */ +int sl_conf_readopts(const char *filename, sl_option_t *options){ + if(!filename || !options) return 0; + FILE *f = fopen(filename, "r"); + if(!f){ + WARN(_("Can't open %s"), filename); + return 0; + } + int N = 0; + char key[SL_KEY_LEN], val[SL_VAL_LEN]; + 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; + } + 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; + }while(1); + return N; +} diff --git a/daemon.c b/daemon.c index c287ada..ba54db4 100644 --- a/daemon.c +++ b/daemon.c @@ -31,13 +31,13 @@ #include "usefull_macros.h" /** - * @brief readPSname - read process name from /proc/PID/cmdline + * @brief sl_getPSname - read process name from /proc/PID/cmdline * @param pid - PID of interesting process * @return filename or NULL if not found * don't use this function twice for different names without copying * its returning by strdup, because `name` contains in static array */ -char *readPSname(pid_t pid){ +char *sl_getPSname(pid_t pid){ static char name[PATH_MAX]; char *pp = name, byte, path[PATH_MAX]; FILE *file; @@ -61,11 +61,11 @@ char *readPSname(pid_t pid){ } /** - * @brief iffound_default - default action when running process found + * @brief sl_iffound_deflt - default action when running process found * @param pid - another process' pid * Redefine this function for user action */ -void WEAK iffound_default(pid_t pid){ +void WEAK sl_iffound_deflt(pid_t pid){ /// \nОбнаружен одноименный процесс (pid=%d), выход.\n fprintf(stderr, _("\nFound running process (pid=%d), exit.\n"), pid); exit(-1); @@ -81,7 +81,7 @@ void WEAK iffound_default(pid_t pid){ * @param selfname - argv[0] or NULL for non-locking * @param pidfilename - name of pidfile or NULL if none */ -void check4running(char *selfname, char *pidfilename){ +void sl_check4running(char *selfname, char *pidfilename){ DIR *dir; FILE *pidfile, *fself; struct dirent *de; @@ -102,7 +102,7 @@ void check4running(char *selfname, char *pidfilename){ goto selfpid; } if(fl.l_type != F_UNLCK){ // file is locking - exit - iffound_default(fl.l_pid); + sl_iffound_deflt(fl.l_pid); } fl.l_type = F_RDLCK; if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){ @@ -114,7 +114,7 @@ void check4running(char *selfname, char *pidfilename){ if(!(dir = opendir(PROC_BASE))){ // open /proc directory ERR(PROC_BASE); } - if(!(name = readPSname(self))){ // error reading self name + if(!(name = sl_getPSname(self))){ // error reading self name ERR(_("Can't read self name")); } myname = strdup(name); @@ -122,8 +122,8 @@ void check4running(char *selfname, char *pidfilename){ pidfile = fopen(pidfilename, "r"); if(pidfile){ if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process - if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0) - iffound_default(pid); + if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0) + sl_iffound_deflt(pid); } fclose(pidfile); } @@ -132,8 +132,8 @@ void check4running(char *selfname, char *pidfilename){ while((de = readdir(dir))){ // scan /proc if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self continue; - if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0) - iffound_default(pid); + if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0) + sl_iffound_deflt(pid); } closedir(dir); free(myname); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 32d95dd..274a513 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -9,3 +9,4 @@ link_libraries(usefull_macros) add_executable(helloworld helloworld.c) add_executable(options options.c cmdlnopts.c) add_executable(fifo fifo.c) +add_executable(conffile conffile.c) diff --git a/examples/cmdlnopts.c b/examples/cmdlnopts.c index af414dc..3593503 100644 --- a/examples/cmdlnopts.c +++ b/examples/cmdlnopts.c @@ -38,6 +38,9 @@ static glob_pars G; // DEFAULTS // default global parameters static glob_pars const Gdefault = { + .lo0 = INT_MIN, + .lo1 = INT_MIN, + .lo2 = INT_MIN, .device = NULL, .pidfile = DEFAULT_PIDFILE, .speed = 9600, @@ -48,16 +51,22 @@ static glob_pars const Gdefault = { * Define command line options by filling structure: * name has_arg flag val type argptr help */ -static myoption cmdlnopts[] = { -// common options +static sl_option_t cmdlnopts[] = { + {"lo0", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo0), _("only long arg 0")}, {"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")}, {"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")}, - end_option + // 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")}, + end_option }; /** @@ -75,10 +84,10 @@ glob_pars *parse_args(int argc, char **argv){ char helpstring[1024], *hptr = helpstring; snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n"); // format of help: "Usage: progname [args]\n" - change_helpstring(helpstring); + sl_helpstring(helpstring); // parse arguments - parseargs(&argc, &argv, cmdlnopts); - if(help) showhelp(-1, cmdlnopts); + sl_parseargs(&argc, &argv, cmdlnopts); + if(help) sl_showhelp(-1, cmdlnopts); if(argc > 0){ G.rest_pars_num = argc; G.rest_pars = MALLOC(char *, argc); diff --git a/examples/cmdlnopts.h b/examples/cmdlnopts.h index e35e77f..7e105f7 100644 --- a/examples/cmdlnopts.h +++ b/examples/cmdlnopts.h @@ -31,8 +31,14 @@ typedef struct{ char *pidfile; // name of PID file char *logfile; // logging to this file int speed; // connection speed - int rest_pars_num; // number of rest parameters int exclusive; // exclusive open port + int **intarr; // integer multopt + double **dblarr; // double -//- + char **strarr; // char -//- + int lo0; // only long options + int lo1; + int lo2; + 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 new file mode 100644 index 0000000..5be72fd --- /dev/null +++ b/examples/conffile.c @@ -0,0 +1,101 @@ +/* + * This file is part of the Snippets project. + * Copyright 2024 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include // NaN +#include +#include + +#include "usefull_macros.h" + +typedef struct{ + char *sp1; + char *sp2; + int ip1; + int ip2; + double dp1; + double dp2; + float fp1; + float fp2; + int help; + int verbose; + char *confname; +} parameters; + +static parameters G = { + .ip1 = INT_MIN, + .ip2 = INT_MIN, + .dp1 = NAN, + .dp2 = NAN, + .fp1 = NAN, + .fp2 = NAN +}; + +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"}, + {"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 +}; + + +int main(int argc, char **argv){ + sl_init(); + sl_parseargs(&argc, &argv, cmdlnopts); + if(G.help) sl_showhelp(-1, cmdlnopts); + 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); + } + } + green("Starting parameters values:\n"); + char *buf = sl_print_opts(cmdlnopts, TRUE); + printf("%s\n", buf); + FREE(buf); + if(G.confname){ + int o = sl_conf_readopts(G.confname, cmdlnopts); + if(o > 0){ + printf("got %d options in '%s'\n", o, G.confname); + green("And after reading of conffile:\n"); + buf = sl_print_opts(cmdlnopts, TRUE); + printf("%s\n", buf); + FREE(buf); + } + } + return 0; +} diff --git a/examples/fifo.c b/examples/fifo.c index 62cff5e..08464a2 100644 --- a/examples/fifo.c +++ b/examples/fifo.c @@ -23,8 +23,8 @@ */ int main(int argc, char *argv[argc]) { - List *f = NULL; - printf("Available memory: %luMB\n", get_available_mem()/1024/1024); + sl_list_t *f = NULL; + printf("Available memory: %luMB\n", sl_mem_avail()/1024/1024); //initial_setup(); - there's no need for this function if you don't use locale & don't want to have // specific output in non-tty if(argc == 1){ @@ -33,26 +33,26 @@ int main(int argc, char *argv[argc]) { } red("\n\nLIFO example\n"); for(int i = 1; i < argc; ++i){ - if(!list_push(&f, argv[i])) ERR("Allocation error!"); + if(!sl_list_push(&f, argv[i])) ERR("Allocation error!"); green("push to list "); printf("%s\n", argv[i]); } char *d; printf("\n"); while(f){ - d = list_pop(&f); + d = sl_list_pop(&f); green("pull: "); printf("%s\n", d); } red("\n\nFIFO example\n"); for(int i = 1; i < argc; ++i){ - if(!list_push_tail(&f, argv[i])) ERR("Allocation error!"); + if(!sl_list_push_tail(&f, argv[i])) ERR("Allocation error!"); green("push to list "); printf("%s\n", argv[i]); } printf("\n"); while(f){ - d = list_pop(&f); + d = sl_list_pop(&f); green("pull: "); printf("%s\n", d); // after last usage we should FREE data, but here it is parameter of main() diff --git a/examples/helloworld.c b/examples/helloworld.c index 0a89ef4..f3b8243 100644 --- a/examples/helloworld.c +++ b/examples/helloworld.c @@ -23,13 +23,13 @@ */ int main(){ - initial_setup(); + sl_init(); // setup non-echo non-canonical mode - setup_con(); + sl_setup_con(); green("Press any key...\n"); - char k = mygetchar(); + char k = sl_getchar(); red("You press %c\n", k); // don't forget to restore @exit! - restore_console(); + sl_restore_con(); return 0; } diff --git a/examples/options.c b/examples/options.c index 4ccc1d6..630477d 100644 --- a/examples/options.c +++ b/examples/options.c @@ -24,20 +24,6 @@ #include // sleep #include - -#include // tcsetattr -#include // tcsetattr, close, read, write -#include // ioctl -#include // printf, getchar, fopen, perror -#include // exit -#include // read -#include // read -#include // signal -#include // time -#include // memcpy -#include // int types -#include // gettimeofday - /** * This is an example of usage: * - command line arguments, @@ -48,7 +34,7 @@ * The `cmdlnopts.[hc]` are intrinsic files of this demo. */ -static TTY_descr *dev = NULL; // shoul be global to restore if die +static sl_tty_t *dev = NULL; // shoul be global to restore if die static glob_pars *GP = NULL; // for GP->pidfile need in `signals` /** @@ -62,28 +48,26 @@ void signals(int sig){ LOGERR("Exit with status %d", sig); if(GP && GP->pidfile) // remove unnesessary PID file unlink(GP->pidfile); - restore_console(); - if(dev) close_tty(&dev); + sl_restore_con(); + if(dev) sl_tty_close(&dev); exit(sig); } -void iffound_default(pid_t pid){ +void sl_iffound_deflt(pid_t pid){ ERRX("Another copy of this process found, pid=%d. Exit.", pid); } int main(int argc, char *argv[]){ - initial_setup(); - char *self = strdup(argv[0]); + sl_init(); GP = parse_args(argc, argv); if(GP->rest_pars_num){ printf("%d extra options:\n", GP->rest_pars_num); for(int i = 0; i < GP->rest_pars_num; ++i) printf("%s\n", GP->rest_pars[i]); } - check4running(self, GP->pidfile); - red("%s started, snippets library version is %s\n", self, sl_libversion()); - free(self); - setup_con(); + 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 @@ -95,10 +79,25 @@ int main(int argc, char *argv[]){ for(int i = 0; i < GP->rest_pars_num; ++i) printf("Extra argument: %s\n", GP->rest_pars[i]); } + if(GP->intarr){ + int **p = GP->intarr; + for(int i = 0; *p; ++i) printf("Integer[%d]: %d\n", i, **p++); + } + if(GP->dblarr){ + double **p = GP->dblarr; + for(int i = 0; *p; ++i) printf("Double[%d]: %g\n", i, **p++); + } + if(GP->strarr){ + char **p = GP->strarr; + for(int i = 0; *p; ++i) 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->device){ LOGDBG("Try to open serial %s", GP->device); - dev = new_tty(GP->device, GP->speed, 4096); - if(dev) dev = tty_open(dev, GP->exclusive); + dev = sl_tty_new(GP->device, GP->speed, 4096); + if(dev) dev = sl_tty_open(dev, GP->exclusive); if(!dev){ LOGERR("Can't open %s with speed %d. Exit.", GP->device, GP->speed); signals(0); @@ -106,26 +105,26 @@ int main(int argc, char *argv[]){ } // main stuff goes here - long seed = throw_random_seed(); + 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); LOGWARN("warning message example"); LOGWARNADD("with next string without timestamp"); - double t0 = dtime(); + double t0 = sl_dtime(); char b[2] = {0}; - while(dtime() - t0 < 10.){ // read data from port and print in into terminal + while(sl_dtime() - t0 < 10.){ // read data from port and print in into terminal if(dev){ - if(read_tty(dev)){ + if(sl_tty_read(dev)){ printf("Got %zd bytes from port: %s\n", dev->buflen, dev->buf); LOGMSG("Got from serial: %s", dev->buf); - t0 = dtime(); + t0 = sl_dtime(); } - int r = read_console(); + int r = sl_read_con(); if(r < 1) continue; - t0 = dtime(); + t0 = sl_dtime(); b[0] = (char) r; printf("send to tty: %d (%c)\n", r, b[0]); LOGMSG("send to tty: %d (%c)\n", r, b[0]); - write_tty(dev->comfd, b, 1); + sl_tty_write(dev->comfd, b, 1); } } // clean everything diff --git a/fifo_lifo.c b/fifo_lifo.c index bfa3e58..f57f3df 100644 --- a/fifo_lifo.c +++ b/fifo_lifo.c @@ -22,60 +22,60 @@ #include "usefull_macros.h" /** - * @brief list_push_tail - push data into the tail of a stack (like FIFO) + * @brief sl_list_push_tail - push data into the tail of a stack (like FIFO) * @param lst (io) - list * @param v (i) - data to push * @return pointer to just pused node or NULL in case of error */ -List *list_push_tail(List **lst, void *v){ - List *node; - if(!lst) return NULL; - if((node = MALLOC(List, 1)) == NULL) - return NULL; // allocation error - node->data = v; // insert data - if(!*lst){ - *lst = node; +sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v){ + sl_list_t *node; + if(!lst) return NULL; + if((node = MALLOC(sl_list_t, 1)) == NULL) + return NULL; // allocation error + node->data = v; // insert data + if(!*lst){ + *lst = node; }else{ (*lst)->last->next = node; } - (*lst)->last = node; - return node; + (*lst)->last = node; + return node; } /** - * @brief list_push - push data into the head of a stack (like LIFO) + * @brief sl_list_push - push data into the head of a stack (like LIFO) * @param lst (io) - list * @param v (i) - data to push * @return pointer to just pused node */ -List *list_push(List **lst, void *v){ - List *node; - if(!lst) return NULL; - if((node = MALLOC(List, 1)) == NULL) - return NULL; // allocation error - node->data = v; // insert data - if(!*lst){ - *lst = node; - (*lst)->last = node; - return node; - } - node->next = *lst; - node->last = (*lst)->last; - *lst = node; - return node; +sl_list_t *sl_list_push(sl_list_t **lst, void *v){ + sl_list_t *node; + if(!lst) return NULL; + if((node = MALLOC(sl_list_t, 1)) == NULL) + return NULL; // allocation error + node->data = v; // insert data + if(!*lst){ + *lst = node; + (*lst)->last = node; + return node; + } + node->next = *lst; + node->last = (*lst)->last; + *lst = node; + return node; } /** - * @brief list_pop - get data from head of list. Don't forget to FREE list data after `pop` + * @brief sl_list_pop - get data from head of list. Don't forget to FREE list data after `pop` * @param lst (io) - list * @return data from lst head */ -void *list_pop(List **lst){ - void *ret; - List *node = *lst; - if(!lst || !*lst) return NULL; - ret = (*lst)->data; - *lst = (*lst)->next; - FREE(node); - return ret; +void *sl_list_pop(sl_list_t **lst){ + void *ret; + sl_list_t *node = *lst; + if(!lst || !*lst) return NULL; + ret = (*lst)->data; + *lst = (*lst)->next; + FREE(node); + return ret; } diff --git a/locale/ru/LC_MESSAGES/usefull_macros.mo b/locale/ru/LC_MESSAGES/usefull_macros.mo index 9d9c048173259d77109547c7db4fbf403238646d..00e50e2a9f0082316aa2cc3ac8943221f61e5e76 100644 GIT binary patch literal 4190 zcma);-ESOM8O9H#P`Z4TQV7sejsqLF#cL;pLbOgHOS}nIyteU2PYRGk5XBHTiy}`oZsj_k#37Xqu^b4EA?6MKJe4vW8f#jT=F~%KEQJg8~~S+@jrvI{#8(7GvFt{ ze}gjr-Ven79|pzV*FkxI5_}N+KKMoO8BpSWAsK%elz3kQ^WfjXGuuR68K}t7ymDSV&`>m4*VZ@94viIse|Aj!Eb_ZffCn) zkR^OhfnNpBff?{m;8E}`Fb6(#pHk4J02>EjFJz-AiRGTD3cA#%xTTg*qWZYyLI$>u zqB*$;+g|$ zkQyQZrBcVerd!s2({-zEMK^q}bJd!Y43s?#muk(RddAUS z!*P@M6(`hQt^97b>P;6r$vj!2(;YX-lb-W@?K{OX5%kU*Dm!O}{Gwa->bkjr^&A3sFTiu?oS8( zdcFv)t|t~6q2JdXuq8ZGtTi1~fKBcAKAZ_}RkzU$)p&1-<0#UB4i_3uDjp-A%#R%3 z)2EwrbNH8~Qg|q?mK-(iSxSW}U7@yapPH12?Tc!l>(wBT>dd-9vmv=aq1Onjo@>W&()THnIz$t~9(Q@XL;|Fk= z)swknCvzwA`c!^udURquHRTA|nL<#hmNVaKR)Wlom({7siOdv*lQo%+o>}ahz==qiI93m);7V_P&4-Fnn9rNJV#T}x{ z>d-kC4mE1UsynPp^F^P{^yKW!@yy}wJP8XaX7X;yldYfChfi0-luDV)=8Cy#TG3*( zVqP&D=B74VW}R1CrfqJ>3v)BNXs&6q9W9!z=oe-qT8fr>x}RB#E=E6>RY?zI$p(*C z;+JAZiv#mAF0939xlftjn+?t5mb}){QsS!}Ek~=`Mzq9VJeq6fx@qGc=Zn#@Y3*8S zu9}v)&d^1>)HYH=Ug1f?j?OcD&1~*owM`z2#JOhkS?=+m&6^n>?Y3Dnsf`;Xud=dV8jBCod z89k??3oMj+wxj3vUenv*_cSiY!)*zc;=zW~yn3sW)bBdlyNOjP>*{X8(7Y(pY+61gzs_-GerF53B%%@3it<~c z?nKVKWPilcXTwFyTGF+{tC|Y6&34bJwkymw@mROfDze-{mMu$eXLm@CG%r!*O$Kp- zYz5YN+D3#TMsdcbGh6byO=yH@Z9#!DFX~wI6s{GYlVX7&*}K>6L>!8JsgDrM@^~Y4 zdIeHvcmI|dm!b=?37h!_4PhnD_o}QEX@FLYm!oG1<%)Q)UqY5`qTH*6&@&Rcn0SN4 zQ_B20j!Rm_5)^FM#4SfQB|smEN1SUOu2&pX3}EK($91WtywYSLG%-L;M#sqIx; zKK>;lk%qL+jWb(Y*?UPBGA+2djH4y&%CBt>F)JO>6MVPN*=nz2PDqx%LpJ1cBkpan zT8L$vzT7tLm^ax@Ehe@_xShUrUd$3GXKU|8yE%(zucN;!EN;trot!Ch#-LQ1JQeSr);lRrzLCrx2IzOf Q8j^%Jd*I(5i~s-t literal 2478 zcmZ{kO>7%g5XYBNz6@=ld=`qB6w*?)o3;m}%CsmZaodPEiPE&FT+r5jaTe^|Wk1@6 z3vfZ=jEcmO3mmv`LgLm9-n1Kktg~L%NoaGS5+^9)hJ*zFS+5hPz)I_%_q~~UGyi$- zt?%A`Rl>73dH(_j@!q-*KX{C7l5_z40A!ml!RNs5!Dqn$d>;G-d<@)nza;GecY)7< z2f-)7;e<2E_+0Y+5x4{MpM(3sufV6k8wqcN9RCmSWpKxKN!kq_1KDmAulobYdDOudz&i=02T%uU+zTGX_-hYJ(o5iM!cW0N`2HF^42IxN@Ne)%aOXqa zc!xpO;|$1pC?M-J53&wlB>WB>!uJx$>--5m3hsHho8KTfg6|U`*Yz>@G)={@59`L7 z@`JGP!!@Gi64L3`0jBVB&fFi?g!M<6(k{IGa37`ADc#WuWICFrYDF?kJufVaUNzLh z$-%TaVSJ-U6(ts&P{Vwm?}g zh?MlUsbd?U5T`6OoAwP_`?vr@F_E5)^?4+*niD0%vQ2~^NJciy(yUcCmSu2-mz0tt zFj^Rp3c7O3nwj?AYW)YFZfZlyg>5Ey!;=MN6L1)0CQ;l&7H=`^ck;jYKYFGt+WzQXb9FnaOl+ z?9e-t6WP?bV%hQxx@DCVTQ}2m>fHD^O=NOeI+4v~#>R&;qodQ=vuEFcsaAB9qL62V zQclx&X5#cY&g8`S_;5)b(#_(g4Km1$<+7V;9~(NF8qu*wG%Po7ARk*?v5y!fMb%!X z{G4K951xEqSC7l)rXlBrEg)yLyv~OpO~)^)c1lX!U1+#r`=-~RpwbBZy4Pr|E>SZK ze)L+czO&p}y%AJ!n1A!A9$arz3zmMgLJhYSg~3wQr)CgF?#BF@TMbt+AD`()J3`P! zcd529-}FMSzlSgt-a2RRlKEE)M;P4?R)>baB}>6 zc?MB85A4@UZJE|1q`kZmxi{N?kni>6%V10DJ*Qf5eakpRTSrAJTdsv(w60EEL^Wu% zg4)VP4xQCzP>Z$fT@=5%t(M=RrW?9-59)TP!K&Z%B0_@@BJ!Jk7sk(CE%2xVn@Z4Z zLsB#-po;G{-L;_MN_T(q7piXD30`!~t+3} z&w6m%-x{mu9bY+{zEE?m;jUwGk6P6r44`TE{;9MVYJSKYa0yaWYJT^$B#lZgqOC^k OA60#vsZA@~NdExyiLQYF diff --git a/locale/ru/messages.po b/locale/ru/messages.po index 6d0a30d..c8f1267 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: 2020-12-24 12:22+0300\n" +"POT-Creation-Date: 2024-11-15 11:38+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,8 +17,36 @@ msgstr "" "Content-Type: text/plain; charset=koi8-r\n" "Content-Transfer-Encoding: 8bit\n" -#. / \nОбнаружен одноименный процесс (pid=%d), выход.\n -#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:70 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:225 +msgid "Unsupported option type" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:228 +#, c-format +msgid "Wrong number format '%s'" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:272 +#, c-format +msgid "Can't open %s" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:283 +#, c-format +msgid "Wrong key: '%s'" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:289 +#, c-format +msgid "Key '%s' need value" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:295 +#, c-format +msgid "Key '%s' have no argptr!" +msgstr "" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69 #, c-format msgid "" "\n" @@ -26,154 +54,137 @@ msgid "" msgstr "" #. error reading self name -#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:118 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:117 msgid "Can't read self name" msgstr "" -#. / Не могу открыть PID файл -#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:143 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141 msgid "Can't open PID file" msgstr "" #. amount of pcount and/or scount wrong -#. / Неправильный формат строки помощи -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:54 +#: /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:84 +#: /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/parseargs.c:151 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166 msgid "Can't use multiple args with arg_none!" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:252 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267 #, c-format msgid "double long arguments: --%s" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:258 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273 #, c-format msgid "double short arguments: -%c" msgstr "" -#. / Неправильный параметр: %s -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:470 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332 +#, c-format +msgid "Need argument with %s type\n" +msgstr "" + +#: /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/parseargs.c:475 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490 #, c-format msgid "%s: need argument!" msgstr "" -#. / Неправильный аргумент \"%s\" параметра \"%s\" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:480 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494 #, c-format msgid "Wrong argument \"%s\" of parameter \"%s\"" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:92 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87 #, c-format -msgid "Wrong speed value: %d!" +msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2" msgstr "" -#. / Не могу использовать порт %s -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:105 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102 #, c-format msgid "Can't use port %s" msgstr "" #. Get settings -#. / Не могу получить действующие настройки порта -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:110 +#: /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/term.c:121 +#: /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/term.c:128 +#: /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 "" + +#: /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/term.c:164 +#: /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/term.c:170 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168 msgid "Need non-zero buffer for TTY device" msgstr "" -#. / Не могу открыть /dev/random -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:185 +#: /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:190 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198 msgid "Can't read /dev/random" msgstr "" -#. / Не задано имя файла! -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:241 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248 msgid "No filename given!" msgstr "" -#. / Не могу открыть %s для чтения -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:246 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252 #, c-format msgid "Can't open %s for reading" msgstr "" -#. / Не могу выполнить stat %s -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:251 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256 #, c-format msgid "Can't stat %s" msgstr "" -#. / Ошибка mmap -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:258 +#: /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/usefull_macros.c:263 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266 msgid "Can't close mmap'ed file" msgstr "" -#. / Не могу munmap -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:277 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279 msgid "Can't munmap" msgstr "" -#. / Не могу настроить консоль -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:308 +#: /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:383 -msgid "Need filename for log file" -msgstr "" - -#. / Пробую открыть логфайл %s в режиме дополнения\n -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:387 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403 #, c-format -msgid "Try to open log file %s in append mode\n" +msgid "Wrong double number format '%s'" msgstr "" -#. / Не могу открыть логфайл -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:391 -msgid "Can't open log file" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416 +#, c-format +msgid "Wrong integer number format '%s'" msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po index 872b72d..34b2c96 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: 2020-12-24 12:22+0300\n" + "POT-Creation-Date: 2024-11-15 11:38+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,164 +16,177 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n" "Content-Type: text/plain; charset=koi8-r\n" "Content-Transfer-Encoding: 8bit\n" -#. / \nОбнаружен одноименный процесс (pid=%d), выход.\n -#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:70 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69 #, c-format msgid "\n" "Found running process (pid=%d), exit.\n" msgstr "\n" "Обнаружен одноименный процесс (pid=%d), выход.\n" -#. / %s: необходим аргумент! -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:475 -#, fuzzy, c-format +#: /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/term.c:121 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119 msgid "Can't apply new TTY settings" msgstr "Не могу сменить настройки порта" -#. / Не могу закрыть mmap'нутый файл -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:263 +#: /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/term.c:128 +#: /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/term.c:110 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106 msgid "Can't get old TTY settings" msgstr "Не могу получить действующие настройки порта" -#. / Не могу munmap -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:277 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279 msgid "Can't munmap" msgstr "Не могу munmap" -#. / Не могу открыть %s для чтения -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:246 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:272 +#, 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 для чтения" -#. / Не могу открыть /dev/random -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:185 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194 msgid "Can't open /dev/random" msgstr "Не могу открыть /dev/random" -#. / Не могу открыть PID файл -#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:143 +#: /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:391 -msgid "Can't open log file" -msgstr "Не могу открыть логфайл" - -#. / Не могу прочесть /dev/random -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:190 +#: /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:118 -#, fuzzy +#: /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/usefull_macros.c:308 +#: /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/usefull_macros.c:347 msgid "Can't setup console" msgstr "Не могу настроить консоль" -#. / Не могу выполнить stat %s -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:251 +#: /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:151 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166 msgid "Can't use multiple args with arg_none!" msgstr "Массив параметров не может иметь тип arg_none!" -#. / Не могу использовать порт %s -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:105 -#, fuzzy, c-format +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102 +#, c-format msgid "Can't use port %s" msgstr "Не могу использовать порт %s" -#. / Целое вне допустимого диапазона -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:84 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:82 msgid "Integer out of range" msgstr "Целое вне допустимого диапазона" -#. / Ошибка mmap -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:258 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:295 +#, c-format +msgid "Key '%s' have no argptr!" +msgstr "У ключа '%s' нет указателя на переменную!" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:289 +#, c-format +msgid "Key '%s' need value" +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/usefull_macros.c:383 -msgid "Need filename for log file" -msgstr "Не задано имя логфайла" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332 +#, c-format +msgid "Need argument with %s type\n" +msgstr "Необходим аргумент с типом %s\n" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:170 +#: /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:241 +#: /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/term.c:164 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162 msgid "Port name is missing" msgstr "Отсутствует имя порта" -#. / Пробую открыть логфайл %s в режиме дополнения\n -#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:387 -#, c-format -msgid "Try to open log file %s in append mode\n" -msgstr "Пробую открыть логфайл %s в режиме дополнения\n" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:225 +msgid "Unsupported option type" +msgstr "Неподдерживаемый тип опции" -#. / Неправильный аргумент \"%s\" параметра \"%s\" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:480 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87 +#, c-format +msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: " + "1/2" +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 #, c-format msgid "Wrong argument \"%s\" of parameter \"%s\"" msgstr "Неправильный аргумент \"%s\" параметра \"%s\"" +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403 +#, c-format +msgid "Wrong double number format '%s'" +msgstr "Неправильный формат double: %s" + #. amount of pcount and/or scount wrong -#. / Неправильный формат строки помощи -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:54 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53 msgid "Wrong helpstring!" msgstr "Неправильный формат строки помощи" -#. / Неправильный параметр: %s -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:470 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416 +#, c-format +msgid "Wrong integer number format '%s'" +msgstr "Неправильный формат целого: %s" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:283 +#, c-format +msgid "Wrong key: '%s'" +msgstr "Неправильный ключ: %s" + +#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:228 +#, c-format +msgid "Wrong number format '%s'" +msgstr "Неправильный формат числа: %s" + +#: /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/term.c:92 -#, c-format -msgid "Wrong speed value: %d!" -msgstr "Неправильное значение скорости: %d!" - -#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:252 +#: /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:258 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273 #, c-format msgid "double short arguments: -%c" msgstr "дублирующийся короткий параметр: -%c" + diff --git a/parseargs.c b/parseargs.c index 00de3ab..6fad51e 100644 --- a/parseargs.c +++ b/parseargs.c @@ -33,10 +33,10 @@ char *helpstring = "%s\n"; /** - * @brief change_helpstring - change standard help header + * @brief sl_helpstring - change standard help header * @param str (i) - new format (MAY consist ONE "%s" for progname) */ -void change_helpstring(char *s){ +void sl_helpstring(char *s){ int pcount = 0, scount = 0; char *str = s; // check `helpstring` and set it to default in case of error @@ -63,7 +63,7 @@ void change_helpstring(char *s){ * @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more) * @return TRUE if conversion done without errors, FALSE otherwise */ -static bool myatoll(void *num, char *str, argtype t){ +static int myatoll(void *num, char *str, sl_argtype_e t){ long long tmp, *llptr; int *iptr; char *endptr; @@ -92,7 +92,7 @@ static bool myatoll(void *num, char *str, argtype t){ // the same as myatoll but for double // There's no NAN & INF checking here (what if they would be needed?) -static bool myatod(void *num, const char *str, argtype t){ +static int myatod(void *num, const char *str, sl_argtype_e t){ double tmp, *dptr; float *fptr; char *endptr; @@ -114,21 +114,39 @@ static bool myatod(void *num, const char *str, argtype t){ return TRUE; } + /** * @brief get_optind - get index of current option in array options * @param opt (i) - returning val of getopt_long * @param options (i) - array of options * @return index in array */ -static int get_optind(int opt, myoption *options){ +static int get_optind(int opt, sl_option_t *options){ int oind; - myoption *opts = options; + sl_option_t *opts = options; assert(opts); - for(oind = 0; opts->name && opts->val != opt; oind++, opts++); + for(oind = 0; opts->name && opts->val != opt; oind++, opts++){ + DBG("cmp %c and %c", opt, opts->val); + } if(!opts->name || opts->val != opt) // no such parameter - showhelp(-1, options); + 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 @@ -136,7 +154,7 @@ static int get_optind(int opt, myoption *options){ * @arg type - its type (for realloc) * @return pointer to new (next) value */ -void *get_aptr(void *paptr, argtype type){ +void *get_aptr(void *paptr, sl_argtype_e type){ int i = 1; void **aptr = *((void***)paptr); if(aptr){ // there's something in array @@ -181,7 +199,7 @@ void *get_aptr(void *paptr, argtype type){ /** - * @brief parseargs - parse command line arguments + * @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` @@ -192,11 +210,11 @@ void *get_aptr(void *paptr, argtype type){ * * @exit: in case of error this function show help & make `exit(-1)` */ -void parseargs(int *argc, char ***argv, myoption *options){ +void sl_parseargs(int *argc, char ***argv, sl_option_t *options){ char *short_options, *soptr; struct option *long_options, *loptr; size_t optsize, i; - myoption *opts = options; + sl_option_t *opts = options; // check whether there is at least one options assert(opts); assert(opts[0].name); @@ -263,31 +281,45 @@ void parseargs(int *argc, char ***argv, myoption *options){ // now we have both long_options & short_options and can parse `getopt_long` while(1){ int opt; - int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[] - if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break; - if(opt == '?'){ - opt = optopt; - optind = get_optind(opt, options); + 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 + // be careful with "-?" flag: all wrong or ambiguous flags will be interpreted as this! + /* + if(opt == '?'){ // real '?', wrong option or no argument when need + opt = optopt; // original short opt or 0 for wrong or long + if(opt == 0){ // '?' or wrong long + // there's no way to understand difference between real '-?' and unknown long flag + DBG("'?' or unknown parameter"); + sl_showhelp(-1, options); + }else optind = get_optind(opt, options); + DBG("optind = %d", optind); if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR) - showhelp(optind, options); // need argument - } - else{ - if(opt == 0 || oindex > 0) optind = oindex; + sl_showhelp(optind, options); // need argument + }else{ + // we should call functions get_optind / get_optindl for options where there's no long analog + if(opt < ' ') optind = get_optindl(&long_options[oindex], options); else optind = get_optind(opt, options); } + if(optind < 0) sl_showhelp(-1, options); // wrong argument + */ + DBG("index=%d", optind); opts = &options[optind]; - // if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag + DBG("Got option %s", opts->name); + // now check option if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR) - if(!optarg) showhelp(optind, options); // need argument + if(!optarg) sl_showhelp(optind, options); // need argument void *aptr; if(opts->has_arg == MULT_PAR){ aptr = get_aptr(opts->argptr, opts->type); }else aptr = opts->argptr; - bool result = TRUE; + int result = TRUE; // even if there is no argument, but argptr != NULL, think that optarg = "1" if(!optarg) optarg = "1"; + const char *type = NULL; switch(opts->type){ default: case arg_none: @@ -295,25 +327,31 @@ void parseargs(int *argc, char ***argv, myoption *options){ break; case arg_int: result = myatoll(aptr, optarg, arg_int); + type = _("integer"); break; case arg_longlong: result = myatoll(aptr, optarg, arg_longlong); + type = _("long long"); break; case arg_double: result = myatod(aptr, optarg, arg_double); + type = _("double"); break; case arg_float: result = myatod(aptr, optarg, arg_float); + type = _("float"); break; case arg_string: - result = (*((void**)aptr) = (void*)strdup(optarg)); + result = (*((void**)aptr) = (void*)strdup(optarg)) != NULL; + type = _("string"); break; case arg_function: - result = ((argfn)aptr)(optarg); + result = ((sl_argfn_t)aptr)(optarg); break; } if(!result){ - showhelp(optind, options); + if(type) fprintf(stderr, _("Need argument with %s type\n"), type); + sl_showhelp(optind, options); } } *argc -= optind; @@ -325,7 +363,7 @@ void parseargs(int *argc, char ***argv, myoption *options){ * first - sort by short options; second - sort arguments without sort opts (by long options) */ static int argsort(const void *a1, const void *a2){ - const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2; + const sl_option_t *o1 = (sl_option_t*)a1, *o2 = (sl_option_t*)a2; const char *l1 = o1->name, *l2 = o2->name; int s1 = o1->val, s2 = o2->val; int *f1 = o1->flag, *f2 = o2->flag; @@ -341,17 +379,17 @@ static int argsort(const void *a1, const void *a2){ } /** - * @brief showhelp - show help information based on myoption->help values - * @param oindex (i) - if non-negative, show only help by myoption[oindex].help - * @param options (i) - array of `myoption` + * @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 + * @param options (i) - array of `sl_option_t` * * @exit: run `exit(-1)` !!! */ -void showhelp(int oindex, myoption *options){ +void sl_showhelp(int oindex, sl_option_t *options){ int max_opt_len = 0; // max len of options substring - for right indentation const int bufsz = 255; char buf[bufsz+1]; - myoption *opts = options; + sl_option_t *opts = options; assert(opts); assert(opts[0].name); // check whether there is at least one options if(oindex > -1){ // print only one message @@ -383,7 +421,7 @@ void showhelp(int oindex, myoption *options){ if(N == 0) exit(-2); // Now print all help (sorted) opts = options; - qsort(opts, N, sizeof(myoption), argsort); + qsort(opts, N, sizeof(sl_option_t), argsort); do{ int p = sprintf(buf, " "); // a little indent if(!opts->flag && opts->val) // .val is short argument @@ -402,13 +440,13 @@ void showhelp(int oindex, myoption *options){ } /** - * @brief get_suboption - get suboptions from parameter string + * @brief sl_get_suboption - get suboptions from parameter string * @param str - parameter string * @param opt - pointer to suboptions structure * @return TRUE if all OK */ -bool get_suboption(char *str, mysuboption *opt){ - int findsubopt(char *par, mysuboption *so){ +int sl_get_suboption(char *str, sl_suboption_t *opt){ + int findsubopt(char *par, sl_suboption_t *so){ int idx = 0; if(!par) return -1; while(so[idx].name){ @@ -417,9 +455,9 @@ bool get_suboption(char *str, mysuboption *opt){ } return -1; // badarg } - bool opt_setarg(mysuboption *so, int idx, char *val){ - mysuboption *soptr = &so[idx]; - bool result = FALSE; + int opt_setarg(sl_suboption_t *so, int idx, char *val){ + sl_suboption_t *soptr = &so[idx]; + int result = FALSE; void *aptr = soptr->argptr; switch(soptr->type){ default: @@ -440,16 +478,16 @@ bool get_suboption(char *str, mysuboption *opt){ result = myatod(aptr, val, arg_float); break; case arg_string: - result = (*((void**)aptr) = (void*)strdup(val)); + result = (*((void**)aptr) = (void*)strdup(val)) != NULL; break; case arg_function: - result = ((argfn)aptr)(val); + result = ((sl_argfn_t)aptr)(val); break; } return result; } char *tok; - bool ret = FALSE; + int ret = FALSE; char *tmpbuf; tok = strtok_r(str, ":,", &tmpbuf); do{ diff --git a/term.c b/term.c index f0adda3..20c55b7 100644 --- a/term.c +++ b/term.c @@ -1,11 +1,10 @@ /* - * client.c - simple terminal client + * This file is part of the Snippets project. + * Copyright 2013 Edward V. Emelianov . * - * Copyright 2013 Edward V. Emelianoff - * - * This program is free software; you can redistribute it and/or modify + * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -14,10 +13,9 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. + * along with this program. If not, see . */ + #include // tcsetattr, close, read, write #include // ioctl #include // printf, getchar, fopen, perror @@ -39,7 +37,7 @@ typedef struct { tcflag_t bspeed; // baudrate from termios.h } spdtbl; -static int tty_init(TTY_descr *descr); +static int tty_init(sl_tty_t *descr); static spdtbl speeds[] = { {50, B50}, @@ -76,11 +74,11 @@ static spdtbl speeds[] = { }; /** - * @brief conv_spd - test if `speed` is in .speed of `speeds` array + * @brief sl_tty_convspd - test if `speed` is in .speed of `speeds` array * @param speed - integer speed (bps) * @return 0 if error, Bxxx if all OK */ -tcflag_t conv_spd(int speed){ +tcflag_t sl_tty_convspd(int speed){ spdtbl *spd = speeds; int curspeed = 0; do{ @@ -98,7 +96,7 @@ tcflag_t conv_spd(int speed){ * @param descr (io) - port descriptor * @return 0 if all OK or error code */ -static int tty_init(TTY_descr *descr){ +static int tty_init(sl_tty_t *descr){ // |O_NONBLOCK ? if ((descr->comfd = open(descr->portname, O_RDWR|O_NOCTTY)) < 0){ /// Не могу использовать порт %s @@ -134,9 +132,9 @@ static int tty_init(TTY_descr *descr){ /** * @brief restore_tty - restore opened TTY to previous state and close it */ -void close_tty(TTY_descr **descr){ +void sl_tty_close(sl_tty_t **descr){ if(descr == NULL || *descr == NULL) return; - TTY_descr *d = *descr; + sl_tty_t *d = *descr; if(d->comfd){ ioctl(d->comfd, TCSANOW, &d->oldtty); // return TTY to previous state close(d->comfd); @@ -147,16 +145,16 @@ void close_tty(TTY_descr **descr){ } /** - * @brief new_tty - create new TTY structure with partially filled fields + * @brief sl_tty_new - create new TTY structure with partially filled fields * @param comdev - TTY device filename * @param speed - speed (number) * @param bufsz - size of buffer for input data (or 0 if opened only to write) * @return pointer to TTY structure if all OK */ -TTY_descr *new_tty(char *comdev, int speed, size_t bufsz){ - tcflag_t spd = conv_spd(speed); +sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){ + tcflag_t spd = sl_tty_convspd(speed); if(!spd) return NULL; - TTY_descr *descr = MALLOC(TTY_descr, 1); + sl_tty_t *descr = MALLOC(sl_tty_t, 1); descr->portname = strdup(comdev); descr->baudrate = spd; descr->speed = speed; @@ -176,12 +174,12 @@ TTY_descr *new_tty(char *comdev, int speed, size_t bufsz){ } /** - * @brief tty_open - init & open tty device + * @brief sl_tty_open - init & open tty device * @param d - already filled structure (with new_tty or by hands) * @param exclusive - == 1 to make exclusive open * @return pointer to TTY structure if all OK */ -TTY_descr *tty_open(TTY_descr *d, int exclusive){ +sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive){ if(!d || !d->portname || !d->baudrate) return NULL; if(exclusive) d->exclusive = TRUE; else d->exclusive = FALSE; @@ -191,11 +189,11 @@ TTY_descr *tty_open(TTY_descr *d, int exclusive){ static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000}; /** - * @brief tty_timeout - set timeout for select() on reading + * @brief sl_tty_tmout - set timeout for select() on reading * @param usec - microseconds of timeout * @return -1 if usec < 0, 0 if all OK */ -int tty_timeout(double usec){ +int sl_tty_tmout(double usec){ if(usec < 0.) return -1; tvdefault.tv_sec = 0; if(usec > 999999){ @@ -207,12 +205,12 @@ int tty_timeout(double usec){ } /** - * @brief read_tty - read data from TTY with 10ms timeout + * @brief sl_tty_read - read data from TTY with 10ms timeout * @param buff (o) - buffer for data read * @param length - buffer len * @return amount of bytes read or -1 if disconnected */ -int read_tty(TTY_descr *d){ +int sl_tty_read(sl_tty_t *d){ if(!d || d->comfd < 0) return 0; size_t L = 0; ssize_t l; @@ -246,12 +244,12 @@ int read_tty(TTY_descr *d){ } /** - * @brief write_tty - write data to serial port + * @brief sl_tty_write - write data to serial port * @param buff (i) - data to write * @param length - its length * @return 0 if all OK */ -int write_tty(int comfd, const char *buff, size_t length){ +int sl_tty_write(int comfd, const char *buff, size_t length){ ssize_t L = write(comfd, buff, length); if((size_t)L != length){ /// "Ошибка записи!" diff --git a/term2.c b/term2.c new file mode 100644 index 0000000..5a6ee7f --- /dev/null +++ b/term2.c @@ -0,0 +1,264 @@ +/* + * This file is part of the Snippets project. + * Copyright 2024 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include // tcsetattr, close, read, write +#include // read +#include // printf, getchar, fopen, perror +#include // exit, realloc +#include // memcpy +#include // ioctl +#include // read +#include // gettimeofday +#include // time +#include // usleep, tcsetattr, close, read, write +#include "usefull_macros.h" + +#define LOGBUFSZ (1024) + +// return FALSE if failed +static int parse_format(const char *iformat, tcflag_t *flags){ + tcflag_t f = 0; + if(!iformat){ // default + if(flags) *flags = CS8; + return TRUE; + } + if(strlen(iformat) != 3) goto someerr; + switch(iformat[0]){ + case '5': + f |= CS5; + break; + case '6': + f |= CS6; + break; + case '7': + f |= CS7; + break; + case '8': + f |= CS8; + break; + default: + goto someerr; + } + switch(iformat[1]){ + case '0': // always 0 + f |= PARENB | CMSPAR; + break; + case '1': // always 1 + f |= PARENB | CMSPAR | PARODD; + break; + case 'E': // even + f |= PARENB; + break; + case 'N': // none + break; + case 'O': // odd + f |= PARENB | PARODD; + break; + default: + goto someerr; + } + switch(iformat[2]){ + case '1': + break; + case '2': + f |= CSTOPB; + break; + default: + goto someerr; + } + if(flags) *flags = f; + return TRUE; +someerr: + WARNX(_("Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2"), iformat); + return FALSE; +} + +/** + * @brief tty_init - open & setup terminal + * @param descr (io) - port descriptor + * !!! default settings are "8N1". + * !!! If you want other, set it like `descr->format = "7O2"` between `sl_tty_new` and `sl_tty_open` + * @return 0 if all OK, last error code or 1 + */ +static int tty_init(sl_tty_t *descr){ + tcflag_t flags; + if(!parse_format(descr->format, &flags)) return 1; + if((descr->comfd = open(descr->portname, O_RDWR|O_NOCTTY)) < 0){ + /// Не могу использовать порт %s + WARN(_("Can't use port %s"), descr->portname); + return globErr ? globErr : 1; + } + if(ioctl(descr->comfd, TCGETS2, &descr->oldtty)){ // Get settings + /// Не могу получить действующие настройки порта + WARN(_("Can't get old TTY settings")); + return globErr ? globErr : 1; + } + descr->tty = descr->oldtty; + descr->tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG) + descr->tty.c_iflag = 0; // don't do any changes in input stream + descr->tty.c_oflag = 0; // don't do any changes in output stream + descr->tty.c_cflag = BOTHER | flags | CREAD | CLOCAL; // other speed, user format, RW, ignore line ctrl + descr->tty.c_ispeed = descr->speed; + descr->tty.c_ospeed = descr->speed; + descr->tty.c_cc[VMIN] = 0; // non-canonical mode + descr->tty.c_cc[VTIME] = 5; + if(ioctl(descr->comfd, TCSETS2, &descr->tty)){ + /// Не могу сменить настройки порта + WARN(_("Can't apply new TTY settings")); + return globErr ? globErr : 1; + } + ioctl(descr->comfd, TCGETS2, &descr->tty); + if(descr->tty.c_ispeed != (speed_t)descr->speed || descr->tty.c_ospeed != (speed_t)descr->speed){ + WARN(_("Can't set speed %d, got ispeed=%d, ospeed=%d"), descr->speed, descr->tty.c_ispeed, descr->tty.c_ospeed); + descr->speed = descr->tty.c_ispeed; + } + // make exclusive open + if(descr->exclusive){ + if(ioctl(descr->comfd, TIOCEXCL)){ + /// Не могу сделать порт эксклюзивным + WARN(_("Can't do exclusive open")); + }} + return 0; +} + +/** + * @brief restore_tty - restore opened TTY to previous state and close it + */ +void sl_tty_close(sl_tty_t **descr){ + if(descr == NULL || *descr == NULL) return; + sl_tty_t *d = *descr; + if(d->comfd){ + ioctl(d->comfd, TCSETS2, &d->oldtty); // return TTY to previous state + close(d->comfd); + } + FREE(d->portname); + FREE(d->buf); + FREE(*descr); +} + +/** + * @brief sl_tty_new - create new TTY structure with partially filled fields + * @param comdev - TTY device filename + * @param speed - speed (number) + * @param bufsz - size of buffer for input data (or 0 if opened only to write) + * @return pointer to TTY structure if all OK + */ +sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){ + sl_tty_t *descr = MALLOC(sl_tty_t, 1); + descr->portname = strdup(comdev); + descr->speed = speed; + if(!descr->portname){ + /// Отсутствует имя порта + WARNX(_("Port name is missing")); + }else{ + if(bufsz){ + descr->buf = MALLOC(char, bufsz+1); + descr->bufsz = bufsz; + return descr; + }else WARNX(_("Need non-zero buffer for TTY device")); + } + FREE(descr->portname); + FREE(descr); + return NULL; +} + +/** + * @brief sl_tty_open - init & open tty device + * @param d - already filled structure (with new_tty or by hands) + * @param exclusive - == 1 to make exclusive open + * @return pointer to TTY structure if all OK + */ +sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive){ + if(!d || !d->portname) return NULL; + if(exclusive) d->exclusive = TRUE; + else d->exclusive = FALSE; + if(tty_init(d)) return NULL; + return d; +} + +static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000}; +/** + * @brief sl_tty_tmout - set timeout for select() on reading + * @param usec - microseconds of timeout + * @return -1 if usec < 0, 0 if all OK + */ +int sl_tty_tmout(double usec){ + if(usec < 0.) return -1; + tvdefault.tv_sec = 0; + if(usec > 999999){ + tvdefault.tv_sec = (__time_t)(usec / 1e6); + usec -= tvdefault.tv_sec * 1e6; + } + tvdefault.tv_usec = (__suseconds_t) usec; + return 0; +} + +/** + * @brief sl_tty_read - read data from TTY with 10ms timeout + * @param buff (o) - buffer for data read + * @param length - buffer len + * @return amount of bytes read or -1 if disconnected + */ +int sl_tty_read(sl_tty_t *d){ + if(!d || d->comfd < 0) return 0; + size_t L = 0; + ssize_t l; + size_t length = d->bufsz; + char *ptr = d->buf; + fd_set rfds; + struct timeval tv; + int retval; + do{ + l = 0; + FD_ZERO(&rfds); + FD_SET(d->comfd, &rfds); + //memcpy(&tv, &tvdefault, sizeof(struct timeval)); + tv = tvdefault; + retval = select(d->comfd + 1, &rfds, NULL, NULL, &tv); + if(!retval) break; + if(retval < 0){ + if(errno == EINTR) continue; + return -1; + } + if(FD_ISSET(d->comfd, &rfds)){ + l = read(d->comfd, ptr, length); + if(l < 1) return -1; // disconnected + ptr += l; L += l; + length -= l; + } + }while(l && length); + d->buflen = L; + d->buf[L] = 0; + return (size_t)L; +} + +/** + * @brief sl_tty_write - write data to serial port + * @param buff (i) - data to write + * @param length - its length + * @return 0 if all OK + */ +int sl_tty_write(int comfd, const char *buff, size_t length){ + ssize_t L = write(comfd, buff, length); + if((size_t)L != length){ + /// "Ошибка записи!" + WARN("Write error"); + return 1; + } + return 0; +} diff --git a/usefull_macros.c b/usefull_macros.c index 7a95404..3f8b9f1 100644 --- a/usefull_macros.c +++ b/usefull_macros.c @@ -18,23 +18,25 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ -#include -#include -#include -#include -#include -#include -#include -#include + +#include // isspace #include -#include -#include -#include // flock -#include -#include -#include +#include #include // PATH_MAX +#include #include // floor +#include +#include +#include +#include +#include // flock +#include +#include +#include +#include +#include +#include +#include #include "usefull_macros.h" @@ -56,10 +58,10 @@ const char *sl_libversion(){ } /** - * @brief dtime - function for different purposes that need to know time intervals + * @brief sl_dtime - function for different purposes that need to know time intervals * @return double value: UNIX time in seconds */ -double dtime(){ +double sl_dtime(){ double t; struct timeval tv; gettimeofday(&tv, NULL); @@ -77,7 +79,7 @@ int globErr = 0; // errno for WARN/ERR * @param fmt ... - printf-like format * @return number of printed symbols */ -int r_pr_(const char *fmt, ...){ +static int r_pr_(const char *fmt, ...){ va_list ar; int i; printf(COLOR_RED); va_start(ar, fmt); @@ -86,7 +88,7 @@ int r_pr_(const char *fmt, ...){ printf(COLOR_OLD); return i; } -int g_pr_(const char *fmt, ...){ +static int g_pr_(const char *fmt, ...){ va_list ar; int i; printf(COLOR_GREEN); va_start(ar, fmt); @@ -101,7 +103,7 @@ int g_pr_(const char *fmt, ...){ * @param fmt ... - printf-like format * @return number of printed symbols */ -int r_WARN(const char *fmt, ...){ +static int r_WARN(const char *fmt, ...){ va_list ar; int i = 1; fprintf(stderr, COLOR_RED); va_start(ar, fmt); @@ -128,7 +130,7 @@ static const char stars[] = "****************************************"; * @param fmt ... - printf-like format * @return number of printed symbols */ -int s_WARN(const char *fmt, ...){ +static int s_WARN(const char *fmt, ...){ va_list ar; int i; i = fprintf(stderr, "\n%s\n", stars); va_start(ar, fmt); @@ -143,7 +145,7 @@ int s_WARN(const char *fmt, ...){ i += fprintf(stderr, "\n"); return i; } -int r_pr_notty(const char *fmt, ...){ +static int r_pr_notty(const char *fmt, ...){ va_list ar; int i; i = printf("\n%s\n", stars); va_start(ar, fmt); @@ -154,10 +156,10 @@ int r_pr_notty(const char *fmt, ...){ } /** - * @brief initial_setup - setup locale & console + * @brief sl_init - setup locale & console * Run this function in the beginning of main() to setup locale & coloured output */ -void initial_setup(){ +void sl_init(){ // setup coloured output if(isatty(STDOUT_FILENO)){ // make color output in tty red = r_pr_; green = g_pr_; @@ -180,10 +182,10 @@ void initial_setup(){ \******************************************************************************/ /** -* @brief throw_random_seed - Generate a quasy-random number to initialize PRNG +* @brief sl_random_seed - Generate a quasy-random number to initialize PRNG * @return value for srand48 */ -long throw_random_seed(){ +long sl_random_seed(){ long r_ini; int fail = 0; int fd = open("/dev/random", O_RDONLY); @@ -201,7 +203,7 @@ long throw_random_seed(){ close(fd); }while(0); if(fail){ - double tt = dtime() * 1e6; + double tt = sl_dtime() * 1e6; double mx = (double)LONG_MAX; r_ini = (long)(tt - mx * floor(tt/mx)); } @@ -209,10 +211,10 @@ long throw_random_seed(){ } /** - * @brief get_available_mem + * @brief sl_mem_avail * @return system available physical memory */ -uint64_t get_available_mem(){ +uint64_t sl_mem_avail(){ return sysconf(_SC_AVPHYS_PAGES) * (uint64_t) sysconf(_SC_PAGE_SIZE); } @@ -222,12 +224,12 @@ uint64_t get_available_mem(){ * Memory \******************************************************************************/ /** - * @brief my_alloc - safe memory allocation for macro ALLOC + * @brief sl_alloc - safe memory allocation for macro ALLOC * @param N - number of elements to allocate * @param S - size of single element (typically sizeof) * @return pointer to allocated memory area */ -void *my_alloc(size_t N, size_t S){ +void *sl_alloc(size_t N, size_t S){ void *p = calloc(N, S); if(!p) ERR("malloc"); //assert(p); @@ -235,11 +237,11 @@ void *my_alloc(size_t N, size_t S){ } /** - * @brief My_mmap - mmap file to a memory area + * @brief sl_mmap - mmap file to a memory area * @param filename (i) - name of file to mmap * @return stuct with mmap'ed file or die */ -mmapbuf *My_mmap(char *filename){ +sl_mmapbuf_t *sl_mmap(char *filename){ int fd; char *ptr; size_t Mlen; @@ -269,17 +271,17 @@ mmapbuf *My_mmap(char *filename){ } /// Не могу закрыть mmap'нутый файл if(close(fd)) WARN(_("Can't close mmap'ed file")); - mmapbuf *ret = MALLOC(mmapbuf, 1); + sl_mmapbuf_t *ret = MALLOC(sl_mmapbuf_t, 1); ret->data = ptr; ret->len = Mlen; return ret; } /** - * @brief My_munmap - unmap memory file + * @brief sl_munmap - unmap memory file * @param b (i) - mmap'ed buffer */ -void My_munmap(mmapbuf *b){ +void sl_munmap(sl_mmapbuf_t *b){ if(munmap(b->data, b->len)){ /// Не могу munmap ERR(_("Can't munmap")); @@ -287,44 +289,86 @@ void My_munmap(mmapbuf *b){ FREE(b); } +/** + * @brief sl_omitspaces - omit leading spaces + * @param v - source string + * @return pointer to first non-blank character (could be '\0' if end of string reached) + */ +char *sl_omitspaces(const char *v){ + if(!v) return NULL; + char *p = (char*)v; + while(*p && isspace(*p)) ++p; + return p; +} + +// the same as sl_omitspaces, but return (last non-space char + 1) in string or its first char +char *sl_omitspacesr(const char *v){ + if(!v) return NULL; + char *eol = (char*)v + strlen(v) - 1; + while(eol > v && isspace(*eol)) --eol; + if(eol == v && isspace(*eol)) return eol; + return eol + 1; +} + /******************************************************************************\ * Terminal in no-echo mode * BE CAREFULL! These functions aren't thread-safe! \******************************************************************************/ -static struct termios oldt, newt; // console flags +// console flags +#ifndef SL_USE_OLD_TTY +static struct termios2 oldt, newt; +#else +static struct termios oldt, newt; +#endif static int console_changed = 0; /** - * @brief restore_console - restore console to default mode + * @brief sl_restore_con - restore console to default mode */ -void restore_console(){ +void sl_restore_con(){ if(console_changed) +#ifndef SL_USE_OLD_TTY + ioctl(STDIN_FILENO, TCSETS2, &oldt); +#else tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state +#endif console_changed = 0; } /** - * @brief setup_con - setup console to non-canonical noecho mode + * @brief sl_setup_con - setup console to non-canonical noecho mode */ -void setup_con(){ +void sl_setup_con(){ if(console_changed) return; +#ifndef SL_USE_OLD_TTY + ioctl(STDIN_FILENO, TCGETS2, &oldt); +#else tcgetattr(STDIN_FILENO, &oldt); +#endif newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); +#ifndef SL_USE_OLD_TTY + if(ioctl(STDIN_FILENO, TCSETS2, &newt)){ +#else if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){ +#endif /// Не могу настроить консоль WARN(_("Can't setup console")); +#ifndef SL_USE_OLD_TTY + ioctl(STDIN_FILENO, TCSETS2, &oldt); +#else tcsetattr(STDIN_FILENO, TCSANOW, &oldt); +#endif signals(1); //quit? } console_changed = 1; } /** - * @brief read_console - read character from console without echo + * @brief sl_read_con - read character from console without echo * @return char read or zero */ -int read_console(){ +int sl_read_con(){ int rb; struct timeval tv; int retval; @@ -346,102 +390,58 @@ int read_console(){ * wait until at least one character pressed * @return character read */ -int mygetchar(){ +int sl_getchar(){ int ret; - do ret = read_console(); + do ret = sl_read_con(); while(ret == 0); return ret; } /** - * @brief str2double - safely convert data from string to double + * @brief sl_str2d - safely convert data from string to double * @param num (o) - double number read from string * @param str (i) - input string * @return 1 if success, 0 if fails */ -int str2double(double *num, const char *str){ +int sl_str2d(double *num, const char *str){ double res; char *endptr; - if(!str) return 0; + if(!str) return FALSE; res = strtod(str, &endptr); if(endptr == str || *str == '\0' || *endptr != '\0'){ - /// "Неправильный формат числа double!" - WARNX("Wrong double number format!"); + /// "Неправильный формат числа double '%s'" + WARNX(_("Wrong double number format '%s'"), str); return FALSE; } if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number return TRUE; } - -/******************************************************************************\ - * Logging to file - * DEPRECATED!!! DEPRECATED!!! DEPRECATED!!! DEPRECATED!!! DEPRECATED!!! DEPRECATED!!! -\******************************************************************************/ -FILE *Flog = NULL; // log file descriptor -char *logname = NULL; -time_t log_open_time = 0; -/** - * @brief openlogfile - try to open log file - * @param name (i) - log file name - * if failed show warning message - */ -void openlogfile(char *name){ - if(!name){ - /// Не задано имя логфайла - WARNX(_("Need filename for log file")); - return; +// and so on +int sl_str2ll(long long *num, const char *str){ + long long res; + char *endptr; + if(!str) return FALSE; + res = strtoll(str, &endptr, 0); + if(endptr == str || *str == '\0' || *endptr != '\0'){ + /// "Неправильный формат числа double!" + WARNX(_("Wrong number format!")); + return FALSE; } - /// Пробую открыть логфайл %s в режиме дополнения\n - green(_("Try to open log file %s in append mode\n"), name); - fflush(stdout); - if(!(Flog = fopen(name, "a"))){ - /// Не могу открыть логфайл - WARN(_("Can't open log file")); - return; - } - log_open_time = time(NULL); - logname = name; -} - -/** - * Save message to log file, rotate logs every 24 hours - */ -int putlog(const char *fmt, ...){ - if(!Flog) return 0; - time_t t_now = time(NULL); - if(t_now - log_open_time > 86400){ // rotate log - fprintf(Flog, "\n\t\t%sRotate log\n", ctime(&t_now)); - fclose(Flog); - char newname[PATH_MAX]; - snprintf(newname, PATH_MAX, "%s.old", logname); - if(rename(logname, newname)) WARN("rename()"); - openlogfile(logname); - if(!Flog) return 0; - } - //int i = fprintf(Flog, "%s\t\t", ctime(&t_now)); - char buf[256]; - strftime(buf, 255, "%Y/%m/%d %H:%M:%S", localtime(&t_now)); - int i = fprintf(Flog, "%s\t\t", buf); - va_list ar; - va_start(ar, fmt); - i = vfprintf(Flog, fmt, ar); - va_end(ar); - fprintf(Flog, "\n"); - fflush(Flog); - return i; + if(num) *num = res; + return TRUE; } /******************************************************************************\ * Logging to file \******************************************************************************/ -sl_log *globlog = NULL; // "global" log file (the first opened logfile) +sl_log_t *sl_globlog = NULL; // "global" log file (the first opened logfile) /** * @brief sl_createlog - create log file, test file open ability * @param logpath - path to log file * @param level - lowest message level (e.g. LOGLEVEL_ERR won't allow to write warn/msg/dbg) * @return allocated structure (should be free'd later by Cl_deletelog) or NULL */ -sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix){ +sl_log_t *sl_createlog(const char *logpath, sl_loglevel_e level, int prefix){ if(level < LOGLEVEL_NONE || level > LOGLEVEL_ANY) return NULL; if(!logpath) return NULL; FILE *logfd = fopen(logpath, "a"); @@ -450,7 +450,7 @@ sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix){ return NULL; } fclose(logfd); - sl_log *log = MALLOC(sl_log, 1); + sl_log_t *log = MALLOC(sl_log_t, 1); log->logpath = strdup(logpath); if(!log->logpath){ WARN("strdup()"); @@ -462,7 +462,7 @@ sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix){ return log; } -void sl_deletelog(sl_log **log){ +void sl_deletelog(sl_log_t **log){ if(!log || !*log) return; FREE((*log)->logpath); FREE(*log); @@ -476,7 +476,7 @@ void sl_deletelog(sl_log **log){ * @param fmt - format and the rest part of message * @return amount of symbols saved in file */ -int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...){ +int sl_putlogt(int timest, sl_log_t *log, sl_loglevel_e lvl, const char *fmt, ...){ if(!log || !log->logpath) return 0; if(lvl > log->loglevel) return 0; int i = 0; @@ -484,9 +484,9 @@ int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...){ if(!logfd) return 0; int lfd = fileno(logfd); // try to lock file - double t0 = dtime(); + double t0 = sl_dtime(); int locked = 0; - while(dtime() - t0 < 0.1){ // timeout for 0.1s + while(sl_dtime() - t0 < 0.1){ // timeout for 0.1s if(-1 == flock(lfd, LOCK_EX | LOCK_NB)) continue; locked = 1; break; diff --git a/usefull_macros.h b/usefull_macros.h index 8df2d8f..1194d9d 100644 --- a/usefull_macros.h +++ b/usefull_macros.h @@ -20,14 +20,17 @@ */ #pragma once -#ifndef __USEFULL_MACROS_H__ -#define __USEFULL_MACROS_H__ -#include // bool -#include // pid_t -#include // errno +#ifdef SL_USE_OLD_TTY #include // termios +#else +#include +#endif + +#include // errno #include // alloc, free +#include // pid_t +#include // pid_t // just for different purposes #include #include @@ -103,8 +106,8 @@ void WEAK signals(int sig); /* * Memory allocation */ -#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type))) -#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type))) +#define ALLOC(type, var, size) type * var = ((type *)sl_alloc(size, sizeof(type))) +#define MALLOC(type, size) ((type *)sl_alloc(size, sizeof(type))) #define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0) #ifndef DBL_EPSILON @@ -115,38 +118,48 @@ void WEAK signals(int sig); const char *sl_libversion(); // double value of UNIX time -double dtime(); +double sl_dtime(); // functions for color output in tty & no-color in pipes extern int (*red)(const char *fmt, ...); extern int (*_WARN)(const char *fmt, ...); extern int (*green)(const char *fmt, ...); // safe allocation -void * my_alloc(size_t N, size_t S); +void *sl_alloc(size_t N, size_t S); // setup locales & other -void initial_setup(); +void sl_init(); // mmap file typedef struct{ char *data; size_t len; -} mmapbuf; -mmapbuf *My_mmap(char *filename); -void My_munmap(mmapbuf *b); +} sl_mmapbuf_t; +sl_mmapbuf_t *sl_mmap(char *filename); +void sl_munmap(sl_mmapbuf_t *b); // console in non-echo mode -void restore_console(); -void setup_con(); -int read_console(); -int mygetchar(); +void sl_restore_con(); +void sl_setup_con(); +int sl_read_con(); +int sl_getchar(); -long throw_random_seed(); +long sl_random_seed(); -uint64_t get_available_mem(); +uint64_t sl_mem_avail(); + +// omit leading spaces +char *sl_omitspaces(const char *str); +// omit trailing spaces +char *sl_omitspacesr(const char *v); + +// convert string to double with checking +int sl_str2d(double *num, const char *str); +int sl_str2ll(long long *num, const char *str); /******************************************************************************\ The original term.h \******************************************************************************/ +#ifdef SL_USE_OLD_TTY typedef struct { char *portname; // device filename (should be freed before structure freeing) int speed; // baudrate in human-readable format @@ -157,23 +170,31 @@ typedef struct { char *buf; // buffer for data read size_t bufsz; // size of buf size_t buflen; // length of data read into buf - bool exclusive; // should device be exclusive opened -} TTY_descr; + int exclusive; // should device be exclusive opened +} sl_tty_t; -void close_tty(TTY_descr **descr); -TTY_descr *new_tty(char *comdev, int speed, size_t bufsz); -TTY_descr *tty_open(TTY_descr *d, int exclusive); -int read_tty(TTY_descr *descr); -int tty_timeout(double usec); -int write_tty(int comfd, const char *buff, size_t length); -tcflag_t conv_spd(int speed); +tcflag_t sl_tty_convspd(int speed); +#else +typedef struct { + char *portname; // device filename (should be freed before structure freeing) + int speed; // baudrate in human-readable format + char *format; // format like 8N1 + struct termios2 oldtty; // TTY flags for previous port settings + struct termios2 tty; // TTY flags for current settings + int comfd; // TTY file descriptor + char *buf; // buffer for data read + size_t bufsz; // size of buf + size_t buflen; // length of data read into buf + int exclusive; // should device be exclusive opened +} sl_tty_t; +#endif -// convert string to double with checking -int str2double(double *num, const char *str); - -// logging (deprecated) -void __attribute__ ((deprecated)) openlogfile(char *name); -int __attribute__ ((deprecated)) putlog(const char *fmt, ...); +void sl_tty_close(sl_tty_t **descr); +sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz); +sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive); +int sl_tty_read(sl_tty_t *descr); +int sl_tty_tmout(double usec); +int sl_tty_write(int comfd, const char *buff, size_t length); /******************************************************************************\ Logging @@ -184,31 +205,32 @@ typedef enum{ LOGLEVEL_WARN, // only warnings and errors LOGLEVEL_MSG, // all without debug LOGLEVEL_DBG, // all messages - LOGLEVEL_ANY // all shit -} sl_loglevel; + LOGLEVEL_ANY, // all shit + LOGLEVEL_AMOUNT // total amount +} sl_loglevel_e; typedef struct{ char *logpath; // full path to logfile - sl_loglevel loglevel; // loglevel + sl_loglevel_e loglevel; // loglevel int addprefix; // if !=0 add record type to each line(e.g. [ERR]) -} sl_log; +} sl_log_t; -extern sl_log *globlog; // "global" log file +extern sl_log_t *sl_globlog; // "global" log file -sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix); -void sl_deletelog(sl_log **log); -int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...); +sl_log_t *sl_createlog(const char *logpath, sl_loglevel_e level, int prefix); +void sl_deletelog(sl_log_t **log); +int sl_putlogt(int timest, sl_log_t *log, sl_loglevel_e lvl, const char *fmt, ...); // open "global" log -#define OPENLOG(nm, lvl, prefix) (globlog = sl_createlog(nm, lvl, prefix)) +#define OPENLOG(nm, lvl, prefix) (sl_globlog = sl_createlog(nm, lvl, prefix)) // shortcuts for different log levels; ..ADD - add message without timestamp -#define LOGERR(...) do{sl_putlogt(1, globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0) -#define LOGERRADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0) -#define LOGWARN(...) do{sl_putlogt(1, globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0) -#define LOGWARNADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0) -#define LOGMSG(...) do{sl_putlogt(1, globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0) -#define LOGMSGADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0) -#define LOGDBG(...) do{sl_putlogt(1, globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0) -#define LOGDBGADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0) +#define LOGERR(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0) +#define LOGERRADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0) +#define LOGWARN(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0) +#define LOGWARNADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0) +#define LOGMSG(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0) +#define LOGMSGADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0) +#define LOGDBG(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0) +#define LOGDBGADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0) /******************************************************************************\ The original parseargs.h @@ -217,7 +239,7 @@ int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...); #define APTR(x) ((void*)x) // if argptr is a function: -typedef bool(*argfn)(void *arg); +typedef int(*sl_argfn_t)(void *arg); /** * type of getopt's argument @@ -238,8 +260,8 @@ typedef enum { arg_double, // double arg_float, // float arg_string, // char * - arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)` -} argtype; + arg_function // parse_args will run function `int (*fn)(char *optarg, int N)` +} sl_argtype_e; /** * Structure for getopt_long & help @@ -265,30 +287,30 @@ typedef enum{ NEED_ARG = 1, OPT_ARG = 2, MULT_PAR -} hasarg; +} sl_hasarg_e; typedef struct{ // these are from struct option: const char *name; // long option's name - hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array) + sl_hasarg_e has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 3 - need arg & key can repeat (args are stored in null-terminated array) int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0) int val; // short opt name (if flag == NULL) or flag's value // and these are mine: - argtype type; // type of argument - void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)` + sl_argtype_e type; // type of argument + void *argptr; // pointer to variable to assign optarg value or function `int (*fn)(char *optarg, int N)` const char *help; // help string which would be shown in function `showhelp` or NULL -} myoption; +} sl_option_t; /* - * Suboptions structure, almost the same like myoption + * Suboptions structure, almost the same like sl_option_t * used in parse_subopts() */ typedef struct{ - const char *name; - hasarg has_arg; - argtype type; - void *argptr; -} mysuboption; + const char *name; + sl_hasarg_e has_arg; + sl_argtype_e type; + void *argptr; +} sl_suboption_t; // last string of array (all zeros) #define end_option {0,0,0,0,0,0,0} @@ -296,14 +318,14 @@ typedef struct{ extern const char *__progname; -void showhelp(int oindex, myoption *options); -void parseargs(int *argc, char ***argv, myoption *options); +void sl_showhelp(int oindex, sl_option_t *options); +void sl_parseargs(int *argc, char ***argv, sl_option_t *options); /** - * @brief change_helpstring - change standard help header + * @brief sl_helpstring - change standard help header * @param str (i) - new format (MAY consist ONE "%s" for progname) */ -void change_helpstring(char *s); -bool get_suboption(char *str, mysuboption *opt); +void sl_helpstring(char *s); +int sl_get_suboption(char *str, sl_suboption_t *opt); /******************************************************************************\ @@ -314,24 +336,44 @@ bool get_suboption(char *str, mysuboption *opt); #endif // default function to run if another process found -void WEAK iffound_default(pid_t pid); +void WEAK sl_iffound_deflt(pid_t pid); // check that our process is exclusive -void check4running(char *selfname, char *pidfilename); +void sl_check4running(char *selfname, char *pidfilename); // read name of process by its PID -char *readPSname(pid_t pid); +char *sl_getPSname(pid_t pid); /******************************************************************************\ The original fifo_lifo.h \******************************************************************************/ -typedef struct buff_node{ +typedef struct sl_buff_node{ void *data; - struct buff_node *next, *last; -} List; + struct sl_buff_node *next, *last; +} sl_list_t; -List *list_push_tail(List **lst, void *v); -List *list_push(List **lst, void *v); -void *list_pop(List **lst); +sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v); +sl_list_t *sl_list_push(sl_list_t **lst, void *v); +void *sl_list_pop(sl_list_t **lst); +/******************************************************************************\ + The original config.h +\******************************************************************************/ +// max length of key (including '\0') +#define SL_KEY_LEN (32) +// max length of value (including '\0') +#define SL_VAL_LEN (128) -#endif // __USEFULL_MACROS_H__ +// starting symbol of any comment +#define SL_COMMENT_CHAR '#' + +// option or simple configuration value (don't work for functions) +typedef struct{ + union{ + int ival; long long llval; double dval; float fval; + }; + sl_argtype_e type; +} 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);