Compare commits

...

4 Commits

21 changed files with 1163 additions and 598 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.9)
cmake_minimum_required(VERSION 4.0)
set(PROJ usefull_macros)
set(MINOR_VERSION "2")
set(MINOR_VERSION "4")
set(MID_VERSION "3")
set(MAJOR_VERSION "0")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
@ -110,7 +110,7 @@ add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\"
-DMAJOR_VERSION=\"${MAJOR_VESION}\")
# -l
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES})
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} -lm)
set(PCFILE "${CMAKE_BINARY_DIR}/${PROJ}.pc")
configure_file("${PROJ}.pc.in" ${PCFILE} @ONLY)
@ -154,7 +154,7 @@ if(NOT DEFINED NOGETTEXT)
# we need this to prevent ru.po & .mo from deleting by make clean
add_custom_target(
RU_FILE
COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE}
COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -UiF ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE}
DEPENDS ${PO_FILE} ${SOURCES}
)
add_custom_target(

View File

@ -1,10 +1,22 @@
Wed Dec 23 17:22:39 MSK 2020
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)...
VERSION 0.1.1:
add tty_timeout()
fix read_tty() for disconnect
add logging
add sl_libversion()
Wed Sep 10 14:19:24 MSK 2025
(still version 0.3.3: I forgot to add changelog last commits)
- fixed minor bugs and memory leaks
- use common way for parsing commandline arguments and content of configuration files, so now you can use arrays in configurations (several parameters with same name)
- add functions:
- - int sl_remove_quotes(char *string) - remove outern quotes (" and '), return amount of pairs found
- - void sl_conf_showhelp(int idx, sl_option_t *options) - show help for config file parameters (only long options, without '--' and without exit(1) at the end of function)
- - void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*)) - now this funtion available for external using, parsing of arguments with user help function
Dec 16 2024
VERSION 0.2.1:
RENAME:
@ -54,3 +66,11 @@ 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
Wed Dec 23 17:22:39 MSK 2020
VERSION 0.1.1:
add tty_timeout()
fix read_tty() for disconnect
add logging
add sl_libversion()

37
Readme.rus Normal file
View File

@ -0,0 +1,37 @@
<a href=https://github.com/eddyem/snippets_library>Репозиторий с кодом</a>.
Сначала избавился от нескольких багов. Потом решил добавить сетевой функционал. Ну, а коль сеть — то и кольцевой буфер нужен. В общем, теперь там — не просто макросы и функционал, который в 99% случаев нужен (тот же разбор параметров командной строки), но и дополнительные облегчалки.
<lj-cut text="Подробней">
В релизе 0.2.1 код подвергся рефакторингу, многие функции переименованы, чтобы было единообразие: почти все функции и т.п. имеют префикс <tt>sl_</tt>; типы-перечисления имеют суффикс <tt>_e</tt>, типы данных — суффикс <tt>_t</tt>. Без префикса я оставил лишь функции <tt>red</tt> и <tt>green</tt> для цветного вывода сообщений. И многие макросы лень было переименовывать.
<p><b>"Старое":</b></p>
Для использования функционала в самом начале <tt>main()</tt> нужно выполнить функцию <tt>sl_init</tt>. Для работы с терминалом ввода в режиме "без эха" — <tt>sl_setup_con()</tt> (и не забыть в конце и в функции <tt>signals()</tt> вызвать <tt>sl_restore_con()</tt>, иначе по завершению придется ручками вводить <tt>reset</tt>). Функции <tt>sl_read_con()</tt> и <tt>sl_getchar()</tt> дают, соответственно, неблокирующее и блокирующее чтение символа, введенного пользователем.
Осталось и упрощение работы с <tt>mmap</tt>: <tt>sl_mmap()</tt> и <tt>sl_munmap()</tt>.
<tt>sl_random_seed()</tt> позволяет при помощи <tt>/dev/random</tt> сгенерировать случайную затравку для использования в ГСЧ. <tt>sl_libversion()</tt> позволяет получить строку с текущей версией библиотеки.
Для работы с gettext теперь нужно определить макрос <tt>GETTEXT</tt>, в этом случае макрос <tt>_()</tt> будет вызывать <tt>gettext()</tt>.
Функции работы с блочными устройствами не просто переименовались: по умолчанию я перешел на termios2. Если нужно собрать библиотеку с поддержкой "классической" termios со всеми ее убогими косяками, нужно определить макрос <tt>SL_USE_OLD_TTY</tt> (в этом случае еще и будет доступна функция <tt>sl_tty_convspd()</tt> для преобразования скорости интерфейса из числа в макрос). А остальные функции — как и были: <tt>sl_tty_new()</tt> для выделения памяти и инициализации структур; <tt>sl_tty_open()</tt> — открыть порт; <tt>sl_tty_read()</tt> и <tt>sl_tty_write()</tt> для чтения/записи; <tt>sl_tty_tmout()</tt> позволяет задать таймаут для <tt>select()</tt> в микросекундах. <tt>sl_tty_close()</tt> — закрыть устройство и очистить структуры данных.
Логгирование имеет "умолчальный" глобальный лог (<tt>sl_globlog</tt>), для которого нарисованы макросы вроде <tt>LOGERR()</tt>, <tt>LOGERRADD()</tt> и т.п. (все с суффиксом <tt>ADD</tt> добавляют текст без записи timestamp и уровня сообщения — если нужно вывести несколько строк текста; но лучше ими не пользоваться часто, особенно в многопоточных/многопроцессных приложениях). Чтобы логи писались вменяемо в многопоточных/многопроцессных, пришлось лог-файл каждый раз для записи открывать, блокировать, потом писать, закрывать и разблокировать. Чтобы избежать дедлоков или слишком долгих ожиданий, установлен "гвоздями прибитый" таймаут в 0.1с: если за это время заблокировать файл не получится, то отменяем запись и возвращаем пользователю 0; иначе возвращаем количество записанных символов. "Глобальный" лог-файл активируется при помощи макроса <tt>OPENLOG(nm, lvl, prefix)</tt> (имя, уровень, флаг добавления "префикса": строчки с расшифровкой уровня сообщения, например, [WARN] или [ERR]). Функции <tt>sl_createlog()</tt>, <tt>sl_deletelog()</tt> и <tt>sl_putlogt()</tt> позволяют активировать структуру-лог (и создать файл, если надо), удалить структуру (сам файл не удаляется) и что-нибудь туда записать/дописать.
В работе с аргументами командной строки я исправил один баг, связанный с выводом справки по "только длинным" аргументам, у которых в качестве "короткой альтернативы" проставлены одинаковые числа. Ну и была проблема с опцией "-?", которая криво обрабатывалась (т.к. я пользуюсь <tt>getopt_long</tt>). В остальном все осталось, как было: поддерживаются целочисленные аргументы (причем, arg_none означает, что к заданной переменной будет добавляться 1 каждый раз, как встречается этот флаг — удобно для задания уровня отображения как, например, <tt>-vvvv</tt>), long long, double, float, string и "аргумент-функция" (т.е. вместо присвоения значения будет выполняться заданная функция — например, чтобы парсить свои вложенные аргументы). Все также для каждого флага можно указать, что он не имеет аргументов, имеет обязательный, необязательный или множественный (в этом случае переменной-целью должен быть указатель соответствующего типа: при разборе получится NULL-terminated массив со всеми введенными пользователем данными (удобно, например, когда тебе нужно указать несколько файлов для считывания из них однообразной информации и т.п.). Поддерживаются "подопции" (когда за флагом следует строка вида "so1=par1;so2=par2..."). <tt>sl_showhelp()</tt> позволяет вызвать справку по всем опциям или только одной. Она же вызывается автоматом, если пользователь что-то не то введет. Чтобы поменять форматную строку справки, используется <tt>sl_helpstring()</tt> (там должен быть один-единственный "%s", куда вставится имя запускаемого файла). <tt>sl_parseargs()</tt> нужно запускать сразу после инициализации, и дальше уже пользоваться своей структурой с параметрами.
Для проверки, не запущен ли второй экземпляр, есть функция <tt>sl_check4running()</tt>. Если найден процесс, запускается слабая функция <tt>sl_iffound_deflt()</tt>, которую под свои нужды можно изменить. <tt>sl_getPSname()</tt> парсит <tt>/proc</tt> и выдает имя процесса с заданным PID.
FIFO/LIFO также остались без изменений: <tt>sl_list_push()</tt> и <tt>sl_list_push_tail()</tt> позволяют воткнуть запись в начало или хвост списка, а <tt>sl_list_pop()</tt> — извлечь последнюю запись.
<p><b>"Новое":</b></p>
<tt>sl_mem_avail()</tt> позволяет определить доступный объем ОЗУ. <tt>sl_omitspaces()</tt> и <tt>sl_omitspacesr()</tt> — удалить начальные и конечные пробелы. Помимо <tt>sl_str2d()</tt> ("безопасное" преобразование строки в double) я добавил <tt>sl_str2ll()</tt> и <tt>sl_str2i()</tt> — long long и int, соответственно.
Для проверки, можно ли в данный дескриптор писать или читать из него без блокировки, добавил функции <tt>sl_canread()</tt> и <tt>sl_canwrite()</tt>.
Поддержку конфигурационных файлов тоже добавил в библиотеку. В файле все данные должны быть в формате "параметр = значение" или просто "параметр". Допускается наличие пустых строк и комментариев (все, что за символом "#" в строке). "Гвоздями" прибил максимальную длину параметра (<tt>SL_KEY_LEN</tt>, 32 байта) и значения (<tt>SL_VAL_LEN</tt>, 128 байт). Работа с флагами похожа на работу с аргументами командной строки. <tt>sl_get_keyval()</tt> чистит лишние пробелы и комментарии, выделяя непосредственно строчку "параметр [= значение]" и помещая ее в отдельные аргументы (возвращает 0 если ничего в строке не найдено, 1 — если только параметр, 2 — если и параметр, и значение); эта функция используется и в обработке запросов по сети. <tt>sl_print_opts()</tt> печатает изменяемые параметры (или все, если задан флаг) с комментариями. <tt>sl_conf_readopts()</tt> читает конфигурационный файл. Опции задаются полностью аналогично опциям командной строки, что позволяет печатать сопровождающий текст помимо флага и его значения.
Для работы с кольцевым буфером есть следующие функции: <tt>sl_RB_new()</tt> и <tt>sl_RB_delete()</tt> — инициализация и удаление данных структуры кольцевого буфера. <tt>sl_RB_hasbyte()</tt> позволяет определить, есть ли в буфере нужный символ (например, конец строки), а <tt>sl_RB_readto()</tt> — считать до него (включительно). <tt>sl_RB_putbyte()</tt>, <tt>sl_RB_write()</tt> и <tt>sl_RB_writestr()</tt> позволяют записать данные в буфер (возвращают количество записанных символов, чтобы юзер мог понять, что что-то не то, если место кончилось). <tt>sl_RB_datalen()</tt> и <tt>sl_RB_freesize()</tt> позволяют узнать, сколько места в буфере занимают данные, и сколько там свободно. Нужно помнить, что размер данных будет на 1 байт меньше длины буфера (по понятным причинам). <tt>sl_RB_clearbuf()</tt> сбрасывает содержимое буфера, как будто бы там ничего нет.
Ну и последняя, самая жирная по количеству функций и структур — сетевая часть.
Для активации соединения есть две функции: <tt>sl_sock_run_client()</tt> и <tt>sl_sock_run_server()</tt> — для клиента и сервера, соответственно. Они инициализируют структуры, открывают соединение и подключаются для клиента или вызывают bind для сервера. Клиенту заодно запускается поток, читающий приходящие данные и складывающий в кольцевой буфер. У сервера, если указать не-NULL аргумент handlers, тоже запускается отдельный поток: он обслуживает присоединенных клиентов, запускает accept для новых, закрывает отключившиеся; полученные от клиентов данные парсятся и, если среди переданной структуры handlers находится подобный ключ, то запускается соответствующий обработчик + есть три стандартных обработчика: <tt>sl_sock_inthandler()</tt>, <tt>sl_sock_dblhandler()</tt> и <tt>sl_sock_strhandler()</tt> — для int64_t, double и char* (для того, чтобы знать, когда было произведено изменение значения, я дополнительные типы данных ввел, где помимо данных есть timestamp). Предельное количество клиентов можно задать функцией <tt>sl_sock_changemaxclients()</tt>, а узнать текущее — <tt>sl_sock_getmaxclients()</tt>. <tt>sl_sock_delete()</tt> закрывает соединение и удаляет структуру.
Помимо этого, есть еще обработчики событий: подключение клиента, отключение и подключение клиента сверх предельного количества. По умолчанию там NULL (т.е. ничего не делается), но можно сменить при помощи, соответственно, функций <tt>sl_sock_connhandler()</tt>, <tt>sl_sock_dischandler()</tt> и <tt>sl_sock_maxclhandler()</tt>. В них можно, например, в логи писать, мол клиент fd=xx ip=yyy подключился/отключился. А в последнем — писать клиенту "подожди, все соединения заняты".
Отправлять данные можно непосредственно при помощи write/send (не забывая сначала заблокировать, а потом разблокировать соответствующий мьютекс), либо же при помощи оберток: <tt>sl_sock_sendbyte()</tt>, <tt>sl_sock_sendbinmessage()</tt> и <tt>sl_sock_sendstrmessage()</tt>. Читать тоже можно или при помощи соответствующих функций работы с кольцевым буфером, или же текстовые строковые сообщения можно считывать посредством <tt>sl_sock_readline()</tt>.
У сервера бывают ситуации, когда одно и то же сообщение нужно отправить сразу всем клиентам. Для этого есть функция <tt>sl_sock_sendall()</tt> (она тупо перебирает все открытые и подключенные дескрипторы, да отправляет туда данные).
<p><b>Примеры</b></p>
<i>clientserver</i> — пример организации простейшего сервера и клиента. 226 строк всего: аргументы командной строки, примеры обработчиков, пример неблокирующего чтения с клавиатуры одновременно с чтением из сокета. Здесь я не парился насчет типа "локальный сетевой": можно открыть либо UNIX-сокет, либо INET. Заодно логи можно писать.
<i>conffile</i> — 101 строка. Дает пример ввода конфигурационных параметров как из командной строки, так и из конфигурационного файла. Также парсит строковый параметр вида "параметр=значение". Отображает состояние флагов после обработки аргументов командной строки, а затем — после считывания из конфигурационного файла. К сожалению, чтобы считать путь к конфигурационному файлу, надо сначала обработать аргументы командной строки, поэтому, если не постараться, а просто на тот же самый массив структур вызывать чтение конф-файла, то приоритет будет за ним. Хотя, логика подсказывает, что приоритет всегда должен быть за командной строкой. Поэтому в случае, когда необходимо именно иметь приоритет командной строки, нужно будет завести 2 раздельных структуры, инициализировать их сначала каким-то дефолтом (с данными, которые пользователь точно не соможет ввести), а после сравнить и все неинициализированное из командной строки инициализировать записями из конф-файла.
<i>fifo</i> — 61 строка. Пример работы с FIFO/LIFO. Показывает реальный размер свободной ОЗУ и затем все параметры командной строки помещает в списки: LIFO и FIFO. А затем из каждого списка по одному извлекает, демонстрируя, как это работает.
<i>helloworld</i> — 35 строк (из которых только 10 приходится на собственно код). Демонстрирует цветной вывод и ввод без эха.
<i>options</i> — 134 строки. Демонстрирует работу с разным типом опций (кроме функции: я и сам уже забыл, зачем оно мне нужно было), в т.ч. массивами. Ну и по умолчанию демонстрирует простейший терминал для работы с символьными устройствами (если пользователь введет путь).
<i>ringbuffer</i> — 80 строк, демонстрирующих работу кольцевого буфера. По умолчанию он имеет размер 1024 байт (т.е. вмещает 1023 символа). Дальше пользователю предлагается вводить построчно текст, который будет помещаться в буфер. Ввод заканчивается на переполнении буфера или же при нажатии ctrl+D. После чего весь буфер вычитывается и выводится на консоль.
</lj-cut>
Теперь как минимум все, что пользуется этой библиотекой, надо обновить. Ну, а сетевые демоны роботелескопов вообще радикально так переделать: чтобы "шапкой" с метеоданными не демон монтировки занимался (что вообще нелогично), а демон погоды; чтобы демоны купола, монтировки и телескопа "слушались" демона погоды. Скажем, если стоит prohibited=1, то прекращать наблюдения, когда они идут, или не давать наблюдателю открыться. Вот на БТА такую функцию АСУшник выполняет (правда, некоторые из рук вон плохо работают, позволяя наблюдателям-вредителям открывать телескоп при влажности свыше 90%, облачности или даже тумане). А на Ц-1000 почему-то до сих пор нет такого "супердемона", который бы "давал по шапке" наблюдателям-вредителям. Иначе угробят телескоп, а за это даже никакого наказания не понесут (я бы отстранял наблюдателей минимум на полгода от наблюдений, если они открывают телескоп тогда, когда этого делать ни в коем случае нельзя).

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

View File

@ -122,11 +122,12 @@ static void runclient(sl_sock_t *s){
static sl_sock_int_t iflag = {0};
static sl_sock_double_t dflag = {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){
char buf[32];
snprintf(buf, 31, "UNIXT=%.2f\n", sl_dtime());
sl_sock_sendall((uint8_t*)buf, strlen(buf));
sl_sock_sendall(s, (uint8_t*)buf, strlen(buf));
return RESULT_SILENCE;
}
@ -158,16 +159,52 @@ static void toomuch(int fd){
DBG("Disc after %gs", sl_dtime() - t0);
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
}
// new connections handler
static void connected(sl_sock_t *c){
// new connections handler (return FALSE to reject client)
static int connected(sl_sock_t *c){
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
return TRUE;
}
// disconnected handler
static void disconnected(sl_sock_t *c){
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
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){
if(!s || !str) return RESULT_FAIL;
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
sl_sock_sendstrmessage(s, str);
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
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[] = {
{sl_sock_inthandler, "int", "set/get integer flag", (void*)&iflag},
@ -175,6 +212,7 @@ static sl_sock_hitem_t handlers[] = {
{sl_sock_strhandler, "str", "set/get string variable", (void*)&sflag},
{show, "show", "show current flags @ server console", 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}
};
@ -185,16 +223,20 @@ int main(int argc, char **argv){
if(!G.node) ERRX("Point node");
sl_socktype_e type = (G.isunix) ? SOCKT_UNIX : SOCKT_NET;
if(G.isserver){
sl_sock_changemaxclients(G.maxclients);
sl_sock_maxclhandler(toomuch);
sl_sock_connhandler(connected);
sl_sock_dischandler(disconnected);
//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);
} else {
sl_setup_con();
s = sl_sock_run_client(type, G.node, -1);
}
if(!s) ERRX("Can't create socket and/or run threads");
if(G.isserver){
sl_sock_changemaxclients(s, G.maxclients);
sl_sock_maxclhandler(s, toomuch);
sl_sock_connhandler(s, connected);
sl_sock_dischandler(s, disconnected);
sl_sock_defmsghandler(s, defhandler);
}
sl_loglevel_e lvl = G.verbose + LOGLEVEL_ERR;
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
if(G.logfile) OPENLOG(G.logfile, lvl, 1);

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@
int main(int argc, char *argv[argc]) {
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
sl_init();
// specific output in non-tty
if(argc == 1){
green("Usage:\n\t%s args - fill fifo with arguments\n", __progname);
@ -55,7 +55,6 @@ int main(int argc, char *argv[argc]) {
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()
}
return 0;
}

View File

@ -67,12 +67,6 @@ int main(int argc, char *argv[]){
}
sl_check4running((char*)__progname, GP->pidfile);
red("%s started, snippets library version is %s\n", __progname, sl_libversion());
sl_setup_con();
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
if(GP->logfile) OPENLOG(GP->logfile, LOGLEVEL_ANY, 1);
LOGMSG("Start application...");
if(GP->rest_pars_num){
@ -89,11 +83,22 @@ int main(int argc, char *argv[]){
}
if(GP->strarr){
char **p = GP->strarr;
for(int i = 0; *p; ++i) printf("String[%d]: \"%s\"\n", i, *p++);
for(int i = 0; *p; ++i){
sl_remove_quotes(*p);
printf("String[%d]: \"%s\"\n", i, *p++);
}
}
if(GP->lo0 != INT_MIN) printf("You set lo0 to %d\n", GP->lo0);
if(GP->lo1 != INT_MIN) printf("You set lo1 to %d\n", GP->lo1);
if(GP->lo2 != INT_MIN) printf("You set lo2 to %d\n", GP->lo2);
if(GP->so1){
sl_remove_quotes(GP->so1);
printf("String so1=%s\n", GP->so1);
}
if(GP->so2){
sl_remove_quotes(GP->so2);
printf("String so2=%s\n", GP->so2);
}
if(GP->device){
LOGDBG("Try to open serial %s", GP->device);
dev = sl_tty_new(GP->device, GP->speed, 4096);
@ -103,7 +108,13 @@ int main(int argc, char *argv[]){
signals(0);
}
}
if(!dev) return 0;
sl_setup_con();
signal(SIGTERM, signals); // kill (-15) - quit
signal(SIGHUP, SIG_IGN); // hup - ignore
signal(SIGINT, signals); // ctrl+C - quit
signal(SIGQUIT, signals); // ctrl+\ - quit
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
// main stuff goes here
long seed = sl_random_seed();
green("Now I will sleep for 10 seconds after your last input.\n Do whatever you want. Random seed: %ld\n", seed);

View File

@ -24,7 +24,7 @@
/**
* @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
* @param v (i) - data to push (DON'T FREE it after this function as it would be just a link to original!)
* @return pointer to just pused node or NULL in case of error
*/
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v){

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-12-16 11:25+0300\n"
"POT-Creation-Date: 2025-11-06 15:03+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,33 +17,23 @@ msgstr ""
"Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226
msgid "Unsupported option type"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:91
#, c-format
msgid "Wrong number format '%s'"
msgid "sl_get_keyval(): key would be trunkated to %d symbols"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:245
#, c-format
msgid "Can't open %s"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284
#, c-format
msgid "Wrong key: '%s'"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:316
msgid "sl_conf_showhelp(): wrong index"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322
#, c-format
msgid "Key '%s' need value"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296
#, c-format
msgid "Key '%s' have no argptr!"
msgid "Configuration file options (format: key=value):\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
@ -71,7 +61,19 @@ msgstr ""
msgid "Integer out of range"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166
#. `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked
#. not found
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:129
#, c-format
msgid "No such parameter: `%s`\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:138
#, c-format
msgid "Parameter `%s` needs value\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:163
msgid "Can't use multiple args with arg_none!"
msgstr ""
@ -85,58 +87,69 @@ msgstr ""
msgid "double short arguments: -%c"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:343
#, c-format
msgid "Need argument with %s type\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486
#. print only one message
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:435
msgid "sl_showhelp(): option index out of range"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:441
#, c-format
msgid "Usage: %s [arguments]\n"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:528
#, c-format
msgid "Wrong parameter: %s"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:532
#, c-format
msgid "%s: need argument!"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:536
#, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:148
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147
msgid "Server disconnected"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:223
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:434
msgid "Can't start server handlers thread"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:285
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503
msgid "Limit of connections reached"
msgstr ""
#. check for RB overflow
#. -1 - buffer empty (can't be), -2 - buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:325
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:547
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:548
#, c-format
msgid "Server thread: ring buffer overflow for fd=%d"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:339
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:562
#, c-format
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
msgstr ""
#. buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:351
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:574
#, c-format
msgid "Server thread: buffer overflow from fd=%d"
msgstr ""
#. never reached
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:416
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:664
#, c-format
msgid "Unsupported socket type %d"
msgstr ""
@ -214,18 +227,3 @@ msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347
msgid "Can't setup console"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403
#, c-format
msgid "Wrong double number format '%s'"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428
#, c-format
msgid "Wrong integer number format '%s'"
msgstr ""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432
msgid "Number out of integer limits"
msgstr ""

View File

@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-12-13 09:10+0300\n"
"POT-Creation-Date: 2025-11-06 14:57+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,6 +16,25 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:91
#, c-format
msgid "sl_get_keyval(): key would be trunkated to %d symbols"
msgstr "sl_get_keyval(): ËÌÀÞ ÂÕÄÅÔ ÏÂÒÅÚÁÎ ÄÏ %d ÓÉÍ×ÏÌÏ×"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:245
#, c-format
msgid "Can't open %s"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:316
msgid "sl_conf_showhelp(): wrong index"
msgstr "sl_conf_showhelp(): ÎÅ×ÅÒÎÙÊ ÉÎÄÅËÓ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:322
#, c-format
msgid "Configuration file options (format: key=value):\n"
msgstr "ïÐÃÉÉ ËÏÎÆÉÇÕÒÁÃÉÏÎÎÏÇÏ ÆÁÊÌÁ (ÆÏÒÍÁÔ: ËÌÀÞ=ÚÎÁÞÅÎÉÅ):\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69
#, c-format
msgid "\n"
@ -23,152 +42,114 @@ msgid "\n"
msgstr "\n"
"ïÂÎÁÒÕÖÅÎ ÏÄÎÏÉÍÅÎÎÙÊ ÐÒÏÃÅÓÓ (pid=%d), ×ÙÈÏÄ.\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:490
#, c-format
msgid "%s: need argument!"
msgstr "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119
msgid "Can't apply new TTY settings"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266
msgid "Can't close mmap'ed file"
msgstr "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
msgid "Can't do exclusive open"
msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ÐÏÒÔ × ÜËÓËÌÀÚÉ×ÎÙÊ ÒÅÖÉÍ"
#. Get settings
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106
msgid "Can't get old TTY settings"
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279
msgid "Can't munmap"
msgstr "îÅ ÍÏÇÕ munmap"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:273
#, c-format
msgid "Can't open %s"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252
#, c-format
msgid "Can't open %s for reading"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
msgid "Can't open /dev/random"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ /dev/random"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141
msgid "Can't open PID file"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ PID ÆÁÊÌ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198
msgid "Can't read /dev/random"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ /dev/random"
#. error reading self name
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:117
msgid "Can't read self name"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ ÉÍÑ ÐÒÏÃÅÓÓÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
#, c-format
msgid "Can't set speed %d, got ispeed=%d, ospeed=%d"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÓËÏÒÏÓÔØ ÎÁ %d; ispeed=%d, ospeed=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:141
msgid "Can't open PID file"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ PID ÆÁÊÌ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347
msgid "Can't setup console"
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:223
msgid "Can't start server handlers thread"
msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#, c-format
msgid "Can't stat %s"
msgstr "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:166
msgid "Can't use multiple args with arg_none!"
msgstr "íÁÓÓÉ× ÐÁÒÁÍÅÔÒÏ× ÎÅ ÍÏÖÅÔ ÉÍÅÔØ ÔÉÐ arg_none!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102
#, c-format
msgid "Can't use port %s"
msgstr "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s"
#. amount of pcount and/or scount wrong
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53
msgid "Wrong helpstring!"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:82
msgid "Integer out of range"
msgstr "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:296
#. `opt` should be ':' for "missed arguments", '?' for "not found" and short flag if found and checked
#. not found
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:129
#, c-format
msgid "Key '%s' have no argptr!"
msgstr "õ ËÌÀÞÁ '%s' ÎÅÔ ÕËÁÚÁÔÅÌÑ ÎÁ ÐÅÒÅÍÅÎÎÕÀ!"
msgid "No such parameter: `%s`\n"
msgstr "îÅÔ ÔÁËÏÇÏ ÐÁÒÁÍÅÔÒÁ: %s\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:290
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:138
#, c-format
msgid "Key '%s' need value"
msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ"
msgid "Parameter `%s` needs value\n"
msgstr "ëÌÀÞ '%s' ÔÒÅÂÕÅÔ ÚÎÁÞÅÎÉÑ\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:285
msgid "Limit of connections reached"
msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:163
msgid "Can't use multiple args with arg_none!"
msgstr "íÁÓÓÉ× ÐÁÒÁÍÅÔÒÏ× ÎÅ ÍÏÖÅÔ ÉÍÅÔØ ÔÉÐ arg_none!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262
msgid "Mmap error for input"
msgstr "ïÛÉÂËÁ mmap"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267
#, c-format
msgid "double long arguments: --%s"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ÄÌÉÎÎÙÊ ÐÁÒÁÍÅÔÒ: --%s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:332
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273
#, c-format
msgid "double short arguments: -%c"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:343
#, c-format
msgid "Need argument with %s type\n"
msgstr "îÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ Ó ÔÉÐÏÍ %s\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168
msgid "Need non-zero buffer for TTY device"
msgstr "äÌÑ ÐÏÓÌÅÄÏ×ÁÔÅÌØÎÏÇÏ ÕÓÔÒÏÊÓÔ×Á ÔÒÅÂÕÅÔÓÑ ÂÕÆÅÒ ÎÅÎÕÌÅ×ÏÇÏ ÒÁÚÍÅÒÁ"
#. print only one message
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:435
msgid "sl_showhelp(): option index out of range"
msgstr "sl_showhelp(): ÉÎÄÅËÓ ÏÐÃÉÉ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248
msgid "No filename given!"
msgstr "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:441
#, c-format
msgid "Usage: %s [arguments]\n"
msgstr "éÓÐÏÌØÚÏ×ÁÎÉÅ: %s [ÁÒÇÕÍÅÎÔÙ]\n"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:432
msgid "Number out of integer limits"
msgstr "þÉÓÌÏ ÚÁ ÐÒÅÄÅÌÁÍÉ int"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:528
#, c-format
msgid "Wrong parameter: %s"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
msgid "Port name is missing"
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:532
#, c-format
msgid "%s: need argument!"
msgstr "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:148
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:536
#, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:147
msgid "Server disconnected"
msgstr "óÅÒ×ÅÒ ÏÔËÌÀÞÅÎ"
#. buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:351
msgid "Server thread: buffer overflow from fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:434
msgid "Can't start server handlers thread"
msgstr "îÅ ÍÏÇÕ ÚÁÐÕÓÔÉÔØ ÐÏÔÏË-ÏÂÒÁÂÏÔÞÉË ÓÅÒ×ÅÒÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:339
msgid "Server thread: can't write data to ringbuffer, overflow from fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:503
msgid "Limit of connections reached"
msgstr "äÏÓÔÉÇÎÕÔ ÐÒÅÄÅÌ ÓÏÅÄÉÎÅÎÉÊ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:325
#. check for RB overflow
#. -1 - buffer empty (can't be), -2 - buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:547
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:548
#, c-format
msgid "Server thread: ring buffer overflow for fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:226
msgid "Unsupported option type"
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÏÐÃÉÉ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:562
#, c-format
msgid "Server thread: can't write data to ringbuffer: overflow from fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÎÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÄÁÎÎÙÅ × ËÏÌØÃÅ×ÏÍ ÂÕÆÅÒÅ, "
"ÐÅÒÅÐÏÌÎÅÎÉÅ ÏÔ fd=%d"
#. buffer overflow
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:574
#, c-format
msgid "Server thread: buffer overflow from fd=%d"
msgstr "ðÏÔÏË ÓÅÒ×ÅÒÁ: ÐÅÒÅÐÏÌÎÅÎÉÅ ÂÕÆÅÒÁ ÏÔ fd=%d"
#. never reached
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:416
#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:664
#, c-format
msgid "Unsupported socket type %d"
msgstr "îÅÐÏÄÄÅÒÖÉ×ÁÅÍÙÊ ÔÉÐ ÓÏËÅÔÁ %d"
@ -180,48 +161,71 @@ msgid "Wrong USART format \"%s\"; use NPS, where N: 5..8; P: N/E/O/1/0, S: "
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ USART \"%s\"; ÎÕÖÅÎ NPS, ÇÄÅ N: 5..8; P: N/E/O/"
"1/0, S: 1/2"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:494
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:102
#, c-format
msgid "Wrong argument \"%s\" of parameter \"%s\""
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
msgid "Can't use port %s"
msgstr "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÏÒÔ %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:403
#. Get settings
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:106
msgid "Can't get old TTY settings"
msgstr "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÄÅÊÓÔ×ÕÀÝÉÅ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:119
msgid "Can't apply new TTY settings"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÎÁÓÔÒÏÊËÉ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:124
#, c-format
msgid "Wrong double number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ double: %s"
msgid "Can't set speed %d, got ispeed=%d, ospeed=%d"
msgstr "îÅ ÍÏÇÕ ÓÍÅÎÉÔØ ÓËÏÒÏÓÔØ ÎÁ %d; ispeed=%d, ospeed=%d"
#. amount of pcount and/or scount wrong
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:53
msgid "Wrong helpstring!"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:130
msgid "Can't do exclusive open"
msgstr "îÅ ÍÏÇÕ ÐÅÒÅ×ÅÓÔÉ ÐÏÒÔ × ÜËÓËÌÀÚÉ×ÎÙÊ ÒÅÖÉÍ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:416
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:428
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:162
msgid "Port name is missing"
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/term2.c:168
msgid "Need non-zero buffer for TTY device"
msgstr "äÌÑ ÐÏÓÌÅÄÏ×ÁÔÅÌØÎÏÇÏ ÕÓÔÒÏÊÓÔ×Á ÔÒÅÂÕÅÔÓÑ ÂÕÆÅÒ ÎÅÎÕÌÅ×ÏÇÏ ÒÁÚÍÅÒÁ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:194
msgid "Can't open /dev/random"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ /dev/random"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:198
msgid "Can't read /dev/random"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ /dev/random"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:248
msgid "No filename given!"
msgstr "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:252
#, c-format
msgid "Wrong integer number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÃÅÌÏÇÏ: %s"
msgid "Can't open %s for reading"
msgstr "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:284
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:256
#, c-format
msgid "Wrong key: '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ËÌÀÞ: %s"
msgid "Can't stat %s"
msgstr "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/config.c:229
#, c-format
msgid "Wrong number format '%s'"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:262
msgid "Mmap error for input"
msgstr "ïÛÉÂËÁ mmap"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:486
#, c-format
msgid "Wrong parameter: %s"
msgstr "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:266
msgid "Can't close mmap'ed file"
msgstr "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:267
#, c-format
msgid "double long arguments: --%s"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ÄÌÉÎÎÙÊ ÐÁÒÁÍÅÔÒ: --%s"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:279
msgid "Can't munmap"
msgstr "îÅ ÍÏÇÕ munmap"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/parseargs.c:273
#, c-format
msgid "double short arguments: -%c"
msgstr "ÄÕÂÌÉÒÕÀÝÉÊÓÑ ËÏÒÏÔËÉÊ ÐÁÒÁÍÅÔÒ: -%c"
#: /home/eddy/Docs/SAO/C_diff/snippets_library/usefull_macros.c:347
msgid "Can't setup console"
msgstr "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"

View File

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

View File

@ -82,6 +82,9 @@ size_t sl_RB_freesize(sl_ringbuffer_t *b){
static ssize_t hasbyte(sl_ringbuffer_t *b, uint8_t byte){
if(b->head == b->tail) return -1; // no data in buffer
size_t startidx = b->head;
/*DBG("head: %zd, tail: %zd (%c %c %c %c), search %02x",
b->head, b->tail, b->data[startidx], b->data[startidx+1],
b->data[startidx+2], b->data[startidx+3], byte);*/
if(b->head > b->tail){
for(size_t found = b->head; found < b->length; ++found)
if(b->data[found] == byte) return found;
@ -222,6 +225,13 @@ size_t sl_RB_write(sl_ringbuffer_t *b, const uint8_t *str, size_t len){
DBG("rest: %zd, need: %zd", r, len);
if(len > r) len = r;
if(!len) goto ret;
/*green("buf:\n");
for(size_t i = 0; i < len; ++i){
char c = (char)str[i];
if(c > 31) printf("%c", c);
else printf("\\x%02x", c);
}
green("(end)\n");*/
size_t _1st = b->length - b->tail;
if(_1st > len) _1st = len;
memcpy(b->data + b->tail, str, _1st);

411
socket.c
View File

@ -17,6 +17,8 @@
*/
#include <arpa/inet.h>
#include <ctype.h>
#include <inttypes.h>
#include <netdb.h>
#include <poll.h>
#include <stdio.h>
@ -24,42 +26,39 @@
#include <strings.h>
#include <sys/ioctl.h>
#include <sys/un.h> // unix socket
#include <inttypes.h>
#include <unistd.h>
#include "usefull_macros.h"
// max clients amount
static int maxclients = 32;
// too much clients handler; it is running for client connected with number>maxclients (before closing its fd)
static sl_sock_maxclh_t toomuch_handler = NULL;
// new client connected handler; it will be run each new connection
static sl_sock_connh_t newconnect_handler = NULL;
// client disconnected handler
static sl_sock_disch_t disconnect_handler = NULL;
/**
* @brief sl_sock_changemaxclients - change amount of max simultaneously connected clients
* SHOULD BE run BEFORE running of server
* @param val - maximal clients number
*/
void sl_sock_changemaxclients(int val){
maxclients = val;
void sl_sock_changemaxclients(sl_sock_t *sock, int val){
if(sock) sock->maxclients = val;
}
int sl_sock_getmaxclients(sl_sock_t *sock){
if(sock) return sock->maxclients;
else return -1;
}
int sl_sock_getmaxclients(){ return maxclients; }
// in each next function changed default handlers you can set h to NULL to remove your handler
// setter of "too much clients" handler
void sl_sock_maxclhandler(sl_sock_maxclh_t h){
toomuch_handler = h;
void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int)){
if(sock) sock->toomuch_handler = h;
}
// setter of "new client connected" handler
void sl_sock_connhandler(sl_sock_connh_t h){
newconnect_handler = h;
void sl_sock_connhandler(sl_sock_t *sock, int(*h)(struct sl_sock*)){
if(sock) sock->newconnect_handler = h;
}
// setter of "client disconnected" handler
void sl_sock_dischandler(sl_sock_disch_t h){
disconnect_handler = h;
void sl_sock_dischandler(sl_sock_t *sock, void(*h)(struct sl_sock*)){
if(sock) sock->disconnect_handler = h;
}
// setter of default client message handler
void sl_sock_defmsghandler(struct sl_sock *sock, sl_sock_hresult_e(*h)(struct sl_sock *s, const char *str)){
if(sock) sock->defmsg_handler = h;
}
// text messages for `hresult`
@ -163,33 +162,174 @@ errex:
return NULL;
}
// common for server thread and `sendall`
static sl_sock_t **clients = NULL;
/**
* @brief sl_sock_sendall - send data to all clients connected (works only for server)
* @param data - message
* @param len - its length
* @return N of sends or -1 if no server process running
*/
int sl_sock_sendall(uint8_t *data, size_t len){
if(!clients) return -1;
int sl_sock_sendall(sl_sock_t *sock, uint8_t *data, size_t len){
FNAME();
if(!sock || !sock->clients) return -1;
int nsent = 0;
for(int i = maxclients; i > 0; --i){
if(!clients[i]) continue;
if(clients[i]->fd < 0 || !clients[i]->connected) continue;
if((ssize_t)len == sl_sock_sendbinmessage(clients[i], data, len)) ++nsent;
for(int i = sock->maxclients; i > 0; --i){
if(!sock->clients[i]) continue;
if(sock->clients[i]->fd < 0 || !sock->clients[i]->connected) continue;
if((ssize_t)len == sl_sock_sendbinmessage(sock->clients[i], data, len)) ++nsent;
}
return nsent;
}
static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str);
// return TRUE if this is header without data (also modify c->sockmethod)
static int iswebheader(sl_sock_t *client, char *str){
DBG("check header: _%s_", str);
const char *methods[] = {
[SOCKM_GET] = "GET",
[SOCKM_PUT] = "PUT",
[SOCKM_POST] = "POST",
[SOCKM_PATCH] = "PATCH",
[SOCKM_DELETE] = "DELETE"
};
const int metlengths[] = {
[SOCKM_GET] = 3,
[SOCKM_PUT] = 3,
[SOCKM_POST] = 4,
[SOCKM_PATCH] = 5,
[SOCKM_DELETE] = 6
};
for(sl_sockmethod_e m = SOCKM_GET; m < SOCKM_AMOUNT; ++m){
if(0 == strncmp(str, methods[m], metlengths[m])){
client->sockmethod = m;
DBG("SOCK method: %s", methods[m]);
if(m == SOCKM_GET){ // modify `str` by GET
char *slash = strchr(str, '/');
if(slash){
char *eol = strstr(slash, "HTTP");
if(eol){ // move to `str` head
size_t l = eol - slash - 2;
memmove(str, slash+1, l);
str[l] = 0;
DBG("User asks GET '%s' -> run parser", str);
//parse_post_data(client, str);
return TRUE;
}
}
}
return TRUE;
}
}
return FALSE;
}
static char *stringscan(char *str, char *needle){
char *a, *end = str + strlen(str);
a = strstr(str, needle);
if(!a) return NULL;
a += strlen(needle);
while (a < end && isspace(*a)) a++;
if(a >= end) return NULL;
DBG("Found a needle \"%s\"", needle);
return a;
}
// just check for header words - if so, return TRUE
static int chkwebreq(sl_sock_t *client, char *str){
if(client->contlen == 0){
char *cl = stringscan(str, "Content-Length:");
if(cl){
DBG("Contlen: _%s_", cl);
sl_str2i(&client->contlen, cl);
return TRUE;
}
}
DBG("retval: %d", !client->gotemptyline);
return !client->gotemptyline;
}
// In-place URL parser
void url_decode(char *str) {
if (!str) return;
char *src = str;
char *dst = str;
char hex[3] = {0};
DBG("STR ori: _%s_", str);
while(*src){
if(*src == '+'){
*dst++ = ' ';
src++;
}else if(*src == '%' && isxdigit((unsigned char)src[1]) && isxdigit((unsigned char)src[2])){
hex[0] = src[1];
hex[1] = src[2];
*dst++ = (char)strtol(hex, NULL, 16);
src += 3;
}else{
*dst++ = *src++;
}
}
*dst = 0;
DBG("STR DECODED to _%s_", str);
}
static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str);
// parser of web-encoded data by POST/GET:
static sl_sock_hresult_e parse_post_data(sl_sock_t *c, char *str){
if (!c || !str) return RESULT_BADKEY;
if(0 == strcmp("favicon.ico", str)){
DBG("icon -> omit");
return RESULT_SILENCE;
}
char *start = str;
char *current = str;
DBG("\n\n\nSTART parser");
while(*current){
if(*current == '&'){
*current = '\0';
DBG("\n\n`start` = _%s_", start);
url_decode(start);
sl_sock_hresult_e r = msgparser(c, start);
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
start = current + 1;
}
current++;
}
if(*start){
url_decode(start);
sl_sock_hresult_e r = msgparser(c, start);
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
}
DBG("\n\n\nEND parser");
return RESULT_SILENCE;
}
// parser of client's message
// "only-server's" fields of `client` are copies of server's
static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
char key[SL_KEY_LEN], val[SL_VAL_LEN], *valptr;
if(!str || !*str) return RESULT_BADKEY;
// check web headers and fields
DBG("\n\nLINENO: %lu", client->lineno);
if(client->lineno == 0){ // first data line from client -> check if it's a header
if(iswebheader(client, str)){
if(client->sockmethod == SOCKM_GET)
return parse_post_data(client, str);
return RESULT_SILENCE;
}
}else if(client->sockmethod != SOCKM_RAW){
if(chkwebreq(client, str)) return RESULT_SILENCE;
}
if(!client->handlers){ // have only default handler
if(!client->defmsg_handler) return RESULT_BADKEY;
return client->defmsg_handler(client, str);
}
int N = sl_get_keyval(str, key, val);
DBG("getval=%d, key=%s, val=%s", N, key, val);
if(N == 0) return RESULT_BADKEY;
if(N == 0){
if(client->defmsg_handler) return client->defmsg_handler(client, str);
return RESULT_BADKEY;
}
if(N == 1) valptr = NULL;
else valptr = val;
if(0 == strcmp(key, "help")){
@ -205,13 +345,84 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
sl_sock_sendbyte(client, '\n');
return RESULT_SILENCE;
}
// check for strict params like `key=val`
for(sl_sock_hitem_t *h = client->handlers; h->handler; ++h){
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);
}
// 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(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);
return RESULT_BADKEY;
}
// send http responce with data in c->outbuffer
static void send_http_response(sl_sock_t* c){
if(c->sockmethod == SOCKM_RAW) return;
DBG("Send to client HTTP response");
c->sockmethod = SOCKM_RAW; // dirty hack to send data by sl_sock_sendbinmessage
char tbuf[BUFSIZ];
ssize_t L = snprintf(tbuf, BUFSIZ-1,
"HTTP/2.0 200 OK\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Methods: GET, POST\r\n"
"Access-Control-Allow-Credentials: true\r\n"
"Content-type: text/plain\r\nContent-Length: %zd\r\n\r\n", c->outplen);
if(L < 0){
WARN("sprintf()");
LOGWARN("sprintf()");
return;
}
if(L != sl_sock_sendbinmessage(c, (uint8_t*)tbuf, L)) return;
if(c->outplen != (size_t)sl_sock_sendbinmessage(c, (uint8_t*)c->outbuffer, c->outplen)) return;
}
/**
* @brief serverrbthread - thread for standard server procedure (when user give non-NULL `handlers`)
* @param d - socket descriptor
@ -219,43 +430,50 @@ static sl_sock_hresult_e msgparser(sl_sock_t *client, char *str){
*/
static void *serverthread(void _U_ *d){
sl_sock_t *s = (sl_sock_t*) d;
if(!s || !s->handlers){
if(!s || (!s->handlers && !s->defmsg_handler)){
WARNX(_("Can't start server handlers thread"));
goto errex;
}
int sockfd = s->fd;
if(listen(sockfd, maxclients) == -1){
if(listen(sockfd, s->maxclients) == -1){
WARN("listen");
goto errex;
}
DBG("Start server handlers thread");
int nfd = 1; // only one socket @start
struct pollfd *poll_set = MALLOC(struct pollfd, maxclients+1);
clients = MALLOC(sl_sock_t*, maxclients+1);
struct pollfd *poll_set = MALLOC(struct pollfd, s->maxclients+1);
sl_sock_t **clients = MALLOC(sl_sock_t*, s->maxclients+1);
s->clients = clients;
// init default clients records
for(int i = maxclients; i > 0; --i){
for(int i = s->maxclients; i > 0; --i){
DBG("fill %dth client info", i);
clients[i] = MALLOC(sl_sock_t, 1);
sl_sock_t *c = clients[i];
c->type = s->type;
if(s->node) c->node = strdup(s->node);
if(s->service) c->service = strdup(s->service);
c->handlers = s->handlers;
// fill addrinfo
c->addrinfo = MALLOC(struct addrinfo, 1);
c->addrinfo->ai_addr = MALLOC(struct sockaddr, 1);
// copy server data: we have no `self`, so use so
c->handlers = s->handlers;
c->defmsg_handler = s->defmsg_handler;
}
// ZERO - listening server socket
poll_set[0].fd = sockfd;
poll_set[0].events = POLLIN;
// disconnect client
void disconnect_(sl_sock_t *c, int N){
DBG("client \"%s\" (fd=%d) disconnected", c->IP, c->fd);
if(disconnect_handler) disconnect_handler(c);
DBG("Disconnect client \"%s\" (fd=%d)", c->IP, c->fd);
if(s->disconnect_handler) s->disconnect_handler(c);
if(c->sockmethod != SOCKM_RAW) send_http_response(c); // we are closing HTTP request - send headers and answer
pthread_mutex_lock(&c->mutex);
DBG("close fd %d", c->fd);
c->connected = 0;
close(c->fd);
c->outplen = 0;
c->lineno = 0;
c->gotemptyline = 0;
sl_RB_clearbuf(c->buffer);
// now move all data of last client to disconnected
if(nfd > 2 && N != nfd - 1){ // don't move the only or the last
@ -281,9 +499,9 @@ static void *serverthread(void _U_ *d){
socklen_t len = sizeof(struct sockaddr);
int client = accept(sockfd, &a, &len);
DBG("New connection, nfd=%d, len=%d", nfd, len);
if(nfd == maxclients + 1){
if(nfd == s->maxclients + 1){
WARNX(_("Limit of connections reached"));
if(toomuch_handler) toomuch_handler(client);
if(s->toomuch_handler) s->toomuch_handler(client);
close(client);
}else{
memset(&poll_set[nfd], 0, sizeof(struct pollfd));
@ -302,12 +520,16 @@ static void *serverthread(void _U_ *d){
*c->IP = 0;
}
DBG("got IP:%s", c->IP);
if(newconnect_handler) newconnect_handler(c);
if(!c->buffer){ // allocate memory for client's ringbuffer
DBG("allocate ringbuffer");
c->buffer = sl_RB_new(s->buffer->length); // the same size as for master
}
++nfd;
if(s->newconnect_handler && s->newconnect_handler(c) == FALSE){
DBG("Client %s rejected", c->IP);
disconnect_(c, nfd);
}else{
if(!c->buffer){ // allocate memory for client's ringbuffer
DBG("allocate ringbuffer");
c->buffer = sl_RB_new(s->buffer->length); // the same size as for master
}
}
}
}
// scan connections
@ -323,6 +545,7 @@ static void *serverthread(void _U_ *d){
// check for RB overflow
if(sl_RB_hasbyte(c->buffer, '\n') < 0){ // -1 - buffer empty (can't be), -2 - buffer overflow
WARNX(_("Server thread: ring buffer overflow for fd=%d"), fd);
LOGERR(_("Server thread: ring buffer overflow for fd=%d"), fd);
disconnect_(c, fdidx);
--fdidx;
}
@ -352,15 +575,39 @@ static void *serverthread(void _U_ *d){
disconnect_(c, fdidx);
--fdidx;
continue;
}else if(got == 0) continue;
sl_sock_hresult_e r = msgparser(c, (char*)buf);
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
}else if(got == 0){ // check last data in POST/GET methods
if(c->sockmethod == SOCKM_RAW) continue;
size_t l = sl_RB_datalen(c->buffer);
if(c->sockmethod == SOCKM_POST){
if(l != (size_t)c->contlen) continue; // wait for last data
if(l < bufsize - 1){
sl_RB_read(c->buffer, buf, l);
buf[l] = 0;
parse_post_data(c, (char*)buf);
}
}
disconnect_(c, fdidx);
--fdidx;
continue;
}
if(got > 1 && *buf && *buf != '\r'){ // not empty line
if(buf[got-2] == '\r'){
buf[got-2] = 0; // omit '\r' for "\r\n"
DBG("delete \\r: _%s_", buf);
}
sl_sock_hresult_e r = msgparser(c, (char*)buf);
if(r != RESULT_SILENCE) sl_sock_sendstrmessage(c, sl_sock_hresult2str(r));
}else{
DBG("EMPTY line");
if(c->sockmethod != SOCKM_RAW) c->gotemptyline = TRUE;
}
++c->lineno;
}
}
// clear memory
FREE(buf);
FREE(poll_set);
for(int i = maxclients; i > 0; --i){
for(int i = s->maxclients; i > 0; --i){
DBG("Clear %dth client data", i);
sl_sock_t *c = clients[i];
if(c->fd > -1) close(c->fd);
@ -372,6 +619,7 @@ static void *serverthread(void _U_ *d){
FREE(c);
}
FREE(clients);
s->clients = NULL;
errex:
s->rthread = 0;
return NULL;
@ -420,11 +668,14 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
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);
else{ // fill node/service
if(type == SOCKT_UNIX) s->node = str; // str now is converted path
if(!s->buffer){
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
@ -444,13 +695,19 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
// now try to open socket
if(type != SOCKT_UNIX){
DBG("try to get addrinfo for node '%s' and service '%s'", s->node, s->service);
char *node = s->node;
if(isserver){
if(s->type == SOCKT_NET) node = NULL; // common net server -> node==NULL
else node = "127.0.0.1"; // localhost
if(!s->node){
DBG("Socket type now is SOCKT_NET");
s->type = SOCKT_NET;
}
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'", node, s->service);
int e = getaddrinfo(node, s->service, &ai, &res);
DBG("---> node '%s', service '%s'", s->node, s->service);
int e = getaddrinfo(s->node, s->service, &ai, &res);
if(e){
WARNX("getaddrinfo(): %s", gai_strerror(e));
sl_sock_delete(&s);
@ -491,7 +748,7 @@ static sl_sock_t *sl_sock_open(sl_socktype_e type, const char *path, sl_sock_hit
DBG("s->fd=%d, node=%s, service=%s", s->fd, s->node, s->service);
int r = -1;
if(isserver){
if(s->handlers)
if(s->handlers || s->defmsg_handler)
r = pthread_create(&s->rthread, NULL, serverthread, (void*)s);
else r = 0;
}else{
@ -543,6 +800,15 @@ sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz,
*/
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l){
if(!msg || l < 1) return -1;
if(socket->sockmethod != SOCKM_RAW){ // just fill buffer while socket isn't marked as "RAW"
DBG("Put to buffer: _%s_", (char*)msg);
size_t L = BUFSIZ - socket->outplen;
if(l > L) l = L;
memcpy(socket->outbuffer + socket->outplen, msg, l);
socket->outplen += l;
DBG("Now buflen=%zd, buf: ```%s```", socket->outplen, socket->outbuffer);
return l;
}
DBG("send to fd=%d message with len=%zd (%s)", socket->fd, l, msg);
while(socket && socket->connected && !sl_canwrite(socket->fd));
if(!socket || !socket->connected) return -1;
@ -578,6 +844,13 @@ ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg){
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte){
while(socket && socket->connected && !sl_canwrite(socket->fd));
if(!socket || !socket->connected) return -1;
if(socket->sockmethod != SOCKM_RAW){ // just fill buffer while socket isn't marked as "RAW"
DBG("Put to buffer: _%c_", (char)byte);
if(socket->outplen == BUFSIZ) return 0;
socket->outbuffer[socket->outplen++] = (char)byte;
DBG("Now buflen=%zd, buf: ```%s```", socket->outplen, socket->outbuffer);
return 1;
}
DBG("lock");
pthread_mutex_lock(&socket->mutex);
ssize_t r = send(socket->fd, &byte, 1, MSG_NOSIGNAL);
@ -602,6 +875,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){
char buf[128];
sl_sock_int_t *i = (sl_sock_int_t *)hitem->data;
if(!i) return RESULT_FAIL;
if(!str){ // getter
snprintf(buf, 127, "%s=%" PRId64 "\n", hitem->key, i->val);
sl_sock_sendstrmessage(client, buf);
@ -617,6 +891,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){
char buf[128];
sl_sock_double_t *d = (sl_sock_double_t *)hitem->data;
if(!d) return RESULT_FAIL;
if(!str){ // getter
snprintf(buf, 127, "%s=%g\n", hitem->key, d->val);
sl_sock_sendstrmessage(client, buf);
@ -631,6 +906,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){
char buf[SL_VAL_LEN + SL_KEY_LEN + 3];
sl_sock_string_t *s = (sl_sock_string_t*) hitem->data;
if(!s) return RESULT_FAIL;
if(!str){ // getter
snprintf(buf, SL_VAL_LEN + SL_KEY_LEN + 2, "%s=%s\n", hitem->key, s->val);
sl_sock_sendstrmessage(client, buf);
@ -644,3 +920,24 @@ sl_sock_hresult_e sl_sock_strhandler(sl_sock_t *client, sl_sock_hitem_t *hitem,
s->val[l] = 0;
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;
}

View File

@ -400,7 +400,7 @@ int sl_str2d(double *num, const char *str){
if(!str) return FALSE;
res = strtod(str, &endptr);
if(endptr == str || *str == '\0' || *endptr != '\0'){
WARNX(_("Wrong double number format '%s'"), str);
//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
@ -413,7 +413,7 @@ int sl_str2ll(long long *num, const char *str){
if(!str) return FALSE;
res = strtoll(str, &endptr, 0);
if(endptr == str || *str == '\0' || *endptr != '\0'){
WARNX(_("Wrong integer number format '%s'"));
//WARNX(_("Wrong integer number format '%s'"));
return FALSE;
}
if(num) *num = res;
@ -425,11 +425,11 @@ int sl_str2i(int *num, const char *str){
if(!str) return FALSE;
res = strtoll(str, &endptr, 0);
if(endptr == str || *str == '\0' || *endptr != '\0'){
WARNX(_("Wrong integer number format '%s'"));
//WARNX(_("Wrong integer number format '%s'"));
return FALSE;
}
if(res > INT_MAX || res < INT_MIN){
WARNX(_("Number out of integer limits"));
//WARNX(_("Number out of integer limits"));
return FALSE;
}
if(num) *num = (int)res;

View File

@ -21,6 +21,7 @@
#pragma once
#include <stdio.h>
#ifdef SL_USE_OLD_TTY
#include <termios.h> // termios
#else
@ -35,6 +36,7 @@
#include <unistd.h> // pid_t
// just for different purposes
#include <limits.h>
#include <math.h> // inf/isinf
#include <stdint.h>
#if defined GETTEXT
@ -325,6 +327,7 @@ typedef struct{
extern const char *__progname;
void sl_showhelp(int oindex, sl_option_t *options);
void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options, void (*helpfun)(int, sl_option_t*));
void sl_parseargs(int *argc, char ***argv, sl_option_t *options);
/**
* @brief sl_helpstring - change standard help header
@ -373,16 +376,18 @@ void *sl_list_pop(sl_list_t **lst);
#define SL_COMMENT_CHAR '#'
// option or simple configuration value (don't work for functions)
typedef struct{
/*typedef struct{
union{
int ival; long long llval; double dval; float fval;
};
sl_argtype_e type;
} sl_optval;
} sl_optval;*/
int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]);
char *sl_print_opts(sl_option_t *opt, int showall);
int sl_conf_readopts(const char *filename, sl_option_t *options);
void sl_conf_showhelp(int idx, sl_option_t *options);
int sl_remove_quotes(char *string);
/******************************************************************************\
The original ringbuffer.h
@ -441,11 +446,20 @@ typedef struct{
int len; // strlen of `val`
} 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 (sl_sock_keyno_t){.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;
// socket `key` handlers
typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *client, struct sl_sock_hitem *item, const char *val);
typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *s, struct sl_sock_hitem *item, const char *val);
// handler item
typedef struct sl_sock_hitem{
sl_sock_msghandler handler; // function-handler
@ -462,6 +476,30 @@ typedef enum{
SOCKT_AMOUNT // amount of types
} sl_socktype_e;
struct sl_sock;
// default max clients amount
#define SL_DEF_MAXCLIENTS (32)
// custom socket handlers: connect/disconnect/etc
// max clients handler
void sl_sock_maxclhandler(struct sl_sock *s, void (*h)(int));
// client connected handler
void sl_sock_connhandler(struct sl_sock *s, int(*h)(struct sl_sock*));
// client disconnected handler
void sl_sock_dischandler(struct sl_sock *s, void(*h)(struct sl_sock*));
// unknown message handler (instead of "BADKEY" default message)
void sl_sock_defmsghandler(struct sl_sock *s, sl_sock_hresult_e(*h)(struct sl_sock *s, const char *str));
typedef enum{
SOCKM_RAW = 0, // default sockets
SOCKM_GET, // http methods - client should be closed after data processing
SOCKM_PUT,
SOCKM_POST,
SOCKM_PATCH,
SOCKM_DELETE,
SOCKM_AMOUNT
} sl_sockmethod_e;
// socket itself
typedef struct sl_sock{
int fd; // file descriptor
@ -476,29 +514,33 @@ typedef struct sl_sock{
pthread_t rthread; // reading ring buffer thread for client and main server thread for server
char IP[INET_ADDRSTRLEN]; // client's IP address
sl_sock_hitem_t *handlers; // if non-NULL, run handler's thread when opened
sl_sockmethod_e sockmethod; // method
uint64_t lineno; // number of line read
int contlen; // content length for POST method
int gotemptyline; // == TRUE when found empty line in web request (to know that header is over)
char outbuffer[BUFSIZ]; // buffer for output data (if client is WEB)
size_t outplen; // amount of bytes in `outbuffer`
// server-only items
int maxclients; // max clients amount
void (*toomuch_handler)(int); // too much clients handler; it is running for client connected with number>maxclients (before closing its fd)
int (*newconnect_handler)(struct sl_sock*); // new client connected handler; it will be run each new connection
void (*disconnect_handler)(struct sl_sock*); // client disconnected handler
sl_sock_hresult_e(*defmsg_handler)(struct sl_sock *s, const char *str); // default message handler (the only without `handlers` array or instead of "BADKEY" answer
struct sl_sock **clients; // pointer to clients array for `sendall`
} sl_sock_t;
const char *sl_sock_hresult2str(sl_sock_hresult_e r);
void sl_sock_delete(sl_sock_t **sock);
sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz);
sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path, int bufsiz, sl_sock_hitem_t *handlers);
void sl_sock_changemaxclients(int val);
int sl_sock_getmaxclients();
// max clients handler
typedef void (*sl_sock_maxclh_t)(int fd);
void sl_sock_maxclhandler(sl_sock_maxclh_t h);
// new client connected handler
typedef void (*sl_sock_connh_t)(sl_sock_t *s);
void sl_sock_connhandler(sl_sock_connh_t h);
// client disconnected handler
typedef void (*sl_sock_disch_t)(sl_sock_t *s);
void sl_sock_dischandler(sl_sock_disch_t h);
void sl_sock_changemaxclients(sl_sock_t *sock, int val);
int sl_sock_getmaxclients(sl_sock_t *sock);
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l);
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte);
ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg);
ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len);
int sl_sock_sendall(uint8_t *data, size_t len);
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_dblhandler(sl_sock_t *client, sl_sock_hitem_t *hitem, const char *str);

View File

@ -1,7 +1,6 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: @PROJ@
Description: Library with a lot of usefull snippets