Add fixes performance and static web server

main
Justine Tunney 2020-10-05 23:11:49 -07:00
parent b6793d42d5
commit c45e46f871
108 changed files with 2927 additions and 819 deletions

View File

@ -45,6 +45,7 @@ COUNTERMAND=
# The GNU Compiler Collection passes a lot of CFLAGS to the preprocessor # The GNU Compiler Collection passes a lot of CFLAGS to the preprocessor
# (which we call CCFLAGS) but it should pass more; and we do just that. # (which we call CCFLAGS) but it should pass more; and we do just that.
NOPG=0
FIRST=1 FIRST=1
OUTARG=0 OUTARG=0
INVISIBLE=0 INVISIBLE=0
@ -65,13 +66,16 @@ for x; do
-w) -w)
set -- "$@" "$x" -D__W__ set -- "$@" "$x" -D__W__
;; ;;
-x-no-pg)
NOPG=1
;;
-pg) -pg)
if [ $INVISIBLE -eq 0 ]; then if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
set -- "$@" "$x" -D__PG__ # @see libc/macros.h set -- "$@" "$x" -D__PG__ # @see libc/macros.h
fi fi
;; ;;
-mfentry) -mfentry)
if [ $INVISIBLE -eq 0 ]; then if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
set -- "$@" "$x" -D__MFENTRY__ set -- "$@" "$x" -D__MFENTRY__
fi fi
;; ;;

View File

@ -63,6 +63,7 @@ FC = gfortran #/opt/cross9f/bin/x86_64-linux-musl-gfortran
AS = o/third_party/gcc/bin/x86_64-linux-musl-as AS = o/third_party/gcc/bin/x86_64-linux-musl-as
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++ CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++
CXXFILT = o/third_party/gcc/bin/x86_64-linux-musl-c++filt
LD = o/third_party/gcc/bin/x86_64-linux-musl-ld.bfd LD = o/third_party/gcc/bin/x86_64-linux-musl-ld.bfd
AR = o/third_party/gcc/bin/x86_64-linux-musl-ar AR = o/third_party/gcc/bin/x86_64-linux-musl-ar
NM = o/third_party/gcc/bin/x86_64-linux-musl-nm NM = o/third_party/gcc/bin/x86_64-linux-musl-nm
@ -319,6 +320,7 @@ OBJECTIFY.c2x.c = $(CC) $(OBJECTIFY.c.flags) -std=c2x -Wextra -Werror -pedantic-
OBJECTIFY.real.c = \ OBJECTIFY.real.c = \
$(GCC) \ $(GCC) \
-x-no-pg \
$(OBJECTIFY.c.flags) \ $(OBJECTIFY.c.flags) \
-wrapper build/realify.sh \ -wrapper build/realify.sh \
-D__REAL_MODE__ \ -D__REAL_MODE__ \
@ -330,7 +332,12 @@ OBJECTIFY.real.c = \
-ffixed-r13 \ -ffixed-r13 \
-ffixed-r14 \ -ffixed-r14 \
-ffixed-r15 \ -ffixed-r15 \
-mno-red-zone \
-fcall-used-rbx \ -fcall-used-rbx \
-fno-jump-tables \
-fno-shrink-wrap \
-fno-schedule-insns2 \
-flive-range-shrinkage \
-fno-omit-frame-pointer \ -fno-omit-frame-pointer \
-momit-leaf-frame-pointer \ -momit-leaf-frame-pointer \
-mpreferred-stack-boundary=3 \ -mpreferred-stack-boundary=3 \

View File

@ -15,17 +15,6 @@
# remove comments # remove comments
s/[ \t][ \t]*#.*// s/[ \t][ \t]*#.*//
# preserve hardcoded stack offsets
# bloats code size 13% compared to recomputing stack solution
s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/
s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/
s/ret\(q\|\)/retw\t$6/
s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
# can be used instead if
# 1. functions have 6 args or fewer
# 2. long double parameters forceinline
#s/leave\(q\|\)/leavew/ #s/leave\(q\|\)/leavew/
#s/call\(q\|\)/callw/ #s/call\(q\|\)/callw/
#s/ret\(q\|\)/retw/ #s/ret\(q\|\)/retw/
@ -34,6 +23,14 @@ s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
#s/pushq\t\(.*\)/sub $6,%sp\n\tpush \1/ #s/pushq\t\(.*\)/sub $6,%sp\n\tpush \1/
#s/popq\t\(.*\)/pop \1\n\tadd $6,%sp/ #s/popq\t\(.*\)/pop \1\n\tadd $6,%sp/
# preserve hardcoded stack offsets
# bloats code size 13%
s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/
s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/
s/ret\(q\|\)/retw\t$6/
s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
s/, /,/g s/, /,/g
# 32-bitify # 32-bitify

View File

@ -72,7 +72,7 @@ o/$(MODE)/%.c11.o: %.c11.c; @ACTION=OBJECTIFY.c11 build/compile $(OBJECTIFY.c11.
o/$(MODE)/%.c2x.o: %.c2x.c; @ACTION=OBJECTIFY.c2x build/compile $(OBJECTIFY.c2x.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.c2x.o: %.c2x.c; @ACTION=OBJECTIFY.c2x build/compile $(OBJECTIFY.c2x.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.initabi.o: %.initabi.c; @ACTION=OBJECTIFY.init build/compile $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.initabi.o: %.initabi.c; @ACTION=OBJECTIFY.init build/compile $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.ncabi.o: %.ncabi.c; @ACTION=OBJECTIFY.nc build/compile $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.ncabi.o: %.ncabi.c; @ACTION=OBJECTIFY.nc build/compile $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $<
o/tiny/%.real.o: %.real.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $< o/$(MODE)/%.real.o: %.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.runs: o/$(MODE)/%; @ACTION=CHECK.runs TARGET=$< build/runcom $< $(TESTARGS) && touch $@ o/$(MODE)/%.runs: o/$(MODE)/%; @ACTION=CHECK.runs TARGET=$< build/runcom $< $(TESTARGS) && touch $@
o/$(MODE)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^) o/$(MODE)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $< o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $<

View File

@ -49,6 +49,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_CONV \ LIBC_CONV \
LIBC_FMT \ LIBC_FMT \
LIBC_LOG \ LIBC_LOG \
LIBC_LOG_ASAN \
LIBC_MEM \ LIBC_MEM \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_NT_KERNELBASE \ LIBC_NT_KERNELBASE \

View File

@ -55,7 +55,7 @@ int main(int argc, char *argv[], char **envp) {
unsigned long val; unsigned long val;
char fmt[64], **env; char fmt[64], **env;
printf("\nArguments:\n"); printf("\nArguments:\n");
for (i = 0; i < argc; ++i) { for (i = 0; i < g_argc; ++i) {
printf(" ☼ %s\n", argv[i]); printf(" ☼ %s\n", argv[i]);
} }
printf("\nEnvironment:\n"); printf("\nEnvironment:\n");

View File

@ -71,27 +71,25 @@ static size_t KnuthMorrisPratt(m, T, W, n, S)
* @param needlelen is its character count * @param needlelen is its character count
* @return pointer to first result or NULL if not found * @return pointer to first result or NULL if not found
*/ */
void *(memmem)(const void *haystack_, size_t haystacklen, const void *needle_, void *(memmem)(const void *haystackp, size_t haystacklen, const void *needlep,
size_t needlelen) { size_t needlelen) {
long *T;
size_t i;
const char *haystack, *needle, *h; const char *haystack, *needle, *h;
haystack = haystack_; needle = needlep;
needle = needle_; haystack = haystackp;
if (needlelen > haystacklen) return NULL; if (needlelen > haystacklen) return NULL;
if (!needlelen) return (/*unconst*/ void *)haystack; if (!needlelen) return haystack;
h = memchr(haystack, *needle, haystacklen); h = memchr(haystack, *needle, haystacklen);
if (!h || needlelen == 1) return (/*unconst*/ void *)h; if (!h || needlelen == 1) return h;
haystacklen -= h - haystack; haystacklen -= h - haystack;
long stacktmp[16]; if (needlelen < haystacklen && memcmp(h, needle, needlelen) == 0) {
void *freeme = NULL; return h;
long *T = (needlelen + 1 < ARRAYLEN(stacktmp))
? &stacktmp[0]
: (freeme = malloc((needlelen + 1) * sizeof(long)));
KnuthMorrisPrattInit(needlelen, T, needle);
size_t i = KnuthMorrisPratt(needlelen, T, needle, haystacklen, h);
free(freeme);
if (i < haystacklen) {
return (/*unconst*/ char *)h + i * sizeof(char);
} else { } else {
return NULL; T = malloc((needlelen + 1) * sizeof(long));
KnuthMorrisPrattInit(needlelen, T, needle);
i = KnuthMorrisPratt(needlelen, T, needle, haystacklen, h);
free(T);
return i < haystacklen ? h + i * sizeof(char) : NULL;
} }
} }

View File

@ -17,10 +17,7 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/bits/bits.h"
#include "libc/calls/internal.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"

View File

@ -77,7 +77,7 @@ int clock_gettime(int clockid, struct timespec *out_ts) {
} else { } else {
struct NtFileTime ft; struct NtFileTime ft;
GetSystemTimeAsFileTime(&ft); GetSystemTimeAsFileTime(&ft);
*out_ts = filetimetotimespec(ft); *out_ts = FileTimeToTimeSpec(ft);
return 0; return 0;
} }
} }

View File

@ -49,9 +49,9 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) {
: (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) | : (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) |
((filetype == kNtFileTypeChar) ? S_IFCHR : 0) | ((filetype == kNtFileTypeChar) ? S_IFCHR : 0) |
((filetype == kNtFileTypePipe) ? S_IFIFO : 0)))); ((filetype == kNtFileTypePipe) ? S_IFIFO : 0))));
st->st_atim = filetimetotimespec(wst.ftLastAccessFileTime); st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
st->st_mtim = filetimetotimespec(wst.ftLastWriteFileTime); st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = filetimetotimespec(wst.ftCreationFileTime); st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow; st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
st->st_blksize = PAGESIZE; st->st_blksize = PAGESIZE;
st->st_dev = wst.dwVolumeSerialNumber; st->st_dev = wst.dwVolumeSerialNumber;

View File

@ -28,7 +28,6 @@
static int g_pid; static int g_pid;
static void __updatepid(void) { static void __updatepid(void) {
atfork(__updatepid, NULL);
g_pid = __getpid(); g_pid = __getpid();
} }
@ -47,8 +46,9 @@ int __getpid(void) {
int getpid(void) { int getpid(void) {
static bool once; static bool once;
if (!once) { if (!once) {
once = true;
__updatepid(); __updatepid();
atfork(__updatepid, NULL);
once = true;
} }
return g_pid; return g_pid;
} }

View File

@ -36,8 +36,8 @@ textwindows int getrusage$nt(int who, struct rusage *usage) {
if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)( if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(), (who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
&CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) { &CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) {
filetimetotimeval(&usage->ru_utime, UserFileTime); FileTimeToTimeVal(&usage->ru_utime, UserFileTime);
filetimetotimeval(&usage->ru_stime, KernelFileTime); FileTimeToTimeVal(&usage->ru_stime, KernelFileTime);
return 0; return 0;
} else { } else {
return winerr(); return winerr();

View File

@ -29,7 +29,7 @@
int gettimeofday$nt(struct timeval *tv, struct timezone *tz) { int gettimeofday$nt(struct timeval *tv, struct timezone *tz) {
struct NtFileTime ft; struct NtFileTime ft;
GetSystemTimeAsFileTime(&ft); GetSystemTimeAsFileTime(&ft);
filetimetotimeval(tv, ft); FileTimeToTimeVal(tv, ft);
if (tz) memset(tz, 0, sizeof(*tz)); if (tz) memset(tz, 0, sizeof(*tz));
return 0; return 0;
} }

View File

@ -35,7 +35,7 @@
* @param mode is an octal user/group/other permission signifier, that's * @param mode is an octal user/group/other permission signifier, that's
* ignored if O_CREAT or O_TMPFILE weren't passed * ignored if O_CREAT or O_TMPFILE weren't passed
* @return number needing close(), or -1 w/ errno * @return number needing close(), or -1 w/ errno
* @asyncsignalsafe * @note don't call open() from signal handlers
*/ */
nodiscard int open(const char *file, int flags, ...) { nodiscard int open(const char *file, int flags, ...) {
va_list va; va_list va;

View File

@ -155,7 +155,7 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
ap->sa_restorer = &__restore_rt; ap->sa_restorer = &__restore_rt;
} }
} }
if (rva >= 0) { if (rva >= kSigactionMinRva) {
ap->sa_sigaction = (sigaction_f)__sigenter; ap->sa_sigaction = (sigaction_f)__sigenter;
} }
} }

View File

@ -63,7 +63,7 @@ textwindows int utimensat$nt(int dirfd, const char *path,
} else if (ts[i].tv_nsec == UTIME_OMIT) { } else if (ts[i].tv_nsec == UTIME_OMIT) {
ftp[i] = NULL; ftp[i] = NULL;
} else { } else {
ft[i] = timespectofiletime(ts[i]); ft[i] = TimeSpecToFileTime(ts[i]);
ftp[i] = &ft[i]; ftp[i] = &ft[i];
} }
} }

View File

@ -50,8 +50,8 @@ textwindows int wait4$nt(int pid, int *opt_out_wstatus, int options,
memset(opt_out_rusage, 0, sizeof(*opt_out_rusage)); memset(opt_out_rusage, 0, sizeof(*opt_out_rusage));
GetProcessTimes(GetCurrentProcess(), &createfiletime, &exitfiletime, GetProcessTimes(GetCurrentProcess(), &createfiletime, &exitfiletime,
&kernelfiletime, &userfiletime); &kernelfiletime, &userfiletime);
filetimetotimeval(&opt_out_rusage->ru_utime, userfiletime); FileTimeToTimeVal(&opt_out_rusage->ru_utime, userfiletime);
filetimetotimeval(&opt_out_rusage->ru_stime, kernelfiletime); FileTimeToTimeVal(&opt_out_rusage->ru_stime, kernelfiletime);
} }
return pid; return pid;
} else if (options & WNOHANG) { } else if (options & WNOHANG) {

View File

@ -15,8 +15,8 @@
* of the UNIX operation system signalled the end of modernity. Windows * of the UNIX operation system signalled the end of modernity. Windows
* timestamps are living proof. * timestamps are living proof.
*/ */
#define MODERNITYSECONDS 11644473600 #define MODERNITYSECONDS 11644473600ull
#define HECTONANOSECONDS 10000000 #define HECTONANOSECONDS 10000000ull
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
@ -46,12 +46,13 @@ long strtol(const char *, char **, int)
cosmopolitan § conversion » time cosmopolitan § conversion » time
*/ */
struct timespec filetimetotimespec(struct NtFileTime); int64_t DosDateTimeToUnix(unsigned, unsigned);
struct NtFileTime timespectofiletime(struct timespec); struct timespec FileTimeToTimeSpec(struct NtFileTime);
struct NtFileTime timetofiletime(int64_t) nothrow pureconst; struct NtFileTime TimeSpecToFileTime(struct timespec);
struct NtFileTime TimeToFileTime(int64_t) nothrow pureconst;
int64_t filetimetotime(struct NtFileTime) nothrow pureconst; int64_t filetimetotime(struct NtFileTime) nothrow pureconst;
void filetimetotimeval(struct timeval *, struct NtFileTime) nothrow; void FileTimeToTimeVal(struct timeval *, struct NtFileTime) nothrow;
struct NtFileTime timevaltofiletime(const struct timeval *) nosideeffect; struct NtFileTime TimeValToFileTime(const struct timeval *) nosideeffect;
long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect; long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect;
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗

View File

@ -52,6 +52,7 @@ $(LIBC_CONV_A).pkg: \
$(LIBC_CONV_A_OBJS) \ $(LIBC_CONV_A_OBJS) \
$(foreach x,$(LIBC_CONV_A_DIRECTDEPS),$($(x)_A).pkg) $(foreach x,$(LIBC_CONV_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/libc/conv/dosdatetimetounix.o \
o/$(MODE)/libc/conv/itoa64radix10.greg.o \ o/$(MODE)/libc/conv/itoa64radix10.greg.o \
o/$(MODE)/libc/conv/timetofiletime.o \ o/$(MODE)/libc/conv/timetofiletime.o \
o/$(MODE)/libc/conv/filetimetotime.o \ o/$(MODE)/libc/conv/filetimetotime.o \

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/conv/conv.h"
#include "libc/macros.h"
#include "libc/time/time.h"
/**
* Converts MS-DOS timestamp to UNIX.
*
* @note type signature supports dates greater than 2100
* @see PKZIP, FAT
*/
int64_t DosDateTimeToUnix(unsigned date, unsigned time) {
unsigned weekday, year, month, day, hour, minute, second, yday, leap;
year = ((date & 0xfffffe00) >> 9) + 1980 - 1900;
month = MAX(1, MIN(12, (date & 0x01e0) >> 5));
day = (date & 0x001f) ? (date & 0x001f) - 1 : 0;
hour = (time & 0x0000f800) >> 11;
minute = (time & 0x000007e0) >> 5;
second = (time & 0x0000001f) << 1;
leap = year % 4 == 0 && (year % 100 || year % 400 == 0);
yday = day + kMonthYearDay[leap][month - 1];
return second + minute * 60 + hour * 3600 + yday * 86400 +
(year - 70) * 31536000ull + ((year - 69) / 4) * 86400ull -
((year - 1) / 100) * 86400ull + ((year + 299) / 400) * 86400ull;
}

View File

@ -24,11 +24,11 @@
/** /**
* Converts Windows COBOL timestamp to UNIX epoch in nanoseconds. * Converts Windows COBOL timestamp to UNIX epoch in nanoseconds.
*/ */
struct timespec filetimetotimespec(struct NtFileTime ft) { struct timespec FileTimeToTimeSpec(struct NtFileTime ft) {
uint64_t x; uint64_t x;
x = ft.dwHighDateTime; x = ft.dwHighDateTime;
x <<= 32; x <<= 32;
x |= ft.dwLowDateTime; x |= ft.dwLowDateTime;
x -= MODERNITYSECONDS; return (struct timespec){x / HECTONANOSECONDS - MODERNITYSECONDS,
return (struct timespec){x / HECTONANOSECONDS, x % HECTONANOSECONDS * 100}; x % HECTONANOSECONDS * 100};
} }

View File

@ -22,9 +22,11 @@
#include "libc/conv/conv.h" #include "libc/conv/conv.h"
#include "libc/nt/struct/filetime.h" #include "libc/nt/struct/filetime.h"
void filetimetotimeval(struct timeval *tv, struct NtFileTime ft) { void FileTimeToTimeVal(struct timeval *tv, struct NtFileTime ft) {
uint64_t t = (uint64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime; uint64_t x;
uint64_t x = t - MODERNITYSECONDS * HECTONANOSECONDS; x = ft.dwHighDateTime;
tv->tv_sec = x / HECTONANOSECONDS; x <<= 32;
x |= ft.dwLowDateTime;
tv->tv_sec = x / HECTONANOSECONDS - MODERNITYSECONDS;
tv->tv_usec = x % HECTONANOSECONDS / 10; tv->tv_usec = x % HECTONANOSECONDS / 10;
} }

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/time/time.h"
const unsigned short kMonthYearDay[2][12] = {
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};

View File

@ -25,7 +25,7 @@
/** /**
* Converts UNIX nanosecond timestamp to Windows COBOL timestamp. * Converts UNIX nanosecond timestamp to Windows COBOL timestamp.
*/ */
struct NtFileTime timespectofiletime(struct timespec ts) { struct NtFileTime TimeSpecToFileTime(struct timespec ts) {
uint64_t x; uint64_t x;
x = MODERNITYSECONDS; x = MODERNITYSECONDS;
x += ts.tv_sec * HECTONANOSECONDS; x += ts.tv_sec * HECTONANOSECONDS;

View File

@ -20,7 +20,7 @@
#include "libc/conv/conv.h" #include "libc/conv/conv.h"
#include "libc/nt/struct/filetime.h" #include "libc/nt/struct/filetime.h"
struct NtFileTime timetofiletime(int64_t t) { struct NtFileTime TimeToFileTime(int64_t t) {
uint64_t t2 = (t + MODERNITYSECONDS) * HECTONANOSECONDS; uint64_t t2 = (t + MODERNITYSECONDS) * HECTONANOSECONDS;
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)}; return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
} }

View File

@ -22,7 +22,7 @@
#include "libc/nt/struct/filetime.h" #include "libc/nt/struct/filetime.h"
#include "libc/time/time.h" #include "libc/time/time.h"
struct NtFileTime timevaltofiletime(const struct timeval *tv) { struct NtFileTime TimeValToFileTime(const struct timeval *tv) {
uint64_t t2 = tv->tv_sec * HECTONANOSECONDS + tv->tv_usec * 10 + uint64_t t2 = tv->tv_sec * HECTONANOSECONDS + tv->tv_usec * 10 +
MODERNITYSECONDS * HECTONANOSECONDS; MODERNITYSECONDS * HECTONANOSECONDS;
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)}; return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};

View File

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_DOS_H_ #ifndef COSMOPOLITAN_LIBC_DOS_H_
#define COSMOPOLITAN_LIBC_DOS_H_ #define COSMOPOLITAN_LIBC_DOS_H_
#include "libc/macros.h"
/** /**
* @fileoverview MS-DOS Data Structures. * @fileoverview MS-DOS Data Structures.

128
libc/fmt/fcvt.c 100644
View File

@ -0,0 +1,128 @@
/*-*- 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 2009 Ian Piumarta
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, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to
do so, provided that the above copyright notice(s) and this permission
notice appear in all copies of the Software. Inclusion of the above
copyright notice(s) and this permission notice in supporting documentation
would be appreciated but is not required.
THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
*/
#include "libc/assert.h"
#include "libc/fmt/fmt.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
ecvt, fcvt (MIT License)\\n\
Copyright 2009 Ian Piumarta\"");
/**
* @fileoverview Replacements for the functions ecvt() and fcvt()
*
* These functions were recently deprecated in POSIX. The interface and
* behaviour is identical to the functions that they replace and faster.
*
* For details on the use of these functions, see your ecvt(3) manual
* page. If you don't have one handy, there might still be one available
* here: http://opengroup.org/onlinepubs/007908799/xsh/ecvt.html
*
* @see https://www.piumarta.com/software/fcvt/
*/
static char *Fcvt(double value, int ndigit, int *decpt, int *sign, int fflag) {
static char buf[128];
double i;
uint64_t l, mant;
int exp2, exp10, ptr;
memcpy(&l, &value, 8);
exp2 = (0x7ff & (l >> 52)) - 1023;
mant = l & 0x000fffffffffffffULL;
if ((*sign = l >> 63)) value = -value;
if (exp2 == 0x400) {
*decpt = 0;
return mant ? "nan" : "inf";
}
exp10 = (value == 0) ? !fflag : (int)ceil(log10(value));
if (exp10 < -307) exp10 = -307; /* otherwise overflow in pow() */
value *= pow(10.0, -exp10);
if (value) {
while (value < 0.1) {
value *= 10;
--exp10;
}
while (value >= 1.0) {
value /= 10;
++exp10;
}
}
assert(value == 0 || (0.1 <= value && value < 1.0));
if (fflag) {
if (ndigit + exp10 < 0) {
*decpt = -ndigit;
return "";
}
ndigit += exp10;
}
*decpt = exp10;
if (ARRAYLEN(buf) < ndigit + 2) abort();
ptr = 1;
#if 0 /* slow and safe (and dreadfully boring) */
while (ptr <= ndigit) {
i;
value = modf(value * 10, &i);
buf[ptr++] = '0' + (int)i;
}
if (value >= 0.5) {
while (--ptr && ++buf[ptr] > '9') {
buf[ptr] = '0';
}
}
#else /* faster */
memcpy(&l, &value, 8);
exp2 = (0x7ff & (l >> 52)) - 1023;
assert(value == 0 || (-4 <= exp2 && exp2 <= -1));
mant = l & 0x000fffffffffffffULL;
if (exp2 == -1023) {
++exp2;
} else {
mant |= 0x0010000000000000ULL;
}
mant <<= (exp2 + 4); /* 56-bit denormalised signifier */
while (ptr <= ndigit) {
mant &= 0x00ffffffffffffffULL; /* mod 1.0 */
mant = (mant << 1) + (mant << 3);
buf[ptr++] = '0' + (mant >> 56);
}
if (mant & 0x0080000000000000ULL) /* 1/2 << 56 */
while (--ptr && ++buf[ptr] > '9') buf[ptr] = '0';
#endif
if (ptr) {
buf[ndigit + 1] = 0;
return buf + 1;
}
if (fflag) {
++ndigit;
++*decpt;
}
buf[0] = '1';
buf[ndigit] = 0;
return buf;
}
char *ecvt(double value, int ndigit, int *decpt, int *sign) {
return Fcvt(value, ndigit, decpt, sign, 0);
}
char *fcvt(double value, int ndigit, int *decpt, int *sign) {
return Fcvt(value, ndigit, decpt, sign, 1);
}

View File

@ -29,6 +29,8 @@ char *strerror(int) returnsnonnull nothrow nocallback;
int strerror_r(int, char *, size_t) nothrow nocallback; int strerror_r(int, char *, size_t) nothrow nocallback;
int palandprintf(void *, void *, const char *, va_list) hidden; int palandprintf(void *, void *, const char *, va_list) hidden;
char *itoa(int, char *, int) compatfn; char *itoa(int, char *, int) compatfn;
char *fcvt(double, int, int *, int *);
char *ecvt(double, int, int *, int *);
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § string formatting » optimizations cosmopolitan § string formatting » optimizations

View File

@ -40,7 +40,7 @@
* @see xdtoa() for higher precision at the cost of bloat * @see xdtoa() for higher precision at the cost of bloat
* @see palandprintf() which is intended caller * @see palandprintf() which is intended caller
*/ */
int ftoa(int out(int, void *), void *arg, long double value, int prec, int ftoa(int out(long, void *), void *arg, long double value, int prec,
unsigned long width, unsigned long flags) { unsigned long width, unsigned long flags) {
long whole, frac; long whole, frac;
long double tmp, diff; long double tmp, diff;

View File

@ -31,7 +31,7 @@
uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *); uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *);
static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len, static int ntoaformat(int out(long, void *), void *arg, char *buf, unsigned len,
bool negative, unsigned log2base, unsigned prec, bool negative, unsigned log2base, unsigned prec,
unsigned width, unsigned char flags) { unsigned width, unsigned char flags) {
unsigned i, idx; unsigned i, idx;
@ -103,7 +103,7 @@ static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len,
return 0; return 0;
} }
int ntoa2(int out(int, void *), void *arg, uintmax_t value, bool neg, int ntoa2(int out(long, void *), void *arg, uintmax_t value, bool neg,
unsigned log2base, unsigned prec, unsigned width, unsigned flags, unsigned log2base, unsigned prec, unsigned width, unsigned flags,
const char *alphabet) { const char *alphabet) {
uintmax_t remainder; uintmax_t remainder;
@ -135,7 +135,7 @@ int ntoa2(int out(int, void *), void *arg, uintmax_t value, bool neg,
return ntoaformat(out, arg, buf, len, neg, log2base, prec, width, flags); 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, int ntoa(int out(long, void *), void *arg, va_list va, unsigned char signbit,
unsigned long log2base, unsigned long prec, unsigned long width, unsigned long log2base, unsigned long prec, unsigned long width,
unsigned char flags, const char *lang) { unsigned char flags, const char *lang) {
bool neg; bool neg;

View File

@ -75,7 +75,7 @@ static int ppatoi(const char **str) {
* - `%Lf` long double * - `%Lf` long double
* - `%p` pointer (48-bit hexadecimal) * - `%p` pointer (48-bit hexadecimal)
* *
* Length Modifiers * Size Modifiers
* *
* - `%hhd` char (8-bit) * - `%hhd` char (8-bit)
* - `%hd` short (16-bit) * - `%hd` short (16-bit)
@ -89,7 +89,11 @@ static int ppatoi(const char **str) {
* - `%08d` fixed columns w/ zero leftpadding * - `%08d` fixed columns w/ zero leftpadding
* - `%8d` fixed columns w/ space leftpadding * - `%8d` fixed columns w/ space leftpadding
* - `%*s` variable column string (thompson-pike) * - `%*s` variable column string (thompson-pike)
* - `%.*s` variable column data (ignore nul terminator) *
* Precision Modifiers
*
* - `%.8s` supplied character length (ignore nul terminator)
* - `%.*s` supplied character length argument (ignore nul terminator)
* *
* Formatting Modifiers * Formatting Modifiers
* *
@ -112,9 +116,9 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
long double ldbl; long double ldbl;
wchar_t charbuf[3]; wchar_t charbuf[3];
const char *alphabet; const char *alphabet;
int (*out)(int, void *); int (*out)(long, void *);
unsigned char signbit, log2base; unsigned char signbit, log2base;
int w, rc, flags, width, lasterr, precision; int w, flags, width, lasterr, precision;
lasterr = errno; lasterr = errno;
out = fn ? fn : (int (*)(int, void *))missingno; out = fn ? fn : (int (*)(int, void *))missingno;
@ -255,7 +259,8 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
case 'u': { case 'u': {
flags &= ~FLAGS_HASH; /* no hash for dec format */ flags &= ~FLAGS_HASH; /* no hash for dec format */
DoNumber: DoNumber:
if (weaken(ntoa)(out, arg, va, signbit, log2base, precision, width, if (!weaken(ntoa) ||
weaken(ntoa)(out, arg, va, signbit, log2base, precision, width,
flags, alphabet) == -1) { flags, alphabet) == -1) {
return -1; return -1;
} }
@ -269,7 +274,8 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
} else { } else {
ldbl = va_arg(va, double); ldbl = va_arg(va, double);
} }
if (weaken(ftoa)(out, arg, ldbl, precision, width, flags) == -1) { if (!weaken(ftoa) ||
weaken(ftoa)(out, arg, ldbl, precision, width, flags) == -1) {
return -1; return -1;
} }
break; break;
@ -297,8 +303,10 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
case 's': case 's':
p = va_arg(va, void *); p = va_arg(va, void *);
showstr: showstr:
rc = weaken(stoa)(out, arg, p, flags, precision, width, signbit, qchar); if (!weaken(stoa) || weaken(stoa)(out, arg, p, flags, precision, width,
if (rc == -1) return -1; signbit, qchar) == -1) {
return -1;
}
break; break;
case '%': case '%':

View File

@ -7,12 +7,12 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
int spacepad(int(int, void *), void *, unsigned long) hidden; int spacepad(int(long, void *), void *, unsigned long) hidden;
int ftoa(int(int, void *), void *, long double, int, unsigned long, int ftoa(int(long, void *), void *, long double, int, unsigned long,
unsigned long) hidden; unsigned long) hidden;
int stoa(int(int, void *), void *, void *, unsigned long, unsigned long, int stoa(int(long, void *), void *, void *, unsigned long, unsigned long,
unsigned long, unsigned char, unsigned char) hidden; unsigned long, unsigned char, unsigned char) hidden;
int ntoa(int(int, void *), void *, va_list, unsigned char, unsigned long, int ntoa(int(long, void *), void *, va_list, unsigned char, unsigned long,
unsigned long, unsigned long, unsigned char, const char *) hidden; unsigned long, unsigned long, unsigned char, const char *) hidden;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_

View File

@ -19,7 +19,7 @@
*/ */
#include "libc/fmt/palandprintf.h" #include "libc/fmt/palandprintf.h"
int spacepad(int out(int, void *), void *arg, unsigned long n) { int spacepad(int out(long, void *), void *arg, unsigned long n) {
int i, rc; int i, rc;
for (rc = i = 0; i < n; ++i) rc |= out(' ', arg); for (rc = i = 0; i < n; ++i) rc |= out(' ', arg);
return rc; return rc;

View File

@ -17,47 +17,54 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/escape/escape.h" #include "libc/escape/escape.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/paland.inc" #include "libc/fmt/paland.inc"
#include "libc/fmt/palandprintf.h" #include "libc/fmt/palandprintf.h"
#include "libc/nexgen32e/tinystrlen.h" #include "libc/nexgen32e/tinystrlen.h"
#include "libc/str/internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/str/tpdecode.h" #include "libc/str/thompike.h"
#include "libc/str/tpencode.h" #include "libc/str/tpenc.h"
#include "libc/str/utf16.h"
#include "libc/unicode/unicode.h" #include "libc/unicode/unicode.h"
forceinline unsigned long tpiencode(wint_t wc) { typedef int (*emit_f)(int (*)(long, void *), void *, wint_t);
char buf[8];
memset(buf, 0, sizeof(buf)); static int StoaEmitByte(int f(long, void *), void *a, wint_t c) {
tpencode(buf, sizeof(buf), wc, false); return f(c, a);
return read64le(buf);
} }
forceinline int emitwc(int out(int, void *), void *arg, unsigned flags, static int StoaEmitWordEncodedString(int f(long, void *), void *a, uint64_t w) {
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 { do {
if (out(pending & 0xff, arg) == -1) return -1; if (f(w & 0xff, a) == -1) {
} while ((pending >>= 8)); return -1;
}
} while ((w >>= 8));
return 0; return 0;
} }
forceinline int emitquote(int out(int, void *), void *arg, unsigned flags, static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) {
char ch, unsigned char signbit) { if (0 <= c && c <= 127) {
return f(c, a);
} else {
return StoaEmitWordEncodedString(f, a, tpenc(c));
}
}
static int StoaEmitQuoted(int f(long, void *), void *a, wint_t c) {
if (0 <= c && c <= 127) {
return StoaEmitWordEncodedString(f, a, cescapec(c));
} else {
return StoaEmitWordEncodedString(f, a, tpenc(c));
}
}
static int StoaEmitVisualized(int f(long, void *), void *a, wint_t c) {
return StoaEmitUnicode(f, a, (*weaken(kCp437))[c]);
}
static int StoaEmitQuote(int out(long, void *), void *arg, unsigned flags,
char ch, unsigned char signbit) {
if (flags & FLAGS_REPR) { if (flags & FLAGS_REPR) {
if (signbit == 63) { if (signbit == 63) {
if (out('L', arg) == -1) return -1; if (out('L', arg) == -1) return -1;
@ -78,13 +85,15 @@ forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
* *
* @see palandprintf() * @see palandprintf()
*/ */
int stoa(int out(int, void *), void *arg, void *data, unsigned long flags, int stoa(int out(long, void *), void *arg, void *data, unsigned long flags,
unsigned long precision, unsigned long width, unsigned char signbit, unsigned long precision, unsigned long width, unsigned char signbit,
unsigned char qchar) { unsigned char qchar) {
char *p; char *p;
wint_t wc; wint_t wc;
unsigned w, c; unsigned n;
bool ignorenul; emit_f emit;
bool justdobytes;
unsigned w, c, pad;
p = data; p = data;
if (!p) { if (!p) {
@ -93,89 +102,102 @@ int stoa(int out(int, void *), void *arg, void *data, unsigned long flags,
flags |= FLAGS_NOQUOTE; flags |= FLAGS_NOQUOTE;
signbit = 0; signbit = 0;
} else { } else {
if (emitquote(out, arg, flags, qchar, signbit) == -1) return -1; if (StoaEmitQuote(out, arg, flags, qchar, signbit) == -1) return -1;
} }
w = precision ? precision : -1; if (!(flags & FLAGS_PRECISION)) {
if (signbit == 63) {
precision = tinywcsnlen((const wchar_t *)p, -1);
} else if (signbit == 15) {
precision = tinystrnlen16((const char16_t *)p, -1);
} else {
precision = strlen(p);
}
}
pad = 0;
if (width) { if (width) {
w = precision;
if (signbit == 63) { if (signbit == 63) {
if (weaken(wcsnwidth)) { if (weaken(wcsnwidth)) {
w = weaken(wcsnwidth)((const wchar_t *)p, w); w = weaken(wcsnwidth)((const wchar_t *)p, precision);
} else {
w = tinywcsnlen((const wchar_t *)p, w);
} }
} else if (signbit == 15) { } else if (signbit == 15) {
if (weaken(strnwidth16)) { if (weaken(strnwidth16)) {
w = weaken(strnwidth16)((const char16_t *)p, w); w = weaken(strnwidth16)((const char16_t *)p, precision);
} else {
w = tinystrnlen16((const char16_t *)p, w);
} }
} else if (weaken(strnwidth)) { } else if (weaken(strnwidth)) {
w = weaken(strnwidth)(p, w); w = weaken(strnwidth)(p, precision);
} else { }
w = strnlen(p, w); if (w < width) {
pad = width - w;
} }
} }
if (flags & FLAGS_PRECISION) { if (pad && !(flags & FLAGS_LEFT)) {
w = MIN(w, precision); if (spacepad(out, arg, pad) == -1) return -1;
} }
if (w < width && !(flags & FLAGS_LEFT)) { justdobytes = false;
if (spacepad(out, arg, width - w) == -1) return -1; if (signbit == 15 || signbit == 63) {
if (flags & FLAGS_QUOTE) {
emit = StoaEmitQuoted;
} else {
emit = StoaEmitUnicode;
}
} else if ((flags & FLAGS_HASH) && weaken(kCp437)) {
justdobytes = true;
emit = StoaEmitVisualized;
} else if (flags & FLAGS_QUOTE) {
emit = StoaEmitQuoted;
} else {
justdobytes = true;
emit = StoaEmitByte;
} }
ignorenul = (flags & FLAGS_PRECISION) && (flags & (FLAGS_HASH | FLAGS_QUOTE)); if (justdobytes) {
for (; !(flags & FLAGS_PRECISION) || precision; --precision) { while (precision--) {
if (signbit == 15) { wc = *p++ & 0xff;
if ((wc = *(const char16_t *)p) || ignorenul) { if (emit(out, arg, wc) == -1) return -1;
if ((1 <= wc && wc <= 0xD7FF)) { }
} else {
while (precision--) {
if (signbit == 15) {
wc = *(const char16_t *)p;
if (IsUcs2(wc)) {
p += sizeof(char16_t); p += sizeof(char16_t);
} else if ((wc & UTF16_MASK) == UTF16_CONT) { } else if (IsUtf16Cont(wc)) {
p += sizeof(char16_t); p += sizeof(char16_t);
continue; continue;
} else if (!precision) {
break;
} else { } else {
char16_t buf[4] = {wc}; --precision;
if (!(flags & FLAGS_PRECISION) || precision > 1) { wc = MergeUtf16(wc, *(const char16_t *)p);
buf[1] = ((const char16_t *)p)[1]; }
} else if (signbit == 63) {
wc = *(const wint_t *)p;
p += sizeof(wint_t);
if (!wc) break;
} else {
wc = *p++ & 0xff;
if (!isascii(wc)) {
if (ThomPikeCont(wc)) continue;
n = ThomPikeLen(wc) - 1;
wc = ThomPikeByte(wc);
if (n > precision) break;
precision -= n;
while (n--) {
wc = ThomPikeMerge(wc, *p++);
} }
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 (emit(out, arg, wc) == -1) return -1;
} }
if (emitwc(out, arg, flags, wc) == -1) return -1;
} }
if (w <= width && (flags & FLAGS_LEFT)) { if (pad && (flags & FLAGS_LEFT)) {
if (spacepad(out, arg, width - w) == -1) return -1; if (spacepad(out, arg, pad) == -1) return -1;
} }
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) { if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {

View File

@ -25,17 +25,13 @@
struct SprintfStr { struct SprintfStr {
char *p; char *p;
size_t i, n; size_t i;
size_t n;
}; };
static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) { static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
if (str->i < str->n) { if (str->i < str->n) str->p[str->i] = c;
if (str->p) str->p[str->i] = c; str->i++;
str->i++;
} else {
if (!IsTrustworthy() && str->i >= INT_MAX) abort();
str->i++;
}
return 0; return 0;
} }
@ -48,15 +44,11 @@ static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
* @return number of bytes written, excluding the NUL terminator; or, * @return number of bytes written, excluding the NUL terminator; or,
* if the output buffer wasn't passed, or was too short, then the * if the output buffer wasn't passed, or was too short, then the
* number of characters that *would* have been written is returned * 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 palandprintf() and printf() for detailed documentation * @see palandprintf() and printf() for detailed documentation
*/ */
int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) { int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) {
struct SprintfStr str = {buf, 0, size}; struct SprintfStr str = {buf, 0, size};
palandprintf(vsnprintfputchar, &str, fmt, va); palandprintf(vsnprintfputchar, &str, fmt, va);
if (str.p && str.n) { if (str.n) str.p[min(str.i, str.n - 1)] = '\0';
str.p[min(str.i, str.n - 1)] = '\0';
}
return str.i; return str.i;
} }

View File

@ -66,7 +66,7 @@
#endif #endif
#define BIGPAGESIZE 0x200000 #define BIGPAGESIZE 0x200000
#define STACKSIZE 0x80000 /* todo: zlib's fault? */ #define STACKSIZE 0x10000
#define FRAMESIZE 0x10000 /* 8086 */ #define FRAMESIZE 0x10000 /* 8086 */
#define PAGESIZE 0x1000 /* i386+ */ #define PAGESIZE 0x1000 /* i386+ */
#define BUFSIZ 0x1000 /* best stdio default */ #define BUFSIZ 0x1000 /* best stdio default */

View File

@ -3,7 +3,7 @@
PKGS += LIBC_LINUX PKGS += LIBC_LINUX
LIBC_LINUX_HDRS = $(filter %.h,$(LIBC_FILES)) LIBC_LINUX_HDRS = $(filter %.h,$(LIBC_LINUX_FILES))
LIBC_LINUX_FILES := $(wildcard libc/linux/*) LIBC_LINUX_FILES := $(wildcard libc/linux/*)
LIBC_LINUX_CHECKS = $(LIBC_LINUX_HDRS:%=o/$(MODE)/%.ok) LIBC_LINUX_CHECKS = $(LIBC_LINUX_HDRS:%=o/$(MODE)/%.ok)

View File

@ -41,6 +41,8 @@
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "third_party/dlmalloc/dlmalloc.h" #include "third_party/dlmalloc/dlmalloc.h"
STATIC_YOINK("_init_asan");
/** /**
* @fileoverview Cosmopolitan Address Sanitizer Runtime. * @fileoverview Cosmopolitan Address Sanitizer Runtime.
* *

View File

@ -31,6 +31,10 @@
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/auxv.h"
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("ftoa");
/** /**
* Handles failure of CHECK_xx() macros. * Handles failure of CHECK_xx() macros.
*/ */
@ -47,30 +51,30 @@ relegated void __check_fail(const char *suffix, const char *opstr,
if (!memccpy(sufbuf, suffix, '\0', sizeof(sufbuf))) strcpy(sufbuf, "?"); if (!memccpy(sufbuf, suffix, '\0', sizeof(sufbuf))) strcpy(sufbuf, "?");
strtoupper(sufbuf); strtoupper(sufbuf);
fprintf(stderr, (fprintf)(stderr,
"check failed\n" "check failed\n"
"\tCHECK_%s(%s, %s);\n" "\tCHECK_%s(%s, %s);\n"
"\t\t → %#lx (%s)\n" "\t\t → %#lx (%s)\n"
"\t\t%s %#lx (%s)\n", "\t\t%s %#lx (%s)\n",
sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr); sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr);
if (!isempty(fmt)) { if (!isempty(fmt)) {
fputc('\t', stderr); fputc('\t', stderr);
va_start(va, fmt); va_start(va, fmt);
vfprintf(stderr, fmt, va); (vfprintf)(stderr, fmt, va);
va_end(va); va_end(va);
fputc('\n', stderr); fputc('\n', stderr);
} }
fprintf(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE, (fprintf)(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET); getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
for (i = 1; i < g_argc; ++i) { for (i = 1; i < g_argc; ++i) {
fprintf(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : ""); (fprintf)(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : "");
} }
if (!IsTiny() && lasterr == ENOMEM) { if (!IsTiny() && lasterr == ENOMEM) {
fprintf(stderr, "\n"); (fprintf)(stderr, "\n");
fflush(stderr); fflush(stderr);
PrintMemoryIntervals(fileno(stderr), &_mmi); PrintMemoryIntervals(fileno(stderr), &_mmi);
} }

View File

@ -15,7 +15,7 @@
* Log level for compile-time DCE. * Log level for compile-time DCE.
*/ */
#ifndef LOGGABLELEVEL #ifndef LOGGABLELEVEL
#ifndef NDEBUG #ifndef TINY
#define LOGGABLELEVEL kLogDebug #define LOGGABLELEVEL kLogDebug
/* #elif IsTiny() */ /* #elif IsTiny() */
/* #define LOGGABLELEVEL kLogInfo */ /* #define LOGGABLELEVEL kLogInfo */

View File

@ -65,6 +65,11 @@ $(LIBC_LOG_A_OBJS): \
$(NO_MAGIC) \ $(NO_MAGIC) \
-fwrapv -fwrapv
ifeq (,$(MODE))
LIBC_LOG_ASAN = o/$(MODE)/libc/log/asan.o
endif
LIBC_LOG_ASAN_A = o/$(MODE)/libc/log/log.a
LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x))) LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)))
LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS)) LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS))
LIBC_LOG_HDRS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_HDRS)) LIBC_LOG_HDRS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_HDRS))

View File

@ -37,6 +37,10 @@
#define kNontrivialSize (8 * 1000 * 1000) #define kNontrivialSize (8 * 1000 * 1000)
STATIC_YOINK("ntoa");
STATIC_YOINK("stoa");
STATIC_YOINK("ftoa");
static int loglevel2char(unsigned level) { static int loglevel2char(unsigned level) {
switch (level) { switch (level) {
case kLogInfo: case kLogInfo:
@ -47,6 +51,8 @@ static int loglevel2char(unsigned level) {
return 'W'; return 'W';
case kLogFatal: case kLogFatal:
return 'F'; return 'F';
case kLogVerbose:
return 'V';
default: default:
return '?'; return '?';
} }
@ -80,8 +86,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
long double t2; long double t2;
const char *prog; const char *prog;
int64_t secs, nsec, dots; int64_t secs, nsec, dots;
char zonebuf[8], *zonebufp; char timebuf[32], *timebufp;
char timebuf[24], *timebufp;
if (!f) f = g_logfile; if (!f) f = g_logfile;
if (fileno(f) == -1) return; if (fileno(f) == -1) return;
t2 = nowl(); t2 = nowl();
@ -89,22 +94,19 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
nsec = rem1000000000int64(t2 * 1e9L); nsec = rem1000000000int64(t2 * 1e9L);
if (secs > ts.tv_sec) { if (secs > ts.tv_sec) {
localtime_r(&secs, &tm); localtime_r(&secs, &tm);
strftime(timebuf, sizeof(timebuf), "%Y%m%dT%H%M%S", &tm); strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S.", &tm);
strftime(zonebuf, sizeof(zonebuf), "%Z", &tm);
timebufp = timebuf; timebufp = timebuf;
zonebufp = zonebuf;
dots = nsec; dots = nsec;
} else { } else {
timebufp = "---------------"; timebufp = "--------------------";
zonebufp = "---";
dots = nsec - ts.tv_nsec; dots = nsec - ts.tv_nsec;
} }
ts.tv_sec = secs; ts.tv_sec = secs;
ts.tv_nsec = nsec; ts.tv_nsec = nsec;
prog = basename(program_invocation_name); prog = basename(program_invocation_name);
if (fprintf(f, "%c%s%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp, if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp,
zonebufp, rem1000000int64(div1000int64(dots)), file, line, rem1000000int64(div1000int64(dots)), file, line,
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) { strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
vflogf_onfail(f); vflogf_onfail(f);
} }
(vfprintf)(f, fmt, va); (vfprintf)(f, fmt, va);
@ -112,7 +114,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
fputc('\n', f); fputc('\n', f);
if (level == kLogFatal) { if (level == kLogFatal) {
startfatal(file, line); startfatal(file, line);
fprintf(stderr, "fatal error see logfile\n"); (fprintf)(stderr, "fatal error see logfile\n");
die(); die();
unreachable; unreachable;
} }

View File

@ -1,6 +1,8 @@
#ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_ #ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_
#define COSMOPOLITAN_LIBC_NT_WINSOCK_H_ #define COSMOPOLITAN_LIBC_NT_WINSOCK_H_
#include "libc/nt/struct/overlapped.h" #include "libc/nt/struct/overlapped.h"
#include "libc/nt/struct/pollfd.h"
#include "libc/sock/sock.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#if 0 #if 0
@ -55,10 +57,6 @@ COSMOPOLITAN_C_START_
#define kNtTfUseSystemThread 0x10 #define kNtTfUseSystemThread 0x10
#define kNtTfUseKernelApc 0x20 #define kNtTfUseKernelApc 0x20
struct sockaddr;
struct sockaddr_in;
struct pollfd$nt;
enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS }; enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS };
enum NtWsaCompletionType { enum NtWsaCompletionType {

View File

@ -53,8 +53,7 @@ _executive:
call _setstack call _setstack
mov %eax,%edi mov %eax,%edi
call exit call exit
9: .endfn _executive,weak,hidden .endfn _executive,weak,hidden
ud2 ud2
#ifdef __PG__ #ifdef __PG__

View File

@ -28,9 +28,9 @@
*/ */
void *_getstack(void) { void *_getstack(void) {
char *p; char *p;
p = mmap((char *)0x700000000000 - STACKSIZE, STACKSIZE, p = mmap((char *)0x700000000000 /* IMAGE_BASE_VIRTUAL */ - STACKSIZE,
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, STACKSIZE, PROT_READ | PROT_WRITE,
0); MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED) abort(); if (p == MAP_FAILED) abort();
return p + STACKSIZE; return p + STACKSIZE;
} }

View File

@ -28,8 +28,11 @@
*/ */
bool isheap(void *p) { bool isheap(void *p) {
int x, i; int x, i;
#if 1
register intptr_t rsp asm("rsp"); register intptr_t rsp asm("rsp");
if ((intptr_t)p >= rsp) return false; if ((intptr_t)p >= rsp) return false;
#endif
if ((intptr_t)p <= (intptr_t)_end) return false;
x = (intptr_t)p >> 16; x = (intptr_t)p >> 16;
i = FindMemoryInterval(&_mmi, x); i = FindMemoryInterval(&_mmi, x);
return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y;

View File

@ -17,11 +17,9 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/fmt/fmt.h" #include "libc/conv/itoa.h"
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/sysv/errfuns.h"
#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/af.h"
#include "libc/sysv/errfuns.h"
/** /**
* Formats internet address to string. * Formats internet address to string.
@ -33,11 +31,21 @@
* @return dst on success or NULL w/ errno * @return dst on success or NULL w/ errno
*/ */
const char *inet_ntop(int af, const void *src, char *dst, uint32_t size) { const char *inet_ntop(int af, const void *src, char *dst, uint32_t size) {
char *p;
unsigned char *ip4;
if (src) { if (src) {
if (af == AF_INET) { if (af == AF_INET) {
unsigned char *p = (unsigned char *)src; if (size >= 16) {
if (snprintf(dst, size, "%hhu.%hhu.%hhu.%hhu", p[0], p[1], p[2], p[3]) < p = dst;
size) { ip4 = src;
p += uint64toarray_radix10(ip4[0], p);
*p++ = '.';
p += uint64toarray_radix10(ip4[1], p);
*p++ = '.';
p += uint64toarray_radix10(ip4[2], p);
*p++ = '.';
p += uint64toarray_radix10(ip4[3], p);
*p = '\0';
return dst; return dst;
} else { } else {
enospc(); enospc();

View File

@ -37,6 +37,7 @@ int fwritebuf(FILE *f) {
unsigned bytes; unsigned bytes;
bytes = (f->beg < f->end ? f->end : f->size) - f->beg; bytes = (f->beg < f->end ? f->end : f->size) - f->beg;
if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) { if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) {
if (errno == EINTR) return 0;
return (int)fseterrno(f); return (int)fseterrno(f);
} }
f->beg = (unsigned)((f->beg + put) & (f->size - 1)); f->beg = (unsigned)((f->beg + put) & (f->size - 1));

View File

@ -19,6 +19,7 @@
*/ */
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/str/utf16.h"
/** /**
* Decodes UTF-16 character. * Decodes UTF-16 character.
@ -30,16 +31,15 @@
*/ */
forcealignargpointer unsigned(getutf16)(const char16_t *p, wint_t *wc) { forcealignargpointer unsigned(getutf16)(const char16_t *p, wint_t *wc) {
unsigned skip = 0; unsigned skip = 0;
while ((p[skip] & UTF16_MASK) == UTF16_CONT) skip++; while (IsUtf16Cont(p[skip])) skip++;
if ((p[skip] & UTF16_MASK) != UTF16_MOAR) { if (IsUcs2(p[skip])) {
*wc = p[skip]; *wc = p[skip];
return skip + 1; return skip + 1;
} else if ((p[skip + 1] & UTF16_MASK) != UTF16_CONT) { } else if (IsUtf16Cont(p[skip + 1])) {
*wc = INVALID_CODEPOINT; *wc = INVALID_CODEPOINT;
return -1; return -1;
} else { } else {
*wc = 0x10000 + ((((unsigned)p[skip + 0] - 0xd800) << 10) + *wc = MergeUtf16(p[skip], p[skip + 1]);
((unsigned)p[skip + 1] - 0xdc00));
return skip + 2; return skip + 2;
} }
} }

View File

@ -64,10 +64,6 @@ void *isnotplaintext(const void *, size_t) nothrow nocallback nosideeffect;
#define INVALID_CODEPOINT 0xfffd #define INVALID_CODEPOINT 0xfffd
#define UTF16_MASK 0b1111110000000000
#define UTF16_MOAR 0b1101100000000000 /* 0xD800..0xDBFF */
#define UTF16_CONT 0b1101110000000000 /* 0xDC00..0xDBFF */
unsigned getutf16(const char16_t *, wint_t *); unsigned getutf16(const char16_t *, wint_t *);
int pututf16(char16_t *, size_t, wint_t, bool); int pututf16(char16_t *, size_t, wint_t, bool);
int iswalnum(wint_t); int iswalnum(wint_t);

View File

@ -18,11 +18,14 @@
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/str/utf16.h"
/** /**
* Returns number of characters in UTF-16 or UCS-2 string. * Returns number of characters in UTF-16 or UCS-2 string.
*/ */
size_t strclen16(const char16_t *s) { return strnclen16(s, -1ull); } size_t strclen16(const char16_t *s) {
return strnclen16(s, -1ull);
}
noinline size_t strnclen16(const char16_t *p, size_t n) { noinline size_t strnclen16(const char16_t *p, size_t n) {
size_t l = 0; size_t l = 0;

View File

@ -2,9 +2,10 @@
#define COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ #define COSMOPOLITAN_LIBC_STR_THOMPIKE_H_
#include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/bsr.h"
#define ThomPikeCont(x) ((x & 0b11000000) == 0b10000000) #define ThomPikeCont(x) (((x)&0b11000000) == 0b10000000)
#define ThomPikeByte(x) (x & (((1 << ThomPikeMsb(x)) - 1) | 3)) #define ThomPikeByte(x) ((x) & (((1 << ThomPikeMsb(x)) - 1) | 3))
#define ThomPikeLen(x) (7 - ThomPikeMsb(x)) #define ThomPikeLen(x) (7 - ThomPikeMsb(x))
#define ThomPikeMsb(x) (x < 252 ? bsr(~x & 0xff) : 1) #define ThomPikeMsb(x) ((x) < 252 ? bsr(~(x)&0xff) : 1)
#define ThomPikeMerge(x, y) ((x) << 6 | (y)&0b00111111)
#endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */ #endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */

12
libc/str/utf16.h 100644
View File

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_LIBC_STR_UTF16_H_
#define COSMOPOLITAN_LIBC_STR_UTF16_H_
#define UTF16_MASK 0xfc00
#define UTF16_MOAR 0xd800 /* 0xD800..0xDBFF */
#define UTF16_CONT 0xdc00 /* 0xDC00..0xDBFF */
#define IsUcs2(wc) (((wc)&UTF16_MASK) != UTF16_MOAR)
#define IsUtf16Cont(wc) (((wc)&UTF16_MASK) != UTF16_MOAR)
#define MergeUtf16(lo, hi) ((((lo)-0xD800) << 10) + ((hi)-0xDC00) + 0x10000)
#endif /* COSMOPOLITAN_LIBC_STR_UTF16_H_ */

View File

@ -1421,11 +1421,11 @@ offtime(timep, offset)
** where, to make the math easy, the answer for year zero is defined as zero. ** where, to make the math easy, the answer for year zero is defined as zero.
*/ */
pureconst static int pureconst optimizespeed static int
leaps_thru_end_of(y) leaps_thru_end_of(y)
register const int y; register const int y;
{ {
return (y >= 0) ? (y / 4 - div100int64(y) + y / 400) : return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
-(leaps_thru_end_of(-(y + 1)) + 1); -(leaps_thru_end_of(-(y + 1)) + 1);
} }
@ -1605,15 +1605,19 @@ timesub(timep, offset, sp, tmp)
** Simplified normalize logic courtesy Paul Eggert. ** Simplified normalize logic courtesy Paul Eggert.
*/ */
static int static inline int
increment_overflow(number, delta) increment_overflow(number, delta)
int * number; int * number;
int delta; int delta;
{ {
#ifdef __GNUC__
return __builtin_add_overflow(*number, delta, number);
#else
int number0; int number0;
number0 = *number; number0 = *number;
*number += delta; *number += delta;
return (*number < number0) != (delta < 0); return (*number < number0) != (delta < 0);
#endif
} }
static int static int

View File

@ -12,6 +12,7 @@ extern const char kWeekdayNameShort[7][4];
extern const char kWeekdayName[7][10]; extern const char kWeekdayName[7][10];
extern const char kMonthNameShort[12][4]; extern const char kMonthNameShort[12][4];
extern const char kMonthName[12][10]; extern const char kMonthName[12][10];
extern const unsigned short kMonthYearDay[2][12];
extern char *tzname[2]; extern char *tzname[2];
extern long CLOCKS_PER_SEC; extern long CLOCKS_PER_SEC;

View File

@ -47,9 +47,9 @@ static noinline long times2(struct tms *out_times, struct rusage *ru) {
&KernelTime, &UserTime)) { &KernelTime, &UserTime)) {
return winerr(); return winerr();
} }
filetimetotimeval(&tv, UserTime); FileTimeToTimeVal(&tv, UserTime);
out_times->tms_utime = convertmicros(&tv, tick); out_times->tms_utime = convertmicros(&tv, tick);
filetimetotimeval(&tv, KernelTime); FileTimeToTimeVal(&tv, KernelTime);
out_times->tms_stime = convertmicros(&tv, tick); out_times->tms_stime = convertmicros(&tv, tick);
out_times->tms_cutime = 0; out_times->tms_cutime = 0;
out_times->tms_cstime = 0; out_times->tms_cstime = 0;

View File

@ -57,6 +57,7 @@
#define kZipCfileOffsetLastmodifieddate 14 #define kZipCfileOffsetLastmodifieddate 14
#define kZipCfileOffsetCrc32 16 #define kZipCfileOffsetCrc32 16
#define kZipCfileOffsetCompressedsize 20 #define kZipCfileOffsetCompressedsize 20
#define kZipCfileOffsetUncompressedsize 24
#define kZipCfileOffsetExternalattributes 38 #define kZipCfileOffsetExternalattributes 38
#define kZipCfileOffsetOffset 42 #define kZipCfileOffsetOffset 42
@ -71,10 +72,10 @@
#define kZipGflagUtf8 0x800 #define kZipGflagUtf8 0x800
#define kZipExtraHdrSize 4 #define kZipExtraHdrSize 4
#define kZipExtraZip64 0x0001 #define kZipExtraZip64 0x0001
#define kZipExtraNtfs 0x000a #define kZipExtraNtfs 0x000a
#define kZipExtraNtfsFiletimes 0x0001 #define kZipExtraExtendedTimestamp 0x5455
#define kZipCfileMagic "PK\001\002" #define kZipCfileMagic "PK\001\002"
@ -105,9 +106,10 @@
READ16LE((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */ READ16LE((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */
#define ZIP_CFILE_LASTMODIFIEDDATE(P) \ #define ZIP_CFILE_LASTMODIFIEDDATE(P) \
READ16LE((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */ READ16LE((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */
#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32) #define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32)
#define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize) #define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize)
#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) READ32LE((P) + 24) #define ZIP_CFILE_UNCOMPRESSEDSIZE(P) \
READ32LE((P) + kZipCfileOffsetUncompressedsize)
#define ZIP_CFILE_NAMESIZE(P) READ16LE((P) + 28) #define ZIP_CFILE_NAMESIZE(P) READ16LE((P) + 28)
#define ZIP_CFILE_EXTRASIZE(P) READ16LE((P) + 30) #define ZIP_CFILE_EXTRASIZE(P) READ16LE((P) + 30)
#define ZIP_CFILE_COMMENTSIZE(P) READ16LE((P) + 32) #define ZIP_CFILE_COMMENTSIZE(P) READ16LE((P) + 32)
@ -124,7 +126,7 @@
(ZIP_CFILE_NAMESIZE(P) + ZIP_CFILE_EXTRASIZE(P) + ZIP_CFILE_COMMENTSIZE(P) + \ (ZIP_CFILE_NAMESIZE(P) + ZIP_CFILE_EXTRASIZE(P) + ZIP_CFILE_COMMENTSIZE(P) + \
kZipCfileHdrMinSize) kZipCfileHdrMinSize)
/* central directory file header */ /* local file header */
#define ZIP_LFILE_MAGIC(P) READ32LE(P) #define ZIP_LFILE_MAGIC(P) READ32LE(P)
#define ZIP_LFILE_VERSIONNEED(P) ((P)[4]) #define ZIP_LFILE_VERSIONNEED(P) ((P)[4])
#define ZIP_LFILE_OSNEED(P) ((P)[5]) #define ZIP_LFILE_OSNEED(P) ((P)[5])

View File

@ -29,7 +29,7 @@
*/ */
int __zipos_close(struct ZiposHandle *h) { int __zipos_close(struct ZiposHandle *h) {
if (h) { if (h) {
munmap(h->map, h->mapsize); free(h->map);
free(h); free(h);
} }
return 0; return 0;

View File

@ -27,16 +27,14 @@
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) { ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
size_t i, cf; size_t i, cf;
if ((zipos = __zipos_get())) { assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic); for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir); i < ZIP_CDIR_RECORDS(zipos->cdir);
i < ZIP_CDIR_RECORDS(zipos->cdir); ++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) { assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic); if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) && memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) { return cf;
return cf;
}
} }
} }
return -1; return -1;

View File

@ -99,9 +99,9 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf))) { if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf))) {
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) { if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)); assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf));
h->mapsize = ROUNDUP(h->size + FRAMESIZE, FRAMESIZE); h->mapsize = h->size;
if ((h->map = mapanon(h->mapsize)) != MAP_FAILED) { if ((h->map = malloc(h->mapsize)) != MAP_FAILED) {
h->mem = h->map + FRAMESIZE / 2; h->mem = h->map;
if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)( if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)(
h, ZIP_LFILE_CONTENT(zipos->map + lf), h, ZIP_LFILE_CONTENT(zipos->map + lf),
ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) == -1) { ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) == -1) {
@ -132,7 +132,7 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store. * Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
* *
* @param uri is obtained via __zipos_parseuri() * @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe * @note don't call open() from signal handlers
*/ */
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) { int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
int fd; int fd;
@ -140,7 +140,6 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
sigset_t oldmask; sigset_t oldmask;
struct Zipos *zipos; struct Zipos *zipos;
assert((flags & O_ACCMODE) == O_RDONLY); assert((flags & O_ACCMODE) == O_RDONLY);
sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
if ((zipos = __zipos_get())) { if ((zipos = __zipos_get())) {
if ((cf = __zipos_find(zipos, name)) != -1) { if ((cf = __zipos_find(zipos, name)) != -1) {
fd = __zipos_load(zipos, cf, flags, mode); fd = __zipos_load(zipos, cf, flags, mode);
@ -150,6 +149,5 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
} else { } else {
fd = enoexec(); fd = enoexec();
} }
sigprocmask(SIG_SETMASK, &oldmask, NULL);
return fd; return fd;
} }

View File

@ -0,0 +1,67 @@
/*-*- 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/str/str.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "net/http/http.h"
/**
* Formats HTTP timestamp, e.g.
*
* Sun, 04 Oct 2020 19:50:10 GMT
*
* @param tm must be zulu see gmtime_r() and nowl()
* @see ParseHttpDateTime()
*/
char *FormatHttpDateTime(char p[hasatleast 30], struct tm *tm) {
unsigned i;
p = mempcpy(p, kWeekdayNameShort[tm->tm_wday], 3);
*p++ = ',';
*p++ = ' ';
i = tm->tm_mday;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ' ';
p = mempcpy(p, kMonthNameShort[tm->tm_mon], 3);
*p++ = ' ';
i = tm->tm_year + 1900;
*p++ = '0' + i / 1000;
*p++ = '0' + i / 100 % 10;
*p++ = '0' + i / 10 % 10;
*p++ = '0' + i % 10;
*p++ = ' ';
i = tm->tm_hour;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ':';
i = tm->tm_min;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ':';
i = tm->tm_sec;
*p++ = '0' + i / 10;
*p++ = '0' + i % 10;
*p++ = ' ';
*p++ = 'G';
*p++ = 'M';
*p++ = 'T';
*p = '\0';
return p;
}

View File

@ -58,5 +58,5 @@ Retry-After, kHttpRetryAfter
Server, kHttpServer Server, kHttpServer
Vary, kHttpVary Vary, kHttpVary
Warning, kHttpWarning Warning, kHttpWarning
WWW-Authenticate, kHttpWwwAuthenticate Www-Authenticate, kHttpWwwAuthenticate
Last-Modified, kHttpLastModified Last-Modified, kHttpLastModified

View File

@ -216,7 +216,7 @@ LookupHttpHeader (register const char *str, register size_t len)
#line 43 "gethttpheader.gperf" #line 43 "gethttpheader.gperf"
{"Keep-Alive", kHttpKeepAlive}, {"Keep-Alive", kHttpKeepAlive},
#line 61 "gethttpheader.gperf" #line 61 "gethttpheader.gperf"
{"WWW-Authenticate", kHttpWwwAuthenticate}, {"Www-Authenticate", kHttpWwwAuthenticate},
#line 35 "gethttpheader.gperf" #line 35 "gethttpheader.gperf"
{"Expires", kHttpExpires}, {"Expires", kHttpExpires},
#line 46 "gethttpheader.gperf" #line 46 "gethttpheader.gperf"

View File

@ -9,7 +9,7 @@
%readonly-tables %readonly-tables
%struct-type %struct-type
%define lookup-function-name LookupHttpMethod %define lookup-function-name LookupHttpMethod
struct HttpMethodSlot { char *name; char code; }; struct HttpMethodSlot { char name[8]; char code; };
%% %%
DELETE, kHttpDelete DELETE, kHttpDelete
GET, kHttpGet GET, kHttpGet
@ -20,17 +20,11 @@ CONNECT, kHttpConnect
OPTIONS, kHttpOptions OPTIONS, kHttpOptions
TRACE, kHttpTrace TRACE, kHttpTrace
COPY, kHttpCopy COPY, kHttpCopy
CHECKOUT, kHttpCheckout
LOCK, kHttpLock LOCK, kHttpLock
MERGE, kHttpMerge MERGE, kHttpMerge
MKACTIVITY, kHttpMkactivity
MKCOL, kHttpMkcol MKCOL, kHttpMkcol
MOVE, kHttpMove MOVE, kHttpMove
NOTIFY, kHttpNotify NOTIFY, kHttpNotify
PATCH, kHttpPatch PATCH, kHttpPatch
PROPFIND, kHttpPropfind
PROPPATCH, kHttpProppatch
REPORT, kHttpReport REPORT, kHttpReport
SUBSCRIBE, kHttpSubscribe
UNLOCK, kHttpUnlock UNLOCK, kHttpUnlock
UNSUBSCRIBE, kHttpUnsubscribe

View File

@ -35,14 +35,14 @@
#include "net/http/http.h" #include "net/http/http.h"
#define GPERF_DOWNCASE #define GPERF_DOWNCASE
#line 12 "gethttpmethod.gperf" #line 12 "gethttpmethod.gperf"
struct HttpMethodSlot { char *name; char code; }; struct HttpMethodSlot { char name[8]; char code; };
#define TOTAL_KEYWORDS 23 #define TOTAL_KEYWORDS 17
#define MIN_WORD_LENGTH 3 #define MIN_WORD_LENGTH 3
#define MAX_WORD_LENGTH 11 #define MAX_WORD_LENGTH 7
#define MIN_HASH_VALUE 4 #define MIN_HASH_VALUE 3
#define MAX_HASH_VALUE 34 #define MAX_HASH_VALUE 25
/* maximum key range = 31, duplicates = 0 */ /* maximum key range = 23, duplicates = 0 */
#ifndef GPERF_DOWNCASE #ifndef GPERF_DOWNCASE
#define GPERF_DOWNCASE 1 #define GPERF_DOWNCASE 1
@ -101,32 +101,32 @@ hash (register const char *str, register size_t len)
{ {
static const unsigned char asso_values[] = static const unsigned char asso_values[] =
{ {
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 0, 35, 5, 10, 10, 26, 26, 26, 26, 26, 0, 26, 5, 15, 0,
35, 5, 10, 35, 35, 0, 30, 15, 0, 0, 26, 5, 0, 26, 26, 10, 15, 10, 0, 5,
0, 35, 5, 15, 0, 5, 35, 35, 35, 35, 0, 26, 10, 26, 5, 0, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 0, 35, 5, 26, 26, 26, 26, 26, 26, 26, 0, 26, 5,
10, 10, 35, 5, 10, 35, 35, 0, 30, 15, 15, 0, 26, 5, 0, 26, 26, 10, 15, 10,
0, 0, 0, 35, 5, 15, 0, 5, 35, 35, 0, 5, 0, 26, 10, 26, 5, 0, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
35, 35, 35, 35, 35, 35 26, 26, 26, 26, 26, 26
}; };
return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]]; return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]];
} }
@ -136,58 +136,46 @@ LookupHttpMethod (register const char *str, register size_t len)
{ {
static const struct HttpMethodSlot wordlist[] = static const struct HttpMethodSlot wordlist[] =
{ {
{""}, {""}, {""}, {""}, {""}, {""}, {""},
#line 17 "gethttpmethod.gperf"
{"POST", kHttpPost},
#line 30 "gethttpmethod.gperf"
{"PATCH", kHttpPatch},
#line 29 "gethttpmethod.gperf"
{"NOTIFY", kHttpNotify},
#line 20 "gethttpmethod.gperf"
{"OPTIONS", kHttpOptions},
#line 18 "gethttpmethod.gperf" #line 18 "gethttpmethod.gperf"
{"PUT", kHttpPut}, {"PUT", kHttpPut},
#line 22 "gethttpmethod.gperf" #line 16 "gethttpmethod.gperf"
{"COPY", kHttpCopy}, {"HEAD", kHttpHead},
#line 21 "gethttpmethod.gperf" #line 28 "gethttpmethod.gperf"
{"TRACE", kHttpTrace}, {"PATCH", kHttpPatch},
#line 35 "gethttpmethod.gperf" #line 30 "gethttpmethod.gperf"
{"UNLOCK", kHttpUnlock}, {"UNLOCK", kHttpUnlock},
#line 19 "gethttpmethod.gperf"
{"CONNECT", kHttpConnect},
#line 31 "gethttpmethod.gperf"
{"PROPFIND", kHttpPropfind},
#line 32 "gethttpmethod.gperf"
{"PROPPATCH", kHttpProppatch},
{""},
#line 36 "gethttpmethod.gperf"
{"UNSUBSCRIBE", kHttpUnsubscribe},
{""}, {""},
#line 15 "gethttpmethod.gperf" #line 15 "gethttpmethod.gperf"
{"GET", kHttpGet}, {"GET", kHttpGet},
#line 28 "gethttpmethod.gperf" #line 17 "gethttpmethod.gperf"
{"MOVE", kHttpMove}, {"POST", kHttpPost},
#line 27 "gethttpmethod.gperf" {""},
{"MKCOL", kHttpMkcol}, #line 27 "gethttpmethod.gperf"
#line 33 "gethttpmethod.gperf" {"NOTIFY", kHttpNotify},
{"REPORT", kHttpReport}, #line 20 "gethttpmethod.gperf"
{"OPTIONS", kHttpOptions},
{""},
#line 22 "gethttpmethod.gperf"
{"COPY", kHttpCopy},
#line 24 "gethttpmethod.gperf"
{"MERGE", kHttpMerge},
#line 29 "gethttpmethod.gperf"
{"REPORT", kHttpReport},
#line 19 "gethttpmethod.gperf"
{"CONNECT", kHttpConnect},
{""}, {""},
#line 23 "gethttpmethod.gperf"
{"CHECKOUT", kHttpCheckout},
#line 16 "gethttpmethod.gperf"
{"HEAD", kHttpHead},
#line 26 "gethttpmethod.gperf" #line 26 "gethttpmethod.gperf"
{"MKACTIVITY", kHttpMkactivity}, {"MOVE", kHttpMove},
#line 21 "gethttpmethod.gperf"
{"TRACE", kHttpTrace},
#line 14 "gethttpmethod.gperf" #line 14 "gethttpmethod.gperf"
{"DELETE", kHttpDelete}, {"DELETE", kHttpDelete},
{""}, {""}, {""}, {""},
#line 34 "gethttpmethod.gperf" #line 23 "gethttpmethod.gperf"
{"SUBSCRIBE", kHttpSubscribe}, {"LOCK", kHttpLock},
#line 25 "gethttpmethod.gperf" #line 25 "gethttpmethod.gperf"
{"MERGE", kHttpMerge}, {"MKCOL", kHttpMkcol}
{""}, {""}, {""},
#line 24 "gethttpmethod.gperf"
{"LOCK", kHttpLock}
}; };
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)

View File

@ -1,30 +1,25 @@
#ifndef COSMOPOLITAN_LIBC_HTTP_HTTP_H_ #ifndef COSMOPOLITAN_LIBC_HTTP_HTTP_H_
#define COSMOPOLITAN_LIBC_HTTP_HTTP_H_ #define COSMOPOLITAN_LIBC_HTTP_HTTP_H_
#include "libc/alg/alg.h" #include "libc/alg/alg.h"
#include "libc/time/struct/tm.h"
#define kHttpGet 0 #define kHttpGet 0
#define kHttpHead 1 #define kHttpHead 1
#define kHttpPost 2 #define kHttpPost 2
#define kHttpPut 3 #define kHttpPut 3
#define kHttpDelete 4 #define kHttpDelete 4
#define kHttpConnect 5 #define kHttpConnect 5
#define kHttpOptions 6 #define kHttpOptions 6
#define kHttpTrace 7 #define kHttpTrace 7
#define kHttpCopy 8 #define kHttpCopy 8
#define kHttpCheckout 9 #define kHttpLock 9
#define kHttpLock 10 #define kHttpMerge 10
#define kHttpMerge 11 #define kHttpMkcol 11
#define kHttpMkactivity 12 #define kHttpMove 12
#define kHttpMkcol 13 #define kHttpNotify 13
#define kHttpMove 14 #define kHttpPatch 14
#define kHttpNotify 15 #define kHttpReport 15
#define kHttpPatch 16 #define kHttpUnlock 16
#define kHttpPropfind 17
#define kHttpProppatch 18
#define kHttpReport 19
#define kHttpSubscribe 20
#define kHttpUnlock 21
#define kHttpUnsubscribe 22
#define kHttpAccept 0 #define kHttpAccept 0
#define kHttpAcceptCharset 1 #define kHttpAcceptCharset 1
@ -86,17 +81,24 @@ struct HttpRequestSlice {
struct HttpRequest { struct HttpRequest {
int method; int method;
int length;
struct HttpRequestSlice uri; struct HttpRequestSlice uri;
struct HttpRequestSlice version; struct HttpRequestSlice version;
struct HttpRequestSlice scratch; struct HttpRequestSlice scratch;
struct HttpRequestSlice headers[kHttpHeadersMax]; struct HttpRequestSlice headers[kHttpHeadersMax];
}; };
extern const char kHttpMethod[17][8];
int GetHttpHeader(const char *, size_t); int GetHttpHeader(const char *, size_t);
int GetHttpMethod(const char *, size_t); int GetHttpMethod(const char *, size_t);
int ParseHttpRequest(struct HttpRequest *, const char *, size_t); int ParseHttpRequest(struct HttpRequest *, const char *, size_t);
int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *, int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *,
uint32_t *, bool, long double); uint32_t *, bool, long double);
char *FormatHttpDateTime(char[hasatleast 30], struct tm *);
bool ParseHttpRange(const char *, size_t, long, long *, long *);
unsigned ParseHttpVersion(const char *, size_t);
int64_t ParseHttpDateTime(const char *, size_t);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View File

@ -12,65 +12,70 @@ NET_HTTP_A_SRCS_S = $(filter %.S,$(NET_HTTP_A_FILES))
NET_HTTP_A_SRCS_C = $(filter %.c,$(NET_HTTP_A_FILES)) NET_HTTP_A_SRCS_C = $(filter %.c,$(NET_HTTP_A_FILES))
NET_HTTP_A_SRCS_R = $(filter %.rl,$(NET_HTTP_A_FILES)) NET_HTTP_A_SRCS_R = $(filter %.rl,$(NET_HTTP_A_FILES))
NET_HTTP_A_SRCS = \ NET_HTTP_A_SRCS = \
$(NET_HTTP_A_SRCS_S) \ $(NET_HTTP_A_SRCS_S) \
$(NET_HTTP_A_SRCS_C) \ $(NET_HTTP_A_SRCS_C) \
$(NET_HTTP_A_SRCS_R) $(NET_HTTP_A_SRCS_R)
NET_HTTP_A_OBJS = \ NET_HTTP_A_OBJS = \
$(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \ $(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \
$(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \ $(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \ $(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.o) $(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.o)
NET_HTTP_A_CHECKS = \ NET_HTTP_A_CHECKS = \
$(NET_HTTP_A).pkg \ $(NET_HTTP_A).pkg \
$(NET_HTTP_A_HDRS:%=o/$(MODE)/%.ok) $(NET_HTTP_A_HDRS:%=o/$(MODE)/%.ok)
NET_HTTP_A_DIRECTDEPS = \ NET_HTTP_A_DIRECTDEPS = \
LIBC_ALG \ LIBC_ALG \
LIBC_CALLS \ LIBC_CALLS \
LIBC_CONV \ LIBC_CONV \
LIBC_FMT \ LIBC_FMT \
LIBC_LOG \ LIBC_LOG \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_RUNTIME \ LIBC_RUNTIME \
LIBC_SOCK \ LIBC_SOCK \
LIBC_STDIO \ LIBC_STDIO \
LIBC_STUBS \ LIBC_STUBS \
LIBC_SYSV \ LIBC_SYSV \
LIBC_TIME \ LIBC_TIME \
LIBC_X LIBC_X
NET_HTTP_A_DEPS := \ NET_HTTP_A_DEPS := \
$(call uniq,$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x)))) $(call uniq,$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x))))
$(NET_HTTP_A): net/http/ \ $(NET_HTTP_A): net/http/ \
$(NET_HTTP_A).pkg \ $(NET_HTTP_A).pkg \
$(NET_HTTP_A_OBJS) $(NET_HTTP_A_OBJS)
$(NET_HTTP_A).pkg: \ $(NET_HTTP_A).pkg: \
$(NET_HTTP_A_OBJS) \ $(NET_HTTP_A_OBJS) \
$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x)_A).pkg) $(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/net/http/formathttpdatetime.o: \
OVERRIDE_CFLAGS += \
-O3
ifeq (,$(MODE))
$(NET_HTTP_A_OBJS): \
OVERRIDE_CFLAGS += \
-fsanitize=address
endif
NET_HTTP_LIBS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x))) NET_HTTP_LIBS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)))
NET_HTTP_SRCS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_SRCS)) NET_HTTP_SRCS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_SRCS))
NET_HTTP_HDRS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_HDRS)) NET_HTTP_HDRS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_HDRS))
NET_HTTP_CHECKS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_CHECKS)) NET_HTTP_CHECKS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_CHECKS))
NET_HTTP_OBJS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_OBJS)) NET_HTTP_OBJS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_OBJS))
$(NET_HTTP_OBJS): $(BUILD_FILES) net/http/http.mk .PRECIOUS: \
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.c) \
.PRECIOUS: \ o/$(MODE)/net/http/uricspn.s \
$(NET_HTTP_A_SRCS_R:%.rl=build/bootstrap/%.c) \ o/$(MODE)/net/http/uricspn.i \
o/$(MODE)/net/http/uricspn.s \
o/$(MODE)/net/http/uriparse.s \
o/$(MODE)/net/http/uricspn.i \
o/$(MODE)/net/http/uriparse.i \
o/$(MODE)/net/http/uriparse.c \
o/$(MODE)/net/http/uricspn.c o/$(MODE)/net/http/uricspn.c
.PHONY: o/$(MODE)/net/http .PHONY: o/$(MODE)/net/http
o/$(MODE)/net/http: \ o/$(MODE)/net/http: \
$(NET_HTTP_CHECKS) \ $(NET_HTTP_CHECKS) \
$(NET_HTTP_A_SRCS_R:%.rl=%.svgz) $(NET_HTTP_A_SRCS_R:%.rl=%.svgz)

View File

@ -0,0 +1,40 @@
/*-*- 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 "net/http/http.h"
const char kHttpMethod[17][8] = {
"GET", //
"HEAD", //
"POST", //
"PUT", //
"DELETE", //
"CONNECT", //
"OPTIONS", //
"TRACE", //
"COPY", //
"LOCK", //
"MERGE", //
"MKCOL", //
"MOVE", //
"NOTIFY", //
"PATCH", //
"REPORT", //
"UNLOCK", //
};

View File

@ -0,0 +1,57 @@
/*-*- 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/time/time.h"
#include "net/http/http.h"
static unsigned ParseMonth(const char *p) {
int i;
for (i = 0; i < 12; ++i) {
if ((READ32LE(p) & 0x00ffffff) == READ32LE(kMonthNameShort[i])) {
return i + 1;
}
}
return 1;
}
/**
* Parses HTTP timestamp, e.g.
*
* Sun, 04 Oct 2020 19:50:10 GMT
*
* @return seconds from unix epoch
* @see FormatHttpDateTime()
*/
int64_t ParseHttpDateTime(const char *p, size_t n) {
unsigned weekday, year, month, day, hour, minute, second, yday, leap;
if (n != 29) return 0;
day = (p[5] - '0') * 10 + (p[6] - '0') - 1;
month = ParseMonth(p + 8);
year = (p[12] - '0') * 1000 + (p[13] - '0') * 100 + (p[14] - '0') * 10 +
(p[15] - '0') - 1900;
hour = (p[17] - '0') * 10 + (p[18] - '0');
minute = (p[20] - '0') * 10 + (p[21] - '0');
second = (p[23] - '0') * 10 + (p[24] - '0');
leap = year % 4 == 0 && (year % 100 || year % 400 == 0);
yday = day + kMonthYearDay[leap][month - 1];
return second + minute * 60 + hour * 3600 + yday * 86400 +
(year - 70) * 31536000ull + ((year - 69) / 4) * 86400ull -
((year - 1) / 100) * 86400ull + ((year + 299) / 400) * 86400ull;
}

View File

@ -0,0 +1,72 @@
/*-*- 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/str/str.h"
#include "net/http/http.h"
/**
* Parses HTTP Range request header.
*/
bool ParseHttpRange(const char *p, size_t n, long resourcelength,
long *out_start, long *out_length) {
long start, length, ending;
*out_start = 0;
*out_length = 0;
if (memchr(p, ',', n)) return false;
if (n < 7 || memcmp(p, "bytes=", 6) != 0) return false;
p += 6, n -= 6;
if (n && *p == '-') {
++p, --n;
length = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(length, 10, &length)) return false;
if (__builtin_add_overflow(length, *p - '0', &length)) return false;
++p, --n;
}
if (__builtin_sub_overflow(resourcelength, length, &start)) return false;
} else {
start = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(start, 10, &start)) return false;
if (__builtin_add_overflow(start, *p - '0', &start)) return false;
++p, --n;
}
if (n && *p == '-') {
++p, --n;
length = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(length, 10, &length)) return false;
if (__builtin_add_overflow(length, *p - '0', &length)) return false;
++p, --n;
}
if (__builtin_add_overflow(length, 1, &length)) return false;
if (__builtin_sub_overflow(length, start, &length)) return false;
} else if (__builtin_sub_overflow(resourcelength, start, &length)) {
return false;
}
}
if (n) return false;
if (start < 0) return false;
if (length < 0) return false;
*out_start = start;
*out_length = length;
if (__builtin_add_overflow(start, length, &ending)) return false;
if (ending > resourcelength) return false;
return true;
}

View File

@ -112,7 +112,10 @@ int ParseHttpRequest(struct HttpRequest *req, const char *p, size_t n) {
} }
break; break;
case LF2: case LF2:
if (c == '\n') return 0; if (c == '\n') {
req->length = i + 1;
return 0;
}
return ebadmsg(); return ebadmsg();
default: default:
unreachable; unreachable;

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/bits/bits.h"
#include "net/http/http.h"
unsigned ParseHttpVersion(const char *p, size_t n) {
unsigned x;
if (n >= 8 && READ32LE(p) == ('H' | 'T' << 8 | 'T' << 16 | 'P' << 24)) {
if (READ32LE(p + 4) == ('/' | '1' << 8 | '.' << 16 | '1' << 24)) {
return 101;
} else if (READ32LE(p + 4) == ('/' | '1' << 8 | '.' << 16 | '0' << 24)) {
return 100;
}
}
return -1;
}

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/dos.h"
#include "libc/testlib/testlib.h"
TEST(DosDateTimeToUnix, test) {
EXPECT_EQ(1601929396,
DosDateTimeToUnix(DOS_DATE(2020, 10, 5), DOS_TIME(20, 23, 16)));
}
TEST(DosDateTimeToUnix, testNotLeapYear) {
EXPECT_EQ(4107529396,
DosDateTimeToUnix(DOS_DATE(2100, 2, 28), DOS_TIME(20, 23, 16)));
}

View File

@ -25,6 +25,9 @@ TEST_LIBC_CONV_CHECKS = \
TEST_LIBC_CONV_DIRECTDEPS = \ TEST_LIBC_CONV_DIRECTDEPS = \
LIBC_CONV \ LIBC_CONV \
LIBC_FMT \
LIBC_LOG \
LIBC_STDIO \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_STUBS \ LIBC_STUBS \
LIBC_TINYMATH \ LIBC_TINYMATH \

View File

@ -23,11 +23,11 @@
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/time/time.h" #include "libc/time/time.h"
TEST(timevaltofiletime, roundTrip) { TEST(TimeValToFileTime, roundTrip) {
struct timeval tv1, tv2; struct timeval tv1, tv2;
tv1.tv_sec = 31337; tv1.tv_sec = 31337;
tv1.tv_usec = 1337; tv1.tv_usec = 1337;
filetimetotimeval(&tv2, timevaltofiletime(&tv1)); FileTimeToTimeVal(&tv2, TimeValToFileTime(&tv1));
EXPECT_EQ(31337, tv2.tv_sec); EXPECT_EQ(31337, tv2.tv_sec);
EXPECT_EQ(1337, tv2.tv_usec); EXPECT_EQ(1337, tv2.tv_usec);
} }

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/fmt/fmt.h"
#include "libc/runtime/gc.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
TEST(fcvt, test) {
int decpt, sign;
ASSERT_STREQ("3.14159265358979", xasprintf("%.14f", 3.14159265358979323846));
ASSERT_STREQ("3141592653589793",
fcvt(3.14159265358979323846, 15, &decpt, &sign));
ASSERT_EQ(1, decpt);
ASSERT_EQ(0, sign);
}

View File

@ -679,9 +679,12 @@ TEST(snprintf, formatStringLiteral) {
} }
BENCH(palandprintf, bench) { BENCH(palandprintf, bench) {
EZBENCH2("snprintf %x", donothing, Format("%x", VEIL("r", INT_MIN))); EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23)));
EZBENCH2("snprintf %d", donothing, Format("%d", VEIL("r", INT_MIN))); EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23)));
EZBENCH2("snprintf %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯"))); EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN)));
EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN)));
EZBENCH2("ascii %s", donothing, Format("%s", VEIL("r", "hiuhcreohucreo")));
EZBENCH2("utf8 %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯")));
EZBENCH2("snprintf %hs", donothing, Format("%hs", VEIL("r", u"hi (╯°□°)╯"))); EZBENCH2("snprintf %hs", donothing, Format("%hs", VEIL("r", u"hi (╯°□°)╯")));
EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯"))); EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯")));
EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer)); EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer));

View File

@ -72,6 +72,22 @@ TEST(SUITE(snprintf), testStringPrecision_showsTrueBinary) {
EXPECT_STREQ("\3\4\0", Format("%.*s", 3, "\3\4\0")); EXPECT_STREQ("\3\4\0", Format("%.*s", 3, "\3\4\0"));
} }
TEST(SUITE(snprintf), testPrecision_usesCodepointCount) { TEST(SUITE(snprintf), testPrecision_usesByteCount) {
EXPECT_STREQ("ちゃぶ台返し", Format("%.*s", 6, "ちゃぶ台返し")); EXPECT_STREQ("ちゃ", Format("%.*s", 6, "ちゃぶ台返し"));
}
TEST(SUITE(snprintf), testReprChar16) {
EXPECT_STREQ("u'♥'", Format("%`'hc", u'♥'));
}
TEST(SUITE(snprintf), testReprChar32) {
EXPECT_STREQ("L'♥'", Format("%`'Lc", L'♥'));
}
TEST(SUITE(snprintf), testReprUtf8) {
EXPECT_STREQ("\"\"", Format("%`'s", u8""));
}
TEST(SUITE(snprintf), testReprUtf8Precision_countsBytes) {
EXPECT_STREQ("\"\"", Format("%`'.*s", 3, u8""));
} }

View File

@ -54,10 +54,3 @@ TEST(inet_ntop, testNoSpace) {
ASSERT_STREQ("", buf); ASSERT_STREQ("", buf);
tfree(buf); tfree(buf);
} }
TEST(inet_ntop, testSqueeze) {
char *buf = memcpy(tmalloc(8), "hi", 3);
uint8_t localhost[4] = {0, 0, 0, 0};
ASSERT_STREQ("0.0.0.0", inet_ntop(AF_INET, localhost, buf, 8));
tfree(buf);
}

View File

@ -0,0 +1,48 @@
/*-*- 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/str/str.h"
#include "libc/testlib/testlib.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
#include "net/http/http.h"
#define PARSE(s) ParseHttpDateTime(s, sizeof(s) - 1)
TEST(ParseHttpDateTime, testRoundTrip) {
int64_t t;
struct tm tm;
char b[30], *s = "Mon, 05 Oct 2020 20:23:16 GMT";
t = ParseHttpDateTime(s, strlen(s));
EXPECT_EQ(1601929396, t);
gmtime_r(&t, &tm);
FormatHttpDateTime(b, &tm);
EXPECT_STREQ(s, b);
}
TEST(ParseHttpDateTime, test64bit) {
int64_t t;
struct tm tm;
char b[30], *s = "Tue, 05 Oct 2100 20:23:16 GMT";
t = ParseHttpDateTime(s, strlen(s));
EXPECT_EQ(4126450996, t);
gmtime_r(&t, &tm);
FormatHttpDateTime(b, &tm);
EXPECT_STREQ(s, b);
}

View File

@ -0,0 +1,102 @@
/*-*- 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/str/str.h"
#include "libc/testlib/testlib.h"
#include "net/http/http.h"
TEST(ParseHttpRange, testEmptyHack) {
long start, length;
const char *s = "bytes=-0";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(100, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testEmptyRange_isntEmpty) {
long start, length;
const char *s = "bytes=0-0";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(1, length);
}
TEST(ParseHttpRange, testInclusiveIndexing) {
long start, length;
const char *s = "bytes=0-10";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(11, length);
}
TEST(ParseHttpRange, testOffset) {
long start, length;
const char *s = "bytes=1-10";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(1, start);
EXPECT_EQ(10, length);
}
TEST(ParseHttpRange, testToEnd) {
long start, length;
const char *s = "bytes=40";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(40, start);
EXPECT_EQ(60, length);
}
TEST(ParseHttpRange, testFromEnd) {
long start, length;
const char *s = "bytes=-40";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(60, start);
EXPECT_EQ(40, length);
}
TEST(ParseHttpRange, testOutOfRange) {
long start, length;
const char *s = "bytes=0-100";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(101, length);
}
TEST(ParseHttpRange, testInvalidRange) {
long start, length;
const char *s = "bytes=10-0";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testOverflow_duringIntepretation_doesntSetRanges) {
long start, length;
const char *s = "bytes=99-9223372036854775808";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(0, length);
}
TEST(ParseHttpRange, testOverflow_duringAddition_setsErrorRange) {
long start, length;
const char *s = "bytes=4611686018427387904-4611686018427387915";
EXPECT_FALSE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(4611686018427387904, start);
EXPECT_EQ(12, length);
}

View File

@ -17,6 +17,10 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/rand/lcg.h"
#include "libc/rand/rand.h"
#include "libc/stdio/stdio.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "tool/build/lib/dis.h" #include "tool/build/lib/dis.h"
#include "tool/build/lib/modrm.h" #include "tool/build/lib/modrm.h"
@ -227,7 +231,7 @@ TEST(DisInst, testOrImmCode16gcc) {
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL); xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op))); ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
DisInst(d, b1, DisSpec(d->xedd, b2)); DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("or $0xc00,12(%esp)", b1); EXPECT_STREQ("orw $0xc00,12(%esp)", b1);
} }
TEST(DisInst, testPause) { TEST(DisInst, testPause) {
@ -269,3 +273,19 @@ TEST(DisInst, testJmpEq) {
DisInst(d, b1, DisSpec(d->xedd, b2)); DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("jmp %rax", b1); EXPECT_STREQ("jmp %rax", b1);
} }
TEST(DisInst, testMovswSs) {
uint8_t op[] = {0x36, 0xA5};
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("movs %ss:(%si),(%di)", b1);
}
TEST(DisInst, testRealModrm_sibOverlap_showsNoDisplacement) {
uint8_t op[] = {0x8b, 0b00100101};
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
DisInst(d, b1, DisSpec(d->xedd, b2));
EXPECT_STREQ("mov (%di),%sp", b1);
}

View File

@ -57,6 +57,12 @@ o/$(MODE)/third_party/zlib/adler32.o: \
-ffunction-sections \ -ffunction-sections \
-fdata-sections -fdata-sections
ifeq (,$(MODE))
$(THIRD_PARTY_ZLIB_A_OBJS): \
OVERRIDE_CFLAGS += \
-fsanitize=address
endif
THIRD_PARTY_ZLIB_LIBS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x))) THIRD_PARTY_ZLIB_LIBS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)))
THIRD_PARTY_ZLIB_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_SRCS)) THIRD_PARTY_ZLIB_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_ZLIB_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_HDRS)) THIRD_PARTY_ZLIB_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_HDRS))

View File

@ -35,6 +35,7 @@ TOOL_BUILD_DIRECTDEPS = \
LIBC_ELF \ LIBC_ELF \
LIBC_FMT \ LIBC_FMT \
LIBC_LOG \ LIBC_LOG \
LIBC_LOG_ASAN \
LIBC_TINYMATH \ LIBC_TINYMATH \
LIBC_MEM \ LIBC_MEM \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \

View File

@ -27,6 +27,7 @@ TOOL_BUILD_EMUBIN_CHECKS = \
TOOL_BUILD_EMUBIN_DIRECTDEPS = \ TOOL_BUILD_EMUBIN_DIRECTDEPS = \
LIBC_STUBS \ LIBC_STUBS \
LIBC_NEXGEN32E \
LIBC_TINYMATH LIBC_TINYMATH
TOOL_BUILD_EMUBIN_DEPS := \ TOOL_BUILD_EMUBIN_DEPS := \
@ -57,10 +58,10 @@ o/dbg/tool/build/emubin/lisp.real.com.dbg: \
$(APE) $(APE)
-@$(APELINK) -@$(APELINK)
o/tiny/tool/build/emubin/lisp.bin.dbg: \ o/$(MODE)/tool/build/emubin/lisp.bin.dbg: \
$(TOOL_BUILD_EMUBIN_DEPS) \ $(TOOL_BUILD_EMUBIN_DEPS) \
o/tiny/tool/build/emubin/lisp.real.o \ o/$(MODE)/tool/build/emubin/lisp.real.o \
o/tiny/tool/build/emubin/lispstart.o \ o/$(MODE)/tool/build/emubin/lispstart.o \
tool/build/emubin/lisp.lds tool/build/emubin/lisp.lds
@$(ELFLINK) -z max-page-size=0x10 @$(ELFLINK) -z max-page-size=0x10

View File

@ -3,36 +3,35 @@
Copyright 2020 Justine Alexandra Roberts Tunney Copyright 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify Permission to use, copy, modify, and/or distribute this software for
it under the terms of the GNU General Public License as published by any purpose with or without fee is hereby granted, provided that the
the Free Software Foundation; version 2 of the License. above copyright notice and this permission notice appear in all copies.
This program is distributed in the hope that it will be useful, but THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WITHOUT ANY WARRANTY; without even the implied warranty of WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
General Public License for more details. AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
You should have received a copy of the GNU General Public License PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
along with this program; if not, write to the Free Software TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA PERFORMANCE OF THIS SOFTWARE.
02110-1301 USA
*/ */
#define TRACE 0 #define TRACE 0
#define ERRORS 1 #define ERRORS 1
#define LONG long #define LONG long
#define WORD short #define WORD short
#define WORDS 2048 #define WORDS 8192
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § 8086 PC BIOS / x86_64 Linux System Integration The LISP Challenge § Impure x86_64 Linux 8086 PC BIOS System Integration
*/ */
#define ATOM(x) /* a.k.a. !(x&1) */ \ #define TYPE(x) /* a.k.a. x&1 */ \
({ \ ({ \
char IsAtom; \ char IsAtom; \
asm("test%z1\t$1,%1" : "=@ccz"(IsAtom) : "Qm"((char)x)); \ asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \
IsAtom; \ IsAtom; \
}) })
#define OBJECT(t, v) /* a.k.a. v<<1|t */ \ #define OBJECT(t, v) /* a.k.a. v<<1|t */ \
@ -57,71 +56,109 @@
c; \ c; \
}) })
#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \ #define REAL_READ_(REG, BASE, INDEX, DISP) \
({ \ ({ \
__typeof(*(BASE)) Reg; \ __typeof(*(BASE)) Reg; \
if (__builtin_constant_p(INDEX) && !(INDEX)) { \ if (__builtin_constant_p(INDEX) && !(INDEX)) { \
asm("mov\t%c2(%1),%0" \ asm("mov\t%c2(%1),%0" \
: "=Q"(Reg) \ : REG(Reg) \
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE)))); \ : "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \
} else { \ "m"(BASE[(INDEX) + (DISP)])); \
asm("mov\t%c3(%1,%2),%0" \ } else { \
: "=Q"(Reg) \ asm("mov\t%c3(%1,%2),%0" \
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \ : REG(Reg) \
"i"((DISP) * sizeof(*(BASE)))); \ : "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
} \ "i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \
Reg; \ } \
Reg; \
}) })
#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \ /* #ifdef __REAL_MODE__ */
#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
(sizeof(*(BASE)) == 1 ? REAL_READ_("=Q", BASE, INDEX, DISP) \
: REAL_READ_("=r", BASE, INDEX, DISP))
/* #else */
/* #define REAL_READ(BASE, INDEX, DISP) BASE[INDEX + DISP] */
/* #endif */
#define REAL_READ_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP) \
({ \ ({ \
__typeof(*(OBJECT->MEMBER)) Reg; \ __typeof(*(OBJECT->MEMBER)) Reg; \
if (!(OBJECT)) { \ if (!(OBJECT)) { \
asm("mov\t%c2(%1),%0" \ asm("mov\t%c2(%1),%0" \
: "=Q"(Reg) \ : REG(Reg) \
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ : "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \ sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} else { \ } else { \
asm("mov\t%c3(%1,%2),%0" \ asm("mov\t%c3(%1,%2),%0" \
: "=Q"(Reg) \ : REG(Reg) \
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ : "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \ sizeof(*(OBJECT->MEMBER)) * (DISP)), \
"m"(OBJECT->MEMBER)); \
} \ } \
Reg; \ Reg; \
}) })
#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \ /* #ifdef __REAL_MODE__ */
do { \ #define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
__typeof(*(OBJECT->MEMBER)) Reg; \ (sizeof(*(OBJECT->MEMBER)) == 1 \
if (!(OBJECT)) { \ ? REAL_READ_ARRAY_FIELD_("=Q", OBJECT, MEMBER, INDEX, DISP) \
asm volatile("mov\t%0,%c2(%1)" \ : REAL_READ_ARRAY_FIELD_("=r", OBJECT, MEMBER, INDEX, DISP))
: /* manual output */ \ /* #else */
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), \ /* #define REAL_READ_ARRAY_FIELD(o, m, i, d) o->m[i + d] */
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ /* #endif */
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)) \ #define REAL_WRITE_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \
: "memory"); \ do { \
} else { \ if (!(OBJECT)) { \
asm volatile("mov\t%0,%c3(%1,%2)" \ asm("mov\t%1,%c3(%2)" \
: /* manual output */ \ : "=m"(OBJECT->MEMBER) \
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \ : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP)) \ sizeof(*(OBJECT->MEMBER)) * (DISP))); \
: "memory"); \ } else { \
} \ asm("mov\t%1,%c4(%2,%3)" \
: "=m"(OBJECT->MEMBER) \
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
} \
} while (0) } while (0)
static void *SetMemory(void *di, int al, unsigned long cx) { /* #ifdef __REAL_MODE__ */
#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \
do { \
__typeof(*(OBJECT->MEMBER)) Reg; \
switch (sizeof(*(OBJECT->MEMBER))) { \
case 1: \
REAL_WRITE_ARRAY_FIELD_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
default: \
REAL_WRITE_ARRAY_FIELD_("ri", OBJECT, MEMBER, INDEX, DISP, VALUE); \
break; \
} \
} while (0)
/* #else */
/* #define REAL_WRITE_ARRAY_FIELD(o, m, i, d, v) o->m[i + d] = v */
/* #endif */
long jb[8];
int setjmp(void *) __attribute__((__returns_twice__));
int longjmp(void *, int) __attribute__((__noreturn__));
static inline void *SetMemory(void *di, int al, unsigned long cx) {
asm("rep stosb" asm("rep stosb"
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(cx), "a"(al)); : "0"(di), "1"(cx), "a"(al));
return di; return di;
} }
static void *CopyMemory(void *di, void *si, unsigned long cx) { static inline void *CopyMemory(void *di, void *si, unsigned long cx) {
asm("rep movsb" asm("rep movsb"
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(si), "2"(cx)); : "0"(di), "1"(si), "2"(cx));
@ -147,14 +184,13 @@ static void RawMode(void) {
#endif #endif
} }
__attribute__((__noinline__)) static int PrintChar(LONG c) { __attribute__((__noinline__)) static void PrintChar(LONG c) {
#ifdef __REAL_MODE__ #ifdef __REAL_MODE__
asm volatile("mov\t$0x0E,%%ah\n\t" asm volatile("mov\t$0x0E,%%ah\n\t"
"int\t$0x10" "int\t$0x10"
: /* no outputs */ : /* no outputs */
: "a"(c), "b"(7) : "a"(c), "b"(7)
: "memory"); : "memory");
return 0;
#else #else
static short buf; static short buf;
int rc; int rc;
@ -163,7 +199,6 @@ __attribute__((__noinline__)) static int PrintChar(LONG c) {
: "=a"(rc) : "=a"(rc)
: "0"(1), "D"(1), "S"(&buf), "d"(1) : "0"(1), "D"(1), "S"(&buf), "d"(1)
: "rcx", "r11", "memory"); : "rcx", "r11", "memory");
return rc;
#endif #endif
} }
@ -177,6 +212,7 @@ static void PrintString(char *s) {
} }
static int XlatChar(LONG c) { static int XlatChar(LONG c) {
if (c == 0x7F) return '\b';
if (c >= 'a') { if (c >= 'a') {
asm volatile("" ::: "memory"); asm volatile("" ::: "memory");
if (c <= 'z') c -= 'a' - 'A'; if (c <= 'z') c -= 'a' - 'A';
@ -185,19 +221,16 @@ static int XlatChar(LONG c) {
} }
static int EchoChar(LONG c) { static int EchoChar(LONG c) {
if (c == '\b' || c == 0x7F) { if (c != '\b') {
PrintString("\b \b");
return '\b';
} else {
PrintChar(c); PrintChar(c);
if (c == '\r') { if (c == '\r') {
PrintChar('\n'); PrintChar('\n');
} }
return c;
} }
return c;
} }
static noinline int ReadChar(void) { __attribute__((__noinline__)) static noinline int ReadChar(void) {
int c; int c;
#ifdef __REAL_MODE__ #ifdef __REAL_MODE__
asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory"); asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory");
@ -213,13 +246,13 @@ static noinline int ReadChar(void) {
} }
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
The LISP Challenge § LISP Machine The LISP Challenge § Pure Original LISP Machine
*/ */
#define TYPE_ATOM 0 #define ATOM 0
#define TYPE_CONS 1 #define CONS 1
#define ATOM_NIL 0 #define NIL 0
#define ATOM_T 8 #define ATOM_T 8
#define ATOM_QUOTE 12 #define ATOM_QUOTE 12
#define ATOM_ATOM 24 #define ATOM_ATOM 24
@ -234,7 +267,7 @@ static noinline int ReadChar(void) {
#define ATOM_DEFUN 110 #define ATOM_DEFUN 110
#define Quote(x) List(ATOM_QUOTE, x) #define Quote(x) List(ATOM_QUOTE, x)
#define List(x, y) Cons(x, Cons(y, ATOM_NIL)) #define List(x, y) Cons(x, Cons(y, NIL))
#define Caar(x) Car(Car(x)) // ((A B C D) (E F G) H I) → A #define Caar(x) Car(Car(x)) // ((A B C D) (E F G) H I) → A
#define Cdar(x) Cdr(Car(x)) // ((A B C D) (E F G) H I) → (B C D) #define Cdar(x) Cdr(Car(x)) // ((A B C D) (E F G) H I) → (B C D)
#define Cadar(x) Cadr(Car(x)) // ((A B C D) (E F G) H I) → B #define Cadar(x) Cadr(Car(x)) // ((A B C D) (E F G) H I) → B
@ -242,22 +275,40 @@ static noinline int ReadChar(void) {
#define Cadr(x) Car(Cdr(x)) // ((A B C D) (E F G) H I) → (E F G) #define Cadr(x) Car(Cdr(x)) // ((A B C D) (E F G) H I) → (E F G)
#define Caddr(x) Cadr(Cdr(x)) // ((A B C D) (E F G) H I) → H #define Caddr(x) Cadr(Cdr(x)) // ((A B C D) (E F G) H I) → H
#define BOOL(x) ((x) ? ATOM_T : ATOM_NIL) #define BOOL(x) ((x) ? ATOM_T : NIL)
#define VALUE(x) ((x) >> 1) #define VALUE(x) ((x) >> 1)
#define PTR(i) ((i) << 1 | CONS)
#define ARRAYLEN(A) \
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
struct Lisp { struct Lisp {
WORD memory[WORDS]; WORD mem[WORDS];
unsigned char syntax[256]; unsigned char syntax[256];
unsigned char look; WORD look;
char token[16];
WORD globals; WORD globals;
int index; WORD index;
char token[128];
char str[WORDS]; char str[WORDS];
}; };
const char kSymbols[] aligned(1) = "\ _Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600,
"LISP Machine too large for real mode");
_Alignas(char) const char kSymbols[] = "\
NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0"; NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0";
_Alignas(WORD) const WORD kGlobals[] = {
[0] = PTR(2), // ((T . T) (NIL . NIL))
[1] = PTR(4), //
[2] = ATOM_T, // (T . T)
[3] = ATOM_T, //
[4] = PTR(6), // ((NIL . NIL))
[5] = NIL, //
[6] = NIL, // (NIL . NIL)
[7] = NIL, //
};
#ifdef __REAL_MODE__ #ifdef __REAL_MODE__
static struct Lisp *const q; static struct Lisp *const q;
#else #else
@ -281,28 +332,39 @@ static void SetupSyntax(void) {
q->syntax['\''] = '\''; q->syntax['\''] = '\'';
} }
forceinline WORD Car(LONG x) { static inline WORD Car(LONG x) {
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 0); return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 0);
} }
forceinline WORD Cdr(LONG x) { static inline WORD Cdr(LONG x) {
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 1); return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 1);
} }
static WORD Cons(WORD car, WORD cdr) { static WORD Cons(WORD car, WORD cdr) {
int i, c; #if TRACE
PrintString("CONS->");
Print(car);
PrintString(" ");
Print(cdr);
#endif
int i, cell;
i = q->index; i = q->index;
REAL_WRITE_ARRAY_FIELD(q, memory, i, 0, car); REAL_WRITE_ARRAY_FIELD(q, mem, i, 0, car);
REAL_WRITE_ARRAY_FIELD(q, memory, i, 1, cdr); REAL_WRITE_ARRAY_FIELD(q, mem, i, 1, cdr);
q->index += 2; q->index = i + 2;
c = OBJECT(TYPE_CONS, i); cell = OBJECT(CONS, i);
return c; #if TRACE
PrintString("CONS<-");
Print(cell);
#endif
return cell;
} }
static void SetupBuiltins(void) { static void SetupBuiltins(void) {
CopyMemory(q->str, kSymbols, sizeof(kSymbols)); CopyMemory(q->str, kSymbols, sizeof(kSymbols));
q->globals = CopyMemory(q->mem, kGlobals, sizeof(kGlobals));
Cons(Cons(ATOM_NIL, ATOM_NIL), Cons(Cons(ATOM_T, ATOM_T), ATOM_NIL)); q->index = ARRAYLEN(kGlobals);
q->globals = PTR(0);
} }
static char *StpCpy(char *d, char *s) { static char *StpCpy(char *d, char *s) {
@ -314,7 +376,7 @@ static char *StpCpy(char *d, char *s) {
return d; return d;
} }
static WORD Intern(char *s) { WORD Intern(char *s) {
int j, cx; int j, cx;
char c, *z, *t; char c, *z, *t;
z = q->str; z = q->str;
@ -325,7 +387,7 @@ static WORD Intern(char *s) {
break; break;
} }
if (!c) { if (!c) {
return OBJECT(TYPE_ATOM, z - q->str - j - 1); return OBJECT(ATOM, z - q->str - j - 1);
} }
c = LODS(z); c = LODS(z);
} }
@ -334,11 +396,11 @@ static WORD Intern(char *s) {
} }
--z; --z;
StpCpy(z, s); StpCpy(z, s);
return OBJECT(TYPE_ATOM, SUB((long)z, q->str)); return OBJECT(ATOM, SUB((long)z, q->str));
} }
forceinline unsigned char XlatSyntax(unsigned char b) { forceinline unsigned char XlatSyntax(unsigned char b) {
return REAL_READ_ARRAY_FIELD(q, syntax, b, 0); /* a.k.a. q->syntax[b] */ return REAL_READ_ARRAY_FIELD(q, syntax, b, 0);
} }
static void GetToken(void) { static void GetToken(void) {
@ -357,7 +419,8 @@ static void GetToken(void) {
if (b != '\b') { if (b != '\b') {
STOS(t, b); STOS(t, b);
} else if (t > q->token) { } else if (t > q->token) {
--t; PrintString("\b \b");
if (t > q->token) --t;
} }
b = ReadChar(); b = ReadChar();
} }
@ -387,7 +450,7 @@ static WORD GetList(void) {
case '\'': case '\'':
return AddList(GetQuote()); return AddList(GetQuote());
case ')': case ')':
return ATOM_NIL; return NIL;
case '.': case '.':
return ConsumeObject(); return ConsumeObject();
} }
@ -422,7 +485,7 @@ static void PrintList(LONG x) {
PrintChar('('); PrintChar('(');
PrintObject(Car(x)); PrintObject(Car(x));
while ((x = Cdr(x))) { while ((x = Cdr(x))) {
if (!ATOM(x)) { if (TYPE(x) == CONS) {
PrintChar(' '); PrintChar(' ');
PrintObject(Car(x)); PrintObject(Car(x));
} else { } else {
@ -434,7 +497,7 @@ static void PrintList(LONG x) {
} }
static void PrintObject(LONG x) { static void PrintObject(LONG x) {
if (ATOM(x)) { if (TYPE(x) == ATOM) {
PrintAtom(x); PrintAtom(x);
} else { } else {
PrintList(x); PrintList(x);
@ -447,8 +510,7 @@ static void Print(LONG i) {
} }
__attribute__((__noreturn__)) static void Reset(void) { __attribute__((__noreturn__)) static void Reset(void) {
asm volatile("jmp\tRepl"); longjmp(jb, 1);
__builtin_unreachable();
} }
__attribute__((__noreturn__)) static void OnUndefined(LONG x) { __attribute__((__noreturn__)) static void OnUndefined(LONG x) {
@ -472,7 +534,7 @@ __attribute__((__noreturn__)) static void OnArity(void) {
*/ */
static WORD Atom(LONG x) { static WORD Atom(LONG x) {
return BOOL(ATOM(x)); return BOOL(TYPE(x) == ATOM);
} }
static WORD Null(LONG x) { static WORD Null(LONG x) {
@ -480,24 +542,32 @@ static WORD Null(LONG x) {
} }
static WORD Eq(LONG x, LONG y) { static WORD Eq(LONG x, LONG y) {
return BOOL(x == y); /* undefiled if !ATOM(x)||!ATOM(y) */ return BOOL(x == y); /* undefined if !Atom(x)||!Atom(y) */
} }
static WORD Assoc(LONG x, LONG y) { static WORD Assoc(LONG x, LONG y) {
for (;;) { if (Null(y)) OnUndefined(x);
if (!y) OnUndefined(x); if (Eq(Caar(y), x)) return Cdar(y);
if (Eq(Caar(y), x)) break; return Assoc(x, Cdr(y));
y = Cdr(y);
}
return Cdar(y);
} }
static WORD Append(LONG x, LONG y) { static WORD Append(LONG x, LONG y) {
if (x) { #if TRACE
return Cons(Car(x), Append(Cdr(x), y)); PrintString("APPEND->");
Print(x);
PrintString(" ");
Print(y);
#endif
if (!Null(x)) {
x = Cons(Car(x), Append(Cdr(x), y));
} else { } else {
return y; x = y;
} }
#if TRACE
PrintString("APPEND<-");
Print(x);
#endif
return x;
} }
/** /**
@ -506,26 +576,41 @@ static WORD Append(LONG x, LONG y) {
* @note recoded to make lists in dot notation * @note recoded to make lists in dot notation
* @note it's zip() basically * @note it's zip() basically
*/ */
static WORD Pair(LONG x, LONG y) { static WORD Pair_(LONG x, LONG y) {
if (!x && !y) { if (Null(x) && Null(y)) {
return ATOM_NIL; return NIL;
} else if (!ATOM(x) && !ATOM(y)) { } else if (TYPE(x) == CONS && TYPE(y) == CONS) {
return Cons(Cons(Car(x), Car(y)), Pair(Cdr(x), Cdr(y))); return Cons(Cons(Car(x), Car(y)), Pair_(Cdr(x), Cdr(y)));
} else { } else {
OnArity(); OnArity();
} }
} }
static WORD Pair(LONG x, LONG y) {
#if TRACE
PrintString("PAIR->");
Print(x);
PrintString(" ");
Print(y);
#endif
x = Pair_(x, y);
#if TRACE
PrintString("PAIR<-");
Print(x);
#endif
return x;
}
static WORD Appq(long m) { static WORD Appq(long m) {
if (m) { if (m) {
return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m))); return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m)));
} else { } else {
return ATOM_NIL; return NIL;
} }
} }
static WORD Apply(long f, long a) { static WORD Apply(long f, long a) {
return Eval(Cons(f, Appq(a)), ATOM_NIL); return Eval(Cons(f, Appq(a)), NIL);
} }
static WORD Evcon(LONG c, LONG a) { static WORD Evcon(LONG c, LONG a) {
@ -536,14 +621,29 @@ static WORD Evcon(LONG c, LONG a) {
} }
} }
static WORD Evlis(LONG m, LONG a) { static WORD Evlis_(LONG m, LONG a) {
if (m) { if (m) {
return Cons(Eval(Car(m), a), Evlis(Cdr(m), a)); return Cons(Eval(Car(m), a), Evlis_(Cdr(m), a));
} else { } else {
return ATOM_NIL; return NIL;
} }
} }
static WORD Evlis(LONG m, LONG a) {
#if TRACE
PrintString("EVLIS->");
Print(m);
PrintString(" ");
Print(a);
#endif
m = Evlis_(m, a);
#if TRACE
PrintString("EVLIS<-");
Print(m);
#endif
return m;
}
static WORD Set(LONG e) { static WORD Set(LONG e) {
WORD name, value; WORD name, value;
name = Car(e); name = Car(e);
@ -563,9 +663,9 @@ static WORD Defun(LONG e) {
} }
static WORD Evaluate(LONG e, LONG a) { static WORD Evaluate(LONG e, LONG a) {
if (ATOM(e)) { if (Atom(e)) {
return Assoc(e, a); return Assoc(e, a);
} else if (ATOM(Car(e))) { } else if (Atom(Car(e))) {
switch (Car(e)) { switch (Car(e)) {
case ATOM_QUOTE: case ATOM_QUOTE:
return Cadr(e); return Cadr(e);
@ -601,6 +701,8 @@ static WORD Eval(LONG e, LONG a) {
#if TRACE #if TRACE
PrintString("->"); PrintString("->");
Print(e); Print(e);
PrintString(" ");
Print(a);
#endif #endif
e = Evaluate(e, a); e = Evaluate(e, a);
#if TRACE #if TRACE
@ -615,6 +717,7 @@ static WORD Eval(LONG e, LONG a) {
*/ */
void Repl(void) { void Repl(void) {
setjmp(jb);
for (;;) { for (;;) {
PrintString("* "); PrintString("* ");
Print(Eval(Read(), q->globals)); Print(Eval(Read(), q->globals));
@ -622,7 +725,7 @@ void Repl(void) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
RawMode(); /* RawMode(); */
SetupSyntax(); SetupSyntax();
SetupBuiltins(); SetupBuiltins();
PrintString("THE LISP CHALLENGE V1\r\n" PrintString("THE LISP CHALLENGE V1\r\n"

View File

@ -29,6 +29,7 @@ SECTIONS {
. = 0x1fe; . = 0x1fe;
SHORT(0xaa55); SHORT(0xaa55);
*(.text .text.*) *(.text .text.*)
_etext = .;
. = ALIGN(512); . = ALIGN(512);
} }
@ -43,10 +44,11 @@ SECTIONS {
} }
} }
syntax = 0x600+2048*2; boot = 0x7c00;
look = 0x600+2048*2+256; q.syntax = 8192*2;
token = 0x600+2048*2+256+1; q.look = 8192*2+256;
globals = 0x600+2048*2+256+1+16; q.globals = 8192*2+256+2;
index = 0x600+2048*2+256+1+16+2; q.index = 8192*2+256+2+2;
str = 0x600+2048*2+256+1+16+2+4; q.token = 8192*2+256+2+2+2;
v_sectors = SIZEOF(.text) / 512; q.str = 8192*2+256+2+2+2+128;
v_sectors = SIZEOF(.text) / 512;

View File

@ -21,8 +21,8 @@
.code16 .code16
.section .start,"ax",@progbits .section .start,"ax",@progbits
_start: jmp 1f _start: jmp 1f
1: ljmp $0x600>>4,$2f 1: ljmp $0x600>>4,$_begin
2: push %cs _begin: push %cs
pop %ds pop %ds
push %cs push %cs
pop %es pop %es
@ -50,3 +50,45 @@ _start: jmp 1f
.globl _start .globl _start
.globl v_sectors .globl v_sectors
.globl main .globl main
setjmp: mov %sp,%ax
stosw # sp
xchg %ax,%si
movsw %ss:(%si),(%di) # ip
mov %bp,%ax
stosw # bp
ret
.type setjmp,@function
.size setjmp,.-setjmp
.globl setjmp
longjmp:
mov (%di),%sp
mov 2(%di),%dx
mov 4(%di),%bp
pop %ax
mov %si,%ax
jmp *%dx
.type longjmp,@function
.size longjmp,.-longjmp
.globl longjmp
.globl q.syntax
.type q.syntax,@function
.globl q.look
.type q.look,@function
.globl q.globals
.type q.globals,@function
.globl q.index
.type q.index,@function
.globl q.token
.type q.token,@function
.globl q.str
.type q.str,@function
.globl boot
.type boot,@function
.globl bss
.type bss,@function
.globl rodata
.type rodata,@function

View File

@ -56,6 +56,7 @@
#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h" #include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/time/time.h"
#include "libc/unicode/unicode.h" #include "libc/unicode/unicode.h"
#include "libc/x/x.h" #include "libc/x/x.h"
#include "third_party/dtoa/dtoa.h" #include "third_party/dtoa/dtoa.h"
@ -91,8 +92,7 @@ DESCRIPTION\n\
\n\ \n\
FLAGS\n\ FLAGS\n\
\n\ \n\
-h\n\ -h help\n\
-? help\n\
-v verbosity\n\ -v verbosity\n\
-r real mode\n\ -r real mode\n\
-s statistics\n\ -s statistics\n\
@ -137,8 +137,9 @@ COMPLETENESS\n\
#define QUIT 0x200 #define QUIT 0x200
#define EXIT 0x400 #define EXIT 0x400
#define CTRL(C) ((C) ^ 0100) #define CTRL(C) ((C) ^ 0100)
#define ALT(C) (('\e' << 010) | (C)) #define ALT(C) (('\e' << 010) | (C))
#define SEX(x, b) ((x) | ((x) & (1ull << (b)) ? -(1ull << (b)) : 0))
struct Panels { struct Panels {
union { union {
@ -215,16 +216,6 @@ static struct Breakpoints breakpoints;
static void SetupDraw(void); static void SetupDraw(void);
static void Redraw(void); static void Redraw(void);
static uint64_t SignExtend(uint64_t x, uint8_t b) {
uint64_t s;
s = 1;
b -= 1;
b &= 63;
s <<= b;
if (x & s) x |= ~(s - 1);
return x;
}
static char *FormatDouble(char *b, double x) { static char *FormatDouble(char *b, double x) {
return g_fmt(b, x); return g_fmt(b, x);
} }
@ -234,16 +225,15 @@ static void SetCarry(bool cf) {
} }
static bool IsCall(void) { static bool IsCall(void) {
return m->xedd->op.map == XED_ILD_MAP0 && return (m->xedd->op.dispatch == 0x0E8 ||
(m->xedd->op.opcode == 0xE8 || (m->xedd->op.dispatch == 0x0FF && m->xedd->op.reg == 2));
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 2));
} }
static bool IsLongBranch(void) { static bool IsLongBranch(void) {
return m->xedd && m->xedd->op.map == XED_ILD_MAP0 && return m->mode != XED_MODE_LONG &&
(m->xedd->op.opcode == 0xEA || m->xedd->op.opcode == 0x9A || (m->xedd->op.dispatch == 0x0EA || m->xedd->op.dispatch == 0x09A ||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 3) || (m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 3) ||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 5)); (m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 5));
} }
static bool IsDebugBreak(void) { static bool IsDebugBreak(void) {
@ -370,13 +360,16 @@ static void GetTtySize(void) {
static void TuiRejuvinate(void) { static void TuiRejuvinate(void) {
GetTtySize(); GetTtySize();
ttyhidecursor(STDOUT_FILENO); ttyhidecursor(STDOUT_FILENO);
ttyraw(0); ttyraw(kTtySigs);
xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL); xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL);
} }
static void OnCtrlC(void) { static void OnCtrlC(void) {
LOGF("OnCtrlC"); if (tuimode) {
action |= INT; action |= INT;
} else {
HaltMachine(m, kMachineExit);
}
} }
static void OnQ(void) { static void OnQ(void) {
@ -404,7 +397,7 @@ static void OnCont(void) {
static void TuiCleanup(void) { static void TuiCleanup(void) {
sigaction(SIGWINCH, oldsig + 0, NULL); sigaction(SIGWINCH, oldsig + 0, NULL);
sigaction(SIGCONT, oldsig + 2, NULL); sigaction(SIGCONT, oldsig + 2, NULL);
ttyraw((enum TtyRawFlags)(-1u)); ttyraw(-1);
ttyshowcursor(STDOUT_FILENO); ttyshowcursor(STDOUT_FILENO);
CHECK_NE(-1, close(ttyfd)); CHECK_NE(-1, close(ttyfd));
tuimode = false; tuimode = false;
@ -447,6 +440,7 @@ void TuiSetup(void) {
CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR))); CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR)));
xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0); xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0);
xsigaction(SIGCONT, OnCont, SA_RESTART, 0, oldsig + 2); xsigaction(SIGCONT, OnCont, SA_RESTART, 0, oldsig + 2);
xsigaction(SIGINT, OnCtrlC, 0 /* SA_NODEFER */, 0, oldsig + 3);
memcpy(&m[1], &m[0], sizeof(m[0])); memcpy(&m[1], &m[0], sizeof(m[0]));
TuiRejuvinate(); TuiRejuvinate();
} }
@ -660,12 +654,19 @@ static void SetupDraw(void) {
pan.display.right - pan.display.left); pan.display.right - pan.display.left);
} }
static long Disassemble(void) {
long lines, current;
lines = pan.disassembly.bottom - pan.disassembly.top * 2;
CHECK_NE(-1, Dis(dis, m, GetIp(), m->ip, lines));
current = DisFind(dis, GetIp());
CHECK_NE(-1, current);
return current;
}
static long GetDisIndex(void) { static long GetDisIndex(void) {
long i; long i;
if ((i = DisFind(dis, GetIp())) == -1 || IsLongBranch()) { if ((i = DisFind(dis, GetIp())) == -1) {
Dis(dis, m, GetIp(), m->ip, i = Disassemble();
pan.disassembly.bottom - pan.disassembly.top * 2);
CHECK_NE(-1, (i = DisFind(dis, GetIp())));
} }
while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++i; while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++i;
return i; return i;
@ -864,7 +865,7 @@ static void DrawXmm(struct Panel *p, long i, long r) {
if (0 && ssewidth == 1 && (040 <= ival && ival < 0200 - 1)) { if (0 && ssewidth == 1 && (040 <= ival && ival < 0200 - 1)) {
sprintf(buf, "%`'c", ival); sprintf(buf, "%`'c", ival);
} else { } else {
int64toarray_radix10(SignExtend(ival, ssewidth * 8), buf); int64toarray_radix10(SEX(ival, ssewidth * 8), buf);
} }
} else { } else {
uint64toarray_fixed16(ival, buf, ssewidth * 8); uint64toarray_fixed16(ival, buf, ssewidth * 8);
@ -977,7 +978,7 @@ static void DrawBreakpoints(struct Panel *p) {
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
s = buf; s = buf;
s += sprintf(s, "%p ", addr); s += sprintf(s, "%p ", addr);
CHECK_LT(Demangle(s, name), buf + ARRAYLEN(buf)); CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf));
AppendPanel(p, line, buf); AppendPanel(p, line, buf);
if (sym != -1 && addr != dis->syms.p[sym].addr) { if (sym != -1 && addr != dis->syms.p[sym].addr) {
snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr); snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr);
@ -1015,7 +1016,7 @@ static void DrawTrace(struct Panel *p) {
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
s = line; s = line;
s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp); s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp);
s = Demangle(s, name); s = Demangle(s, name, DIS_MAX_SYMBOL_LENGTH);
AppendPanel(p, i, line); AppendPanel(p, i, line);
if (sym != -1 && rp != dis->syms.p[sym].addr) { if (sym != -1 && rp != dis->syms.p[sym].addr) {
snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr); snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr);
@ -1417,7 +1418,6 @@ static void OnVidyaServiceTeletypeOutput(void) {
char buf[12]; char buf[12];
n = FormatCga(m->bx[0], buf); n = FormatCga(m->bx[0], buf);
n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false); n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false);
LOGF("teletype output %`'.*s", n, buf);
MachinePtyWrite(pty, buf, n); MachinePtyWrite(pty, buf, n);
} }
@ -1520,6 +1520,7 @@ static void OnInt15h(void) {
} }
static bool OnHalt(int interrupt) { static bool OnHalt(int interrupt) {
LOGF("OnHalt(%d)", interrupt);
ReactiveDraw(); ReactiveDraw();
switch (interrupt) { switch (interrupt) {
case 1: case 1:
@ -1577,15 +1578,24 @@ static void OnPageDown(void) {
static void OnUpArrow(void) { static void OnUpArrow(void) {
if (action & CONTINUE) { if (action & CONTINUE) {
speed = MIN(0x40000000, MAX(1, speed) << 1); if (speed >= -1) {
speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip
} else {
speed >>= 1;
}
} else { } else {
--opstart; --opstart;
} }
LOGF("speed %d", speed);
} }
static void OnDownArrow(void) { static void OnDownArrow(void) {
if (action & CONTINUE) { if (action & CONTINUE) {
speed >>= 1; if (speed > 0) {
speed >>= 1;
} else {
speed = MAX(-(5 * 1000), MIN(-10, speed) << 1); // 10ms..5s delay
}
} else { } else {
++opstart; ++opstart;
} }
@ -1897,6 +1907,9 @@ static void Tui(void) {
action &= ~WINCHED; action &= ~WINCHED;
} }
interactive = ++tick > speed; interactive = ++tick > speed;
if (interactive && speed < 0) {
dsleep(.001L * -speed);
}
if (!(action & CONTINUE) || interactive) { if (!(action & CONTINUE) || interactive) {
tick = 0; tick = 0;
GetDisIndex(); GetDisIndex();
@ -1907,7 +1920,8 @@ static void Tui(void) {
LOGF("TUI FAILURE"); LOGF("TUI FAILURE");
PrintMessageBox(ttyfd, systemfailure, tyn, txn); PrintMessageBox(ttyfd, systemfailure, tyn, txn);
ReadKeyboard(); ReadKeyboard();
} else if (!IsExecuting() || (interactive && HasPendingKeyboard())) { } else if (!IsExecuting() || ((interactive || !(action & CONTINUE)) &&
!(action & INT) && HasPendingKeyboard())) {
ReadKeyboard(); ReadKeyboard();
} }
if (action & INT) { if (action & INT) {
@ -1961,6 +1975,9 @@ static void Tui(void) {
} }
if (!IsDebugBreak()) { if (!IsDebugBreak()) {
ExecuteInstruction(m); ExecuteInstruction(m);
if (IsLongBranch()) {
Disassemble();
}
} else { } else {
m->ip += m->xedd->length; m->ip += m->xedd->length;
action &= ~NEXT; action &= ~NEXT;
@ -1997,7 +2014,7 @@ static void Tui(void) {
static void GetOpts(int argc, char *argv[]) { static void GetOpts(int argc, char *argv[]) {
int opt; int opt;
stpcpy(stpcpy(stpcpy(logpath, kTmpPath), basename(argv[0])), ".log"); stpcpy(stpcpy(stpcpy(logpath, kTmpPath), basename(argv[0])), ".log");
while ((opt = getopt(argc, argv, "?hvtrRsb:HL:")) != -1) { while ((opt = getopt(argc, argv, "hvtrRsb:HL:")) != -1) {
switch (opt) { switch (opt) {
case 't': case 't':
tuimode = true; tuimode = true;
@ -2007,6 +2024,7 @@ static void GetOpts(int argc, char *argv[]) {
break; break;
case 'r': case 'r':
m->mode = XED_MACHINE_MODE_REAL; m->mode = XED_MACHINE_MODE_REAL;
g_disisprog_disable = true;
break; break;
case 's': case 's':
printstats = true; printstats = true;
@ -2024,7 +2042,6 @@ static void GetOpts(int argc, char *argv[]) {
strcpy(logpath, optarg); strcpy(logpath, optarg);
break; break;
case 'h': case 'h':
case '?':
PrintUsage(EXIT_SUCCESS, stdout); PrintUsage(EXIT_SUCCESS, stdout);
default: default:
PrintUsage(EX_USAGE, stderr); PrintUsage(EX_USAGE, stderr);

View File

@ -64,13 +64,12 @@ ssize_t WriteBuffer(struct Buffer *b, int fd) {
p = b->p; p = b->p;
n = b->i; n = b->i;
do { do {
TryAgain:
if ((rc = write(fd, p, n)) != -1) { if ((rc = write(fd, p, n)) != -1) {
wrote = rc; wrote = rc;
p += wrote; p += wrote;
n -= wrote; n -= wrote;
} else if (errno == EINTR) { } else if (errno == EINTR) {
goto TryAgain; break;
} else { } else {
return -1; return -1;
} }

View File

@ -17,10 +17,14 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA 02110-1301 USA
*/ */
#include "libc/assert.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/hefty/spawn.h" #include "libc/calls/hefty/spawn.h"
#include "libc/calls/struct/iovec.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "tool/build/lib/demangle.h" #include "tool/build/lib/demangle.h"
@ -37,12 +41,14 @@ void CloseCxxFilt(void) {
void SpawnCxxFilt(void) { void SpawnCxxFilt(void) {
int pid; int pid;
const char *cxxfilt;
char path[PATH_MAX]; char path[PATH_MAX];
if (commandv("c++filt", path)) { cxxfilt = firstnonnull(getenv("CXXFILT"), "c++filt");
if (commandv(cxxfilt, path)) {
g_cxxfilt.fds[0] = -1; g_cxxfilt.fds[0] = -1;
g_cxxfilt.fds[1] = -1; g_cxxfilt.fds[1] = -1;
g_cxxfilt.fds[2] = 2; g_cxxfilt.fds[2] = 2;
if ((pid = spawnve(0, g_cxxfilt.fds, path, (char *const[]){"c++filt", NULL}, if ((pid = spawnve(0, g_cxxfilt.fds, path, (char *const[]){cxxfilt, NULL},
environ)) != -1) { environ)) != -1) {
atexit(CloseCxxFilt); atexit(CloseCxxFilt);
} }
@ -52,30 +58,50 @@ void SpawnCxxFilt(void) {
g_cxxfilt.pid = pid; g_cxxfilt.pid = pid;
} }
char *DemangleCxxFilt(char *p, const char *symbol) { char *CopySymbol(char *p, size_t pn, const char *s, size_t sn) {
int n; size_t extra;
char buf[512]; bool showdots, iscomplicated;
bool iscomplicated; assert(pn >= 1 + 3 + 1 + 1);
iscomplicated = memchr(s, ' ', sn) || memchr(s, '(', sn);
extra = 1;
if (iscomplicated) extra += 2;
if (sn + extra > pn) {
sn = pn - extra - 3;
showdots = true;
} else {
showdots = false;
}
if (iscomplicated) *p++ = '"';
p = mempcpy(p, s, sn);
if (showdots) p = stpcpy(p, "...");
if (iscomplicated) *p++ = '"';
*p = '\0';
return p;
}
char *DemangleCxxFilt(char *p, size_t pn, const char *s, size_t sn) {
ssize_t rc;
size_t got;
struct iovec iov[2];
static char buf[PAGESIZE];
if (!g_cxxfilt.pid) SpawnCxxFilt(); if (!g_cxxfilt.pid) SpawnCxxFilt();
if (g_cxxfilt.pid == -1) return NULL; if (g_cxxfilt.pid == -1) return NULL;
if ((n = strlen(symbol)) >= ARRAYLEN(buf)) return NULL; buf[0] = '\n';
memcpy(buf, symbol, n); iov[0].iov_base = s;
buf[n] = '\n'; iov[0].iov_len = sn;
write(g_cxxfilt.fds[0], buf, n + 1); iov[1].iov_base = buf;
n = read(g_cxxfilt.fds[1], buf, ARRAYLEN(buf)); iov[1].iov_len = 1;
if (n > 1 && buf[n - 1] == '\n') { writev(g_cxxfilt.fds[0], iov, ARRAYLEN(iov));
if (buf[n - 2] == '\r') --n; if ((rc = read(g_cxxfilt.fds[1], buf, sizeof(buf))) != -1) {
--n; got = rc;
iscomplicated = memchr(buf, ' ', n) || memchr(buf, '(', n); if (got >= 2 && buf[got - 1] == '\n') {
if (iscomplicated) *p++ = '"'; if (buf[got - 2] == '\r') --got;
p = mempcpy(p, buf, n); --got;
if (iscomplicated) *p++ = '"'; return CopySymbol(p, pn, buf, got);
*p = '\0'; }
return p;
} else {
CloseCxxFilt();
return NULL;
} }
CloseCxxFilt();
return NULL;
} }
/** /**
@ -85,10 +111,12 @@ char *DemangleCxxFilt(char *p, const char *symbol) {
* x86_64 disassembler. That's just for the GNU encoding scheme. So * x86_64 disassembler. That's just for the GNU encoding scheme. So
* what we'll do, is just offload this work to the c++filt program. * what we'll do, is just offload this work to the c++filt program.
*/ */
char *Demangle(char *p, const char *symbol) { char *Demangle(char *p, const char *symbol, size_t n) {
char *r; char *r;
size_t sn;
sn = strlen(symbol);
if (startswith(symbol, "_Z")) { if (startswith(symbol, "_Z")) {
if ((r = DemangleCxxFilt(p, symbol))) return r; if ((r = DemangleCxxFilt(p, n, symbol, sn))) return r;
} }
return stpcpy(p, symbol); return CopySymbol(p, n, symbol, sn);
} }

View File

@ -3,7 +3,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
char *Demangle(char *, const char *); char *Demangle(char *, const char *, size_t);
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View File

@ -150,7 +150,7 @@ static char *DisLineData(struct Dis *d, char *p, const uint8_t *b, size_t n) {
static char *DisLabel(struct Dis *d, char *p, const char *name) { static char *DisLabel(struct Dis *d, char *p, const char *name) {
p = DisColumn(DisAddr(d, p), p, ADDRLEN); p = DisColumn(DisAddr(d, p), p, ADDRLEN);
p = HighStart(p, g_high.label); p = HighStart(p, g_high.label);
p = Demangle(p, name); p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
p = HighEnd(p); p = HighEnd(p);
*p++ = ':'; *p++ = ':';
*p = '\0'; *p = '\0';

View File

@ -6,6 +6,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#define DIS_MAX_SYMBOL_LENGTH 32
struct Dis { struct Dis {
struct DisOps { struct DisOps {
size_t i, n; size_t i, n;
@ -50,6 +52,8 @@ struct Dis {
char buf[512]; char buf[512];
}; };
extern bool g_disisprog_disable;
long Dis(struct Dis *, struct Machine *, uint64_t, uint64_t, int); long Dis(struct Dis *, struct Machine *, uint64_t, uint64_t, int);
long DisFind(struct Dis *, int64_t); long DisFind(struct Dis *, int64_t);
void DisFree(struct Dis *); void DisFree(struct Dis *);

View File

@ -143,21 +143,30 @@ static char *DisInt(char *p, int64_t x) {
return p; return p;
} }
static char *DisSym(struct Dis *d, char *p, int64_t addr, int64_t ip) { static char *DisSymImpl(struct Dis *d, char *p, int64_t x, long sym) {
long sym;
int64_t addend; int64_t addend;
const char *name; const char *name;
if ((sym = DisFindSym(d, ip)) != -1 && d->syms.p[sym].name) { addend = x - d->syms.p[sym].addr;
addend = ip - d->syms.p[sym].addr; name = d->syms.stab + d->syms.p[sym].name;
name = d->syms.stab + d->syms.p[sym].name; p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
p = Demangle(p, name); if (addend) {
if (addend) { *p++ = '+';
*p++ = '+'; p = DisInt(p, addend);
p = DisInt(p, addend); }
} return p;
return p; }
static char *DisSym(struct Dis *d, char *p, int64_t x1, int64_t x2,
bool isrelative) {
long sym;
if ((sym = DisFindSym(d, x2)) != -1 && d->syms.p[sym].name &&
(d->syms.p[sym].isabs ^ isrelative)) {
return DisSymImpl(d, p, x2, sym);
} else if ((sym = DisFindSym(d, x1)) != -1 && d->syms.p[sym].name &&
(d->syms.p[sym].isabs ^ isrelative)) {
return DisSymImpl(d, p, x1, sym);
} else { } else {
return DisInt(p, addr); return DisInt(p, x1);
} }
} }
@ -165,7 +174,7 @@ static char *DisSymLiteral(struct Dis *d, uint32_t rde, char *p, uint64_t addr,
uint64_t ip) { uint64_t ip) {
*p++ = '$'; *p++ = '$';
p = HighStart(p, g_high.literal); p = HighStart(p, g_high.literal);
p = DisSym(d, p, addr, ip); p = DisSym(d, p, addr, addr, false);
p = HighEnd(p); p = HighEnd(p);
return p; return p;
} }
@ -196,27 +205,36 @@ static bool IsRealModrmAbsolute(uint32_t rde) {
return Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde); return Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde);
} }
static char *DisM(struct Dis *d, uint32_t rde, char *p) { static char *DisDisp(struct Dis *d, uint32_t rde, char *p) {
bool rela;
int64_t disp; int64_t disp;
const char *base, *index, *scale;
p = DisSego(d, rde, p);
base = index = scale = NULL;
if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) || if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) ||
IsRealModrmAbsolute(rde) || IsRealModrmAbsolute(rde) ||
(ModrmMod(rde) == 0b00 && ModrmRm(rde) == 0b100 && (Eamode(rde) != XED_MODE_REAL && ModrmMod(rde) == 0b00 &&
SibBase(d->xedd) == 0b101)) { ModrmRm(rde) == 0b100 && SibBase(d->xedd) == 0b101)) {
disp = d->xedd->op.disp; disp = d->xedd->op.disp;
if (IsRipRelative(rde)) { if (IsRipRelative(rde)) {
if (Mode(rde) == XED_MODE_LONG) { if (Mode(rde) == XED_MODE_LONG) {
disp = RipRelative(d, disp); disp = RipRelative(d, disp);
rela = true;
} else { } else {
disp = Unrelative(rde, disp); disp = Unrelative(rde, disp);
rela = false;
} }
} else if (IsRealModrmAbsolute(rde)) { } else if (IsRealModrmAbsolute(rde)) {
disp = Unrelative(rde, disp); disp = Unrelative(rde, disp);
rela = false;
} else {
rela = true;
} }
p = DisSym(d, p, disp, disp); p = DisSym(d, p, disp, disp, rela);
} }
return p;
}
static char *DisBis(struct Dis *d, uint32_t rde, char *p) {
const char *base, *index, *scale;
base = index = scale = NULL;
if (Eamode(rde) != XED_MODE_REAL) { if (Eamode(rde) != XED_MODE_REAL) {
if (!SibExists(rde)) { if (!SibExists(rde)) {
DCHECK(!d->xedd->op.has_sib); DCHECK(!d->xedd->op.has_sib);
@ -290,6 +308,13 @@ static char *DisM(struct Dis *d, uint32_t rde, char *p) {
return p; return p;
} }
static char *DisM(struct Dis *d, uint32_t rde, char *p) {
p = DisSego(d, rde, p);
p = DisDisp(d, rde, p);
p = DisBis(d, rde, p);
return p;
}
static char *DisRegMem(struct Dis *d, uint32_t rde, char *p, static char *DisRegMem(struct Dis *d, uint32_t rde, char *p,
char *f(struct Dis *, uint32_t, char *)) { char *f(struct Dis *, uint32_t, char *)) {
if (IsModrmRegister(rde)) { if (IsModrmRegister(rde)) {
@ -452,11 +477,11 @@ static char *DisJb(struct Dis *d, uint32_t rde, char *p) {
static char *DisJvds(struct Dis *d, uint32_t rde, char *p) { static char *DisJvds(struct Dis *d, uint32_t rde, char *p) {
return DisSym(d, p, RipRelative(d, d->xedd->op.disp), return DisSym(d, p, RipRelative(d, d->xedd->op.disp),
RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs)); RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs), true);
} }
static char *DisAbs(struct Dis *d, uint32_t rde, char *p) { static char *DisAbs(struct Dis *d, uint32_t rde, char *p) {
return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp); return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp, false);
} }
static char *DisSw(struct Dis *d, uint32_t rde, char *p) { static char *DisSw(struct Dis *d, uint32_t rde, char *p) {
@ -477,12 +502,12 @@ static char *DisY(struct Dis *d, uint32_t rde, char *p) {
} }
static char *DisX(struct Dis *d, uint32_t rde, char *p) { static char *DisX(struct Dis *d, uint32_t rde, char *p) {
DisSego(d, rde, p); p = DisSego(d, rde, p);
return DisSpecialAddr(d, rde, p, 6); // ds:si return DisSpecialAddr(d, rde, p, 6); // ds:si
} }
static char *DisBBb(struct Dis *d, uint32_t rde, char *p) { static char *DisBBb(struct Dis *d, uint32_t rde, char *p) {
DisSego(d, rde, p); p = DisSego(d, rde, p);
return DisSpecialAddr(d, rde, p, 3); // ds:bx return DisSpecialAddr(d, rde, p, 3); // ds:bx
} }

View File

@ -22,9 +22,13 @@
#include "libc/elf/elf.h" #include "libc/elf/elf.h"
#include "libc/elf/struct/sym.h" #include "libc/elf/struct/sym.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/str/str.h"
#include "tool/build/lib/dis.h" #include "tool/build/lib/dis.h"
bool g_disisprog_disable;
static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) { static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) {
if (a->addr != b->addr) { if (a->addr != b->addr) {
if (a->addr < b->addr) return -1; if (a->addr < b->addr) return -1;
@ -72,6 +76,8 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
if (!st[i].st_name) continue; if (!st[i].st_name) continue;
if (!(0 <= st[i].st_name && st[i].st_name < stablen)) continue; if (!(0 <= st[i].st_name && st[i].st_name < stablen)) continue;
if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION) continue; if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION) continue;
if (ELF64_ST_TYPE(st[i].st_info) == STT_FILE) continue;
if (startswith(d->syms.stab + st[i].st_name, "v_")) continue;
isabs = st[i].st_shndx == SHN_ABS; isabs = st[i].st_shndx == SHN_ABS;
isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK; isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK;
islocal = ELF64_ST_BIND(st[i].st_info) == STB_LOCAL; islocal = ELF64_ST_BIND(st[i].st_info) == STB_LOCAL;
@ -85,6 +91,7 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
t.addr = st[i].st_value; t.addr = st[i].st_value;
t.rank = -islocal + -isweak + -isabs + isprotected + isobject + isfunc; t.rank = -islocal + -isweak + -isabs + isprotected + isobject + isfunc;
t.iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc; t.iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc;
t.isabs = isabs;
APPEND(&d->syms.p, &d->syms.i, &d->syms.n, &t); APPEND(&d->syms.p, &d->syms.i, &d->syms.n, &t);
} }
} }
@ -93,6 +100,7 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
bool DisIsProg(struct Dis *d, int64_t addr) { bool DisIsProg(struct Dis *d, int64_t addr) {
long i; long i;
if (g_disisprog_disable) return true;
for (i = 0; i < d->loads.i; ++i) { for (i = 0; i < d->loads.i; ++i) {
if (addr >= d->loads.p[i].addr && if (addr >= d->loads.p[i].addr &&
addr < d->loads.p[i].addr + d->loads.p[i].size) { addr < d->loads.p[i].addr + d->loads.p[i].size) {

View File

@ -20,6 +20,7 @@
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/nexgen32e/tinystrcmp.h" #include "libc/nexgen32e/tinystrcmp.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/zlib/zlib.h"
#include "tool/build/lib/dis.h" #include "tool/build/lib/dis.h"
#include "tool/build/lib/high.h" #include "tool/build/lib/high.h"
#include "tool/build/lib/modrm.h" #include "tool/build/lib/modrm.h"
@ -163,7 +164,7 @@ static char *DisName(struct Dis *d, char *bp, const char *name,
} else if (wantsuffix || (ambiguous && !startswith(name, "f") && } else if (wantsuffix || (ambiguous && !startswith(name, "f") &&
!startswith(name, "set"))) { !startswith(name, "set"))) {
if (Osz(rde)) { if (Osz(rde)) {
if (Mode(rde) != XED_MODE_REAL) { if (ambiguous || Mode(rde) != XED_MODE_REAL) {
*p++ = 'w'; *p++ = 'w';
} }
} else if (Rexw(rde)) { } else if (Rexw(rde)) {
@ -187,8 +188,8 @@ static char *DisName(struct Dis *d, char *bp, const char *name,
*/ */
char *DisInst(struct Dis *d, char *p, const char *spec) { char *DisInst(struct Dis *d, char *p, const char *spec) {
long i, n; long i, n;
char sbuf[300]; char sbuf[64];
char args[4][300]; char args[4][256];
char *s, *name, *state; char *s, *name, *state;
bool hasarg, hasmodrm, hasregister, hasmemory; bool hasarg, hasmodrm, hasregister, hasmemory;
CHECK_EQ(0, (int)d->xedd->op.error); CHECK_EQ(0, (int)d->xedd->op.error);

View File

@ -48,7 +48,7 @@ COSMOPOLITAN_C_START_
#define SibHasIndex(x) (SibIndex(x) != 4 || Rexx(x)) #define SibHasIndex(x) (SibIndex(x) != 4 || Rexx(x))
#define SibHasBase(x, r) (SibBase(x) != 5 || ModrmMod(r)) #define SibHasBase(x, r) (SibBase(x) != 5 || ModrmMod(r))
#define SibIsAbsolute(x, r) (!SibHasBase(x, r) && !SibHasIndex(x)) #define SibIsAbsolute(x, r) (!SibHasBase(x, r) && !SibHasIndex(x))
#define IsRipRelative(x) (ModrmRm(x) == 5 && !ModrmMod(x)) #define IsRipRelative(x) (Eamode(x) && ModrmRm(x) == 5 && !ModrmMod(x))
struct AddrSeg { struct AddrSeg {
int64_t addr; int64_t addr;

View File

@ -474,11 +474,8 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) {
break; break;
case kMachinePtyUtf8: case kMachinePtyUtf8:
if (ThomPikeCont(p[i])) { if (ThomPikeCont(p[i])) {
pty->u8 <<= 6; pty->u8 = ThomPikeMerge(pty->u8, p[i]);
pty->u8 |= p[i] & 0b00111111; if (--pty->n8) break;
if (--pty->n8) {
break;
}
} }
SetMachinePtyCell(pty, pty->u8); SetMachinePtyCell(pty, pty->u8);
pty->state = kMachinePtyAscii; pty->state = kMachinePtyAscii;

Some files were not shown because too many files have changed in this diff Show More