Version: 0.3.5
Author: Edward V. Emelianov (edward.emelianoff@gmail.com)
License: GPLv3+
Repository: github.com/eddyem/snippets_library
- Overview
- Installation
- Quick Start
- Module Reference
- Initialization & Locale
- Colored Terminal Output
- Error & Warning Macros
- Memory Management
- String Utilities
- Number Conversion
- Console / Terminal I/O
- Logging
- Command-line Argument Parsing
- Configuration Files
- Daemon Support
- FIFO / LIFO Linked List
- Ring Buffer
- TCP / UNIX Socket Server & Client
- Serial Port (TTY)
- Sub-options Parsing
- Miscellaneous Utilities
- Data Structures
- Examples
- Internationalization (i18n)
- Thread Safety
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
gettextintegration for internationalization
All public identifiers are prefixed with sl_ (for "snippets library") to avoid naming collisions.
git clone https://github.com/eddyem/snippets_library.git
cd snippets_library
mkdir build && cd build
cmake ..
make -j$(nproc)
make installThe build produces a shared library libusefull_macros.so and a pkg-config file.
| 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:
cmake .. -DDEBUG=1 -DEXAMPLES=1A pkg-config file is installed:
pkg-config --cflags --libs usefull_macrosOr manually:
gcc -o myapp myapp.c -I/usr/local/include -L/usr/local/lib -lusefull_macros -lm -lpthread#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:
gcc -o hello hello.c $(pkg-config --cflags --libs usefull_macros)void sl_init(void);Must be called once at the beginning of main(). It:
- Detects whether
stdout/stderrare terminals and sets up colored output functions accordingly. - Calls
setlocale(LC_ALL, "")andsetlocale(LC_NUMERIC, "C")(decimal point is always a dot). - If compiled with
GETTEXTdefined, binds the message domain.
Important: sl_init() must be called before any other library function that generates output.
When output is a terminal (not redirected to a file or pipe), text can be printed in color:
extern int (*red)(const char *fmt, ...);
extern int (*green)(const char *fmt, ...);These function pointers are set by sl_init(). Use them like printf:
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.
#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), continueThese 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)):
void signals(int sig) {
// custom cleanup
exit(sig);
}Debug macros (active only when -DEBUG is defined):
FNAME() // print current function name, file, line
DBG(...) // printf-like debug messagevoid *sl_alloc(size_t N, size_t S);Safe calloc wrapper. Exits with error message if allocation fails.
Convenience macros:
ALLOC(type, var, size) // declare + allocate: type *var = calloc(size, sizeof(type))
MALLOC(type, size) // allocate without declaration
FREE(ptr) // free and set to NULLMemory-mapped files:
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:
uint64_t sl_mem_avail(void); // available physical memory in byteschar *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
0if the line is empty or a comment (starts with#). - Returns
1if only a key is present. - Returns
2if both key and value are found. - Ignores inline comments, strips surrounding whitespace and quotes.
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.
For non-canonical, no-echo terminal input:
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 characterTypical usage:
sl_setup_con();
int ch = sl_getchar();
sl_restore_con();Important: These functions are not thread-safe - they use a global struct termios2.
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:
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.
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:
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:
#define APTR(x) ((void*)x)Functions:
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 headerAfter calling sl_parseargs, argc and argv are updated to point to remaining non-option
arguments.
Example:
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)).
Reads key-value pairs from a file and treats them as command-line options.
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().
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 - overridablesl_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
/procfor 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))):
void sl_iffound_deflt(pid_t pid) {
fprintf(stderr, "Already running (pid %d)\n", pid);
exit(1);
}A simple singly-linked list with both head and tail pointers.
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 headsl_list_pop returns the data pointer and frees the node. The caller is responsible for freeing
the data if needed.
A thread-safe, fixed-size ring buffer for byte streams, protected by pthread_mutex_t.
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_readlinereads up to and including a newline (\n), replaces\nwith\0.sl_RB_readtoreads until (and including) a specified byte.sl_RB_writestrensures the string ends with\nbefore writing.- All read/write operations are atomic with respect to the mutex.
A high-level socket framework supporting TCP and UNIX domain sockets, with built-in HTTP method detection.
Socket types:
typedef enum { SOCKT_UNIX, SOCKT_NETLOCAL, SOCKT_NET } sl_socktype_e;Creating and destroying:
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);pathfor UNIX sockets: file path; prefix with\0or@for abstract namespace.pathfor 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:
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 onlyReading (client):
ssize_t sl_sock_readline(sl_sock_t *sock, char *str, size_t len);Handler dispatch (server):
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:
typedef enum {
RESULT_OK, RESULT_FAIL, RESULT_BADVAL,
RESULT_BADKEY, RESULT_SILENCE
} sl_sock_hresult_e;Built-in handlers for common types:
sl_sock_hresult_e sl_sock_inthandler(...); // int64_t
sl_sock_hresult_e sl_sock_dblhandler(...); // double
sl_sock_hresult_e sl_sock_strhandler(...); // stringOptional key numbering (key[0], key(1), key{2}, key3):
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:
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.
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.
Parses strings like key1=val1:key2=val2,key3:
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).
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| 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 |
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:
cmake .. -DEXAMPLES=1
makeIf 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.
#define _(String) gettext(String) // when GETTEXT is defined
#define _(String) (String) // otherwise- 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_conare not thread-safe (global terminal state).