diff --git a/CMakeLists.txt b/CMakeLists.txt index 83079b6..778882f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 4.0) set(PROJ usefull_macros) -set(MINOR_VERSION "3") +set(MINOR_VERSION "4") set(MID_VERSION "3") set(MAJOR_VERSION "0") set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") diff --git a/Changelog b/Changelog index 5132dfc..47eb83c 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,12 @@ +Thu Nov 6 11:25:07 MSK 2025 +VERSION 0.3.4: +add data type: +- sl_sock_keyno_t - number of key (like key[keyno] = val) using in sl_sock_hitem_t.data +add functions: +- sl_sock_keyno_init - init keyno (or use macro SL_SOCK_KEYNO_DEFAUL in assignment) +- sl_sock_keyno_check - return -1 if there's no keyno, else return its value +New functional listed in examples/clientserver.c introducing key `flags` that allows to work with bit flags as a whole or by bits like flags[1]=1, flags21=0, flags{31} or flags(14)... + Wed Sep 10 14:19:24 MSK 2025 (still version 0.3.3: I forgot to add changelog last commits) - fixed minor bugs and memory leaks diff --git a/examples/clientserver.c b/examples/clientserver.c index 9ac67a1..9df11ce 100644 --- a/examples/clientserver.c +++ b/examples/clientserver.c @@ -122,6 +122,7 @@ 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]; @@ -169,6 +170,7 @@ 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"); @@ -176,6 +178,33 @@ static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){ sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n"); 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}, @@ -183,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} }; @@ -193,6 +223,7 @@ 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_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(); diff --git a/locale/ru/messages.po b/locale/ru/messages.po index 5701fe6..81f1d4d 100644 --- a/locale/ru/messages.po +++ b/locale/ru/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-09-10 12:08+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 \n" "Language-Team: LANGUAGE \n" @@ -121,35 +121,35 @@ msgstr "" msgid "Server disconnected" msgstr "" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:385 +#: /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:454 +#: /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:498 -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:499 +#: /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:513 +#: /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:525 +#: /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:615 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:664 #, c-format msgid "Unsupported socket type %d" msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po index fbadb83..dc054b0 100644 --- a/locale/ru/ru.po +++ b/locale/ru/ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2025-09-10 12:08+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 \n" "Language-Team: LANGUAGE \n" @@ -33,7 +33,7 @@ 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 "" +msgstr "Опции конфигурационного файла (формат: ключ=значение):\n" #: /home/eddy/Docs/SAO/C_diff/snippets_library/daemon.c:69 #, c-format @@ -120,36 +120,36 @@ msgstr " msgid "Server disconnected" msgstr "Сервер отключен" -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:385 +#: /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:454 +#: /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:498 -#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:499 +#: /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/socket.c:513 +#: /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:525 +#: /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:615 +#: /home/eddy/Docs/SAO/C_diff/snippets_library/socket.c:664 #, c-format msgid "Unsupported socket type %d" msgstr "Неподдерживаемый тип сокета %d" diff --git a/socket.c b/socket.c index e9e9f10..ccb1650 100644 --- a/socket.c +++ b/socket.c @@ -16,8 +16,9 @@ * along with this program. If not, see . */ -#include #include +#include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include // unix socket -#include #include #include "usefull_macros.h" @@ -345,10 +345,59 @@ 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; } @@ -826,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); @@ -841,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); @@ -855,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); @@ -868,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; +} diff --git a/usefull_macros.h b/usefull_macros.h index 3a5ecd8..c4f6db3 100644 --- a/usefull_macros.h +++ b/usefull_macros.h @@ -36,6 +36,7 @@ #include // pid_t // just for different purposes #include +#include // inf/isinf #include #if defined GETTEXT @@ -445,6 +446,15 @@ 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;