Initial import

This commit is contained in:
Justine Tunney 2020-06-15 07:18:57 -07:00
commit c91b3c5006
14915 changed files with 590219 additions and 0 deletions

61
libc/fmt/bing.c Normal file
View file

@ -0,0 +1,61 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/assert.h"
#include "libc/fmt/bing.h"
#include "libc/str/str.h"
/**
* Turns binary octet into unicode glyph representation.
*
* Cosmopolitan displays RADIX-256 numbers using these digits:
*
* 0123456789abcdef
* 0 
* 1§
* 2 !"#$%&'()*+,-./
* 30123456789:;<=>?
* 4@ABCDEFGHIJKLMNO
* 5PQRSTUVWXYZ[\]^_
* 6`abcdefghijklmno
* 7pqrstuvwxyz{|}~
* 8ÇüéâäàåçêëèïîìÄÅ
* 9ÉæÆôöòûùÿÖÜ¢£¥ƒ
* aáíóúñѪº¿¬½¼¡«»
* b
* c
* d
* eαßΓπΣσμτΦΘΩδφε
* f±÷°·²λ
*
* IBM designed these glyphs for the PC to map onto the display bytes at
* (char *)0xb8000. Because IBM authorized buyers of its PCs to inspect
* and/or modify this region of memory, it became widely understood by
* many developers as a quick way to visualize arbitrary data that's so
* superior to hexdump -- a use-case that's lived on longer than the CGA
* graphics card for which it was designed.
*
* @param b is binary octet to pictorally represent
* @param intent controls canonical multimappings:  ,\n,ε, λ,etc.
* @see unbing() for inverse
*/
int bing(int b, int intent) {
assert(0 <= b && b < 256);
return kCp437[b & 0xff]; /* TODO(jart): multimappings */
}

16
libc/fmt/bing.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_FMT_BING_H_
#define COSMOPOLITAN_LIBC_FMT_BING_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int bing(int, int) nosideeffect;
int unbing(int) nosideeffect;
void *unbingbuf(void *, size_t, const char16_t *, int);
void *unbingstr(const char16_t *) paramsnonnull() mallocesque;
void *unhexbuf(void *, size_t, const char *);
void *unhexstr(const char *) mallocesque;
short *bingblit(int ys, int xs, unsigned char[ys][xs], int, int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_BING_H_ */

48
libc/fmt/fmt.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef COSMOPOLITAN_LIBC_FMT_FMT_H_
#define COSMOPOLITAN_LIBC_FMT_FMT_H_
#include "libc/fmt/pflink.h"
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § string formatting
*/
#if __SIZEOF_POINTER__ == 8
#define POINTER_XDIGITS 12 /* math.log(2**48-1,16) */
#else
#define POINTER_XDIGITS 8
#endif
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int snprintf(char *, size_t, const char *, ...) printfesque(3)
paramsnonnull((3)) nothrow nocallback;
int vsnprintf(char *, size_t, const char *, va_list)
paramsnonnull((3)) nothrow nocallback;
int sprintf(char *, const char *, ...) printfesque(2)
paramsnonnull((2)) nothrow nocallback frownedupon(snprintf);
int vsprintf(char *, const char *, va_list)
paramsnonnull((2)) nothrow nocallback frownedupon(vsnprintf);
int sscanf(const char *, const char *, ...) scanfesque(2);
int vsscanf(const char *, const char *, va_list);
int vcscanf(int callback(void *), void *, const char *, va_list);
char *strerror(int) returnsnonnull nothrow nocallback;
int strerror_r(int, char *, size_t) nothrow nocallback;
int palandprintf(void *, void *, const char *, va_list) hidden;
char *itoa(int, char *, int) compatfn;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § string formatting » optimizations
*/
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define sprintf(BUF, FMT, ...) (sprintf)(BUF, PFLINK(FMT), ##__VA_ARGS__)
#define vsprintf(BUF, FMT, VA) (vsprintf)(BUF, PFLINK(FMT), VA)
#define snprintf(B, Z, F, ...) (snprintf)(B, Z, PFLINK(F), ##__VA_ARGS__)
#define vsnprintf(BUF, SIZE, FMT, VA) (vsnprintf)(BUF, SIZE, PFLINK(FMT), VA)
#define sscanf(STR, FMT, ...) (sscanf)(STR, SFLINK(FMT), ##__VA_ARGS__)
#define vsscanf(STR, FMT, VA) (vsscanf)(STR, SFLINK(FMT), VA)
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_FMT_H_ */

67
libc/fmt/fmt.mk Normal file
View file

@ -0,0 +1,67 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
#
# SYNOPSIS
#
# Cosmopolitan String Formatting
#
# DESCRIPTION
#
# This package provides incredible domain-specific languages for
# encoding and decoding strings.
PKGS += LIBC_FMT
LIBC_FMT_ARTIFACTS += LIBC_FMT_A
LIBC_FMT = $(LIBC_FMT_A_DEPS) $(LIBC_FMT_A)
LIBC_FMT_A = o/$(MODE)/libc/fmt/fmt.a
LIBC_FMT_A_FILES := $(wildcard libc/fmt/*)
LIBC_FMT_A_HDRS = $(filter %.h,$(LIBC_FMT_A_FILES))
LIBC_FMT_A_SRCS_S = $(filter %.S,$(LIBC_FMT_A_FILES))
LIBC_FMT_A_SRCS_C = $(filter %.c,$(LIBC_FMT_A_FILES))
LIBC_FMT_A_SRCS = \
$(LIBC_FMT_A_SRCS_S) \
$(LIBC_FMT_A_SRCS_C)
LIBC_FMT_A_OBJS = \
$(LIBC_FMT_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(LIBC_FMT_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(LIBC_FMT_A_SRCS_C:%.c=o/$(MODE)/%.o)
LIBC_FMT_A_CHECKS = \
$(LIBC_FMT_A).pkg \
$(LIBC_FMT_A_HDRS:%=o/$(MODE)/%.ok)
LIBC_FMT_A_DIRECTDEPS = \
LIBC_STUBS \
LIBC_STR \
LIBC_TINYMATH \
LIBC_NEXGEN32E \
LIBC_NT_KERNELBASE \
LIBC_SYSV
LIBC_FMT_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_FMT_A_DIRECTDEPS),$($(x))))
$(LIBC_FMT_A): libc/fmt/ \
$(LIBC_FMT_A).pkg \
$(LIBC_FMT_A_OBJS)
$(LIBC_FMT_A).pkg: \
$(LIBC_FMT_A_OBJS) \
$(foreach x,$(LIBC_FMT_A_DIRECTDEPS),$($(x)_A).pkg)
$(LIBC_FMT_A_OBJS): \
OVERRIDE_CFLAGS += \
-fno-jump-tables
LIBC_FMT_LIBS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)))
LIBC_FMT_SRCS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_SRCS))
LIBC_FMT_HDRS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_HDRS))
LIBC_FMT_CHECKS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_CHECKS))
LIBC_FMT_OBJS = $(foreach x,$(LIBC_FMT_ARTIFACTS),$($(x)_OBJS))
$(LIBC_FMT_OBJS): $(BUILD_FILES) libc/fmt/fmt.mk
.PHONY: o/$(MODE)/libc/fmt
o/$(MODE)/libc/fmt: $(LIBC_FMT_CHECKS)

32
libc/fmt/itoa.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/conv/conv.h"
#include "libc/fmt/fmt.h"
STATIC_YOINK("ntoa");
compatfn char *itoa(int value, char *str, int radix) {
(sprintf)(
str,
VEIL("r",
radix == 16 ? "%x" : radix == 8 ? "%d" : radix == 2 ? "%b" : "%d"),
value);
return str;
}

311
libc/fmt/kerrnonames.S Normal file
View file

@ -0,0 +1,311 @@
/*-*- mode:asm; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify │
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License. │
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of │
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software │
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/notice.inc"
#include "libc/macros.h"
.yoink __FILE__
/ Embeds ASCII names for errno constants into binary.
/
/ @see libc/sysv/consts.sh
.section .rodata,"aS",@progbits
kErrnoNames:
/ <SORTED-ORDER-C>
.asciz "2BIG"
.asciz "ACCES"
.asciz "ADDRINUSE"
.asciz "ADDRNOTAVAIL"
.asciz "ADV"
.asciz "AFNOSUPPORT"
.asciz "AGAIN"
.asciz "ALREADY"
.asciz "BADE"
.asciz "BADF"
.asciz "BADFD"
.asciz "BADMSG"
.asciz "BADR"
.asciz "BADRQC"
.asciz "BADSLT"
.asciz "BFONT"
.asciz "BUSY"
.asciz "CANCELED"
.asciz "CHILD"
.asciz "CHRNG"
.asciz "COMM"
.asciz "CONNABORTED"
.asciz "CONNREFUSED"
.asciz "CONNRESET"
.asciz "DEADLK"
.asciz "DESTADDRREQ"
.asciz "DOM"
.asciz "DOTDOT"
.asciz "DQUOT"
.asciz "EXIST"
.asciz "FAULT"
.asciz "FBIG"
.asciz "HOSTDOWN"
.asciz "HOSTUNREACH"
.asciz "HWPOISON"
.asciz "IDRM"
.asciz "ILSEQ"
.asciz "INPROGRESS"
.asciz "INTR"
.asciz "INVAL"
.asciz "IO"
.asciz "ISCONN"
.asciz "ISDIR"
.asciz "ISNAM"
.asciz "KEYEXPIRED"
.asciz "KEYREJECTED"
.asciz "KEYREVOKED"
.asciz "L2HLT"
.asciz "L2NSYNC"
.asciz "L3HLT"
.asciz "L3RST"
.asciz "LIBACC"
.asciz "LIBBAD"
.asciz "LIBEXEC"
.asciz "LIBMAX"
.asciz "LIBSCN"
.asciz "LNRNG"
.asciz "LOOP"
.asciz "MEDIUMTYPE"
.asciz "MFILE"
.asciz "MLINK"
.asciz "MSGSIZE"
.asciz "MULTIHOP"
.asciz "NAMETOOLONG"
.asciz "NAVAIL"
.asciz "NETDOWN"
.asciz "NETRESET"
.asciz "NETUNREACH"
.asciz "NFILE"
.asciz "NOANO"
.asciz "NOBUFS"
.asciz "NOCSI"
.asciz "NODATA"
.asciz "NODEV"
.asciz "NOENT"
.asciz "NOEXEC"
.asciz "NOKEY"
.asciz "NOLCK"
.asciz "NOLINK"
.asciz "NOMEDIUM"
.asciz "NOMEM"
.asciz "NOMSG"
.asciz "NONET"
.asciz "NOPKG"
.asciz "NOPROTOOPT"
.asciz "NOSPC"
.asciz "NOSR"
.asciz "NOSTR"
.asciz "NOSYS"
.asciz "NOTBLK"
.asciz "NOTCONN"
.asciz "NOTDIR"
.asciz "NOTEMPTY"
.asciz "NOTNAM"
.asciz "NOTRECOVERABLE"
.asciz "NOTSOCK"
.asciz "NOTSUP"
.asciz "NOTTY"
.asciz "NOTUNIQ"
.asciz "NXIO"
.asciz "OPNOTSUPP"
.asciz "OVERFLOW"
.asciz "OWNERDEAD"
.asciz "PERM"
.asciz "PFNOSUPPORT"
.asciz "PIPE"
.asciz "PROTO"
.asciz "PROTONOSUPPORT"
.asciz "PROTOTYPE"
.asciz "RANGE"
.asciz "REMCHG"
.asciz "REMOTE"
.asciz "REMOTEIO"
.asciz "RESTART"
.asciz "RFKILL"
.asciz "ROFS"
.asciz "SHUTDOWN"
.asciz "SOCKTNOSUPPORT"
.asciz "SPIPE"
.asciz "SRCH"
.asciz "SRMNT"
.asciz "STALE"
.asciz "STRPIPE"
.asciz "TIME"
.asciz "TIMEDOUT"
.asciz "TOOMANYREFS"
.asciz "TXTBSY"
.asciz "UCLEAN"
.asciz "UNATCH"
.asciz "USERS"
.asciz "XDEV"
.asciz "XFULL"
/ </SORTED-ORDER-C>
.byte 0
.endobj kErrnoNames,globl,hidden
.previous
/ Pulls errno constants into linkage.
/
/ @assume linker relocates these in sorted order
/ @assume linker invoked as LC_ALL=C ld ...
/ @see libc/sysv/systemfive.S
/ @see libc/sysv/consts/syscon.h
yoink E2BIG
yoink EACCES
yoink EADDRINUSE
yoink EADDRNOTAVAIL
yoink EADV
yoink EAFNOSUPPORT
yoink EAGAIN
yoink EALREADY
yoink EBADE
yoink EBADF
yoink EBADFD
yoink EBADMSG
yoink EBADR
yoink EBADRQC
yoink EBADSLT
yoink EBFONT
yoink EBUSY
yoink ECANCELED
yoink ECHILD
yoink ECHRNG
yoink ECOMM
yoink ECONNABORTED
yoink ECONNREFUSED
yoink ECONNRESET
yoink EDEADLK
yoink EDESTADDRREQ
yoink EDOM
yoink EDOTDOT
yoink EDQUOT
yoink EEXIST
yoink EFAULT
yoink EFBIG
yoink EHOSTDOWN
yoink EHOSTUNREACH
yoink EHWPOISON
yoink EIDRM
yoink EILSEQ
yoink EINPROGRESS
yoink EINTR
yoink EINVAL
yoink EIO
yoink EISCONN
yoink EISDIR
yoink EISNAM
yoink EKEYEXPIRED
yoink EKEYREJECTED
yoink EKEYREVOKED
yoink EL2HLT
yoink EL2NSYNC
yoink EL3HLT
yoink EL3RST
yoink ELIBACC
yoink ELIBBAD
yoink ELIBEXEC
yoink ELIBMAX
yoink ELIBSCN
yoink ELNRNG
yoink ELOOP
yoink EMEDIUMTYPE
yoink EMFILE
yoink EMLINK
yoink EMSGSIZE
yoink EMULTIHOP
yoink ENAMETOOLONG
yoink ENAVAIL
yoink ENETDOWN
yoink ENETRESET
yoink ENETUNREACH
yoink ENFILE
yoink ENOANO
yoink ENOBUFS
yoink ENOCSI
yoink ENODATA
yoink ENODEV
yoink ENOENT
yoink ENOEXEC
yoink ENOKEY
yoink ENOLCK
yoink ENOLINK
yoink ENOMEDIUM
yoink ENOMEM
yoink ENOMSG
yoink ENONET
yoink ENOPKG
yoink ENOPROTOOPT
yoink ENOSPC
yoink ENOSR
yoink ENOSTR
yoink ENOSYS
yoink ENOTBLK
yoink ENOTCONN
yoink ENOTDIR
yoink ENOTEMPTY
yoink ENOTNAM
yoink ENOTRECOVERABLE
yoink ENOTSOCK
yoink ENOTSUP
yoink ENOTTY
yoink ENOTUNIQ
yoink ENXIO
yoink EOPNOTSUPP
yoink EOVERFLOW
yoink EOWNERDEAD
yoink EPERM
yoink EPFNOSUPPORT
yoink EPIPE
yoink EPROTO
yoink EPROTONOSUPPORT
yoink EPROTOTYPE
yoink ERANGE
yoink EREMCHG
yoink EREMOTE
yoink EREMOTEIO
yoink ERESTART
yoink ERFKILL
yoink EROFS
yoink ESHUTDOWN
yoink ESOCKTNOSUPPORT
yoink ESPIPE
yoink ESRCH
yoink ESRMNT
yoink ESTALE
yoink ESTRPIPE
yoink ETIME
yoink ETIMEDOUT
yoink ETOOMANYREFS
yoink ETXTBSY
yoink EUCLEAN
yoink EUNATCH
yoink EUSERS
yoink EXDEV
yoink EXFULL
.type kErrnoStart,@object
.type kErrnoEnd,@object
.globl kErrnoStart, kErrnoEnd, EXFULL, E2BIG
.hidden kErrnoStart, kErrnoEnd
kErrnoStart = E2BIG
kErrnoEnd = EXFULL + 8

46
libc/fmt/mbrtowc.c Normal file
View file

@ -0,0 +1,46 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
static mbstate_t g_mbrtowc;
size_t mbrtowc(wchar_t *pwc, const char *src, size_t n, mbstate_t *ps) {
/* TODO(jart): Need to fix. */
wchar_t t;
mbstate_t c;
const unsigned char *p;
if (!ps) ps = &g_mbrtowc;
p = (const unsigned char *)src;
c = *ps;
if (!p && c) return eilseq();
if (!p) return 0;
if (!pwc) pwc = &t;
if (n) {
if (!c) {
if (0 <= *p && *p < 0x80) {
return !!(*pwc = *p);
}
abort(); /* TODO(jart) */
}
}
return -2;
}

43
libc/fmt/mbsrtowcs.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
#include "libc/str/str.h"
size_t mbsrtowcs(wchar_t *dest, const char **src, size_t len, mbstate_t *ps) {
/* TODO(jart): Need to fix. */
const unsigned char *s = (const void *)*src;
size_t wn0 = len;
unsigned c = 0;
if (!dest) return strlen((const char *)s);
for (;;) {
if (!len) {
*src = (const void *)s;
return wn0;
}
if (!*s) break;
c = *s++;
*dest++ = c;
len--;
}
*dest = 0;
*src = 0;
return wn0 - len;
}

25
libc/fmt/mbstowcs.c Normal file
View file

@ -0,0 +1,25 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
#include "libc/str/str.h"
size_t mbstowcs(wchar_t *pwc, const char *s, size_t wn) {
return mbsrtowcs(pwc, (void *)&s, wn, 0);
}

62
libc/fmt/paland.inc Normal file
View file

@ -0,0 +1,62 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-
│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│
╚══════════════════════════════════════════════════════════════════════════════╝
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal│
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
└─────────────────────────────────────────────────────────────────────────────*/
asm(".ident\t\"\\n\\n\
Paland Printf (MIT License)\\n\
Copyright 2014-2019 Marco Paland\\n\
PALANDesign Hannover, Germany\\n\
info@paland.com\"");
#include "libc/mem/mem.h"
#include "libc/str/internal.h"
#include "libc/sysv/errfuns.h"
/* 'ntoa' conversion buffer size, this must be big enough to hold one
converted numeric number including padded zeros (dynamically created
on stack) default: 64 byte */
#ifndef PRINTF_NTOA_BUFFER_SIZE
#define PRINTF_NTOA_BUFFER_SIZE 64
#endif
/* 'ftoa' conversion buffer size, this must be big enough to hold one
converted float number including padded zeros (dynamically created on
stack) default: 32 byte */
#ifndef PRINTF_FTOA_BUFFER_SIZE
#define PRINTF_FTOA_BUFFER_SIZE 64
#endif
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_PRECISION (1U << 5U)
#define FLAGS_ISSIGNED (1U << 6U)
#define FLAGS_NOQUOTE (1U << 7U)
#define FLAGS_QUOTE FLAGS_SPACE
#define FLAGS_GROUPING FLAGS_NOQUOTE
#define FLAGS_REPR FLAGS_PLUS

174
libc/fmt/palandftoa.c Normal file
View file

@ -0,0 +1,174 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@brief Tiny printf, sprintf and (v)snprintf implementation, optimized for
embedded systems with a very limited resources. These routines are
thread safe and reentrant! Use this instead of the bloated
standard/newlib printf cause these use malloc for printf (and may not
be thread safe).
*/
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
#include "libc/math.h"
static const int kPow10[] = {1, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000};
int ftoa(int out(int, void *), void *arg, long double value, unsigned long prec,
unsigned long width, unsigned long flags) {
long whole, frac;
long double tmp, diff;
unsigned i, len, count, idx;
char buf[PRINTF_FTOA_BUFFER_SIZE];
len = 0;
diff = 0;
if (isnan(value)) {
buf[0] = 'N';
buf[1] = 'A';
buf[2] = 'N';
buf[3] = '\0';
len += 3;
} else if (isinf(value) ||
(/* TODO(jart): need this? */ fabsl(value) > 0x7fffffff)) {
buf[0] = 'Y';
buf[1] = 'T';
buf[2] = 'I';
buf[3] = 'N';
buf[4] = 'I';
buf[5] = 'F';
buf[6] = 'N';
buf[7] = 'I';
buf[8] = '\0';
len += 8;
} else {
/* set default precision to 6, if not set explicitly */
if (!(flags & FLAGS_PRECISION)) {
prec = 6;
}
/* limit precision to 9, cause a prec >= 10 can lead to overflow errors */
while (len < PRINTF_FTOA_BUFFER_SIZE && prec > 9) {
buf[len++] = '0';
prec--;
}
whole = truncl(fabsl(value));
tmp = (fabsl(value) - whole) * kPow10[prec];
frac = tmp;
diff = tmp - frac;
if (diff > 0.5) {
++frac; /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= kPow10[prec]) {
frac = 0;
++whole;
}
} else if (diff < 0.5) {
} else if ((frac == 0U) || (frac & 1U)) {
++frac; /* if halfway, round up if odd OR if last digit is 0 */
}
if (prec == 0U) {
diff = fabsl(value) - whole;
if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
/* exactly 0.5 and ODD, then round up */
/* 1.5 -> 2, but 2.5 -> 2 */
++whole;
}
} else {
count = prec;
/* now do fractional part, as an unsigned number */
while (len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = 48 + (frac % 10);
if (!(frac /= 10)) {
break;
}
}
/* add extra 0s */
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
/* add decimal */
buf[len++] = '.';
}
}
/* do whole part, number is reversed */
while (len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if (!(whole /= 10)) {
break;
}
}
/* pad leading zeros */
if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
if (width && (signbit(value) || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
if (signbit(value)) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; /* ignore the space if the '+' exists */
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
/* pad spaces up to given width */
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
if (len < width) {
if (spacepad(out, arg, width - len) == -1) return -1;
}
}
/* reverse string */
for (idx = i = 0; i < len; i++) {
if (out(buf[len - i - 1U], arg) == -1) return -1;
idx++;
}
/* append pad spaces up to given width */
if (flags & FLAGS_LEFT) {
if (len < width) {
if (spacepad(out, arg, width - len) == -1) return -1;
}
}
return 0;
}

176
libc/fmt/palandntoa.c Normal file
View file

@ -0,0 +1,176 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/conv/conv.h"
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len,
bool negative, unsigned log2base, unsigned prec,
unsigned width, unsigned flags) {
unsigned i, idx;
idx = 0;
/* pad leading zeros */
if (!(flags & FLAGS_LEFT)) {
if (width && (flags & FLAGS_ZEROPAD) &&
(negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
width--;
}
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while ((flags & FLAGS_ZEROPAD) && (len < width) &&
(len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
}
/* handle hash */
if (flags & FLAGS_HASH) {
if (!(flags & FLAGS_PRECISION) && len &&
((len == prec) || (len == width)) && buf[len - 1] == '0') {
len--;
if (len && (log2base == 4 || log2base == 1) && buf[len - 1] == '0') {
len--;
}
}
if (log2base == 4 && len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = 'x';
} else if (log2base == 1 && len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = 'b';
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; /* ignore the space if the '+' exists */
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
/* pad spaces up to given width */
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
if (len < width) {
if (spacepad(out, arg, width - len) == -1) return -1;
}
}
/* reverse string */
for (i = 0U; i < len; i++) {
if (out(buf[len - i - 1], arg) == -1) return -1;
idx++;
}
/* append pad spaces up to given width */
if (flags & FLAGS_LEFT) {
if (idx < width) {
if (spacepad(out, arg, width - idx) == -1) return -1;
}
}
return 0;
}
static int ntoa2(int out(int, void *), void *arg, uintmax_t value, bool neg,
unsigned log2base, unsigned prec, unsigned width,
unsigned flags, const char *alphabet) {
unsigned len, count, digit;
char buf[PRINTF_NTOA_BUFFER_SIZE];
len = 0;
if (!value) flags &= ~FLAGS_HASH;
if (value || !(flags & FLAGS_PRECISION)) {
count = 0;
do {
assert(len < PRINTF_NTOA_BUFFER_SIZE);
if (log2base) {
digit = value;
digit &= (1u << log2base) - 1;
value >>= log2base;
} else {
value = div10(value, &digit);
}
if ((flags & FLAGS_GROUPING) && count == 3) {
buf[len++] = ',';
count = 1;
} else {
count++;
}
buf[len++] = alphabet[digit];
} while (value);
}
return ntoaformat(out, arg, buf, len, neg, log2base, prec, width, flags);
}
int ntoa(int out(int, void *), void *arg, va_list va, unsigned char signbit,
unsigned long log2base, unsigned long precision, unsigned long width,
unsigned long flags, const char *alphabet) {
bool negative;
uintmax_t value, sign;
/* ignore '0' flag when precision is given */
if (flags & FLAGS_PRECISION) {
flags &= ~FLAGS_ZEROPAD;
}
/* no plus / space flag for u, x, X, o, b */
if (!(flags & FLAGS_ISSIGNED)) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
if (signbit > 63) {
value = va_arg(va, uint128_t);
} else {
value = va_arg(va, uint64_t);
}
negative = false;
sign = (uintmax_t)1 << signbit;
if (value > (sign | (sign - 1))) erange();
if (flags & FLAGS_ISSIGNED) {
if (value != sign) {
if (value & sign) {
value = ~value + 1;
negative = true;
}
value &= sign - 1;
}
} else {
value &= sign | (sign - 1);
}
if (ntoa2(out, arg, value, negative, log2base, precision, width, flags,
alphabet) == -1) {
return -1;
}
return 0;
}

264
libc/fmt/palandprintf.c Normal file
View file

@ -0,0 +1,264 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
@author (c) Marco Paland (info@paland.com)
2014-2019, PALANDesign Hannover, Germany
@license The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@brief Tiny printf, sprintf and (v)snprintf implementation, optimized for
embedded systems with a very limited resources. These routines are
thread safe and reentrant! Use this instead of the bloated
standard/newlib printf cause these use malloc for printf (and may not
be thread safe).
@brief Modified by Justine Tunney to support three different types of
UNICODE, 128-bit arithmetic, binary conversion, string escaping,
AVX2 character scanning, and possibly a tinier footprint too, so
long as extremely wild linker hacks aren't considered cheating.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/conv/conv.h"
#include "libc/escape/escape.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
#include "libc/mem/mem.h"
#include "libc/runtime/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
static unsigned ppatoi(const char **str) {
unsigned i;
i = 0;
while (isdigit(**str)) i = i * 10u + (unsigned)(*((*str)++) - '0');
return i;
}
/**
* Implements {,v}{,s{,n},{,{,x}as},f,d}printf state machine.
* @see libc/stdio/printf.c for documentation
*/
hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
int (*out)(int, void *);
unsigned flags, width, precision;
int lasterr;
lasterr = errno;
out = fn ? fn : (int (*)(int, void *))missingno;
while (*format) {
/* %[flags][width][.precision][length] */
if (*format != '%') {
/* no */
if (out(*format, arg) == -1) return -1;
format++;
continue;
} else {
/* yes, evaluate it */
format++;
}
/* evaluate flags */
flags = 0;
getflag:
switch (*format++) {
case '0':
flags |= FLAGS_ZEROPAD;
goto getflag;
case '-':
flags |= FLAGS_LEFT;
goto getflag;
case '+':
flags |= FLAGS_PLUS;
goto getflag;
case ' ':
flags |= FLAGS_SPACE;
goto getflag;
case '#':
flags |= FLAGS_HASH;
goto getflag;
case ',':
flags |= FLAGS_GROUPING;
goto getflag;
case '`':
flags |= FLAGS_REPR;
/* fallthrough */
case '\'':
flags |= FLAGS_QUOTE;
goto getflag;
default:
format--;
break;
}
/* evaluate width field */
width = 0;
if (isdigit(*format)) {
width = ppatoi(&format);
} else if (*format == '*') {
const int w = va_arg(va, int);
if (w < 0) {
flags |= FLAGS_LEFT; /* reverse padding */
width = -w;
} else {
width = w;
}
format++;
}
/* evaluate precision field */
precision = 0;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (isdigit(*format)) {
precision = ppatoi(&format);
} else if (*format == '*') {
const int prec = (int)va_arg(va, int);
precision = prec > 0 ? prec : 0;
format++;
}
}
/* evaluate length field */
unsigned char signbit = 31;
switch (*format) {
case 'j': /* intmax_t */
format++;
signbit = sizeof(intmax_t) * 8 - 1;
break;
case 'l':
if (format[1] == 'l') format++;
/* fallthrough */
case 't': /* ptrdiff_t */
case 'Z': /* size_t */
case 'z': /* size_t */
case 'L': /* long double */
format++;
signbit = 63;
break;
case 'h':
format++;
if (*format == 'h') {
format++;
signbit = 7;
} else {
signbit = 15;
}
break;
default:
break;
}
/* evaluate specifier */
void *p;
const char *alphabet = "0123456789abcdef";
unsigned log2base = 0;
wchar_t charbuf[3];
int rc;
char qchar = '"';
switch (*format++) {
case 'p':
flags |= FLAGS_ZEROPAD;
width = POINTER_XDIGITS;
log2base = 4;
signbit = 47;
goto DoNumber;
case 'X':
alphabet = "0123456789ABCDEF";
/* fallthrough */
case 'x':
log2base = 4;
goto DoNumber;
case 'b':
log2base = 1;
goto DoNumber;
case 'o':
log2base = 3;
goto DoNumber;
case 'd':
case 'i':
flags |= FLAGS_ISSIGNED;
/* fallthrough */
case 'u': {
flags &= ~FLAGS_HASH; /* no hash for dec format */
DoNumber:
if (weaken(ntoa)(out, arg, va, signbit, log2base, precision, width,
flags, alphabet) == -1) {
return -1;
}
break;
}
case 'f':
case 'F': {
long double value;
if (signbit == 63) {
value = va_arg(va, long double);
} else {
value = va_arg(va, double);
}
if (weaken(ftoa)(out, arg, value, precision, width, flags) == -1) {
return -1;
}
break;
}
case 'c':
qchar = '\'';
p = charbuf;
charbuf[0] = (wchar_t)va_arg(va, int); /* @assume little endian */
charbuf[1] = L'\0';
goto showstr;
case 'm':
p = weaken(strerror)(lasterr);
signbit = 0;
goto showstr;
case 'r':
flags |= FLAGS_REPR;
/* fallthrough */
case 'q':
flags |= FLAGS_QUOTE;
/* fallthrough */
case 's':
p = va_arg(va, void *);
showstr:
rc = weaken(stoa)(out, arg, p, flags, precision, width, signbit, qchar);
if (rc == -1) return -1;
break;
case '%':
if (out('%', arg) == -1) return -1;
break;
default:
if (out(format[-1], arg) == -1) return -1;
break;
}
}
return 0;
}

16
libc/fmt/palandprintf.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_FMT_PALANDPRINTF_H_
#define COSMOPOLITAN_LIBC_FMT_PALANDPRINTF_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int spacepad(int(int, void *), void *, unsigned long) hidden;
int ftoa(int(int, void *), void *, long double, unsigned long, unsigned long,
unsigned long) hidden;
int stoa(int(int, void *), void *, void *, unsigned long, unsigned long,
unsigned long, unsigned char, unsigned char) hidden;
int ntoa(int(int, void *), void *, va_list, unsigned char, unsigned long,
unsigned long, unsigned long, unsigned long, const char *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_PALANDPRINTF_H_ */

60
libc/fmt/pflink.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef COSMOPOLITAN_LIBC_FMT_PFLINK_H_
#define COSMOPOLITAN_LIBC_FMT_PFLINK_H_
#include "libc/dce.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#ifndef __STRICT_ANSI__
/**
* @fileoverview builtin+preprocessor+linker tricks for printf/scanf.
*
* Your printf() function only requires that you pay for what you use.
* These macros ensure that its code size starts at under 4kb, growing
* to about 40kb for a fully-loaded implementation. This works best when
* format strings are constexprs that only contain directives.
*/
#define PFLINK(FMT) \
({ \
if (___PFLINK(FMT, strpbrk, "bxdinupo")) STATIC_YOINK("ntoa"); \
if (___PFLINK(FMT, strpbrk, "fFgGaA")) STATIC_YOINK("ftoa"); \
if (___PFLINK(FMT, strpbrk, "cmrqs")) { \
STATIC_YOINK("stoa"); \
if (___PFLINK(FMT, strchr, '#')) STATIC_YOINK("kCp437"); \
if (___PFLINK(FMT, strstr, "%m")) STATIC_YOINK("strerror"); \
if (!IsTiny() && (___PFLINK(FMT, strstr, "%*") || \
___PFLINK(FMT, strpbrk, "0123456789"))) { \
STATIC_YOINK("strnwidth"); \
STATIC_YOINK("strnwidth16"); \
STATIC_YOINK("wcsnwidth"); \
} \
} \
FMT; \
})
#define SFLINK(FMT) \
({ \
if (___PFLINK(FMT, strchr, 'm')) { \
STATIC_YOINK("malloc"); \
STATIC_YOINK("calloc"); \
STATIC_YOINK("free_s"); \
STATIC_YOINK("grow"); \
} \
FMT; \
})
#if __GNUC__ + 0 < 4 || defined(__llvm__)
#define ___PFLINK(FMT, FN, C) 1
#else
#define ___PFLINK(FMT, FN, C) \
!isconstant(FMT) || ((FMT) && __builtin_##FN(FMT, C) != NULL)
#endif
#if defined(__GNUC__) && __GNUC__ < 6
#pragma GCC diagnostic ignored "-Wformat-security"
#endif /* __GNUC__ + 0 < 6 */
#else
#define PFLINK(FMT) FMT
#define SFLINK(FMT) FMT
#endif /* __STRICT_ANSI__ */
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_PFLINK_H_ */

37
libc/fmt/snprintf.c Normal file
View file

@ -0,0 +1,37 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
/**
* Formats string to buffer.
*
* @return number of bytes written, excluding the NUL terminator; or,
* if the output buffer wasn't passed, or was too short, then the
* number of characters that *would* have been written is returned
* @see libc/fmt/fmt.h for documentation
*/
int(snprintf)(char* buf, size_t count, const char* fmt, ...) {
int rc;
va_list va;
va_start(va, fmt);
rc = (vsnprintf)(buf, count, fmt, va);
va_end(va);
return rc;
}

26
libc/fmt/spacepad.c Normal file
View file

@ -0,0 +1,26 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/palandprintf.h"
int spacepad(int out(int, void *), void *arg, unsigned long n) {
int i, rc;
for (rc = i = 0; i < n; ++i) rc |= out(' ', arg);
return rc;
}

30
libc/fmt/sprintf.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
int(sprintf)(char *buf, const char *fmt, ...) {
int rc;
va_list va;
va_start(va, fmt);
rc = (vsnprintf)(buf, INT_MAX, fmt, va);
va_end(va);
return rc;
}

38
libc/fmt/sscanf.c Normal file
View file

@ -0,0 +1,38 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/dce.h"
#include "libc/fmt/vsscanf.h"
/**
* String decoder.
* @see libc/fmt/vcscanf.h (for docs and implementation)
*/
int(sscanf)(const char *str, const char *fmt, ...) {
int rc;
va_list va;
va_start(va, fmt);
if (IsTiny()) {
rc = (vsscanf)(str, fmt, va);
} else {
rc = __vsscanf(str, fmt, va, __vcscanf);
}
va_end(va);
return rc;
}

180
libc/fmt/stoa.c Normal file
View file

@ -0,0 +1,180 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/escape/escape.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h"
#include "libc/nexgen32e/tinystrlen.h"
#include "libc/str/internal.h"
#include "libc/str/str.h"
#include "libc/unicode/unicode.h"
forceinline unsigned long tpiencode(wint_t wc) {
char buf[8];
memset(buf, 0, sizeof(buf));
tpencode(buf, sizeof(buf), wc, false);
return read64le(buf);
}
forceinline int emitwc(int out(int, void *), void *arg, unsigned flags,
wint_t wc) {
unsigned long pending;
if (flags & FLAGS_QUOTE) {
if (wc > 127) {
pending = tpiencode(wc);
} else {
pending = cescapec(wc);
}
} else {
pending = tpiencode(wc);
}
do {
if (out(pending & 0xff, arg) == -1) return -1;
} while ((pending >>= 8));
return 0;
}
forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
char ch, unsigned char signbit) {
if (flags & FLAGS_REPR) {
if (signbit == 63) {
if (out('L', arg) == -1) return -1;
} else if (signbit == 15) {
if (out('u', arg) == -1) return -1;
}
if (out(ch, arg) == -1) return -1;
}
return 0;
}
/**
* Converts string to array.
*
* This function is used by palandprintf() to implement the %s and %c
* directives. The content outputted to the array is always UTF-8, but
* the input may be UTF-16 or UTF-32.
*/
int stoa(int out(int, void *), void *arg, void *data, unsigned long flags,
unsigned long precision, unsigned long width, unsigned char signbit,
unsigned char qchar) {
char *p;
wint_t wc;
unsigned w, c;
bool ignorenul;
p = data;
if (!p) {
p = ((flags & FLAGS_REPR) ? "NULL" : "(null)");
flags &= ~FLAGS_PRECISION;
flags |= FLAGS_NOQUOTE;
signbit = 0;
} else {
if (emitquote(out, arg, flags, qchar, signbit) == -1) return -1;
}
w = precision ? precision : -1;
if (width) {
if (signbit == 63) {
if (weaken(wcsnwidth)) {
w = weaken(wcsnwidth)((const wchar_t *)p, w);
} else {
w = tinywcsnlen((const wchar_t *)p, w);
}
} else if (signbit == 15) {
if (weaken(strnwidth16)) {
w = weaken(strnwidth16)((const char16_t *)p, w);
} else {
w = tinystrnlen16((const char16_t *)p, w);
}
} else if (weaken(strnwidth)) {
w = weaken(strnwidth)(p, w);
} else {
w = strnlen(p, w);
}
}
if (flags & FLAGS_PRECISION) {
w = MIN(w, precision);
}
if (w < width && !(flags & FLAGS_LEFT)) {
if (spacepad(out, arg, width - w) == -1) return -1;
}
ignorenul = (flags & FLAGS_PRECISION) && (flags & (FLAGS_HASH | FLAGS_QUOTE));
for (; !(flags & FLAGS_PRECISION) || precision; --precision) {
if (signbit == 15) {
if ((wc = *(const char16_t *)p) || ignorenul) {
if ((1 <= wc && wc <= 0xD7FF)) {
p += sizeof(char16_t);
} else if ((wc & UTF16_MASK) == UTF16_CONT) {
p += sizeof(char16_t);
continue;
} else {
char16_t buf[4] = {wc};
if (!(flags & FLAGS_PRECISION) || precision > 1) {
buf[1] = ((const char16_t *)p)[1];
}
p += max(1, getutf16((const char16_t *)p, &wc)) * sizeof(char16_t);
}
} else {
break;
}
} else if (signbit == 63) {
wc = *(const wint_t *)p;
p += sizeof(wint_t);
if (!wc) break;
} else if (flags & FLAGS_HASH) {
c = *p & 0xff;
if (!c && !ignorenul) break;
wc = (*weaken(kCp437))[c];
p++;
} else {
if ((wc = *p & 0xff) || ignorenul) {
if (1 <= wc && wc <= 0x7f) {
++p;
} else if (iscont(wc & 0xff)) {
++p;
continue;
} else {
char buf[8];
memset(buf, 0, sizeof(buf));
memcpy(buf, p,
!(flags & FLAGS_PRECISION) ? 7 : MIN(7, precision - 1));
p += max(1, tpdecode(p, &wc));
}
} else {
break;
}
}
if (emitwc(out, arg, flags, wc) == -1) return -1;
}
if (w <= width && (flags & FLAGS_LEFT)) {
if (spacepad(out, arg, width - w) == -1) return -1;
}
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {
if (out(qchar, arg) == -1) return -1;
}
return 0;
}

30
libc/fmt/strerror.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- mode:c; indent-tabs-mode:nil; tab-width:2; coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
/**
* Converts errno value to string non-reentrantly.
* @see strerror_r()
*/
char *strerror(int err) {
alignas(1) static char buf[512];
strerror_r(err, buf, sizeof(buf));
return &buf[0];
}

68
libc/fmt/strerror_r.c Normal file
View file

@ -0,0 +1,68 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/bits/safemacros.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/macros.h"
#include "libc/nt/enum/formatmessageflags.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/str/str.h"
static const char *geterrname(int code) {
extern const char kErrnoNames[];
const long *e;
const char *s;
size_t i;
for (i = 0, e = &E2BIG; e <= &EXFULL; ++e, ++i) {
if (code == *e && (s = indexdoublenulstring(&kErrnoNames[0], i))) {
return s;
}
}
return NULL;
}
/**
* Converts errno value to string.
* @return 0 on success, or error code
*/
int strerror_r(int err, char *buf, size_t size) {
const char *s;
s = (err == -1 || IsTiny()) ? "?" : firstnonnull(geterrname(err), "?");
if (!SupportsWindows()) {
snprintf(buf, size, "E%s[%d]", s, err);
} else {
char16_t buf16[100];
int winstate, sysvstate;
winstate = GetLastError();
sysvstate = errno;
if (FormatMessage(
kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, NULL,
err, 0, buf16, ARRAYLEN(buf16) - 1, 0) > 0) {
chomp(buf16);
} else {
buf16[0] = u'\0';
}
snprintf(buf, size, "E%s/err=%d/errno:%d/GetLastError:%d%s%hs", s, err,
sysvstate, winstate, buf16[0] ? " " : "", buf16);
}
return 0;
}

28
libc/fmt/typefmt.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef COSMOPOLITAN_LIBC_FMT_TYPEFMT_H_
#define COSMOPOLITAN_LIBC_FMT_TYPEFMT_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* Returns printf directive to format scalar type of expression.
*
* @see TYPE_NAME()
*/
#define TYPE_FMT(X, DXOB, UXOB) \
_Generic((X), long double \
: "Lf", double \
: "f", float \
: "f", char \
: "c", signed char \
: "hh" DXOB, unsigned char \
: "hh" UXOB, short \
: "h" DXOB, unsigned short \
: "h" UXOB, int \
: DXOB, unsigned \
: UXOB, long \
: "l" DXOB, unsigned long \
: "l" UXOB)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_TYPEFMT_H_ */

77
libc/fmt/unbing.c Normal file
View file

@ -0,0 +1,77 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/alg/alg.h"
#include "libc/fmt/bing.h"
#include "libc/log/log.h"
#include "libc/str/str.h"
#define ALTCOUNT 21
static const struct Cp437Multimappings {
unsigned char b[ALTCOUNT];
char16_t c[ALTCOUNT];
} kCp437iMultimappings = {
#define ALT(I, C, B) .c[I] = C, .b[I] = B
ALT(0, u'\n', '\n'),
ALT(1, u'\r', '\r'),
ALT(2, u'?', '?'), /* TRIGRAPH */
ALT(3, u'\'', '\''), /* CHARACTER LITERAL */
ALT(4, u'\"', '\"'), /* STRING LITERAL */
ALT(5, u'\\', '\\'), /* ESCAPE LITERAL */
ALT(6, u'', '\0'), /* EMPTY SET */
ALT(7, u'', '\0'), /* SYMBOL FOR NULL [sic] */
ALT(7, 0x20A7, 0x9E), /* PESETA SIGN */
ALT(8, u'Π' /* 03A0: GREEK CAPITAL LETTER PI */, 0xE3),
ALT(9, u'' /* 220F: N-ARY PRODUCT */, 0xE3),
ALT(10, u'' /* 2211: N-ARY SUMMATION */, 0xE4),
ALT(11, u'µ' /* 03BC: MICRO SIGN */, 0xE6),
ALT(12, u'' /* 2126: OHM SIGN */, 0xEA),
ALT(13, u'' /* 2202: PARTIAL DIFFERENTIAL */, 0xEB),
ALT(14, u'ε' /* 03D5: PHI SMALL (CLOSED FORM) */, 0xED),
ALT(15, u'ϕ' /* 03D5: PHI SMALL (CLOSED FORM) */, 0xED),
ALT(16, u'' /* 2208: ELEMENT-OF SIGN */, 0xED),
ALT(17, u'' /* 220A: SMALL ELEMENT OF */, 0xEE),
ALT(18, u'' /* 03B5: ELEMENT-OF SIGN */, 0xEE),
ALT(19, u'β' /* 03B2: GREEK SMALL BETA */, 0xE1),
ALT(20, u'ſ' /* 017F: LATIN SMALL LETTER LONG S */, 0xF4),
#undef ALT
};
/**
* Turns CP437 unicode glyph into its binary representation.
*
* @param c is a unicode character
* @return byte representation, or -1 if ch wasn't ibm cp437
* @see bing()
*/
int unbing(int c) {
int i;
for (i = 0; i < 256; ++i) {
if (c == kCp437[i]) {
return i;
}
}
for (i = 0; i < ALTCOUNT; ++i) {
if (c == kCp437iMultimappings.c[i]) {
return kCp437iMultimappings.b[i];
}
}
return -1;
}

36
libc/fmt/unhexbuf.c Normal file
View file

@ -0,0 +1,36 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/assert.h"
#include "libc/fmt/bing.h"
#include "libc/str/str.h"
void *unhexbuf(void *buf, size_t size, const char *hexdigs) {
size_t i;
unsigned char *p;
assert(size * 2 == strlen(hexdigs));
p = buf;
for (i = 0; i < size; ++i) {
assert(isxdigit(hexdigs[i / 2 + 0]) && isxdigit(hexdigs[i / 2 + 1]));
}
for (i = 0; i < size; ++i) {
p[i] = hextoint(hexdigs[i * 2 + 0]) * 16 + hextoint(hexdigs[i * 2 + 1]);
}
return buf;
}

29
libc/fmt/vcscanf.c Normal file
View file

@ -0,0 +1,29 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
#include "libc/fmt/vcscanf.h"
/**
* Linkable callback-driven string / file / stream decoder.
* @see libc/fmt/vcscanf.h (for docs and implementation)
*/
int(vcscanf)(int callback(void *), void *arg, const char *fmt, va_list ap) {
return __vcscanf(callback, arg, fmt, ap);
}

294
libc/fmt/vcscanf.h Normal file
View file

@ -0,0 +1,294 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#ifndef COSMOPOLITAN_LIBC_FMT_VCSSCANF_H_
#define COSMOPOLITAN_LIBC_FMT_VCSSCANF_H_
#include "libc/conv/conv.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/str/tpdecodecb.h"
#include "libc/sysv/errfuns.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* String / file / stream decoder.
*
* This scanf implementation is able to tokenize strings containing
* 8-bit through 128-bit integers (with validation), floating point
* numbers, etc. It can also be used to convert UTF-8 to UTF-16/32.
*
* @param callback supplies UTF-8 characters using -1 sentinel
* @param fmt is a computer program embedded inside a c string, written
* in a domain-specific programming language that, by design, lacks
* Turing-completeness
* @param va points to the variadic argument state
* @see libc/fmt/pflink.h (dynamic memory is not a requirement)
*/
forceinline int __vcscanf(int callback(void *), void *arg, const char *fmt,
va_list va) {
struct FreeMe {
struct FreeMe *next;
void *ptr;
} *freeme = NULL;
const unsigned char *p = (const unsigned char *)fmt;
unsigned i = 0;
int items = 0;
int c = callback(arg);
while (c != -1) {
switch (p[i++]) {
case '\0':
return items;
case ' ':
case '\t':
case '\n':
case '\r':
case '\v':
while (isspace(c)) c = callback(arg);
break;
case '%': {
uintmax_t number;
void *buf;
size_t bufsize;
unsigned width = 0;
unsigned char bits = 32;
unsigned char charbytes = sizeof(char);
unsigned char diglet;
unsigned char base;
unsigned char prefix;
bool rawmode = false;
bool issigned = false;
bool ismalloc = false;
bool isneg = false;
bool thousands = false;
bool discard = false;
for (;;) {
switch (p[i++]) {
case '%': /* %% → % */
goto NonDirectiveCharacter;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
width *= 10;
width += p[i - 1] - '0';
break;
case '*':
discard = true;
break;
case 'm':
ismalloc = true;
break;
case 'c':
rawmode = true;
if (!width) width = 1;
/* εpsilon transition */
case 's':
goto DecodeString;
case '\'':
thousands = true;
break;
case 'j': /* 128-bit */
bits = sizeof(intmax_t) * 8;
break;
case 'l': /* long */
case 'L': /* loooong */
charbytes = sizeof(wchar_t);
/* fallthrough */
case 't': /* ptrdiff_t */
case 'Z': /* size_t */
case 'z': /* size_t */
bits = 64;
break;
case 'h': /* short and char */
charbytes = sizeof(char16_t);
bits >>= 1;
break;
case 'b': /* binary */
base = 2;
prefix = 'b';
goto ConsumeBasePrefix;
case 'p': /* pointer (NexGen32e) */
bits = 48;
/* fallthrough */
case 'x':
case 'X': /* hexadecimal */
base = 16;
prefix = 'x';
goto ConsumeBasePrefix;
case 'o': /* octal */
base = 8;
goto DecodeNumber;
case 'd': /* decimal */
case 'n': /* TODO(jart): flexidecimal */
issigned = true;
if (c == '+' || (isneg = c == '-')) {
c = callback(arg);
}
/* εpsilon transition */
case 'u':
base = 10;
goto DecodeNumber;
default:
items = einval();
goto Done;
}
}
ConsumeBasePrefix:
if (c == '0') {
c = callback(arg);
if (c == prefix || c == prefix + ('a' - 'A')) {
c = callback(arg);
}
}
DecodeNumber:
if (c != -1) {
number = 0;
do {
diglet = kBase36[(unsigned char)c];
if (1 <= diglet && diglet <= base) {
number *= base;
number += diglet - 1;
} else if (thousands && diglet == ',') {
/* ignore */
} else {
break;
}
} while ((c = callback(arg)) != -1);
if (!discard) {
uintmax_t bane = (uintmax_t)1 << (bits - 1);
if (!(number & ~((bane - 1) | (issigned ? 0 : bane))) ||
(issigned && number == bane /* two's complement bane */)) {
++items;
} else {
items = erange();
goto Done;
}
if (issigned && isneg) {
number = ~number + 1;
}
void *out = va_arg(va, void *);
switch (bits) {
case sizeof(uintmax_t) * CHAR_BIT:
*(uintmax_t *)out = number;
break;
case 48:
case 64:
*(uint64_t *)out = (uint64_t)number;
break;
case 32:
*(uint32_t *)out = (uint32_t)number;
break;
case 16:
*(uint16_t *)out = (uint16_t)number;
break;
case 8:
default:
*(uint8_t *)out = (uint8_t)number;
break;
}
}
}
continue;
DecodeString:
bufsize = !width ? 32 : rawmode ? width : width + 1;
if (discard) {
buf = NULL;
} else if (ismalloc) {
buf = weaken(malloc)(bufsize * charbytes);
struct FreeMe *entry;
if (buf && (entry = weaken(calloc)(1, sizeof(struct FreeMe)))) {
entry->ptr = buf;
entry->next = freeme;
freeme = entry;
}
} else {
buf = va_arg(va, void *);
}
if (buf) {
size_t j = 0;
for (;;) {
if (ismalloc && !width && j + 2 + 1 >= bufsize &&
!weaken(grow)(&buf, &bufsize, charbytes, 0)) {
width = bufsize - 1;
}
if (c != -1 && j + !rawmode < bufsize && (rawmode || !isspace(c))) {
if (charbytes == 1) {
((unsigned char *)buf)[j++] = (unsigned char)c;
c = callback(arg);
} else if (tpdecodecb((wint_t *)&c, c, (void *)callback, arg) !=
-1) {
if (charbytes == sizeof(char16_t)) {
j += abs(pututf16(&((char16_t *)buf)[j], bufsize - j - 1, c,
false));
} else {
((wchar_t *)buf)[j++] = (wchar_t)c;
}
c = callback(arg);
}
} else {
if (!rawmode && j < bufsize) {
if (charbytes == sizeof(char)) {
((unsigned char *)buf)[j] = '\0';
} else if (charbytes == sizeof(char16_t)) {
((char16_t *)buf)[j] = u'\0';
} else if (charbytes == sizeof(wchar_t)) {
((wchar_t *)buf)[j] = L'\0';
}
}
break;
}
}
++items;
if (ismalloc) {
*va_arg(va, char **) = buf;
}
} else {
do {
if (isspace(c)) break;
} while ((c = callback(arg)) != -1);
}
break;
}
default:
NonDirectiveCharacter:
c = (c == p[i - 1]) ? callback(arg) : -1;
break;
}
}
Done:
while (freeme) {
struct FreeMe *entry = freeme;
freeme = entry->next;
if (items == -1) {
weaken(free_s)((void **)&entry->ptr);
}
weaken(free_s)((void **)&entry);
}
return items;
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_FMT_VCSSCANF_H_ */

62
libc/fmt/vsnprintf.c Normal file
View file

@ -0,0 +1,62 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/bits/safemacros.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
#include "libc/runtime/runtime.h"
struct SprintfStr {
char *p;
size_t i, n;
};
static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
if (str->i < str->n) {
if (str->p) str->p[str->i] = c;
str->i++;
} else {
if (!IsTrustworthy() && str->i >= INT_MAX) abort();
str->i++;
}
return 0;
}
/**
* Formats string to buffer.
*
* @param buf stores output and a NUL-terminator is always written,
* provided buf!=NULL && size!=0
* @param size is byte capacity buf
* @return number of bytes written, excluding the NUL terminator; or,
* if the output buffer wasn't passed, or was too short, then the
* number of characters that *would* have been written is returned
* @throw EOVERFLOW when a formatted field exceeds its limit, which can
* be checked by setting errno to 0 before calling
* @see libc/fmt/fmt.h for further documentation
*/
int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) {
struct SprintfStr str = {buf, 0, size};
palandprintf(vsnprintfputchar, &str, fmt, va);
if (str.p && str.n) {
str.p[min(str.i, str.n - 1)] = '\0';
}
return str.i;
}

25
libc/fmt/vsprintf.c Normal file
View file

@ -0,0 +1,25 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
int(vsprintf)(char *buf, const char *fmt, va_list va) {
return (vsnprintf)(buf, INT_MAX, fmt, va);
}

33
libc/fmt/vsscanf.c Normal file
View file

@ -0,0 +1,33 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/dce.h"
#include "libc/fmt/vsscanf.h"
/**
* String decoder.
*
* @see libc/fmt/vcscanf.h (for docs and implementation)
* @note to offer the best performance, we assume small codebases
* needing vsscanf() won't need sscanf() too; and as such, there's
* a small code size penalty to using both
*/
int(vsscanf)(const char *str, const char *fmt, va_list va) {
return __vsscanf(str, fmt, va, IsTiny() ? vcscanf : __vcscanf);
}

39
libc/fmt/vsscanf.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef COSMOPOLITAN_LIBC_INTERNAL_VSSCANF_H_
#define COSMOPOLITAN_LIBC_INTERNAL_VSSCANF_H_
#include "libc/fmt/fmt.h"
#include "libc/fmt/vcscanf.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
struct StringScannerState {
const unsigned char *s;
size_t i;
};
static inline int vsscanfcb(void *arg) {
struct StringScannerState *state = arg;
int res;
if ((res = state->s[state->i])) {
state->i++;
} else {
res = -1;
}
return res;
}
/**
* String decoder builder.
*
* This macro grants sscanf() and vsscanf() the choice to either link or
* inline the full vcscanf() implementation.
*
* @see libc/fmt/vcscanf.h (for docs and implementation)
*/
static inline int __vsscanf(const char *str, const char *fmt, va_list ap,
int impl(int callback(void *), void *arg,
const char *fmt, va_list ap)) {
struct StringScannerState state = {(const unsigned char *)str, 0};
return impl(vsscanfcb, &state, fmt, ap);
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_INTERNAL_VSSCANF_H_ */