/* See LICENSE file for copyright and license details. */ #include "libsimple.h" extern char *argv0; int libsimple_default_failure_exit = 1; void * libsimple_rawmemchr(const void *s_, int c) { char *s = *(char **)(void *)&s_; while ((int)*s++ != c); return &s[-1]; } void * libsimple_memrchr(const void *s_, int c, size_t n_) { char *s = *(char **)(void *)&s_; ssize_t n = n_; while (n-- && (int)s[n] != c); return n < 0 ? NULL : &s[n]; } void * libsimple_rawmemrchr(const void *s_, int c, size_t n) { char *s = *(char **)(void *)&s_; while ((int)s[--n] != c); return &s[n]; } char * libsimple_strchrnul(const char *s_, int c) { char *s = *(char **)(void *)&s_; for (; *s && (int)*s != c; s++) return s; } void * libsimple_memdup(const void *s, size_t n) { void *ret = malloc(n); if (!ret) return NULL; return memcpy(ret, s, n); } char * libsimple_strndup(const char *s, size_t n) { void *ret; if (n == SIZE_MAX) { errno = ENOMEM; return NULL; } if (!(ret = malloc(n + 1))) return NULL; memcpy(ret, s, n); ((char *)ret)[n] = '\0'; return ret; } int libsimple_isutf8(const char *string, int allow_modified_nul) { static long BYTES_TO_MIN_BITS[] = {0, 0, 8, 12, 17, 22, 37}; static long BYTES_TO_MAX_BITS[] = {0, 7, 11, 16, 21, 26, 31}; long int bytes = 0, read_bytes = 0, bits = 0, c, character; /* min bits max bits 0....... 0 7 110..... 10...... 8 11 1110.... 10...... 10...... 12 16 11110... 10...... 10...... 10...... 17 21 111110.. 10...... 10...... 10...... 10...... 22 26 1111110. 10...... 10...... 10...... 10...... 10...... 27 31 */ while ((c = (long int)(*string++))) { if (!read_bytes) { /* First byte of the character. */ if (!(c & 0x80)) /* Single-byte character. */ continue; if ((c & 0xC0) == 0x80) /* Single-byte character marked as multibyte, or a non-first byte in a multibyte character. */ return -1; /* Multibyte character. */ while ((c & 0x80)) bytes++, c <<= 1; read_bytes = 1; character = c & 0x7F; if (bytes > 6) /* 31-bit characters can be encoded with 6-bytes, and UTF-8 does not cover higher code points. */ return -1; } else { /* Not first byte of the character. */ if ((c & 0xC0) != 0x80) /* Beginning of new character before a multibyte character has ended. */ return -1; character = (character << 6) | (c & 0x7F); if (++read_bytes < bytes) /* Not at last byte yet. */ continue; /* Check that the character is not unnecessarily long. */ while (character) character >>= 1, bits++; bits = (!bits && bytes == 2 && allow_modified_nul) ? 8 : bits; if (bits < BYTES_TO_MIN_BITS[bytes] || BYTES_TO_MAX_BITS[bytes] < bits) return -1; read_bytes = bytes = bits = 0; } } /* Make sure we did not stop at the middle of a multibyte character. */ return !read_bytes; } int libsimple_asprintf(char **strp, const char *fmt, ...) { va_list ap; int r; va_start(ap, fmt); r = libsimple_vasprintf(strp, fmt, ap); va_end(ap); return r; } int libsimple_vasprintf(char **strp, const char *fmt, va_list ap) { FILE *fp; size_t siz = 0; int ret; *strp = NULL; fp = open_memstream(strp, &siz); if (!fp) goto fail; ret = vfprintf(fp, fmt, ap); if (ret < 0) goto fail; if (fputc(0, fp)) goto fail; fclose(fp); return ret; fail: free(*strp); *strp = NULL; return -1; } void * libsimple_memmem(const void *hay_, size_t hayn, const void *sub_, size_t subn) { char *hay = *(char **)(void *)&hay_, *end; const char *sub = sub_; if (!subn) return hay; if (hayn < subn) return NULL; if (subn == 1) return memchr(hay, *sub, hayn); for (end = &hay[hayn - subn + 1]; hay != end; hay++) if (*hay == *sub && !memcmp(hay, sub, subn)) return hay; return NULL; } char * libsimple_strcasestr(const char *h_, const char *n) { char *h = *(char **)(void *)&h_; size_t hn = strlen(h); size_t nn = strlen(n); if (hn < nn) return NULL; for (hn -= nn; hn--; h++) if (!strcasecmp(h, n)) return h; return NULL; } int libsimple_memstarts(const void *s_, size_t n, const void *t_, size_t m) { const char *s = s_, *t = t_; size_t i = 0; if (n < m) return 0; while (i < m && s[i] == t[i]) i++; return i == m; } int libsimple_memends(const void *s_, size_t n, const void *t_, size_t m) { const char *s = s_, *t = t_; if (n < m) return 0; while (n--, m--) if (s[n] != t[m]) return 0; return 1; } int libsimple_strstarts(const char *s, const char *t) { for (; *t && *s == *t; s++, t++); return !*t; } int libsimple_strends(const char *s, const char *t) { return memends(s, strlen(s), t, strlen(t)); } static inline size_t alloc_size_product(size_t n, va_list ap) { size_t prod = n; if (!n) { errno = EINVAL; return 0; } for (;;) { n = va_arg(ap, size_t); if (!n) break; if (n >= SIZE_MAX / prod) { errno = ENOMEM; return 0; } prod *= n; } return prod; } void * libsimple_vmalloczn(int clear, size_t n, va_list ap) { n = alloc_size_product(n, ap); return !n ? NULL : clear ? calloc(1, n) : malloc(n); } void * libsimple_vreallocn(void *ptr, size_t n, va_list ap) { n = alloc_size_product(n, ap); return !n ? NULL : realloc(ptr, n); } void * enmalloc(int status, size_t n) { void *ret = malloc(n); if (!ret) { fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno)); exit(status); } return ret; } void * encalloc(int status, size_t n, size_t m) { void *ret = calloc(n, m); if (!ret) { fprintf(stderr, "%s: calloc: %s\n", argv0, strerror(errno)); exit(status); } return ret; } void * enrealloc(int status, void *ptr, size_t n) { char *ret = realloc(ptr, n); if (!ret) { fprintf(stderr, "%s: realloc: %s\n", argv0, strerror(errno)); exit(status); } return ret; } char * enstrdup(int status, const char *s) { char *ret = strdup(s); if (!ret) { fprintf(stderr, "%s: strdup: %s\n", argv0, strerror(errno)); exit(status); } return ret; } char * enstrndup(int status, const char *s, size_t n) { void *ret = strndup(s, n); if (!ret) { fprintf(stderr, "%s: strndup: %s\n", argv0, strerror(errno)); exit(status); } return ret; } void * enmemdup(int status, const void *s, size_t n) { void *ret = memdup(s, n); if (!ret) { fprintf(stderr, "%s: memdup: %s\n", argv0, strerror(errno)); exit(status); } return ret; } void * libsimple_envmalloczn(int status, int clear, size_t n, va_list ap) { void *ret = libsimple_vmalloczn(clear, n, ap); if (!ret) { fprintf(stderr, "%s: %s: %s\n", argv0, clear ? "calloc" : "malloc", strerror(errno)); exit(status); } return ret; } void * libsimple_envreallocn(int status, void *ptr, size_t n, va_list ap) { void *ret = libsimple_vreallocn(ptr, n, ap); if (!ret) { fprintf(stderr, "%s: realloc: %s\n", argv0, strerror(errno)); exit(status); } return ret; } int vputenvf(const char *fmt, va_list ap) { va_list ap2; int n; char *s; va_copy(ap2, ap); n = vsnprintf(NULL, 0, fmt, ap2); va_end(ap2); if (n < 0) return -1; if ((size_t)n == SIZE_MAX) { errno = ENOMEM; return -1; } s = alloca((size_t)n + 1); vsprintf(s, fmt, ap); return putenv(s); } void envputenvf(int status, const char *fmt, va_list ap) { if (vputenvf(fmt, ap)) { fprintf(stderr, "%s: putenvf: %s\n", argv0, strerror(errno)); exit(status); } } void vweprintf(const char *fmt, va_list ap) { int saved_errno = errno, r; const char *end = strchr(fmt, '\0'); const char *prefix1 = argv0; const char *prefix2 = ": "; const char *suffix1 = ""; const char *suffix2 = ""; const char *suffix3 = ""; char *message = NULL; va_list ap1; va_list ap2; if (!argv0 || !strncmp(fmt, "usage: ", strlen("usage: "))) prefix1 = prefix2 = ""; va_copy(ap1, ap); va_copy(ap2, ap); r = vsnprintf(NULL, 0, fmt, ap1); if (0 <= r && (size_t)r < SIZE_MAX) { message = alloca((size_t)r + 1); vsprintf(message, fmt, ap2); } va_end(ap2); va_end(ap1); if (*end == ':') { suffix1 = " "; suffix2 = strerror(saved_errno); suffix3 = "\n"; } else if (*end != '\n') { suffix1 = "\n"; } if (message) { /* This is to avoid mangling when multiple processes are writting. */ fprintf(stderr, "%s%s%s%s%s%s", prefix1, prefix2, message, suffix1, suffix2, suffix3); } else { fprintf(stderr, "%s%s", prefix1, prefix2); vfprintf(stderr, fmt, ap); fprintf(stderr, "%s%s%s", suffix1, suffix2, suffix3); } errno = saved_errno; }