Compare commits

..
9 Commits
Author SHA1 Message Date
elias 2f5c10cf5d Fix config and file modules, add debug output 2019-07-29 11:30:11 +02:00
elias 17ce2c5753 Add config module 2019-07-24 17:11:31 +02:00
elias 93234df78d Add cstuff_file_get_lines function 2019-07-17 22:53:55 +02:00
elias 4ba00ca17a Add file module with calls for daily file operations 2019-06-28 15:58:01 +02:00
elias 0d6fca9b26 Add list module 2019-06-24 07:40:51 +02:00
elias cf2535c9e5 Correct definitions 2019-06-24 07:40:41 +02:00
elias 35c8b96967 Add string module 2019-06-19 21:04:31 +02:00
elias c85c6cc819 Add log module 2019-06-19 21:04:14 +02:00
elias d72aebcd09 Add retcode.h 2019-06-19 21:03:58 +02:00
11 changed files with 1094 additions and 0 deletions
+289
View File
@@ -0,0 +1,289 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file config.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdbool.h>
#include <cStuff/file.h>
#include <cStuff/config.h>
static const char
m_mixed_line_indent[] = "Line indent contains tabs and spaces"
, m_incomplete_key[] = "Incomplete key"
, m_parser_error[] = "Parser error"
, m_indent_error[] = "Indent error"
, m_out_of_memory[] = "Out of memory"
;
struct config_path {
char *key;
int size;
int indent;
int level;
bool is_node;
};
struct config_ctx {
struct cstuff_config cfg;
struct config_path path;
CStuffConfigCallback callback;
void *u_ptr;
};
static int
config_get_indent(char *line)
{
char chr = 0, c;
int result = 0;
while ((c = *line) != '\0') {
if (c == ' ' || c == '\t') {
if (!chr) {
chr = c;
}
if (chr == c) {
result++;
} else {
/* mixed indent */
return -1;
}
} else {
break;
}
line++;
}
return result;
}
static int
config_read_line(char *line, ssize_t length, struct cstuff_config *cfg)
{
const char *err = NULL;
CStuffConfigEvent evt = CSTUFF_CONFIG_NONE;
ssize_t i = 0;
int k_len = 0, v_len = 0, indent = 0;
char c, *key = NULL, *val = NULL;
cfg->line_num++;
fprintf(stderr, "cfg:%d ", cfg->line_num);
if ((indent = config_get_indent(line)) != -1) {
fprintf(stderr, "indent=%d ", indent);
key = &line[indent];
for (i = indent; i < length; i++) {
c = line[i];
if (isalnum(c) || c == '_' || c == '-') {
k_len++;
continue;
}
break;
}
while (i < length) {
switch ((c = line[i++])) {
case ':':
evt = CSTUFF_CONFIG_NODE;
fprintf(stderr, "node! ");
continue;
case '=':
evt = CSTUFF_CONFIG_PAIR;
fprintf(stderr, "pair! ");
continue;
case ' ':
case '\t':
case '\n':
continue;
default:
break;
}
break;
}
if (evt == CSTUFF_CONFIG_NODE) {
if (line[i] && line[i] != ';') {
evt = CSTUFF_CONFIG_ERROR;
err = m_incomplete_key;
}
} else if (evt == CSTUFF_CONFIG_PAIR) {
val = &line[i - 1];
while (i < length) {
switch (line[i++]) {
case ';':
case '\n':
break;
case ' ':
case '\t':
continue;
default:
v_len = (int) (&line[i] - val) - 1;
continue;
}
break;
}
} else {
evt = CSTUFF_CONFIG_ERROR;
err = m_parser_error;
}
} else {
evt = CSTUFF_CONFIG_ERROR;
err = m_mixed_line_indent;
}
/* Key ! */
if (key)
key[k_len] = 0;
fprintf(stderr, "\n");
switch ((cfg->evt = evt)) {
case CSTUFF_CONFIG_PAIR:
cfg->data.pair.key = key;
cfg->data.pair.val = val;
cfg->data.pair.val_len = v_len;
cfg->data.pair.key_len = k_len;
val[v_len] = 0;
return indent;
case CSTUFF_CONFIG_NODE:
cfg->data.node.name = key;
cfg->data.node.name_len = k_len;
return indent;
case CSTUFF_CONFIG_ERROR:
default:
cfg->data.error.char_num = i;
cfg->data.error.line_txt = line;
cfg->data.error.err_txt = err;
return -1;
}
}
static void
config_set_path_key(struct config_ctx *ctx, int line_indent, bool is_node)
{
struct config_path *path = &ctx->path;
const char *key = ctx->cfg.data.pair.key;
char *ptr;
int i, len, level, k_len = ctx->cfg.data.pair.key_len;
if (line_indent) {
if (path->indent) {
level = line_indent / path->indent;
} else {
level = 1;
path->indent = line_indent;
}
} else {
path->indent = 0;
level = 0;
}
if (level > path->level && (!path->is_node ||
level - path->level != path->indent)) {
fprintf(stderr, "cfg:%d ((%d > %d && !%d) || %d - %d != %d)\n",
ctx->cfg.line_num, level,
path->level, path->is_node & 0xFF, level, path->level, path->indent);
ctx->cfg.evt = CSTUFF_CONFIG_ERROR;
ctx->cfg.data.error.line_txt = NULL;
ctx->cfg.data.error.err_txt = m_indent_error;
ctx->cfg.data.error.char_num = 0;
return;
}
ptr = ctx->path.key;
for (i = 0; i < level; i++) {
int l = strcspn(ptr, ".");
ptr += l;
fprintf(stderr, "l = %d\n", l);
}
len = (ptr - ctx->path.key) + 1 + k_len + 1;
if (len > ctx->path.size) {
char *new_key;
if (!(new_key = realloc(ctx->path.key, len))) {
ctx->cfg.evt = CSTUFF_CONFIG_ERROR;
ctx->cfg.data.error.line_txt = NULL;
ctx->cfg.data.error.err_txt = m_out_of_memory;
ctx->cfg.data.error.char_num = 0;
return;
}
ctx->path.key = new_key;
ctx->path.size = len;
}
if (level) {
*(ptr++) = '.';
} else {
*ptr = 0;
}
strcpy(ptr, key);
path->level = level;
path->is_node = is_node;
ctx->cfg.data.pair.key = ctx->path.key;
}
static int
config_get_line(char *line, ssize_t length, void *p_ctx)
{
int result;
struct config_ctx *ctx = (struct config_ctx *)p_ctx;
memset(&ctx->cfg.data, 0, sizeof (ctx->cfg.data));
if ((result = config_read_line(line, length, &ctx->cfg)) != -1) {
bool is_node = (ctx->cfg.evt == CSTUFF_CONFIG_NODE);
/* result == indent */
config_set_path_key(ctx, result, is_node);
} else {
fprintf(stderr, "cfg: read line failed %d\n", result);
}
result = ctx->callback(&ctx->cfg, ctx->u_ptr);
return result;
}
CStuffRetcode
cstuff_config_parse(const char *file, CStuffConfigCallback callback, void *u_ptr)
{
CStuffRetcode rc;
struct config_ctx ctx;
memset(&ctx.cfg, 0, sizeof (ctx.cfg));
memset(&ctx.path, 0, sizeof (ctx.path));
if (!(ctx.path.key = malloc(CSTUFF_CONFIG_PATH_SIZE))) {
return CSTUFF_MALLOC_ERROR;
} else {
ctx.path.size = CSTUFF_CONFIG_PATH_SIZE;
}
ctx.u_ptr = u_ptr;
ctx.callback = callback;
rc = cstuff_file_get_lines(file, config_get_line, (void *)&ctx);
if (rc) {
fprintf(stderr, "cfg: get lines failed %d (%s)\n", rc, strerror(errno));
}
if (ctx.path.key)
free(ctx.path.key);
return rc;
}
+72
View File
@@ -0,0 +1,72 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file config.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*/
#include "retcode.h"
#ifndef CSTUFF_CONFIG_H_6207A85E_26F1_4B64_818F_92C6286FC5F0
#define CSTUFF_CONFIG_H_6207A85E_26F1_4B64_818F_92C6286FC5F0
#ifndef CSTUFF_CONFIG_PATH_SIZE
#define CSTUFF_CONFIG_PATH_SIZE 64
#endif
typedef enum {
CSTUFF_CONFIG_NONE
, CSTUFF_CONFIG_NODE
, CSTUFF_CONFIG_PAIR
, CSTUFF_CONFIG_ERROR
} CStuffConfigEvent;
struct cstuff_config_node {
const char *name;
int name_len;
};
struct cstuff_config_pair {
const char *key;
const char *val;
int key_len;
int val_len;
};
struct cstuff_config_error {
const char *line_txt;
const char *err_txt;
int char_num;
};
struct cstuff_config {
CStuffConfigEvent evt;
int line_num;
union {
struct cstuff_config_node node;
struct cstuff_config_pair pair;
struct cstuff_config_error error;
} data;
};
typedef struct cstuff_config * CStuffConfig;
typedef int
(* CStuffConfigCallback)(struct cstuff_config *config, void *u_ptr);
CStuffRetcode
cstuff_config_parse(const char *file, CStuffConfigCallback callback, void *u_ptr);
#endif /* !CSTUFF_CONFIG_H */
+141
View File
@@ -0,0 +1,141 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file file.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*/
#include <stdio.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include "file.h"
bool
cstuff_file_exists(const char *file)
{
return ( access( file, F_OK ) != -1 ) ? true : false;
}
CStuffRetcode
cstuff_file_copy(const char *src, const char *dest)
{
CStuffRetcode rc = CSTUFF_SYSCALL_ERROR;
int fd_src = open(src, O_RDWR), fd_dest, len;
struct stat src_stat;
if (!(fd_src < 0)) {
if (lockf(fd_src, F_LOCK, 0) != -1) {
if (!stat(src, &src_stat)) {
fd_dest = open(dest, O_CREAT | O_WRONLY | O_TRUNC);
if (!(fd_dest < 0)) {
len = sendfile(fd_dest, fd_src, NULL, src_stat.st_size);
if (len == src_stat.st_size) {
rc = CSTUFF_SUCCESS;
} else {
fprintf(stderr, "sendfile error (%d != %d): %s\n", len,
(int)src_stat.st_size, strerror(errno));
}
close(fd_dest);
} else {
fprintf(stderr, "dest open() error\n");
}
} else {
fprintf(stderr, "src stat() error\n");
}
if (lockf(fd_src, F_TEST, 0) == 0){
lockf(fd_src, F_ULOCK, 0);
}
} else {
fprintf(stderr, "src lockf() error\n");
}
close(fd_src);
} else {
fprintf(stderr, "src open() error\n");
}
return rc;
}
CStuffRetcode
cstuff_file_move(const char *src, const char *dest)
{
CStuffRetcode rc;
if (!(rc = cstuff_file_copy(src, dest))) {
if (unlink(src) != 0)
return CSTUFF_SYSCALL_ERROR;
}
return rc;
}
CStuffRetcode
cstuff_file_get_lines(const char *src, CStuffFileGetLine get_line, void *ctx)
{
FILE *f = NULL;
size_t sz = 256;
char *line;
ssize_t rs;
CStuffRetcode result = CSTUFF_SUCCESS;
if (!(line = malloc(sz)))
goto e_malloc;
if (!(f = fopen(src, "r")))
goto e_syscall;
while ((rs = getdelim(&line, &sz, '\n', f)) != -1) {
if (!get_line(line, rs, ctx)) {
continue;
} else {
goto e_input;
}
}
switch(errno) {
case 0:
break;
case ENOMEM:
goto e_malloc;
default:
goto e_syscall;
}
goto finally;
e_input:
result = CSTUFF_INPUT_ERROR;
goto finally;
e_malloc:
result = CSTUFF_MALLOC_ERROR;
goto finally;
e_syscall:
result = CSTUFF_SYSCALL_ERROR;
goto finally;
finally:
if (line)
free(line);
if (f)
fclose(f);
return result;
}
+42
View File
@@ -0,0 +1,42 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file file.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief
*
* @see https://lowenware.com/
*/
#ifndef CSTUFF_FILE_H_3C3D5EDC_A624_4550_98A4_29AD5A41301D
#define CSTUFF_FILE_H_3C3D5EDC_A624_4550_98A4_29AD5A41301D
#include <stdbool.h>
#include <sys/types.h>
#include "retcode.h"
typedef int
(*CStuffFileGetLine)(char *line, ssize_t length, void *ctx);
bool
cstuff_file_exists(const char *file);
CStuffRetcode
cstuff_file_copy(const char *src, const char *dest);
CStuffRetcode
cstuff_file_move(const char *src, const char *dest);
CStuffRetcode
cstuff_file_get_lines(const char *src, CStuffFileGetLine get_line, void *ctx);
#endif /* !FILE_H */
+132
View File
@@ -0,0 +1,132 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file list.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief cStuff list module
*
* @see https://lowenware.com/
*/
#include <stdlib.h>
#include <string.h>
#include "list.h"
CStuffRetcode
cstuff_list_init(CStuffList self, int size)
{
if (!(self->items = calloc(size, sizeof (void *))))
return CSTUFF_MALLOC_ERROR;
self->length = 0;
self->size = size;
return CSTUFF_SUCCESS;
}
void
cstuff_list_release(CStuffList self, CStuffListFree item_free)
{
if (self->items) {
if (item_free) {
int i;
for (i = 0; i < self->length; i++) {
void *ptr = self->items[i];
if (ptr) {
item_free(ptr);
}
}
}
free(self->items);
}
}
int
cstuff_list_append(CStuffList self, void *item)
{
void **items;
int size = self->length + 1;
if (size > self->size) {
if (!(items = realloc(self->items, size * sizeof (void*))))
return -1;
self->items = items;
self->size = size;
}
self->items[self->length] = item;
return self->length++;
}
int
cstuff_list_insert(CStuffList self, void *item, int index)
{
int r;
if ((r = cstuff_list_append(self, item)) == -1)
return -1;
if (index >= r || index < 0)
return r;
memmove(&self->items[index + 1], &self->items[index],
(self->length - index - 1) * sizeof (void*));
self->items[index] = item;
return index;
}
void *
cstuff_list_remove(CStuffList self, int index)
{
void *result;
int i;
if (index < self->length) {
result = self->items[index];
self->length--;
for (i = index; i < self->length; i++) {
self->items[i] = self->items[i + 1];
}
} else {
result = NULL;
}
return result;
}
void
cstuff_list_remove_item(CStuffList self, void *item)
{
int i;
for (i = 0; i < self->length; i++) {
if (self->items[i] == item) {
cstuff_list_remove(self, i);
break;
}
}
}
void
cstuff_list_set_item(CStuffList self, int index, void *value)
{
if (self->size > index) {
self->items[index] = value;
if (index >= self->length)
self->length = index + 1;
}
}
+64
View File
@@ -0,0 +1,64 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file list.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief cStuff List module header file
*
* @see https://lowenware.com/
*/
#ifndef CSTUFF_LIST_H_372E2D14_8DEE_4A7C_A5AB_8808A87DF436
#define CSTUFF_LIST_H_372E2D14_8DEE_4A7C_A5AB_8808A87DF436
#include "retcode.h"
#define cstuff_list_index(LIST, IDX) (LIST->items[IDX])
struct cstuff_list {
void **items;
int length;
int size;
};
typedef struct cstuff_list * CStuffList;
typedef void
(*CStuffListFree)(void *item);
CStuffRetcode
cstuff_list_init(CStuffList self, int size);
void
cstuff_list_release(CStuffList self, CStuffListFree item_free);
int
cstuff_list_append(CStuffList self, void *item);
int
cstuff_list_insert(CStuffList self, void *item, int index);
void *
cstuff_list_remove(CStuffList self, int index);
void
cstuff_list_remove_item(CStuffList self, void *item);
void
cstuff_list_set_item(CStuffList self, int index, void *value);
#endif /* !LIST_H */
+103
View File
@@ -0,0 +1,103 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file log.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief cStuff log module source file
*
* @see https://lowenware.com/
*/
#include <time.h>
#include "log.h"
void
cstuff_log_init(CStuffLog self, int level)
{
self->target = stderr;
self->level = level;
}
void
cstuff_log_release(CStuffLog self)
{
if (self->target != stderr)
fclose(self->target);
}
CStuffRetcode
cstuff_log_set_file(CStuffLog self, const char *filename)
{
FILE *fp;
if (!(fp = fopen(filename, "a")))
return CSTUFF_SYSCALL_ERROR;
self->target = fp;
return CSTUFF_SUCCESS;
}
int
cstuff_log_printf(CStuffLog self, int level, const char *format, ...)
{
int result;
va_list vl;
va_start(vl, format);
result = cstuff_log_vprintf(self, level, format, vl);
va_end(vl);
return result;
}
int
cstuff_log_vprintf(CStuffLog self, int level, const char *format, va_list vl)
{
int result;
if (self->level & level) {
FILE *f = self->target;
char *label;
time_t rawtime;
char strtime[32+1];
struct tm timeinfo;
time(&rawtime);
localtime_r(&rawtime, &timeinfo);
strftime(strtime, 32, "%F %T", &timeinfo);
switch (level)
{
case CSTUFF_LOG_ERROR:
label = "!!";
break;
case CSTUFF_LOG_ALERT:
label = "~!";
break;
case CSTUFF_LOG_DEBUG:
label = "**";
break;
default:
label = "--";
}
result = fprintf(f, "%s %s ", strtime, label);
result += vfprintf(f, format, vl);
fputc('\n', f);
result++;
fflush(f);
} else {
result = 0;
}
return result;
}
+58
View File
@@ -0,0 +1,58 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file log.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief cStuff log module header file
*
* @see https://lowenware.com/cStuff/
*/
#ifndef CSTUFF_LOG_H_557ADEFE_2BE8_4B9B_A894_F08FF64008EF
#define CSTUFF_LOG_H_557ADEFE_2BE8_4B9B_A894_F08FF64008EF
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include "retcode.h"
#define CSTUFF_LOG_OFF (0)
#define CSTUFF_LOG_ERROR (1<<0)
#define CSTUFF_LOG_ALERT (1<<1)
#define CSTUFF_LOG_STATE (1<<2)
#define CSTUFF_LOG_DEBUG (1<<3)
#define CSTUFF_LOG_ALL (0xFF)
struct cstuff_log {
FILE *target;
uint8_t level;
};
typedef struct cstuff_log * CStuffLog;
void
cstuff_log_init(CStuffLog self, int level);
void
cstuff_log_release(CStuffLog self);
CStuffRetcode
cstuff_log_set_file(CStuffLog self, const char *filename);
int
cstuff_log_printf(CStuffLog self, int level, const char *format, ...);
int
cstuff_log_vprintf(CStuffLog self, int level, const char *format, va_list vl);
#endif /* !CSTUFF_LOG_H */
+28
View File
@@ -0,0 +1,28 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file retcode.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief cStuff return codes
*
* @see https://lowenware.com/
*/
#ifndef CSTUFF_RETCODE_H_CBA3C16B_88D6_49F4_966D_DBDA727583FE
#define CSTUFF_RETCODE_H_CBA3C16B_88D6_49F4_966D_DBDA727583FE
typedef enum {
CSTUFF_INPUT_ERROR = -3
, CSTUFF_MALLOC_ERROR = -2
, CSTUFF_SYSCALL_ERROR = -1
, CSTUFF_SUCCESS = 0
, CSTUFF_IDLE = 1
} CStuffRetcode;
#endif /* !CSTUFF_RETCODE_H */
+104
View File
@@ -0,0 +1,104 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file string.c
* @author Ilja Kartašov <ik@lowenware.com>
* @brief cStuff string module implementation
*
* @see https://lowenware.com/
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "string.h"
int
cstuff_strcpy(char **out, const char *in)
{
return cstuff_strncpy(out, in, strlen(in));
}
int
cstuff_strncpy(char **out, const char *in, int len)
{
char *s;
if (!(s = malloc( len + 1 )))
return -1;
strncpy(s, in, len);
s[len]=0;
*out = s;
return len;
}
int
cstuff_strset(char **out, const char *in)
{
return cstuff_strnset(out, in, strlen(in));
}
int
cstuff_strnset(char **out, const char *in, int len)
{
char *s;
if (!(s = realloc(*out, len+1)))
return -1;
strncpy(s, in, len);
s[len] = 0;
*out = s;
return len;
}
int
cstuff_sprintf(char **out, const char *format, ...)
{
int result;
va_list vl;
va_start(vl, format);
result = cstuff_vsprintf(out, format, vl);
va_end(vl);
return result;
}
int
cstuff_vsprintf(char **out, const char *format, va_list args)
{
int result;
va_list vc;
char tmp, *s;
va_copy(vc, args);
result = vsnprintf(&tmp, 1, format, vc);
va_end(vc);
if (!(s = malloc(result + 1)))
return -1;
*out = s;
result = vsnprintf(s, result+1, format, args);
return result;
}
+61
View File
@@ -0,0 +1,61 @@
/******************************************************************************
*
* Copyright (c) 2017-2019 by Löwenware Ltd
* Please, refer LICENSE file for legal information
*
******************************************************************************/
/**
* @file string.h
* @author Ilja Kartašov <ik@lowenware.com>
* @brief String module declarations
*
* @see https://lowenware.com/
*/
#ifndef CSTUFF_STRING_H_938B242C_B750_40E9_8B67_A69F2F37EB87
#define CSTUFF_STRING_H_938B242C_B750_40E9_8B67_A69F2F37EB87
#include <stdarg.h>
#include "retcode.h"
/** @brief Copies in string to newly allocated out buffer
* @param out a pointer to an address where new pointer must be stored
* @param in an input string
* @return length of string or -1 if out of memory
*/
int
cstuff_strcpy(char **out, const char *in);
int
cstuff_strncpy(char **out, const char *in, int len);
/** @brief Overwrites content of out buffer with in string using realloc
* @param out a pointer to an address where new string must be stored
* @param in an input string
* @return length of string or -1 if out of memory
*/
int
cstuff_strset(char **out, const char *in);
int
cstuff_strnset(char **out, const char *in, int len);
/** @brief Allocates memory and prints a formatted string into it
* @param out a pointer to an address where new string must be stored
* @param format a format for string (see manual for stdlib sprintf)
* @return length of string or -1 if out of memory
*/
int
cstuff_sprintf(char **out, const char *format, ...);
int
cstuff_vsprintf(char **out, const char *format, va_list args);
#endif /* !CSTUFF_STRING_H */