version 0.2.1

This commit is contained in:
Edward Emelianov 2024-11-15 11:42:29 +03:00
parent 511a83e506
commit e77fddc3b8
22 changed files with 1337 additions and 495 deletions

View File

@ -2,3 +2,5 @@ cmake defines:
-DDEBUG=1 - debug mode
-DNOGETTEXT=1 - don't run xgettext
-DEXAMPLES=1 - to compile examples
-DSL_USE_OLD_TTY - use old termios instead of termios2 (you should use this flag always if library works so)

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.9)
set(PROJ usefull_macros)
set(MINOR_VERSION "1")
set(MID_VERSION "1")
set(MID_VERSION "2")
set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
@ -51,6 +51,16 @@ message("Build type: ${CMAKE_BUILD_TYPE}")
# here is one of two variants: all .c in directory or .c files in list
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
get_filename_component(term_c ${CMAKE_CURRENT_SOURCE_DIR}/term.c ABSOLUTE)
if(DEFINED USE_OLD_TTY OR DEFINED SL_USE_OLD_TTY)
get_filename_component(term_c ${CMAKE_CURRENT_SOURCE_DIR}/term2.c ABSOLUTE)
add_definitions(-DSL_USE_OLD_TTY)
endif()
message("remove ${term_c}")
list(REMOVE_ITEM SOURCES ${term_c})
# directory should contain dir locale/ru for gettext translations
set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru)
if(NOT DEFINED LOCALEDIR)
@ -136,7 +146,7 @@ if(NOT DEFINED NOGETTEXT)
add_custom_command(
OUTPUT ${PO_FILE}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=utf-8 ${SOURCES} -c -k_ -kN_ -o ${PO_FILE}
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=koi8-r ${SOURCES} -c -k_ -kN_ -o ${PO_FILE}
COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE}
COMMAND enconv ${PO_FILE}
DEPENDS ${SOURCES}

View File

@ -5,3 +5,52 @@ add tty_timeout()
fix read_tty() for disconnect
add logging
add sl_libversion()
VERSION 0.2.1:
RENAME:
initial_setup -> sl_init
dtime -> sl_dtime
my_alloc -> sl_alloc
mmapbuf -> sl_mmapuf_t
My_mmap -> sl_mmap
My_munmap -> sl_munmap
restore_console -> sl_restore_con
setup_con -> sl_setup_con
read_console -> sl_read_con
mygetchar -> sl_getchar
throw_random_seed -> sl_random_seed
get_available_mem -> sl_mem_avail
TTY_descr -> sl_tty_t
close_tty -> sl_tty_close
new_tty -> sl_tty_new
tty_open -> sl_tty_open
read_tty -> sl_tty_read
tty_timeout -> sl_tty_tmout
write_tty -> sl_tty_write
conv_spd -> sl_tty_convspd
str2double -> sl_str2d
globlog -> sl_globlog
sl_loglevel -> sl_loglevel_e
sl_log -> sl_log_t
argfn -> sl_argfn_t
argtype -> sl_argtype_e
hasarg -> sl_hasarg_e
myoption -> sl_option_t
mysuboption -> sl_suboption_t
showhelp -> sl_showhelp
parseargs -> sl_parseargs
change_helpstring -> sl_helpstring
get_suboption -> sl_get_suboption
iffound_default -> sl_iffound_deflt
check4running -> sl_check4running
readPSname -> sl_getPSname
struct buff_node -> struct sl_buff_node
List -> sl_list_t
list_push_tail -> sl_list_push_tail
list_push -> sl_list_push
list_pop -> sl_list_pop
Remove deprecated code (openlogfile, putlog)
Remove "bool" type (change to int)
FIXED bug with several only long options with same shortopt mark (like 0)
Add simple config files, so you can move some of your parameters settings to it

1
TODO
View File

@ -1,2 +1 @@
BUG in commandline options: '?' don't work!
Add prefixes sl_ to every function for solving further problems

300
config.c Normal file
View File

@ -0,0 +1,300 @@
/*
* This file is part of the Snippets project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include <float.h> // FLT_max/min
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "usefull_macros.h"
/**
* @brief sl_get_keyval - get key name and its value from string pair
* @param pair - empty string, `key = value` or just `key`
* @param key - substring with `key`
* @param value - its value or empty line
* @return 0 if no key found (or line is a comment with '#' first); 1 if only key found and 2 if found both key and value
* this function removes all leading and trailing spaces in `key` and `value`;
* also if `key` have several words only first saved, `value` can be long string with or without quotations
*/
int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){
DBG("got pair: '%s'", pair);
if(!pair || !*pair) return 0; // empty line
char *kstart = sl_omitspaces(pair);
if(!*kstart || *kstart == SL_COMMENT_CHAR) return 0; // only spaces
int ret = 1;
char *eq = strchr(kstart, '='), *kend = kstart + 1;
if(eq == kstart) return 0; // only = (and maybe val)
char *cmnt = strchr(kstart, SL_COMMENT_CHAR);
if(eq && cmnt && cmnt < eq) eq = NULL; // comment starting before equal sign
if(eq){ do{
DBG("got equal symbol: '%s'", eq);
char *vstart = sl_omitspaces(eq + 1);
if(!*vstart) break; // empty line or only spaces after `=`
char *vend = sl_omitspacesr(vstart);
size_t l = SL_VAL_LEN - 1; // truncate value to given length
if(l > (size_t)(vend - vstart)) l = vend - vstart;
DBG("l=%zd", l);
strncpy(value, vstart, l);
value[l] = 0;
cmnt = strchr(value, SL_COMMENT_CHAR);
if(cmnt){ // remove trailing spaces before comment
*cmnt = 0;
*sl_omitspacesr(value) = 0;
}
if(!*value) break;
DBG("Got value: '%s'", value);
ret = 2;
}while(0);
}else eq = kstart + strlen(kstart) - 1;
for(; kend < eq && !isspace(*kend); ++kend);
size_t l = SL_KEY_LEN - 1;
if(l > (size_t)(kend - kstart)) l = kend - kstart;
strncpy(key, kstart, l);
key[l] = 0;
cmnt = strchr(key, SL_COMMENT_CHAR);
if(cmnt) *cmnt = 0;
DBG("Got key: '%s'", key);
return ret;
}
// Read key/value from file; return -1 when file is over
static int read_key(FILE *file, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){
char *line = NULL;
size_t n = 0;
ssize_t got = getline(&line, &n, file);
if(!line) return 0;
if(got < 0){ // EOF
free(line);
return -1;
}
int r = sl_get_keyval(line, key, value);
free(line);
return r;
}
// search `opt` record for given `key`
static sl_option_t *opt_search(const char *key, sl_option_t *options){
while(options->name){
if(0 == strcmp(key, options->name)) return options;
++options;
}
return NULL;
}
/**
* @brief sl_print_opts - fills string buffer with information from opt
* @param opt - options buffer
* @param showall - TRUE to show even uninitialized options with NO_ARGS
* @return allocated string (should be free'd)
*/
char *sl_print_opts(sl_option_t *opt, int showall){
char *buf = MALLOC(char, BUFSIZ);
size_t L = BUFSIZ, l = 0;
for(; opt->name; ++opt){
DBG("check %s", opt->name);
if(!showall && opt->has_arg == NO_ARGS) continue; // show NO_ARGS only when `showall==TRUE`
if(!showall && opt->type == arg_string && !opt->argptr) continue; // empty string
if((ssize_t)L - l < SL_KEY_LEN + SL_VAL_LEN + 5){
L += BUFSIZ;
buf = realloc(buf, L);
if(!buf) ERR("realloc()");
}
l += sprintf(buf + l, "%s=", opt->name);
if(opt->flag){
DBG("got flag '%d'", *opt->flag);
l += sprintf(buf + l, "%d\n", *opt->flag);
continue;
}
if(!opt->argptr){ // ERR!
l += sprintf(buf + l, "\"(no argptr)\"\n");
WARNX("Parameter \"%s\" have no argptr!", opt->name);
continue;
}
int z = 0;
DBG("type: %d", opt->type);
switch(opt->type){
case arg_none:
case arg_int:
DBG("int %d", *(int*) opt->argptr);
l += sprintf(buf + l, "%d", *(int*) opt->argptr);
break;
case arg_longlong:
DBG("long long %lld", *(long long*) opt->argptr);
l += sprintf(buf + l, "%lld", *(long long*) opt->argptr);
break;
case arg_float:
DBG("float %g", *(float*) opt->argptr);
l += sprintf(buf + l, "%g", *(float*) opt->argptr);
break;
case arg_double:
DBG("double %g", *(double*) opt->argptr);
l += sprintf(buf + l, "%g", *(double*) opt->argptr);
break;
case arg_string:
if(!opt->argptr || !(*(char*)opt->argptr)){
l += sprintf(buf + l, "(null)");
break;
}else if(!(**(char**)opt->argptr)){
l += sprintf(buf + l, "(empty)");
break;
}
DBG("string %s", *(char**) opt->argptr);
z = strlen(*(char**) opt->argptr);
while(l + z > L + 3){
L += BUFSIZ;
buf = realloc(buf, L);
}
l += sprintf(buf + l, "%s", *(char**) opt->argptr);
break;
default:
DBG("function");
l += sprintf(buf + l, "\"(unsupported)\"");
break;
}
l += sprintf(buf + l, "\n");
}
return buf;
}
/**
* @brief sl_set_optval - convert `val` to `oval` parameter according to argument type
* @param oval (o) - value in int/long long/double/float
* @param opt (i) - record with options
* @param val (i) - new value
* @return FALSE if failed (or wrong data range)
*/
int sl_set_optval(sl_optval *oval, sl_option_t *opt, const char *val){
if(!oval || !opt || !val) return FALSE;
long long ll;
double d;
switch(opt->type){
case arg_none:
case arg_int:
case arg_longlong:
do{
if(!sl_str2ll(&ll, val)){
break;
}
if(opt->type != arg_longlong){
if(ll > INT_MAX || ll < INT_MIN){
break;
}
oval->ival = (int)ll;
}else oval->llval = ll;
return TRUE;
}while(0);
break;
case arg_double:
case arg_float:
do{
if(!sl_str2d(&d, val)){
break;
}
if(opt->type == arg_double){
oval->dval = d;
}else{
if(d > FLT_MAX || d < FLT_MIN){
break;
}
oval->fval = (float)d;
}
return TRUE;
}while(0);
break;
case arg_string:
return TRUE;
break;
default:
WARNX(_("Unsupported option type"));
return FALSE;
}
WARNX(_("Wrong number format '%s'"), val);
return FALSE;
}
// increment opt->argptr or set it to val
static void setoa(sl_option_t *opt, const char *val){
if(!opt || !opt->argptr || opt->type == arg_function) return;
sl_optval O;
if(!sl_set_optval(&O, opt, val)) return;
switch(opt->type){
case arg_none: // increment integer
*(int*) opt->argptr += O.ival;
break;
case arg_int:
*(int*) opt->argptr = O.ival;
break;
case arg_longlong:
*(long long*) opt->argptr = O.llval;
break;
case arg_double:
*(double*) opt->argptr = O.dval;
break;
case arg_float:
*(float*) opt->argptr = O.fval;
break;
case arg_string:
*(char**)opt->argptr = strdup(val);
break;
default:
break;
}
}
/**
* @brief sl_conf_readopts - simplest configuration:
* read file with lines "par" or "par = val" and set variables like they are options
* @param filename - configuration file name
* @param options - array with options (could be the same like for sl_parseargs)
* @return amount of data read
*/
int sl_conf_readopts(const char *filename, sl_option_t *options){
if(!filename || !options) return 0;
FILE *f = fopen(filename, "r");
if(!f){
WARN(_("Can't open %s"), filename);
return 0;
}
int N = 0;
char key[SL_KEY_LEN], val[SL_VAL_LEN];
do{
int r = read_key(f, key, val);
if(r < 0) break;
if(r == 0) continue;
sl_option_t *opt = opt_search(key, options);
if(!opt){
WARNX(_("Wrong key: '%s'"), key);
continue;
}
if(opt->flag) *opt->flag = opt->val;
if(r == 1){ // only key
if(opt->has_arg != NO_ARGS && opt->has_arg != OPT_ARG){
WARNX(_("Key '%s' need value"), opt->name);
continue;
}
if(opt->argptr) setoa(opt, "1");
}else{ // key + value
if(opt->argptr) setoa(opt, val);
else WARNX(_("Key '%s' have no argptr!"), opt->name);
}
++N;
}while(1);
return N;
}

View File

@ -31,13 +31,13 @@
#include "usefull_macros.h"
/**
* @brief readPSname - read process name from /proc/PID/cmdline
* @brief sl_getPSname - read process name from /proc/PID/cmdline
* @param pid - PID of interesting process
* @return filename or NULL if not found
* don't use this function twice for different names without copying
* its returning by strdup, because `name` contains in static array
*/
char *readPSname(pid_t pid){
char *sl_getPSname(pid_t pid){
static char name[PATH_MAX];
char *pp = name, byte, path[PATH_MAX];
FILE *file;
@ -61,11 +61,11 @@ char *readPSname(pid_t pid){
}
/**
* @brief iffound_default - default action when running process found
* @brief sl_iffound_deflt - default action when running process found
* @param pid - another process' pid
* Redefine this function for user action
*/
void WEAK iffound_default(pid_t pid){
void WEAK sl_iffound_deflt(pid_t pid){
/// \nïÂÎÁÒÕÖÅÎ ÏÄÎÏÉÍÅÎÎÙÊ ÐÒÏÃÅÓÓ (pid=%d), ×ÙÈÏÄ.\n
fprintf(stderr, _("\nFound running process (pid=%d), exit.\n"), pid);
exit(-1);
@ -81,7 +81,7 @@ void WEAK iffound_default(pid_t pid){
* @param selfname - argv[0] or NULL for non-locking
* @param pidfilename - name of pidfile or NULL if none
*/
void check4running(char *selfname, char *pidfilename){
void sl_check4running(char *selfname, char *pidfilename){
DIR *dir;
FILE *pidfile, *fself;
struct dirent *de;
@ -102,7 +102,7 @@ void check4running(char *selfname, char *pidfilename){
goto selfpid;
}
if(fl.l_type != F_UNLCK){ // file is locking - exit
iffound_default(fl.l_pid);
sl_iffound_deflt(fl.l_pid);
}
fl.l_type = F_RDLCK;
if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){
@ -114,7 +114,7 @@ void check4running(char *selfname, char *pidfilename){
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
ERR(PROC_BASE);
}
if(!(name = readPSname(self))){ // error reading self name
if(!(name = sl_getPSname(self))){ // error reading self name
ERR(_("Can't read self name"));
}
myname = strdup(name);
@ -122,8 +122,8 @@ void check4running(char *selfname, char *pidfilename){
pidfile = fopen(pidfilename, "r");
if(pidfile){
if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process
if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0)
iffound_default(pid);
if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0)
sl_iffound_deflt(pid);
}
fclose(pidfile);
}
@ -132,8 +132,8 @@ void check4running(char *selfname, char *pidfilename){
while((de = readdir(dir))){ // scan /proc
if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self
continue;
if((name = readPSname(pid)) && strncmp(name, myname, 255) == 0)
iffound_default(pid);
if((name = sl_getPSname(pid)) && strncmp(name, myname, 255) == 0)
sl_iffound_deflt(pid);
}
closedir(dir);
free(myname);

View File

@ -9,3 +9,4 @@ link_libraries(usefull_macros)
add_executable(helloworld helloworld.c)
add_executable(options options.c cmdlnopts.c)
add_executable(fifo fifo.c)
add_executable(conffile conffile.c)

View File

@ -38,6 +38,9 @@ static glob_pars G;
// DEFAULTS
// default global parameters
static glob_pars const Gdefault = {
.lo0 = INT_MIN,
.lo1 = INT_MIN,
.lo2 = INT_MIN,
.device = NULL,
.pidfile = DEFAULT_PIDFILE,
.speed = 9600,
@ -48,16 +51,22 @@ static glob_pars const Gdefault = {
* Define command line options by filling structure:
* name has_arg flag val type argptr help
*/
static myoption cmdlnopts[] = {
// common options
static sl_option_t cmdlnopts[] = {
{"lo0", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo0), _("only long arg 0")},
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
// {"dup", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name")},
{"lo2", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo2), _("only long arg 2")},
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("serial device speed (default: 9600)")},
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs")},
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")},
{"exclusive",NO_ARGS, NULL, 'e', arg_int, APTR(&G.exclusive), _("open serial device exclusively")},
end_option
// example of multiple options
{"Int", MULT_PAR, NULL, 'I', arg_int, APTR(&G.intarr), _("integer multiplying parameter")},
{"Dbl", MULT_PAR, NULL, 'D', arg_double, APTR(&G.dblarr), _("double multiplying parameter")},
{"Str", MULT_PAR, NULL, 'S', arg_string, APTR(&G.strarr), _("string multiplying parameter")},
{"lo1", NEED_ARG, NULL, 0, arg_int, APTR(&G.lo1), _("only long arg 1")},
end_option
};
/**
@ -75,10 +84,10 @@ glob_pars *parse_args(int argc, char **argv){
char helpstring[1024], *hptr = helpstring;
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
// format of help: "Usage: progname [args]\n"
change_helpstring(helpstring);
sl_helpstring(helpstring);
// parse arguments
parseargs(&argc, &argv, cmdlnopts);
if(help) showhelp(-1, cmdlnopts);
sl_parseargs(&argc, &argv, cmdlnopts);
if(help) sl_showhelp(-1, cmdlnopts);
if(argc > 0){
G.rest_pars_num = argc;
G.rest_pars = MALLOC(char *, argc);

View File

@ -31,8 +31,14 @@ typedef struct{
char *pidfile; // name of PID file
char *logfile; // logging to this file
int speed; // connection speed
int rest_pars_num; // number of rest parameters
int exclusive; // exclusive open port
int **intarr; // integer multopt
double **dblarr; // double -//-
char **strarr; // char -//-
int lo0; // only long options
int lo1;
int lo2;
int rest_pars_num; // number of rest parameters
char** rest_pars; // the rest parameters: array of char*
} glob_pars;

101
examples/conffile.c Normal file
View File

@ -0,0 +1,101 @@
/*
* This file is part of the Snippets project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h> // NaN
#include <stdint.h>
#include <stdio.h>
#include "usefull_macros.h"
typedef struct{
char *sp1;
char *sp2;
int ip1;
int ip2;
double dp1;
double dp2;
float fp1;
float fp2;
int help;
int verbose;
char *confname;
} parameters;
static parameters G = {
.ip1 = INT_MIN,
.ip2 = INT_MIN,
.dp1 = NAN,
.dp2 = NAN,
.fp1 = NAN,
.fp2 = NAN
};
static sl_option_t cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&G.help), "show this help"},
{"string1", NEED_ARG, NULL, 's', arg_string, APTR(&G.sp1), "string one"},
{"string2", NEED_ARG, NULL, 'c', arg_string, APTR(&G.sp2), "string two"},
{"int1", NEED_ARG, NULL, 'i', arg_int, APTR(&G.ip1), "integer one"},
{"int2", NEED_ARG, NULL, 'u', arg_int, APTR(&G.ip2), "integer two"},
{"double1", NEED_ARG, NULL, 'd', arg_double, APTR(&G.dp1), "double one"},
{"double2", NEED_ARG, NULL, 'o', arg_double, APTR(&G.dp2), "double two"},
{"float1", NEED_ARG, NULL, 'f', arg_float, APTR(&G.fp1), "float one"},
{"float2", NEED_ARG, NULL, 'l', arg_float, APTR(&G.fp2), "float two"},
{"config", NEED_ARG, NULL, 'C', arg_string, APTR(&G.confname),"name of configuration file"},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verbose),"verbose level (each -v adds 1)"},
end_option
};
int main(int argc, char **argv){
sl_init();
sl_parseargs(&argc, &argv, cmdlnopts);
if(G.help) sl_showhelp(-1, cmdlnopts);
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
printf("verbose level: %d\n", lvl);
if(G.sp1){
printf("Parsing of string1: ");
char key[SL_KEY_LEN], val[SL_VAL_LEN];
int k = sl_get_keyval(G.sp1, key, val);
switch(k){
case 0:
red("key not found\n");
break;
case 1:
green("got key='%s'\n", key);
break;
default:
green("got key='%s', value='%s'\n", key, val);
}
}
green("Starting parameters values:\n");
char *buf = sl_print_opts(cmdlnopts, TRUE);
printf("%s\n", buf);
FREE(buf);
if(G.confname){
int o = sl_conf_readopts(G.confname, cmdlnopts);
if(o > 0){
printf("got %d options in '%s'\n", o, G.confname);
green("And after reading of conffile:\n");
buf = sl_print_opts(cmdlnopts, TRUE);
printf("%s\n", buf);
FREE(buf);
}
}
return 0;
}

View File

@ -23,8 +23,8 @@
*/
int main(int argc, char *argv[argc]) {
List *f = NULL;
printf("Available memory: %luMB\n", get_available_mem()/1024/1024);
sl_list_t *f = NULL;
printf("Available memory: %luMB\n", sl_mem_avail()/1024/1024);
//initial_setup(); - there's no need for this function if you don't use locale & don't want to have
// specific output in non-tty
if(argc == 1){
@ -33,26 +33,26 @@ int main(int argc, char *argv[argc]) {
}
red("\n\nLIFO example\n");
for(int i = 1; i < argc; ++i){
if(!list_push(&f, argv[i])) ERR("Allocation error!");
if(!sl_list_push(&f, argv[i])) ERR("Allocation error!");
green("push to list ");
printf("%s\n", argv[i]);
}
char *d;
printf("\n");
while(f){
d = list_pop(&f);
d = sl_list_pop(&f);
green("pull: ");
printf("%s\n", d);
}
red("\n\nFIFO example\n");
for(int i = 1; i < argc; ++i){
if(!list_push_tail(&f, argv[i])) ERR("Allocation error!");
if(!sl_list_push_tail(&f, argv[i])) ERR("Allocation error!");
green("push to list ");
printf("%s\n", argv[i]);
}
printf("\n");
while(f){
d = list_pop(&f);
d = sl_list_pop(&f);
green("pull: ");
printf("%s\n", d);
// after last usage we should FREE data, but here it is parameter of main()

View File

@ -23,13 +23,13 @@
*/
int main(){
initial_setup();
sl_init();
// setup non-echo non-canonical mode
setup_con();
sl_setup_con();
green("Press any key...\n");
char k = mygetchar();
char k = sl_getchar();
red("You press %c\n", k);
// don't forget to restore @exit!
restore_console();
sl_restore_con();
return 0;
}

View File

@ -24,20 +24,6 @@
#include <unistd.h> // sleep
#include <usefull_macros.h>
#include <termios.h> // tcsetattr
#include <unistd.h> // tcsetattr, close, read, write
#include <sys/ioctl.h> // ioctl
#include <stdio.h> // printf, getchar, fopen, perror
#include <stdlib.h> // exit
#include <sys/stat.h> // read
#include <fcntl.h> // read
#include <signal.h> // signal
#include <time.h> // time
#include <string.h> // memcpy
#include <stdint.h> // int types
#include <sys/time.h> // gettimeofday
/**
* This is an example of usage:
* - command line arguments,
@ -48,7 +34,7 @@
* The `cmdlnopts.[hc]` are intrinsic files of this demo.
*/
static TTY_descr *dev = NULL; // shoul be global to restore if die
static sl_tty_t *dev = NULL; // shoul be global to restore if die
static glob_pars *GP = NULL; // for GP->pidfile need in `signals`
/**
@ -62,28 +48,26 @@ void signals(int sig){
LOGERR("Exit with status %d", sig);
if(GP && GP->pidfile) // remove unnesessary PID file
unlink(GP->pidfile);
restore_console();
if(dev) close_tty(&dev);
sl_restore_con();
if(dev) sl_tty_close(&dev);
exit(sig);
}
void iffound_default(pid_t pid){
void sl_iffound_deflt(pid_t pid){
ERRX("Another copy of this process found, pid=%d. Exit.", pid);
}
int main(int argc, char *argv[]){
initial_setup();
char *self = strdup(argv[0]);
sl_init();
GP = parse_args(argc, argv);
if(GP->rest_pars_num){
printf("%d extra options:\n", GP->rest_pars_num);
for(int i = 0; i < GP->rest_pars_num; ++i)
printf("%s\n", GP->rest_pars[i]);
}
check4running(self, GP->pidfile);
red("%s started, snippets library version is %s\n", self, sl_libversion());
free(self);
setup_con();
sl_check4running((char*)__progname, GP->pidfile);
red("%s started, snippets library version is %s\n", __progname, sl_libversion());
sl_setup_con();
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
@ -95,10 +79,25 @@ int main(int argc, char *argv[]){
for(int i = 0; i < GP->rest_pars_num; ++i)
printf("Extra argument: %s\n", GP->rest_pars[i]);
}
if(GP->intarr){
int **p = GP->intarr;
for(int i = 0; *p; ++i) printf("Integer[%d]: %d\n", i, **p++);
}
if(GP->dblarr){
double **p = GP->dblarr;
for(int i = 0; *p; ++i) printf("Double[%d]: %g\n", i, **p++);
}
if(GP->strarr){
char **p = GP->strarr;
for(int i = 0; *p; ++i) printf("String[%d]: \"%s\"\n", i, *p++);
}
if(GP->lo0 != INT_MIN) printf("You set lo0 to %d\n", GP->lo0);
if(GP->lo1 != INT_MIN) printf("You set lo1 to %d\n", GP->lo1);
if(GP->lo2 != INT_MIN) printf("You set lo2 to %d\n", GP->lo2);
if(GP->device){
LOGDBG("Try to open serial %s", GP->device);
dev = new_tty(GP->device, GP->speed, 4096);
if(dev) dev = tty_open(dev, GP->exclusive);
dev = sl_tty_new(GP->device, GP->speed, 4096);
if(dev) dev = sl_tty_open(dev, GP->exclusive);
if(!dev){
LOGERR("Can't open %s with speed %d. Exit.", GP->device, GP->speed);
signals(0);
@ -106,26 +105,26 @@ int main(int argc, char *argv[]){
}
// main stuff goes here
long seed = throw_random_seed();
long seed = sl_random_seed();
green("Now I will sleep for 10 seconds after your last input.\n Do whatever you want. Random seed: %ld\n", seed);
LOGWARN("warning message example");
LOGWARNADD("with next string without timestamp");
double t0 = dtime();
double t0 = sl_dtime();
char b[2] = {0};
while(dtime() - t0 < 10.){ // read data from port and print in into terminal
while(sl_dtime() - t0 < 10.){ // read data from port and print in into terminal
if(dev){
if(read_tty(dev)){
if(sl_tty_read(dev)){
printf("Got %zd bytes from port: %s\n", dev->buflen, dev->buf);
LOGMSG("Got from serial: %s", dev->buf);
t0 = dtime();
t0 = sl_dtime();
}
int r = read_console();
int r = sl_read_con();
if(r < 1) continue;
t0 = dtime();
t0 = sl_dtime();
b[0] = (char) r;
printf("send to tty: %d (%c)\n", r, b[0]);
LOGMSG("send to tty: %d (%c)\n", r, b[0]);
write_tty(dev->comfd, b, 1);
sl_tty_write(dev->comfd, b, 1);
}
}
// clean everything

View File

@ -22,60 +22,60 @@
#include "usefull_macros.h"
/**
* @brief list_push_tail - push data into the tail of a stack (like FIFO)
* @brief sl_list_push_tail - push data into the tail of a stack (like FIFO)
* @param lst (io) - list
* @param v (i) - data to push
* @return pointer to just pused node or NULL in case of error
*/
List *list_push_tail(List **lst, void *v){
List *node;
if(!lst) return NULL;
if((node = MALLOC(List, 1)) == NULL)
return NULL; // allocation error
node->data = v; // insert data
if(!*lst){
*lst = node;
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v){
sl_list_t *node;
if(!lst) return NULL;
if((node = MALLOC(sl_list_t, 1)) == NULL)
return NULL; // allocation error
node->data = v; // insert data
if(!*lst){
*lst = node;
}else{
(*lst)->last->next = node;
}
(*lst)->last = node;
return node;
(*lst)->last = node;
return node;
}
/**
* @brief list_push - push data into the head of a stack (like LIFO)
* @brief sl_list_push - push data into the head of a stack (like LIFO)
* @param lst (io) - list
* @param v (i) - data to push
* @return pointer to just pused node
*/
List *list_push(List **lst, void *v){
List *node;
if(!lst) return NULL;
if((node = MALLOC(List, 1)) == NULL)
return NULL; // allocation error
node->data = v; // insert data
if(!*lst){
*lst = node;
(*lst)->last = node;
return node;
}
node->next = *lst;
node->last = (*lst)->last;
*lst = node;
return node;
sl_list_t *sl_list_push(sl_list_t **lst, void *v){
sl_list_t *node;
if(!lst) return NULL;
if((node = MALLOC(sl_list_t, 1)) == NULL)
return NULL; // allocation error
node->data = v; // insert data
if(!*lst){
*lst = node;
(*lst)->last = node;
return node;
}
node->next = *lst;
node->last = (*lst)->last;
*lst = node;
return node;
}
/**
* @brief list_pop - get data from head of list. Don't forget to FREE list data after `pop`
* @brief sl_list_pop - get data from head of list. Don't forget to FREE list data after `pop`
* @param lst (io) - list
* @return data from lst head
*/
void *list_pop(List **lst){
void *ret;
List *node = *lst;
if(!lst || !*lst) return NULL;
ret = (*lst)->data;
*lst = (*lst)->next;
FREE(node);
return ret;
void *sl_list_pop(sl_list_t **lst){
void *ret;
sl_list_t *node = *lst;
if(!lst || !*lst) return NULL;
ret = (*lst)->data;
*lst = (*lst)->next;
FREE(node);
return ret;
}

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-24 12:22+0300\n"
"POT-Creation-Date: 2024-11-15 11:38+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,8 +17,36 @@ msgstr ""
"Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n"
#. / \nОбнаружен одноименный процесс (pid=%d), выход.\n
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:70
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:225
msgid "Unsupported option type"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:228
#, c-format
msgid "Wrong number format '%s'"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:272
#, c-format
msgid "Can't open %s"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:283
#, c-format
msgid "Wrong key: '%s'"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:289
#, c-format
msgid "Key '%s' need value"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:295
#, c-format
msgid "Key '%s' have no argptr!"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
#, c-format
msgid ""
"\n"
@ -26,154 +54,137 @@ msgid ""
msgstr ""
#. error reading self name
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:118
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:117
msgid "Can't read self name"
msgstr ""
#. / Не могу открыть PID файл
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:143
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141
msgid "Can't open PID file"
msgstr ""
#. amount of pcount and/or scount wrong
#. / Неправильный формат строки помощи
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:54
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53
msgid "Wrong helpstring!"
msgstr ""
#. / Целое вне допустимого диапазона
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:84
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:82
msgid "Integer out of range"
msgstr ""
#. / "Не могу использовать несколько параметров без аргументов!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:151
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166
msgid "Can't use multiple args with arg_none!"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:252
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267
#, c-format
msgid "double long arguments: --%s"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:258
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273
#, c-format
msgid "double short arguments: -%c"
msgstr ""
#. / Неправильный параметр: %s
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:470
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332
#, c-format
msgid "Need argument with %s type\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486
#, c-format
msgid "Wrong parameter: %s"
msgstr ""
#. / %s: необходим аргумент!
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:475
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490
#, c-format
msgid "%s: need argument!"
msgstr ""
#. / Неправильный аргумент \"%s\" параметра \"%s\"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:480
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494
#, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:92
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87
#, c-format
msgid "Wrong speed value: %d!"
msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2"
msgstr ""
#. / Не могу использовать порт %s
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:105
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102
#, c-format
msgid "Can't use port %s"
msgstr ""
#. Get settings
#. / Не могу получить действующие настройки порта
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:110
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106
msgid "Can't get old TTY settings"
msgstr ""
#. / Не могу сменить настройки порта
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:121
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119
msgid "Can't apply new TTY settings"
msgstr ""
#. / Не могу сделать порт эксклюзивным
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:128
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
#, c-format
msgid "Can't set speed %d, got ispeed=%d, ospeed=%d"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
msgid "Can't do exclusive open"
msgstr ""
#. / Отсутствует имя порта
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:164
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
msgid "Port name is missing"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:170
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168
msgid "Need non-zero buffer for TTY device"
msgstr ""
#. / Не могу открыть /dev/random
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:185
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
msgid "Can't open /dev/random"
msgstr ""
#. / Не могу прочесть /dev/random
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:190
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198
msgid "Can't read /dev/random"
msgstr ""
#. / Не задано имя файла!
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:241
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248
msgid "No filename given!"
msgstr ""
#. / Не могу открыть %s для чтения
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:246
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252
#, c-format
msgid "Can't open %s for reading"
msgstr ""
#. / Не могу выполнить stat %s
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:251
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#, c-format
msgid "Can't stat %s"
msgstr ""
#. / Ошибка mmap
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:258
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262
msgid "Mmap error for input"
msgstr ""
#. / Не могу закрыть mmap'нутый файл
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:263
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266
msgid "Can't close mmap'ed file"
msgstr ""
#. / Не могу munmap
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:277
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279
msgid "Can't munmap"
msgstr ""
#. / Не могу настроить консоль
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:308
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347
msgid "Can't setup console"
msgstr ""
#. / Не задано имя логфайла
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:383
msgid "Need filename for log file"
msgstr ""
#. / Пробую открыть логфайл %s в режиме дополнения\n
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:387
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403
#, c-format
msgid "Try to open log file %s in append mode\n"
msgid "Wrong double number format '%s'"
msgstr ""
#. / Не могу открыть логфайл
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:391
msgid "Can't open log file"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
#, c-format
msgid "Wrong integer number format '%s'"
msgstr ""

View File

@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-24 12:22+0300\n"
"POT-Creation-Date: 2024-11-15 11:38+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -16,164 +16,177 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n"
#. / \nОбнаружен одноименный процесс (pid=%d), выход.\n
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:70
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
#, c-format
msgid "\n"
"Found running process (pid=%d), exit.\n"
msgstr "\n"
"ïÂÎÁÒÕÖÅÎ ÏÄÎÏÉÍÅÎÎÙÊ ÐÒÏÃÅÓÓ (pid=%d), ×ÙÈÏÄ.\n"
#. / %s: необходим аргумент!
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:475
#, fuzzy, c-format
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490
#, c-format
msgid "%s: need argument!"
msgstr "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
#. / Не могу сменить настройки порта
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:121
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119
msgid "Can't apply new TTY settings"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#. / Не могу закрыть mmap'нутый файл
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:263
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266
msgid "Can't close mmap'ed file"
msgstr "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
#. / Не могу сделать порт эксклюзивным
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:128
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
msgid "Can't do exclusive open"
msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ÐÏÒÔ × ÜËÓËÌÀÚÉ×ÎÙÊ ÒÅÖÉÍ"
#. Get settings
#. / Не могу получить действующие настройки порта
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:110
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106
msgid "Can't get old TTY settings"
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#. / Не могу munmap
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:277
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279
msgid "Can't munmap"
msgstr "îÅ ÍÏÇÕ munmap"
#. / Не могу открыть %s для чтения
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:246
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:272
#, c-format
msgid "Can't open %s"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252
#, c-format
msgid "Can't open %s for reading"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
#. / Не могу открыть /dev/random
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:185
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
msgid "Can't open /dev/random"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ /dev/random"
#. / Не могу открыть PID файл
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:143
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141
msgid "Can't open PID file"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ PID ÆÁÊÌ"
#. / Не могу открыть логфайл
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:391
msgid "Can't open log file"
msgstr "Не могу открыть логфайл"
#. / Не могу прочесть /dev/random
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:190
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198
msgid "Can't read /dev/random"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ /dev/random"
#. error reading self name
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:118
#, fuzzy
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:117
msgid "Can't read self name"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ ÉÍÑ ÐÒÏÃÅÓÓÁ"
#. / Не могу настроить консоль
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:308
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
#, c-format
msgid "Can't set speed %d, got ispeed=%d, ospeed=%d"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÓËÏÒÏÓÔØ ÎÁ %d; ispeed=%d, ospeed=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347
msgid "Can't setup console"
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
#. / Не могу выполнить stat %s
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:251
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#, c-format
msgid "Can't stat %s"
msgstr "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
#. / "Не могу использовать несколько параметров без аргументов!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:151
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166
msgid "Can't use multiple args with arg_none!"
msgstr "íÁÓÓÉ× ÐÁÒÁÍÅÔÒÏ× ÎÅ ÍÏÖÅÔ ÉÍÅÔØ ÔÉÐ arg_none!"
#. / Не могу использовать порт %s
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:105
#, fuzzy, c-format
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102
#, c-format
msgid "Can't use port %s"
msgstr "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s"
#. / Целое вне допустимого диапазона
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:84
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:82
msgid "Integer out of range"
msgstr "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
#. / Ошибка mmap
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:258
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:295
#, c-format
msgid "Key '%s' have no argptr!"
msgstr "õ ËÌÀÞÁ '%s' ÎÅÔ ÕËÁÚÁÔÅÌÑ ÎÁ ÐÅÒÅÍÅÎÎÕÀ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:289
#, c-format
msgid "Key '%s' need value"
msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262
msgid "Mmap error for input"
msgstr "ïÛÉÂËÁ mmap"
#. / Не задано имя логфайла
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:383
msgid "Need filename for log file"
msgstr "Не задано имя логфайла"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332
#, c-format
msgid "Need argument with %s type\n"
msgstr "îÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ Ó ÔÉÐÏÍ %s\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:170
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168
msgid "Need non-zero buffer for TTY device"
msgstr "äÌÑ ÐÏÓÌÅÄÏ×ÁÔÅÌØÎÏÇÏ ÕÓÔÒÏÊÓÔ×Á ÔÒÅÂÕÅÔÓÑ ÂÕÆÅÒ ÎÅÎÕÌÅ×ÏÇÏ ÒÁÚÍÅÒÁ"
#. / Не задано имя файла!
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:241
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248
msgid "No filename given!"
msgstr "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!"
#. / Отсутствует имя порта
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:164
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
msgid "Port name is missing"
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
#. / Пробую открыть логфайл %s в режиме дополнения\n
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:387
#, c-format
msgid "Try to open log file %s in append mode\n"
msgstr "Пробую открыть логфайл %s в режиме дополнения\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:225
msgid "Unsupported option type"
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÏÐÃÉÉ"
#. / Неправильный аргумент \"%s\" параметра \"%s\"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:480
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:87
#, c-format
msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: "
"1/2"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ USART \"%s\"; ÎÕÖÅÎ NPS, ÇÄÅ N: 5..8; P: N/E/"
"O/1/0, S: 1/2"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494
#, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403
#, c-format
msgid "Wrong double number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ double: %s"
#. amount of pcount and/or scount wrong
#. / Неправильный формат строки помощи
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:54
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53
msgid "Wrong helpstring!"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
#. / Неправильный параметр: %s
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:470
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
#, c-format
msgid "Wrong integer number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÃÅÌÏÇÏ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:283
#, c-format
msgid "Wrong key: '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ËÌÀÞ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:228
#, c-format
msgid "Wrong number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486
#, c-format
msgid "Wrong parameter: %s"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term.c:92
#, c-format
msgid "Wrong speed value: %d!"
msgstr "Неправильное значение скорости: %d!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:252
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267
#, c-format
msgid "double long arguments: --%s"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ÄÌÉÎÎÙÊ ÐÁÒÁÍÅÔÒ: --%s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:258
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273
#, c-format
msgid "double short arguments: -%c"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"

View File

@ -33,10 +33,10 @@
char *helpstring = "%s\n";
/**
* @brief change_helpstring - change standard help header
* @brief sl_helpstring - change standard help header
* @param str (i) - new format (MAY consist ONE "%s" for progname)
*/
void change_helpstring(char *s){
void sl_helpstring(char *s){
int pcount = 0, scount = 0;
char *str = s;
// check `helpstring` and set it to default in case of error
@ -63,7 +63,7 @@ void change_helpstring(char *s){
* @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more)
* @return TRUE if conversion done without errors, FALSE otherwise
*/
static bool myatoll(void *num, char *str, argtype t){
static int myatoll(void *num, char *str, sl_argtype_e t){
long long tmp, *llptr;
int *iptr;
char *endptr;
@ -92,7 +92,7 @@ static bool myatoll(void *num, char *str, argtype t){
// the same as myatoll but for double
// There's no NAN & INF checking here (what if they would be needed?)
static bool myatod(void *num, const char *str, argtype t){
static int myatod(void *num, const char *str, sl_argtype_e t){
double tmp, *dptr;
float *fptr;
char *endptr;
@ -114,21 +114,39 @@ static bool myatod(void *num, const char *str, argtype t){
return TRUE;
}
/**
* @brief get_optind - get index of current option in array options
* @param opt (i) - returning val of getopt_long
* @param options (i) - array of options
* @return index in array
*/
static int get_optind(int opt, myoption *options){
static int get_optind(int opt, sl_option_t *options){
int oind;
myoption *opts = options;
sl_option_t *opts = options;
assert(opts);
for(oind = 0; opts->name && opts->val != opt; oind++, opts++);
for(oind = 0; opts->name && opts->val != opt; oind++, opts++){
DBG("cmp %c and %c", opt, opts->val);
}
if(!opts->name || opts->val != opt) // no such parameter
showhelp(-1, options);
sl_showhelp(-1, options);
return oind;
}
#if 0
static int get_optindl(struct option *opt, sl_option_t *options){
int oind;
sl_option_t *opts = options;
assert(opts);
for(oind = 0; opts->name; ){
DBG("cmp '%s' and '%s'", opt->name, opts->name);
if(0 == strcmp(opt->name, opts->name)) break;
oind++, opts++;
}
if(!opts->name || strcmp(opt->name, opts->name)) // no such parameter
sl_showhelp(-1, options);
return oind;
}
#endif
/**
* @brief get_aptr - reallocate new value in array of multiple repeating arguments
@ -136,7 +154,7 @@ static int get_optind(int opt, myoption *options){
* @arg type - its type (for realloc)
* @return pointer to new (next) value
*/
void *get_aptr(void *paptr, argtype type){
void *get_aptr(void *paptr, sl_argtype_e type){
int i = 1;
void **aptr = *((void***)paptr);
if(aptr){ // there's something in array
@ -181,7 +199,7 @@ void *get_aptr(void *paptr, argtype type){
/**
* @brief parseargs - parse command line arguments
* @brief sl_parseargs - parse command line arguments
* ! If arg is string, then value will be strdup'ed!
*
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt`
@ -192,11 +210,11 @@ void *get_aptr(void *paptr, argtype type){
*
* @exit: in case of error this function show help & make `exit(-1)`
*/
void parseargs(int *argc, char ***argv, myoption *options){
void sl_parseargs(int *argc, char ***argv, sl_option_t *options){
char *short_options, *soptr;
struct option *long_options, *loptr;
size_t optsize, i;
myoption *opts = options;
sl_option_t *opts = options;
// check whether there is at least one options
assert(opts);
assert(opts[0].name);
@ -263,31 +281,45 @@ void parseargs(int *argc, char ***argv, myoption *options){
// now we have both long_options & short_options and can parse `getopt_long`
while(1){
int opt;
int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[]
if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break;
if(opt == '?'){
opt = optopt;
optind = get_optind(opt, options);
int /*oindex = -1,*/ optind = -1; // oindex - number of option in long_options, optind - number in options[]
if((opt = getopt_long(*argc, *argv, short_options, long_options, &optind)) == -1) break;
DBG("%c(%d) = getopt_long(argc, argv, %s, long_options, &%d); optopt=%c(%d), errno=%d", opt, opt, short_options, optind, optopt, optopt, errno);
if(optind < 0 ) optind = get_optind(opt, options); // find short option -> need to know index of long
// be careful with "-?" flag: all wrong or ambiguous flags will be interpreted as this!
/*
if(opt == '?'){ // real '?', wrong option or no argument when need
opt = optopt; // original short opt or 0 for wrong or long
if(opt == 0){ // '?' or wrong long
// there's no way to understand difference between real '-?' and unknown long flag
DBG("'?' or unknown parameter");
sl_showhelp(-1, options);
}else optind = get_optind(opt, options);
DBG("optind = %d", optind);
if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR)
showhelp(optind, options); // need argument
}
else{
if(opt == 0 || oindex > 0) optind = oindex;
sl_showhelp(optind, options); // need argument
}else{
// we should call functions get_optind / get_optindl for options where there's no long analog
if(opt < ' ') optind = get_optindl(&long_options[oindex], options);
else optind = get_optind(opt, options);
}
if(optind < 0) sl_showhelp(-1, options); // wrong argument
*/
DBG("index=%d", optind);
opts = &options[optind];
// if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag
DBG("Got option %s", opts->name);
// now check option
if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR)
if(!optarg) showhelp(optind, options); // need argument
if(!optarg) sl_showhelp(optind, options); // need argument
void *aptr;
if(opts->has_arg == MULT_PAR){
aptr = get_aptr(opts->argptr, opts->type);
}else
aptr = opts->argptr;
bool result = TRUE;
int result = TRUE;
// even if there is no argument, but argptr != NULL, think that optarg = "1"
if(!optarg) optarg = "1";
const char *type = NULL;
switch(opts->type){
default:
case arg_none:
@ -295,25 +327,31 @@ void parseargs(int *argc, char ***argv, myoption *options){
break;
case arg_int:
result = myatoll(aptr, optarg, arg_int);
type = _("integer");
break;
case arg_longlong:
result = myatoll(aptr, optarg, arg_longlong);
type = _("long long");
break;
case arg_double:
result = myatod(aptr, optarg, arg_double);
type = _("double");
break;
case arg_float:
result = myatod(aptr, optarg, arg_float);
type = _("float");
break;
case arg_string:
result = (*((void**)aptr) = (void*)strdup(optarg));
result = (*((void**)aptr) = (void*)strdup(optarg)) != NULL;
type = _("string");
break;
case arg_function:
result = ((argfn)aptr)(optarg);
result = ((sl_argfn_t)aptr)(optarg);
break;
}
if(!result){
showhelp(optind, options);
if(type) fprintf(stderr, _("Need argument with %s type\n"), type);
sl_showhelp(optind, options);
}
}
*argc -= optind;
@ -325,7 +363,7 @@ void parseargs(int *argc, char ***argv, myoption *options){
* first - sort by short options; second - sort arguments without sort opts (by long options)
*/
static int argsort(const void *a1, const void *a2){
const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2;
const sl_option_t *o1 = (sl_option_t*)a1, *o2 = (sl_option_t*)a2;
const char *l1 = o1->name, *l2 = o2->name;
int s1 = o1->val, s2 = o2->val;
int *f1 = o1->flag, *f2 = o2->flag;
@ -341,17 +379,17 @@ static int argsort(const void *a1, const void *a2){
}
/**
* @brief showhelp - show help information based on myoption->help values
* @param oindex (i) - if non-negative, show only help by myoption[oindex].help
* @param options (i) - array of `myoption`
* @brief sl_showhelp - show help information based on sl_option_t->help values
* @param oindex (i) - if non-negative, show only help by sl_option_t[oindex].help
* @param options (i) - array of `sl_option_t`
*
* @exit: run `exit(-1)` !!!
*/
void showhelp(int oindex, myoption *options){
void sl_showhelp(int oindex, sl_option_t *options){
int max_opt_len = 0; // max len of options substring - for right indentation
const int bufsz = 255;
char buf[bufsz+1];
myoption *opts = options;
sl_option_t *opts = options;
assert(opts);
assert(opts[0].name); // check whether there is at least one options
if(oindex > -1){ // print only one message
@ -383,7 +421,7 @@ void showhelp(int oindex, myoption *options){
if(N == 0) exit(-2);
// Now print all help (sorted)
opts = options;
qsort(opts, N, sizeof(myoption), argsort);
qsort(opts, N, sizeof(sl_option_t), argsort);
do{
int p = sprintf(buf, " "); // a little indent
if(!opts->flag && opts->val) // .val is short argument
@ -402,13 +440,13 @@ void showhelp(int oindex, myoption *options){
}
/**
* @brief get_suboption - get suboptions from parameter string
* @brief sl_get_suboption - get suboptions from parameter string
* @param str - parameter string
* @param opt - pointer to suboptions structure
* @return TRUE if all OK
*/
bool get_suboption(char *str, mysuboption *opt){
int findsubopt(char *par, mysuboption *so){
int sl_get_suboption(char *str, sl_suboption_t *opt){
int findsubopt(char *par, sl_suboption_t *so){
int idx = 0;
if(!par) return -1;
while(so[idx].name){
@ -417,9 +455,9 @@ bool get_suboption(char *str, mysuboption *opt){
}
return -1; // badarg
}
bool opt_setarg(mysuboption *so, int idx, char *val){
mysuboption *soptr = &so[idx];
bool result = FALSE;
int opt_setarg(sl_suboption_t *so, int idx, char *val){
sl_suboption_t *soptr = &so[idx];
int result = FALSE;
void *aptr = soptr->argptr;
switch(soptr->type){
default:
@ -440,16 +478,16 @@ bool get_suboption(char *str, mysuboption *opt){
result = myatod(aptr, val, arg_float);
break;
case arg_string:
result = (*((void**)aptr) = (void*)strdup(val));
result = (*((void**)aptr) = (void*)strdup(val)) != NULL;
break;
case arg_function:
result = ((argfn)aptr)(val);
result = ((sl_argfn_t)aptr)(val);
break;
}
return result;
}
char *tok;
bool ret = FALSE;
int ret = FALSE;
char *tmpbuf;
tok = strtok_r(str, ":,", &tmpbuf);
do{

50
term.c
View File

@ -1,11 +1,10 @@
/*
* client.c - simple terminal client
* This file is part of the Snippets project.
* Copyright 2013 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -14,10 +13,9 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h> // tcsetattr, close, read, write
#include <sys/ioctl.h> // ioctl
#include <stdio.h> // printf, getchar, fopen, perror
@ -39,7 +37,7 @@ typedef struct {
tcflag_t bspeed; // baudrate from termios.h
} spdtbl;
static int tty_init(TTY_descr *descr);
static int tty_init(sl_tty_t *descr);
static spdtbl speeds[] = {
{50, B50},
@ -76,11 +74,11 @@ static spdtbl speeds[] = {
};
/**
* @brief conv_spd - test if `speed` is in .speed of `speeds` array
* @brief sl_tty_convspd - test if `speed` is in .speed of `speeds` array
* @param speed - integer speed (bps)
* @return 0 if error, Bxxx if all OK
*/
tcflag_t conv_spd(int speed){
tcflag_t sl_tty_convspd(int speed){
spdtbl *spd = speeds;
int curspeed = 0;
do{
@ -98,7 +96,7 @@ tcflag_t conv_spd(int speed){
* @param descr (io) - port descriptor
* @return 0 if all OK or error code
*/
static int tty_init(TTY_descr *descr){
static int tty_init(sl_tty_t *descr){
// |O_NONBLOCK ?
if ((descr->comfd = open(descr->portname, O_RDWR|O_NOCTTY)) < 0){
/// 钆 拖钦 捎邢特谙琢载 邢以 %s
@ -134,9 +132,9 @@ static int tty_init(TTY_descr *descr){
/**
* @brief restore_tty - restore opened TTY to previous state and close it
*/
void close_tty(TTY_descr **descr){
void sl_tty_close(sl_tty_t **descr){
if(descr == NULL || *descr == NULL) return;
TTY_descr *d = *descr;
sl_tty_t *d = *descr;
if(d->comfd){
ioctl(d->comfd, TCSANOW, &d->oldtty); // return TTY to previous state
close(d->comfd);
@ -147,16 +145,16 @@ void close_tty(TTY_descr **descr){
}
/**
* @brief new_tty - create new TTY structure with partially filled fields
* @brief sl_tty_new - create new TTY structure with partially filled fields
* @param comdev - TTY device filename
* @param speed - speed (number)
* @param bufsz - size of buffer for input data (or 0 if opened only to write)
* @return pointer to TTY structure if all OK
*/
TTY_descr *new_tty(char *comdev, int speed, size_t bufsz){
tcflag_t spd = conv_spd(speed);
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){
tcflag_t spd = sl_tty_convspd(speed);
if(!spd) return NULL;
TTY_descr *descr = MALLOC(TTY_descr, 1);
sl_tty_t *descr = MALLOC(sl_tty_t, 1);
descr->portname = strdup(comdev);
descr->baudrate = spd;
descr->speed = speed;
@ -176,12 +174,12 @@ TTY_descr *new_tty(char *comdev, int speed, size_t bufsz){
}
/**
* @brief tty_open - init & open tty device
* @brief sl_tty_open - init & open tty device
* @param d - already filled structure (with new_tty or by hands)
* @param exclusive - == 1 to make exclusive open
* @return pointer to TTY structure if all OK
*/
TTY_descr *tty_open(TTY_descr *d, int exclusive){
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive){
if(!d || !d->portname || !d->baudrate) return NULL;
if(exclusive) d->exclusive = TRUE;
else d->exclusive = FALSE;
@ -191,11 +189,11 @@ TTY_descr *tty_open(TTY_descr *d, int exclusive){
static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000};
/**
* @brief tty_timeout - set timeout for select() on reading
* @brief sl_tty_tmout - set timeout for select() on reading
* @param usec - microseconds of timeout
* @return -1 if usec < 0, 0 if all OK
*/
int tty_timeout(double usec){
int sl_tty_tmout(double usec){
if(usec < 0.) return -1;
tvdefault.tv_sec = 0;
if(usec > 999999){
@ -207,12 +205,12 @@ int tty_timeout(double usec){
}
/**
* @brief read_tty - read data from TTY with 10ms timeout
* @brief sl_tty_read - read data from TTY with 10ms timeout
* @param buff (o) - buffer for data read
* @param length - buffer len
* @return amount of bytes read or -1 if disconnected
*/
int read_tty(TTY_descr *d){
int sl_tty_read(sl_tty_t *d){
if(!d || d->comfd < 0) return 0;
size_t L = 0;
ssize_t l;
@ -246,12 +244,12 @@ int read_tty(TTY_descr *d){
}
/**
* @brief write_tty - write data to serial port
* @brief sl_tty_write - write data to serial port
* @param buff (i) - data to write
* @param length - its length
* @return 0 if all OK
*/
int write_tty(int comfd, const char *buff, size_t length){
int sl_tty_write(int comfd, const char *buff, size_t length){
ssize_t L = write(comfd, buff, length);
if((size_t)L != length){
/// "镗陕肆 诹猩由!"

264
term2.c Normal file
View File

@ -0,0 +1,264 @@
/*
* This file is part of the Snippets project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h> // tcsetattr, close, read, write
#include <fcntl.h> // read
#include <stdio.h> // printf, getchar, fopen, perror
#include <stdlib.h> // exit, realloc
#include <string.h> // memcpy
#include <sys/ioctl.h> // ioctl
#include <sys/stat.h> // read
#include <sys/time.h> // gettimeofday
#include <time.h> // time
#include <unistd.h> // usleep, tcsetattr, close, read, write
#include "usefull_macros.h"
#define LOGBUFSZ (1024)
// return FALSE if failed
static int parse_format(const char *iformat, tcflag_t *flags){
tcflag_t f = 0;
if(!iformat){ // default
if(flags) *flags = CS8;
return TRUE;
}
if(strlen(iformat) != 3) goto someerr;
switch(iformat[0]){
case '5':
f |= CS5;
break;
case '6':
f |= CS6;
break;
case '7':
f |= CS7;
break;
case '8':
f |= CS8;
break;
default:
goto someerr;
}
switch(iformat[1]){
case '0': // always 0
f |= PARENB | CMSPAR;
break;
case '1': // always 1
f |= PARENB | CMSPAR | PARODD;
break;
case 'E': // even
f |= PARENB;
break;
case 'N': // none
break;
case 'O': // odd
f |= PARENB | PARODD;
break;
default:
goto someerr;
}
switch(iformat[2]){
case '1':
break;
case '2':
f |= CSTOPB;
break;
default:
goto someerr;
}
if(flags) *flags = f;
return TRUE;
someerr:
WARNX(_("Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: 1/2"), iformat);
return FALSE;
}
/**
* @brief tty_init - open & setup terminal
* @param descr (io) - port descriptor
* !!! default settings are "8N1".
* !!! If you want other, set it like `descr->format = "7O2"` between `sl_tty_new` and `sl_tty_open`
* @return 0 if all OK, last error code or 1
*/
static int tty_init(sl_tty_t *descr){
tcflag_t flags;
if(!parse_format(descr->format, &flags)) return 1;
if((descr->comfd = open(descr->portname, O_RDWR|O_NOCTTY)) < 0){
/// îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s
WARN(_("Can't use port %s"), descr->portname);
return globErr ? globErr : 1;
}
if(ioctl(descr->comfd, TCGETS2, &descr->oldtty)){ // Get settings
/// îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ
WARN(_("Can't get old TTY settings"));
return globErr ? globErr : 1;
}
descr->tty = descr->oldtty;
descr->tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
descr->tty.c_iflag = 0; // don't do any changes in input stream
descr->tty.c_oflag = 0; // don't do any changes in output stream
descr->tty.c_cflag = BOTHER | flags | CREAD | CLOCAL; // other speed, user format, RW, ignore line ctrl
descr->tty.c_ispeed = descr->speed;
descr->tty.c_ospeed = descr->speed;
descr->tty.c_cc[VMIN] = 0; // non-canonical mode
descr->tty.c_cc[VTIME] = 5;
if(ioctl(descr->comfd, TCSETS2, &descr->tty)){
/// îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ
WARN(_("Can't apply new TTY settings"));
return globErr ? globErr : 1;
}
ioctl(descr->comfd, TCGETS2, &descr->tty);
if(descr->tty.c_ispeed != (speed_t)descr->speed || descr->tty.c_ospeed != (speed_t)descr->speed){
WARN(_("Can't set speed %d, got ispeed=%d, ospeed=%d"), descr->speed, descr->tty.c_ispeed, descr->tty.c_ospeed);
descr->speed = descr->tty.c_ispeed;
}
// make exclusive open
if(descr->exclusive){
if(ioctl(descr->comfd, TIOCEXCL)){
/// îÅ ÍÏÇÕ ÓÄÅÌÁÔØ ÐÏÒÔ ÜËÓËÌÀÚÉ×ÎÙÍ
WARN(_("Can't do exclusive open"));
}}
return 0;
}
/**
* @brief restore_tty - restore opened TTY to previous state and close it
*/
void sl_tty_close(sl_tty_t **descr){
if(descr == NULL || *descr == NULL) return;
sl_tty_t *d = *descr;
if(d->comfd){
ioctl(d->comfd, TCSETS2, &d->oldtty); // return TTY to previous state
close(d->comfd);
}
FREE(d->portname);
FREE(d->buf);
FREE(*descr);
}
/**
* @brief sl_tty_new - create new TTY structure with partially filled fields
* @param comdev - TTY device filename
* @param speed - speed (number)
* @param bufsz - size of buffer for input data (or 0 if opened only to write)
* @return pointer to TTY structure if all OK
*/
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz){
sl_tty_t *descr = MALLOC(sl_tty_t, 1);
descr->portname = strdup(comdev);
descr->speed = speed;
if(!descr->portname){
/// ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ
WARNX(_("Port name is missing"));
}else{
if(bufsz){
descr->buf = MALLOC(char, bufsz+1);
descr->bufsz = bufsz;
return descr;
}else WARNX(_("Need non-zero buffer for TTY device"));
}
FREE(descr->portname);
FREE(descr);
return NULL;
}
/**
* @brief sl_tty_open - init & open tty device
* @param d - already filled structure (with new_tty or by hands)
* @param exclusive - == 1 to make exclusive open
* @return pointer to TTY structure if all OK
*/
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive){
if(!d || !d->portname) return NULL;
if(exclusive) d->exclusive = TRUE;
else d->exclusive = FALSE;
if(tty_init(d)) return NULL;
return d;
}
static struct timeval tvdefault = {.tv_sec = 0, .tv_usec = 5000};
/**
* @brief sl_tty_tmout - set timeout for select() on reading
* @param usec - microseconds of timeout
* @return -1 if usec < 0, 0 if all OK
*/
int sl_tty_tmout(double usec){
if(usec < 0.) return -1;
tvdefault.tv_sec = 0;
if(usec > 999999){
tvdefault.tv_sec = (__time_t)(usec / 1e6);
usec -= tvdefault.tv_sec * 1e6;
}
tvdefault.tv_usec = (__suseconds_t) usec;
return 0;
}
/**
* @brief sl_tty_read - read data from TTY with 10ms timeout
* @param buff (o) - buffer for data read
* @param length - buffer len
* @return amount of bytes read or -1 if disconnected
*/
int sl_tty_read(sl_tty_t *d){
if(!d || d->comfd < 0) return 0;
size_t L = 0;
ssize_t l;
size_t length = d->bufsz;
char *ptr = d->buf;
fd_set rfds;
struct timeval tv;
int retval;
do{
l = 0;
FD_ZERO(&rfds);
FD_SET(d->comfd, &rfds);
//memcpy(&tv, &tvdefault, sizeof(struct timeval));
tv = tvdefault;
retval = select(d->comfd + 1, &rfds, NULL, NULL, &tv);
if(!retval) break;
if(retval < 0){
if(errno == EINTR) continue;
return -1;
}
if(FD_ISSET(d->comfd, &rfds)){
l = read(d->comfd, ptr, length);
if(l < 1) return -1; // disconnected
ptr += l; L += l;
length -= l;
}
}while(l && length);
d->buflen = L;
d->buf[L] = 0;
return (size_t)L;
}
/**
* @brief sl_tty_write - write data to serial port
* @param buff (i) - data to write
* @param length - its length
* @return 0 if all OK
*/
int sl_tty_write(int comfd, const char *buff, size_t length){
ssize_t L = write(comfd, buff, length);
if((size_t)L != length){
/// "ïÛÉÂËÁ ÚÁÐÉÓÉ!"
WARN("Write error");
return 1;
}
return 0;
}

View File

@ -18,23 +18,25 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h> // isspace
#include <err.h>
#include <locale.h>
#include <stdlib.h>
#include <sys/file.h> // flock
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/limits.h> // PATH_MAX
#include <locale.h>
#include <math.h> // floor
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h> // flock
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "usefull_macros.h"
@ -56,10 +58,10 @@ const char *sl_libversion(){
}
/**
* @brief dtime - function for different purposes that need to know time intervals
* @brief sl_dtime - function for different purposes that need to know time intervals
* @return double value: UNIX time in seconds
*/
double dtime(){
double sl_dtime(){
double t;
struct timeval tv;
gettimeofday(&tv, NULL);
@ -77,7 +79,7 @@ int globErr = 0; // errno for WARN/ERR
* @param fmt ... - printf-like format
* @return number of printed symbols
*/
int r_pr_(const char *fmt, ...){
static int r_pr_(const char *fmt, ...){
va_list ar; int i;
printf(COLOR_RED);
va_start(ar, fmt);
@ -86,7 +88,7 @@ int r_pr_(const char *fmt, ...){
printf(COLOR_OLD);
return i;
}
int g_pr_(const char *fmt, ...){
static int g_pr_(const char *fmt, ...){
va_list ar; int i;
printf(COLOR_GREEN);
va_start(ar, fmt);
@ -101,7 +103,7 @@ int g_pr_(const char *fmt, ...){
* @param fmt ... - printf-like format
* @return number of printed symbols
*/
int r_WARN(const char *fmt, ...){
static int r_WARN(const char *fmt, ...){
va_list ar; int i = 1;
fprintf(stderr, COLOR_RED);
va_start(ar, fmt);
@ -128,7 +130,7 @@ static const char stars[] = "****************************************";
* @param fmt ... - printf-like format
* @return number of printed symbols
*/
int s_WARN(const char *fmt, ...){
static int s_WARN(const char *fmt, ...){
va_list ar; int i;
i = fprintf(stderr, "\n%s\n", stars);
va_start(ar, fmt);
@ -143,7 +145,7 @@ int s_WARN(const char *fmt, ...){
i += fprintf(stderr, "\n");
return i;
}
int r_pr_notty(const char *fmt, ...){
static int r_pr_notty(const char *fmt, ...){
va_list ar; int i;
i = printf("\n%s\n", stars);
va_start(ar, fmt);
@ -154,10 +156,10 @@ int r_pr_notty(const char *fmt, ...){
}
/**
* @brief initial_setup - setup locale & console
* @brief sl_init - setup locale & console
* Run this function in the beginning of main() to setup locale & coloured output
*/
void initial_setup(){
void sl_init(){
// setup coloured output
if(isatty(STDOUT_FILENO)){ // make color output in tty
red = r_pr_; green = g_pr_;
@ -180,10 +182,10 @@ void initial_setup(){
\******************************************************************************/
/**
* @brief throw_random_seed - Generate a quasy-random number to initialize PRNG
* @brief sl_random_seed - Generate a quasy-random number to initialize PRNG
* @return value for srand48
*/
long throw_random_seed(){
long sl_random_seed(){
long r_ini;
int fail = 0;
int fd = open("/dev/random", O_RDONLY);
@ -201,7 +203,7 @@ long throw_random_seed(){
close(fd);
}while(0);
if(fail){
double tt = dtime() * 1e6;
double tt = sl_dtime() * 1e6;
double mx = (double)LONG_MAX;
r_ini = (long)(tt - mx * floor(tt/mx));
}
@ -209,10 +211,10 @@ long throw_random_seed(){
}
/**
* @brief get_available_mem
* @brief sl_mem_avail
* @return system available physical memory
*/
uint64_t get_available_mem(){
uint64_t sl_mem_avail(){
return sysconf(_SC_AVPHYS_PAGES) * (uint64_t) sysconf(_SC_PAGE_SIZE);
}
@ -222,12 +224,12 @@ uint64_t get_available_mem(){
* Memory
\******************************************************************************/
/**
* @brief my_alloc - safe memory allocation for macro ALLOC
* @brief sl_alloc - safe memory allocation for macro ALLOC
* @param N - number of elements to allocate
* @param S - size of single element (typically sizeof)
* @return pointer to allocated memory area
*/
void *my_alloc(size_t N, size_t S){
void *sl_alloc(size_t N, size_t S){
void *p = calloc(N, S);
if(!p) ERR("malloc");
//assert(p);
@ -235,11 +237,11 @@ void *my_alloc(size_t N, size_t S){
}
/**
* @brief My_mmap - mmap file to a memory area
* @brief sl_mmap - mmap file to a memory area
* @param filename (i) - name of file to mmap
* @return stuct with mmap'ed file or die
*/
mmapbuf *My_mmap(char *filename){
sl_mmapbuf_t *sl_mmap(char *filename){
int fd;
char *ptr;
size_t Mlen;
@ -269,17 +271,17 @@ mmapbuf *My_mmap(char *filename){
}
/// îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ
if(close(fd)) WARN(_("Can't close mmap'ed file"));
mmapbuf *ret = MALLOC(mmapbuf, 1);
sl_mmapbuf_t *ret = MALLOC(sl_mmapbuf_t, 1);
ret->data = ptr;
ret->len = Mlen;
return ret;
}
/**
* @brief My_munmap - unmap memory file
* @brief sl_munmap - unmap memory file
* @param b (i) - mmap'ed buffer
*/
void My_munmap(mmapbuf *b){
void sl_munmap(sl_mmapbuf_t *b){
if(munmap(b->data, b->len)){
/// îÅ ÍÏÇÕ munmap
ERR(_("Can't munmap"));
@ -287,44 +289,86 @@ void My_munmap(mmapbuf *b){
FREE(b);
}
/**
* @brief sl_omitspaces - omit leading spaces
* @param v - source string
* @return pointer to first non-blank character (could be '\0' if end of string reached)
*/
char *sl_omitspaces(const char *v){
if(!v) return NULL;
char *p = (char*)v;
while(*p && isspace(*p)) ++p;
return p;
}
// the same as sl_omitspaces, but return (last non-space char + 1) in string or its first char
char *sl_omitspacesr(const char *v){
if(!v) return NULL;
char *eol = (char*)v + strlen(v) - 1;
while(eol > v && isspace(*eol)) --eol;
if(eol == v && isspace(*eol)) return eol;
return eol + 1;
}
/******************************************************************************\
* Terminal in no-echo mode
* BE CAREFULL! These functions aren't thread-safe!
\******************************************************************************/
static struct termios oldt, newt; // console flags
// console flags
#ifndef SL_USE_OLD_TTY
static struct termios2 oldt, newt;
#else
static struct termios oldt, newt;
#endif
static int console_changed = 0;
/**
* @brief restore_console - restore console to default mode
* @brief sl_restore_con - restore console to default mode
*/
void restore_console(){
void sl_restore_con(){
if(console_changed)
#ifndef SL_USE_OLD_TTY
ioctl(STDIN_FILENO, TCSETS2, &oldt);
#else
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
#endif
console_changed = 0;
}
/**
* @brief setup_con - setup console to non-canonical noecho mode
* @brief sl_setup_con - setup console to non-canonical noecho mode
*/
void setup_con(){
void sl_setup_con(){
if(console_changed) return;
#ifndef SL_USE_OLD_TTY
ioctl(STDIN_FILENO, TCGETS2, &oldt);
#else
tcgetattr(STDIN_FILENO, &oldt);
#endif
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
#ifndef SL_USE_OLD_TTY
if(ioctl(STDIN_FILENO, TCSETS2, &newt)){
#else
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
#endif
/// îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ
WARN(_("Can't setup console"));
#ifndef SL_USE_OLD_TTY
ioctl(STDIN_FILENO, TCSETS2, &oldt);
#else
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
#endif
signals(1); //quit?
}
console_changed = 1;
}
/**
* @brief read_console - read character from console without echo
* @brief sl_read_con - read character from console without echo
* @return char read or zero
*/
int read_console(){
int sl_read_con(){
int rb;
struct timeval tv;
int retval;
@ -346,102 +390,58 @@ int read_console(){
* wait until at least one character pressed
* @return character read
*/
int mygetchar(){
int sl_getchar(){
int ret;
do ret = read_console();
do ret = sl_read_con();
while(ret == 0);
return ret;
}
/**
* @brief str2double - safely convert data from string to double
* @brief sl_str2d - safely convert data from string to double
* @param num (o) - double number read from string
* @param str (i) - input string
* @return 1 if success, 0 if fails
*/
int str2double(double *num, const char *str){
int sl_str2d(double *num, const char *str){
double res;
char *endptr;
if(!str) return 0;
if(!str) return FALSE;
res = strtod(str, &endptr);
if(endptr == str || *str == '\0' || *endptr != '\0'){
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ double!"
WARNX("Wrong double number format!");
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ double '%s'"
WARNX(_("Wrong double number format '%s'"), str);
return FALSE;
}
if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number
return TRUE;
}
/******************************************************************************\
* Logging to file
* DEPRECATED!!! DEPRECATED!!! DEPRECATED!!! DEPRECATED!!! DEPRECATED!!! DEPRECATED!!!
\******************************************************************************/
FILE *Flog = NULL; // log file descriptor
char *logname = NULL;
time_t log_open_time = 0;
/**
* @brief openlogfile - try to open log file
* @param name (i) - log file name
* if failed show warning message
*/
void openlogfile(char *name){
if(!name){
/// îÅ ÚÁÄÁÎÏ ÉÍÑ ÌÏÇÆÁÊÌÁ
WARNX(_("Need filename for log file"));
return;
// and so on
int sl_str2ll(long long *num, const char *str){
long long res;
char *endptr;
if(!str) return FALSE;
res = strtoll(str, &endptr, 0);
if(endptr == str || *str == '\0' || *endptr != '\0'){
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ double!"
WARNX(_("Wrong number format!"));
return FALSE;
}
/// ðÒÏÂÕÀ ÏÔËÒÙÔØ ÌÏÇÆÁÊÌ %s × ÒÅÖÉÍÅ ÄÏÐÏÌÎÅÎÉÑ\n
green(_("Try to open log file %s in append mode\n"), name);
fflush(stdout);
if(!(Flog = fopen(name, "a"))){
/// îÅ ÍÏÇÕ ÏÔËÒÙÔØ ÌÏÇÆÁÊÌ
WARN(_("Can't open log file"));
return;
}
log_open_time = time(NULL);
logname = name;
}
/**
* Save message to log file, rotate logs every 24 hours
*/
int putlog(const char *fmt, ...){
if(!Flog) return 0;
time_t t_now = time(NULL);
if(t_now - log_open_time > 86400){ // rotate log
fprintf(Flog, "\n\t\t%sRotate log\n", ctime(&t_now));
fclose(Flog);
char newname[PATH_MAX];
snprintf(newname, PATH_MAX, "%s.old", logname);
if(rename(logname, newname)) WARN("rename()");
openlogfile(logname);
if(!Flog) return 0;
}
//int i = fprintf(Flog, "%s\t\t", ctime(&t_now));
char buf[256];
strftime(buf, 255, "%Y/%m/%d %H:%M:%S", localtime(&t_now));
int i = fprintf(Flog, "%s\t\t", buf);
va_list ar;
va_start(ar, fmt);
i = vfprintf(Flog, fmt, ar);
va_end(ar);
fprintf(Flog, "\n");
fflush(Flog);
return i;
if(num) *num = res;
return TRUE;
}
/******************************************************************************\
* Logging to file
\******************************************************************************/
sl_log *globlog = NULL; // "global" log file (the first opened logfile)
sl_log_t *sl_globlog = NULL; // "global" log file (the first opened logfile)
/**
* @brief sl_createlog - create log file, test file open ability
* @param logpath - path to log file
* @param level - lowest message level (e.g. LOGLEVEL_ERR won't allow to write warn/msg/dbg)
* @return allocated structure (should be free'd later by Cl_deletelog) or NULL
*/
sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix){
sl_log_t *sl_createlog(const char *logpath, sl_loglevel_e level, int prefix){
if(level < LOGLEVEL_NONE || level > LOGLEVEL_ANY) return NULL;
if(!logpath) return NULL;
FILE *logfd = fopen(logpath, "a");
@ -450,7 +450,7 @@ sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix){
return NULL;
}
fclose(logfd);
sl_log *log = MALLOC(sl_log, 1);
sl_log_t *log = MALLOC(sl_log_t, 1);
log->logpath = strdup(logpath);
if(!log->logpath){
WARN("strdup()");
@ -462,7 +462,7 @@ sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix){
return log;
}
void sl_deletelog(sl_log **log){
void sl_deletelog(sl_log_t **log){
if(!log || !*log) return;
FREE((*log)->logpath);
FREE(*log);
@ -476,7 +476,7 @@ void sl_deletelog(sl_log **log){
* @param fmt - format and the rest part of message
* @return amount of symbols saved in file
*/
int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...){
int sl_putlogt(int timest, sl_log_t *log, sl_loglevel_e lvl, const char *fmt, ...){
if(!log || !log->logpath) return 0;
if(lvl > log->loglevel) return 0;
int i = 0;
@ -484,9 +484,9 @@ int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...){
if(!logfd) return 0;
int lfd = fileno(logfd);
// try to lock file
double t0 = dtime();
double t0 = sl_dtime();
int locked = 0;
while(dtime() - t0 < 0.1){ // timeout for 0.1s
while(sl_dtime() - t0 < 0.1){ // timeout for 0.1s
if(-1 == flock(lfd, LOCK_EX | LOCK_NB)) continue;
locked = 1;
break;

View File

@ -20,14 +20,17 @@
*/
#pragma once
#ifndef __USEFULL_MACROS_H__
#define __USEFULL_MACROS_H__
#include <stdbool.h> // bool
#include <unistd.h> // pid_t
#include <errno.h> // errno
#ifdef SL_USE_OLD_TTY
#include <termios.h> // termios
#else
#include <asm-generic/termbits.h>
#endif
#include <errno.h> // errno
#include <stdlib.h> // alloc, free
#include <sys/types.h> // pid_t
#include <unistd.h> // pid_t
// just for different purposes
#include <limits.h>
#include <stdint.h>
@ -103,8 +106,8 @@ void WEAK signals(int sig);
/*
* Memory allocation
*/
#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type)))
#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type)))
#define ALLOC(type, var, size) type * var = ((type *)sl_alloc(size, sizeof(type)))
#define MALLOC(type, size) ((type *)sl_alloc(size, sizeof(type)))
#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0)
#ifndef DBL_EPSILON
@ -115,38 +118,48 @@ void WEAK signals(int sig);
const char *sl_libversion();
// double value of UNIX time
double dtime();
double sl_dtime();
// functions for color output in tty & no-color in pipes
extern int (*red)(const char *fmt, ...);
extern int (*_WARN)(const char *fmt, ...);
extern int (*green)(const char *fmt, ...);
// safe allocation
void * my_alloc(size_t N, size_t S);
void *sl_alloc(size_t N, size_t S);
// setup locales & other
void initial_setup();
void sl_init();
// mmap file
typedef struct{
char *data;
size_t len;
} mmapbuf;
mmapbuf *My_mmap(char *filename);
void My_munmap(mmapbuf *b);
} sl_mmapbuf_t;
sl_mmapbuf_t *sl_mmap(char *filename);
void sl_munmap(sl_mmapbuf_t *b);
// console in non-echo mode
void restore_console();
void setup_con();
int read_console();
int mygetchar();
void sl_restore_con();
void sl_setup_con();
int sl_read_con();
int sl_getchar();
long throw_random_seed();
long sl_random_seed();
uint64_t get_available_mem();
uint64_t sl_mem_avail();
// omit leading spaces
char *sl_omitspaces(const char *str);
// omit trailing spaces
char *sl_omitspacesr(const char *v);
// convert string to double with checking
int sl_str2d(double *num, const char *str);
int sl_str2ll(long long *num, const char *str);
/******************************************************************************\
The original term.h
\******************************************************************************/
#ifdef SL_USE_OLD_TTY
typedef struct {
char *portname; // device filename (should be freed before structure freeing)
int speed; // baudrate in human-readable format
@ -157,23 +170,31 @@ typedef struct {
char *buf; // buffer for data read
size_t bufsz; // size of buf
size_t buflen; // length of data read into buf
bool exclusive; // should device be exclusive opened
} TTY_descr;
int exclusive; // should device be exclusive opened
} sl_tty_t;
void close_tty(TTY_descr **descr);
TTY_descr *new_tty(char *comdev, int speed, size_t bufsz);
TTY_descr *tty_open(TTY_descr *d, int exclusive);
int read_tty(TTY_descr *descr);
int tty_timeout(double usec);
int write_tty(int comfd, const char *buff, size_t length);
tcflag_t conv_spd(int speed);
tcflag_t sl_tty_convspd(int speed);
#else
typedef struct {
char *portname; // device filename (should be freed before structure freeing)
int speed; // baudrate in human-readable format
char *format; // format like 8N1
struct termios2 oldtty; // TTY flags for previous port settings
struct termios2 tty; // TTY flags for current settings
int comfd; // TTY file descriptor
char *buf; // buffer for data read
size_t bufsz; // size of buf
size_t buflen; // length of data read into buf
int exclusive; // should device be exclusive opened
} sl_tty_t;
#endif
// convert string to double with checking
int str2double(double *num, const char *str);
// logging (deprecated)
void __attribute__ ((deprecated)) openlogfile(char *name);
int __attribute__ ((deprecated)) putlog(const char *fmt, ...);
void sl_tty_close(sl_tty_t **descr);
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz);
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive);
int sl_tty_read(sl_tty_t *descr);
int sl_tty_tmout(double usec);
int sl_tty_write(int comfd, const char *buff, size_t length);
/******************************************************************************\
Logging
@ -184,31 +205,32 @@ typedef enum{
LOGLEVEL_WARN, // only warnings and errors
LOGLEVEL_MSG, // all without debug
LOGLEVEL_DBG, // all messages
LOGLEVEL_ANY // all shit
} sl_loglevel;
LOGLEVEL_ANY, // all shit
LOGLEVEL_AMOUNT // total amount
} sl_loglevel_e;
typedef struct{
char *logpath; // full path to logfile
sl_loglevel loglevel; // loglevel
sl_loglevel_e loglevel; // loglevel
int addprefix; // if !=0 add record type to each line(e.g. [ERR])
} sl_log;
} sl_log_t;
extern sl_log *globlog; // "global" log file
extern sl_log_t *sl_globlog; // "global" log file
sl_log *sl_createlog(const char *logpath, sl_loglevel level, int prefix);
void sl_deletelog(sl_log **log);
int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...);
sl_log_t *sl_createlog(const char *logpath, sl_loglevel_e level, int prefix);
void sl_deletelog(sl_log_t **log);
int sl_putlogt(int timest, sl_log_t *log, sl_loglevel_e lvl, const char *fmt, ...);
// open "global" log
#define OPENLOG(nm, lvl, prefix) (globlog = sl_createlog(nm, lvl, prefix))
#define OPENLOG(nm, lvl, prefix) (sl_globlog = sl_createlog(nm, lvl, prefix))
// shortcuts for different log levels; ..ADD - add message without timestamp
#define LOGERR(...) do{sl_putlogt(1, globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0)
#define LOGERRADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0)
#define LOGWARN(...) do{sl_putlogt(1, globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0)
#define LOGWARNADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0)
#define LOGMSG(...) do{sl_putlogt(1, globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0)
#define LOGMSGADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0)
#define LOGDBG(...) do{sl_putlogt(1, globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0)
#define LOGDBGADD(...) do{sl_putlogt(0, globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0)
#define LOGERR(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0)
#define LOGERRADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_ERR, __VA_ARGS__);}while(0)
#define LOGWARN(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0)
#define LOGWARNADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_WARN, __VA_ARGS__);}while(0)
#define LOGMSG(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0)
#define LOGMSGADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_MSG, __VA_ARGS__);}while(0)
#define LOGDBG(...) do{sl_putlogt(1, sl_globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0)
#define LOGDBGADD(...) do{sl_putlogt(0, sl_globlog, LOGLEVEL_DBG, __VA_ARGS__);}while(0)
/******************************************************************************\
The original parseargs.h
@ -217,7 +239,7 @@ int sl_putlogt(int timest, sl_log *log, sl_loglevel lvl, const char *fmt, ...);
#define APTR(x) ((void*)x)
// if argptr is a function:
typedef bool(*argfn)(void *arg);
typedef int(*sl_argfn_t)(void *arg);
/**
* type of getopt's argument
@ -238,8 +260,8 @@ typedef enum {
arg_double, // double
arg_float, // float
arg_string, // char *
arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)`
} argtype;
arg_function // parse_args will run function `int (*fn)(char *optarg, int N)`
} sl_argtype_e;
/**
* Structure for getopt_long & help
@ -265,30 +287,30 @@ typedef enum{
NEED_ARG = 1,
OPT_ARG = 2,
MULT_PAR
} hasarg;
} sl_hasarg_e;
typedef struct{
// these are from struct option:
const char *name; // long option's name
hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array)
sl_hasarg_e has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 3 - need arg & key can repeat (args are stored in null-terminated array)
int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0)
int val; // short opt name (if flag == NULL) or flag's value
// and these are mine:
argtype type; // type of argument
void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)`
sl_argtype_e type; // type of argument
void *argptr; // pointer to variable to assign optarg value or function `int (*fn)(char *optarg, int N)`
const char *help; // help string which would be shown in function `showhelp` or NULL
} myoption;
} sl_option_t;
/*
* Suboptions structure, almost the same like myoption
* Suboptions structure, almost the same like sl_option_t
* used in parse_subopts()
*/
typedef struct{
const char *name;
hasarg has_arg;
argtype type;
void *argptr;
} mysuboption;
const char *name;
sl_hasarg_e has_arg;
sl_argtype_e type;
void *argptr;
} sl_suboption_t;
// last string of array (all zeros)
#define end_option {0,0,0,0,0,0,0}
@ -296,14 +318,14 @@ typedef struct{
extern const char *__progname;
void showhelp(int oindex, myoption *options);
void parseargs(int *argc, char ***argv, myoption *options);
void sl_showhelp(int oindex, sl_option_t *options);
void sl_parseargs(int *argc, char ***argv, sl_option_t *options);
/**
* @brief change_helpstring - change standard help header
* @brief sl_helpstring - change standard help header
* @param str (i) - new format (MAY consist ONE "%s" for progname)
*/
void change_helpstring(char *s);
bool get_suboption(char *str, mysuboption *opt);
void sl_helpstring(char *s);
int sl_get_suboption(char *str, sl_suboption_t *opt);
/******************************************************************************\
@ -314,24 +336,44 @@ bool get_suboption(char *str, mysuboption *opt);
#endif
// default function to run if another process found
void WEAK iffound_default(pid_t pid);
void WEAK sl_iffound_deflt(pid_t pid);
// check that our process is exclusive
void check4running(char *selfname, char *pidfilename);
void sl_check4running(char *selfname, char *pidfilename);
// read name of process by its PID
char *readPSname(pid_t pid);
char *sl_getPSname(pid_t pid);
/******************************************************************************\
The original fifo_lifo.h
\******************************************************************************/
typedef struct buff_node{
typedef struct sl_buff_node{
void *data;
struct buff_node *next, *last;
} List;
struct sl_buff_node *next, *last;
} sl_list_t;
List *list_push_tail(List **lst, void *v);
List *list_push(List **lst, void *v);
void *list_pop(List **lst);
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v);
sl_list_t *sl_list_push(sl_list_t **lst, void *v);
void *sl_list_pop(sl_list_t **lst);
/******************************************************************************\
The original config.h
\******************************************************************************/
// max length of key (including '\0')
#define SL_KEY_LEN (32)
// max length of value (including '\0')
#define SL_VAL_LEN (128)
#endif // __USEFULL_MACROS_H__
// starting symbol of any comment
#define SL_COMMENT_CHAR '#'
// option or simple configuration value (don't work for functions)
typedef struct{
union{
int ival; long long llval; double dval; float fval;
};
sl_argtype_e type;
} sl_optval;
int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]);
char *sl_print_opts(sl_option_t *opt, int showall);
int sl_conf_readopts(const char *filename, sl_option_t *options);