aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRatakor <ratakor@disroot.org>2023-08-08 05:01:39 +0200
committerRatakor <ratakor@disroot.org>2023-08-08 05:01:39 +0200
commitb8d2bba45e9bf21714bf65731397faa1e4189866 (patch)
treeb94a6e96940354f6c0ea005e9c74bbcd914a6a75
parentf43349d5830fdaa9cd76781d482bca1e18be3021 (diff)
Remove libre, add dalloc, add ubik func to util
-rw-r--r--README.md1
-rw-r--r--src/Makefile4
-rw-r--r--src/dalloc.c420
-rw-r--r--src/dalloc.h70
-rw-r--r--src/main.c5
-rw-r--r--src/nolan.h17
-rw-r--r--src/stats.c5
-rw-r--r--src/util.c125
8 files changed, 637 insertions, 10 deletions
diff --git a/README.md b/README.md
index 4d1c487..3e7f973 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@ A discord bot for Orna.
- [concord](https://github.com/Cogmasters/concord)
- [tesseract](https://github.com/tesseract-ocr/tesseract)
- [gd](https://github.com/libgd/libgd)
-- [libre](https://github.com/ratakor/libre)
## TODO
diff --git a/src/Makefile b/src/Makefile
index f0a0a42..d165209 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -11,6 +11,7 @@ OBJS = cmd_correct.o \
cmd_source.o \
cmd_time.o \
cmd_uraid.o \
+ dalloc.o \
init.o \
main.o \
ocr.o \
@@ -20,7 +21,6 @@ OBJS = cmd_correct.o \
stats.o \
util.o
-RELIBS = -lre
DISCORDLIBS = -ldiscord -lcurl -lpthread
TESSLIBS = -ltesseract -lleptonica
GDLIBS = -lgd -lpng -lz -ljpeg -lfreetype -lm
@@ -28,7 +28,7 @@ GDLIBS = -lgd -lpng -lz -ljpeg -lfreetype -lm
WFLAGS = -pedantic -Werror -Wall -Wextra -Waggregate-return -Wshadow\
-Wmissing-prototypes -Wunused-macros -Wdouble-promotion
CFLAGS += -std=c99 -D_DEFAULT_SOURCE ${WFLAGS}
-LDFLAGS += ${RELIBS} ${DISCORDLIBS} ${TESSLIBS} ${GDLIBS}
+LDFLAGS += ${DISCORDLIBS} ${TESSLIBS} ${GDLIBS}
all: ${BIN}
diff --git a/src/dalloc.c b/src/dalloc.c
new file mode 100644
index 0000000..9e28c50
--- /dev/null
+++ b/src/dalloc.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright © 2023, Ratakor <ratakor@disroot.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <pthread.h>
+
+#define DALLOC_INTERNAL
+#include "dalloc.h"
+
+#define EXIT_STATUS EXIT_FAILURE
+#define OVER_ALLOC 64
+#define MAGIC_NUMBER 0xdead
+
+#define x1 ((char)(MAGIC_NUMBER))
+#define x2 x1, x1
+#define x4 x2, x2
+#define x8 x4, x4
+#define x16 x8, x8
+#define x32 x16, x16
+#define x64 x32, x32
+#define xCAT(X) x##X
+#define MAGIC_INIT(X) xCAT(X)
+#define OVERFLOW(p, s) (memcmp(((char *)(p)) + (s), magic_numbers, OVER_ALLOC))
+
+typedef struct dalloc_ptr dalloc_ptr;
+struct dalloc_ptr {
+ void *p;
+ size_t siz;
+ char *comment;
+ char *file;
+ int ignored;
+ int line;
+ dalloc_ptr *next;
+};
+
+static void eprintf(const char *fmt, ...);
+static char *xstrdup(const char *s, char *file, int line);
+static dalloc_ptr *find_ptr(void *p, char *file, int line);
+static void check_overflow(dalloc_ptr *dp, char *file, int line);
+
+static const char magic_numbers[OVER_ALLOC] = { MAGIC_INIT(OVER_ALLOC) };
+static pthread_mutex_t dalloc_mutex = PTHREAD_MUTEX_INITIALIZER;
+static dalloc_ptr *head;
+
+static void
+eprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+static char *
+xstrdup(const char *s, char *file, int line)
+{
+ char *p;
+ size_t siz;
+
+ siz = strlen(s) + 1;
+ if ((p = malloc(siz)) == NULL) {
+ eprintf("%s:%d: dalloc: %s", file, line, strerror(errno));
+ pthread_mutex_unlock(&dalloc_mutex);
+ exit(EXIT_STATUS);
+ }
+
+ return memcpy(p, s, siz);
+}
+
+static dalloc_ptr *
+find_ptr(void *p, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ for (dp = head; dp && dp->p != p; dp = dp->next);
+
+ if (dp == NULL) {
+ eprintf("%s:%d: dalloc: Unknown pointer %p\n", file, line, p);
+ pthread_mutex_unlock(&dalloc_mutex);
+ exit(EXIT_STATUS);
+ }
+
+ return dp;
+}
+
+static void
+check_overflow(dalloc_ptr *dp, char *file, int line)
+{
+ if (!OVERFLOW(dp->p, dp->siz))
+ return;
+
+ eprintf("%s:%d: dalloc: Memory overflow on %p, total: %zu bytes\n"
+ "The pointer ", file, line, dp->p, dp->siz);
+ if (dp->comment)
+ eprintf("'%s' ", dp->comment);
+ eprintf("was allocated in '%s' on line %d.\n", dp->file, dp->line);
+ pthread_mutex_unlock(&dalloc_mutex);
+ exit(EXIT_STATUS);
+}
+
+size_t
+dalloc_check_overflow(void)
+{
+ dalloc_ptr *dp;
+ size_t sum = 0;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ eprintf("Memory overflow:");
+ for (dp = head; dp; dp = dp->next) {
+ if (!OVERFLOW(dp->p, dp->siz))
+ continue;
+
+ sum++;
+ eprintf("\n%s:%d: %p, total: %zu bytes",
+ dp->file, dp->line, dp->p, dp->siz);
+ if (dp->comment)
+ eprintf(" /* %s */", dp->comment);
+ }
+ pthread_mutex_unlock(&dalloc_mutex);
+
+ if (sum == 0)
+ eprintf(" 0 overflow :)\n");
+ else
+ eprintf("\nTotal overflow: %zu\n", sum);
+
+ return sum;
+}
+
+void
+dalloc_check_free(void)
+{
+ dalloc_ptr *dp;
+ size_t n = 0, sum = 0;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ eprintf("Memory allocated and not freed:");
+ for (dp = head; dp; dp = dp->next) {
+ if (dp->ignored)
+ continue;
+
+ n++;
+ sum += dp->siz;
+ eprintf("\n%s:%d: %p, %zu bytes",
+ dp->file, dp->line, dp->p, dp->siz);
+ if (dp->comment)
+ eprintf(" /* %s */", dp->comment);
+ }
+ pthread_mutex_unlock(&dalloc_mutex);
+
+ if (sum == 0)
+ eprintf(" 0 byte :)\n");
+ else
+ eprintf("\nTotal: %zu bytes, %zu pointers\n", sum, n);
+}
+
+void
+dalloc_check_all(void)
+{
+ dalloc_check_overflow();
+ dalloc_check_free();
+}
+
+void
+_dalloc_ignore(void *p, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ dp = find_ptr(p, file, line);
+ dp->ignored = 1;
+ pthread_mutex_unlock(&dalloc_mutex);
+}
+
+void
+_dalloc_comment(void *p, const char *comment, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ if (comment == NULL)
+ return;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ dp = find_ptr(p, file, line);
+ free(dp->comment);
+ dp->comment = xstrdup(comment, file, line);
+ pthread_mutex_unlock(&dalloc_mutex);
+}
+
+void
+_dalloc_query(void *p, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ dp = find_ptr(p, file, line);
+ fprintf(stderr, "%s:%d: dalloc: %p: %s:%d: %zu bytes",
+ file, line, dp->p, dp->file, dp->line, dp->siz);
+ if (dp->comment)
+ fprintf(stderr, " /* %s */", dp->comment);
+ fputc('\n', stderr);
+ pthread_mutex_unlock(&dalloc_mutex);
+}
+
+void
+_dalloc_free(void *p, char *file, int line)
+{
+ dalloc_ptr *dp, *prev = NULL; /* cc whines */
+
+ if (p == NULL)
+ return;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ for (dp = head; dp && dp->p != p; prev = dp, dp = dp->next);
+ if (dp == NULL) {
+ eprintf("%s:%d: dalloc: Unknown pointer %p\n", file, line, p);
+ pthread_mutex_unlock(&dalloc_mutex);
+ exit(EXIT_STATUS);
+ }
+
+ check_overflow(dp, file, line);
+
+ if (dp == head)
+ head = dp->next;
+ else
+ prev->next = dp->next;
+ pthread_mutex_unlock(&dalloc_mutex);
+
+ free(dp->p);
+ free(dp->file);
+ free(dp->comment);
+ free(dp);
+}
+
+void *
+_dalloc_malloc(size_t siz, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ if (siz == 0) {
+ eprintf("%s:%d: dalloc: malloc with size == 0\n", file, line);
+ return NULL;
+ }
+
+ if (siz + OVER_ALLOC < OVER_ALLOC) {
+ dp = NULL;
+ errno = ENOMEM;
+ } else if ((dp = calloc(1, sizeof(*dp))) != NULL) {
+ dp->p = malloc(siz + OVER_ALLOC);
+ }
+
+ if (dp == NULL || dp->p == NULL) {
+ eprintf("%s:%d: dalloc: %s\n", file, line, strerror(errno));
+ exit(EXIT_STATUS);
+ }
+
+ memset((char *)dp->p + siz, MAGIC_NUMBER, OVER_ALLOC);
+ dp->siz = siz;
+ dp->line = line;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ dp->file = xstrdup(file, file, line);
+ dp->next = head;
+ head = dp;
+ pthread_mutex_unlock(&dalloc_mutex);
+
+ return dp->p;
+}
+
+void *
+_dalloc_calloc(size_t nmemb, size_t siz, char *file, int line)
+{
+ void *p;
+
+ if (siz != 0 && nmemb > (size_t) -1 / siz) {
+ eprintf("%s:%d: dalloc: calloc: %s\n",
+ file, line, strerror(ENOMEM));
+ exit(EXIT_STATUS);
+ }
+
+ siz *= nmemb;
+ p = _dalloc_malloc(siz, file, line);
+ memset(p, 0, siz);
+
+ return p;
+}
+
+void *
+_dalloc_realloc(void *p, size_t siz, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ if (p == NULL)
+ return _dalloc_malloc(siz, file, line);
+
+ if (siz == 0) {
+ eprintf("%s:%d: dalloc: realloc with size == 0\n", file, line);
+ return NULL;
+ }
+
+ pthread_mutex_lock(&dalloc_mutex);
+ dp = find_ptr(p, file, line);
+ check_overflow(dp, file, line);
+
+ if (siz + OVER_ALLOC < OVER_ALLOC) {
+ dp->p = NULL;
+ errno = ENOMEM;
+ } else {
+ dp->p = realloc(dp->p, siz + OVER_ALLOC);
+ }
+
+ if (dp->p == NULL) {
+ eprintf("%s:%d: dalloc: %s\n", file, line, strerror(errno));
+ pthread_mutex_unlock(&dalloc_mutex);
+ exit(EXIT_STATUS);
+ }
+
+ memset((char *)dp->p + siz, MAGIC_NUMBER, OVER_ALLOC);
+ dp->siz = siz;
+ dp->line = line;
+ free(dp->file);
+ dp->file = xstrdup(file, file, line);
+ pthread_mutex_unlock(&dalloc_mutex);
+
+ return dp->p;
+}
+
+void *
+_dalloc_reallocarray(void *p, size_t n, size_t s, char *file, int line)
+{
+ if (s != 0 && n > (size_t) -1 / s) {
+ eprintf("%s:%d: dalloc: reallocarray: %s\n",
+ file, line, strerror(ENOMEM));
+ exit(EXIT_STATUS);
+ }
+
+ return _dalloc_realloc(p, n * s, file, line);
+}
+
+char *
+_dalloc_strdup(const char *s, char *file, int line)
+{
+ char *p;
+ size_t siz;
+
+ siz = strlen(s) + 1;
+ p = _dalloc_malloc(siz, file, line);
+ memcpy(p, s, siz);
+
+ return p;
+}
+
+char *
+_dalloc_strndup(const char *s, size_t n, char *file, int line)
+{
+ char *p;
+ size_t siz;
+
+ siz = strnlen(s, n);
+ p = _dalloc_malloc(siz + 1, file, line);
+ memcpy(p, s, siz);
+ p[siz] = '\0';
+
+ return p;
+}
+
+#if __STDC_VERSION__ >= 199901L
+int
+_dalloc_vasprintf(char **p, const char *fmt, va_list ap, char *file, int line)
+{
+ va_list ap2;
+ size_t siz;
+ int rv;
+
+ va_copy(ap2, ap);
+ rv = vsnprintf(NULL, 0, fmt, ap2);
+ va_end(ap2);
+ if (rv < 0) {
+ eprintf("%s:%d: dalloc: asprintf: %s\n",
+ file, line, strerror(errno));
+ exit(EXIT_STATUS);
+ }
+ siz = (size_t)rv + 1;
+ *p = _dalloc_malloc(siz, file, line);
+ rv = vsnprintf(*p, siz, fmt, ap);
+ if (rv < 0) {
+ eprintf("%s:%d: dalloc: asprintf: %s\n",
+ file, line, strerror(errno));
+ exit(EXIT_STATUS);
+ }
+
+ return rv;
+}
+
+int
+_dalloc_asprintf(char **p, char *file, int line, const char *fmt, ...)
+{
+ int rv;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rv = _dalloc_vasprintf(p, fmt, ap, file, line);
+ va_end(ap);
+
+ return rv;
+}
+#endif /* __STDC_VERSION__ >= 199901L */
diff --git a/src/dalloc.h b/src/dalloc.h
new file mode 100644
index 0000000..441137f
--- /dev/null
+++ b/src/dalloc.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2023, Ratakor <ratakor@disroot.org>
+ *
+ * This library is free software. You can redistribute it and/or modify it
+ * under the terms of the ISC license. See dalloc.c for details.
+ */
+
+#ifndef DALLOC_H
+#define DALLOC_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if defined(DALLOC) && !defined(DALLOC_INTERNAL)
+#define free(p) (_dalloc_free(p, __FILE__, __LINE__))
+#define malloc(siz) (_dalloc_malloc(siz, __FILE__, __LINE__))
+#define calloc(nmemb, siz) (_dalloc_calloc(nmemb, siz, __FILE__, __LINE__))
+#define realloc(p, siz) (_dalloc_realloc(p, siz, __FILE__, __LINE__))
+#define reallocarray(p, n, s) (_dalloc_reallocarray(p, n, s, __FILE__, __LINE__))
+#define strdup(s) (_dalloc_strdup(s, __FILE__, __LINE__))
+#define strndup(s, n) (_dalloc_strndup(s, n, __FILE__, __LINE__))
+#if __STDC_VERSION__ >= 199901L
+#define vasprintf(p, fmt, ap) (_dalloc_vasprintf(p, fmt, ap, __FILE__, __LINE__))
+#define asprintf(p, ...) (_dalloc_asprintf(p, __FILE__, __LINE__, __VA_ARGS__))
+#endif /* __STDC_VERSION__ >= 199901L */
+
+#define dalloc_ignore(p) (_dalloc_ignore(p, __FILE__, __LINE__))
+#define dalloc_comment(p, str) (_dalloc_comment(p, str, __FILE__, __LINE__))
+#define dalloc_query(p) (_dalloc_query(p, __FILE__, __LINE__))
+#endif /* DALLOC && !DALLOC_INTERNAL */
+
+#if defined(DALLOC) || defined(DALLOC_INTERNAL)
+size_t dalloc_check_overflow(void);
+void dalloc_check_free(void);
+void dalloc_check_all(void) __attribute__((destructor));
+
+void _dalloc_ignore(void *p, char *file, int line);
+void _dalloc_comment(void *p, const char *comment, char *file, int line);
+void _dalloc_query(void *p, char *file, int line);
+void _dalloc_free(void *p, char *file, int line);
+void *_dalloc_malloc(size_t siz, char *file, int line);
+void *_dalloc_calloc(size_t nmemb, size_t siz, char *file, int line);
+void *_dalloc_realloc(void *p, size_t siz, char *file, int line);
+void *_dalloc_reallocarray(void *p, size_t n, size_t s, char *file, int line);
+char *_dalloc_strdup(const char *s, char *file, int line);
+char *_dalloc_strndup(const char *s, size_t n, char *file, int line);
+#if __STDC_VERSION__ >= 199901L
+int _dalloc_vasprintf(char **p, const char *fmt, va_list ap, char *file, int line);
+int _dalloc_asprintf(char **p, char *file, int line, const char *fmt, ...);
+#endif /* __STDC_VERSION__ >= 199901L */
+#else
+#define dalloc_check_overflow() 0
+#define dalloc_check_free()
+#define dalloc_check_all()
+#define dalloc_ignore(p)
+#define dalloc_comment(p, str)
+#define dalloc_query(p)
+#endif /* DALLOC || DALLOC_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* DALLOC_H */
diff --git a/src/main.c b/src/main.c
index c72bdb4..9dcefba 100644
--- a/src/main.c
+++ b/src/main.c
@@ -77,11 +77,14 @@ sighandler(int sig)
}
int
-main(void)
+main(int argc, char *argv[])
{
char *src[] = { "src", "source" };
char *lb[] = { "lb", "leaderboard" };
+ UNUSED(argc);
+ progname = argv[0];
+
create_folders();
create_stats_file();
init_players();
diff --git a/src/nolan.h b/src/nolan.h
index a76a17c..fd90d52 100644
--- a/src/nolan.h
+++ b/src/nolan.h
@@ -6,9 +6,8 @@
#include <pthread.h>
#include <concord/discord.h>
#include <concord/log.h>
-#include <libre/ubik.h>
-#include <libre/dalloc.h>
+#include "dalloc.h"
#include "../config.h"
/* normally 50 but let's do * 3 to prevent from buffer overflow :) */
@@ -27,6 +26,12 @@
#define IMAGES_FOLDER SAVE_FOLDER "images/"
#define RAIDS_FOLDER SAVE_FOLDER "raids/"
#define STATS_FILE SAVE_FOLDER FILENAME
+
+#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+#define LENGTH(X) (sizeof(X) / sizeof(X[0]))
+#define STRLEN(X) (sizeof(X) - 1)
+#define UNUSED(X) ((void)(X))
#define VALID_STATS(X) (strchr(X, DELIM) == 0)
#define U32CAST(player)\
((uint32_t *)((char *)(player) + MAX_USERNAME_SIZ + MAX_KINGDOM_SIZ) - 2)
@@ -94,11 +99,19 @@ typedef struct {
bool found_in_file;
} Slayer;
+extern char *progname;
extern Player *player_head;
extern pthread_mutex_t player_mutex;
extern const char *fields[24];
/* util.c */
+void warn(const char *fmt, ...);
+void die(int status, const char *fmt, ...);
+char *nstrchr(const char *s, int c, size_t n);
+size_t ufmt(char *dst, size_t dsiz, uintmax_t n);
+size_t ifmt(char *dst, size_t dsiz, intmax_t n);
+size_t strlcpy(char *dst, const char *src, size_t siz);
+size_t strlcat(char *dst, const char *src, size_t siz);
#ifdef DALLOC
#define xmalloc(siz) malloc(siz)
#define xcalloc(nmemb, siz) calloc(nmemb, siz)
diff --git a/src/stats.c b/src/stats.c
index 7237c61..584d68a 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -459,9 +459,8 @@ update_players(char *buf, size_t siz, Player *new_player)
player = find_player(new_player->userid);
s += write_quote(buf + s, siz - s);
if (player == NULL) { /* new player */
- player = memdup(new_player, sizeof(*new_player));
- if (player == NULL)
- die(1, "memdup:");
+ player = xmalloc(sizeof(*player));
+ memcpy(player, new_player, sizeof(*player));
player->next = player_head;
player_head = player;
s += snprintf(buf + s, siz - s,
diff --git a/src/util.c b/src/util.c
index e5b1c07..1dc4ad7 100644
--- a/src/util.c
+++ b/src/util.c
@@ -2,11 +2,134 @@
#include <sys/stat.h>
#include <errno.h>
+#include <stdarg.h>
#include <string.h>
-#include <libre/ubik.h>
#include "nolan.h"
+char *progname;
+
+static void
+vwarn(const char *fmt, va_list ap)
+{
+ fprintf(stderr, "%s: ", progname);
+
+ if (fmt == NULL) {
+ perror(NULL);
+ return;
+ }
+
+ vfprintf(stderr, fmt, ap);
+ if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarn(fmt, ap);
+ va_end(ap);
+}
+
+void
+die(int status, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarn(fmt, ap);
+ va_end(ap);
+
+ exit(status);
+}
+
+char *
+nstrchr(const char *s, int c, size_t n)
+{
+ char *p;
+
+ if (n == 0 || s == NULL)
+ return NULL;
+
+ p = strchr(s, c);
+ while (--n != 0 && (p = strchr(p + 1, c)) != NULL);
+
+ return p;
+}
+
+size_t
+ufmt(char *dst, size_t dsiz, uintmax_t n)
+{
+ char buf[sizeof(n) * 4], *p;
+ size_t sizn = 1;
+
+ p = buf + sizeof(buf) - 1;
+ *p-- = '\0';
+ for (;;) {
+ *p-- = (n % 10) + '0';
+ if ((n /= 10) == 0)
+ break;
+ if ((sizn % 3) == 0)
+ *p-- = ',';
+ sizn++;
+ }
+
+ return strlcpy(dst, p + 1, dsiz);
+}
+
+size_t
+ifmt(char *dst, size_t dsiz, intmax_t n)
+{
+ if (dsiz == 0) {
+ if (n < 0)
+ n = -n;
+ return ufmt(NULL, 0, (uintmax_t)n) + 1;
+ }
+
+ if (n < 0) {
+ *dst = '-';
+ n = -n;
+ } else {
+ *dst = '+';
+ }
+
+ return ufmt(dst + 1, dsiz - 1, (uintmax_t)n) + 1;
+}
+
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ size_t slen, len;
+
+ slen = strlen(src);
+ if (siz != 0) {
+ len = MIN(slen, siz - 1);
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ }
+
+ return slen;
+}
+
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+ size_t dlen;
+
+ dlen = strlen(dst);
+ if (dlen >= siz)
+ return strlen(src) + siz;
+
+ return dlen + strlcpy(dst + dlen, src, siz - dlen);
+}
+
#ifndef DALLOC
void *
xmalloc(size_t siz)