mirror of
https://github.com/eddyem/snippets_library.git
synced 2026-06-21 18:56:23 +03:00
751 lines
21 KiB
Markdown
751 lines
21 KiB
Markdown
# `libusefull_macros` - A collection of useful C snippets for Linux
|
|
|
|
**Version:** 0.3.5
|
|
**Author:** Edward V. Emelianov (<edward.emelianoff@gmail.com>)
|
|
**License:** GPLv3+
|
|
**Repository:** [github.com/eddyem/snippets_library](https://github.com/eddyem/snippets_library)
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Overview](#overview)
|
|
2. [Installation](#installation)
|
|
- [Building from source](#building-from-source)
|
|
- [CMake options](#cmake-options)
|
|
- [Linking](#linking)
|
|
3. [Quick Start](#quick-start)
|
|
4. [Module Reference](#module-reference)
|
|
- [Initialization & Locale](#initialization--locale)
|
|
- [Colored Terminal Output](#colored-terminal-output)
|
|
- [Error & Warning Macros](#error--warning-macros)
|
|
- [Memory Management](#memory-management)
|
|
- [String Utilities](#string-utilities)
|
|
- [Number Conversion](#number-conversion)
|
|
- [Console / Terminal I/O](#console--terminal-io)
|
|
- [Logging](#logging)
|
|
- [Command-line Argument Parsing](#command-line-argument-parsing)
|
|
- [Configuration Files](#configuration-files)
|
|
- [Daemon Support](#daemon-support)
|
|
- [FIFO / LIFO Linked List](#fifo--lifo-linked-list)
|
|
- [Ring Buffer](#ring-buffer)
|
|
- [TCP / UNIX Socket Server & Client](#tcp--unix-socket-server--client)
|
|
- [Serial Port (TTY)](#serial-port-tty)
|
|
- [Sub-options Parsing](#sub-options-parsing)
|
|
- [Miscellaneous Utilities](#miscellaneous-utilities)
|
|
5. [Data Structures](#data-structures)
|
|
6. [Examples](#examples)
|
|
7. [Internationalization (i18n)](#internationalization-i18n)
|
|
8. [Thread Safety](#thread-safety)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
`libusefull_macros` is a C shared library that bundles many frequently needed utility routines for
|
|
Linux application development. It covers:
|
|
|
|
- Colored, locale-aware terminal output
|
|
- Safe memory allocation and memory-mapped file I/O
|
|
- GNU `getopt_long`-style command-line argument parsing with type-safe callbacks
|
|
- INI-like configuration file reading/writing
|
|
- Daemonization with PID-file management
|
|
- Thread-safe ring buffer for producer-consumer patterns
|
|
- FIFO/LIFO linked list
|
|
- TCP and UNIX socket server/client framework with built-in HTTP parsing and key-value handler dispatch
|
|
- Serial port (TTY) management with non-standard baud rates
|
|
- File-based logging with multiple severity levels
|
|
- `gettext` integration for internationalization
|
|
|
|
All public identifiers are prefixed with `sl_` (for "snippets library") to avoid naming collisions.
|
|
|
|
---
|
|
|
|
## Installation
|
|
|
|
### Building from source
|
|
|
|
```bash
|
|
git clone https://github.com/eddyem/snippets_library.git
|
|
cd snippets_library
|
|
mkdir build && cd build
|
|
cmake ..
|
|
make -j$(nproc)
|
|
make install
|
|
```
|
|
|
|
The build produces a shared library `libusefull_macros.so` and a pkg-config file.
|
|
|
|
### CMake options
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `DEBUG=1` | off | Build with `-Wextra -Wall -Werror -W` and enable debug output |
|
|
| `EXAMPLES=1` | off | Build example programs in the `examples/` subdirectory |
|
|
| `NOGETTEXT` | not set | Disable gettext integration |
|
|
| `PROCESSOR_COUNT` | auto | Number of threads for parallel operations (default detects from `/proc/cpuinfo`) |
|
|
|
|
Example:
|
|
|
|
```bash
|
|
cmake .. -DDEBUG=1 -DEXAMPLES=1
|
|
```
|
|
|
|
### Linking
|
|
|
|
A pkg-config file is installed:
|
|
|
|
```bash
|
|
pkg-config --cflags --libs usefull_macros
|
|
```
|
|
|
|
Or manually:
|
|
|
|
```bash
|
|
gcc -o myapp myapp.c -I/usr/local/include -L/usr/local/lib -lusefull_macros -lm -lpthread
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
```c
|
|
#include <usefull_macros.h>
|
|
|
|
int main(int argc, char **argv) {
|
|
sl_init(); // locale, gettext, colored output
|
|
green("Hello, world!\n"); // green text on tty
|
|
red("An error occurred\n"); // red text on tty
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
Compile:
|
|
|
|
```bash
|
|
gcc -o hello hello.c $(pkg-config --cflags --libs usefull_macros)
|
|
```
|
|
|
|
---
|
|
|
|
## Module Reference
|
|
|
|
### Initialization & Locale
|
|
|
|
```c
|
|
void sl_init(void);
|
|
```
|
|
|
|
Must be called once at the beginning of `main()`. It:
|
|
|
|
- Detects whether `stdout`/`stderr` are terminals and sets up colored output functions accordingly.
|
|
- Calls `setlocale(LC_ALL, "")` and `setlocale(LC_NUMERIC, "C")` (decimal point is always a dot).
|
|
- If compiled with `GETTEXT` defined, binds the message domain.
|
|
|
|
**Important:** `sl_init()` must be called before any other library function that generates output.
|
|
|
|
---
|
|
|
|
### Colored Terminal Output
|
|
|
|
When output is a terminal (not redirected to a file or pipe), text can be printed in color:
|
|
|
|
```c
|
|
extern int (*red)(const char *fmt, ...);
|
|
extern int (*green)(const char *fmt, ...);
|
|
```
|
|
|
|
These function pointers are set by `sl_init()`. Use them like `printf`:
|
|
|
|
```c
|
|
red("Error code: %d\n", err);
|
|
green("Operation successful\n");
|
|
```
|
|
|
|
When output is not a tty, `red` wraps the message between lines of asterisks, and `green` falls
|
|
back to plain `printf`.
|
|
|
|
---
|
|
|
|
### Error & Warning Macros
|
|
|
|
```c
|
|
#define ERR(...) // print errno + message, then exit(9)
|
|
#define ERRX(...) // print message (no errno), then exit(9)
|
|
#define WARN(...) // print errno + message, continue
|
|
#define WARNX(...) // print message (no errno), continue
|
|
```
|
|
|
|
These use the `_WARN` function pointer (respects colored output). They automatically include the
|
|
current `errno` value when using `ERR`/`WARN`.
|
|
|
|
The default signal handler for `ERR`/`ERRX` is `signals(9)`, which simply calls `exit(9)`. You can
|
|
override the `signals` function since it is declared `__attribute__((weak))`:
|
|
|
|
```c
|
|
void signals(int sig) {
|
|
// custom cleanup
|
|
exit(sig);
|
|
}
|
|
```
|
|
|
|
**Debug macros** (active only when `-DEBUG` is defined):
|
|
|
|
```c
|
|
FNAME() // print current function name, file, line
|
|
DBG(...) // printf-like debug message
|
|
```
|
|
|
|
---
|
|
|
|
### Memory Management
|
|
|
|
```c
|
|
void *sl_alloc(size_t N, size_t S);
|
|
```
|
|
|
|
Safe `calloc` wrapper. Exits with error message if allocation fails.
|
|
|
|
Convenience macros:
|
|
|
|
```c
|
|
ALLOC(type, var, size) // declare + allocate: type *var = calloc(size, sizeof(type))
|
|
MALLOC(type, size) // allocate without declaration
|
|
FREE(ptr) // free and set to NULL
|
|
```
|
|
|
|
**Memory-mapped files:**
|
|
|
|
```c
|
|
typedef struct { char *data; size_t len; } sl_mmapbuf_t;
|
|
sl_mmapbuf_t *sl_mmap(char *filename);
|
|
void sl_munmap(sl_mmapbuf_t *b);
|
|
```
|
|
|
|
Maps a file read-only into memory; `sl_munmap` unmaps and frees the structure.
|
|
|
|
**System memory query:**
|
|
|
|
```c
|
|
uint64_t sl_mem_avail(void); // available physical memory in bytes
|
|
```
|
|
|
|
---
|
|
|
|
### String Utilities
|
|
|
|
```c
|
|
char *sl_omitspaces(const char *str); // skip leading whitespace
|
|
char *sl_omitspacesr(const char *str); // pointer to (last non-space char + 1)
|
|
int sl_remove_quotes(char *string); // remove outer matching quotes (' or ")
|
|
int sl_get_keyval(const char *pair, char key[32], char value[128]); // parse "key = value"
|
|
```
|
|
|
|
`sl_remove_quotes` strips matched pairs of single or double quotes from both ends. Returns the
|
|
number of pairs removed (0 if none).
|
|
|
|
`sl_get_keyval` parses a line into key and value:
|
|
- Returns `0` if the line is empty or a comment (starts with `#`).
|
|
- Returns `1` if only a key is present.
|
|
- Returns `2` if both key and value are found.
|
|
- Ignores inline comments, strips surrounding whitespace and quotes.
|
|
|
|
---
|
|
|
|
### Number Conversion
|
|
|
|
```c
|
|
int sl_str2i(int *num, const char *str);
|
|
int sl_str2ll(long long *num, const char *str);
|
|
int sl_str2d(double *num, const char *str);
|
|
```
|
|
|
|
Safe `strtol`/`strtod` wrappers. Return `TRUE` (1) on success, `FALSE` (0) on failure. The output
|
|
pointer may be `NULL` to only check validity.
|
|
|
|
---
|
|
|
|
### Console / Terminal I/O
|
|
|
|
For non-canonical, no-echo terminal input:
|
|
|
|
```c
|
|
void sl_setup_con(void); // switch terminal to raw mode
|
|
void sl_restore_con(void); // restore original terminal settings
|
|
int sl_read_con(void); // non-blocking read (0 if no key)
|
|
int sl_getchar(void); // blocking read of one character
|
|
```
|
|
|
|
Typical usage:
|
|
|
|
```c
|
|
sl_setup_con();
|
|
int ch = sl_getchar();
|
|
sl_restore_con();
|
|
```
|
|
|
|
**Important:** These functions are **not thread-safe** - they use a global `struct termios2`.
|
|
|
|
---
|
|
|
|
### Logging
|
|
|
|
```c
|
|
typedef enum {
|
|
LOGLEVEL_NONE, // no logging
|
|
LOGLEVEL_ERR, // only errors
|
|
LOGLEVEL_WARN, // warnings + errors
|
|
LOGLEVEL_MSG, // all except debug
|
|
LOGLEVEL_DBG, // all messages
|
|
LOGLEVEL_ANY // everything
|
|
} sl_loglevel_e;
|
|
|
|
sl_log_t *sl_createlog(const char *logpath, sl_loglevel_e level, int prefix);
|
|
void sl_deletelog(sl_log_t **log);
|
|
int sl_putlogt(int timest, sl_log_t *log, sl_loglevel_e lvl, const char *fmt, ...);
|
|
```
|
|
|
|
A "global" log is managed through the pointer `sl_globlog`:
|
|
|
|
```c
|
|
extern sl_log_t *sl_globlog;
|
|
```
|
|
|
|
Convenience macros (write to `sl_globlog`):
|
|
|
|
| Macro | Meaning |
|
|
|-------|---------|
|
|
| `OPENLOG(path, level, prefix)` | Open global log |
|
|
| `LOGERR(...)` | Error with timestamp |
|
|
| `LOGERRADD(...)` | Error without timestamp |
|
|
| `LOGWARN(...)` / `LOGWARNADD(...)` | Warning |
|
|
| `LOGMSG(...)` / `LOGMSGADD(...)` | Message |
|
|
| `LOGDBG(...)` / `LOGDBGADD(...)` | Debug |
|
|
|
|
Timestamps use format `YYYY/MM/DD-HH:MM:SS`. Each log call locks the file with `flock` for
|
|
concurrent access.
|
|
|
|
---
|
|
|
|
### Command-line Argument Parsing
|
|
|
|
Built on top of `getopt_long`. Supports:
|
|
|
|
- Short and long options
|
|
- Required, optional, and no arguments
|
|
- Multiple occurrences of the same option (multi-parameters)
|
|
- Six data types: `int`, `long long`, `double`, `float`, `char*`, and function callback
|
|
- Automatic help generation
|
|
|
|
**Option descriptor:**
|
|
|
|
```c
|
|
typedef struct {
|
|
const char *name; // long option (NULL for short-only)
|
|
sl_hasarg_e has_arg; // NO_ARGS, NEED_ARG, OPT_ARG, or MULT_PAR
|
|
int *flag; // NULL - return val; else set *flag = val
|
|
int val; // short option character or flag value
|
|
sl_argtype_e type; // arg_int, arg_longlong, arg_double, arg_float,
|
|
// arg_string, arg_function
|
|
void *argptr; // pointer to variable or callback function
|
|
const char *help; // help text (mandatory; end_option marks end)
|
|
} sl_option_t;
|
|
```
|
|
|
|
**Helper macro:**
|
|
|
|
```c
|
|
#define APTR(x) ((void*)x)
|
|
```
|
|
|
|
**Functions:**
|
|
|
|
```c
|
|
void sl_parseargs(int *argc, char ***argv, sl_option_t *options);
|
|
void sl_parseargs_hf(int *argc, char ***argv, sl_option_t *options,
|
|
void (*helpfun)(int, sl_option_t*));
|
|
void sl_showhelp(int oindex, sl_option_t *options);
|
|
void sl_helpstring(char *s); // customize help header
|
|
```
|
|
|
|
After calling `sl_parseargs`, `argc` and `argv` are updated to point to remaining non-option
|
|
arguments.
|
|
|
|
**Example:**
|
|
|
|
```c
|
|
int verbose = 0;
|
|
char *output = NULL;
|
|
sl_option_t opts[] = {
|
|
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&verbose), "increase verbosity"},
|
|
{"output", NEED_ARG, NULL, 'o', arg_string, APTR(&output), "output file"},
|
|
end_option
|
|
};
|
|
|
|
int main(int argc, char **argv) {
|
|
sl_init();
|
|
sl_parseargs(&argc, &argv, opts);
|
|
// argc, argv now contain non-option arguments
|
|
}
|
|
```
|
|
|
|
**Multi-parameters** (`MULT_PAR`): Options that may appear multiple times. The library allocates a
|
|
`NULL`-terminated array of pointers, each pointing to a newly allocated value. For `arg_string`,
|
|
each element is a pointer to a `strdup`'d string; for numeric types, each is a pointer to a
|
|
heap-allocated number.
|
|
|
|
**Function callback** (`arg_function`): The callback must have signature `int (*fn)(void *arg)` and
|
|
receives a `strdup`'d argument string.
|
|
|
|
**Custom help function:** Pass a function pointer to `sl_parseargs_hf` to handle errors differently
|
|
than the default `sl_showhelp` (which calls `exit(-1)`).
|
|
|
|
---
|
|
|
|
### Configuration Files
|
|
|
|
Reads key-value pairs from a file and treats them as command-line options.
|
|
|
|
```c
|
|
int sl_conf_readopts(const char *filename, sl_option_t *options);
|
|
char *sl_print_opts(sl_option_t *opt, int showall);
|
|
void sl_conf_showhelp(int idx, sl_option_t *options);
|
|
```
|
|
|
|
`sl_conf_readopts` reads a file with lines like:
|
|
|
|
```
|
|
# comment
|
|
key1 = value1
|
|
key2
|
|
key3 = "quoted value"
|
|
```
|
|
|
|
Each non-comment line is converted to `--key=value` (or `--key` if no value) and passed to
|
|
`sl_parseargs`. Returns the number of recognized options.
|
|
|
|
`sl_print_opts` generates a string representation of current option values (useful for debugging or
|
|
saving state). The returned string must be freed with `free()`.
|
|
|
|
---
|
|
|
|
### Daemon Support
|
|
|
|
```c
|
|
int sl_daemonize(void);
|
|
void sl_check4running(char *selfname, char *pidfilename);
|
|
char *sl_getPSname(pid_t pid);
|
|
void sl_iffound_deflt(pid_t pid); // WEAK - overridable
|
|
```
|
|
|
|
`sl_daemonize()`:
|
|
- `chdir("/")`
|
|
- `umask(0)`
|
|
- Closes stdin/stdout/stderr, reopens to `/dev/null`
|
|
- Ignores `SIGHUP`
|
|
- Returns 0 on success, -1 on failure
|
|
|
|
`sl_check4running()`:
|
|
- Checks a PID file and `/proc` for a running process with the same name.
|
|
- If found, calls `sl_iffound_deflt` (by default prints a message and exits).
|
|
- Otherwise writes its own PID to the PID file.
|
|
|
|
Override `sl_iffound_deflt` in your application (it is `__attribute__((weak))`):
|
|
|
|
```c
|
|
void sl_iffound_deflt(pid_t pid) {
|
|
fprintf(stderr, "Already running (pid %d)\n", pid);
|
|
exit(1);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### FIFO / LIFO Linked List
|
|
|
|
A simple singly-linked list with both head and tail pointers.
|
|
|
|
```c
|
|
typedef struct sl_buff_node {
|
|
void *data;
|
|
struct sl_buff_node *next, *last;
|
|
} sl_list_t;
|
|
|
|
sl_list_t *sl_list_push(sl_list_t **lst, void *v); // LIFO (push to head)
|
|
sl_list_t *sl_list_push_tail(sl_list_t **lst, void *v); // FIFO (push to tail)
|
|
void *sl_list_pop(sl_list_t **lst); // pop from head
|
|
```
|
|
|
|
`sl_list_pop` returns the data pointer and frees the node. The caller is responsible for freeing
|
|
the data if needed.
|
|
|
|
---
|
|
|
|
### Ring Buffer
|
|
|
|
A thread-safe, fixed-size ring buffer for byte streams, protected by `pthread_mutex_t`.
|
|
|
|
```c
|
|
typedef struct {
|
|
uint8_t *data;
|
|
size_t length, head, tail;
|
|
pthread_mutex_t busy;
|
|
} sl_ringbuffer_t;
|
|
|
|
sl_ringbuffer_t *sl_RB_new(size_t size);
|
|
void sl_RB_delete(sl_ringbuffer_t **b);
|
|
size_t sl_RB_read(sl_ringbuffer_t *b, uint8_t *s, size_t len);
|
|
ssize_t sl_RB_readto(sl_ringbuffer_t *b, uint8_t byte, uint8_t *s, size_t len);
|
|
ssize_t sl_RB_readline(sl_ringbuffer_t *b, char *s, size_t len);
|
|
int sl_RB_putbyte(sl_ringbuffer_t *b, uint8_t byte);
|
|
size_t sl_RB_write(sl_ringbuffer_t *b, const uint8_t *str, size_t len);
|
|
size_t sl_RB_writestr(sl_ringbuffer_t *b, char *s);
|
|
size_t sl_RB_datalen(sl_ringbuffer_t *b);
|
|
size_t sl_RB_freesize(sl_ringbuffer_t *b);
|
|
void sl_RB_clearbuf(sl_ringbuffer_t *b);
|
|
ssize_t sl_RB_hasbyte(sl_ringbuffer_t *b, uint8_t byte);
|
|
```
|
|
|
|
Key behaviors:
|
|
|
|
- `sl_RB_readline` reads up to and including a newline (`\n`), replaces `\n` with `\0`.
|
|
- `sl_RB_readto` reads until (and including) a specified byte.
|
|
- `sl_RB_writestr` ensures the string ends with `\n` before writing.
|
|
- All read/write operations are atomic with respect to the mutex.
|
|
|
|
---
|
|
|
|
### TCP / UNIX Socket Server & Client
|
|
|
|
A high-level socket framework supporting TCP and UNIX domain sockets, with built-in HTTP method
|
|
detection.
|
|
|
|
**Socket types:**
|
|
|
|
```c
|
|
typedef enum { SOCKT_UNIX, SOCKT_NETLOCAL, SOCKT_NET } sl_socktype_e;
|
|
```
|
|
|
|
**Creating and destroying:**
|
|
|
|
```c
|
|
sl_sock_t *sl_sock_run_server(sl_socktype_e type, const char *path,
|
|
int bufsiz, sl_sock_hitem_t *handlers);
|
|
sl_sock_t *sl_sock_run_client(sl_socktype_e type, const char *path, int bufsiz);
|
|
void sl_sock_delete(sl_sock_t **sock);
|
|
```
|
|
|
|
- `path` for UNIX sockets: file path; prefix with `\0` or `@` for abstract namespace.
|
|
- `path` for INET sockets: `"host:port"` (client) or `":port"` (server).
|
|
- `handlers`: `NULL`-terminated array of key-value handlers (see below).
|
|
- `bufsiz`: internal ring buffer size (minimum 256).
|
|
|
|
**Sending data:**
|
|
|
|
```c
|
|
ssize_t sl_sock_sendbinmessage(sl_sock_t *socket, const uint8_t *msg, size_t l);
|
|
ssize_t sl_sock_sendstrmessage(sl_sock_t *socket, const char *msg);
|
|
ssize_t sl_sock_sendbyte(sl_sock_t *socket, uint8_t byte);
|
|
int sl_sock_sendall(sl_sock_t *sock, uint8_t *data, size_t len); // server only
|
|
```
|
|
|
|
**Reading (client):**
|
|
|
|
```c
|
|
ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len);
|
|
```
|
|
|
|
**Handler dispatch (server):**
|
|
|
|
```c
|
|
typedef sl_sock_hresult_e (*sl_sock_msghandler)(struct sl_sock *s,
|
|
struct sl_sock_hitem *item, const char *val);
|
|
|
|
typedef struct sl_sock_hitem {
|
|
sl_sock_msghandler handler;
|
|
const char *key;
|
|
const char *help;
|
|
void *data; // user data (e.g., &variable)
|
|
} sl_sock_hitem_t;
|
|
```
|
|
|
|
Handler results:
|
|
|
|
```c
|
|
typedef enum {
|
|
RESULT_OK, RESULT_FAIL, RESULT_BADVAL,
|
|
RESULT_BADKEY, RESULT_SILENCE
|
|
} sl_sock_hresult_e;
|
|
```
|
|
|
|
Built-in handlers for common types:
|
|
|
|
```c
|
|
sl_sock_hresult_e sl_sock_inthandler(...); // int64_t
|
|
sl_sock_hresult_e sl_sock_dblhandler(...); // double
|
|
sl_sock_hresult_e sl_sock_strhandler(...); // string
|
|
```
|
|
|
|
**Optional key numbering** (`key[0]`, `key(1)`, `key{2}`, `key3`):
|
|
|
|
```c
|
|
typedef struct { double magick; int n; } sl_sock_keyno_t;
|
|
#define SL_SOCK_KEYNO_DEFAULT { .magick = -INFINITY, .n = -1 }
|
|
void sl_sock_keyno_init(sl_sock_keyno_t *k);
|
|
int sl_sock_keyno_check(sl_sock_keyno_t *k);
|
|
```
|
|
|
|
**Server hooks:**
|
|
|
|
```c
|
|
void sl_sock_changemaxclients(sl_sock_t *sock, int val);
|
|
void sl_sock_maxclhandler(sl_sock_t *sock, void (*h)(int));
|
|
void sl_sock_connhandler(sl_sock_t *sock, int (*h)(struct sl_sock*));
|
|
void sl_sock_dischandler(sl_sock_t *sock, void (*h)(struct sl_sock*));
|
|
void sl_sock_defmsghandler(sl_sock_t *sock, sl_sock_hresult_e (*h)(struct sl_sock*, const char*));
|
|
```
|
|
|
|
The server thread automatically handles `POLLIN` events, parses messages using `sl_get_keyval`, and
|
|
dispatches them to matching handlers. HTTP `GET`/`POST` requests are partially parsed: `GET`
|
|
parameters are URL-decoded and dispatched; `POST` data is accumulated and then parsed.
|
|
|
|
---
|
|
|
|
### Serial Port (TTY)
|
|
|
|
```c
|
|
typedef struct {
|
|
char *portname;
|
|
int speed;
|
|
char *format; // e.g., "8N1"
|
|
int comfd;
|
|
char *buf;
|
|
size_t bufsz, buflen;
|
|
int exclusive;
|
|
} sl_tty_t;
|
|
|
|
int sl_tty_fdescr(const char *comdev, const char *format, int speed, int exclusive);
|
|
sl_tty_t *sl_tty_new(char *comdev, int speed, size_t bufsz);
|
|
int sl_tty_setformat(sl_tty_t *d, const char *format);
|
|
sl_tty_t *sl_tty_open(sl_tty_t *d, int exclusive);
|
|
int sl_tty_read(sl_tty_t *d);
|
|
int sl_tty_write(int comfd, const char *buff, size_t length);
|
|
void sl_tty_close(sl_tty_t **descr);
|
|
int sl_tty_tmout(double usec);
|
|
```
|
|
|
|
Format string: three characters - data bits (5-8), parity (N/E/O/0/1), stop bits (1/2). Example:
|
|
`"8N1"`.
|
|
|
|
Uses `struct termios2` via `ioctl(TCGETS2/TCSETS2)` to support arbitrary baud rates (not limited to
|
|
the standard `Bxxx` constants).
|
|
|
|
`sl_tty_read` uses `select()` with a configurable timeout (default 5 ms, change with
|
|
`sl_tty_tmout`). Returns the number of bytes read; data is placed in `d->buf` with length
|
|
`d->buflen`.
|
|
|
|
`sl_tty_fdescr` allows to use library functions for opening serial device with given path, format string,
|
|
non-standard speed, marking it as exclusive (not share with other processes) or not. It doesn't allocates
|
|
memory and just returns opened tty file descriptor or `-1` in case of error.
|
|
|
|
|
|
---
|
|
|
|
### Sub-options Parsing
|
|
|
|
Parses strings like `key1=val1:key2=val2,key3`:
|
|
|
|
```c
|
|
typedef struct {
|
|
const char *name;
|
|
sl_hasarg_e has_arg;
|
|
sl_argtype_e type;
|
|
void *argptr;
|
|
} sl_suboption_t;
|
|
|
|
int sl_get_suboption(char *str, sl_suboption_t *opt);
|
|
```
|
|
|
|
The input string is tokenized on `:` and `,`; each token is matched against option names
|
|
(case-insensitive).
|
|
|
|
---
|
|
|
|
### Miscellaneous Utilities
|
|
|
|
```c
|
|
const char *sl_libversion(void); // returns PACKAGE_VERSION string
|
|
double sl_dtime(void); // UNIX time as double (seconds)
|
|
long sl_random_seed(void); // seed from /dev/random or time
|
|
int sl_canread(int fd); // non-blocking select() for read
|
|
int sl_canwrite(int fd); // non-blocking select() for write
|
|
```
|
|
|
|
---
|
|
|
|
## Data Structures
|
|
|
|
| Structure | Purpose |
|
|
|-----------|---------|
|
|
| `sl_option_t` | Command-line option descriptor |
|
|
| `sl_suboption_t` | Sub-option descriptor |
|
|
| `sl_tty_t` | Serial port state |
|
|
| `sl_log_t` | Log file descriptor |
|
|
| `sl_mmapbuf_t` | Memory-mapped file |
|
|
| `sl_list_t` | Linked list node |
|
|
| `sl_ringbuffer_t` | Thread-safe ring buffer |
|
|
| `sl_sock_t` | Socket state (client or server) |
|
|
| `sl_sock_hitem_t` | Socket handler item |
|
|
| `sl_sock_int_t` | Timestamped `int64_t` |
|
|
| `sl_sock_double_t` | Timestamped `double` |
|
|
| `sl_sock_string_t` | Timestamped string |
|
|
| `sl_sock_keyno_t` | Optional key number |
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
The repository includes several example programs in the `examples/` directory:
|
|
|
|
| Example | Demonstrates |
|
|
|---------|-------------|
|
|
| `helloworld` | Minimal usage: `sl_init`, colored output, `sl_setup_con`/`sl_getchar`/`sl_restore_con` |
|
|
| `options` + `cmdlnopts` | Full command-line parsing with all types, logging, serial port, signals |
|
|
| `conffile` | Configuration file reading, `sl_print_opts`, multi-parameters |
|
|
| `fifo` | LIFO and FIFO list operations |
|
|
| `ringbuffer` | Ring buffer creation, line reading, overflow handling |
|
|
| `clientserver` | Socket server/client with custom handlers, bit flags, logging |
|
|
| `daemon` | Daemonization, PID file, child process monitoring |
|
|
|
|
Build examples with:
|
|
|
|
```bash
|
|
cmake .. -DEXAMPLES=1
|
|
make
|
|
```
|
|
|
|
---
|
|
|
|
## Internationalization (i18n)
|
|
|
|
If compiled with `GETTEXT` defined, the `_()` macro wraps `gettext()`. Translation files are
|
|
expected in the `locale/` directory. The library generates `.po` and `.mo` files during the build
|
|
(in Debug mode). To disable, define `NOGETTEXT`.
|
|
|
|
```c
|
|
#define _(String) gettext(String) // when GETTEXT is defined
|
|
#define _(String) (String) // otherwise
|
|
```
|
|
|
|
---
|
|
|
|
## Thread Safety
|
|
|
|
- **Ring buffer:** all operations are protected by a `pthread_mutex_t`.
|
|
- **Logging:** file writes are guarded with `flock(LOCK_EX)`.
|
|
- **Sockets:** server thread uses `poll()`; client read thread is separate; send operations lock the socket mutex.
|
|
- **Console I/O:** `sl_setup_con`/`sl_read_con`/`sl_getchar`/`sl_restore_con` are **not** thread-safe (global terminal state).
|
|
|
|
---
|
|
|