compiles
This commit is contained in:
parent
048bf96e51
commit
54c8012836
8 changed files with 604 additions and 4 deletions
|
|
@ -3,7 +3,19 @@ cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
project(AutoFW)
|
project(AutoFW)
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin)
|
||||||
|
|
||||||
add_executable(autofw
|
add_executable(autofw
|
||||||
autofw.c
|
autofw.c
|
||||||
|
matcher.c
|
||||||
|
pbuf.c
|
||||||
|
vbuf.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND pcre2-config --libs8
|
||||||
|
OUTPUT_VARIABLE LIBPCRE
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
|
||||||
|
target_link_libraries(autofw ${LIBPCRE})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,8 @@
|
||||||
|
|
||||||
Inspired by fail2ban.
|
Inspired by fail2ban.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
On Ubuntu:
|
||||||
|
apt-get install -y cmake libpcre2-dev
|
||||||
|
|
||||||
|
|
|
||||||
114
matcher.c
Normal file
114
matcher.c
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
|
||||||
|
#include "matcher.h"
|
||||||
|
#include "vbuf.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
matcher_free_match_data_ptr(pcre2_match_data** p)
|
||||||
|
{
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
pcre2_match_data_free(*p);
|
||||||
|
*p = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
matcher_pcre2_code_free_ptr(pcre2_code** p)
|
||||||
|
{
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
pcre2_code_free(*p);
|
||||||
|
*p = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
matcher_init_regexp(
|
||||||
|
regexp_matcher_t* matcher,
|
||||||
|
const char* regexp)
|
||||||
|
{
|
||||||
|
if (!matcher || !regexp)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int errorcode;
|
||||||
|
PCRE2_SIZE errorpos;
|
||||||
|
|
||||||
|
matcher->regexp = pcre2_compile(
|
||||||
|
(PCRE2_SPTR8) regexp,
|
||||||
|
PCRE2_ZERO_TERMINATED,
|
||||||
|
0, /* options */
|
||||||
|
&errorcode,
|
||||||
|
&errorpos,
|
||||||
|
NULL /* context */);
|
||||||
|
if (!matcher->regexp) {
|
||||||
|
PCRE2_UCHAR8 errortext[1024];
|
||||||
|
pcre2_get_error_message(errorcode, errortext, sizeof(errortext));
|
||||||
|
fprintf(stderr, "%s: regexp at %p errorpos %ld: `%s'\n",
|
||||||
|
__FUNCTION__, matcher->regexp, errorpos, errortext);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbuf_init(&matcher->results, 10, 0, (pbuf_func_free_t) vbuf_destroy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
matcher_destroy(regexp_matcher_t* matcher)
|
||||||
|
{
|
||||||
|
if (!matcher || !matcher->regexp)
|
||||||
|
return;
|
||||||
|
pcre2_code_free(matcher->regexp);
|
||||||
|
pbuf_destroy(&matcher->results);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
matcher_match(regexp_matcher_t* matcher, const char* subject)
|
||||||
|
{
|
||||||
|
if (!matcher || !subject)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pbuf_clear(&matcher->results);
|
||||||
|
pcre2_match_data* __attribute__((cleanup(matcher_free_match_data_ptr))) match_data =
|
||||||
|
pcre2_match_data_create(30, NULL);
|
||||||
|
int const n = pcre2_match(matcher->regexp,
|
||||||
|
(PCRE2_SPTR8) subject, strlen(subject),
|
||||||
|
0, // startoffset
|
||||||
|
PCRE2_ANCHORED, // options
|
||||||
|
match_data, // match data
|
||||||
|
NULL // match context
|
||||||
|
);
|
||||||
|
fprintf(stderr, "%s: pcre2_match returned %d for `%s'\n",
|
||||||
|
__FUNCTION__, n, subject);
|
||||||
|
if (n < 0)
|
||||||
|
return n;
|
||||||
|
#define START_IX(i) ((i) * 2 + 0)
|
||||||
|
#define END_IX(i) ((i) * 2 + 1)
|
||||||
|
|
||||||
|
PCRE2_SIZE* const ovector = pcre2_get_ovector_pointer(match_data);
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
if (ovector[START_IX(i)] == ovector[END_IX(i)])
|
||||||
|
continue;
|
||||||
|
#if 1
|
||||||
|
fprintf(stderr, "%s: match %d: %ld .. %ld: `%.*s'\n",
|
||||||
|
__FUNCTION__, i,
|
||||||
|
ovector[START_IX(i)], ovector[END_IX(i)] - 1,
|
||||||
|
(int) (ovector[END_IX(i)] - ovector[START_IX(i)]),
|
||||||
|
subject + ovector[START_IX(i)]);
|
||||||
|
#endif
|
||||||
|
pbuf_putat(&matcher->results, i,
|
||||||
|
vbuf_createbuf2(subject + ovector[START_IX(i)],
|
||||||
|
ovector[END_IX(i)] - ovector[START_IX(i)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
matcher_result(regexp_matcher_t* matcher, int index)
|
||||||
|
{
|
||||||
|
if (!matcher)
|
||||||
|
return NULL;
|
||||||
|
return vbuf_notnull(pbuf_getat(&matcher->results, index));
|
||||||
|
}
|
||||||
|
|
||||||
14
matcher.h
14
matcher.h
|
|
@ -9,3 +9,17 @@ typedef struct {
|
||||||
pbuf_t results;
|
pbuf_t results;
|
||||||
} regexp_matcher_t;
|
} regexp_matcher_t;
|
||||||
|
|
||||||
|
int
|
||||||
|
matcher_init_regexp(
|
||||||
|
regexp_matcher_t* matcher,
|
||||||
|
const char* regexp);
|
||||||
|
|
||||||
|
void
|
||||||
|
matcher_destroy(regexp_matcher_t* matcher);
|
||||||
|
|
||||||
|
int
|
||||||
|
matcher_match(regexp_matcher_t* matcher, const char* needle);
|
||||||
|
|
||||||
|
const char*
|
||||||
|
matcher_result(regexp_matcher_t* matcher, int index);
|
||||||
|
|
||||||
|
|
|
||||||
168
pbuf.c
Normal file
168
pbuf.c
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
|
||||||
|
#include "pbuf.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_init(pbuf_t* pbuf, int size, int chunksize, pbuf_func_free_t func_free)
|
||||||
|
{
|
||||||
|
if (!pbuf)
|
||||||
|
return;
|
||||||
|
pbuf->used = 0;
|
||||||
|
if (size <= 0)
|
||||||
|
size = 1;
|
||||||
|
pbuf->allocated = size;
|
||||||
|
if (chunksize <= 0)
|
||||||
|
chunksize = size / 2;
|
||||||
|
if (chunksize <= 8)
|
||||||
|
chunksize = 8;
|
||||||
|
pbuf->chunksize = chunksize;
|
||||||
|
pbuf->ptr = calloc(pbuf->allocated, sizeof(pbuf->ptr[0]));
|
||||||
|
pbuf->func_free = func_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pbuf_clear_range(pbuf_t* pbuf, int first, int firstNot)
|
||||||
|
{
|
||||||
|
if (!pbuf)
|
||||||
|
return;
|
||||||
|
for (int i = first; i < firstNot; ++i) {
|
||||||
|
void* obj = pbuf->ptr[i];
|
||||||
|
if (!obj)
|
||||||
|
continue;
|
||||||
|
if (pbuf->func_free != PBUF_NONE) {
|
||||||
|
if (pbuf->func_free)
|
||||||
|
pbuf->func_free(obj);
|
||||||
|
else
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
pbuf->ptr[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_clear(pbuf_t* pbuf)
|
||||||
|
{
|
||||||
|
if (!pbuf || (pbuf->used == 0))
|
||||||
|
return;
|
||||||
|
pbuf_clear_range(pbuf, 0, pbuf->used);
|
||||||
|
pbuf->used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_destroy(pbuf_t *pbuf)
|
||||||
|
{
|
||||||
|
if (!pbuf)
|
||||||
|
return;
|
||||||
|
pbuf_clear(pbuf);
|
||||||
|
free(pbuf->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pbuf_size(const pbuf_t* pbuf)
|
||||||
|
{
|
||||||
|
if (!pbuf)
|
||||||
|
return 0;
|
||||||
|
return pbuf->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pbuf_ensure(pbuf_t *pbuf, int amount)
|
||||||
|
{
|
||||||
|
int const needed = pbuf->used + amount;
|
||||||
|
if (needed < pbuf->allocated)
|
||||||
|
return;
|
||||||
|
int const blocks = needed / pbuf->chunksize + 1;
|
||||||
|
int const current = pbuf->allocated;
|
||||||
|
pbuf->allocated = blocks * pbuf->chunksize;
|
||||||
|
pbuf->ptr = realloc(pbuf->ptr, pbuf->allocated * sizeof(pbuf->ptr[0]));
|
||||||
|
memset(pbuf->ptr + current, 0,
|
||||||
|
(pbuf->allocated - current) * sizeof(pbuf->ptr[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_append(pbuf_t* pbuf, void* ptr)
|
||||||
|
{
|
||||||
|
if (!pbuf)
|
||||||
|
return;
|
||||||
|
pbuf_ensure(pbuf, 1);
|
||||||
|
pbuf->ptr[pbuf->used++] = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_sort(pbuf_t* pbuf, pbuf_func_sort_t func)
|
||||||
|
{
|
||||||
|
if (!pbuf || (pbuf->used < 2))
|
||||||
|
return;
|
||||||
|
qsort(pbuf->ptr, pbuf->used, sizeof(pbuf->ptr[0]), func);
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
pbuf_find(const pbuf_t* pbuf, pbuf_func_find_t func_find, const void* arg)
|
||||||
|
{
|
||||||
|
if (!pbuf || !func_find)
|
||||||
|
return NULL;
|
||||||
|
for (int i = 0; i < pbuf->used; ++i) {
|
||||||
|
void* const obj = pbuf->ptr[i];
|
||||||
|
if (!obj)
|
||||||
|
continue;
|
||||||
|
if (func_find(obj, arg))
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pbuf_trim_end(pbuf_t* pbuf)
|
||||||
|
{
|
||||||
|
if (!pbuf)
|
||||||
|
return;
|
||||||
|
while (pbuf->used > 0) {
|
||||||
|
if (pbuf->ptr[pbuf->used - 1])
|
||||||
|
break;
|
||||||
|
pbuf->used--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
pbuf_clearat(pbuf_t* pbuf, int ix)
|
||||||
|
{
|
||||||
|
if (!pbuf || (pbuf->used <= ix))
|
||||||
|
return false;
|
||||||
|
void* const obj = pbuf->ptr[ix];
|
||||||
|
if (!obj)
|
||||||
|
return false;
|
||||||
|
pbuf->ptr[ix] = NULL;
|
||||||
|
if (pbuf->func_free != PBUF_NONE) {
|
||||||
|
if (pbuf->func_free)
|
||||||
|
pbuf->func_free(obj);
|
||||||
|
else
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
pbuf_trim_end(pbuf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_putat(pbuf_t* pbuf, int ix, void* obj)
|
||||||
|
{
|
||||||
|
if (!pbuf)
|
||||||
|
return;
|
||||||
|
pbuf_ensure(pbuf, ix + 1 - pbuf->used);
|
||||||
|
pbuf_clearat(pbuf, ix);
|
||||||
|
pbuf->ptr[ix] = obj;
|
||||||
|
if (ix + 1 > pbuf->used)
|
||||||
|
pbuf->used = ix + 1;
|
||||||
|
pbuf_trim_end(pbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
pbuf_getat(const pbuf_t* pbuf, int ix)
|
||||||
|
{
|
||||||
|
if (!pbuf || (pbuf->used <= ix) || (ix < 0))
|
||||||
|
return NULL;
|
||||||
|
return pbuf->ptr[ix];
|
||||||
|
}
|
||||||
|
|
||||||
49
pbuf.h
49
pbuf.h
|
|
@ -1,6 +1,8 @@
|
||||||
|
|
||||||
typedef void (*pbuf_func_free_t)(void*);
|
typedef void (*pbuf_func_free_t)(void*);
|
||||||
|
|
||||||
|
#define PBUF_NONE ((void*)-1)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int used;
|
int used;
|
||||||
int allocated;
|
int allocated;
|
||||||
|
|
@ -9,6 +11,49 @@ typedef struct {
|
||||||
pbuf_func_free_t func_free;
|
pbuf_func_free_t func_free;
|
||||||
} pbuf_t;
|
} pbuf_t;
|
||||||
|
|
||||||
// Add a new item at the end of the pbuf_t.
|
void pbuf_init(
|
||||||
extern void pbuf_append(pbuf_t* pbuf, void* ptr);
|
pbuf_t* pbuf,
|
||||||
|
int size,
|
||||||
|
int chunksize,
|
||||||
|
pbuf_func_free_t func_free);
|
||||||
|
|
||||||
|
void pbuf_destroy(pbuf_t* pbuf);
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_clear(pbuf_t* pbuf);
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_append(pbuf_t* pbuf, void* ptr);
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_putat(pbuf_t* pbuf, int ix, void* obj);
|
||||||
|
|
||||||
|
void*
|
||||||
|
pbuf_getat(const pbuf_t* pbuf, int ix);
|
||||||
|
|
||||||
|
int
|
||||||
|
pbuf_size(const pbuf_t* pbuf);
|
||||||
|
|
||||||
|
typedef int (*pbuf_func_find_t)(void* obj, const void* arg);
|
||||||
|
|
||||||
|
void*
|
||||||
|
pbuf_find(
|
||||||
|
const pbuf_t* pbuf,
|
||||||
|
pbuf_func_find_t func,
|
||||||
|
const void* arg);
|
||||||
|
|
||||||
|
typedef int (*pbuf_func_sort_t)(const void* obj1, const void* obj2);
|
||||||
|
|
||||||
|
void
|
||||||
|
pbuf_sort(
|
||||||
|
pbuf_t* pbuf,
|
||||||
|
pbuf_func_sort_t func);
|
||||||
|
|
||||||
|
#define PBUF_FOREACH(element, list) { \
|
||||||
|
int pbuf_i; \
|
||||||
|
if ((void*)list != NULL) for (pbuf_i = 0; pbuf_i < (list)->used; ++pbuf_i) { \
|
||||||
|
element = (list)->ptr[pbuf_i]; \
|
||||||
|
if (!element) continue;
|
||||||
|
|
||||||
|
#define PBUF_FOREACH_END } }
|
||||||
|
|
||||||
|
|
|
||||||
201
vbuf.c
Normal file
201
vbuf.c
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
|
||||||
|
#include "vbuf.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_init3(vbuf_t* vbuf, int size, int chunksize)
|
||||||
|
{
|
||||||
|
if (!vbuf)
|
||||||
|
return;
|
||||||
|
// There is no idea to use a smaller buffer than the "abuf",
|
||||||
|
// so we might as well use it all.
|
||||||
|
// Besides, size may be 0 here, and we have to watch for that.
|
||||||
|
//
|
||||||
|
unsigned int sz = size;
|
||||||
|
if (size < (int)sizeof(vbuf->abuf))
|
||||||
|
sz = sizeof(vbuf->abuf);
|
||||||
|
if (chunksize <= 0)
|
||||||
|
chunksize = sz / 2;
|
||||||
|
if (chunksize < 32)
|
||||||
|
chunksize = 32;
|
||||||
|
*vbuf = (vbuf_t) { .chunksize = chunksize };
|
||||||
|
if (sz <= sizeof(vbuf->abuf)) {
|
||||||
|
vbuf->ptr = vbuf->abuf;
|
||||||
|
vbuf->size = sz;
|
||||||
|
} else {
|
||||||
|
vbuf_ensure(vbuf, sz);
|
||||||
|
vbuf_init(vbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_init0(vbuf_t* vbuf)
|
||||||
|
{
|
||||||
|
if (!vbuf)
|
||||||
|
return;
|
||||||
|
vbuf_init3(vbuf, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vbuf_t*
|
||||||
|
vbuf_createbuf2(const char* buf, int len)
|
||||||
|
{
|
||||||
|
vbuf_t* const vbuf = malloc(sizeof(vbuf_t));
|
||||||
|
vbuf_init3(vbuf, len + 1, 0);
|
||||||
|
vbuf->len = len;
|
||||||
|
if (buf)
|
||||||
|
memcpy(vbuf->ptr, buf, vbuf->len);
|
||||||
|
vbuf->ptr[vbuf->len] = '\0';
|
||||||
|
return vbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_init(vbuf_t* vbuf)
|
||||||
|
{
|
||||||
|
if (!vbuf)
|
||||||
|
return;
|
||||||
|
vbuf->len = 0;
|
||||||
|
if (vbuf->ptr)
|
||||||
|
vbuf->ptr[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_freeptr(vbuf_t* vbuf)
|
||||||
|
{
|
||||||
|
if (!vbuf)
|
||||||
|
return;
|
||||||
|
if (vbuf->ptr != vbuf->abuf)
|
||||||
|
free(vbuf->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_destroy(vbuf_t* vbuf)
|
||||||
|
{
|
||||||
|
if (!vbuf)
|
||||||
|
return;
|
||||||
|
vbuf_freeptr(vbuf);
|
||||||
|
free(vbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char nullbyte = 0;
|
||||||
|
|
||||||
|
const char*
|
||||||
|
vbuf_notnull(const vbuf_t* vbuf)
|
||||||
|
{
|
||||||
|
if (!vbuf || !vbuf->ptr)
|
||||||
|
return &nullbyte;
|
||||||
|
return vbuf->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
vbuf_tostring(const vbuf_t* vbuf)
|
||||||
|
{
|
||||||
|
if (!vbuf)
|
||||||
|
return NULL;
|
||||||
|
if (!vbuf->ptr)
|
||||||
|
return &nullbyte;
|
||||||
|
return vbuf->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_ensure(vbuf_t* vbuf, unsigned int len)
|
||||||
|
{
|
||||||
|
unsigned int const needed = vbuf->len + len + 1;
|
||||||
|
if (needed < vbuf->size)
|
||||||
|
return;
|
||||||
|
unsigned int const blocks = needed / vbuf->chunksize + 1;
|
||||||
|
unsigned int const oldsize = vbuf->size;
|
||||||
|
vbuf->size = blocks * vbuf->chunksize;
|
||||||
|
if ((vbuf->size > sizeof(vbuf->abuf)) || (vbuf->ptr != vbuf->abuf)) {
|
||||||
|
const char* const oldptr = vbuf->ptr;
|
||||||
|
if (oldptr == vbuf->abuf) {
|
||||||
|
vbuf->ptr = malloc(vbuf->size);
|
||||||
|
memcpy(vbuf->ptr, oldptr, oldsize);
|
||||||
|
} else {
|
||||||
|
vbuf->ptr = realloc(vbuf->ptr, vbuf->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
vbuf_append2(vbuf_t* vbuf, const char* const ptr, const int len)
|
||||||
|
{
|
||||||
|
if (!vbuf)
|
||||||
|
return 0;
|
||||||
|
if (!ptr || (len == 0))
|
||||||
|
return vbuf->len;
|
||||||
|
|
||||||
|
int datalen = len >= 0 ? len : (int) strlen(ptr);
|
||||||
|
vbuf_ensure(vbuf, datalen);
|
||||||
|
switch (datalen) {
|
||||||
|
case 8:
|
||||||
|
vbuf->ptr[vbuf->len + 7] = ptr[7];
|
||||||
|
// fall-through
|
||||||
|
case 7:
|
||||||
|
vbuf->ptr[vbuf->len + 6] = ptr[6];
|
||||||
|
// fall-through
|
||||||
|
case 6:
|
||||||
|
vbuf->ptr[vbuf->len + 5] = ptr[5];
|
||||||
|
// fall-through
|
||||||
|
case 5:
|
||||||
|
vbuf->ptr[vbuf->len + 4] = ptr[4];
|
||||||
|
// fall-through
|
||||||
|
case 4:
|
||||||
|
vbuf->ptr[vbuf->len + 3] = ptr[3];
|
||||||
|
// fall-through
|
||||||
|
case 3:
|
||||||
|
vbuf->ptr[vbuf->len + 2] = ptr[2];
|
||||||
|
// fall-through
|
||||||
|
case 2:
|
||||||
|
vbuf->ptr[vbuf->len + 1] = ptr[1];
|
||||||
|
// fall-through
|
||||||
|
case 1:
|
||||||
|
vbuf->ptr[vbuf->len + 0] = ptr[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (vbuf && vbuf->ptr)
|
||||||
|
memcpy(&vbuf->ptr[vbuf->len], ptr, datalen);
|
||||||
|
}
|
||||||
|
vbuf->len += datalen;
|
||||||
|
if (vbuf && vbuf->ptr)
|
||||||
|
vbuf->ptr[vbuf->len] = '\0';
|
||||||
|
return vbuf->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vbuf_gets2(vbuf_t* vbuf, FILE* fp, bool keepnewline)
|
||||||
|
{
|
||||||
|
if (!fp)
|
||||||
|
return -1;
|
||||||
|
if (!vbuf)
|
||||||
|
return 0;
|
||||||
|
vbuf_init(vbuf);
|
||||||
|
bool done = false;
|
||||||
|
while (!done) {
|
||||||
|
char buf[1024];
|
||||||
|
buf[0] = 0;
|
||||||
|
const char* const p = fgets(buf, sizeof(buf), fp);
|
||||||
|
if (!p) {
|
||||||
|
if (vbuf->len == 0)
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int len = strlen(buf);
|
||||||
|
if ((len > 0) && (buf[len - 1] == '\n')) {
|
||||||
|
done = true;
|
||||||
|
if (!keepnewline)
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
vbuf_append2(vbuf, buf, len);
|
||||||
|
}
|
||||||
|
return vbuf->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
vbuf_gets(vbuf_t* vbuf, FILE* fp)
|
||||||
|
{
|
||||||
|
return vbuf_gets2(vbuf, fp, false);
|
||||||
|
}
|
||||||
|
|
||||||
41
vbuf.h
41
vbuf.h
|
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define VBUF_ABUF_SIZE 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int flags;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned int size;
|
||||||
|
unsigned int chunksize;
|
||||||
|
char* ptr;
|
||||||
|
char abuf[VBUF_ABUF_SIZE];
|
||||||
|
} vbuf_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_init0(vbuf_t* vbuf);
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_destroy(vbuf_t* vbuf);
|
||||||
|
|
||||||
|
vbuf_t*
|
||||||
|
vbuf_createbuf2(const char* buf, int len);
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_init(vbuf_t* vbuf);
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_freeptr(vbuf_t* vbuf);
|
||||||
|
|
||||||
|
int
|
||||||
|
vbuf_gets(vbuf_t* vbuf, FILE* fp);
|
||||||
|
|
||||||
|
void
|
||||||
|
vbuf_ensure(vbuf_t* vbuf, unsigned int delta);
|
||||||
|
|
||||||
|
char*
|
||||||
|
vbuf_tostring(const vbuf_t* vbuf);
|
||||||
|
|
||||||
|
const char*
|
||||||
|
vbuf_notnull(const vbuf_t* vbuf);
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue