commit 21ef5111f4ced2aa63a2c18c97d47750b966e3dc
parent 7b0c6311f7d2357b5fc79bfe918700f47e300389
Author: Brian Swetland <swetland@frotz.net>
Date: Fri, 22 Apr 2022 11:13:26 -0700
libc: import minimal subset of lk libc
as of lk at ad246760ff47ca4f1f8484ec0726849e0360e600 2019.07.13
- pick up s*printf(), atoi() and friends, ctype utils, and
the common mem*() and str*() functions
- disable long long and float support in printf
- WITH_PRINTF_LL and WITH_PRINTF_FP can re-enable
- use stdint.h instead of sys/types.h
Diffstat:
29 files changed, 1579 insertions(+), 0 deletions(-)
diff --git a/libc/inc/assert.h b/libc/inc/assert.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#ifdef __cplusplus
+// static_assert(a, b) already exists
+#else
+#define static_assert(a, b) _Static_assert(a, b)
+#endif
+
diff --git a/libc/inc/ctype.h b/libc/inc/ctype.h
@@ -0,0 +1,16 @@
+#pragma once
+
+int isblank(int c);
+int isspace(int c);
+int islower(int c);
+int isupper(int c);
+int isdigit(int c);
+int isalpha(int c);
+int isalnum(int c);
+int isxdigit(int c);
+int isgraph(int c);
+int iscntrl(int c);
+int isprint(int c);
+int ispunct(int c);
+int tolower(int c);
+int toupper(int c);
diff --git a/libc/inc/errno.h b/libc/inc/errno.h
diff --git a/libc/inc/stdio.h b/libc/inc/stdio.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stdarg.h>
+#include <stddef.h>
+
+int vsprintf(char *str, const char *format, va_list ap);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+int sprintf(char *str, const char *fmt, ...);
+int snprintf(char *str, size_t len, const char *fmt, ...);
diff --git a/libc/inc/stdlib.h b/libc/inc/stdlib.h
diff --git a/libc/inc/string.h b/libc/inc/string.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#pragma once
+
+#include <stddef.h>
+
+#define __PURE
+#define __CONST
+#define __MALLOC
+
+void *memchr (void const *, int, size_t) __PURE;
+int memcmp (void const *, const void *, size_t) __PURE;
+void *memcpy (void *, void const *, size_t);
+void *memmove(void *, void const *, size_t);
+void *memset (void *, int, size_t);
+
+char *strcat(char *, char const *);
+char *strchr(char const *, int) __PURE;
+int strcmp(char const *, char const *) __PURE;
+char *strcpy(char *, char const *);
+char const *strerror(int) __CONST;
+size_t strlen(char const *) __PURE;
+char *strncat(char *, char const *, size_t);
+int strncmp(char const *, char const *, size_t) __PURE;
+char *strncpy(char *, char const *, size_t);
+char *strpbrk(char const *, char const *) __PURE;
+char *strrchr(char const *, int) __PURE;
+size_t strspn(char const *, char const *) __PURE;
+size_t strcspn(const char *s, const char *) __PURE;
+char *strstr(char const *, char const *) __PURE;
+char *strtok(char *, char const *);
+int strcoll(const char *s1, const char *s2) __PURE;
+size_t strxfrm(char *dest, const char *src, size_t n) __PURE;
+char *strdup(const char *str) __MALLOC;
+
+/* non standard */
+void bcopy(void const *, void *, size_t);
+void bzero(void *, size_t);
+size_t strlcat(char *, char const *, size_t);
+size_t strlcpy(char *, char const *, size_t);
+int strncasecmp(char const *, char const *, size_t) __PURE;
+int strnicmp(char const *, char const *, size_t) __PURE;
+size_t strnlen(char const *s, size_t count) __PURE;
+
diff --git a/libc/src/atoi.c b/libc/src/atoi.c
@@ -0,0 +1,172 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+
+#include <limits.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#define LONG_IS_INT 1
+
+static int hexval(char c) {
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return 0;
+}
+
+int atoi(const char *num) {
+#if !LONG_IS_INT
+ // XXX fail
+#else
+ return atol(num);
+#endif
+}
+
+unsigned int atoui(const char *num) {
+#if !LONG_IS_INT
+ // XXX fail
+#else
+ return atoul(num);
+#endif
+}
+
+long atol(const char *num) {
+ long value = 0;
+ int neg = 0;
+
+ if (num[0] == '0' && num[1] == 'x') {
+ // hex
+ num += 2;
+ while (*num && isxdigit(*num))
+ value = value * 16 + hexval(*num++);
+ } else {
+ // decimal
+ if (num[0] == '-') {
+ neg = 1;
+ num++;
+ }
+ while (*num && isdigit(*num))
+ value = value * 10 + *num++ - '0';
+ }
+
+ if (neg)
+ value = -value;
+
+ return value;
+}
+
+unsigned long atoul(const char *num) {
+ unsigned long value = 0;
+ if (num[0] == '0' && num[1] == 'x') {
+ // hex
+ num += 2;
+ while (*num && isxdigit(*num))
+ value = value * 16 + hexval(*num++);
+ } else {
+ // decimal
+ while (*num && isdigit(*num))
+ value = value * 10 + *num++ - '0';
+ }
+
+ return value;
+}
+
+unsigned long long atoull(const char *num) {
+ unsigned long long value = 0;
+ if (num[0] == '0' && num[1] == 'x') {
+ // hex
+ num += 2;
+ while (*num && isxdigit(*num))
+ value = value * 16 + hexval(*num++);
+ } else {
+ // decimal
+ while (*num && isdigit(*num))
+ value = value * 10 + *num++ - '0';
+ }
+
+ return value;
+}
+
+unsigned long strtoul(const char *nptr, char **endptr, int base) {
+ int neg = 0;
+ unsigned long ret = 0;
+
+ if (base < 0 || base == 1 || base > 36) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ while (isspace(*nptr)) {
+ nptr++;
+ }
+
+ if (*nptr == '+') {
+ nptr++;
+ } else if (*nptr == '-') {
+ neg = 1;
+ nptr++;
+ }
+
+ if ((base == 0 || base == 16) && nptr[0] == '0' && nptr[1] == 'x') {
+ base = 16;
+ nptr += 2;
+ } else if (base == 0 && nptr[0] == '0') {
+ base = 8;
+ nptr++;
+ } else if (base == 0) {
+ base = 10;
+ }
+
+ for (;;) {
+ char c = *nptr;
+ int v = -1;
+ unsigned long new_ret;
+
+ if (c >= 'A' && c <= 'Z') {
+ v = c - 'A' + 10;
+ } else if (c >= 'a' && c <= 'z') {
+ v = c - 'a' + 10;
+ } else if (c >= '0' && c <= '9') {
+ v = c - '0';
+ }
+
+ if (v < 0 || v >= base) {
+ if (endptr) {
+ *endptr = (char *) nptr;
+ }
+ break;
+ }
+
+ new_ret = ret * base;
+ if (new_ret / base != ret ||
+ new_ret + v < new_ret ||
+ ret == ULONG_MAX) {
+ ret = ULONG_MAX;
+ errno = ERANGE;
+ } else {
+ ret = new_ret + v;
+ }
+
+ nptr++;
+ }
+
+ if (neg && ret != ULONG_MAX) {
+ ret = -ret;
+ }
+
+ return ret;
+}
diff --git a/libc/src/ctype.c b/libc/src/ctype.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <ctype.h>
+
+int isblank(int c) {
+ return (c == ' ' || c == '\t');
+}
+
+int isspace(int c) {
+ return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v');
+}
+
+int islower(int c) {
+ return ((c >= 'a') && (c <= 'z'));
+}
+
+int isupper(int c) {
+ return ((c >= 'A') && (c <= 'Z'));
+}
+
+int isdigit(int c) {
+ return ((c >= '0') && (c <= '9'));
+}
+
+int isalpha(int c) {
+ return isupper(c) || islower(c);
+}
+
+int isalnum(int c) {
+ return isalpha(c) || isdigit(c);
+}
+
+int isxdigit(int c) {
+ return isdigit(c) || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F'));
+}
+
+int isgraph(int c) {
+ return ((c > ' ') && (c < 0x7f));
+}
+
+int iscntrl(int c) {
+ return ((c < ' ') || (c == 0x7f));
+}
+
+int isprint(int c) {
+ return ((c >= 0x20) && (c < 0x7f));
+}
+
+int ispunct(int c) {
+ return isgraph(c) && (!isalnum(c));
+}
+
+int tolower(int c) {
+ if ((c >= 'A') && (c <= 'Z'))
+ return c + ('a' - 'A');
+ return c;
+}
+
+int toupper(int c) {
+ if ((c >= 'a') && (c <= 'z'))
+ return c + ('A' - 'a');
+ return c;
+}
+
diff --git a/libc/src/printf-engine.h b/libc/src/printf-engine.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#pragma once
+
+#include <stdarg.h>
+#include <stddef.h>
+
+/* printf engine that parses the format string and generates output */
+
+/* function pointer to pass the printf engine, called back during the formatting.
+ * input is a string to output, length bytes to output,
+ * return code is number of characters that would have been written, or error code (if negative)
+ */
+typedef int (*_printf_engine_output_func)(const char *str, size_t len, void *state);
+
+int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap);
+
diff --git a/libc/src/printf.c b/libc/src/printf.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2008-2014 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "printf-engine.h"
+
+#define __NO_INLINE __attribute((noinline))
+
+#ifndef WITH_PRINTF_FP
+#define WITH_PRINTF_FP 0
+#endif
+#ifndef WITH_PRINTF_LL
+#define WITH_PRINTF_LL 0
+#endif
+
+int sprintf(char *str, const char *fmt, ...) {
+ int err;
+
+ va_list ap;
+ va_start(ap, fmt);
+ err = vsprintf(str, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+int snprintf(char *str, size_t len, const char *fmt, ...) {
+ int err;
+
+ va_list ap;
+ va_start(ap, fmt);
+ err = vsnprintf(str, len, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+int vsprintf(char *str, const char *fmt, va_list ap) {
+ return vsnprintf(str, INT_MAX, fmt, ap);
+}
+
+struct _output_args {
+ char *outstr;
+ size_t len;
+ size_t pos;
+};
+
+static int _vsnprintf_output(const char *str, size_t len, void *state) {
+ struct _output_args *args = state;
+
+ size_t count = 0;
+ while (count < len) {
+ if (args->pos < args->len) {
+ args->outstr[args->pos++] = *str;
+ }
+
+ str++;
+ count++;
+ }
+
+ return count;
+}
+
+int vsnprintf(char *str, size_t len, const char *fmt, va_list ap) {
+ struct _output_args args;
+ int wlen;
+
+ args.outstr = str;
+ args.len = len;
+ args.pos = 0;
+
+ wlen = _printf_engine(&_vsnprintf_output, (void *)&args, fmt, ap);
+ if (args.pos >= len)
+ str[len-1] = '\0';
+ else
+ str[wlen] = '\0';
+ return wlen;
+}
+
+#define LONGFLAG 0x00000001
+#define LONGLONGFLAG 0x00000002
+#define HALFFLAG 0x00000004
+#define HALFHALFFLAG 0x00000008
+#define SIZETFLAG 0x00000010
+#define INTMAXFLAG 0x00000020
+#define PTRDIFFFLAG 0x00000040
+#define ALTFLAG 0x00000080
+#define CAPSFLAG 0x00000100
+#define SHOWSIGNFLAG 0x00000200
+#define SIGNEDFLAG 0x00000400
+#define LEFTFORMATFLAG 0x00000800
+#define LEADZEROFLAG 0x00001000
+#define BLANKPOSFLAG 0x00002000
+
+#if WITH_PRINTF_LL
+typedef unsigned long long x_ull_t;
+typedef long long x_ll_t;
+#else
+typedef unsigned long x_ull_t;
+typedef long x_ll_t;
+#endif
+
+__NO_INLINE static char *longlong_to_string(char *buf, x_ull_t n, size_t len, unsigned flag, char *signchar) {
+ size_t pos = len;
+ int negative = 0;
+
+ if ((flag & SIGNEDFLAG) && (x_ll_t)n < 0) {
+ negative = 1;
+ n = -n;
+ }
+
+ buf[--pos] = 0;
+
+ /* only do the math if the number is >= 10 */
+ while (n >= 10) {
+ int digit = n % 10;
+
+ n /= 10;
+
+ buf[--pos] = digit + '0';
+ }
+ buf[--pos] = n + '0';
+
+ if (negative)
+ *signchar = '-';
+ else if ((flag & SHOWSIGNFLAG))
+ *signchar = '+';
+ else if ((flag & BLANKPOSFLAG))
+ *signchar = ' ';
+ else
+ *signchar = '\0';
+
+ return &buf[pos];
+}
+
+static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+__NO_INLINE static char *longlong_to_hexstring(char *buf, x_ull_t u, size_t len, unsigned flag) {
+ size_t pos = len;
+ const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
+
+ buf[--pos] = 0;
+ do {
+ unsigned int digit = u % 16;
+ u /= 16;
+
+ buf[--pos] = table[digit];
+ } while (u != 0);
+
+ return &buf[pos];
+}
+
+#if WITH_PRINTF_FP
+union double_int {
+ double d;
+ uint64_t i;
+};
+
+#define OUT(c) buf[pos++] = (c)
+#define OUTSTR(str) do { for (size_t i = 0; (str)[i] != 0; i++) OUT((str)[i]); } while (0)
+
+/* print up to a 4 digit exponent as string, with sign */
+__NO_INLINE static size_t exponent_to_string(char *buf, int32_t exponent) {
+ size_t pos = 0;
+
+ /* handle sign */
+ if (exponent < 0) {
+ OUT('-');
+ exponent = -exponent;
+ } else {
+ OUT('+');
+ }
+
+ /* see how far we need to bump into the string to print from the right */
+ if (exponent >= 1000) pos += 4;
+ else if (exponent >= 100) pos += 3;
+ else if (exponent >= 10) pos += 2;
+ else pos++;
+
+ /* print decimal string, from the right */
+ unsigned i = pos;
+ do {
+ unsigned digit = (uint32_t)exponent % 10;
+
+ buf[--i] = digit + '0';
+
+ exponent /= 10;
+ } while (exponent != 0);
+
+ /* return number of characters printed */
+ return pos;
+}
+
+__NO_INLINE static char *double_to_string(char *buf, size_t len, double d, unsigned flag) {
+ size_t pos = 0;
+ union double_int u = { d };
+
+ uint32_t exponent = (u.i >> 52) & 0x7ff;
+ uint64_t fraction = (u.i & ((1ULL << 52) - 1));
+ bool neg = !!(u.i & (1ULL << 63));
+
+ /* start constructing the string */
+ if (neg) {
+ OUT('-');
+ d = -d;
+ }
+
+ /* longest:
+ * 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000o
+ */
+
+ /* look for special cases */
+ if (exponent == 0x7ff) {
+ if (fraction == 0) {
+ /* infinity */
+ if (flag & CAPSFLAG) OUTSTR("INF");
+ else OUTSTR("inf");
+ } else {
+ /* NaN */
+ if (flag & CAPSFLAG) OUTSTR("NAN");
+ else OUTSTR("nan");
+ }
+ } else if (exponent == 0) {
+ if (fraction == 0) {
+ /* zero */
+ OUTSTR("0.000000");
+ } else {
+ /* denormalized */
+ /* XXX does not handle */
+ if (flag & CAPSFLAG) OUTSTR("DEN");
+ else OUTSTR("den");
+ }
+ } else {
+ /* see if it's in the range of floats we can easily print */
+ int exponent_signed = exponent - 1023;
+ if (exponent_signed < -52 || exponent_signed > 52) {
+ OUTSTR("<range>");
+ } else {
+ /* start by walking backwards through the string */
+#define OUTREV(c) do { if (&buf[pos] == buf) goto done; else buf[--pos] = (c); } while (0)
+ pos = len;
+ OUTREV(0);
+
+ /* reserve space for the fractional component first */
+ for (int i = 0; i <= 6; i++)
+ OUTREV('0');
+ size_t decimal_spot = pos;
+
+ /* print the integer portion */
+ uint64_t u;
+ if (exponent_signed >= 0) {
+ u = fraction;
+ u |= (1ULL<<52);
+ u >>= (52 - exponent_signed);
+
+ char *s = longlong_to_string(buf, u, pos + 1, flag, &(char) {0});
+
+ pos = s - buf;
+ } else {
+ /* exponent is negative */
+ u = 0;
+ OUTREV('0');
+ }
+
+ buf[decimal_spot] = '.';
+
+ /* handle the fractional part */
+ uint32_t frac = ((d - u) * 1000000) + .5;
+
+ unsigned i = decimal_spot + 6 + 1;
+ while (frac != 0) {
+ unsigned digit = frac % 10;
+
+ buf[--i] = digit + '0';
+
+ frac /= 10;
+ }
+
+ if (neg)
+ OUTREV('-');
+
+done:
+ /* separate return path, since we've been walking backwards through the string */
+ return &buf[pos];
+ }
+#undef OUTREV
+ }
+
+ buf[pos] = 0;
+ return buf;
+}
+
+__NO_INLINE static char *double_to_hexstring(char *buf, size_t len, double d, unsigned flag) {
+ size_t pos = 0;
+ union double_int u = { d };
+
+ uint32_t exponent = (u.i >> 52) & 0x7ff;
+ uint64_t fraction = (u.i & ((1ULL << 52) - 1));
+ bool neg = !!(u.i & (1ULL << 63));
+
+ /* start constructing the string */
+ if (neg) {
+ OUT('-');
+ }
+
+ /* look for special cases */
+ if (exponent == 0x7ff) {
+ if (fraction == 0) {
+ /* infinity */
+ if (flag & CAPSFLAG) OUTSTR("INF");
+ else OUTSTR("inf");
+ } else {
+ /* NaN */
+ if (flag & CAPSFLAG) OUTSTR("NAN");
+ else OUTSTR("nan");
+ }
+ } else if (exponent == 0) {
+ if (fraction == 0) {
+ /* zero */
+ if (flag & CAPSFLAG) OUTSTR("0X0P+0");
+ else OUTSTR("0x0p+0");
+ } else {
+ /* denormalized */
+ /* XXX does not handle */
+ if (flag & CAPSFLAG) OUTSTR("DEN");
+ else OUTSTR("den");
+ }
+ } else {
+ /* regular normalized numbers:
+ * 0x1p+1
+ * 0x1.0000000000001p+1
+ * 0X1.FFFFFFFFFFFFFP+1023
+ * 0x1.FFFFFFFFFFFFFP+1023
+ */
+ int exponent_signed = exponent - 1023;
+
+ /* implicit 1. */
+ if (flag & CAPSFLAG) OUTSTR("0X1");
+ else OUTSTR("0x1");
+
+ /* select the appropriate hex case table */
+ const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
+
+ int zero_count = 0;
+ bool output_dot = false;
+ for (int i = 52 - 4; i >= 0; i -= 4) {
+ unsigned digit = (fraction >> i) & 0xf;
+
+ if (digit == 0) {
+ zero_count++;
+ } else {
+ /* output a . the first time we output a char */
+ if (!output_dot) {
+ OUT('.');
+ output_dot = true;
+ }
+ /* if we have a non zero digit, see if we need to output a string of zeros */
+ while (zero_count > 0) {
+ OUT('0');
+ zero_count--;
+ }
+ buf[pos++] = table[digit];
+ }
+ }
+
+ /* handle the exponent */
+ buf[pos++] = (flag & CAPSFLAG) ? 'P' : 'p';
+ pos += exponent_to_string(&buf[pos], exponent_signed);
+ }
+
+ buf[pos] = 0;
+ return buf;
+}
+
+#undef OUT
+#undef OUTSTR
+
+#endif
+
+int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap) {
+ int err = 0;
+ char c;
+ unsigned char uc;
+ const char *s;
+ size_t string_len;
+ x_ull_t n;
+ void *ptr;
+ int flags;
+ unsigned int format_num;
+ char signchar;
+ size_t chars_written = 0;
+ char num_buffer[32];
+
+#define OUTPUT_STRING(str, len) do { err = out(str, len, state); if (err < 0) { goto exit; } else { chars_written += err; } } while(0)
+#define OUTPUT_CHAR(c) do { char __temp[1] = { c }; OUTPUT_STRING(__temp, 1); } while (0)
+
+ for (;;) {
+ /* reset the format state */
+ flags = 0;
+ format_num = 0;
+ signchar = '\0';
+
+ /* handle regular chars that aren't format related */
+ s = fmt;
+ string_len = 0;
+ while ((c = *fmt++) != 0) {
+ if (c == '%')
+ break; /* we saw a '%', break and start parsing format */
+ string_len++;
+ }
+
+ /* output the string we've accumulated */
+ OUTPUT_STRING(s, string_len);
+
+ /* make sure we haven't just hit the end of the string */
+ if (c == 0)
+ break;
+
+next_format:
+ /* grab the next format character */
+ c = *fmt++;
+ if (c == 0)
+ break;
+
+ switch (c) {
+ case '0'...'9':
+ if (c == '0' && format_num == 0)
+ flags |= LEADZEROFLAG;
+ format_num *= 10;
+ format_num += c - '0';
+ goto next_format;
+ case '.':
+ /* XXX for now eat numeric formatting */
+ goto next_format;
+ case '%':
+ OUTPUT_CHAR('%');
+ break;
+ case 'c':
+ uc = va_arg(ap, unsigned int);
+ OUTPUT_CHAR(uc);
+ break;
+ case 's':
+ s = va_arg(ap, const char *);
+ if (s == 0)
+ s = "<null>";
+ flags &= ~LEADZEROFLAG; /* doesn't make sense for strings */
+ goto _output_string;
+ case '-':
+ flags |= LEFTFORMATFLAG;
+ goto next_format;
+ case '+':
+ flags |= SHOWSIGNFLAG;
+ goto next_format;
+ case ' ':
+ flags |= BLANKPOSFLAG;
+ goto next_format;
+ case '#':
+ flags |= ALTFLAG;
+ goto next_format;
+ case 'l':
+ if (flags & LONGFLAG)
+ flags |= LONGLONGFLAG;
+ flags |= LONGFLAG;
+ goto next_format;
+ case 'h':
+ if (flags & HALFFLAG)
+ flags |= HALFHALFFLAG;
+ flags |= HALFFLAG;
+ goto next_format;
+ case 'z':
+ flags |= SIZETFLAG;
+ goto next_format;
+ case 'j':
+ flags |= INTMAXFLAG;
+ goto next_format;
+ case 't':
+ flags |= PTRDIFFFLAG;
+ goto next_format;
+ case 'i':
+ case 'd':
+ n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) :
+ (flags & LONGFLAG) ? va_arg(ap, long) :
+ (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) :
+ (flags & HALFFLAG) ? (short)va_arg(ap, int) :
+ //(flags & SIZETFLAG) ? va_arg(ap, ssize_t) : // no ssize_t available
+ (flags & INTMAXFLAG) ? va_arg(ap, intmax_t) :
+ (flags & PTRDIFFFLAG) ? va_arg(ap, ptrdiff_t) :
+ va_arg(ap, int);
+ flags |= SIGNEDFLAG;
+ s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
+ goto _output_string;
+ case 'u':
+ n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
+ (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
+ (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
+ (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
+ (flags & SIZETFLAG) ? va_arg(ap, size_t) :
+ (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
+ (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
+ va_arg(ap, unsigned int);
+ s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
+ goto _output_string;
+ case 'p':
+ flags |= LONGFLAG | ALTFLAG;
+ goto hex;
+ case 'X':
+ flags |= CAPSFLAG;
+ /* fallthrough */
+hex:
+ case 'x':
+ n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
+ (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
+ (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
+ (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
+ (flags & SIZETFLAG) ? va_arg(ap, size_t) :
+ (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
+ (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
+ va_arg(ap, unsigned int);
+ s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags);
+ if (flags & ALTFLAG) {
+ OUTPUT_CHAR('0');
+ OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x');
+ }
+ goto _output_string;
+ case 'n':
+ ptr = va_arg(ap, void *);
+ if (flags & LONGLONGFLAG)
+ *(long long *)ptr = chars_written;
+ else if (flags & LONGFLAG)
+ *(long *)ptr = chars_written;
+ else if (flags & HALFHALFFLAG)
+ *(signed char *)ptr = chars_written;
+ else if (flags & HALFFLAG)
+ *(short *)ptr = chars_written;
+ else if (flags & SIZETFLAG)
+ *(size_t *)ptr = chars_written;
+ else
+ *(int *)ptr = chars_written;
+ break;
+#if WITH_PRINTF_FP
+ case 'F':
+ flags |= CAPSFLAG;
+ /* fallthrough */
+ case 'f': {
+ double d = va_arg(ap, double);
+ s = double_to_string(num_buffer, sizeof(num_buffer), d, flags);
+ goto _output_string;
+ }
+ case 'A':
+ flags |= CAPSFLAG;
+ /* fallthrough */
+ case 'a': {
+ double d = va_arg(ap, double);
+ s = double_to_hexstring(num_buffer, sizeof(num_buffer), d, flags);
+ goto _output_string;
+ }
+#endif
+ default:
+ OUTPUT_CHAR('%');
+ OUTPUT_CHAR(c);
+ break;
+ }
+
+ /* move on to the next field */
+ continue;
+
+ /* shared output code */
+_output_string:
+ string_len = strlen(s);
+
+ if (flags & LEFTFORMATFLAG) {
+ /* left justify the text */
+ OUTPUT_STRING(s, string_len);
+ unsigned written = err;
+
+ /* pad to the right (if necessary) */
+ for (; format_num > written; format_num--)
+ OUTPUT_CHAR(' ');
+ } else {
+ /* right justify the text (digits) */
+
+ /* if we're going to print a sign digit,
+ it'll chew up one byte of the format size */
+ if (signchar != '\0' && format_num > 0)
+ format_num--;
+
+ /* output the sign char before the leading zeros */
+ if (flags & LEADZEROFLAG && signchar != '\0')
+ OUTPUT_CHAR(signchar);
+
+ /* pad according to the format string */
+ for (; format_num > string_len; format_num--)
+ OUTPUT_CHAR(flags & LEADZEROFLAG ? '0' : ' ');
+
+ /* if not leading zeros, output the sign char just before the number */
+ if (!(flags & LEADZEROFLAG) && signchar != '\0')
+ OUTPUT_CHAR(signchar);
+
+ /* output the string */
+ OUTPUT_STRING(s, string_len);
+ }
+ continue;
+ }
+
+#undef OUTPUT_STRING
+#undef OUTPUT_CHAR
+
+exit:
+ return (err < 0) ? err : (int)chars_written;
+}
diff --git a/libc/src/string/memchr.c b/libc/src/string/memchr.c
@@ -0,0 +1,29 @@
+/*
+** Copyright 2001, Manuel J. Petit. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+void *
+memchr(void const *buf, int c, size_t len) {
+ size_t i;
+ unsigned char const *b= buf;
+ unsigned char x= (c&0xff);
+
+ for (i= 0; i< len; i++) {
+ if (b[i]== x) {
+ return (void *)(b+i);
+ }
+ }
+
+ return NULL;
+}
+
diff --git a/libc/src/string/memcmp.c b/libc/src/string/memcmp.c
@@ -0,0 +1,24 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+int
+memcmp(const void *cs, const void *ct, size_t count) {
+ const unsigned char *su1, *su2;
+ signed char res = 0;
+
+ for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+ if ((res = *su1 - *su2) != 0)
+ break;
+ return res;
+}
diff --git a/libc/src/string/memcpy.c b/libc/src/string/memcpy.c
@@ -0,0 +1,53 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+
+#if !_ASM_MEMCPY
+
+typedef long word;
+
+#define lsize sizeof(word)
+#define lmask (lsize - 1)
+
+void *memcpy(void *dest, const void *src, size_t count) {
+ char *d = (char *)dest;
+ const char *s = (const char *)src;
+ int len;
+
+ if (count == 0 || dest == src)
+ return dest;
+
+ if (((long)d | (long)s) & lmask) {
+ // src and/or dest do not align on word boundary
+ if ((((long)d ^ (long)s) & lmask) || (count < lsize))
+ len = count; // copy the rest of the buffer with the byte mover
+ else
+ len = lsize - ((long)d & lmask); // move the ptrs up to a word boundary
+
+ count -= len;
+ for (; len > 0; len--)
+ *d++ = *s++;
+ }
+ for (len = count / lsize; len > 0; len--) {
+ *(word *)d = *(word *)s;
+ d += lsize;
+ s += lsize;
+ }
+ for (len = count & lmask; len > 0; len--)
+ *d++ = *s++;
+
+ return dest;
+}
+
+#endif
diff --git a/libc/src/string/memmove.c b/libc/src/string/memmove.c
@@ -0,0 +1,77 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+#if !_ASM_MEMMOVE
+
+typedef long word;
+
+#define lsize sizeof(word)
+#define lmask (lsize - 1)
+
+void *
+memmove(void *dest, void const *src, size_t count) {
+ char *d = (char *)dest;
+ const char *s = (const char *)src;
+ int len;
+
+ if (count == 0 || dest == src)
+ return dest;
+
+ if ((long)d < (long)s) {
+ if (((long)d | (long)s) & lmask) {
+ // src and/or dest do not align on word boundary
+ if ((((long)d ^ (long)s) & lmask) || (count < lsize))
+ len = count; // copy the rest of the buffer with the byte mover
+ else
+ len = lsize - ((long)d & lmask); // move the ptrs up to a word boundary
+
+ count -= len;
+ for (; len > 0; len--)
+ *d++ = *s++;
+ }
+ for (len = count / lsize; len > 0; len--) {
+ *(word *)d = *(word *)s;
+ d += lsize;
+ s += lsize;
+ }
+ for (len = count & lmask; len > 0; len--)
+ *d++ = *s++;
+ } else {
+ d += count;
+ s += count;
+ if (((long)d | (long)s) & lmask) {
+ // src and/or dest do not align on word boundary
+ if ((((long)d ^ (long)s) & lmask) || (count <= lsize))
+ len = count;
+ else
+ len = ((long)d & lmask);
+
+ count -= len;
+ for (; len > 0; len--)
+ *--d = *--s;
+ }
+ for (len = count / lsize; len > 0; len--) {
+ d -= lsize;
+ s -= lsize;
+ *(word *)d = *(word *)s;
+ }
+ for (len = count & lmask; len > 0; len--)
+ *--d = *--s;
+ }
+
+ return dest;
+}
+
+#endif
+
diff --git a/libc/src/string/memset.c b/libc/src/string/memset.c
@@ -0,0 +1,47 @@
+/*
+** Copyright 2005, Michael Noisternig. All rights reserved.
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+void *
+memset(void *s, int c, size_t count) {
+ char *xs = (char *) s;
+ size_t len = (-(size_t)s) & (sizeof(size_t)-1);
+ size_t cc = c & 0xff;
+
+ if ( count > len ) {
+ count -= len;
+ cc |= cc << 8;
+ cc |= cc << 16;
+ if (sizeof(size_t) == 8)
+ cc |= (uint64_t)cc << 32; // should be optimized out on 32 bit machines
+
+ // write to non-aligned memory byte-wise
+ for ( ; len > 0; len-- )
+ *xs++ = c;
+
+ // write to aligned memory dword-wise
+ for ( len = count/sizeof(size_t); len > 0; len-- ) {
+ *((size_t *)xs) = (size_t)cc;
+ xs += sizeof(size_t);
+ }
+
+ count &= sizeof(size_t)-1;
+ }
+
+ // write remaining bytes
+ for ( ; count > 0; count-- )
+ *xs++ = c;
+
+ return s;
+}
diff --git a/libc/src/string/strcat.c b/libc/src/string/strcat.c
@@ -0,0 +1,26 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strcat(char *dest, char const *src) {
+ char *tmp = dest;
+
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++) != '\0')
+ ;
+
+ return tmp;
+}
+
diff --git a/libc/src/string/strchr.c b/libc/src/string/strchr.c
@@ -0,0 +1,21 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strchr(const char *s, int c) {
+ for (; *s != (char) c; ++s)
+ if (*s == '\0')
+ return NULL;
+ return (char *) s;
+}
diff --git a/libc/src/string/strcmp.c b/libc/src/string/strcmp.c
@@ -0,0 +1,25 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+int
+strcmp(char const *cs, char const *ct) {
+ signed char __res;
+
+ while (1) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ }
+
+ return __res;
+}
diff --git a/libc/src/string/strcpy.c b/libc/src/string/strcpy.c
@@ -0,0 +1,23 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strcpy(char *dest, char const *src) {
+ char *tmp = dest;
+
+ while ((*dest++ = *src++) != '\0')
+ ;
+ return tmp;
+}
+
diff --git a/libc/src/string/strlen.c b/libc/src/string/strlen.c
@@ -0,0 +1,25 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+size_t
+strlen(char const *s) {
+ size_t i;
+
+ i= 0;
+ while (s[i]) {
+ i+= 1;
+ }
+
+ return i;
+}
diff --git a/libc/src/string/strncat.c b/libc/src/string/strncat.c
@@ -0,0 +1,32 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strncat(char *dest, char const *src, size_t count) {
+ char *tmp = dest;
+
+ if (count > 0) {
+ while (*dest)
+ dest++;
+ while ((*dest++ = *src++)) {
+ if (--count == 0) {
+ *dest = '\0';
+ break;
+ }
+ }
+ }
+
+ return tmp;
+}
+
diff --git a/libc/src/string/strncmp.c b/libc/src/string/strncmp.c
@@ -0,0 +1,26 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+int
+strncmp(char const *cs, char const *ct, size_t count) {
+ signed char __res = 0;
+
+ while (count > 0) {
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
+ break;
+ count--;
+ }
+
+ return __res;
+}
diff --git a/libc/src/string/strncpy.c b/libc/src/string/strncpy.c
@@ -0,0 +1,27 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strncpy(char *dest, char const *src, size_t count) {
+ char *tmp = dest;
+
+ size_t i;
+ for (i = 0; i < count && (*dest++ = *src++) != '\0'; i++)
+ ;
+ for (; i < count; i++)
+ *dest++ = '\0';
+
+ return tmp;
+}
+
diff --git a/libc/src/string/strnlen.c b/libc/src/string/strnlen.c
@@ -0,0 +1,22 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+size_t
+strnlen(char const *s, size_t count) {
+ const char *sc;
+
+ for (sc = s; count-- && *sc != '\0'; ++sc)
+ ;
+ return sc - s;
+}
diff --git a/libc/src/string/strpbrk.c b/libc/src/string/strpbrk.c
@@ -0,0 +1,28 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strpbrk(char const *cs, char const *ct) {
+ const char *sc1;
+ const char *sc2;
+
+ for (sc1 = cs; *sc1 != '\0'; ++sc1) {
+ for (sc2 = ct; *sc2 != '\0'; ++sc2) {
+ if (*sc1 == *sc2)
+ return (char *)sc1;
+ }
+ }
+
+ return NULL;
+}
diff --git a/libc/src/string/strrchr.c b/libc/src/string/strrchr.c
@@ -0,0 +1,29 @@
+/*
+** Copyright 2001, Manuel J. Petit. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strrchr(char const *s, int c) {
+ char const *last= c?0:s;
+
+
+ while (*s) {
+ if (*s== c) {
+ last= s;
+ }
+
+ s+= 1;
+ }
+
+ return (char *)last;
+}
diff --git a/libc/src/string/strspn.c b/libc/src/string/strspn.c
@@ -0,0 +1,32 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+size_t
+strspn(char const *s, char const *accept) {
+ const char *p;
+ const char *a;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (a = accept; *a != '\0'; ++a) {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return count;
+ ++count;
+ }
+
+ return count;
+}
diff --git a/libc/src/string/strstr.c b/libc/src/string/strstr.c
@@ -0,0 +1,30 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+char *
+strstr(char const *s1, char const *s2) {
+ int l1, l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ return (char *)s1;
+ l1 = strlen(s1);
+ while (l1 >= l2) {
+ l1--;
+ if (!memcmp(s1,s2,l2))
+ return (char *)s1;
+ s1++;
+ }
+ return NULL;
+}
diff --git a/libc/src/string/strtok.c b/libc/src/string/strtok.c
@@ -0,0 +1,35 @@
+/*
+** Copyright 2001, Travis Geiselbrecht. All rights reserved.
+** Distributed under the terms of the NewOS License.
+*/
+/*
+ * Copyright (c) 2008 Travis Geiselbrecht
+ *
+ * Use of this source code is governed by a MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT
+ */
+#include <string.h>
+#include <stdint.h>
+
+static char *___strtok = NULL;
+
+char *
+strtok(char *s, char const *ct) {
+ char *sbegin, *send;
+
+ sbegin = s ? s : ___strtok;
+ if (!sbegin) {
+ return NULL;
+ }
+ sbegin += strspn(sbegin,ct);
+ if (*sbegin == '\0') {
+ ___strtok = NULL;
+ return ( NULL );
+ }
+ send = strpbrk( sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ___strtok = send;
+ return (sbegin);
+}