mirror of
https://github.com/eddyem/snippets_library.git
synced 2025-12-07 03:05:15 +03:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56ee64dd78 | |||
| 9e285bbe34 | |||
| 82e66080b3 | |||
| a445aec8da |
@ -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(
|
||||
|
||||
32
Changelog
32
Changelog
@ -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
37
Readme.rus
Normal 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
2
TODO
Normal file
@ -0,0 +1,2 @@
|
||||
Add common case of binary search?
|
||||
Add hash generation for certain keys list?
|
||||
364
config.c
364
config.c
@ -24,6 +24,28 @@
|
||||
|
||||
#include "usefull_macros.h"
|
||||
|
||||
/**
|
||||
* @brief sl_remove_quotes - remove all outern quotes - starting and trailng " and '
|
||||
* @param (io) string to modify (if quotes found rest of string will be moved to head, tail will be zeroed)
|
||||
* @return amount of quotation pair found
|
||||
*/
|
||||
int sl_remove_quotes(char *string){
|
||||
if(!string) return 0;
|
||||
int l = strlen(string);
|
||||
if(l < 2) return 0;
|
||||
int nq = 0, half = l/2;
|
||||
for(; nq < half; ++nq){
|
||||
char _1st = string[nq];
|
||||
if(_1st != string[l-1-nq]) break;
|
||||
if(_1st != '\'' && _1st != '"') break;
|
||||
}
|
||||
if(nq == 0) return 0;
|
||||
l -= 2 * nq;
|
||||
memmove(string, string + nq, l);
|
||||
string[l] = 0;
|
||||
return nq;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sl_get_keyval - get key name and its value from string pair
|
||||
* @param pair - empty string, `key = value` or just `key`
|
||||
@ -66,6 +88,7 @@ int sl_get_keyval(const char *pair, char key[SL_KEY_LEN], char value[SL_VAL_LEN]
|
||||
for(; kend < eq && !isspace(*kend); ++kend);
|
||||
size_t l = SL_KEY_LEN - 1;
|
||||
if(l > (size_t)(kend - kstart)) l = kend - kstart;
|
||||
else if(l < (size_t)(kend - kstart)) WARNX(_("sl_get_keyval(): key would be trunkated to %d symbols"), l);
|
||||
//DBG("kend=%c, kstart=%c, l=%zd", *kend, *kstart, l);
|
||||
strncpy(key, kstart, l);
|
||||
key[l] = 0;
|
||||
@ -90,13 +113,74 @@ static int read_key(FILE *file, char key[SL_KEY_LEN], char value[SL_VAL_LEN]){
|
||||
return r;
|
||||
}
|
||||
|
||||
// search `opt` record for given `key`
|
||||
static sl_option_t *opt_search(const char *key, sl_option_t *options){
|
||||
while(options->name){
|
||||
if(0 == strcmp(key, options->name)) return options;
|
||||
++options;
|
||||
// print option value
|
||||
static size_t pr_val(sl_argtype_e type, void *argptr, char **buffer, size_t *buflen, size_t pos){
|
||||
size_t maxlen = *buflen - pos - 1;
|
||||
char *buf = *buffer + pos;
|
||||
switch(type){
|
||||
case arg_none:
|
||||
case arg_int:
|
||||
DBG("int %d", *(int*) argptr);
|
||||
return snprintf(buf, maxlen, "%d", *(int*) argptr);
|
||||
case arg_longlong:
|
||||
DBG("long long %lld", *(long long*) argptr);
|
||||
return snprintf(buf, maxlen, "%lld", *(long long*)argptr);
|
||||
case arg_float:
|
||||
DBG("float %g", *(float*) argptr);
|
||||
return snprintf(buf, maxlen, "%g", *(float*) argptr);
|
||||
case arg_double:
|
||||
DBG("double %g", *(double*) argptr);
|
||||
return snprintf(buf, maxlen, "%g", *(double*) argptr);
|
||||
case arg_string:
|
||||
if(!argptr || !(*(char**)argptr)){
|
||||
return snprintf(buf, maxlen, "(null)");
|
||||
}else if(!(**(char**)argptr)){
|
||||
return snprintf(buf, maxlen, "(empty)");
|
||||
}
|
||||
char *str = *(char**) argptr;
|
||||
DBG("string %s", str);
|
||||
size_t z = strlen(str);
|
||||
while(pos + z > *buflen + 5){
|
||||
*buflen += BUFSIZ;
|
||||
*buffer = realloc(*buffer, *buflen);
|
||||
if(!*buffer) ERRX("realloc()");
|
||||
maxlen += BUFSIZ;
|
||||
buf = *buffer + pos;
|
||||
}
|
||||
return snprintf(buf, maxlen, "\"%s\"", str);
|
||||
default:
|
||||
DBG("function");
|
||||
return snprintf(buf, maxlen, "\"(unsupported)\"");
|
||||
}
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// print one option
|
||||
static size_t print_opt(sl_option_t *opt, char **buffer, size_t *buflen, size_t pos){
|
||||
if((ssize_t)*buflen - pos < 3 *(SL_KEY_LEN + SL_VAL_LEN)){
|
||||
*buflen += BUFSIZ;
|
||||
*buffer = realloc(*buffer, *buflen);
|
||||
if(!*buffer) ERR("realloc()");
|
||||
}
|
||||
char *buf = *buffer + pos;
|
||||
size_t l = 0, maxlen = *buflen - pos - 1;
|
||||
size_t got = snprintf(buf, maxlen, "%s = ", opt->name);
|
||||
l = got; maxlen -= got; buf += got;
|
||||
if(opt->flag){
|
||||
DBG("got flag '%d'", *opt->flag);
|
||||
l += snprintf(buf, maxlen, "%d\n", *opt->flag);
|
||||
return l;
|
||||
}
|
||||
if(!opt->argptr){ // ERR!
|
||||
l += snprintf(buf, maxlen, "\"(no argptr)\"\n");
|
||||
WARNX("Parameter \"%s\" have no argptr!", opt->name);
|
||||
return l;
|
||||
}
|
||||
DBG("type: %d", opt->type);
|
||||
got = pr_val(opt->type, opt->argptr, buffer, buflen, pos + l);
|
||||
l += got; maxlen -= got; buf += got;
|
||||
l += snprintf(buf, maxlen, "\n");
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,156 +192,44 @@ static sl_option_t *opt_search(const char *key, sl_option_t *options){
|
||||
char *sl_print_opts(sl_option_t *opt, int showall){
|
||||
char *buf = MALLOC(char, BUFSIZ);
|
||||
size_t L = BUFSIZ, l = 0;
|
||||
for(; opt->name; ++opt){
|
||||
for(; opt->help; ++opt){
|
||||
if(!opt->name) continue; // only show help - not config option!
|
||||
DBG("check %s", opt->name);
|
||||
if(!showall && opt->has_arg == NO_ARGS) continue; // show NO_ARGS only when `showall==TRUE`
|
||||
if(!showall && opt->type == arg_string && !opt->argptr) continue; // empty string
|
||||
if((ssize_t)L - l < SL_KEY_LEN + SL_VAL_LEN + 5){
|
||||
L += BUFSIZ;
|
||||
buf = realloc(buf, L);
|
||||
if(!buf) ERR("realloc()");
|
||||
}
|
||||
l += sprintf(buf + l, "%s=", opt->name);
|
||||
if(opt->flag){
|
||||
DBG("got flag '%d'", *opt->flag);
|
||||
l += sprintf(buf + l, "%d\n", *opt->flag);
|
||||
continue;
|
||||
}
|
||||
if(!opt->argptr){ // ERR!
|
||||
l += sprintf(buf + l, "\"(no argptr)\"\n");
|
||||
WARNX("Parameter \"%s\" have no argptr!", opt->name);
|
||||
continue;
|
||||
}
|
||||
int z = 0;
|
||||
DBG("type: %d", opt->type);
|
||||
switch(opt->type){
|
||||
case arg_none:
|
||||
case arg_int:
|
||||
DBG("int %d", *(int*) opt->argptr);
|
||||
l += sprintf(buf + l, "%d", *(int*) opt->argptr);
|
||||
break;
|
||||
case arg_longlong:
|
||||
DBG("long long %lld", *(long long*) opt->argptr);
|
||||
l += sprintf(buf + l, "%lld", *(long long*) opt->argptr);
|
||||
break;
|
||||
case arg_float:
|
||||
DBG("float %g", *(float*) opt->argptr);
|
||||
l += sprintf(buf + l, "%g", *(float*) opt->argptr);
|
||||
break;
|
||||
case arg_double:
|
||||
DBG("double %g", *(double*) opt->argptr);
|
||||
l += sprintf(buf + l, "%g", *(double*) opt->argptr);
|
||||
break;
|
||||
case arg_string:
|
||||
if(!opt->argptr || !(*(char*)opt->argptr)){
|
||||
l += sprintf(buf + l, "(null)");
|
||||
break;
|
||||
}else if(!(**(char**)opt->argptr)){
|
||||
l += sprintf(buf + l, "(empty)");
|
||||
break;
|
||||
}
|
||||
DBG("string %s", *(char**) opt->argptr);
|
||||
z = strlen(*(char**) opt->argptr);
|
||||
while(l + z > L + 3){
|
||||
L += BUFSIZ;
|
||||
buf = realloc(buf, L);
|
||||
}
|
||||
l += sprintf(buf + l, "%s", *(char**) opt->argptr);
|
||||
break;
|
||||
default:
|
||||
DBG("function");
|
||||
l += sprintf(buf + l, "\"(unsupported)\"");
|
||||
break;
|
||||
}
|
||||
l += sprintf(buf + l, "\n");
|
||||
if(opt->has_arg == MULT_PAR){
|
||||
sl_option_t tmpopt = *opt;
|
||||
DBG("type: %d", tmpopt.type);
|
||||
if(!opt->argptr){ DBG("No pointer to array!"); continue; }
|
||||
#if 0
|
||||
void ***pp = (void***)opt->argptr;
|
||||
if(!*(char***)pp){ DBG("Array is empty"); continue; }
|
||||
while(**pp){
|
||||
if(opt->type == arg_string){
|
||||
DBG("str");
|
||||
tmpopt.argptr = *pp; // string is pointer to pointer!
|
||||
}else tmpopt.argptr = **pp;
|
||||
if(!tmpopt.argptr){ DBG("null"); break; }
|
||||
l += print_opt(&tmpopt, &buf, &L, l);
|
||||
++(*pp);
|
||||
}
|
||||
#endif
|
||||
void **pp = *(void***)opt->argptr;
|
||||
if(!(char**)pp){ DBG("Array is empty"); continue; }
|
||||
while(*pp){
|
||||
if(opt->type == arg_string){
|
||||
DBG("str");
|
||||
tmpopt.argptr = pp; // string is pointer to pointer!
|
||||
}else tmpopt.argptr = *pp;
|
||||
if(!tmpopt.argptr){ DBG("null"); break; }
|
||||
l += print_opt(&tmpopt, &buf, &L, l);
|
||||
++(pp);
|
||||
}
|
||||
}else l += print_opt(opt, &buf, &L, l);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sl_set_optval - convert `val` to `oval` parameter according to argument type
|
||||
* @param oval (o) - value in int/long long/double/float
|
||||
* @param opt (i) - record with options
|
||||
* @param val (i) - new value
|
||||
* @return FALSE if failed (or wrong data range)
|
||||
*/
|
||||
int sl_set_optval(sl_optval *oval, sl_option_t *opt, const char *val){
|
||||
if(!oval || !opt || !val) return FALSE;
|
||||
long long ll;
|
||||
double d;
|
||||
switch(opt->type){
|
||||
case arg_none:
|
||||
case arg_int:
|
||||
case arg_longlong:
|
||||
do{
|
||||
if(!sl_str2ll(&ll, val)){
|
||||
break;
|
||||
}
|
||||
if(opt->type != arg_longlong){
|
||||
if(ll > INT_MAX || ll < INT_MIN){
|
||||
break;
|
||||
}
|
||||
oval->ival = (int)ll;
|
||||
}else oval->llval = ll;
|
||||
return TRUE;
|
||||
}while(0);
|
||||
break;
|
||||
case arg_double:
|
||||
case arg_float:
|
||||
do{
|
||||
if(!sl_str2d(&d, val)){
|
||||
break;
|
||||
}
|
||||
if(opt->type == arg_double){
|
||||
oval->dval = d;
|
||||
}else{
|
||||
if(d > FLT_MAX || d < FLT_MIN){
|
||||
break;
|
||||
}
|
||||
oval->fval = (float)d;
|
||||
}
|
||||
return TRUE;
|
||||
}while(0);
|
||||
break;
|
||||
case arg_string:
|
||||
return TRUE;
|
||||
break;
|
||||
default:
|
||||
WARNX(_("Unsupported option type"));
|
||||
return FALSE;
|
||||
}
|
||||
WARNX(_("Wrong number format '%s'"), val);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// increment opt->argptr or set it to val
|
||||
static void setoa(sl_option_t *opt, const char *val){
|
||||
if(!opt || !opt->argptr || opt->type == arg_function) return;
|
||||
sl_optval O;
|
||||
if(!sl_set_optval(&O, opt, val)) return;
|
||||
switch(opt->type){
|
||||
case arg_none: // increment integer
|
||||
*(int*) opt->argptr += O.ival;
|
||||
break;
|
||||
case arg_int:
|
||||
*(int*) opt->argptr = O.ival;
|
||||
break;
|
||||
case arg_longlong:
|
||||
*(long long*) opt->argptr = O.llval;
|
||||
break;
|
||||
case arg_double:
|
||||
*(double*) opt->argptr = O.dval;
|
||||
break;
|
||||
case arg_float:
|
||||
*(float*) opt->argptr = O.fval;
|
||||
break;
|
||||
case arg_string:
|
||||
*(char**)opt->argptr = strdup(val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sl_conf_readopts - simplest configuration:
|
||||
@ -273,29 +245,85 @@ int sl_conf_readopts(const char *filename, sl_option_t *options){
|
||||
WARN(_("Can't open %s"), filename);
|
||||
return 0;
|
||||
}
|
||||
int N = 0;
|
||||
char key[SL_KEY_LEN], val[SL_VAL_LEN];
|
||||
int argc = 1;
|
||||
#define BUFSZ (SL_KEY_LEN+SL_VAL_LEN+8)
|
||||
char key[SL_KEY_LEN], val[SL_VAL_LEN], obuf[BUFSZ];
|
||||
int argvsize = 0;
|
||||
char **argv = NULL;
|
||||
do{
|
||||
int r = read_key(f, key, val);
|
||||
if(r < 0) break;
|
||||
if(r == 0) continue;
|
||||
sl_option_t *opt = opt_search(key, options);
|
||||
if(!opt){
|
||||
WARNX(_("Wrong key: '%s'"), key);
|
||||
continue;
|
||||
DBG("key='%s', val='%s'", key, (r == 2) ? val : "(absent)");
|
||||
++argc;
|
||||
if(argvsize <= argc){
|
||||
argvsize += 256;
|
||||
argv = realloc(argv, sizeof(char*) * argvsize);
|
||||
if(!argv) ERRX("sl_conf_readopts: realloc() error");
|
||||
}
|
||||
if(opt->flag) *opt->flag = opt->val;
|
||||
if(r == 1){ // only key
|
||||
if(opt->has_arg != NO_ARGS && opt->has_arg != OPT_ARG){
|
||||
WARNX(_("Key '%s' need value"), opt->name);
|
||||
continue;
|
||||
}
|
||||
if(opt->argptr) setoa(opt, "1");
|
||||
}else{ // key + value
|
||||
if(opt->argptr) setoa(opt, val);
|
||||
else WARNX(_("Key '%s' have no argptr!"), opt->name);
|
||||
}
|
||||
++N;
|
||||
if(argc == 2) argv[0] = strdup(__progname); // all as should be
|
||||
if(r == 2){
|
||||
// remove trailing/ending quotes
|
||||
sl_remove_quotes(val);
|
||||
snprintf(obuf, BUFSZ-1, "--%s=%s", key, val);
|
||||
}else snprintf(obuf, BUFSZ-1, "--%s", key);
|
||||
DBG("next argv: '%s'", obuf);
|
||||
argv[argc-1] = strdup(obuf);
|
||||
}while(1);
|
||||
return N;
|
||||
if(!argc) return 0;
|
||||
int N = argc; char **a = argv;
|
||||
sl_parseargs_hf(&argc, &a, options, sl_conf_showhelp);
|
||||
for(int n = 0; n < N; ++n) free(argv[n]);
|
||||
free(argv);
|
||||
return N - argc; // amount of recognized options
|
||||
}
|
||||
|
||||
// sort only by long options
|
||||
static int confsort(const void *a1, const void *a2){
|
||||
const sl_option_t *o1 = (sl_option_t*)a1, *o2 = (sl_option_t*)a2;
|
||||
const char *l1 = o1->name, *l2 = o2->name;
|
||||
// move empty options to end of list
|
||||
if(!l1 && !l2) return 1;
|
||||
if(!l1) return 1;
|
||||
if(!l2) return -1;
|
||||
return strcmp(l1, l2);
|
||||
}
|
||||
|
||||
static void pr_helpstring(sl_option_t *opt){
|
||||
if(!opt->name || !opt->help) return;
|
||||
printf(" %s", opt->name);
|
||||
if(opt->has_arg == NEED_ARG || opt->has_arg == MULT_PAR) // required argument
|
||||
printf(" = arg");
|
||||
else if(opt->has_arg == OPT_ARG) // optional argument
|
||||
printf(" [= arg]");
|
||||
printf(" -- %s", opt->help);
|
||||
if(opt->has_arg == MULT_PAR) printf(" (can occur multiple times)");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sl_conf_showhelp - show help for config file
|
||||
* (the same as `sl_showhelp`, but without "--", short opts and exit()
|
||||
* @param options - config options (only long opts used)
|
||||
*/
|
||||
void sl_conf_showhelp(int idx, sl_option_t *options){
|
||||
if(!options || !(*options).help) return;
|
||||
// count amount of options
|
||||
sl_option_t *opts = options;
|
||||
int N; for(N = 0; opts->help; ++N, ++opts);
|
||||
if(N == 0) exit(-2);
|
||||
if(idx > -1){
|
||||
if(idx >=N) WARNX(_("sl_conf_showhelp(): wrong index"));
|
||||
else pr_helpstring(&options[idx]);
|
||||
return;
|
||||
}
|
||||
sl_option_t *tmpopts = MALLOC(sl_option_t, N);
|
||||
memcpy(tmpopts, options, N*sizeof(sl_option_t));
|
||||
printf(_("Configuration file options (format: key=value):\n"));
|
||||
qsort(tmpopts, N, sizeof(sl_option_t), confsort);
|
||||
for(int _ = 0; _ < N; ++_){
|
||||
if(!tmpopts[_].name) continue;
|
||||
pr_helpstring(&tmpopts[_]);
|
||||
}
|
||||
free(tmpopts);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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){
|
||||
|
||||
Binary file not shown.
@ -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 ""
|
||||
|
||||
294
locale/ru/ru.po
294
locale/ru/ru.po
@ -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 "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
|
||||
|
||||
226
parseargs.c
226
parseargs.c
@ -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);
|
||||
|
||||
10
ringbuffer.c
10
ringbuffer.c
@ -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
411
socket.c
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user