6 Commits

18 changed files with 1127 additions and 784 deletions

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 4.0) cmake_minimum_required(VERSION 4.0)
set(PROJ usefull_macros) set(PROJ usefull_macros)
set(MINOR_VERSION "3") set(MINOR_VERSION "5")
set(MID_VERSION "3") set(MID_VERSION "3")
set(MAJOR_VERSION "0") set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
@@ -8,7 +8,7 @@ set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
project(${PROJ} VERSION ${VERSION} LANGUAGES C) project(${PROJ} VERSION ${VERSION} LANGUAGES C)
# default flags # default flags
set(CMAKE_C_FLAGS "${CFLAGS} -O2") set(CMAKE_C_FLAGS "${CFLAGS} -O2 -pedantic-errors")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W")
set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_COLOR_MAKEFILE ON)
@@ -51,16 +51,6 @@ message("Build type: ${CMAKE_BUILD_TYPE}")
# here is one of two variants: all .c in directory or .c files in list # here is one of two variants: all .c in directory or .c files in list
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) 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 # directory should contain dir locale/ru for gettext translations
set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru) set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru)
if(NOT DEFINED LOCALEDIR) if(NOT DEFINED LOCALEDIR)
@@ -110,7 +100,7 @@ add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\"
-DMAJOR_VERSION=\"${MAJOR_VESION}\") -DMAJOR_VERSION=\"${MAJOR_VESION}\")
# -l # -l
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES}) target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
set(PCFILE "${CMAKE_BINARY_DIR}/${PROJ}.pc") set(PCFILE "${CMAKE_BINARY_DIR}/${PROJ}.pc")
configure_file("${PROJ}.pc.in" ${PCFILE} @ONLY) configure_file("${PROJ}.pc.in" ${PCFILE} @ONLY)
@@ -154,7 +144,7 @@ if(NOT DEFINED NOGETTEXT)
# we need this to prevent ru.po & .mo from deleting by make clean # we need this to prevent ru.po & .mo from deleting by make clean
add_custom_target( add_custom_target(
RU_FILE RU_FILE
COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -UiF ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE}
DEPENDS ${PO_FILE} ${SOURCES} DEPENDS ${PO_FILE} ${SOURCES}
) )
add_custom_target( add_custom_target(

View File

@@ -1,3 +1,36 @@
Wed Mar 4 14:18:15 MSK 2026
VERSION 0.3.4:
- fixed wrong UNIX-sockets names
- code changed for `-pedantic-errors`
VERSION 0.3.5
- add int sl_sock_open(sl_socktype_e type, const char *path, int isserver, int ai_socktype);
open socket, bind or connect it and return descriptor
- deprecation of SL_USE_OLD_TTY
- add int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive)
open and configure serial port and return descriptor
- remove both fields `struct termios2` from `sl_tty_t`
- add int sl_tty_setformat(sl_tty_t *d, const char *format)
change default format "8N1" to given
Thu Nov 6 11:25:07 MSK 2025
VERSION 0.3.4:
add data type:
- sl_sock_keyno_t - number of key (like key[keyno] = val) using in sl_sock_hitem_t.data
add functions:
- sl_sock_keyno_init - init keyno (or use macro SL_SOCK_KEYNO_DEFAUL in assignment)
- sl_sock_keyno_check - return -1 if there's no keyno, else return its value
New functional listed in examples/clientserver.c introducing key `flags` that allows to work with bit flags as a whole or by bits like flags[1]=1, flags21=0, flags{31} or flags(14)...
Wed Sep 10 14:19:24 MSK 2025
(still version 0.3.3: I forgot to add changelog last commits)
- fixed minor bugs and memory leaks
- use common way for parsing commandline arguments and content of configuration files, so now you can use arrays in configurations (several parameters with same name)
- add functions:
- - int sl_remove_quotes(char *string) - remove outern quotes (" and '), return amount of pairs found
- - void sl_conf_showhelp(int idx, sl_option_t *options) - show help for config file parameters (only long options, without '--' and without exit(1) at the end of function)
- - void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*)) - now this funtion available for external using, parsing of arguments with user help function
Dec 16 2024 Dec 16 2024
VERSION 0.2.1: VERSION 0.2.1:

2
TODO Normal file
View File

@@ -0,0 +1,2 @@
Add common case of binary search?
Add hash generation for certain keys list?

364
config.c
View File

@@ -24,6 +24,28 @@
#include "usefull_macros.h" #include "usefull_macros.h"
/**
* @brief sl_remove_quotes - remove all outern quotes - starting and trailng " and '
* @param (io) string to modify (if quotes found rest of string will be moved to head, tail will be zeroed)
* @return amount of quotation pair found
*/
int sl_remove_quotes(char *string){
if(!string) return 0;
int l = strlen(string);
if(l < 2) return 0;
int nq = 0, half = l/2;
for(; nq < half; ++nq){
char _1st = string[nq];
if(_1st != string[l-1-nq]) break;
if(_1st != '\'' && _1st != '"') break;
}
if(nq == 0) return 0;
l -= 2 * nq;
memmove(string, string + nq, l);
string[l] = 0;
return nq;
}
/** /**
* @brief sl_get_keyval - get key name and its value from string pair * @brief sl_get_keyval - get key name and its value from string pair
* @param pair - empty string, `key = value` or just `key` * @param pair - empty string, `key = value` or just `key`
@@ -66,6 +88,7 @@ int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]
for(; kend < eq && !isspace(*kend); ++kend); for(; kend < eq && !isspace(*kend); ++kend);
size_t l = SL_KEY_LEN - 1; size_t l = SL_KEY_LEN - 1;
if(l > (size_t)(kend - kstart)) l = kend - kstart; if(l > (size_t)(kend - kstart)) l = kend - kstart;
else if(l < (size_t)(kend - kstart)) WARNX(_("sl_get_keyval(): key would be trunkated to %d symbols"), l);
//DBG("kend=%c, kstart=%c, l=%zd", *kend, *kstart, l); //DBG("kend=%c, kstart=%c, l=%zd", *kend, *kstart, l);
strncpy(key, kstart, l); strncpy(key, kstart, l);
key[l] = 0; key[l] = 0;
@@ -90,13 +113,74 @@ static int read_key(FILE *file, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){
return r; return r;
} }
// search `opt` record for given `key` // print option value
static sl_option_t *opt_search(const char *key, sl_option_t *options){ static size_t pr_val(sl_argtype_e type, void *argptr, char **buffer, size_t *buflen, size_t pos){
while(options->name){ size_t maxlen = *buflen - pos - 1;
if(0 == strcmp(key, options->name)) return options; char *buf = *buffer + pos;
++options; switch(type){
case arg_none:
case arg_int:
DBG("int %d", *(int*) argptr);
return snprintf(buf, maxlen, "%d", *(int*) argptr);
case arg_longlong:
DBG("long long %lld", *(long long*) argptr);
return snprintf(buf, maxlen, "%lld", *(long long*)argptr);
case arg_float:
DBG("float %g", *(float*) argptr);
return snprintf(buf, maxlen, "%g", *(float*) argptr);
case arg_double:
DBG("double %g", *(double*) argptr);
return snprintf(buf, maxlen, "%g", *(double*) argptr);
case arg_string:
if(!argptr || !(*(char**)argptr)){
return snprintf(buf, maxlen, "(null)");
}else if(!(**(char**)argptr)){
return snprintf(buf, maxlen, "(empty)");
}
char *str = *(char**) argptr;
DBG("string %s", str);
size_t z = strlen(str);
while(pos + z > *buflen + 5){
*buflen += BUFSIZ;
*buffer = realloc(*buffer, *buflen);
if(!*buffer) ERRX("realloc()");
maxlen += BUFSIZ;
buf = *buffer + pos;
}
return snprintf(buf, maxlen, "\"%s\"", str);
default:
DBG("function");
return snprintf(buf, maxlen, "\"(unsupported)\"");
} }
return NULL; return 0;
}
// print one option
static size_t print_opt(sl_option_t *opt, char **buffer, size_t *buflen, size_t pos){
if((ssize_t)*buflen - pos < 3 *(SL_KEY_LEN + SL_VAL_LEN)){
*buflen += BUFSIZ;
*buffer = realloc(*buffer, *buflen);
if(!*buffer) ERR("realloc()");
}
char *buf = *buffer + pos;
size_t l = 0, maxlen = *buflen - pos - 1;
size_t got = snprintf(buf, maxlen, "%s = ", opt->name);
l = got; maxlen -= got; buf += got;
if(opt->flag){
DBG("got flag '%d'", *opt->flag);
l += snprintf(buf, maxlen, "%d\n", *opt->flag);
return l;
}
if(!opt->argptr){ // ERR!
l += snprintf(buf, maxlen, "\"(no argptr)\"\n");
WARNX("Parameter \"%s\" have no argptr!", opt->name);
return l;
}
DBG("type: %d", opt->type);
got = pr_val(opt->type, opt->argptr, buffer, buflen, pos + l);
l += got; maxlen -= got; buf += got;
l += snprintf(buf, maxlen, "\n");
return l;
} }
/** /**
@@ -108,156 +192,44 @@ static sl_option_t *opt_search(const char *key, sl_option_t *options){
char *sl_print_opts(sl_option_t *opt, int showall){ char *sl_print_opts(sl_option_t *opt, int showall){
char *buf = MALLOC(char, BUFSIZ); char *buf = MALLOC(char, BUFSIZ);
size_t L = BUFSIZ, l = 0; size_t L = BUFSIZ, l = 0;
for(; opt->name; ++opt){ for(; opt->help; ++opt){
if(!opt->name) continue; // only show help - not config option!
DBG("check %s", opt->name); DBG("check %s", opt->name);
if(!showall && opt->has_arg == NO_ARGS) continue; // show NO_ARGS only when `showall==TRUE` 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(!showall && opt->type == arg_string && !opt->argptr) continue; // empty string
if((ssize_t)L - l < SL_KEY_LEN + SL_VAL_LEN + 5){ if(opt->has_arg == MULT_PAR){
L += BUFSIZ; sl_option_t tmpopt = *opt;
buf = realloc(buf, L); DBG("type: %d", tmpopt.type);
if(!buf) ERR("realloc()"); if(!opt->argptr){ DBG("No pointer to array!"); continue; }
} #if 0
l += sprintf(buf + l, "%s=", opt->name); void ***pp = (void***)opt->argptr;
if(opt->flag){ if(!*(char***)pp){ DBG("Array is empty"); continue; }
DBG("got flag '%d'", *opt->flag); while(**pp){
l += sprintf(buf + l, "%d\n", *opt->flag); if(opt->type == arg_string){
continue; DBG("str");
} tmpopt.argptr = *pp; // string is pointer to pointer!
if(!opt->argptr){ // ERR! }else tmpopt.argptr = **pp;
l += sprintf(buf + l, "\"(no argptr)\"\n"); if(!tmpopt.argptr){ DBG("null"); break; }
WARNX("Parameter \"%s\" have no argptr!", opt->name); l += print_opt(&tmpopt, &buf, &L, l);
continue; ++(*pp);
} }
int z = 0; #endif
DBG("type: %d", opt->type); void **pp = *(void***)opt->argptr;
switch(opt->type){ if(!(char**)pp){ DBG("Array is empty"); continue; }
case arg_none: while(*pp){
case arg_int: if(opt->type == arg_string){
DBG("int %d", *(int*) opt->argptr); DBG("str");
l += sprintf(buf + l, "%d", *(int*) opt->argptr); tmpopt.argptr = pp; // string is pointer to pointer!
break; }else tmpopt.argptr = *pp;
case arg_longlong: if(!tmpopt.argptr){ DBG("null"); break; }
DBG("long long %lld", *(long long*) opt->argptr); l += print_opt(&tmpopt, &buf, &L, l);
l += sprintf(buf + l, "%lld", *(long long*) opt->argptr); ++(pp);
break; }
case arg_float: }else l += print_opt(opt, &buf, &L, l);
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; 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: * @brief sl_conf_readopts - simplest configuration:
@@ -273,29 +245,85 @@ int sl_conf_readopts(const char *filename, sl_option_t *options){
WARN(_("Can't open %s"), filename); WARN(_("Can't open %s"), filename);
return 0; return 0;
} }
int N = 0; int argc = 1;
char key[SL_KEY_LEN], val[SL_VAL_LEN]; #define BUFSZ (SL_KEY_LEN+SL_VAL_LEN+8)
char key[SL_KEY_LEN], val[SL_VAL_LEN], obuf[BUFSZ];
int argvsize = 0;
char **argv = NULL;
do{ do{
int r = read_key(f, key, val); int r = read_key(f, key, val);
if(r < 0) break; if(r < 0) break;
if(r == 0) continue; if(r == 0) continue;
sl_option_t *opt = opt_search(key, options); DBG("key='%s', val='%s'", key, (r == 2) ? val : "(absent)");
if(!opt){ ++argc;
WARNX(_("Wrong key: '%s'"), key); if(argvsize <= argc){
continue; argvsize += 256;
argv = realloc(argv, sizeof(char*) * argvsize);
if(!argv) ERRX("sl_conf_readopts: realloc() error");
} }
if(opt->flag) *opt->flag = opt->val; if(argc == 2) argv[0] = strdup(__progname); // all as should be
if(r == 1){ // only key if(r == 2){
if(opt->has_arg != NO_ARGS && opt->has_arg != OPT_ARG){ // remove trailing/ending quotes
WARNX(_("Key '%s' need value"), opt->name); sl_remove_quotes(val);
continue; snprintf(obuf, BUFSZ-1, "--%s=%s", key, val);
} }else snprintf(obuf, BUFSZ-1, "--%s", key);
if(opt->argptr) setoa(opt, "1"); DBG("next argv: '%s'", obuf);
}else{ // key + value argv[argc-1] = strdup(obuf);
if(opt->argptr) setoa(opt, val);
else WARNX(_("Key '%s' have no argptr!"), opt->name);
}
++N;
}while(1); }while(1);
return N; if(!argc) return 0;
int N = argc; char **a = argv;
sl_parseargs_hf(&argc, &a, options, sl_conf_showhelp);
for(int n = 0; n < N; ++n) free(argv[n]);
free(argv);
return N - argc; // amount of recognized options
}
// sort only by long options
static int confsort(const void *a1, const void *a2){
const sl_option_t *o1 = (sl_option_t*)a1, *o2 = (sl_option_t*)a2;
const char *l1 = o1->name, *l2 = o2->name;
// move empty options to end of list
if(!l1 && !l2) return 1;
if(!l1) return 1;
if(!l2) return -1;
return strcmp(l1, l2);
}
static void pr_helpstring(sl_option_t *opt){
if(!opt->name || !opt->help) return;
printf(" %s", opt->name);
if(opt->has_arg == NEED_ARG || opt->has_arg == MULT_PAR) // required argument
printf(" = arg");
else if(opt->has_arg == OPT_ARG) // optional argument
printf(" [= arg]");
printf(" -- %s", opt->help);
if(opt->has_arg == MULT_PAR) printf(" (can occur multiple times)");
printf("\n");
}
/**
* @brief sl_conf_showhelp - show help for config file
* (the same as `sl_showhelp`, but without "--", short opts and exit()
* @param options - config options (only long opts used)
*/
void sl_conf_showhelp(int idx, sl_option_t *options){
if(!options || !(*options).help) return;
// count amount of options
sl_option_t *opts = options;
int N; for(N = 0; opts->help; ++N, ++opts);
if(N == 0) exit(-2);
if(idx > -1){
if(idx >=N) WARNX(_("sl_conf_showhelp(): wrong index"));
else pr_helpstring(&options[idx]);
return;
}
sl_option_t *tmpopts = MALLOC(sl_option_t, N);
memcpy(tmpopts, options, N*sizeof(sl_option_t));
printf(_("Configuration file options (format: key=value):\n"));
qsort(tmpopts, N, sizeof(sl_option_t), confsort);
for(int _ = 0; _ < N; ++_){
if(!tmpopts[_].name) continue;
pr_helpstring(&tmpopts[_]);
}
free(tmpopts);
} }

View File

@@ -2,8 +2,10 @@ cmake_minimum_required(VERSION 3.9)
project(examples) project(examples)
# common includes & library # common includes & library
include_directories(../) #include_directories(../)
link_libraries(usefull_macros) #link_libraries(usefull_macros)
link_libraries(${CMAKE_BINARY_DIR}/libusefull_macros.so)
include_directories(${CMAKE_SOURCE_DIR})
# exe list # exe list
add_executable(helloworld helloworld.c) add_executable(helloworld helloworld.c)

View File

@@ -122,6 +122,7 @@ static void runclient(sl_sock_t *s){
static sl_sock_int_t iflag = {0}; static sl_sock_int_t iflag = {0};
static sl_sock_double_t dflag = {0}; static sl_sock_double_t dflag = {0};
static sl_sock_string_t sflag = {0}; static sl_sock_string_t sflag = {0};
static uint32_t bitflags = 0;
static sl_sock_hresult_e dtimeh(sl_sock_t _U_ *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){ static sl_sock_hresult_e dtimeh(sl_sock_t _U_ *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
char buf[32]; char buf[32];
@@ -169,6 +170,7 @@ static void disconnected(sl_sock_t *c){
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd); if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP); else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
} }
// default (unknown key) handler
static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
if(!s || !str) return RESULT_FAIL; if(!s || !str) return RESULT_FAIL;
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n"); sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
@@ -176,6 +178,33 @@ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n"); sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
return RESULT_SILENCE; return RESULT_SILENCE;
} }
// if we use this macro, there's no need to run `sl_sock_keyno_init` later
static sl_sock_keyno_t kph_number = SL_SOCK_KEYNO_DEFAULT;
// handler for key with optional parameter number
static sl_sock_hresult_e keyparhandler(struct sl_sock *s, sl_sock_hitem_t *item, const char *req){
if(!item->data) return RESULT_FAIL;
char buf[1024];
int no = sl_sock_keyno_check((sl_sock_keyno_t*)item->data);
long long newval = -1;
if(req){
if(!sl_str2ll(&newval, req) || newval < 0 || newval > 0xffffffff) return RESULT_BADVAL;
}
printf("no = %d\n", no);
if(no < 0){ // flags as a whole
if(req) bitflags = (uint32_t)newval;
snprintf(buf, 1023, "flags = 0x%08X\n", bitflags);
sl_sock_sendstrmessage(s, buf);
}else if(no < 32){ // bit access
int bitmask = 1 << no;
if(req){
if(newval) bitflags |= bitmask;
else bitflags &= ~bitmask;
}
snprintf(buf, 1023, "flags[%d] = %d\n", no, bitflags & bitmask ? 1 : 0);
sl_sock_sendstrmessage(s, buf);
}else return RESULT_BADKEY;
return RESULT_SILENCE;
}
static sl_sock_hitem_t handlers[] = { static sl_sock_hitem_t handlers[] = {
{sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag}, {sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
@@ -183,6 +212,7 @@ static sl_sock_hitem_t handlers[] = {
{sl_sock_strhandler, "str", "set/get string variable", (void*)&sflag}, {sl_sock_strhandler, "str", "set/get string variable", (void*)&sflag},
{show, "show", "show current flags @ server console", NULL}, {show, "show", "show current flags @ server console", NULL},
{dtimeh, "dtime", "get server's UNIX time for all clients connected", NULL}, {dtimeh, "dtime", "get server's UNIX time for all clients connected", NULL},
{keyparhandler, "flags", "set/get bit flags as whole (flags=val) or by bits (flags[bit]=val)", (void*)&kph_number},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };
@@ -193,10 +223,13 @@ int main(int argc, char **argv){
if(!G.node) ERRX("Point node"); if(!G.node) ERRX("Point node");
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET; sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
if(G.isserver){ if(G.isserver){
//sl_sock_keyno_init(&kph_number); // don't forget to init first or use macro in initialisation
s = sl_sock_run_server(type, G.node, -1, handlers); s = sl_sock_run_server(type, G.node, -1, handlers);
DBG("Server started");
} else { } else {
sl_setup_con(); sl_setup_con();
s = sl_sock_run_client(type, G.node, -1); s = sl_sock_run_client(type, G.node, -1);
DBG("Client started");
} }
if(!s) ERRX("Can't create socket and/or run threads"); if(!s) ERRX("Can't create socket and/or run threads");
if(G.isserver){ if(G.isserver){

View File

@@ -50,22 +50,28 @@ static glob_pars const Gdefault = {
/* /*
* Define command line options by filling structure: * Define command line options by filling structure:
* name has_arg flag val type argptr help * name has_arg flag val type argptr help
* BE carefull! The `help` field is mandatory! Omitting it equivalent of 'end_option'
*/ */
static sl_option_t cmdlnopts[] = { static sl_option_t cmdlnopts[] = {
{"lo0", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo0), _("only long arg 0")}, // short option in only-long options should be zeroed, or you can add flag to set it to given value
{"lo0", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo0), _("only long arg 0 (int)")},
// for short-only options long option can be NULL
{NULL, NEED_ARG, NULL, '0', arg_string, APTR(&G.so1), _("only short arg 1 (string)")},
// if you change `arg_int` to `arg_none`, value will be incremented each `-h`
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, {"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")}, {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name")},
{"lo2", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo2), _("only long arg 2")}, // for short-only options long option can also be an empty string
{"", NEED_ARG, NULL, '1', arg_string, APTR(&G.so2), _("only short arg 2 (string)")},
{"lo2", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo2), _("only long arg 2 (int)")},
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("serial device speed (default: 9600)")}, {"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")}, {"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 ")")}, {"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")}, {"exclusive",NO_ARGS, NULL, 'e', arg_int, APTR(&G.exclusive), _("open serial device exclusively")},
// example of multiple options // example of multiple options
{"Int", MULT_PAR, NULL, 'I', arg_int, APTR(&G.intarr), _("integer multiplying parameter")}, {"Int", MULT_PAR, NULL, 'I', arg_int, APTR(&G.intarr), _("integer parameter")},
{"Dbl", MULT_PAR, NULL, 'D', arg_double, APTR(&G.dblarr), _("double multiplying parameter")}, {"Dbl", MULT_PAR, NULL, 'D', arg_double, APTR(&G.dblarr), _("double parameter")},
{"Str", MULT_PAR, NULL, 'S', arg_string, APTR(&G.strarr), _("string multiplying parameter")}, {"Str", MULT_PAR, NULL, 'S', arg_string, APTR(&G.strarr), _("string parameter")},
{"lo1", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo1), _("only long arg 1")}, {"lo1", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo1), _("only long arg 1 (int)")},
end_option end_option
}; };

View File

@@ -38,6 +38,8 @@ typedef struct{
int lo0; // only long options int lo0; // only long options
int lo1; int lo1;
int lo2; int lo2;
char *so1; // only short options
char *so2;
int rest_pars_num; // number of rest parameters int rest_pars_num; // number of rest parameters
char** rest_pars; // the rest parameters: array of char* char** rest_pars; // the rest parameters: array of char*
} glob_pars; } glob_pars;

View File

@@ -23,12 +23,10 @@
#include "usefull_macros.h" #include "usefull_macros.h"
typedef struct{ typedef struct{
char *sp1; char **sp;
char *sp2;
int ip1; int ip1;
int ip2; int ip2;
double dp1; double **dp;
double dp2;
float fp1; float fp1;
float fp2; float fp2;
int help; int help;
@@ -36,66 +34,94 @@ typedef struct{
char *confname; char *confname;
} parameters; } parameters;
static parameters G = { static parameters G, parini = {
.ip1 = INT_MIN, .ip1 = INT_MIN,
.ip2 = INT_MIN, .ip2 = INT_MIN,
.dp1 = NAN,
.dp2 = NAN,
.fp1 = NAN, .fp1 = NAN,
.fp2 = NAN .fp2 = NAN
}; };
#define CONFOPTS \
{"string", MULT_PAR, NULL, 's', arg_string, APTR(&G.sp), "string array"}, \
{"int1", NEED_ARG, NULL, 'i', arg_int, APTR(&G.ip1), "integer one"}, \
{"int2", NEED_ARG, NULL, 'u', arg_int, APTR(&G.ip2), "integer two"}, \
{"double", MULT_PAR, NULL, 'd', arg_double, APTR(&G.dp), "double array"}, \
{"float1", NEED_ARG, NULL, 'f', arg_float, APTR(&G.fp1), "float one"}, \
{"float2", NEED_ARG, NULL, 'l', arg_float, APTR(&G.fp2), "float two"}, \
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose),"verbose level (each -v adds 1)"},
static sl_option_t cmdlnopts[] = { static sl_option_t cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"}, {"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"}, CONFOPTS
{"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"}, {"config", NEED_ARG, NULL, 'C', arg_string, APTR(&G.confname),"name of configuration file"},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose),"verbose level (each -v adds 1)"}, end_option
};
// config options - without some unneed
static sl_option_t confopts[] = {
CONFOPTS
end_option end_option
}; };
int main(int argc, char **argv){ int main(int argc, char **argv){
sl_init(); sl_init();
G = parini;
sl_parseargs(&argc, &argv, cmdlnopts); sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, cmdlnopts); if(G.help) sl_showhelp(-1, cmdlnopts);
// if you will end main options with '--', you can write some additional options after and again run sl_parseargs with other sl_option_t array
if(argc) for(int i = 0; i < argc; ++i){
red("Extra arg: `%s`\n", argv[i]);
}
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR; sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1; if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
printf("verbose level: %d\n", lvl); printf("verbose level: %d\n", lvl);
if(G.sp1){ if(G.sp){
printf("Parsing of string1: "); char **s = G.sp;
char key[SL_KEY_LEN], val[SL_VAL_LEN]; while(*s){
int k = sl_get_keyval(G.sp1, key, val); printf("Parsing of string: ");
switch(k){ char key[SL_KEY_LEN], val[SL_VAL_LEN];
case 0: int k = sl_get_keyval(*s, key, val);
red("key not found\n"); switch(k){
break; case 0:
case 1: red("key not found\n");
green("got key='%s'\n", key); break;
break; case 1:
default: green("got key='%s'\n", key);
green("got key='%s', value='%s'\n", key, val); break;
default:
green("got key='%s', value='%s'\n", key, val);
}
++s;
} }
} }
green("Starting parameters values:\n"); green("Starting parameters values:\n");
char *buf = sl_print_opts(cmdlnopts, TRUE); char *buf = sl_print_opts(cmdlnopts, TRUE);
printf("%s\n", buf); printf("%s\n", buf);
FREE(buf); FREE(buf); // don't forget to `free` this buffer
if(G.confname){ if(G.confname){
int o = sl_conf_readopts(G.confname, cmdlnopts); const char *confname = G.confname;
G = parini;
printf("now v=%d\n", G.verbose);
int o = sl_conf_readopts(confname, confopts);
if(o > 0){ if(o > 0){
printf("got %d options in '%s'\n", o, G.confname); printf("got %d options in '%s'\n", o, confname);
green("And after reading of conffile:\n"); green("And after reading of conffile:\n");
buf = sl_print_opts(cmdlnopts, TRUE); buf = sl_print_opts(confopts, TRUE);
printf("%s\n", buf); printf("%s\n", buf);
FREE(buf); FREE(buf);
} }
// if we want to re-read conffile many times over program runs, don't forget to `free` old arrays like this:
if(G.dp){
DBG("Clear double array");
double **p = G.dp;
while(*p){ FREE(*p); ++p; }
FREE(G.dp);
}
if(G.sp){
DBG("Clear string array %s", *G.sp);
char **s = G.sp;
while(*s){ FREE(*s); ++s; }
FREE(G.sp);
}
} }
return 0; return 0;
} }

View File

@@ -48,8 +48,13 @@ void signals(int sig){
LOGERR("Exit with status %d", sig); LOGERR("Exit with status %d", sig);
if(GP && GP->pidfile) // remove unnesessary PID file if(GP && GP->pidfile) // remove unnesessary PID file
unlink(GP->pidfile); unlink(GP->pidfile);
DBG("restore console");
sl_restore_con(); sl_restore_con();
if(dev) sl_tty_close(&dev); if(dev){
DBG("Close serial device");
sl_tty_close(&dev);
}
DBG("OK, exit");
exit(sig); exit(sig);
} }
@@ -67,12 +72,6 @@ int main(int argc, char *argv[]){
} }
sl_check4running((char*)__progname, GP->pidfile); sl_check4running((char*)__progname, GP->pidfile);
red("%s started, snippets library version is %s\n", __progname, sl_libversion()); red("%s started, snippets library version is %s\n", __progname, sl_libversion());
sl_setup_con();
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
if(GP->logfile) OPENLOG(GP->logfile, LOGLEVEL_ANY, 1); if(GP->logfile) OPENLOG(GP->logfile, LOGLEVEL_ANY, 1);
LOGMSG("Start application..."); LOGMSG("Start application...");
if(GP->rest_pars_num){ if(GP->rest_pars_num){
@@ -89,21 +88,42 @@ int main(int argc, char *argv[]){
} }
if(GP->strarr){ if(GP->strarr){
char **p = GP->strarr; char **p = GP->strarr;
for(int i = 0; *p; ++i) printf("String[%d]: \"%s\"\n", i, *p++); for(int i = 0; *p; ++i){
sl_remove_quotes(*p);
printf("String[%d]: \"%s\"\n", i, *p++);
}
} }
if(GP->lo0 != INT_MIN) printf("You set lo0 to %d\n", GP->lo0); if(GP->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->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->lo2 != INT_MIN) printf("You set lo2 to %d\n", GP->lo2);
if(GP->so1){
sl_remove_quotes(GP->so1);
printf("String so1=%s\n", GP->so1);
}
if(GP->so2){
sl_remove_quotes(GP->so2);
printf("String so2=%s\n", GP->so2);
}
if(GP->device){ if(GP->device){
LOGDBG("Try to open serial %s", GP->device); LOGMSG("Try to open serial %s at speed %d", GP->device, GP->speed);
dev = sl_tty_new(GP->device, GP->speed, 4096); dev = sl_tty_new(GP->device, GP->speed, 4096);
if(dev) dev = sl_tty_open(dev, GP->exclusive); if(dev) dev = sl_tty_open(dev, GP->exclusive);
if(!dev){ if(!dev){
LOGERR("Can't open %s with speed %d. Exit.", GP->device, GP->speed); LOGERR("Can't open %s with speed %d. Exit.", GP->device, GP->speed);
signals(0); signals(0);
} }
if(GP->speed != dev->speed){
LOGERR("Can't set exact speed! Opened %s at speed %d", dev->portname, dev->speed);
ERRX("Can't set speed %d (try %d)", GP->speed, dev->speed);
}
} }
if(!dev) return 0;
sl_setup_con();
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
// main stuff goes here // main stuff goes here
long seed = sl_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); green("Now I will sleep for 10 seconds after your last input.\n Do whatever you want. Random seed: %ld\n", seed);

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-27 11:51+0300\n" "POT-Creation-Date: 2026-03-04 17:48+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,33 +17,23 @@ msgstr ""
"Content-Type: text/plain; charset=koi8-r\n" "Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226 #: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:91
msgid "Unsupported option type"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229
#, c-format #, c-format
msgid "Wrong number format '%s'" msgid "sl_get_keyval(): key would be trunkated to %d symbols"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273 #: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:245
#, c-format #, c-format
msgid "Can't open %s" msgid "Can't open %s"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284 #: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:316
#, c-format msgid "sl_conf_showhelp(): wrong index"
msgid "Wrong key: '%s'"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290 #: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322
#, c-format #, c-format
msgid "Key '%s' need value" msgid "Configuration file options (format: key=value):\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296
#, c-format
msgid "Key '%s' have no argptr!"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69 #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
@@ -71,73 +61,95 @@ msgstr ""
msgid "Integer out of range" msgid "Integer out of range"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166 #. `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked
#. not found
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:129
#, c-format
msgid "No such parameter: `%s`\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:138
#, c-format
msgid "Parameter `%s` needs value\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:163
msgid "Can't use multiple args with arg_none!" msgid "Can't use multiple args with arg_none!"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:268
#, c-format #, c-format
msgid "double long arguments: --%s" msgid "double long arguments: --%s"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:274
#, c-format #, c-format
msgid "double short arguments: -%c" msgid "double short arguments: -%c"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:347
#, c-format #, c-format
msgid "Need argument with %s type\n" msgid "Need argument with %s type\n"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486 #. print only one message
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:439
msgid "sl_showhelp(): option index out of range"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:445
#, c-format
msgid "Usage: %s [arguments]\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:535
#, c-format #, c-format
msgid "Wrong parameter: %s" msgid "Wrong parameter: %s"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:539
#, c-format #, c-format
msgid "%s: need argument!" msgid "%s: need argument!"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:543
#, c-format #, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\"" msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:132
msgid "Server disconnected" msgid "Server disconnected"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:419
msgid "Can't start server handlers thread" msgid "Can't start server handlers thread"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:491
msgid "Limit of connections reached" msgid "Limit of connections reached"
msgstr "" msgstr ""
#. check for RB overflow #. check for RB overflow
#. -1 - buffer empty (can't be), -2 - buffer overflow #. -1 - buffer empty (can't be), -2 - buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:535
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:536
#, c-format #, c-format
msgid "Server thread: ring buffer overflow for fd=%d" msgid "Server thread: ring buffer overflow for fd=%d"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:550
#, c-format #, c-format
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d" msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
msgstr "" msgstr ""
#. buffer overflow #. buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:562
#, c-format #, c-format
msgid "Server thread: buffer overflow from fd=%d" msgid "Server thread: buffer overflow from fd=%d"
msgstr "" msgstr ""
#. never reached #. never reached
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:714
#, c-format #, c-format
msgid "Unsupported socket type %d" msgid "Unsupported socket type %d"
msgstr "" msgstr ""
@@ -147,37 +159,43 @@ msgstr ""
msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2" msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:105
#, c-format #, c-format
msgid "Can't use port %s" msgid "Can't use port %s"
msgstr "" msgstr ""
#. Get settings #. Get settings
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:111
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
msgid "Can't get old TTY settings" msgid "Can't get old TTY settings"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
msgid "Can't apply new TTY settings" msgid "Can't apply new TTY settings"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:136
#, 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" msgid "Can't do exclusive open"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:175
msgid "Port name is missing" msgid "Port name is missing"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:183
msgid "Need non-zero buffer for TTY device" msgid "Need non-zero buffer for TTY device"
msgstr "" msgstr ""
#. Get settings
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:219
msgid "Can't get current TTY settings"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:222
#, c-format
msgid "Can't set exact speed %d"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
msgid "Can't open /dev/random" msgid "Can't open /dev/random"
msgstr "" msgstr ""
@@ -212,21 +230,6 @@ msgstr ""
msgid "Can't munmap" msgid "Can't munmap"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:331
msgid "Can't setup console" msgid "Can't setup console"
msgstr "" msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403
#, c-format
msgid "Wrong double number format '%s'"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428
#, c-format
msgid "Wrong integer number format '%s'"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432
msgid "Number out of integer limits"
msgstr ""

View File

@@ -7,7 +7,7 @@
msgid "" msgid ""
msgstr "Project-Id-Version: PACKAGE VERSION\n" msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-27 11:49+0300\n" "POT-Creation-Date: 2026-03-04 17:30+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,6 +16,25 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Content-Type: text/plain; charset=koi8-r\n" "Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:91
#, c-format
msgid "sl_get_keyval(): key would be trunkated to %d symbols"
msgstr "sl_get_keyval(): ËÌÀÞ ÂÕÄÅÔ ÏÂÒÅÚÁÎ ÄÏ %d ÓÉÍ×ÏÌÏ×"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:245
#, c-format
msgid "Can't open %s"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:316
msgid "sl_conf_showhelp(): wrong index"
msgstr "sl_conf_showhelp(): ÎÅ×ÅÒÎÙÊ ÉÎÄÅËÓ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322
#, c-format
msgid "Configuration file options (format: key=value):\n"
msgstr "ïÐÃÉÉ ËÏÎÆÉÇÕÒÁÃÉÏÎÎÏÇÏ ÆÁÊÌÁ (ÆÏÒÍÁÔ: ËÌÀÞ=ÚÎÁÞÅÎÉÅ):\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69 #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
#, c-format #, c-format
msgid "\n" msgid "\n"
@@ -23,159 +42,114 @@ msgid "\n"
msgstr "\n" msgstr "\n"
"ïÂÎÁÒÕÖÅÎ ÏÄÎÏÉÍÅÎÎÙÊ ÐÒÏÃÅÓÓ (pid=%d), ×ÙÈÏÄ.\n" "ïÂÎÁÒÕÖÅÎ ÏÄÎÏÉÍÅÎÎÙÊ ÐÒÏÃÅÓÓ (pid=%d), ×ÙÈÏÄ.\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490
#, c-format
msgid "%s: need argument!"
msgstr "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119
msgid "Can't apply new TTY settings"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266
msgid "Can't close mmap'ed file"
msgstr "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
msgid "Can't do exclusive open"
msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ÐÏÒÔ × ÜËÓËÌÀÚÉ×ÎÙÊ ÒÅÖÉÍ"
#. Get settings
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106
msgid "Can't get old TTY settings"
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279
msgid "Can't munmap"
msgstr "îÅ ÍÏÇÕ munmap"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273
#, c-format
msgid "Can't open %s"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252
#, c-format
msgid "Can't open %s for reading"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
msgid "Can't open /dev/random"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ /dev/random"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141
msgid "Can't open PID file"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ PID ÆÁÊÌ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198
msgid "Can't read /dev/random"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ /dev/random"
#. error reading self name #. error reading self name
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:117 #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:117
msgid "Can't read self name" msgid "Can't read self name"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ ÉÍÑ ÐÒÏÃÅÓÓÁ" msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ ÉÍÑ ÐÒÏÃÅÓÓÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124 #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141
#, c-format msgid "Can't open PID file"
msgid "Can't set speed %d, got ispeed=%d, ospeed=%d" msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ PID ÆÁÊÌ"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÓËÏÒÏÓÔØ ÎÁ %d; ispeed=%d, ospeed=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347 #. amount of pcount and/or scount wrong
msgid "Can't setup console" #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ" msgid "Wrong helpstring!"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:390
msgid "Can't start server handlers thread"
msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#, c-format
msgid "Can't stat %s"
msgstr "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166
msgid "Can't use multiple args with arg_none!"
msgstr "íÁÓÓÉ× ÐÁÒÁÍÅÔÒÏ× ÎÅ ÍÏÖÅÔ ÉÍÅÔØ ÔÉÐ arg_none!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102
#, c-format
msgid "Can't use port %s"
msgstr "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:82 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:82
msgid "Integer out of range" msgid "Integer out of range"
msgstr "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ" msgstr "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296 #. `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked
#. not found
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:129
#, c-format #, c-format
msgid "Key '%s' have no argptr!" msgid "No such parameter: `%s`\n"
msgstr "õ ËÌÀÞÁ '%s' ÎÅÔ ÕËÁÚÁÔÅÌÑ ÎÁ ÐÅÒÅÍÅÎÎÕÀ!" msgstr "îÅÔ ÔÁËÏÇÏ ÐÁÒÁÍÅÔÒÁ: %s\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:138
#, c-format #, c-format
msgid "Key '%s' need value" msgid "Parameter `%s` needs value\n"
msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ" msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:459 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:163
msgid "Limit of connections reached" msgid "Can't use multiple args with arg_none!"
msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ" msgstr "íÁÓÓÉ× ÐÁÒÁÍÅÔÒÏ× ÎÅ ÍÏÖÅÔ ÉÍÅÔØ ÔÉÐ arg_none!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:268
msgid "Mmap error for input" #, c-format
msgstr "ïÛÉÂËÁ mmap" msgid "double long arguments: --%s"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ÄÌÉÎÎÙÊ ÐÁÒÁÍÅÔÒ: --%s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:274
#, c-format
msgid "double short arguments: -%c"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:347
#, c-format #, c-format
msgid "Need argument with %s type\n" msgid "Need argument with %s type\n"
msgstr "îÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ Ó ÔÉÐÏÍ %s\n" msgstr "îÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ Ó ÔÉÐÏÍ %s\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168 #. print only one message
msgid "Need non-zero buffer for TTY device" #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:439
msgstr "äÌÑ ÐÏÓÌÅÄÏ×ÁÔÅÌØÎÏÇÏ ÕÓÔÒÏÊÓÔ×Á ÔÒÅÂÕÅÔÓÑ ÂÕÆÅÒ ÎÅÎÕÌÅ×ÏÇÏ ÒÁÚÍÅÒÁ" msgid "sl_showhelp(): option index out of range"
msgstr "sl_showhelp(): ÉÎÄÅËÓ ÏÐÃÉÉ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:445
msgid "No filename given!" #, c-format
msgstr "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!" msgid "Usage: %s [arguments]\n"
msgstr "éÓÐÏÌØÚÏ×ÁÎÉÅ: %s [ÁÒÇÕÍÅÎÔÙ]\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:535
msgid "Number out of integer limits" #, c-format
msgstr "þÉÓÌÏ ÚÁ ÐÒÅÄÅÌÁÍÉ int" msgid "Wrong parameter: %s"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:539
msgid "Port name is missing" #, c-format
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ" msgid "%s: need argument!"
msgstr "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147 #: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:543
#, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:132
msgid "Server disconnected" msgid "Server disconnected"
msgstr "óÅÒ×ÅÒ ÏÔËÌÀÞÅÎ" msgstr "óÅÒ×ÅÒ ÏÔËÌÀÞÅÎ"
#. buffer overflow #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:419
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:530 msgid "Can't start server handlers thread"
#, c-format msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
msgid "Server thread: buffer overflow from fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:518 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:491
#, fuzzy, c-format msgid "Limit of connections reached"
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d" msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, "
"ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
#. check for RB overflow #. check for RB overflow
#. -1 - buffer empty (can't be), -2 - buffer overflow #. -1 - buffer empty (can't be), -2 - buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:535
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:504 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:536
#, c-format #, c-format
msgid "Server thread: ring buffer overflow for fd=%d" msgid "Server thread: ring buffer overflow for fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d" msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:550
msgid "Unsupported option type" #, c-format
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÏÐÃÉÉ" msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, "
"ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
#. buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:562
#, c-format
msgid "Server thread: buffer overflow from fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
#. never reached #. never reached
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:620 #: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:714
#, c-format #, c-format
msgid "Unsupported socket type %d" msgid "Unsupported socket type %d"
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÓÏËÅÔÁ %d" msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÓÏËÅÔÁ %d"
@@ -187,48 +161,80 @@ msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: "
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ USART \"%s\"; ÎÕÖÅÎ NPS, ÇÄÅ N: 5..8; P: N/E/O/" msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ USART \"%s\"; ÎÕÖÅÎ NPS, ÇÄÅ N: 5..8; P: N/E/O/"
"1/0, S: 1/2" "1/0, S: 1/2"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494 #: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:105
#, c-format #, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\"" msgid "Can't use port %s"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\"" msgstr "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403 #. Get settings
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:111
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
msgid "Can't get old TTY settings"
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
msgid "Can't apply new TTY settings"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:136
msgid "Can't do exclusive open"
msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ÐÏÒÔ × ÜËÓËÌÀÚÉ×ÎÙÊ ÒÅÖÉÍ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:175
msgid "Port name is missing"
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:183
msgid "Need non-zero buffer for TTY device"
msgstr "äÌÑ ÐÏÓÌÅÄÏ×ÁÔÅÌØÎÏÇÏ ÕÓÔÒÏÊÓÔ×Á ÔÒÅÂÕÅÔÓÑ ÂÕÆÅÒ ÎÅÎÕÌÅ×ÏÇÏ ÒÁÚÍÅÒÁ"
#. Get settings
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:219
#, fuzzy
msgid "Can't get current TTY settings"
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:222
#, fuzzy, c-format
msgid "Can't set exact speed %d"
msgstr "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
msgid "Can't open /dev/random"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ /dev/random"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198
msgid "Can't read /dev/random"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ /dev/random"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248
msgid "No filename given!"
msgstr "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252
#, c-format #, c-format
msgid "Wrong double number format '%s'" msgid "Can't open %s for reading"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ double: %s" msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
#. amount of pcount and/or scount wrong #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53
msgid "Wrong helpstring!"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428
#, c-format #, c-format
msgid "Wrong integer number format '%s'" msgid "Can't stat %s"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÃÅÌÏÇÏ: %s" msgstr "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262
#, c-format msgid "Mmap error for input"
msgid "Wrong key: '%s'" msgstr "ïÛÉÂËÁ mmap"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ËÌÀÞ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266
#, c-format msgid "Can't close mmap'ed file"
msgid "Wrong number format '%s'" msgstr "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279
#, c-format msgid "Can't munmap"
msgid "Wrong parameter: %s" msgstr "îÅ ÍÏÇÕ munmap"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267 #: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:331
#, c-format msgid "Can't setup console"
msgid "double long arguments: --%s" msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ÄÌÉÎÎÙÊ ÐÁÒÁÍÅÔÒ: --%s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273
#, c-format #, c-format
msgid "double short arguments: -%c"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"

View File

@@ -30,7 +30,7 @@
#include <ctype.h> // isalpha #include <ctype.h> // isalpha
#include "usefull_macros.h" #include "usefull_macros.h"
char *helpstring = "%s\n"; const char *helpstring = NULL; // will be inited later, can't init with gettext on this stage
/** /**
* @brief sl_helpstring - change standard help header * @brief sl_helpstring - change standard help header
@@ -115,36 +115,33 @@ static int myatod(void *num, const char *str, sl_argtype_e t){
/** /**
* @brief get_optind - get index of current option in array options * @brief get_optind - get index of current option in array options
* @param key (i) - original key (short or long)
* @param opt (i) - returning val of getopt_long * @param opt (i) - returning val of getopt_long
* @param options (i) - array of options * @param options (i) - array of options
* @return index in array * @return index in array
*/ */
static int get_optind(int opt, sl_option_t *options){ static int get_optind(const char *key, int opt, sl_option_t *options, void (*helpfun)(int, sl_option_t*)){
int oind; int oind = 0, theopt = opt;
sl_option_t *opts = options; sl_option_t *opts = options;
assert(opts); assert(opts);
for(oind = 0; opts->name && opts->val != opt; oind++, opts++){ // `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked
DBG("cmp %c and %c", opt, opts->val); if(opt == '?'){ // not found
fprintf(stderr, _("No such parameter: `%s`\n"), key);
helpfun(-1, options);
return -1; // never reached until `helpfun` changed
}else if(opt == ':') theopt = optopt; // search to show helpstring "need parameter"
for(oind = 0; opts->help && opts->val != theopt; oind++, opts++){
DBG("cmp %c and %c", theopt, opts->val);
}
if(!opts->help) return -1;
if(opt == ':'){
fprintf(stderr, _("Parameter `%s` needs value\n"), key);
helpfun(oind, options);
return -1; // never reached until `helpfun` changed
} }
if(!opts->name || opts->val != opt) // no such parameter
sl_showhelp(-1, options);
return oind; 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 * @brief get_aptr - reallocate new value in array of multiple repeating arguments
@@ -194,41 +191,50 @@ void *get_aptr(void *paptr, sl_argtype_e type){
return aptr[i - 1]; return aptr[i - 1];
} }
static int cmpstringp(const void *p1, const void *p2){
if(!p1 || !p2) return 0;
const char *str1 = * (char * const *) p1, *str2 = * (char * const *) p2;
if(!str1 && !str2) return 0;
else if(!str1) return 1;
else if(!str2) return -1;
return strcmp(str1, str2);
}
static int cmpcharp(const void *p1, const void *p2){
return (int)(*(char * const)p1 - *(char *const)p2);
}
/** /**
* @brief sl_parseargs - parse command line arguments * @brief sl_parseargs_hf - parse arguments with user help funtion
* ! If arg is string, then value will be strdup'ed! * @param argc - amount of arguments
* * @param argv - arguments
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt` * @param options - array with opts
* @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt` * @param helpfun - function called in case of wrong arg
* BE CAREFUL! if you wanna use full argc & argv, save their original values before */
* calling this function void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*)){
* @param options (i) - array of `myoption` for arguments parcing
*
* @exit: in case of error this function show help & make `exit(-1)`
*/
void sl_parseargs(int *argc, char ***argv, sl_option_t *options){
char *short_options, *soptr; char *short_options, *soptr;
struct option *long_options, *loptr; struct option *long_options, *loptr;
size_t optsize, i; int optsize = 0;
sl_option_t *opts = options; sl_option_t *opts = options;
// check whether there is at least one options // check whether there is at least one options
assert(opts); assert(opts);
assert(opts[0].name); //assert(opts[0].name);
// first we count how much values are in opts // first we count how much values are in opts
for(optsize = 0; opts->name; optsize++, opts++); for(optsize = 0; opts->help; optsize++, opts++);
// now we can allocate memory // now we can allocate memory
short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts // TODO: FREE all unneeded memory at end of function
short_options = calloc(optsize * 3 + 2, 1); // multiply by three for '::' in case of args in opts, add 1 for starting ':'
long_options = calloc(optsize + 1, sizeof(struct option)); long_options = calloc(optsize + 1, sizeof(struct option));
opts = options; loptr = long_options; soptr = short_options; opts = options; loptr = long_options; soptr = short_options;
*soptr++ = ':';
// check the parameters are not repeated // check the parameters are not repeated
char **longlist = MALLOC(char*, optsize); char **longlist = MALLOC(char*, optsize);
char *shortlist = MALLOC(char, optsize); char *shortlist = MALLOC(char, optsize);
// fill short/long parameters and make a simple checking // fill short/long parameters and make a simple checking
for(i = 0; i < optsize; i++, loptr++, opts++){ for(int i = 0; i < optsize; i++, loptr++, opts++){
// check // check
assert(opts->name); // check name //assert(opts->name); // check name
longlist[i] = strdup(opts->name); DBG("opts: val=%c, name=%s", opts->val, opts->name);
longlist[i] = opts->name ? strdup(opts->name) : NULL;
if(opts->has_arg){ if(opts->has_arg){
assert(opts->type != arg_none); // check error with arg type assert(opts->type != arg_none); // check error with arg type
assert(opts->argptr); // check pointer assert(opts->argptr); // check pointer
@@ -237,7 +243,7 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){
assert(opts->argptr); assert(opts->argptr);
// fill long_options // fill long_options
// don't do memcmp: what if there would be different alignment? // don't do memcmp: what if there would be different alignment?
loptr->name = opts->name; loptr->name = opts->name ? opts->name : "";
loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1; loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1;
loptr->flag = opts->flag; loptr->flag = opts->flag;
loptr->val = opts->val; loptr->val = opts->val;
@@ -252,17 +258,12 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){
} }
} }
// sort all lists & check for repeating // sort all lists & check for repeating
int cmpstringp(const void *p1, const void *p2){
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
int cmpcharp(const void *p1, const void *p2){
return (int)(*(char * const)p1 - *(char *const)p2);
}
qsort(longlist, optsize, sizeof(char *), cmpstringp); qsort(longlist, optsize, sizeof(char *), cmpstringp);
qsort(shortlist,optsize, sizeof(char), cmpcharp); qsort(shortlist,optsize, sizeof(char), cmpcharp);
char *prevl = longlist[0], prevshrt = shortlist[0]; char *prevl = longlist[0], prevshrt = shortlist[0];
for(i = 1; i < optsize; ++i){ // check for repeated args
if(longlist[i]){ for(int i = 1; i < optsize; ++i){
if(longlist[i] && *longlist[i]){
if(prevl){ if(prevl){
if(strcmp(prevl, longlist[i]) == 0) ERRX(_("double long arguments: --%s"), prevl); if(strcmp(prevl, longlist[i]) == 0) ERRX(_("double long arguments: --%s"), prevl);
} }
@@ -275,21 +276,32 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){
prevshrt = shortlist[i]; prevshrt = shortlist[i];
} }
} }
FREE(longlist); FREE(shortlist);
#ifdef EBUG
DBG("Argc=%d, argv[0]=%s, argv[1]=%s, short=%s", *argc, (*argv)[0], (*argv)[1], short_options);
for(int _ = 0; _ <= optsize; ++_) fprintf(stderr, "\tlo[%d]='%s'\n", _, long_options[_].name);
DBG("AND argv:");
for(int _ = 0; _ < *argc; ++_) fprintf(stderr, "\t[%d]='%s'\n", _ ,(*argv)[_]);
#endif
// reinit global `optind` for ability of sequentional run
optind = 1;
// now we have both long_options & short_options and can parse `getopt_long` // now we have both long_options & short_options and can parse `getopt_long`
while(1){ while(1){
int opt; int opt;
int /*oindex = -1,*/ optind = -1; // oindex - number of option in long_options, optind - number in options[] int /*oindex = -1,*/ loptind = -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("optind=%d", optind);
DBG("%c(%d) = getopt_long(argc, argv, %s, long_options, &%d); optopt=%c(%d), errno=%d", opt, opt, short_options, optind, optopt, optopt, errno); const char *curopt = (*argv)[optind];
if(optind < 0 ) optind = get_optind(opt, options); // find short option -> need to know index of long if((opt = getopt_long(*argc, *argv, short_options, long_options, &loptind)) == -1) break;
DBG("search `%s`, %c(%d) = getopt_long(argc, argv, %s, long_options, &%d); optopt=%c(%d), errno=%d", curopt, opt, opt, short_options, loptind, optopt, optopt, errno);
if(loptind < 0 ) loptind = get_optind(curopt, opt, options, helpfun); // find short option -> need to know index of long
if(loptind < 0 || loptind >= optsize) continue;
// be careful with "-?" flag: all wrong or ambiguous flags will be interpreted as this! // be careful with "-?" flag: all wrong or ambiguous flags will be interpreted as this!
DBG("index=%d", optind); DBG("index=%d", loptind);
opts = &options[optind]; opts = &options[loptind];
DBG("Got option %s", opts->name); DBG("Got option %s (%c)", opts->name, opts->val);
// now check option // now check option
if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR) if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR)
if(!optarg) sl_showhelp(optind, options); // need argument if(!optarg) helpfun(loptind, options); // need argument
void *aptr; void *aptr;
if(opts->has_arg == MULT_PAR){ if(opts->has_arg == MULT_PAR){
aptr = get_aptr(opts->argptr, opts->type); aptr = get_aptr(opts->argptr, opts->type);
@@ -303,39 +315,58 @@ void sl_parseargs(int *argc, char ***argv, sl_option_t *options){
default: default:
case arg_none: case arg_none:
if(opts->argptr) *((int*)aptr) += 1; // increment value if(opts->argptr) *((int*)aptr) += 1; // increment value
break; break;
case arg_int: case arg_int:
result = myatoll(aptr, optarg, arg_int); result = myatoll(aptr, optarg, arg_int);
type = "integer"; type = "integer";
break; break;
case arg_longlong: case arg_longlong:
result = myatoll(aptr, optarg, arg_longlong); result = myatoll(aptr, optarg, arg_longlong);
type = "long long"; type = "long long";
break; break;
case arg_double: case arg_double:
result = myatod(aptr, optarg, arg_double); result = myatod(aptr, optarg, arg_double);
type = "double"; type = "double";
break; break;
case arg_float: case arg_float:
result = myatod(aptr, optarg, arg_float); result = myatod(aptr, optarg, arg_float);
type = "float"; type = "float";
break; break;
case arg_string: case arg_string:
result = (*((void**)aptr) = (void*)strdup(optarg)) != NULL; result = (*((void**)aptr) = (void*)strdup(optarg)) != NULL;
type = "string"; type = "string";
break; break;
case arg_function: case arg_function:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
result = ((sl_argfn_t)aptr)(optarg); result = ((sl_argfn_t)aptr)(optarg);
break; #pragma GCC diagnostic pop
break;
} }
if(!result){ if(!result){
if(type) fprintf(stderr, _("Need argument with %s type\n"), type); if(type) fprintf(stderr, _("Need argument with %s type\n"), type);
sl_showhelp(optind, options); helpfun(loptind, options);
} }
} }
FREE(short_options); FREE(long_options);
*argc -= optind; *argc -= optind;
*argv += optind; *argv += optind;
} }
/**
* @brief sl_parseargs - parse command line arguments
* ! If arg is string, then value will be strdup'ed!
*
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt`
* @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt`
* BE CAREFUL! if you wanna use full argc & argv, save their original values before
* calling this function
* @param options (i) - array of `myoption` for arguments parcing
*
* @exit: in case of error this function show help & make `exit(-1)`
*/
void sl_parseargs(int *argc, char ***argv, sl_option_t *options){
sl_parseargs_hf(argc, argv, options, sl_showhelp);
}
/** /**
* @brief argsort - compare function for qsort * @brief argsort - compare function for qsort
@@ -350,6 +381,7 @@ static int argsort(const void *a1, const void *a2){
if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg
return (s1 - s2); return (s1 - s2);
}else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long }else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long
assert(l1); assert(l2); // no way to omit long option if short is absent
return strcmp(l1, l2); return strcmp(l1, l2);
}else{ // only one have short arg -- return it }else{ // only one have short arg -- return it
if(f2 || !s2) return -1; // a1 have short - it is 'lesser' if(f2 || !s2) return -1; // a1 have short - it is 'lesser'
@@ -357,6 +389,34 @@ static int argsort(const void *a1, const void *a2){
} }
} }
// print one string of help
static void pr_helpstring(sl_option_t *opt, char *buf, int indent, size_t bufsz){
size_t p = sprintf(buf, " "); // a little indent
int havelongopt = opt->name && *opt->name;
if(!opt->flag && opt->val){ // .val is short argument
p += snprintf(buf+p, bufsz-p, "-%c", opt->val);
if(havelongopt) p += snprintf(buf+p, bufsz-p, ", "); // show comma only it there's shor arg
}
if(havelongopt){
p += snprintf(buf+p, bufsz-p, "--%s", opt->name);
if(opt->has_arg == NEED_ARG || opt->has_arg == MULT_PAR) // required argument
p += snprintf(buf+p, bufsz-p, "=arg");
else if(opt->has_arg == OPT_ARG) // optional argument
p += snprintf(buf+p, bufsz-p, "[=arg]");
}else{
if(opt->has_arg == NEED_ARG || opt->has_arg == MULT_PAR)
p += snprintf(buf+p, bufsz-p, " arg");
else if(opt->has_arg == OPT_ARG)
p += snprintf(buf+p, bufsz-p, " [arg]");
}
if(indent > 0){
assert(p < (size_t)indent);
printf("%-*s%s", indent+1, buf, _(opt->help)); // write options & at least 2 spaces after
}else printf("%s %s", buf, _(opt->help));
if(opt->has_arg == MULT_PAR) printf(" (can occur multiple times)");
printf("\n");
}
/** /**
* @brief sl_showhelp - show help information based on sl_option_t->help values * @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 oindex (i) - if non-negative, show only help by sl_option_t[oindex].help
@@ -370,54 +430,83 @@ void sl_showhelp(int oindex, sl_option_t *options){
char buf[bufsz+1]; char buf[bufsz+1];
sl_option_t *opts = options; sl_option_t *opts = options;
assert(opts); assert(opts);
assert(opts[0].name); // check whether there is at least one options assert(opts[0].help); // check whether there is at least one options
// count amount of options
int N; for(N = 0; opts->help; ++N, ++opts);
if(N == 0) exit(-2);
DBG("got %d options, oindex=%d", N, oindex);
if(oindex > -1){ // print only one message if(oindex > -1){ // print only one message
opts = &options[oindex]; if(oindex >= N || oindex < 0) ERRX(_("sl_showhelp(): option index out of range"));
printf(" "); pr_helpstring(&options[oindex], buf, 0, bufsz);
if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val);
printf("--%s", opts->name);
if(opts->has_arg == 1) printf("=arg");
else if(opts->has_arg == 2) printf("[=arg]");
printf(" %s\n", _(opts->help));
exit(-1); exit(-1);
} }
// header, by default is just "progname\n" // header, by default is just "progname\n"
printf("\n"); printf("\n");
if(!helpstring) helpstring = _("Usage: %s [arguments]\n");
if(strstr(helpstring, "%s")) // print progname if(strstr(helpstring, "%s")) // print progname
printf(helpstring, __progname); printf(helpstring, __progname);
else // only text else // only text
printf("%s", helpstring); printf("%s", helpstring);
printf("\n"); printf("\n");
// count max_opt_len // count max_opt_len
do{ for(int _ = 0; _ < N; ++_){
int L = strlen(opts->name); if(!options[_].name) continue;
int L = strlen(options[_].name);
if(max_opt_len < L) max_opt_len = L; if(max_opt_len < L) max_opt_len = L;
}while((++opts)->name); }
max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols max_opt_len += 14; // format: '-S, --long[=arg]' - get addition 13 symbols
opts = options;
// count amount of options
int N; for(N = 0; opts->name; ++N, ++opts);
if(N == 0) exit(-2);
// Now print all help (sorted) // Now print all help (sorted)
opts = options; qsort(options, N, sizeof(sl_option_t), argsort);
qsort(opts, N, sizeof(sl_option_t), argsort);
do{ do{
int p = sprintf(buf, " "); // a little indent pr_helpstring(options++, buf, max_opt_len, bufsz);
if(!opts->flag && opts->val) // .val is short argument
p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val);
p += snprintf(buf+p, bufsz-p, "--%s", opts->name);
if(opts->has_arg == 1) // required argument
p += snprintf(buf+p, bufsz-p, "=arg");
else if(opts->has_arg == 2) // optional argument
p += snprintf(buf+p, bufsz-p, "[=arg]");
assert(p < max_opt_len); // there would be magic if p >= max_opt_len
printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after
++opts;
}while(--N); }while(--N);
printf("\n\n"); printf("\n\n");
exit(-1); exit(-1);
} }
static int findsubopt(char *par, sl_suboption_t *so){
int idx = 0;
if(!par) return -1;
while(so[idx].name){
if(strcasecmp(par, so[idx].name) == 0) return idx;
++idx;
}
return -1; // badarg
}
static 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:
case arg_none:
if(soptr->argptr) *((int*)aptr) += 1; // increment value
result = TRUE;
break;
case arg_int:
result = myatoll(aptr, val, arg_int);
break;
case arg_longlong:
result = myatoll(aptr, val, arg_longlong);
break;
case arg_double:
result = myatod(aptr, val, arg_double);
break;
case arg_float:
result = myatod(aptr, val, arg_float);
break;
case arg_string:
result = (*((void**)aptr) = (void*)strdup(val)) != NULL;
break;
case arg_function:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
result = ((sl_argfn_t)aptr)(val);
#pragma GCC diagnostic pop
break;
}
return result;
}
/** /**
* @brief sl_get_suboption - get suboptions from parameter string * @brief sl_get_suboption - get suboptions from parameter string
* @param str - parameter string * @param str - parameter string
@@ -425,46 +514,6 @@ void sl_showhelp(int oindex, sl_option_t *options){
* @return TRUE if all OK * @return TRUE if all OK
*/ */
int sl_get_suboption(char *str, sl_suboption_t *opt){ 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){
if(strcasecmp(par, so[idx].name) == 0) return idx;
++idx;
}
return -1; // badarg
}
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:
case arg_none:
if(soptr->argptr) *((int*)aptr) += 1; // increment value
result = TRUE;
break;
case arg_int:
result = myatoll(aptr, val, arg_int);
break;
case arg_longlong:
result = myatoll(aptr, val, arg_longlong);
break;
case arg_double:
result = myatod(aptr, val, arg_double);
break;
case arg_float:
result = myatod(aptr, val, arg_float);
break;
case arg_string:
result = (*((void**)aptr) = (void*)strdup(val)) != NULL;
break;
case arg_function:
result = ((sl_argfn_t)aptr)(val);
break;
}
return result;
}
char *tok; char *tok;
int ret = FALSE; int ret = FALSE;
char *tmpbuf; char *tmpbuf;

356
socket.c
View File

@@ -16,8 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <ctype.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ctype.h>
#include <inttypes.h>
#include <netdb.h> #include <netdb.h>
#include <poll.h> #include <poll.h>
#include <stdio.h> #include <stdio.h>
@@ -25,7 +26,6 @@
#include <strings.h> #include <strings.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/un.h> // unix socket #include <sys/un.h> // unix socket
#include <inttypes.h>
#include <unistd.h> #include <unistd.h>
#include "usefull_macros.h" #include "usefull_macros.h"
@@ -48,7 +48,7 @@ int sl_sock_getmaxclients(sl_sock_t *sock){
void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int)){ void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int)){
if(sock) sock->toomuch_handler = h; if(sock) sock->toomuch_handler = h;
} }
// setter of "new client connected" handler // setter of "new client connected" han;dler
void sl_sock_connhandler(sl_sock_t *sock, int(*h)(struct sl_sock*)){ void sl_sock_connhandler(sl_sock_t *sock, int(*h)(struct sl_sock*)){
if(sock) sock->newconnect_handler = h; if(sock) sock->newconnect_handler = h;
} }
@@ -80,21 +80,6 @@ const char *sl_sock_hresult2str(sl_sock_hresult_e r){
return resmessages[r]; return resmessages[r];
} }
// convert UNIX socket name for unaddr; result should be free'd
static char *convunsname(const char *path){
char *apath = MALLOC(char, 106);
if(*path == 0){
DBG("convert name starting from 0");
apath[0] = 0;
strncpy(apath+1, path+1, 104);
}else if(strncmp("\\0", path, 2) == 0){
DBG("convert name starting from \\0");
apath[0] = 0;
strncpy(apath+1, path+2, 104);
}else strncpy(apath, path, 105);
return apath;
}
/** /**
* @brief sl_sock_delete - close socket and delete descriptor * @brief sl_sock_delete - close socket and delete descriptor
* @param sock - pointer to socket descriptor * @param sock - pointer to socket descriptor
@@ -345,10 +330,59 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
sl_sock_sendbyte(client, '\n'); sl_sock_sendbyte(client, '\n');
return RESULT_SILENCE; return RESULT_SILENCE;
} }
// check for strict params like `key=val`
for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){ for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
if(strcmp(h->key, key)) continue; if(strcmp(h->key, key)) continue;
if(h->data){
sl_sock_keyno_t *kn = (sl_sock_keyno_t*)h->data;
if(-1 == isinf(kn->magick)) kn->n = -1; // no value number
}
return h->handler(client, h, valptr); return h->handler(client, h, valptr);
} }
// now check for optional key's number like key0=val, key[1]=val, key(2)=val or key{3}=val
int keylen = strlen(key);
char *numstart = NULL;
const char *bra = "([{", *ket = ")]}";
char *found = strchr((char*)ket, key[keylen - 1]);
DBG("found=%s", found);
if(found){
char *keyend = strchr(key, bra[found - ket]); // find starting bracket
DBG("keyend=%s", keyend);
if(keyend){
numstart = keyend + 1;
keylen = keyend - key;
}
}else{ // maybe this is key123=val ?
numstart = &key[keylen-1];
while(numstart > key && isdigit(*numstart)) --numstart;
if(numstart == &key[keylen-1]) numstart = NULL;
else{
keylen = (++numstart) - key;
DBG("numstart=%s", numstart);
}
}
if(numstart){
char *eptr;
long long LL = strtoll(numstart, &eptr, 0);
DBG("LL=%lld, len=%d", LL, keylen);
if(eptr != numstart && LL >= 0 && LL <= INT_MAX){
int parno = (int)LL;
for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
if(strncmp(h->key, key, keylen)) continue; // now search only in first `keylen` bytes
DBG("found %s", h->key);
if(h->data){
sl_sock_keyno_t *kn = (sl_sock_keyno_t*)h->data;
if(-1 == isinf(kn->magick)){
kn->n = parno;
DBG("run handler, parno=%d", parno);
return h->handler(client, h, valptr);
}
}
DBG("NO data");
break;
}
}
}
if(client->defmsg_handler) return client->defmsg_handler(client, str); if(client->defmsg_handler) return client->defmsg_handler(client, str);
return RESULT_BADKEY; return RESULT_BADKEY;
} }
@@ -413,7 +447,9 @@ static void *serverthread(void _U_ *d){
// ZERO - listening server socket // ZERO - listening server socket
poll_set[0].fd = sockfd; poll_set[0].fd = sockfd;
poll_set[0].events = POLLIN; poll_set[0].events = POLLIN;
// disconnect client // disconnect client (no way to make this function non-nested)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
void disconnect_(sl_sock_t *c, int N){ void disconnect_(sl_sock_t *c, int N){
DBG("Disconnect client \"%s\" (fd=%d)", c->IP, c->fd); DBG("Disconnect client \"%s\" (fd=%d)", c->IP, c->fd);
if(s->disconnect_handler) s->disconnect_handler(c); if(s->disconnect_handler) s->disconnect_handler(c);
@@ -440,6 +476,7 @@ static void *serverthread(void _U_ *d){
pthread_mutex_unlock(&c->mutex); pthread_mutex_unlock(&c->mutex);
--nfd; --nfd;
} }
#pragma GCC diagnostic pop
// allocate buffer with size not less than RB size // allocate buffer with size not less than RB size
size_t bufsize = s->buffer->length; // as RB should be 1 byte less, this is OK size_t bufsize = s->buffer->length; // as RB should be 1 byte less, this is OK
uint8_t *buf = MALLOC(uint8_t, bufsize); uint8_t *buf = MALLOC(uint8_t, bufsize);
@@ -576,98 +613,111 @@ errex:
return NULL; return NULL;
} }
// convert UNIX socket name for unaddr; result should be free'd
static char *convunsname(const char *path, socklen_t *nbytes){
char *apath = MALLOC(char, UNIX_SOCK_PATH_MAX);
socklen_t len = 0;
if(*path == 0 || *path == '@'){
DBG("convert name starting from %s", (*path == 0) ? "\\0" : "@");
apath[0] = 0;
strncpy(apath+1, path+1, UNIX_SOCK_PATH_MAX-2);
len = 1 + strlen(path+1);
}else if(strncmp("\\0", path, 2) == 0){
DBG("convert name starting from \\0");
apath[0] = 0;
strncpy(apath+1, path+2, UNIX_SOCK_PATH_MAX-2);
len = 1 + strlen(path+2);
}else{
strncpy(apath, path, UNIX_SOCK_PATH_MAX-1);
len = strlen(path);
}
if(nbytes) *nbytes = len + sizeof(sa_family_t); // amount of bytes for `bind()` (parameter `len`)
return apath;
}
/** /**
* @brief sl_sock_open - open socket (client or server) * @brief mknodeservice - break `path` into node name and port for INET socket
* @param type - socket type * @param path (i) - path like "node:service"
* @param path - path (for UNIX socket); for NET: ":port" for server, "server:port" for client * @param node (o) - pointer to `char *` - node name
* @param handlers - standard handlers when read data (or NULL) * @param service (o) - pointer to `char *` - port ("service")
* @param bufsiz - input ring buffer size
* @param isserver - 1 for server, 0 for client
* @return socket descriptor or NULL if failed
* to create anonymous UNIX-socket you can start "path" from 0 or from string "\0"
*/ */
static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hitem_t *handlers, int bufsiz, int isserver){ static void mknodeservice(const char *path, char **node, char **service){
if(!path || type >= SOCKT_AMOUNT) return NULL; if(!path || !node || !service) return;
if(bufsiz < 256) bufsiz = 256; char *delim = strchr((char*)path, ':');
if(!delim) *service = strdup(path); // only port
else{
if(delim == path) *service = strdup(path+1);
else{
size_t l = delim - path;
*node = MALLOC(char, l + 1);
strncpy(*node, path, l);
*service = strdup(delim + 1);
}
}
}
/**
* @brief sl_sock_open - open socket, run bind or connect and return its file descriptor
* @param type - socket type
* @param path - path to UNIX-socket, port or "host:port" for INET-socket
* @param isserver - 1 for server, 0 for client
* @param socktype - custom socket type or "<1" for default (SOCK_STREAM)
* @return file descriptor or -1 if can't open
*/
int sl_sock_open(sl_socktype_e type, const char *path, int isserver, int ai_socktype){
FNAME();
if(!path || type >= SOCKT_AMOUNT) return -1;
if(ai_socktype < 1) ai_socktype = SOCK_STREAM;
int sock = -1; int sock = -1;
struct addrinfo ai = {0}, *res = &ai; struct addrinfo ai = {0}, *res = &ai;
struct sockaddr_un unaddr = {0}; struct sockaddr_un unaddr = {0};
char *str = NULL; ai.ai_socktype = ai_socktype;
ai.ai_socktype = SOCK_STREAM;
switch(type){ switch(type){
case SOCKT_UNIX: case SOCKT_UNIX:
str = convunsname(path); {
if(!str) return NULL; DBG("SOCKT_UNIX");
unaddr.sun_family = AF_UNIX; char *str = convunsname(path, &ai.ai_addrlen);
ai.ai_addr = (struct sockaddr*) &unaddr; DBG("path+1: %s", str+1);
ai.ai_addrlen = sizeof(unaddr); if(!str) return -1;
memcpy(unaddr.sun_path, str, 106); unaddr.sun_family = AF_UNIX;
ai.ai_family = AF_UNIX; ai.ai_addr = (struct sockaddr*) &unaddr;
//ai.ai_socktype = SOCK_SEQPACKET; memcpy(unaddr.sun_path, str, UNIX_SOCK_PATH_MAX);
break; FREE(str); // don't forget!
case SOCKT_NET: ai.ai_family = AF_UNIX;
case SOCKT_NETLOCAL: //ai.ai_socktype = SOCK_SEQPACKET;
//ai.ai_socktype = SOCK_DGRAM; // = SOCK_STREAM;
ai.ai_family = AF_INET;
if(isserver) ai.ai_flags = AI_PASSIVE;
break;
default: // never reached
WARNX(_("Unsupported socket type %d"), type);
return NULL;
break;
} }
sl_sock_t *s = MALLOC(sl_sock_t, 1); break;
s->type = type; case SOCKT_NET:
s->fd = -1; case SOCKT_NETLOCAL:
s->maxclients = SL_DEF_MAXCLIENTS; {
s->handlers = handlers; DBG("SOCKT_%s", (type == SOCKT_NET) ? "NET" : "NETLOCAL");
s->buffer = sl_RB_new(bufsiz); char *node = NULL, *service = NULL;
if(!s->buffer){ mknodeservice(path, &node, &service);
sl_sock_delete(&s);
return NULL;
}else{ // fill node/service
if(type == SOCKT_UNIX) s->node = strdup(str); // str now is converted path
else{
char *delim = strchr(path, ':');
if(!delim) s->service = strdup(path); // only port
else{
if(delim == path) s->service = strdup(path+1);
else{
size_t l = delim - path;
s->node = MALLOC(char, l + 1);
strncpy(s->node, path, l);
s->service = strdup(delim + 1);
}
}
DBG("socket->service=%s", s->service);
}
DBG("socket->node=%s", s->node);
}
// now try to open socket
if(type != SOCKT_UNIX){
DBG("try to get addrinfo for node '%s' and service '%s'", s->node, s->service);
if(isserver){ if(isserver){
if(!s->node){ ai.ai_flags = AI_PASSIVE;
DBG("Socket type now is SOCKT_NET"); if(type == SOCKT_NETLOCAL){
s->type = SOCKT_NET; FREE(node);
} node = strdup("127.0.0.1");
if(s->type == SOCKT_NETLOCAL){
DBG("SOCKT_NETLOCAL: change `node` to localhost");
FREE(s->node);
s->node = strdup("127.0.0.1"); // localhost
} }
} }
DBG("---> node '%s', service '%s'", s->node, s->service); ai.ai_family = AF_UNSPEC; // not AF_INET for client as there maybe problems with IPv6
int e = getaddrinfo(s->node, s->service, &ai, &res); int e = getaddrinfo(node, service, &ai, &res);
if(e){ if(e){
WARNX("getaddrinfo(): %s", gai_strerror(e)); WARNX("getaddrinfo(): %s", gai_strerror(e));
sl_sock_delete(&s); return -1;
return NULL;
} }
FREE(node);
FREE(service);
}
break;
default: // never reached
WARNX(_("Unsupported socket type %d"), type);
return -1;
break;
} }
for(struct addrinfo *p = res; p; p = p->ai_next){ for(struct addrinfo *p = res; p; p = p->ai_next){
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) continue; if((sock = socket(p->ai_family, p->ai_socktype|SOCK_NONBLOCK, p->ai_protocol)) < 0) continue;
DBG("Try proto %d, type %d", p->ai_protocol, p->ai_socktype); DBG("Try proto %d, type %d, socktype %d", p->ai_protocol, p->ai_socktype, p->ai_socktype);
if(isserver){ if(isserver){
int reuseaddr = 1; int reuseaddr = 1;
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
@@ -688,30 +738,80 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){ if(connect(sock, p->ai_addr, p->ai_addrlen) == -1){
WARN("connect()"); WARN("connect()");
close(sock); sock = -1; close(sock); sock = -1;
continue;
} }
} }
break; break;
} }
if(sock < 0) sl_sock_delete(&s); if(type != SOCKT_UNIX) freeaddrinfo(res); // don't forget to free memory allocated with getaddrinfo
else{ return sock;
s->fd = sock; }
pthread_mutex_init(&s->mutex, NULL);
DBG("s->fd=%d, node=%s, service=%s", s->fd, s->node, s->service); /**
int r = -1; * @brief sl_sock_run - open socket and run thread (client or server)
* @param type - socket type
* @param path - path (for UNIX socket); for NET: ":port" for server, "server:port" for client
* @param handlers - standard handlers when read data (or NULL)
* @param bufsiz - input ring buffer size
* @param isserver - 1 for server, 0 for client
* @return socket descriptor or NULL if failed
* to create anonymous UNIX-socket you can start "path" from 0 or from string "\0"
*/
static sl_sock_t *sl_sock_run(sl_socktype_e type, const char *path, sl_sock_hitem_t *handlers, int bufsiz, int isserver){
FNAME();
if(bufsiz < 256) bufsiz = 256;
int sock = sl_sock_open(type, path, isserver, 0);
if(sock < 0) return NULL;
sl_sock_t *s = MALLOC(sl_sock_t, 1);
s->type = type;
s->fd = -1;
s->maxclients = SL_DEF_MAXCLIENTS;
s->handlers = handlers;
s->buffer = sl_RB_new(bufsiz);
if(!s->buffer){
sl_sock_delete(&s);
return NULL;
}else{ // fill node/service
if(type == SOCKT_UNIX) s->node = strdup(path); // str now is path copy
else{
mknodeservice(path, &s->node, &s->service);
DBG("socket->service=%s", s->service);
}
DBG("socket->node=%s", s->node);
}
// now try to open socket
if(type != SOCKT_UNIX){
DBG("try to get addrinfo for node '%s' and service '%s'", s->node, s->service);
if(isserver){ if(isserver){
if(s->handlers || s->defmsg_handler) if(!s->node){
r = pthread_create(&s->rthread, NULL, serverthread, (void*)s); DBG("Socket type now is SOCKT_NET");
else r = 0; s->type = SOCKT_NET;
}else{ }
r = pthread_create(&s->rthread, NULL, clientrbthread, (void*)s); if(s->type == SOCKT_NETLOCAL){
} DBG("SOCKT_NETLOCAL: change `node` to localhost");
if(r){ FREE(s->node);
WARN("pthread_create()"); s->node = strdup("127.0.0.1"); // localhost
sl_sock_delete(&s); }
}else{
s->connected = TRUE;
DBG("fd=%d CONNECTED", s->fd);
} }
DBG("---> node '%s', service '%s'", s->node, s->service);
}
s->fd = sock;
pthread_mutex_init(&s->mutex, NULL);
DBG("s->fd=%d, node=%s, service=%s", s->fd, s->node, s->service);
int r = -1;
if(isserver){
if(s->handlers || s->defmsg_handler)
r = pthread_create(&s->rthread, NULL, serverthread, (void*)s);
else r = 0;
}else{
r = pthread_create(&s->rthread, NULL, clientrbthread, (void*)s);
}
if(r){
WARN("pthread_create()");
sl_sock_delete(&s);
}else{
s->connected = TRUE;
DBG("fd=%d CONNECTED", s->fd);
} }
return s; return s;
} }
@@ -725,7 +825,7 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
* @return socket descriptor or NULL if failed * @return socket descriptor or NULL if failed
*/ */
sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz){ sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz){
sl_sock_t *s = sl_sock_open(type, path, NULL, bufsiz, 0); sl_sock_t *s = sl_sock_run(type, path, NULL, bufsiz, 0);
return s; return s;
} }
@@ -738,7 +838,7 @@ sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz){
* @return socket descriptor or NULL if failed * @return socket descriptor or NULL if failed
*/ */
sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz, sl_sock_hitem_t *handlers){ sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz, sl_sock_hitem_t *handlers){
sl_sock_t *s = sl_sock_open(type, path, handlers, bufsiz, 1); sl_sock_t *s = sl_sock_run(type, path, handlers, bufsiz, 1);
return s; return s;
} }
@@ -826,6 +926,7 @@ ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len){
sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){ sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
char buf[128]; char buf[128];
sl_sock_int_t *i = (sl_sock_int_t *)hitem->data; sl_sock_int_t *i = (sl_sock_int_t *)hitem->data;
if(!i) return RESULT_FAIL;
if(!str){ // getter if(!str){ // getter
snprintf(buf, 127, "%s=%" PRId64 "\n", hitem->key, i->val); snprintf(buf, 127, "%s=%" PRId64 "\n", hitem->key, i->val);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
@@ -841,6 +942,7 @@ sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem,
sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){ sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
char buf[128]; char buf[128];
sl_sock_double_t *d = (sl_sock_double_t *)hitem->data; sl_sock_double_t *d = (sl_sock_double_t *)hitem->data;
if(!d) return RESULT_FAIL;
if(!str){ // getter if(!str){ // getter
snprintf(buf, 127, "%s=%g\n", hitem->key, d->val); snprintf(buf, 127, "%s=%g\n", hitem->key, d->val);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
@@ -855,6 +957,7 @@ sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem,
sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){ sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str){
char buf[SL_VAL_LEN + SL_KEY_LEN + 3]; char buf[SL_VAL_LEN + SL_KEY_LEN + 3];
sl_sock_string_t *s = (sl_sock_string_t*) hitem->data; sl_sock_string_t *s = (sl_sock_string_t*) hitem->data;
if(!s) return RESULT_FAIL;
if(!str){ // getter if(!str){ // getter
snprintf(buf, SL_VAL_LEN + SL_KEY_LEN + 2, "%s=%s\n", hitem->key, s->val); snprintf(buf, SL_VAL_LEN + SL_KEY_LEN + 2, "%s=%s\n", hitem->key, s->val);
sl_sock_sendstrmessage(client, buf); sl_sock_sendstrmessage(client, buf);
@@ -868,3 +971,24 @@ sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem,
s->val[l] = 0; s->val[l] = 0;
return RESULT_OK; return RESULT_OK;
} }
/**
* @brief sl_sock_keyno_init - init k->magick and k-> to default values
* @param k - key's number value
*/
void sl_sock_keyno_init(sl_sock_keyno_t* k){
if(!k) return;
k->magick = -INFINITY;
k->n = -1;
}
/**
* @brief sl_sock_keyno_check - check if this is a really `sl_sock_keyno_t`
* @param k - pointer to check
* @return k.n or -1 if failed
*/
int sl_sock_keyno_check(sl_sock_keyno_t* k){
DBG("magick=%g (%d)", k->magick, isinf(k->magick));
if(!k || -1 != isinf(k->magick)) return -1;
DBG("k->n=%d", k->n);
return k->n;
}

113
term2.c
View File

@@ -89,47 +89,57 @@ someerr:
} }
/** /**
* @brief tty_init - open & setup terminal * @brief sl_tty_fdescr - open terminal device and give it's file descriptor
* @param descr (io) - port descriptor * @param comdev - path to device
* !!! default settings are "8N1". * @param speed - baudrate
* !!! If you want other, set it like `descr->format = "7O2"` between `sl_tty_new` and `sl_tty_open` * @param exclusive - ==1 for exclusive open
* @return 0 if all OK, last error code or 1 * @return fd or -1 in case of error
*/ */
static int tty_init(sl_tty_t *descr){ int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive){
if(!comdev || speed < 1) return -1;
tcflag_t flags; tcflag_t flags;
if(!parse_format(descr->format, &flags)) return 1; if(!parse_format(format, &flags)) return -1;
if((descr->comfd = open(descr->portname, O_RDWR|O_NOCTTY)) < 0){ DBG("open");
WARN(_("Can't use port %s"), descr->portname); int comfd = open(comdev, O_RDWR|O_NOCTTY);
return globErr ? globErr : 1; if(comfd < 0){
WARN(_("Can't use port %s"), comdev);
return -1;
} }
if(ioctl(descr->comfd, TCGETS2, &descr->oldtty)){ // Get settings DBG("fd=%d", comfd);
struct termios2 newtty;
if(ioctl(comfd, TCGETS2, &newtty)){ // Get settings
WARN(_("Can't get old TTY settings")); WARN(_("Can't get old TTY settings"));
return globErr ? globErr : 1; return -1;
} }
descr->tty = descr->oldtty; DBG("change termios flags");
descr->tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG) newtty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
descr->tty.c_iflag = 0; // don't do any changes in input stream newtty.c_iflag = 0; // don't do any changes in input stream
descr->tty.c_oflag = 0; // don't do any changes in output stream newtty.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 newtty.c_cflag = BOTHER | flags | CREAD | CLOCAL; // other speed, user format, RW, ignore line ctrl
descr->tty.c_ispeed = descr->speed; newtty.c_ispeed = speed;
descr->tty.c_ospeed = descr->speed; newtty.c_ospeed = speed;
descr->tty.c_cc[VMIN] = 0; // non-canonical mode newtty.c_cc[VMIN] = 0; // non-canonical mode
descr->tty.c_cc[VTIME] = 5; newtty.c_cc[VTIME] = 1;
if(ioctl(descr->comfd, TCSETS2, &descr->tty)){ if(ioctl(comfd, TCSETS2, &newtty)){
WARN(_("Can't apply new TTY settings")); WARN(_("Can't apply new TTY settings"));
return globErr ? globErr : 1; close(comfd);
return -1;
} }
ioctl(descr->comfd, TCGETS2, &descr->tty); DBG("OK, check");
if(descr->tty.c_ispeed != (speed_t)descr->speed || descr->tty.c_ospeed != (speed_t)descr->speed){ if(ioctl(comfd, TCGETS2, &newtty)){ // Get settings
WARN(_("Can't set speed %d, got ispeed=%d, ospeed=%d"), descr->speed, descr->tty.c_ispeed, descr->tty.c_ospeed); WARN(_("Can't get old TTY settings"));
descr->speed = descr->tty.c_ispeed; return -1;
} }
// make exclusive open if(exclusive){
if(descr->exclusive){ DBG("make exclusive");
if(ioctl(descr->comfd, TIOCEXCL)){ if(ioctl(comfd, TIOCEXCL)){
WARN(_("Can't do exclusive open")); WARN(_("Can't do exclusive open"));
}} close(comfd);
return 0; return -1;
}
}
DBG("device %s opened", comdev);
return comfd;
} }
/** /**
@@ -138,13 +148,16 @@ static int tty_init(sl_tty_t *descr){
void sl_tty_close(sl_tty_t **descr){ void sl_tty_close(sl_tty_t **descr){
if(descr == NULL || *descr == NULL) return; if(descr == NULL || *descr == NULL) return;
sl_tty_t *d = *descr; sl_tty_t *d = *descr;
if(d->comfd){ if(d->comfd > -1){
ioctl(d->comfd, TCSETS2, &d->oldtty); // return TTY to previous state DBG("close");
close(d->comfd); close(d->comfd);
} }
DBG("Free mem");
FREE(d->portname); FREE(d->portname);
FREE(d->buf); FREE(d->buf);
FREE(d->format);
FREE(*descr); FREE(*descr);
DBG("tty closed");
} }
/** /**
@@ -164,6 +177,8 @@ sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){
if(bufsz){ if(bufsz){
descr->buf = MALLOC(char, bufsz+1); descr->buf = MALLOC(char, bufsz+1);
descr->bufsz = bufsz; descr->bufsz = bufsz;
descr->comfd = -1;
DBG("sl_tty_t created");
return descr; return descr;
}else WARNX(_("Need non-zero buffer for TTY device")); }else WARNX(_("Need non-zero buffer for TTY device"));
} }
@@ -172,6 +187,20 @@ sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){
return NULL; return NULL;
} }
/**
* @brief sl_tty_setformat - set format for just created tty object
* @param d - descriptor created with sl_tty_new
* @param format - string like "7O2" (maybe NULL for default, 8N1)
* @return FALSE if format is wrong
*/
int sl_tty_setformat(sl_tty_t *d, const char *format){
if(!d) return FALSE;
if(!format) return TRUE; // default format
if(!parse_format(format, NULL)) return FALSE;
d->format = strdup(format);
return TRUE;
}
/** /**
* @brief sl_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 d - already filled structure (with new_tty or by hands)
@@ -182,13 +211,27 @@ sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive){
if(!d || !d->portname) return NULL; if(!d || !d->portname) return NULL;
if(exclusive) d->exclusive = TRUE; if(exclusive) d->exclusive = TRUE;
else d->exclusive = FALSE; else d->exclusive = FALSE;
if(tty_init(d)) return NULL; DBG("ex: %d", exclusive);
int comfd = sl_tty_fdescr(d->portname, d->format, d->speed, d->exclusive);
if(comfd < 0) return NULL;
struct termios2 tty;
if(ioctl(comfd, TCGETS2, &tty)){ // Get settings
WARN(_("Can't get current TTY settings"));
}else{
if(d->speed != (int)tty.c_ispeed || d->speed != (int)tty.c_ospeed){
WARNX(_("Can't set exact speed %d"), d->speed);
d->speed = (int)tty.c_ispeed;
}
}
d->comfd = comfd;
DBG("sl_tty_t ready");
return d; return d;
} }
static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000}; static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000};
/** /**
* @brief sl_tty_tmout - set timeout for select() on reading * @brief sl_tty_tmout - set timeout for select() on reading
* // WARNING! This function changes timeout for ALL opened ports!
* @param usec - microseconds of timeout * @param usec - microseconds of timeout
* @return -1 if usec < 0, 0 if all OK * @return -1 if usec < 0, 0 if all OK
*/ */

View File

@@ -308,22 +308,14 @@ char *sl_omitspacesr(const char *v){
* BE CAREFULL! These functions aren't thread-safe! * BE CAREFULL! These functions aren't thread-safe!
\******************************************************************************/ \******************************************************************************/
// console flags // console flags
#ifndef SL_USE_OLD_TTY
static struct termios2 oldt, newt; static struct termios2 oldt, newt;
#else
static struct termios oldt, newt;
#endif
static int console_changed = 0; static int console_changed = 0;
/** /**
* @brief sl_restore_con - restore console to default mode * @brief sl_restore_con - restore console to default mode
*/ */
void sl_restore_con(){ void sl_restore_con(){
if(console_changed) if(console_changed)
#ifndef SL_USE_OLD_TTY
ioctl(STDIN_FILENO, TCSETS2, &oldt); ioctl(STDIN_FILENO, TCSETS2, &oldt);
#else
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
#endif
console_changed = 0; console_changed = 0;
} }
@@ -332,24 +324,12 @@ void sl_restore_con(){
*/ */
void sl_setup_con(){ void sl_setup_con(){
if(console_changed) return; if(console_changed) return;
#ifndef SL_USE_OLD_TTY
ioctl(STDIN_FILENO, TCGETS2, &oldt); ioctl(STDIN_FILENO, TCGETS2, &oldt);
#else
tcgetattr(STDIN_FILENO, &oldt);
#endif
newt = oldt; newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO); newt.c_lflag &= ~(ICANON | ECHO);
#ifndef SL_USE_OLD_TTY
if(ioctl(STDIN_FILENO, TCSETS2, &newt)){ if(ioctl(STDIN_FILENO, TCSETS2, &newt)){
#else
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
#endif
WARN(_("Can't setup console")); WARN(_("Can't setup console"));
#ifndef SL_USE_OLD_TTY
ioctl(STDIN_FILENO, TCSETS2, &oldt); ioctl(STDIN_FILENO, TCSETS2, &oldt);
#else
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
#endif
signals(1); //quit? signals(1); //quit?
} }
console_changed = 1; console_changed = 1;
@@ -400,7 +380,7 @@ int sl_str2d(double *num, const char *str){
if(!str) return FALSE; if(!str) return FALSE;
res = strtod(str, &endptr); res = strtod(str, &endptr);
if(endptr == str || *str == '\0' || *endptr != '\0'){ if(endptr == str || *str == '\0' || *endptr != '\0'){
WARNX(_("Wrong double number format '%s'"), str); //WARNX(_("Wrong double number format '%s'"), str);
return FALSE; return FALSE;
} }
if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number
@@ -413,7 +393,7 @@ int sl_str2ll(long long *num, const char *str){
if(!str) return FALSE; if(!str) return FALSE;
res = strtoll(str, &endptr, 0); res = strtoll(str, &endptr, 0);
if(endptr == str || *str == '\0' || *endptr != '\0'){ if(endptr == str || *str == '\0' || *endptr != '\0'){
WARNX(_("Wrong integer number format '%s'")); //WARNX(_("Wrong integer number format '%s'"));
return FALSE; return FALSE;
} }
if(num) *num = res; if(num) *num = res;
@@ -425,11 +405,11 @@ int sl_str2i(int *num, const char *str){
if(!str) return FALSE; if(!str) return FALSE;
res = strtoll(str, &endptr, 0); res = strtoll(str, &endptr, 0);
if(endptr == str || *str == '\0' || *endptr != '\0'){ if(endptr == str || *str == '\0' || *endptr != '\0'){
WARNX(_("Wrong integer number format '%s'")); //WARNX(_("Wrong integer number format '%s'"));
return FALSE; return FALSE;
} }
if(res > INT_MAX || res < INT_MIN){ if(res > INT_MAX || res < INT_MIN){
WARNX(_("Number out of integer limits")); //WARNX(_("Number out of integer limits"));
return FALSE; return FALSE;
} }
if(num) *num = (int)res; if(num) *num = (int)res;

View File

@@ -22,11 +22,7 @@
#pragma once #pragma once
#include <stdio.h> #include <stdio.h>
#ifdef SL_USE_OLD_TTY
#include <termios.h> // termios
#else
#include <asm-generic/termbits.h> #include <asm-generic/termbits.h>
#endif
#include <errno.h> // errno #include <errno.h> // errno
#include <netdb.h> // struct addrinfo #include <netdb.h> // struct addrinfo
@@ -36,6 +32,7 @@
#include <unistd.h> // pid_t #include <unistd.h> // pid_t
// just for different purposes // just for different purposes
#include <limits.h> #include <limits.h>
#include <math.h> // inf/isinf
#include <stdint.h> #include <stdint.h>
#if defined GETTEXT #if defined GETTEXT
@@ -166,42 +163,25 @@ int sl_canwrite(int fd);
/******************************************************************************\ /******************************************************************************\
The original term.h 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
tcflag_t baudrate; // baudrate (B...)
struct termios oldtty; // TTY flags for previous port settings
struct termios 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;
tcflag_t sl_tty_convspd(int speed);
#else
typedef struct { typedef struct {
char *portname; // device filename (should be freed before structure freeing) char *portname; // device filename (should be freed before structure freeing)
int speed; // baudrate in human-readable format int speed; // baudrate in human-readable format
char *format; // format like 8N1 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 int comfd; // TTY file descriptor
char *buf; // buffer for data read char *buf; // buffer for data read
size_t bufsz; // size of buf size_t bufsz; // size of buf
size_t buflen; // length of data read into buf size_t buflen; // length of data read into buf
int exclusive; // should device be exclusive opened int exclusive; // should device be exclusive opened
} sl_tty_t; } sl_tty_t;
#endif
void sl_tty_close(sl_tty_t **descr); int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive);
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz); sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz);
int sl_tty_setformat(sl_tty_t *d, const char *format);
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive); 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_tmout(double usec);
int sl_tty_read(sl_tty_t *descr);
int sl_tty_write(int comfd, const char *buff, size_t length); int sl_tty_write(int comfd, const char *buff, size_t length);
void sl_tty_close(sl_tty_t **descr);
/******************************************************************************\ /******************************************************************************\
Logging Logging
@@ -326,6 +306,7 @@ typedef struct{
extern const char *__progname; extern const char *__progname;
void sl_showhelp(int oindex, sl_option_t *options); void sl_showhelp(int oindex, sl_option_t *options);
void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*));
void sl_parseargs(int *argc, char ***argv, sl_option_t *options); void sl_parseargs(int *argc, char ***argv, sl_option_t *options);
/** /**
* @brief sl_helpstring - change standard help header * @brief sl_helpstring - change standard help header
@@ -374,16 +355,18 @@ void *sl_list_pop(sl_list_t **lst);
#define SL_COMMENT_CHAR '#' #define SL_COMMENT_CHAR '#'
// option or simple configuration value (don't work for functions) // option or simple configuration value (don't work for functions)
typedef struct{ /*typedef struct{
union{ union{
int ival; long long llval; double dval; float fval; int ival; long long llval; double dval; float fval;
}; };
sl_argtype_e type; sl_argtype_e type;
} sl_optval; } sl_optval;*/
int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]); 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); char *sl_print_opts(sl_option_t *opt, int showall);
int sl_conf_readopts(const char *filename, sl_option_t *options); int sl_conf_readopts(const char *filename, sl_option_t *options);
void sl_conf_showhelp(int idx, sl_option_t *options);
int sl_remove_quotes(char *string);
/******************************************************************************\ /******************************************************************************\
The original ringbuffer.h The original ringbuffer.h
@@ -414,6 +397,8 @@ size_t sl_RB_writestr(sl_ringbuffer_t *b, char *s);
/******************************************************************************\ /******************************************************************************\
The original socket.h The original socket.h
\******************************************************************************/ \******************************************************************************/
// max length (including trailing '\0') of UNIX socket path
#define UNIX_SOCK_PATH_MAX 108
// handler result: what to send to client // handler result: what to send to client
typedef enum{ typedef enum{
@@ -442,6 +427,15 @@ typedef struct{
int len; // strlen of `val` int len; // strlen of `val`
} sl_sock_string_t; } sl_sock_string_t;
// optional keyword number like key[12] = 500
typedef struct{
double magick; // -Inf - to distinguish it from sl_sock_*_t
int n; // if n < 0 there was no any number in `key`
} sl_sock_keyno_t;
#define SL_SOCK_KEYNO_DEFAULT {.magick = -INFINITY, .n = -1}
void sl_sock_keyno_init(sl_sock_keyno_t*);
int sl_sock_keyno_check(sl_sock_keyno_t*);
struct sl_sock_hitem; struct sl_sock_hitem;
struct sl_sock; struct sl_sock;
@@ -465,6 +459,9 @@ typedef enum{
struct sl_sock; struct sl_sock;
// opent socket and return its file descriptor
int sl_sock_open(sl_socktype_e type, const char *path, int isserver, int ai_socktype);
// default max clients amount // default max clients amount
#define SL_DEF_MAXCLIENTS (32) #define SL_DEF_MAXCLIENTS (32)
// custom socket handlers: connect/disconnect/etc // custom socket handlers: connect/disconnect/etc
@@ -532,4 +529,3 @@ int sl_sock_sendall(sl_sock_t *sock, uint8_t *data, size_t len);
sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str); sl_sock_hresult_e sl_sock_inthandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str); sl_sock_hresult_e sl_sock_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);
sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str); sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);