Skip to content

About

The common library for my most usefull snippets

Topics

Resources

Stars

4 stars

Watchers

2 watching

Forks

Repository files navigation

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


Table of Contents

  1. Overview
  2. Installation
  3. Quick Start
  4. Module Reference
  5. Data Structures
  6. Examples
  7. Internationalization (i18n)
  8. 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

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:

cmake .. -DDEBUG=1 -DEXAMPLES=1

Linking

A pkg-config file is installed:

pkg-config --cflags --libs usefull_macros

Or manually:

gcc -o myapp myapp.c -I/usr/local/include -L/usr/local/lib -lusefull_macros -lm -lpthread

Quick Start

#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)

Module Reference

Initialization & Locale

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:

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.


Error & Warning Macros

#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)):

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 message

Memory Management

void *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 NULL

Memory-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 bytes

String Utilities

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

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:

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:

sl_setup_con();
int ch = sl_getchar();
sl_restore_con();

Important: These functions are not thread-safe - they use a global struct termios2.


Logging

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.


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:

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 header

After 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)).


Configuration Files

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().


Daemon Support

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))):

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.

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.

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:

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);
  • 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:

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):

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;

Field help allows you to show help message for key when user sends "help" message, format: "key: help". All items shown are strictly in user's order.

Field and key could be NULL, in this case "help" for this item will just send string in help. The handler in this case should be inited with sl_comment.

Field data is for internal user's usage or for key numbering (see later). End of list is handler == NULL.

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(...);  // string

Optional key numbering (key[0], key(1), key{2}, key3):

In this case you should init field data in sl_sock_hitem_t with pointer to sl_sock_keyno_t. Then the appropriate handler called with sl_sock_keyno_t->n inited with this number.

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.


Serial Port (TTY)

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:

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

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

Sorting helpers

With qsort() you can use these functions:

  • sl_cmpchar - sort array of chars;
  • sl_cmpint - sort array of integers;
  • sl_cmpdouble - sort array of doubles;
  • sl_cmpstring - sort array of char*;
  • sl_cmpstringp - sort array of char**.

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:

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.

#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).

About

The common library for my most usefull snippets

Topics

Resources

Stars

4 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages