/* * Alertik: a tiny 'syslog' server & notification tool for Mikrotik routers. * This is free and unencumbered software released into the public domain. */ /* * String/append buffer implementation based on Aqua: * https://gist.github.com/Theldus/09ed2205aa5ba15cdf4571b71cd1c8fc */ #include "str.h" #include "log.h" /* Malloc is only used if AB_USE_MALLOC is defined. */ #ifdef AB_USE_MALLOC #if defined(AB_CALLOC) && defined(AB_REALLOC) && defined(AB_FREE) # define AB_USE_STDLIB #elif !defined(AB_CALLOC) && !defined(AB_REALLOC) && !defined(AB_FREE) # define AB_USE_STDLIB #else #error "For custom memory allocators, you should define all three routines!" #error "Please define: AB_CALLOC, AB_REALLOC and AB_FREE!" #endif #endif #ifndef AB_CALLOC #define AB_CALLOC(nmemb,sz) calloc((nmemb),(sz)) #define AB_REALLOC(p,newsz) realloc((p),(newsz)) #define AB_FREE(p) free((p)) #endif #ifdef AB_USE_STDLIB #include #endif #include #include #include #include /* ========================================================================= */ /* BUFFER ROUTINES */ /* ========================================================================= */ #ifdef AB_USE_MALLOC /** * @brief Rounds up to the next power of two. * * @param target Target number to be rounded. * * @return Returns the next power of two. */ static size_t next_power(size_t target) { target--; target |= target >> 1; target |= target >> 2; target |= target >> 4; target |= target >> 8; target |= target >> 16; target++; return (target); } #endif /** * @brief Checks if the new size fits in the append buffer, if not, * reallocates the buffer size by @p incr bytes. * * If the macro AB_USE_MALLOC is not defined (default), this only * checks if the new size fits the buffer. * * @param sh Aqua highlight context. * @param incr Size (in bytes) to be incremented. * * @return Returns 0 if success, -1 otherwise. * * @note The new size is the next power of two, that is capable * to hold the required buffer size. */ static int increase_buff(struct str_ab *sh, size_t incr) { #ifndef AB_USE_MALLOC if (sh->pos + incr >= MAX_LINE) { log_msg("(increase buffer) Unable to fit appended message!\n"); log_msg("(static storage) incr: %zu, buff_len: %zu, pos: %zu\n", incr, sh->pos, sh->buff_len); return (-1); } #else char *new; size_t new_size; if (sh->pos + incr >= sh->buff_len) { new_size = next_power(sh->buff_len + incr); new = AB_REALLOC(sh->buff, new_size); if (new == NULL) { AB_FREE(sh->buff); sh->buff = NULL; log_msg("(increase buffer) Unable to fit appended message!\n"); log_msg("(realloc storage) incr: %zu, buff_len: %zu, pos: %zu\n", incr, sh->pos, sh->buff_len); return (-1); } sh->buff_len = new_size; sh->buff = new; } #endif return (0); } /** * @brief Initializes the append buffer context. * * @param ab Append buffer structure. * * @return Returns 0 if success, -1 otherwise. */ int ab_init(struct str_ab *ab) { if (!ab) return (-1); memset(ab, 0, sizeof(*ab)); #ifndef AB_USE_MALLOC ab->buff_len = MAX_LINE; #else ab->buff = AB_CALLOC(MAX_LINE, 1); if (!ab->buff) return (-1); ab->buff_len = MAX_LINE; #endif return (0); } /** * @brief Append a given char @p c into the buffer. * * @param sh Aqua highlight context. * @param c Char to be appended. * * @return Returns 0 if success, -1 otherwise. */ int ab_append_chr(struct str_ab *sh, char c) { if (increase_buff(sh, 2) < 0) return (-1); sh->buff[sh->pos + 0] = c; sh->buff[sh->pos + 1] = '\0'; sh->pos++; return (0); } /** * @brief Appends a given string pointed by @p s of size @p len * into the current buffer. * * If @p len is 0, the string is assumed to be null-terminated * and its length is obtained. * * @param ab Append buffer context. * @param s String to be append into the buffer. * @param len String size, if 0, it's length is obtained. * * @return Returns 0 if success, -1 otherwise. */ int ab_append_str(struct str_ab *ab, const char *s, size_t len) { if (!len) len = strlen(s); if (increase_buff(ab, len + 1) < 0) return (-1); memcpy(ab->buff + ab->pos, s, len); ab->pos += len; ab->buff[ab->pos] = '\0'; return (0); } /** * @brief Appends a given formatted string pointed by @p fmt. * * @param ab Append buffer context. * @param fmt Formatted string to be appended. * * @return Returns 0 if success, -1 otherwise. */ int ab_append_fmt(struct str_ab *ab, const char *fmt, ...) { int str_len, ab_len, orig_ab_pos; char *buff_st; va_list ap; buff_st = ab->buff + ab->pos; ab_len = ab->buff_len - ab->pos; va_start(ap, fmt); str_len = vsnprintf(buff_st, ab_len, fmt, ap); if (str_len < 0) { log_msg("Unable to fit appended message!\n"); return (-1); } va_end(ap); /* If it fits, just happily returns. */ if (str_len + 1 <= ab_len) { ab->pos += str_len; return (0); } /* Otherwise, adjust current pos and try to increase buffer. */ else { orig_ab_pos = ab->pos; /* temporarily advance our buffer * to trick our increase buffer. */ ab->pos = ab->buff_len; if (increase_buff(ab, (str_len + 1) - ab_len)) return (-1); ab->pos = orig_ab_pos; } buff_st = ab->buff + ab->pos; ab_len = ab->buff_len - ab->pos; va_start(ap, fmt); str_len = vsnprintf(buff_st, ab_len, fmt, ap); if (str_len < 0 || (str_len + 1) > ab_len) { log_msg("Unable to fit appended message!\n"); return (-1); } va_end(ap); ab->pos += str_len; return (0); }