Add fixes performance and static web server
parent
b6793d42d5
commit
c45e46f871
|
@ -45,6 +45,7 @@ COUNTERMAND=
|
|||
|
||||
# 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.
|
||||
NOPG=0
|
||||
FIRST=1
|
||||
OUTARG=0
|
||||
INVISIBLE=0
|
||||
|
@ -65,13 +66,16 @@ for x; do
|
|||
-w)
|
||||
set -- "$@" "$x" -D__W__
|
||||
;;
|
||||
-x-no-pg)
|
||||
NOPG=1
|
||||
;;
|
||||
-pg)
|
||||
if [ $INVISIBLE -eq 0 ]; then
|
||||
if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
|
||||
set -- "$@" "$x" -D__PG__ # @see libc/macros.h
|
||||
fi
|
||||
;;
|
||||
-mfentry)
|
||||
if [ $INVISIBLE -eq 0 ]; then
|
||||
if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
|
||||
set -- "$@" "$x" -D__MFENTRY__
|
||||
fi
|
||||
;;
|
||||
|
|
|
@ -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
|
||||
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
|
||||
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
|
||||
AR = o/third_party/gcc/bin/x86_64-linux-musl-ar
|
||||
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 = \
|
||||
$(GCC) \
|
||||
-x-no-pg \
|
||||
$(OBJECTIFY.c.flags) \
|
||||
-wrapper build/realify.sh \
|
||||
-D__REAL_MODE__ \
|
||||
|
@ -330,7 +332,12 @@ OBJECTIFY.real.c = \
|
|||
-ffixed-r13 \
|
||||
-ffixed-r14 \
|
||||
-ffixed-r15 \
|
||||
-mno-red-zone \
|
||||
-fcall-used-rbx \
|
||||
-fno-jump-tables \
|
||||
-fno-shrink-wrap \
|
||||
-fno-schedule-insns2 \
|
||||
-flive-range-shrinkage \
|
||||
-fno-omit-frame-pointer \
|
||||
-momit-leaf-frame-pointer \
|
||||
-mpreferred-stack-boundary=3 \
|
||||
|
|
|
@ -15,17 +15,6 @@
|
|||
# remove comments
|
||||
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/call\(q\|\)/callw/
|
||||
#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/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
|
||||
|
||||
# 32-bitify
|
||||
|
|
|
@ -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)/%.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/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)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
|
||||
o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $<
|
||||
|
|
|
@ -49,6 +49,7 @@ EXAMPLES_DIRECTDEPS = \
|
|||
LIBC_CONV \
|
||||
LIBC_FMT \
|
||||
LIBC_LOG \
|
||||
LIBC_LOG_ASAN \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_KERNELBASE \
|
||||
|
|
|
@ -55,7 +55,7 @@ int main(int argc, char *argv[], char **envp) {
|
|||
unsigned long val;
|
||||
char fmt[64], **env;
|
||||
printf("\nArguments:\n");
|
||||
for (i = 0; i < argc; ++i) {
|
||||
for (i = 0; i < g_argc; ++i) {
|
||||
printf(" ☼ %s\n", argv[i]);
|
||||
}
|
||||
printf("\nEnvironment:\n");
|
||||
|
|
|
@ -71,27 +71,25 @@ static size_t KnuthMorrisPratt(m, T, W, n, S)
|
|||
* @param needlelen is its character count
|
||||
* @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) {
|
||||
long *T;
|
||||
size_t i;
|
||||
const char *haystack, *needle, *h;
|
||||
haystack = haystack_;
|
||||
needle = needle_;
|
||||
needle = needlep;
|
||||
haystack = haystackp;
|
||||
if (needlelen > haystacklen) return NULL;
|
||||
if (!needlelen) return (/*unconst*/ void *)haystack;
|
||||
if (!needlelen) return haystack;
|
||||
h = memchr(haystack, *needle, haystacklen);
|
||||
if (!h || needlelen == 1) return (/*unconst*/ void *)h;
|
||||
if (!h || needlelen == 1) return h;
|
||||
haystacklen -= h - haystack;
|
||||
long stacktmp[16];
|
||||
void *freeme = NULL;
|
||||
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);
|
||||
if (needlelen < haystacklen && memcmp(h, needle, needlelen) == 0) {
|
||||
return h;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,7 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ int clock_gettime(int clockid, struct timespec *out_ts) {
|
|||
} else {
|
||||
struct NtFileTime ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
*out_ts = filetimetotimespec(ft);
|
||||
*out_ts = FileTimeToTimeSpec(ft);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,9 +49,9 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) {
|
|||
: (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) |
|
||||
((filetype == kNtFileTypeChar) ? S_IFCHR : 0) |
|
||||
((filetype == kNtFileTypePipe) ? S_IFIFO : 0))));
|
||||
st->st_atim = filetimetotimespec(wst.ftLastAccessFileTime);
|
||||
st->st_mtim = filetimetotimespec(wst.ftLastWriteFileTime);
|
||||
st->st_ctim = filetimetotimespec(wst.ftCreationFileTime);
|
||||
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
|
||||
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
|
||||
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
|
||||
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
|
||||
st->st_blksize = PAGESIZE;
|
||||
st->st_dev = wst.dwVolumeSerialNumber;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
static int g_pid;
|
||||
|
||||
static void __updatepid(void) {
|
||||
atfork(__updatepid, NULL);
|
||||
g_pid = __getpid();
|
||||
}
|
||||
|
||||
|
@ -47,8 +46,9 @@ int __getpid(void) {
|
|||
int getpid(void) {
|
||||
static bool once;
|
||||
if (!once) {
|
||||
once = true;
|
||||
__updatepid();
|
||||
atfork(__updatepid, NULL);
|
||||
once = true;
|
||||
}
|
||||
return g_pid;
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ textwindows int getrusage$nt(int who, struct rusage *usage) {
|
|||
if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
|
||||
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
|
||||
&CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) {
|
||||
filetimetotimeval(&usage->ru_utime, UserFileTime);
|
||||
filetimetotimeval(&usage->ru_stime, KernelFileTime);
|
||||
FileTimeToTimeVal(&usage->ru_utime, UserFileTime);
|
||||
FileTimeToTimeVal(&usage->ru_stime, KernelFileTime);
|
||||
return 0;
|
||||
} else {
|
||||
return winerr();
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
int gettimeofday$nt(struct timeval *tv, struct timezone *tz) {
|
||||
struct NtFileTime ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
filetimetotimeval(tv, ft);
|
||||
FileTimeToTimeVal(tv, ft);
|
||||
if (tz) memset(tz, 0, sizeof(*tz));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
* @param mode is an octal user/group/other permission signifier, that's
|
||||
* ignored if O_CREAT or O_TMPFILE weren't passed
|
||||
* @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, ...) {
|
||||
va_list va;
|
||||
|
|
|
@ -155,7 +155,7 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
|
|||
ap->sa_restorer = &__restore_rt;
|
||||
}
|
||||
}
|
||||
if (rva >= 0) {
|
||||
if (rva >= kSigactionMinRva) {
|
||||
ap->sa_sigaction = (sigaction_f)__sigenter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ textwindows int utimensat$nt(int dirfd, const char *path,
|
|||
} else if (ts[i].tv_nsec == UTIME_OMIT) {
|
||||
ftp[i] = NULL;
|
||||
} else {
|
||||
ft[i] = timespectofiletime(ts[i]);
|
||||
ft[i] = TimeSpecToFileTime(ts[i]);
|
||||
ftp[i] = &ft[i];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
GetProcessTimes(GetCurrentProcess(), &createfiletime, &exitfiletime,
|
||||
&kernelfiletime, &userfiletime);
|
||||
filetimetotimeval(&opt_out_rusage->ru_utime, userfiletime);
|
||||
filetimetotimeval(&opt_out_rusage->ru_stime, kernelfiletime);
|
||||
FileTimeToTimeVal(&opt_out_rusage->ru_utime, userfiletime);
|
||||
FileTimeToTimeVal(&opt_out_rusage->ru_stime, kernelfiletime);
|
||||
}
|
||||
return pid;
|
||||
} else if (options & WNOHANG) {
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
* of the UNIX operation system signalled the end of modernity. Windows
|
||||
* timestamps are living proof.
|
||||
*/
|
||||
#define MODERNITYSECONDS 11644473600
|
||||
#define HECTONANOSECONDS 10000000
|
||||
#define MODERNITYSECONDS 11644473600ull
|
||||
#define HECTONANOSECONDS 10000000ull
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
@ -46,12 +46,13 @@ long strtol(const char *, char **, int)
|
|||
│ cosmopolitan § conversion » time ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
struct timespec filetimetotimespec(struct NtFileTime);
|
||||
struct NtFileTime timespectofiletime(struct timespec);
|
||||
struct NtFileTime timetofiletime(int64_t) nothrow pureconst;
|
||||
int64_t DosDateTimeToUnix(unsigned, unsigned);
|
||||
struct timespec FileTimeToTimeSpec(struct NtFileTime);
|
||||
struct NtFileTime TimeSpecToFileTime(struct timespec);
|
||||
struct NtFileTime TimeToFileTime(int64_t) nothrow pureconst;
|
||||
int64_t filetimetotime(struct NtFileTime) nothrow pureconst;
|
||||
void filetimetotimeval(struct timeval *, struct NtFileTime) nothrow;
|
||||
struct NtFileTime timevaltofiletime(const struct timeval *) nosideeffect;
|
||||
void FileTimeToTimeVal(struct timeval *, struct NtFileTime) nothrow;
|
||||
struct NtFileTime TimeValToFileTime(const struct timeval *) nosideeffect;
|
||||
long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect;
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
|
|
|
@ -52,6 +52,7 @@ $(LIBC_CONV_A).pkg: \
|
|||
$(LIBC_CONV_A_OBJS) \
|
||||
$(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/timetofiletime.o \
|
||||
o/$(MODE)/libc/conv/filetimetotime.o \
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -24,11 +24,11 @@
|
|||
/**
|
||||
* Converts Windows COBOL timestamp to UNIX epoch in nanoseconds.
|
||||
*/
|
||||
struct timespec filetimetotimespec(struct NtFileTime ft) {
|
||||
struct timespec FileTimeToTimeSpec(struct NtFileTime ft) {
|
||||
uint64_t x;
|
||||
x = ft.dwHighDateTime;
|
||||
x <<= 32;
|
||||
x |= ft.dwLowDateTime;
|
||||
x -= MODERNITYSECONDS;
|
||||
return (struct timespec){x / HECTONANOSECONDS, x % HECTONANOSECONDS * 100};
|
||||
return (struct timespec){x / HECTONANOSECONDS - MODERNITYSECONDS,
|
||||
x % HECTONANOSECONDS * 100};
|
||||
}
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
#include "libc/conv/conv.h"
|
||||
#include "libc/nt/struct/filetime.h"
|
||||
|
||||
void filetimetotimeval(struct timeval *tv, struct NtFileTime ft) {
|
||||
uint64_t t = (uint64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
|
||||
uint64_t x = t - MODERNITYSECONDS * HECTONANOSECONDS;
|
||||
tv->tv_sec = x / HECTONANOSECONDS;
|
||||
void FileTimeToTimeVal(struct timeval *tv, struct NtFileTime ft) {
|
||||
uint64_t x;
|
||||
x = ft.dwHighDateTime;
|
||||
x <<= 32;
|
||||
x |= ft.dwLowDateTime;
|
||||
tv->tv_sec = x / HECTONANOSECONDS - MODERNITYSECONDS;
|
||||
tv->tv_usec = x % HECTONANOSECONDS / 10;
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
};
|
|
@ -25,7 +25,7 @@
|
|||
/**
|
||||
* Converts UNIX nanosecond timestamp to Windows COBOL timestamp.
|
||||
*/
|
||||
struct NtFileTime timespectofiletime(struct timespec ts) {
|
||||
struct NtFileTime TimeSpecToFileTime(struct timespec ts) {
|
||||
uint64_t x;
|
||||
x = MODERNITYSECONDS;
|
||||
x += ts.tv_sec * HECTONANOSECONDS;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "libc/conv/conv.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;
|
||||
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "libc/nt/struct/filetime.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 +
|
||||
MODERNITYSECONDS * HECTONANOSECONDS;
|
||||
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_DOS_H_
|
||||
#define COSMOPOLITAN_LIBC_DOS_H_
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* @fileoverview MS-DOS Data Structures.
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -29,6 +29,8 @@ char *strerror(int) returnsnonnull nothrow nocallback;
|
|||
int strerror_r(int, char *, size_t) nothrow nocallback;
|
||||
int palandprintf(void *, void *, const char *, va_list) hidden;
|
||||
char *itoa(int, char *, int) compatfn;
|
||||
char *fcvt(double, int, int *, int *);
|
||||
char *ecvt(double, int, int *, int *);
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § string formatting » optimizations ─╬─│┼
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
* @see xdtoa() for higher precision at the cost of bloat
|
||||
* @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) {
|
||||
long whole, frac;
|
||||
long double tmp, diff;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
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,
|
||||
unsigned width, unsigned char flags) {
|
||||
unsigned i, idx;
|
||||
|
@ -103,7 +103,7 @@ static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len,
|
|||
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,
|
||||
const char *alphabet) {
|
||||
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);
|
||||
}
|
||||
|
||||
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 char flags, const char *lang) {
|
||||
bool neg;
|
||||
|
|
|
@ -75,7 +75,7 @@ static int ppatoi(const char **str) {
|
|||
* - `%Lf` long double
|
||||
* - `%p` pointer (48-bit hexadecimal)
|
||||
*
|
||||
* Length Modifiers
|
||||
* Size Modifiers
|
||||
*
|
||||
* - `%hhd` char (8-bit)
|
||||
* - `%hd` short (16-bit)
|
||||
|
@ -89,7 +89,11 @@ static int ppatoi(const char **str) {
|
|||
* - `%08d` fixed columns w/ zero leftpadding
|
||||
* - `%8d` fixed columns w/ space leftpadding
|
||||
* - `%*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
|
||||
*
|
||||
|
@ -112,9 +116,9 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
|
|||
long double ldbl;
|
||||
wchar_t charbuf[3];
|
||||
const char *alphabet;
|
||||
int (*out)(int, void *);
|
||||
int (*out)(long, void *);
|
||||
unsigned char signbit, log2base;
|
||||
int w, rc, flags, width, lasterr, precision;
|
||||
int w, flags, width, lasterr, precision;
|
||||
|
||||
lasterr = errno;
|
||||
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': {
|
||||
flags &= ~FLAGS_HASH; /* no hash for dec format */
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -269,7 +274,8 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
|
|||
} else {
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
@ -297,8 +303,10 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
|
|||
case 's':
|
||||
p = va_arg(va, void *);
|
||||
showstr:
|
||||
rc = weaken(stoa)(out, arg, p, flags, precision, width, signbit, qchar);
|
||||
if (rc == -1) return -1;
|
||||
if (!weaken(stoa) || weaken(stoa)(out, arg, p, flags, precision, width,
|
||||
signbit, qchar) == -1) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case '%':
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int spacepad(int(int, void *), void *, unsigned long) hidden;
|
||||
int ftoa(int(int, void *), void *, long double, int, unsigned long,
|
||||
int spacepad(int(long, void *), void *, unsigned long) hidden;
|
||||
int ftoa(int(long, void *), void *, long double, int, unsigned long,
|
||||
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;
|
||||
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;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#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;
|
||||
for (rc = i = 0; i < n; ++i) rc |= out(' ', arg);
|
||||
return rc;
|
||||
|
|
194
libc/fmt/stoa.c
194
libc/fmt/stoa.c
|
@ -17,47 +17,54 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/safemacros.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/escape/escape.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/fmt/paland.inc"
|
||||
#include "libc/fmt/palandprintf.h"
|
||||
#include "libc/nexgen32e/tinystrlen.h"
|
||||
#include "libc/str/internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/tpdecode.h"
|
||||
#include "libc/str/tpencode.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/tpenc.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/unicode/unicode.h"
|
||||
|
||||
forceinline unsigned long tpiencode(wint_t wc) {
|
||||
char buf[8];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
tpencode(buf, sizeof(buf), wc, false);
|
||||
return read64le(buf);
|
||||
typedef int (*emit_f)(int (*)(long, void *), void *, wint_t);
|
||||
|
||||
static int StoaEmitByte(int f(long, void *), void *a, wint_t c) {
|
||||
return f(c, a);
|
||||
}
|
||||
|
||||
forceinline int emitwc(int out(int, void *), void *arg, unsigned flags,
|
||||
wint_t wc) {
|
||||
unsigned long pending;
|
||||
if (flags & FLAGS_QUOTE) {
|
||||
if (wc > 127) {
|
||||
pending = tpiencode(wc);
|
||||
} else {
|
||||
pending = cescapec(wc);
|
||||
}
|
||||
} else {
|
||||
pending = tpiencode(wc);
|
||||
}
|
||||
static int StoaEmitWordEncodedString(int f(long, void *), void *a, uint64_t w) {
|
||||
do {
|
||||
if (out(pending & 0xff, arg) == -1) return -1;
|
||||
} while ((pending >>= 8));
|
||||
if (f(w & 0xff, a) == -1) {
|
||||
return -1;
|
||||
}
|
||||
} while ((w >>= 8));
|
||||
return 0;
|
||||
}
|
||||
|
||||
forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
|
||||
char ch, unsigned char signbit) {
|
||||
static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) {
|
||||
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 (signbit == 63) {
|
||||
if (out('L', arg) == -1) return -1;
|
||||
|
@ -78,13 +85,15 @@ forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
|
|||
*
|
||||
* @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 char qchar) {
|
||||
char *p;
|
||||
wint_t wc;
|
||||
unsigned w, c;
|
||||
bool ignorenul;
|
||||
unsigned n;
|
||||
emit_f emit;
|
||||
bool justdobytes;
|
||||
unsigned w, c, pad;
|
||||
|
||||
p = data;
|
||||
if (!p) {
|
||||
|
@ -93,89 +102,102 @@ int stoa(int out(int, void *), void *arg, void *data, unsigned long flags,
|
|||
flags |= FLAGS_NOQUOTE;
|
||||
signbit = 0;
|
||||
} 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) {
|
||||
w = precision;
|
||||
if (signbit == 63) {
|
||||
if (weaken(wcsnwidth)) {
|
||||
w = weaken(wcsnwidth)((const wchar_t *)p, w);
|
||||
} else {
|
||||
w = tinywcsnlen((const wchar_t *)p, w);
|
||||
w = weaken(wcsnwidth)((const wchar_t *)p, precision);
|
||||
}
|
||||
} else if (signbit == 15) {
|
||||
if (weaken(strnwidth16)) {
|
||||
w = weaken(strnwidth16)((const char16_t *)p, w);
|
||||
} else {
|
||||
w = tinystrnlen16((const char16_t *)p, w);
|
||||
w = weaken(strnwidth16)((const char16_t *)p, precision);
|
||||
}
|
||||
} else if (weaken(strnwidth)) {
|
||||
w = weaken(strnwidth)(p, w);
|
||||
} else {
|
||||
w = strnlen(p, w);
|
||||
w = weaken(strnwidth)(p, precision);
|
||||
}
|
||||
if (w < width) {
|
||||
pad = width - w;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & FLAGS_PRECISION) {
|
||||
w = MIN(w, precision);
|
||||
if (pad && !(flags & FLAGS_LEFT)) {
|
||||
if (spacepad(out, arg, pad) == -1) return -1;
|
||||
}
|
||||
|
||||
if (w < width && !(flags & FLAGS_LEFT)) {
|
||||
if (spacepad(out, arg, width - w) == -1) return -1;
|
||||
justdobytes = false;
|
||||
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));
|
||||
for (; !(flags & FLAGS_PRECISION) || precision; --precision) {
|
||||
if (signbit == 15) {
|
||||
if ((wc = *(const char16_t *)p) || ignorenul) {
|
||||
if ((1 <= wc && wc <= 0xD7FF)) {
|
||||
if (justdobytes) {
|
||||
while (precision--) {
|
||||
wc = *p++ & 0xff;
|
||||
if (emit(out, arg, wc) == -1) return -1;
|
||||
}
|
||||
} else {
|
||||
while (precision--) {
|
||||
if (signbit == 15) {
|
||||
wc = *(const char16_t *)p;
|
||||
if (IsUcs2(wc)) {
|
||||
p += sizeof(char16_t);
|
||||
} else if ((wc & UTF16_MASK) == UTF16_CONT) {
|
||||
} else if (IsUtf16Cont(wc)) {
|
||||
p += sizeof(char16_t);
|
||||
continue;
|
||||
} else if (!precision) {
|
||||
break;
|
||||
} else {
|
||||
char16_t buf[4] = {wc};
|
||||
if (!(flags & FLAGS_PRECISION) || precision > 1) {
|
||||
buf[1] = ((const char16_t *)p)[1];
|
||||
--precision;
|
||||
wc = MergeUtf16(wc, *(const char16_t *)p);
|
||||
}
|
||||
} 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 (spacepad(out, arg, width - w) == -1) return -1;
|
||||
if (pad && (flags & FLAGS_LEFT)) {
|
||||
if (spacepad(out, arg, pad) == -1) return -1;
|
||||
}
|
||||
|
||||
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {
|
||||
|
|
|
@ -25,17 +25,13 @@
|
|||
|
||||
struct SprintfStr {
|
||||
char *p;
|
||||
size_t i, n;
|
||||
size_t i;
|
||||
size_t n;
|
||||
};
|
||||
|
||||
static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
|
||||
if (str->i < str->n) {
|
||||
if (str->p) str->p[str->i] = c;
|
||||
str->i++;
|
||||
} else {
|
||||
if (!IsTrustworthy() && str->i >= INT_MAX) abort();
|
||||
str->i++;
|
||||
}
|
||||
if (str->i < str->n) str->p[str->i] = c;
|
||||
str->i++;
|
||||
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,
|
||||
* if the output buffer wasn't passed, or was too short, then the
|
||||
* number of characters that *would* have been written is returned
|
||||
* @throw EOVERFLOW when a formatted field exceeds its limit, which can
|
||||
* be checked by setting errno to 0 before calling
|
||||
* @see palandprintf() and printf() for detailed documentation
|
||||
*/
|
||||
int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) {
|
||||
struct SprintfStr str = {buf, 0, size};
|
||||
palandprintf(vsnprintfputchar, &str, fmt, va);
|
||||
if (str.p && str.n) {
|
||||
str.p[min(str.i, str.n - 1)] = '\0';
|
||||
}
|
||||
if (str.n) str.p[min(str.i, str.n - 1)] = '\0';
|
||||
return str.i;
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
#endif
|
||||
|
||||
#define BIGPAGESIZE 0x200000
|
||||
#define STACKSIZE 0x80000 /* todo: zlib's fault? */
|
||||
#define STACKSIZE 0x10000
|
||||
#define FRAMESIZE 0x10000 /* 8086 */
|
||||
#define PAGESIZE 0x1000 /* i386+ */
|
||||
#define BUFSIZ 0x1000 /* best stdio default */
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
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_CHECKS = $(LIBC_LINUX_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include "libc/sysv/consts/prot.h"
|
||||
#include "third_party/dlmalloc/dlmalloc.h"
|
||||
|
||||
STATIC_YOINK("_init_asan");
|
||||
|
||||
/**
|
||||
* @fileoverview Cosmopolitan Address Sanitizer Runtime.
|
||||
*
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
|
||||
STATIC_YOINK("ntoa");
|
||||
STATIC_YOINK("stoa");
|
||||
STATIC_YOINK("ftoa");
|
||||
|
||||
/**
|
||||
* 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, "?");
|
||||
strtoupper(sufbuf);
|
||||
|
||||
fprintf(stderr,
|
||||
"check failed\n"
|
||||
"\tCHECK_%s(%s, %s);\n"
|
||||
"\t\t → %#lx (%s)\n"
|
||||
"\t\t%s %#lx (%s)\n",
|
||||
sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr);
|
||||
(fprintf)(stderr,
|
||||
"check failed\n"
|
||||
"\tCHECK_%s(%s, %s);\n"
|
||||
"\t\t → %#lx (%s)\n"
|
||||
"\t\t%s %#lx (%s)\n",
|
||||
sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr);
|
||||
|
||||
if (!isempty(fmt)) {
|
||||
fputc('\t', stderr);
|
||||
va_start(va, fmt);
|
||||
vfprintf(stderr, fmt, va);
|
||||
(vfprintf)(stderr, fmt, va);
|
||||
va_end(va);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
|
||||
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
|
||||
(fprintf)(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
|
||||
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "\n");
|
||||
(fprintf)(stderr, "\n");
|
||||
fflush(stderr);
|
||||
PrintMemoryIntervals(fileno(stderr), &_mmi);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* Log level for compile-time DCE.
|
||||
*/
|
||||
#ifndef LOGGABLELEVEL
|
||||
#ifndef NDEBUG
|
||||
#ifndef TINY
|
||||
#define LOGGABLELEVEL kLogDebug
|
||||
/* #elif IsTiny() */
|
||||
/* #define LOGGABLELEVEL kLogInfo */
|
||||
|
|
|
@ -65,6 +65,11 @@ $(LIBC_LOG_A_OBJS): \
|
|||
$(NO_MAGIC) \
|
||||
-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_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_LOG_HDRS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_HDRS))
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
|
||||
#define kNontrivialSize (8 * 1000 * 1000)
|
||||
|
||||
STATIC_YOINK("ntoa");
|
||||
STATIC_YOINK("stoa");
|
||||
STATIC_YOINK("ftoa");
|
||||
|
||||
static int loglevel2char(unsigned level) {
|
||||
switch (level) {
|
||||
case kLogInfo:
|
||||
|
@ -47,6 +51,8 @@ static int loglevel2char(unsigned level) {
|
|||
return 'W';
|
||||
case kLogFatal:
|
||||
return 'F';
|
||||
case kLogVerbose:
|
||||
return 'V';
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
|
@ -80,8 +86,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
long double t2;
|
||||
const char *prog;
|
||||
int64_t secs, nsec, dots;
|
||||
char zonebuf[8], *zonebufp;
|
||||
char timebuf[24], *timebufp;
|
||||
char timebuf[32], *timebufp;
|
||||
if (!f) f = g_logfile;
|
||||
if (fileno(f) == -1) return;
|
||||
t2 = nowl();
|
||||
|
@ -89,22 +94,19 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
nsec = rem1000000000int64(t2 * 1e9L);
|
||||
if (secs > ts.tv_sec) {
|
||||
localtime_r(&secs, &tm);
|
||||
strftime(timebuf, sizeof(timebuf), "%Y%m%dT%H%M%S", &tm);
|
||||
strftime(zonebuf, sizeof(zonebuf), "%Z", &tm);
|
||||
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S.", &tm);
|
||||
timebufp = timebuf;
|
||||
zonebufp = zonebuf;
|
||||
dots = nsec;
|
||||
} else {
|
||||
timebufp = "---------------";
|
||||
zonebufp = "---";
|
||||
timebufp = "--------------------";
|
||||
dots = nsec - ts.tv_nsec;
|
||||
}
|
||||
ts.tv_sec = secs;
|
||||
ts.tv_nsec = nsec;
|
||||
prog = basename(program_invocation_name);
|
||||
if (fprintf(f, "%c%s%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp,
|
||||
zonebufp, rem1000000int64(div1000int64(dots)), file, line,
|
||||
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
||||
if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp,
|
||||
rem1000000int64(div1000int64(dots)), file, line,
|
||||
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
||||
vflogf_onfail(f);
|
||||
}
|
||||
(vfprintf)(f, fmt, va);
|
||||
|
@ -112,7 +114,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
fputc('\n', f);
|
||||
if (level == kLogFatal) {
|
||||
startfatal(file, line);
|
||||
fprintf(stderr, "fatal error see logfile\n");
|
||||
(fprintf)(stderr, "fatal error see logfile\n");
|
||||
die();
|
||||
unreachable;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_
|
||||
#define COSMOPOLITAN_LIBC_NT_WINSOCK_H_
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/struct/pollfd.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
#if 0
|
||||
|
@ -55,10 +57,6 @@ COSMOPOLITAN_C_START_
|
|||
#define kNtTfUseSystemThread 0x10
|
||||
#define kNtTfUseKernelApc 0x20
|
||||
|
||||
struct sockaddr;
|
||||
struct sockaddr_in;
|
||||
struct pollfd$nt;
|
||||
|
||||
enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS };
|
||||
|
||||
enum NtWsaCompletionType {
|
||||
|
|
|
@ -53,8 +53,7 @@ _executive:
|
|||
call _setstack
|
||||
mov %eax,%edi
|
||||
call exit
|
||||
9: .endfn _executive,weak,hidden
|
||||
|
||||
.endfn _executive,weak,hidden
|
||||
ud2
|
||||
|
||||
#ifdef __PG__
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
*/
|
||||
void *_getstack(void) {
|
||||
char *p;
|
||||
p = mmap((char *)0x700000000000 - STACKSIZE, STACKSIZE,
|
||||
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1,
|
||||
0);
|
||||
p = mmap((char *)0x700000000000 /* IMAGE_BASE_VIRTUAL */ - STACKSIZE,
|
||||
STACKSIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (p == MAP_FAILED) abort();
|
||||
return p + STACKSIZE;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,11 @@
|
|||
*/
|
||||
bool isheap(void *p) {
|
||||
int x, i;
|
||||
#if 1
|
||||
register intptr_t rsp asm("rsp");
|
||||
if ((intptr_t)p >= rsp) return false;
|
||||
#endif
|
||||
if ((intptr_t)p <= (intptr_t)_end) return false;
|
||||
x = (intptr_t)p >> 16;
|
||||
i = FindMemoryInterval(&_mmi, x);
|
||||
return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y;
|
||||
|
|
|
@ -17,11 +17,9 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/conv/itoa.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Formats internet address to string.
|
||||
|
@ -33,11 +31,21 @@
|
|||
* @return dst on success or NULL w/ errno
|
||||
*/
|
||||
const char *inet_ntop(int af, const void *src, char *dst, uint32_t size) {
|
||||
char *p;
|
||||
unsigned char *ip4;
|
||||
if (src) {
|
||||
if (af == AF_INET) {
|
||||
unsigned char *p = (unsigned char *)src;
|
||||
if (snprintf(dst, size, "%hhu.%hhu.%hhu.%hhu", p[0], p[1], p[2], p[3]) <
|
||||
size) {
|
||||
if (size >= 16) {
|
||||
p = dst;
|
||||
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;
|
||||
} else {
|
||||
enospc();
|
||||
|
|
|
@ -37,6 +37,7 @@ int fwritebuf(FILE *f) {
|
|||
unsigned bytes;
|
||||
bytes = (f->beg < f->end ? f->end : f->size) - f->beg;
|
||||
if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) {
|
||||
if (errno == EINTR) return 0;
|
||||
return (int)fseterrno(f);
|
||||
}
|
||||
f->beg = (unsigned)((f->beg + put) & (f->size - 1));
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/errno.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
|
||||
/**
|
||||
* Decodes UTF-16 character.
|
||||
|
@ -30,16 +31,15 @@
|
|||
*/
|
||||
forcealignargpointer unsigned(getutf16)(const char16_t *p, wint_t *wc) {
|
||||
unsigned skip = 0;
|
||||
while ((p[skip] & UTF16_MASK) == UTF16_CONT) skip++;
|
||||
if ((p[skip] & UTF16_MASK) != UTF16_MOAR) {
|
||||
while (IsUtf16Cont(p[skip])) skip++;
|
||||
if (IsUcs2(p[skip])) {
|
||||
*wc = p[skip];
|
||||
return skip + 1;
|
||||
} else if ((p[skip + 1] & UTF16_MASK) != UTF16_CONT) {
|
||||
} else if (IsUtf16Cont(p[skip + 1])) {
|
||||
*wc = INVALID_CODEPOINT;
|
||||
return -1;
|
||||
} else {
|
||||
*wc = 0x10000 + ((((unsigned)p[skip + 0] - 0xd800) << 10) +
|
||||
((unsigned)p[skip + 1] - 0xdc00));
|
||||
*wc = MergeUtf16(p[skip], p[skip + 1]);
|
||||
return skip + 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,10 +64,6 @@ void *isnotplaintext(const void *, size_t) nothrow nocallback nosideeffect;
|
|||
|
||||
#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 *);
|
||||
int pututf16(char16_t *, size_t, wint_t, bool);
|
||||
int iswalnum(wint_t);
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
size_t l = 0;
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
#define COSMOPOLITAN_LIBC_STR_THOMPIKE_H_
|
||||
#include "libc/nexgen32e/bsr.h"
|
||||
|
||||
#define ThomPikeCont(x) ((x & 0b11000000) == 0b10000000)
|
||||
#define ThomPikeByte(x) (x & (((1 << ThomPikeMsb(x)) - 1) | 3))
|
||||
#define ThomPikeLen(x) (7 - ThomPikeMsb(x))
|
||||
#define ThomPikeMsb(x) (x < 252 ? bsr(~x & 0xff) : 1)
|
||||
#define ThomPikeCont(x) (((x)&0b11000000) == 0b10000000)
|
||||
#define ThomPikeByte(x) ((x) & (((1 << ThomPikeMsb(x)) - 1) | 3))
|
||||
#define ThomPikeLen(x) (7 - ThomPikeMsb(x))
|
||||
#define ThomPikeMsb(x) ((x) < 252 ? bsr(~(x)&0xff) : 1)
|
||||
#define ThomPikeMerge(x, y) ((x) << 6 | (y)&0b00111111)
|
||||
|
||||
#endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */
|
||||
|
|
|
@ -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_ */
|
|
@ -1421,11 +1421,11 @@ offtime(timep, offset)
|
|||
** 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)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1605,15 +1605,19 @@ timesub(timep, offset, sp, tmp)
|
|||
** Simplified normalize logic courtesy Paul Eggert.
|
||||
*/
|
||||
|
||||
static int
|
||||
static inline int
|
||||
increment_overflow(number, delta)
|
||||
int * number;
|
||||
int delta;
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __builtin_add_overflow(*number, delta, number);
|
||||
#else
|
||||
int number0;
|
||||
number0 = *number;
|
||||
*number += delta;
|
||||
return (*number < number0) != (delta < 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -12,6 +12,7 @@ extern const char kWeekdayNameShort[7][4];
|
|||
extern const char kWeekdayName[7][10];
|
||||
extern const char kMonthNameShort[12][4];
|
||||
extern const char kMonthName[12][10];
|
||||
extern const unsigned short kMonthYearDay[2][12];
|
||||
|
||||
extern char *tzname[2];
|
||||
extern long CLOCKS_PER_SEC;
|
||||
|
|
|
@ -47,9 +47,9 @@ static noinline long times2(struct tms *out_times, struct rusage *ru) {
|
|||
&KernelTime, &UserTime)) {
|
||||
return winerr();
|
||||
}
|
||||
filetimetotimeval(&tv, UserTime);
|
||||
FileTimeToTimeVal(&tv, UserTime);
|
||||
out_times->tms_utime = convertmicros(&tv, tick);
|
||||
filetimetotimeval(&tv, KernelTime);
|
||||
FileTimeToTimeVal(&tv, KernelTime);
|
||||
out_times->tms_stime = convertmicros(&tv, tick);
|
||||
out_times->tms_cutime = 0;
|
||||
out_times->tms_cstime = 0;
|
||||
|
|
18
libc/zip.h
18
libc/zip.h
|
@ -57,6 +57,7 @@
|
|||
#define kZipCfileOffsetLastmodifieddate 14
|
||||
#define kZipCfileOffsetCrc32 16
|
||||
#define kZipCfileOffsetCompressedsize 20
|
||||
#define kZipCfileOffsetUncompressedsize 24
|
||||
#define kZipCfileOffsetExternalattributes 38
|
||||
#define kZipCfileOffsetOffset 42
|
||||
|
||||
|
@ -71,10 +72,10 @@
|
|||
|
||||
#define kZipGflagUtf8 0x800
|
||||
|
||||
#define kZipExtraHdrSize 4
|
||||
#define kZipExtraZip64 0x0001
|
||||
#define kZipExtraNtfs 0x000a
|
||||
#define kZipExtraNtfsFiletimes 0x0001
|
||||
#define kZipExtraHdrSize 4
|
||||
#define kZipExtraZip64 0x0001
|
||||
#define kZipExtraNtfs 0x000a
|
||||
#define kZipExtraExtendedTimestamp 0x5455
|
||||
|
||||
#define kZipCfileMagic "PK\001\002"
|
||||
|
||||
|
@ -105,9 +106,10 @@
|
|||
READ16LE((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */
|
||||
#define ZIP_CFILE_LASTMODIFIEDDATE(P) \
|
||||
READ16LE((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */
|
||||
#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32)
|
||||
#define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize)
|
||||
#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) READ32LE((P) + 24)
|
||||
#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32)
|
||||
#define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize)
|
||||
#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) \
|
||||
READ32LE((P) + kZipCfileOffsetUncompressedsize)
|
||||
#define ZIP_CFILE_NAMESIZE(P) READ16LE((P) + 28)
|
||||
#define ZIP_CFILE_EXTRASIZE(P) READ16LE((P) + 30)
|
||||
#define ZIP_CFILE_COMMENTSIZE(P) READ16LE((P) + 32)
|
||||
|
@ -124,7 +126,7 @@
|
|||
(ZIP_CFILE_NAMESIZE(P) + ZIP_CFILE_EXTRASIZE(P) + ZIP_CFILE_COMMENTSIZE(P) + \
|
||||
kZipCfileHdrMinSize)
|
||||
|
||||
/* central directory file header */
|
||||
/* local file header */
|
||||
#define ZIP_LFILE_MAGIC(P) READ32LE(P)
|
||||
#define ZIP_LFILE_VERSIONNEED(P) ((P)[4])
|
||||
#define ZIP_LFILE_OSNEED(P) ((P)[5])
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
*/
|
||||
int __zipos_close(struct ZiposHandle *h) {
|
||||
if (h) {
|
||||
munmap(h->map, h->mapsize);
|
||||
free(h->map);
|
||||
free(h);
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -27,16 +27,14 @@
|
|||
|
||||
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
|
||||
size_t i, cf;
|
||||
if ((zipos = __zipos_get())) {
|
||||
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
|
||||
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
|
||||
i < ZIP_CDIR_RECORDS(zipos->cdir);
|
||||
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
|
||||
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
|
||||
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
|
||||
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
|
||||
return cf;
|
||||
}
|
||||
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
|
||||
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
|
||||
i < ZIP_CDIR_RECORDS(zipos->cdir);
|
||||
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
|
||||
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
|
||||
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
|
||||
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
|
||||
return cf;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
|
|
@ -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 (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||
assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf));
|
||||
h->mapsize = ROUNDUP(h->size + FRAMESIZE, FRAMESIZE);
|
||||
if ((h->map = mapanon(h->mapsize)) != MAP_FAILED) {
|
||||
h->mem = h->map + FRAMESIZE / 2;
|
||||
h->mapsize = h->size;
|
||||
if ((h->map = malloc(h->mapsize)) != MAP_FAILED) {
|
||||
h->mem = h->map;
|
||||
if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)(
|
||||
h, ZIP_LFILE_CONTENT(zipos->map + lf),
|
||||
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.
|
||||
*
|
||||
* @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 fd;
|
||||
|
@ -140,7 +140,6 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
|||
sigset_t oldmask;
|
||||
struct Zipos *zipos;
|
||||
assert((flags & O_ACCMODE) == O_RDONLY);
|
||||
sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
|
||||
if ((zipos = __zipos_get())) {
|
||||
if ((cf = __zipos_find(zipos, name)) != -1) {
|
||||
fd = __zipos_load(zipos, cf, flags, mode);
|
||||
|
@ -150,6 +149,5 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
|||
} else {
|
||||
fd = enoexec();
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
return fd;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -58,5 +58,5 @@ Retry-After, kHttpRetryAfter
|
|||
Server, kHttpServer
|
||||
Vary, kHttpVary
|
||||
Warning, kHttpWarning
|
||||
WWW-Authenticate, kHttpWwwAuthenticate
|
||||
Www-Authenticate, kHttpWwwAuthenticate
|
||||
Last-Modified, kHttpLastModified
|
||||
|
|
|
@ -216,7 +216,7 @@ LookupHttpHeader (register const char *str, register size_t len)
|
|||
#line 43 "gethttpheader.gperf"
|
||||
{"Keep-Alive", kHttpKeepAlive},
|
||||
#line 61 "gethttpheader.gperf"
|
||||
{"WWW-Authenticate", kHttpWwwAuthenticate},
|
||||
{"Www-Authenticate", kHttpWwwAuthenticate},
|
||||
#line 35 "gethttpheader.gperf"
|
||||
{"Expires", kHttpExpires},
|
||||
#line 46 "gethttpheader.gperf"
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
%readonly-tables
|
||||
%struct-type
|
||||
%define lookup-function-name LookupHttpMethod
|
||||
struct HttpMethodSlot { char *name; char code; };
|
||||
struct HttpMethodSlot { char name[8]; char code; };
|
||||
%%
|
||||
DELETE, kHttpDelete
|
||||
GET, kHttpGet
|
||||
|
@ -20,17 +20,11 @@ CONNECT, kHttpConnect
|
|||
OPTIONS, kHttpOptions
|
||||
TRACE, kHttpTrace
|
||||
COPY, kHttpCopy
|
||||
CHECKOUT, kHttpCheckout
|
||||
LOCK, kHttpLock
|
||||
MERGE, kHttpMerge
|
||||
MKACTIVITY, kHttpMkactivity
|
||||
MKCOL, kHttpMkcol
|
||||
MOVE, kHttpMove
|
||||
NOTIFY, kHttpNotify
|
||||
PATCH, kHttpPatch
|
||||
PROPFIND, kHttpPropfind
|
||||
PROPPATCH, kHttpProppatch
|
||||
REPORT, kHttpReport
|
||||
SUBSCRIBE, kHttpSubscribe
|
||||
UNLOCK, kHttpUnlock
|
||||
UNSUBSCRIBE, kHttpUnsubscribe
|
||||
|
|
|
@ -35,14 +35,14 @@
|
|||
#include "net/http/http.h"
|
||||
#define GPERF_DOWNCASE
|
||||
#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 MAX_WORD_LENGTH 11
|
||||
#define MIN_HASH_VALUE 4
|
||||
#define MAX_HASH_VALUE 34
|
||||
/* maximum key range = 31, duplicates = 0 */
|
||||
#define MAX_WORD_LENGTH 7
|
||||
#define MIN_HASH_VALUE 3
|
||||
#define MAX_HASH_VALUE 25
|
||||
/* maximum key range = 23, duplicates = 0 */
|
||||
|
||||
#ifndef GPERF_DOWNCASE
|
||||
#define GPERF_DOWNCASE 1
|
||||
|
@ -101,32 +101,32 @@ hash (register const char *str, register size_t len)
|
|||
{
|
||||
static const unsigned char asso_values[] =
|
||||
{
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 0, 35, 5, 10, 10,
|
||||
35, 5, 10, 35, 35, 0, 30, 15, 0, 0,
|
||||
0, 35, 5, 15, 0, 5, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 0, 35, 5,
|
||||
10, 10, 35, 5, 10, 35, 35, 0, 30, 15,
|
||||
0, 0, 0, 35, 5, 15, 0, 5, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
||||
35, 35, 35, 35, 35, 35
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 0, 26, 5, 15, 0,
|
||||
26, 5, 0, 26, 26, 10, 15, 10, 0, 5,
|
||||
0, 26, 10, 26, 5, 0, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 0, 26, 5,
|
||||
15, 0, 26, 5, 0, 26, 26, 10, 15, 10,
|
||||
0, 5, 0, 26, 10, 26, 5, 0, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||
26, 26, 26, 26, 26, 26
|
||||
};
|
||||
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[] =
|
||||
{
|
||||
{""}, {""}, {""}, {""},
|
||||
#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"
|
||||
{"PUT", kHttpPut},
|
||||
#line 22 "gethttpmethod.gperf"
|
||||
{"COPY", kHttpCopy},
|
||||
#line 21 "gethttpmethod.gperf"
|
||||
{"TRACE", kHttpTrace},
|
||||
#line 35 "gethttpmethod.gperf"
|
||||
#line 16 "gethttpmethod.gperf"
|
||||
{"HEAD", kHttpHead},
|
||||
#line 28 "gethttpmethod.gperf"
|
||||
{"PATCH", kHttpPatch},
|
||||
#line 30 "gethttpmethod.gperf"
|
||||
{"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"
|
||||
{"GET", kHttpGet},
|
||||
#line 28 "gethttpmethod.gperf"
|
||||
{"MOVE", kHttpMove},
|
||||
#line 27 "gethttpmethod.gperf"
|
||||
{"MKCOL", kHttpMkcol},
|
||||
#line 33 "gethttpmethod.gperf"
|
||||
{"REPORT", kHttpReport},
|
||||
#line 17 "gethttpmethod.gperf"
|
||||
{"POST", kHttpPost},
|
||||
{""},
|
||||
#line 27 "gethttpmethod.gperf"
|
||||
{"NOTIFY", kHttpNotify},
|
||||
#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"
|
||||
{"MKACTIVITY", kHttpMkactivity},
|
||||
{"MOVE", kHttpMove},
|
||||
#line 21 "gethttpmethod.gperf"
|
||||
{"TRACE", kHttpTrace},
|
||||
#line 14 "gethttpmethod.gperf"
|
||||
{"DELETE", kHttpDelete},
|
||||
{""}, {""},
|
||||
#line 34 "gethttpmethod.gperf"
|
||||
{"SUBSCRIBE", kHttpSubscribe},
|
||||
#line 23 "gethttpmethod.gperf"
|
||||
{"LOCK", kHttpLock},
|
||||
#line 25 "gethttpmethod.gperf"
|
||||
{"MERGE", kHttpMerge},
|
||||
{""}, {""}, {""},
|
||||
#line 24 "gethttpmethod.gperf"
|
||||
{"LOCK", kHttpLock}
|
||||
{"MKCOL", kHttpMkcol}
|
||||
};
|
||||
|
||||
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
|
||||
|
|
|
@ -1,30 +1,25 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
||||
#define COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
||||
#include "libc/alg/alg.h"
|
||||
#include "libc/time/struct/tm.h"
|
||||
|
||||
#define kHttpGet 0
|
||||
#define kHttpHead 1
|
||||
#define kHttpPost 2
|
||||
#define kHttpPut 3
|
||||
#define kHttpDelete 4
|
||||
#define kHttpConnect 5
|
||||
#define kHttpOptions 6
|
||||
#define kHttpTrace 7
|
||||
#define kHttpCopy 8
|
||||
#define kHttpCheckout 9
|
||||
#define kHttpLock 10
|
||||
#define kHttpMerge 11
|
||||
#define kHttpMkactivity 12
|
||||
#define kHttpMkcol 13
|
||||
#define kHttpMove 14
|
||||
#define kHttpNotify 15
|
||||
#define kHttpPatch 16
|
||||
#define kHttpPropfind 17
|
||||
#define kHttpProppatch 18
|
||||
#define kHttpReport 19
|
||||
#define kHttpSubscribe 20
|
||||
#define kHttpUnlock 21
|
||||
#define kHttpUnsubscribe 22
|
||||
#define kHttpGet 0
|
||||
#define kHttpHead 1
|
||||
#define kHttpPost 2
|
||||
#define kHttpPut 3
|
||||
#define kHttpDelete 4
|
||||
#define kHttpConnect 5
|
||||
#define kHttpOptions 6
|
||||
#define kHttpTrace 7
|
||||
#define kHttpCopy 8
|
||||
#define kHttpLock 9
|
||||
#define kHttpMerge 10
|
||||
#define kHttpMkcol 11
|
||||
#define kHttpMove 12
|
||||
#define kHttpNotify 13
|
||||
#define kHttpPatch 14
|
||||
#define kHttpReport 15
|
||||
#define kHttpUnlock 16
|
||||
|
||||
#define kHttpAccept 0
|
||||
#define kHttpAcceptCharset 1
|
||||
|
@ -86,17 +81,24 @@ struct HttpRequestSlice {
|
|||
|
||||
struct HttpRequest {
|
||||
int method;
|
||||
int length;
|
||||
struct HttpRequestSlice uri;
|
||||
struct HttpRequestSlice version;
|
||||
struct HttpRequestSlice scratch;
|
||||
struct HttpRequestSlice headers[kHttpHeadersMax];
|
||||
};
|
||||
|
||||
extern const char kHttpMethod[17][8];
|
||||
|
||||
int GetHttpHeader(const char *, size_t);
|
||||
int GetHttpMethod(const char *, size_t);
|
||||
int ParseHttpRequest(struct HttpRequest *, const char *, size_t);
|
||||
int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *,
|
||||
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_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -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_R = $(filter %.rl,$(NET_HTTP_A_FILES))
|
||||
|
||||
NET_HTTP_A_SRCS = \
|
||||
$(NET_HTTP_A_SRCS_S) \
|
||||
$(NET_HTTP_A_SRCS_C) \
|
||||
NET_HTTP_A_SRCS = \
|
||||
$(NET_HTTP_A_SRCS_S) \
|
||||
$(NET_HTTP_A_SRCS_C) \
|
||||
$(NET_HTTP_A_SRCS_R)
|
||||
|
||||
NET_HTTP_A_OBJS = \
|
||||
$(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \
|
||||
$(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \
|
||||
NET_HTTP_A_OBJS = \
|
||||
$(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \
|
||||
$(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \
|
||||
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.o)
|
||||
|
||||
NET_HTTP_A_CHECKS = \
|
||||
$(NET_HTTP_A).pkg \
|
||||
NET_HTTP_A_CHECKS = \
|
||||
$(NET_HTTP_A).pkg \
|
||||
$(NET_HTTP_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
NET_HTTP_A_DIRECTDEPS = \
|
||||
LIBC_ALG \
|
||||
LIBC_CALLS \
|
||||
LIBC_CONV \
|
||||
LIBC_FMT \
|
||||
LIBC_LOG \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_TIME \
|
||||
NET_HTTP_A_DIRECTDEPS = \
|
||||
LIBC_ALG \
|
||||
LIBC_CALLS \
|
||||
LIBC_CONV \
|
||||
LIBC_FMT \
|
||||
LIBC_LOG \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_STUBS \
|
||||
LIBC_SYSV \
|
||||
LIBC_TIME \
|
||||
LIBC_X
|
||||
|
||||
NET_HTTP_A_DEPS := \
|
||||
NET_HTTP_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(NET_HTTP_A): net/http/ \
|
||||
$(NET_HTTP_A).pkg \
|
||||
$(NET_HTTP_A): net/http/ \
|
||||
$(NET_HTTP_A).pkg \
|
||||
$(NET_HTTP_A_OBJS)
|
||||
|
||||
$(NET_HTTP_A).pkg: \
|
||||
$(NET_HTTP_A_OBJS) \
|
||||
$(NET_HTTP_A).pkg: \
|
||||
$(NET_HTTP_A_OBJS) \
|
||||
$(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_SRCS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_SRCS))
|
||||
NET_HTTP_HDRS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_HDRS))
|
||||
NET_HTTP_CHECKS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_CHECKS))
|
||||
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=build/bootstrap/%.c) \
|
||||
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 \
|
||||
.PRECIOUS: \
|
||||
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.c) \
|
||||
o/$(MODE)/net/http/uricspn.s \
|
||||
o/$(MODE)/net/http/uricspn.i \
|
||||
o/$(MODE)/net/http/uricspn.c
|
||||
|
||||
.PHONY: o/$(MODE)/net/http
|
||||
o/$(MODE)/net/http: \
|
||||
$(NET_HTTP_CHECKS) \
|
||||
o/$(MODE)/net/http: \
|
||||
$(NET_HTTP_CHECKS) \
|
||||
$(NET_HTTP_A_SRCS_R:%.rl=%.svgz)
|
||||
|
|
|
@ -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", //
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -112,7 +112,10 @@ int ParseHttpRequest(struct HttpRequest *req, const char *p, size_t n) {
|
|||
}
|
||||
break;
|
||||
case LF2:
|
||||
if (c == '\n') return 0;
|
||||
if (c == '\n') {
|
||||
req->length = i + 1;
|
||||
return 0;
|
||||
}
|
||||
return ebadmsg();
|
||||
default:
|
||||
unreachable;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)));
|
||||
}
|
|
@ -25,6 +25,9 @@ TEST_LIBC_CONV_CHECKS = \
|
|||
|
||||
TEST_LIBC_CONV_DIRECTDEPS = \
|
||||
LIBC_CONV \
|
||||
LIBC_FMT \
|
||||
LIBC_LOG \
|
||||
LIBC_STDIO \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_STUBS \
|
||||
LIBC_TINYMATH \
|
||||
|
|
|
@ -23,11 +23,11 @@
|
|||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
TEST(timevaltofiletime, roundTrip) {
|
||||
TEST(TimeValToFileTime, roundTrip) {
|
||||
struct timeval tv1, tv2;
|
||||
tv1.tv_sec = 31337;
|
||||
tv1.tv_usec = 1337;
|
||||
filetimetotimeval(&tv2, timevaltofiletime(&tv1));
|
||||
FileTimeToTimeVal(&tv2, TimeValToFileTime(&tv1));
|
||||
EXPECT_EQ(31337, tv2.tv_sec);
|
||||
EXPECT_EQ(1337, tv2.tv_usec);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -679,9 +679,12 @@ TEST(snprintf, formatStringLiteral) {
|
|||
}
|
||||
|
||||
BENCH(palandprintf, bench) {
|
||||
EZBENCH2("snprintf %x", donothing, Format("%x", VEIL("r", INT_MIN)));
|
||||
EZBENCH2("snprintf %d", donothing, Format("%d", VEIL("r", INT_MIN)));
|
||||
EZBENCH2("snprintf %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯")));
|
||||
EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23)));
|
||||
EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23)));
|
||||
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 %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯")));
|
||||
EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer));
|
||||
|
|
|
@ -72,6 +72,22 @@ TEST(SUITE(snprintf), testStringPrecision_showsTrueBinary) {
|
|||
EXPECT_STREQ("\3\4\0", Format("%.*s", 3, "\3\4\0"));
|
||||
}
|
||||
|
||||
TEST(SUITE(snprintf), testPrecision_usesCodepointCount) {
|
||||
EXPECT_STREQ("ちゃぶ台返し", Format("%.*s", 6, "ちゃぶ台返し"));
|
||||
TEST(SUITE(snprintf), testPrecision_usesByteCount) {
|
||||
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"♥"));
|
||||
}
|
||||
|
|
|
@ -54,10 +54,3 @@ TEST(inet_ntop, testNoSpace) {
|
|||
ASSERT_STREQ("", 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -17,6 +17,10 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 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 "tool/build/lib/dis.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);
|
||||
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
|
||||
DisInst(d, b1, DisSpec(d->xedd, b2));
|
||||
EXPECT_STREQ("or $0xc00,12(%esp)", b1);
|
||||
EXPECT_STREQ("orw $0xc00,12(%esp)", b1);
|
||||
}
|
||||
|
||||
TEST(DisInst, testPause) {
|
||||
|
@ -269,3 +273,19 @@ TEST(DisInst, testJmpEq) {
|
|||
DisInst(d, b1, DisSpec(d->xedd, b2));
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -57,6 +57,12 @@ o/$(MODE)/third_party/zlib/adler32.o: \
|
|||
-ffunction-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_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_SRCS))
|
||||
THIRD_PARTY_ZLIB_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_HDRS))
|
||||
|
|
|
@ -35,6 +35,7 @@ TOOL_BUILD_DIRECTDEPS = \
|
|||
LIBC_ELF \
|
||||
LIBC_FMT \
|
||||
LIBC_LOG \
|
||||
LIBC_LOG_ASAN \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
|
|
|
@ -27,6 +27,7 @@ TOOL_BUILD_EMUBIN_CHECKS = \
|
|||
|
||||
TOOL_BUILD_EMUBIN_DIRECTDEPS = \
|
||||
LIBC_STUBS \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_TINYMATH
|
||||
|
||||
TOOL_BUILD_EMUBIN_DEPS := \
|
||||
|
@ -57,10 +58,10 @@ o/dbg/tool/build/emubin/lisp.real.com.dbg: \
|
|||
$(APE)
|
||||
-@$(APELINK)
|
||||
|
||||
o/tiny/tool/build/emubin/lisp.bin.dbg: \
|
||||
o/$(MODE)/tool/build/emubin/lisp.bin.dbg: \
|
||||
$(TOOL_BUILD_EMUBIN_DEPS) \
|
||||
o/tiny/tool/build/emubin/lisp.real.o \
|
||||
o/tiny/tool/build/emubin/lispstart.o \
|
||||
o/$(MODE)/tool/build/emubin/lisp.real.o \
|
||||
o/$(MODE)/tool/build/emubin/lispstart.o \
|
||||
tool/build/emubin/lisp.lds
|
||||
@$(ELFLINK) -z max-page-size=0x10
|
||||
|
||||
|
|
|
@ -3,36 +3,35 @@
|
|||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ 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. │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ 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 │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
|
||||
#define TRACE 0
|
||||
#define ERRORS 1
|
||||
#define LONG long
|
||||
#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) */ \
|
||||
({ \
|
||||
char IsAtom; \
|
||||
asm("test%z1\t$1,%1" : "=@ccz"(IsAtom) : "Qm"((char)x)); \
|
||||
IsAtom; \
|
||||
#define TYPE(x) /* a.k.a. x&1 */ \
|
||||
({ \
|
||||
char IsAtom; \
|
||||
asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \
|
||||
IsAtom; \
|
||||
})
|
||||
|
||||
#define OBJECT(t, v) /* a.k.a. v<<1|t */ \
|
||||
|
@ -57,71 +56,109 @@
|
|||
c; \
|
||||
})
|
||||
|
||||
#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
|
||||
({ \
|
||||
__typeof(*(BASE)) Reg; \
|
||||
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
|
||||
asm("mov\t%c2(%1),%0" \
|
||||
: "=Q"(Reg) \
|
||||
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE)))); \
|
||||
} else { \
|
||||
asm("mov\t%c3(%1,%2),%0" \
|
||||
: "=Q"(Reg) \
|
||||
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
|
||||
"i"((DISP) * sizeof(*(BASE)))); \
|
||||
} \
|
||||
Reg; \
|
||||
#define REAL_READ_(REG, BASE, INDEX, DISP) \
|
||||
({ \
|
||||
__typeof(*(BASE)) Reg; \
|
||||
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
|
||||
asm("mov\t%c2(%1),%0" \
|
||||
: REG(Reg) \
|
||||
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \
|
||||
"m"(BASE[(INDEX) + (DISP)])); \
|
||||
} else { \
|
||||
asm("mov\t%c3(%1,%2),%0" \
|
||||
: REG(Reg) \
|
||||
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
|
||||
"i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \
|
||||
} \
|
||||
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; \
|
||||
if (!(OBJECT)) { \
|
||||
asm("mov\t%c2(%1),%0" \
|
||||
: "=Q"(Reg) \
|
||||
: REG(Reg) \
|
||||
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
||||
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
|
||||
"m"(OBJECT->MEMBER)); \
|
||||
} else { \
|
||||
asm("mov\t%c3(%1,%2),%0" \
|
||||
: "=Q"(Reg) \
|
||||
: REG(Reg) \
|
||||
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
||||
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
|
||||
"m"(OBJECT->MEMBER)); \
|
||||
} \
|
||||
Reg; \
|
||||
})
|
||||
|
||||
#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \
|
||||
do { \
|
||||
__typeof(*(OBJECT->MEMBER)) Reg; \
|
||||
if (!(OBJECT)) { \
|
||||
asm volatile("mov\t%0,%c2(%1)" \
|
||||
: /* manual output */ \
|
||||
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
|
||||
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||
sizeof(*(OBJECT->MEMBER)) * (DISP)) \
|
||||
: "memory"); \
|
||||
} else { \
|
||||
asm volatile("mov\t%0,%c3(%1,%2)" \
|
||||
: /* manual output */ \
|
||||
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
|
||||
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||
sizeof(*(OBJECT->MEMBER)) * (DISP)) \
|
||||
: "memory"); \
|
||||
} \
|
||||
/* #ifdef __REAL_MODE__ */
|
||||
#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
|
||||
(sizeof(*(OBJECT->MEMBER)) == 1 \
|
||||
? REAL_READ_ARRAY_FIELD_("=Q", OBJECT, MEMBER, INDEX, DISP) \
|
||||
: REAL_READ_ARRAY_FIELD_("=r", OBJECT, MEMBER, INDEX, DISP))
|
||||
/* #else */
|
||||
/* #define REAL_READ_ARRAY_FIELD(o, m, i, d) o->m[i + d] */
|
||||
/* #endif */
|
||||
|
||||
#define REAL_WRITE_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \
|
||||
do { \
|
||||
if (!(OBJECT)) { \
|
||||
asm("mov\t%1,%c3(%2)" \
|
||||
: "=m"(OBJECT->MEMBER) \
|
||||
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
|
||||
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
||||
} 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)
|
||||
|
||||
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"
|
||||
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
|
||||
: "0"(di), "1"(cx), "a"(al));
|
||||
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"
|
||||
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
|
||||
: "0"(di), "1"(si), "2"(cx));
|
||||
|
@ -147,14 +184,13 @@ static void RawMode(void) {
|
|||
#endif
|
||||
}
|
||||
|
||||
__attribute__((__noinline__)) static int PrintChar(LONG c) {
|
||||
__attribute__((__noinline__)) static void PrintChar(LONG c) {
|
||||
#ifdef __REAL_MODE__
|
||||
asm volatile("mov\t$0x0E,%%ah\n\t"
|
||||
"int\t$0x10"
|
||||
: /* no outputs */
|
||||
: "a"(c), "b"(7)
|
||||
: "memory");
|
||||
return 0;
|
||||
#else
|
||||
static short buf;
|
||||
int rc;
|
||||
|
@ -163,7 +199,6 @@ __attribute__((__noinline__)) static int PrintChar(LONG c) {
|
|||
: "=a"(rc)
|
||||
: "0"(1), "D"(1), "S"(&buf), "d"(1)
|
||||
: "rcx", "r11", "memory");
|
||||
return rc;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -177,6 +212,7 @@ static void PrintString(char *s) {
|
|||
}
|
||||
|
||||
static int XlatChar(LONG c) {
|
||||
if (c == 0x7F) return '\b';
|
||||
if (c >= 'a') {
|
||||
asm volatile("" ::: "memory");
|
||||
if (c <= 'z') c -= 'a' - 'A';
|
||||
|
@ -185,19 +221,16 @@ static int XlatChar(LONG c) {
|
|||
}
|
||||
|
||||
static int EchoChar(LONG c) {
|
||||
if (c == '\b' || c == 0x7F) {
|
||||
PrintString("\b \b");
|
||||
return '\b';
|
||||
} else {
|
||||
if (c != '\b') {
|
||||
PrintChar(c);
|
||||
if (c == '\r') {
|
||||
PrintChar('\n');
|
||||
}
|
||||
return c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static noinline int ReadChar(void) {
|
||||
__attribute__((__noinline__)) static noinline int ReadChar(void) {
|
||||
int c;
|
||||
#ifdef __REAL_MODE__
|
||||
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 TYPE_CONS 1
|
||||
#define ATOM 0
|
||||
#define CONS 1
|
||||
|
||||
#define ATOM_NIL 0
|
||||
#define NIL 0
|
||||
#define ATOM_T 8
|
||||
#define ATOM_QUOTE 12
|
||||
#define ATOM_ATOM 24
|
||||
|
@ -234,7 +267,7 @@ static noinline int ReadChar(void) {
|
|||
#define ATOM_DEFUN 110
|
||||
|
||||
#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 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
|
||||
|
@ -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 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 PTR(i) ((i) << 1 | CONS)
|
||||
|
||||
#define ARRAYLEN(A) \
|
||||
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
|
||||
|
||||
struct Lisp {
|
||||
WORD memory[WORDS];
|
||||
WORD mem[WORDS];
|
||||
unsigned char syntax[256];
|
||||
unsigned char look;
|
||||
char token[16];
|
||||
WORD look;
|
||||
WORD globals;
|
||||
int index;
|
||||
WORD index;
|
||||
char token[128];
|
||||
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";
|
||||
|
||||
_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__
|
||||
static struct Lisp *const q;
|
||||
#else
|
||||
|
@ -281,28 +332,39 @@ static void SetupSyntax(void) {
|
|||
q->syntax['\''] = '\'';
|
||||
}
|
||||
|
||||
forceinline WORD Car(LONG x) {
|
||||
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 0);
|
||||
static inline WORD Car(LONG x) {
|
||||
return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 0);
|
||||
}
|
||||
|
||||
forceinline WORD Cdr(LONG x) {
|
||||
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 1);
|
||||
static inline WORD Cdr(LONG x) {
|
||||
return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 1);
|
||||
}
|
||||
|
||||
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;
|
||||
REAL_WRITE_ARRAY_FIELD(q, memory, i, 0, car);
|
||||
REAL_WRITE_ARRAY_FIELD(q, memory, i, 1, cdr);
|
||||
q->index += 2;
|
||||
c = OBJECT(TYPE_CONS, i);
|
||||
return c;
|
||||
REAL_WRITE_ARRAY_FIELD(q, mem, i, 0, car);
|
||||
REAL_WRITE_ARRAY_FIELD(q, mem, i, 1, cdr);
|
||||
q->index = i + 2;
|
||||
cell = OBJECT(CONS, i);
|
||||
#if TRACE
|
||||
PrintString("CONS<-");
|
||||
Print(cell);
|
||||
#endif
|
||||
return cell;
|
||||
}
|
||||
|
||||
static void SetupBuiltins(void) {
|
||||
CopyMemory(q->str, kSymbols, sizeof(kSymbols));
|
||||
q->globals =
|
||||
Cons(Cons(ATOM_NIL, ATOM_NIL), Cons(Cons(ATOM_T, ATOM_T), ATOM_NIL));
|
||||
CopyMemory(q->mem, kGlobals, sizeof(kGlobals));
|
||||
q->index = ARRAYLEN(kGlobals);
|
||||
q->globals = PTR(0);
|
||||
}
|
||||
|
||||
static char *StpCpy(char *d, char *s) {
|
||||
|
@ -314,7 +376,7 @@ static char *StpCpy(char *d, char *s) {
|
|||
return d;
|
||||
}
|
||||
|
||||
static WORD Intern(char *s) {
|
||||
WORD Intern(char *s) {
|
||||
int j, cx;
|
||||
char c, *z, *t;
|
||||
z = q->str;
|
||||
|
@ -325,7 +387,7 @@ static WORD Intern(char *s) {
|
|||
break;
|
||||
}
|
||||
if (!c) {
|
||||
return OBJECT(TYPE_ATOM, z - q->str - j - 1);
|
||||
return OBJECT(ATOM, z - q->str - j - 1);
|
||||
}
|
||||
c = LODS(z);
|
||||
}
|
||||
|
@ -334,11 +396,11 @@ static WORD Intern(char *s) {
|
|||
}
|
||||
--z;
|
||||
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) {
|
||||
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) {
|
||||
|
@ -357,7 +419,8 @@ static void GetToken(void) {
|
|||
if (b != '\b') {
|
||||
STOS(t, b);
|
||||
} else if (t > q->token) {
|
||||
--t;
|
||||
PrintString("\b \b");
|
||||
if (t > q->token) --t;
|
||||
}
|
||||
b = ReadChar();
|
||||
}
|
||||
|
@ -387,7 +450,7 @@ static WORD GetList(void) {
|
|||
case '\'':
|
||||
return AddList(GetQuote());
|
||||
case ')':
|
||||
return ATOM_NIL;
|
||||
return NIL;
|
||||
case '.':
|
||||
return ConsumeObject();
|
||||
}
|
||||
|
@ -422,7 +485,7 @@ static void PrintList(LONG x) {
|
|||
PrintChar('(');
|
||||
PrintObject(Car(x));
|
||||
while ((x = Cdr(x))) {
|
||||
if (!ATOM(x)) {
|
||||
if (TYPE(x) == CONS) {
|
||||
PrintChar(' ');
|
||||
PrintObject(Car(x));
|
||||
} else {
|
||||
|
@ -434,7 +497,7 @@ static void PrintList(LONG x) {
|
|||
}
|
||||
|
||||
static void PrintObject(LONG x) {
|
||||
if (ATOM(x)) {
|
||||
if (TYPE(x) == ATOM) {
|
||||
PrintAtom(x);
|
||||
} else {
|
||||
PrintList(x);
|
||||
|
@ -447,8 +510,7 @@ static void Print(LONG i) {
|
|||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void Reset(void) {
|
||||
asm volatile("jmp\tRepl");
|
||||
__builtin_unreachable();
|
||||
longjmp(jb, 1);
|
||||
}
|
||||
|
||||
__attribute__((__noreturn__)) static void OnUndefined(LONG x) {
|
||||
|
@ -472,7 +534,7 @@ __attribute__((__noreturn__)) static void OnArity(void) {
|
|||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
static WORD Atom(LONG x) {
|
||||
return BOOL(ATOM(x));
|
||||
return BOOL(TYPE(x) == ATOM);
|
||||
}
|
||||
|
||||
static WORD Null(LONG x) {
|
||||
|
@ -480,24 +542,32 @@ static WORD Null(LONG x) {
|
|||
}
|
||||
|
||||
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) {
|
||||
for (;;) {
|
||||
if (!y) OnUndefined(x);
|
||||
if (Eq(Caar(y), x)) break;
|
||||
y = Cdr(y);
|
||||
}
|
||||
return Cdar(y);
|
||||
if (Null(y)) OnUndefined(x);
|
||||
if (Eq(Caar(y), x)) return Cdar(y);
|
||||
return Assoc(x, Cdr(y));
|
||||
}
|
||||
|
||||
static WORD Append(LONG x, LONG y) {
|
||||
if (x) {
|
||||
return Cons(Car(x), Append(Cdr(x), y));
|
||||
#if TRACE
|
||||
PrintString("APPEND->");
|
||||
Print(x);
|
||||
PrintString(" ");
|
||||
Print(y);
|
||||
#endif
|
||||
if (!Null(x)) {
|
||||
x = Cons(Car(x), Append(Cdr(x), y));
|
||||
} 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 it's zip() basically
|
||||
*/
|
||||
static WORD Pair(LONG x, LONG y) {
|
||||
if (!x && !y) {
|
||||
return ATOM_NIL;
|
||||
} else if (!ATOM(x) && !ATOM(y)) {
|
||||
return Cons(Cons(Car(x), Car(y)), Pair(Cdr(x), Cdr(y)));
|
||||
static WORD Pair_(LONG x, LONG y) {
|
||||
if (Null(x) && Null(y)) {
|
||||
return NIL;
|
||||
} else if (TYPE(x) == CONS && TYPE(y) == CONS) {
|
||||
return Cons(Cons(Car(x), Car(y)), Pair_(Cdr(x), Cdr(y)));
|
||||
} else {
|
||||
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) {
|
||||
if (m) {
|
||||
return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m)));
|
||||
} else {
|
||||
return ATOM_NIL;
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -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) {
|
||||
return Cons(Eval(Car(m), a), Evlis(Cdr(m), a));
|
||||
return Cons(Eval(Car(m), a), Evlis_(Cdr(m), a));
|
||||
} 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) {
|
||||
WORD name, value;
|
||||
name = Car(e);
|
||||
|
@ -563,9 +663,9 @@ static WORD Defun(LONG e) {
|
|||
}
|
||||
|
||||
static WORD Evaluate(LONG e, LONG a) {
|
||||
if (ATOM(e)) {
|
||||
if (Atom(e)) {
|
||||
return Assoc(e, a);
|
||||
} else if (ATOM(Car(e))) {
|
||||
} else if (Atom(Car(e))) {
|
||||
switch (Car(e)) {
|
||||
case ATOM_QUOTE:
|
||||
return Cadr(e);
|
||||
|
@ -601,6 +701,8 @@ static WORD Eval(LONG e, LONG a) {
|
|||
#if TRACE
|
||||
PrintString("->");
|
||||
Print(e);
|
||||
PrintString(" ");
|
||||
Print(a);
|
||||
#endif
|
||||
e = Evaluate(e, a);
|
||||
#if TRACE
|
||||
|
@ -615,6 +717,7 @@ static WORD Eval(LONG e, LONG a) {
|
|||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
void Repl(void) {
|
||||
setjmp(jb);
|
||||
for (;;) {
|
||||
PrintString("* ");
|
||||
Print(Eval(Read(), q->globals));
|
||||
|
@ -622,7 +725,7 @@ void Repl(void) {
|
|||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RawMode();
|
||||
/* RawMode(); */
|
||||
SetupSyntax();
|
||||
SetupBuiltins();
|
||||
PrintString("THE LISP CHALLENGE V1\r\n"
|
|
@ -29,6 +29,7 @@ SECTIONS {
|
|||
. = 0x1fe;
|
||||
SHORT(0xaa55);
|
||||
*(.text .text.*)
|
||||
_etext = .;
|
||||
. = ALIGN(512);
|
||||
}
|
||||
|
||||
|
@ -43,10 +44,11 @@ SECTIONS {
|
|||
}
|
||||
}
|
||||
|
||||
syntax = 0x600+2048*2;
|
||||
look = 0x600+2048*2+256;
|
||||
token = 0x600+2048*2+256+1;
|
||||
globals = 0x600+2048*2+256+1+16;
|
||||
index = 0x600+2048*2+256+1+16+2;
|
||||
str = 0x600+2048*2+256+1+16+2+4;
|
||||
v_sectors = SIZEOF(.text) / 512;
|
||||
boot = 0x7c00;
|
||||
q.syntax = 8192*2;
|
||||
q.look = 8192*2+256;
|
||||
q.globals = 8192*2+256+2;
|
||||
q.index = 8192*2+256+2+2;
|
||||
q.token = 8192*2+256+2+2+2;
|
||||
q.str = 8192*2+256+2+2+2+128;
|
||||
v_sectors = SIZEOF(.text) / 512;
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
.code16
|
||||
.section .start,"ax",@progbits
|
||||
_start: jmp 1f
|
||||
1: ljmp $0x600>>4,$2f
|
||||
2: push %cs
|
||||
1: ljmp $0x600>>4,$_begin
|
||||
_begin: push %cs
|
||||
pop %ds
|
||||
push %cs
|
||||
pop %es
|
||||
|
@ -50,3 +50,45 @@ _start: jmp 1f
|
|||
.globl _start
|
||||
.globl v_sectors
|
||||
.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
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/unicode/unicode.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/dtoa/dtoa.h"
|
||||
|
@ -91,8 +92,7 @@ DESCRIPTION\n\
|
|||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-h\n\
|
||||
-? help\n\
|
||||
-h help\n\
|
||||
-v verbosity\n\
|
||||
-r real mode\n\
|
||||
-s statistics\n\
|
||||
|
@ -137,8 +137,9 @@ COMPLETENESS\n\
|
|||
#define QUIT 0x200
|
||||
#define EXIT 0x400
|
||||
|
||||
#define CTRL(C) ((C) ^ 0100)
|
||||
#define ALT(C) (('\e' << 010) | (C))
|
||||
#define CTRL(C) ((C) ^ 0100)
|
||||
#define ALT(C) (('\e' << 010) | (C))
|
||||
#define SEX(x, b) ((x) | ((x) & (1ull << (b)) ? -(1ull << (b)) : 0))
|
||||
|
||||
struct Panels {
|
||||
union {
|
||||
|
@ -215,16 +216,6 @@ static struct Breakpoints breakpoints;
|
|||
static void SetupDraw(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) {
|
||||
return g_fmt(b, x);
|
||||
}
|
||||
|
@ -234,16 +225,15 @@ static void SetCarry(bool cf) {
|
|||
}
|
||||
|
||||
static bool IsCall(void) {
|
||||
return m->xedd->op.map == XED_ILD_MAP0 &&
|
||||
(m->xedd->op.opcode == 0xE8 ||
|
||||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 2));
|
||||
return (m->xedd->op.dispatch == 0x0E8 ||
|
||||
(m->xedd->op.dispatch == 0x0FF && m->xedd->op.reg == 2));
|
||||
}
|
||||
|
||||
static bool IsLongBranch(void) {
|
||||
return m->xedd && m->xedd->op.map == XED_ILD_MAP0 &&
|
||||
(m->xedd->op.opcode == 0xEA || m->xedd->op.opcode == 0x9A ||
|
||||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 3) ||
|
||||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 5));
|
||||
return m->mode != XED_MODE_LONG &&
|
||||
(m->xedd->op.dispatch == 0x0EA || m->xedd->op.dispatch == 0x09A ||
|
||||
(m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 3) ||
|
||||
(m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 5));
|
||||
}
|
||||
|
||||
static bool IsDebugBreak(void) {
|
||||
|
@ -370,13 +360,16 @@ static void GetTtySize(void) {
|
|||
static void TuiRejuvinate(void) {
|
||||
GetTtySize();
|
||||
ttyhidecursor(STDOUT_FILENO);
|
||||
ttyraw(0);
|
||||
ttyraw(kTtySigs);
|
||||
xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL);
|
||||
}
|
||||
|
||||
static void OnCtrlC(void) {
|
||||
LOGF("OnCtrlC");
|
||||
action |= INT;
|
||||
if (tuimode) {
|
||||
action |= INT;
|
||||
} else {
|
||||
HaltMachine(m, kMachineExit);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnQ(void) {
|
||||
|
@ -404,7 +397,7 @@ static void OnCont(void) {
|
|||
static void TuiCleanup(void) {
|
||||
sigaction(SIGWINCH, oldsig + 0, NULL);
|
||||
sigaction(SIGCONT, oldsig + 2, NULL);
|
||||
ttyraw((enum TtyRawFlags)(-1u));
|
||||
ttyraw(-1);
|
||||
ttyshowcursor(STDOUT_FILENO);
|
||||
CHECK_NE(-1, close(ttyfd));
|
||||
tuimode = false;
|
||||
|
@ -447,6 +440,7 @@ void TuiSetup(void) {
|
|||
CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR)));
|
||||
xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0);
|
||||
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]));
|
||||
TuiRejuvinate();
|
||||
}
|
||||
|
@ -660,12 +654,19 @@ static void SetupDraw(void) {
|
|||
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) {
|
||||
long i;
|
||||
if ((i = DisFind(dis, GetIp())) == -1 || IsLongBranch()) {
|
||||
Dis(dis, m, GetIp(), m->ip,
|
||||
pan.disassembly.bottom - pan.disassembly.top * 2);
|
||||
CHECK_NE(-1, (i = DisFind(dis, GetIp())));
|
||||
if ((i = DisFind(dis, GetIp())) == -1) {
|
||||
i = Disassemble();
|
||||
}
|
||||
while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++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)) {
|
||||
sprintf(buf, "%`'c", ival);
|
||||
} else {
|
||||
int64toarray_radix10(SignExtend(ival, ssewidth * 8), buf);
|
||||
int64toarray_radix10(SEX(ival, ssewidth * 8), buf);
|
||||
}
|
||||
} else {
|
||||
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";
|
||||
s = buf;
|
||||
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);
|
||||
if (sym != -1 && 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";
|
||||
s = line;
|
||||
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);
|
||||
if (sym != -1 && 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];
|
||||
n = FormatCga(m->bx[0], buf);
|
||||
n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false);
|
||||
LOGF("teletype output %`'.*s", n, buf);
|
||||
MachinePtyWrite(pty, buf, n);
|
||||
}
|
||||
|
||||
|
@ -1520,6 +1520,7 @@ static void OnInt15h(void) {
|
|||
}
|
||||
|
||||
static bool OnHalt(int interrupt) {
|
||||
LOGF("OnHalt(%d)", interrupt);
|
||||
ReactiveDraw();
|
||||
switch (interrupt) {
|
||||
case 1:
|
||||
|
@ -1577,15 +1578,24 @@ static void OnPageDown(void) {
|
|||
|
||||
static void OnUpArrow(void) {
|
||||
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 {
|
||||
--opstart;
|
||||
}
|
||||
LOGF("speed %d", speed);
|
||||
}
|
||||
|
||||
static void OnDownArrow(void) {
|
||||
if (action & CONTINUE) {
|
||||
speed >>= 1;
|
||||
if (speed > 0) {
|
||||
speed >>= 1;
|
||||
} else {
|
||||
speed = MAX(-(5 * 1000), MIN(-10, speed) << 1); // 10ms..5s delay
|
||||
}
|
||||
} else {
|
||||
++opstart;
|
||||
}
|
||||
|
@ -1897,6 +1907,9 @@ static void Tui(void) {
|
|||
action &= ~WINCHED;
|
||||
}
|
||||
interactive = ++tick > speed;
|
||||
if (interactive && speed < 0) {
|
||||
dsleep(.001L * -speed);
|
||||
}
|
||||
if (!(action & CONTINUE) || interactive) {
|
||||
tick = 0;
|
||||
GetDisIndex();
|
||||
|
@ -1907,7 +1920,8 @@ static void Tui(void) {
|
|||
LOGF("TUI FAILURE");
|
||||
PrintMessageBox(ttyfd, systemfailure, tyn, txn);
|
||||
ReadKeyboard();
|
||||
} else if (!IsExecuting() || (interactive && HasPendingKeyboard())) {
|
||||
} else if (!IsExecuting() || ((interactive || !(action & CONTINUE)) &&
|
||||
!(action & INT) && HasPendingKeyboard())) {
|
||||
ReadKeyboard();
|
||||
}
|
||||
if (action & INT) {
|
||||
|
@ -1961,6 +1975,9 @@ static void Tui(void) {
|
|||
}
|
||||
if (!IsDebugBreak()) {
|
||||
ExecuteInstruction(m);
|
||||
if (IsLongBranch()) {
|
||||
Disassemble();
|
||||
}
|
||||
} else {
|
||||
m->ip += m->xedd->length;
|
||||
action &= ~NEXT;
|
||||
|
@ -1997,7 +2014,7 @@ static void Tui(void) {
|
|||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
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) {
|
||||
case 't':
|
||||
tuimode = true;
|
||||
|
@ -2007,6 +2024,7 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
break;
|
||||
case 'r':
|
||||
m->mode = XED_MACHINE_MODE_REAL;
|
||||
g_disisprog_disable = true;
|
||||
break;
|
||||
case 's':
|
||||
printstats = true;
|
||||
|
@ -2024,7 +2042,6 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
strcpy(logpath, optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
PrintUsage(EXIT_SUCCESS, stdout);
|
||||
default:
|
||||
PrintUsage(EX_USAGE, stderr);
|
||||
|
|
|
@ -64,13 +64,12 @@ ssize_t WriteBuffer(struct Buffer *b, int fd) {
|
|||
p = b->p;
|
||||
n = b->i;
|
||||
do {
|
||||
TryAgain:
|
||||
if ((rc = write(fd, p, n)) != -1) {
|
||||
wrote = rc;
|
||||
p += wrote;
|
||||
n -= wrote;
|
||||
} else if (errno == EINTR) {
|
||||
goto TryAgain;
|
||||
break;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -17,10 +17,14 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/safemacros.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/hefty/spawn.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "tool/build/lib/demangle.h"
|
||||
|
||||
|
@ -37,12 +41,14 @@ void CloseCxxFilt(void) {
|
|||
|
||||
void SpawnCxxFilt(void) {
|
||||
int pid;
|
||||
const char *cxxfilt;
|
||||
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[1] = -1;
|
||||
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) {
|
||||
atexit(CloseCxxFilt);
|
||||
}
|
||||
|
@ -52,30 +58,50 @@ void SpawnCxxFilt(void) {
|
|||
g_cxxfilt.pid = pid;
|
||||
}
|
||||
|
||||
char *DemangleCxxFilt(char *p, const char *symbol) {
|
||||
int n;
|
||||
char buf[512];
|
||||
bool iscomplicated;
|
||||
char *CopySymbol(char *p, size_t pn, const char *s, size_t sn) {
|
||||
size_t extra;
|
||||
bool showdots, 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 == -1) return NULL;
|
||||
if ((n = strlen(symbol)) >= ARRAYLEN(buf)) return NULL;
|
||||
memcpy(buf, symbol, n);
|
||||
buf[n] = '\n';
|
||||
write(g_cxxfilt.fds[0], buf, n + 1);
|
||||
n = read(g_cxxfilt.fds[1], buf, ARRAYLEN(buf));
|
||||
if (n > 1 && buf[n - 1] == '\n') {
|
||||
if (buf[n - 2] == '\r') --n;
|
||||
--n;
|
||||
iscomplicated = memchr(buf, ' ', n) || memchr(buf, '(', n);
|
||||
if (iscomplicated) *p++ = '"';
|
||||
p = mempcpy(p, buf, n);
|
||||
if (iscomplicated) *p++ = '"';
|
||||
*p = '\0';
|
||||
return p;
|
||||
} else {
|
||||
CloseCxxFilt();
|
||||
return NULL;
|
||||
buf[0] = '\n';
|
||||
iov[0].iov_base = s;
|
||||
iov[0].iov_len = sn;
|
||||
iov[1].iov_base = buf;
|
||||
iov[1].iov_len = 1;
|
||||
writev(g_cxxfilt.fds[0], iov, ARRAYLEN(iov));
|
||||
if ((rc = read(g_cxxfilt.fds[1], buf, sizeof(buf))) != -1) {
|
||||
got = rc;
|
||||
if (got >= 2 && buf[got - 1] == '\n') {
|
||||
if (buf[got - 2] == '\r') --got;
|
||||
--got;
|
||||
return CopySymbol(p, pn, buf, got);
|
||||
}
|
||||
}
|
||||
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
|
||||
* 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;
|
||||
size_t sn;
|
||||
sn = strlen(symbol);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
char *Demangle(char *, const char *);
|
||||
char *Demangle(char *, const char *, size_t);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -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) {
|
||||
p = DisColumn(DisAddr(d, p), p, ADDRLEN);
|
||||
p = HighStart(p, g_high.label);
|
||||
p = Demangle(p, name);
|
||||
p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
|
||||
p = HighEnd(p);
|
||||
*p++ = ':';
|
||||
*p = '\0';
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define DIS_MAX_SYMBOL_LENGTH 32
|
||||
|
||||
struct Dis {
|
||||
struct DisOps {
|
||||
size_t i, n;
|
||||
|
@ -50,6 +52,8 @@ struct Dis {
|
|||
char buf[512];
|
||||
};
|
||||
|
||||
extern bool g_disisprog_disable;
|
||||
|
||||
long Dis(struct Dis *, struct Machine *, uint64_t, uint64_t, int);
|
||||
long DisFind(struct Dis *, int64_t);
|
||||
void DisFree(struct Dis *);
|
||||
|
|
|
@ -143,21 +143,30 @@ static char *DisInt(char *p, int64_t x) {
|
|||
return p;
|
||||
}
|
||||
|
||||
static char *DisSym(struct Dis *d, char *p, int64_t addr, int64_t ip) {
|
||||
long sym;
|
||||
static char *DisSymImpl(struct Dis *d, char *p, int64_t x, long sym) {
|
||||
int64_t addend;
|
||||
const char *name;
|
||||
if ((sym = DisFindSym(d, ip)) != -1 && d->syms.p[sym].name) {
|
||||
addend = ip - d->syms.p[sym].addr;
|
||||
name = d->syms.stab + d->syms.p[sym].name;
|
||||
p = Demangle(p, name);
|
||||
if (addend) {
|
||||
*p++ = '+';
|
||||
p = DisInt(p, addend);
|
||||
}
|
||||
return p;
|
||||
addend = x - d->syms.p[sym].addr;
|
||||
name = d->syms.stab + d->syms.p[sym].name;
|
||||
p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
|
||||
if (addend) {
|
||||
*p++ = '+';
|
||||
p = DisInt(p, addend);
|
||||
}
|
||||
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 {
|
||||
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) {
|
||||
*p++ = '$';
|
||||
p = HighStart(p, g_high.literal);
|
||||
p = DisSym(d, p, addr, ip);
|
||||
p = DisSym(d, p, addr, addr, false);
|
||||
p = HighEnd(p);
|
||||
return p;
|
||||
}
|
||||
|
@ -196,27 +205,36 @@ static bool IsRealModrmAbsolute(uint32_t 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;
|
||||
const char *base, *index, *scale;
|
||||
p = DisSego(d, rde, p);
|
||||
base = index = scale = NULL;
|
||||
if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) ||
|
||||
IsRealModrmAbsolute(rde) ||
|
||||
(ModrmMod(rde) == 0b00 && ModrmRm(rde) == 0b100 &&
|
||||
SibBase(d->xedd) == 0b101)) {
|
||||
(Eamode(rde) != XED_MODE_REAL && ModrmMod(rde) == 0b00 &&
|
||||
ModrmRm(rde) == 0b100 && SibBase(d->xedd) == 0b101)) {
|
||||
disp = d->xedd->op.disp;
|
||||
if (IsRipRelative(rde)) {
|
||||
if (Mode(rde) == XED_MODE_LONG) {
|
||||
disp = RipRelative(d, disp);
|
||||
rela = true;
|
||||
} else {
|
||||
disp = Unrelative(rde, disp);
|
||||
rela = false;
|
||||
}
|
||||
} else if (IsRealModrmAbsolute(rde)) {
|
||||
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 (!SibExists(rde)) {
|
||||
DCHECK(!d->xedd->op.has_sib);
|
||||
|
@ -290,6 +308,13 @@ static char *DisM(struct Dis *d, uint32_t rde, char *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,
|
||||
char *f(struct Dis *, uint32_t, char *)) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
|
@ -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) {
|
||||
DisSego(d, rde, p);
|
||||
p = DisSego(d, rde, p);
|
||||
return DisSpecialAddr(d, rde, p, 6); // ds:si
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,13 @@
|
|||
#include "libc/elf/elf.h"
|
||||
#include "libc/elf/struct/sym.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "tool/build/lib/dis.h"
|
||||
|
||||
bool g_disisprog_disable;
|
||||
|
||||
static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) {
|
||||
if (a->addr != b->addr) {
|
||||
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 (!(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_FILE) continue;
|
||||
if (startswith(d->syms.stab + st[i].st_name, "v_")) continue;
|
||||
isabs = st[i].st_shndx == SHN_ABS;
|
||||
isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK;
|
||||
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.rank = -islocal + -isweak + -isabs + isprotected + 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);
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +100,7 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
|
|||
|
||||
bool DisIsProg(struct Dis *d, int64_t addr) {
|
||||
long i;
|
||||
if (g_disisprog_disable) return true;
|
||||
for (i = 0; i < d->loads.i; ++i) {
|
||||
if (addr >= d->loads.p[i].addr &&
|
||||
addr < d->loads.p[i].addr + d->loads.p[i].size) {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/log/check.h"
|
||||
#include "libc/nexgen32e/tinystrcmp.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
#include "tool/build/lib/dis.h"
|
||||
#include "tool/build/lib/high.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") &&
|
||||
!startswith(name, "set"))) {
|
||||
if (Osz(rde)) {
|
||||
if (Mode(rde) != XED_MODE_REAL) {
|
||||
if (ambiguous || Mode(rde) != XED_MODE_REAL) {
|
||||
*p++ = 'w';
|
||||
}
|
||||
} 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) {
|
||||
long i, n;
|
||||
char sbuf[300];
|
||||
char args[4][300];
|
||||
char sbuf[64];
|
||||
char args[4][256];
|
||||
char *s, *name, *state;
|
||||
bool hasarg, hasmodrm, hasregister, hasmemory;
|
||||
CHECK_EQ(0, (int)d->xedd->op.error);
|
||||
|
|
|
@ -48,7 +48,7 @@ COSMOPOLITAN_C_START_
|
|||
#define SibHasIndex(x) (SibIndex(x) != 4 || Rexx(x))
|
||||
#define SibHasBase(x, r) (SibBase(x) != 5 || ModrmMod(r))
|
||||
#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 {
|
||||
int64_t addr;
|
||||
|
|
|
@ -474,11 +474,8 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) {
|
|||
break;
|
||||
case kMachinePtyUtf8:
|
||||
if (ThomPikeCont(p[i])) {
|
||||
pty->u8 <<= 6;
|
||||
pty->u8 |= p[i] & 0b00111111;
|
||||
if (--pty->n8) {
|
||||
break;
|
||||
}
|
||||
pty->u8 = ThomPikeMerge(pty->u8, p[i]);
|
||||
if (--pty->n8) break;
|
||||
}
|
||||
SetMachinePtyCell(pty, pty->u8);
|
||||
pty->state = kMachinePtyAscii;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue