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
|
# The GNU Compiler Collection passes a lot of CFLAGS to the preprocessor
|
||||||
# (which we call CCFLAGS) but it should pass more; and we do just that.
|
# (which we call CCFLAGS) but it should pass more; and we do just that.
|
||||||
|
NOPG=0
|
||||||
FIRST=1
|
FIRST=1
|
||||||
OUTARG=0
|
OUTARG=0
|
||||||
INVISIBLE=0
|
INVISIBLE=0
|
||||||
|
@ -65,13 +66,16 @@ for x; do
|
||||||
-w)
|
-w)
|
||||||
set -- "$@" "$x" -D__W__
|
set -- "$@" "$x" -D__W__
|
||||||
;;
|
;;
|
||||||
|
-x-no-pg)
|
||||||
|
NOPG=1
|
||||||
|
;;
|
||||||
-pg)
|
-pg)
|
||||||
if [ $INVISIBLE -eq 0 ]; then
|
if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
|
||||||
set -- "$@" "$x" -D__PG__ # @see libc/macros.h
|
set -- "$@" "$x" -D__PG__ # @see libc/macros.h
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
-mfentry)
|
-mfentry)
|
||||||
if [ $INVISIBLE -eq 0 ]; then
|
if [ $NOPG -eq 0 ] && [ $INVISIBLE -eq 0 ]; then
|
||||||
set -- "$@" "$x" -D__MFENTRY__
|
set -- "$@" "$x" -D__MFENTRY__
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -63,6 +63,7 @@ FC = gfortran #/opt/cross9f/bin/x86_64-linux-musl-gfortran
|
||||||
AS = o/third_party/gcc/bin/x86_64-linux-musl-as
|
AS = o/third_party/gcc/bin/x86_64-linux-musl-as
|
||||||
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
|
CC = o/third_party/gcc/bin/x86_64-linux-musl-gcc
|
||||||
CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++
|
CXX = o/third_party/gcc/bin/x86_64-linux-musl-g++
|
||||||
|
CXXFILT = o/third_party/gcc/bin/x86_64-linux-musl-c++filt
|
||||||
LD = o/third_party/gcc/bin/x86_64-linux-musl-ld.bfd
|
LD = o/third_party/gcc/bin/x86_64-linux-musl-ld.bfd
|
||||||
AR = o/third_party/gcc/bin/x86_64-linux-musl-ar
|
AR = o/third_party/gcc/bin/x86_64-linux-musl-ar
|
||||||
NM = o/third_party/gcc/bin/x86_64-linux-musl-nm
|
NM = o/third_party/gcc/bin/x86_64-linux-musl-nm
|
||||||
|
@ -319,6 +320,7 @@ OBJECTIFY.c2x.c = $(CC) $(OBJECTIFY.c.flags) -std=c2x -Wextra -Werror -pedantic-
|
||||||
|
|
||||||
OBJECTIFY.real.c = \
|
OBJECTIFY.real.c = \
|
||||||
$(GCC) \
|
$(GCC) \
|
||||||
|
-x-no-pg \
|
||||||
$(OBJECTIFY.c.flags) \
|
$(OBJECTIFY.c.flags) \
|
||||||
-wrapper build/realify.sh \
|
-wrapper build/realify.sh \
|
||||||
-D__REAL_MODE__ \
|
-D__REAL_MODE__ \
|
||||||
|
@ -330,7 +332,12 @@ OBJECTIFY.real.c = \
|
||||||
-ffixed-r13 \
|
-ffixed-r13 \
|
||||||
-ffixed-r14 \
|
-ffixed-r14 \
|
||||||
-ffixed-r15 \
|
-ffixed-r15 \
|
||||||
|
-mno-red-zone \
|
||||||
-fcall-used-rbx \
|
-fcall-used-rbx \
|
||||||
|
-fno-jump-tables \
|
||||||
|
-fno-shrink-wrap \
|
||||||
|
-fno-schedule-insns2 \
|
||||||
|
-flive-range-shrinkage \
|
||||||
-fno-omit-frame-pointer \
|
-fno-omit-frame-pointer \
|
||||||
-momit-leaf-frame-pointer \
|
-momit-leaf-frame-pointer \
|
||||||
-mpreferred-stack-boundary=3 \
|
-mpreferred-stack-boundary=3 \
|
||||||
|
|
|
@ -15,17 +15,6 @@
|
||||||
# remove comments
|
# remove comments
|
||||||
s/[ \t][ \t]*#.*//
|
s/[ \t][ \t]*#.*//
|
||||||
|
|
||||||
# preserve hardcoded stack offsets
|
|
||||||
# bloats code size 13% compared to recomputing stack solution
|
|
||||||
s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/
|
|
||||||
s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/
|
|
||||||
s/ret\(q\|\)/retw\t$6/
|
|
||||||
s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
|
|
||||||
s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
|
|
||||||
|
|
||||||
# can be used instead if
|
|
||||||
# 1. functions have 6 args or fewer
|
|
||||||
# 2. long double parameters forceinline
|
|
||||||
#s/leave\(q\|\)/leavew/
|
#s/leave\(q\|\)/leavew/
|
||||||
#s/call\(q\|\)/callw/
|
#s/call\(q\|\)/callw/
|
||||||
#s/ret\(q\|\)/retw/
|
#s/ret\(q\|\)/retw/
|
||||||
|
@ -34,6 +23,14 @@ s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
|
||||||
#s/pushq\t\(.*\)/sub $6,%sp\n\tpush \1/
|
#s/pushq\t\(.*\)/sub $6,%sp\n\tpush \1/
|
||||||
#s/popq\t\(.*\)/pop \1\n\tadd $6,%sp/
|
#s/popq\t\(.*\)/pop \1\n\tadd $6,%sp/
|
||||||
|
|
||||||
|
# preserve hardcoded stack offsets
|
||||||
|
# bloats code size 13%
|
||||||
|
s/leave\(q\|\)/leavew\n\tadd\t$6,%sp/
|
||||||
|
s/call\(q\|\)\t/sub\t$6,%sp\n\tcallw\t/
|
||||||
|
s/ret\(q\|\)/retw\t$6/
|
||||||
|
s/pushq\t\(.*\)/sub\t$6,%sp\n\tpush\t\1/
|
||||||
|
s/popq\t\(.*\)/pop\t\1\n\tadd\t$6,%sp/
|
||||||
|
|
||||||
s/, /,/g
|
s/, /,/g
|
||||||
|
|
||||||
# 32-bitify
|
# 32-bitify
|
||||||
|
|
|
@ -72,7 +72,7 @@ o/$(MODE)/%.c11.o: %.c11.c; @ACTION=OBJECTIFY.c11 build/compile $(OBJECTIFY.c11.
|
||||||
o/$(MODE)/%.c2x.o: %.c2x.c; @ACTION=OBJECTIFY.c2x build/compile $(OBJECTIFY.c2x.c) $(OUTPUT_OPTION) $<
|
o/$(MODE)/%.c2x.o: %.c2x.c; @ACTION=OBJECTIFY.c2x build/compile $(OBJECTIFY.c2x.c) $(OUTPUT_OPTION) $<
|
||||||
o/$(MODE)/%.initabi.o: %.initabi.c; @ACTION=OBJECTIFY.init build/compile $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $<
|
o/$(MODE)/%.initabi.o: %.initabi.c; @ACTION=OBJECTIFY.init build/compile $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $<
|
||||||
o/$(MODE)/%.ncabi.o: %.ncabi.c; @ACTION=OBJECTIFY.nc build/compile $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $<
|
o/$(MODE)/%.ncabi.o: %.ncabi.c; @ACTION=OBJECTIFY.nc build/compile $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $<
|
||||||
o/tiny/%.real.o: %.real.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
|
o/$(MODE)/%.real.o: %.c; @ACTION=OBJECTIFY.real build/compile $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
|
||||||
o/$(MODE)/%.runs: o/$(MODE)/%; @ACTION=CHECK.runs TARGET=$< build/runcom $< $(TESTARGS) && touch $@
|
o/$(MODE)/%.runs: o/$(MODE)/%; @ACTION=CHECK.runs TARGET=$< build/runcom $< $(TESTARGS) && touch $@
|
||||||
o/$(MODE)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
|
o/$(MODE)/%.pkg:; @build/package $(OUTPUT_OPTION) $(addprefix -d,$(filter %.pkg,$^)) $(filter %.o,$^)
|
||||||
o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $<
|
o/$(MODE)/%.zip.o: %; @build/zipobj $(OUTPUT_OPTION) $<
|
||||||
|
|
|
@ -49,6 +49,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||||
LIBC_CONV \
|
LIBC_CONV \
|
||||||
LIBC_FMT \
|
LIBC_FMT \
|
||||||
LIBC_LOG \
|
LIBC_LOG \
|
||||||
|
LIBC_LOG_ASAN \
|
||||||
LIBC_MEM \
|
LIBC_MEM \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
LIBC_NT_KERNELBASE \
|
LIBC_NT_KERNELBASE \
|
||||||
|
|
|
@ -55,7 +55,7 @@ int main(int argc, char *argv[], char **envp) {
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
char fmt[64], **env;
|
char fmt[64], **env;
|
||||||
printf("\nArguments:\n");
|
printf("\nArguments:\n");
|
||||||
for (i = 0; i < argc; ++i) {
|
for (i = 0; i < g_argc; ++i) {
|
||||||
printf(" ☼ %s\n", argv[i]);
|
printf(" ☼ %s\n", argv[i]);
|
||||||
}
|
}
|
||||||
printf("\nEnvironment:\n");
|
printf("\nEnvironment:\n");
|
||||||
|
|
|
@ -71,27 +71,25 @@ static size_t KnuthMorrisPratt(m, T, W, n, S)
|
||||||
* @param needlelen is its character count
|
* @param needlelen is its character count
|
||||||
* @return pointer to first result or NULL if not found
|
* @return pointer to first result or NULL if not found
|
||||||
*/
|
*/
|
||||||
void *(memmem)(const void *haystack_, size_t haystacklen, const void *needle_,
|
void *(memmem)(const void *haystackp, size_t haystacklen, const void *needlep,
|
||||||
size_t needlelen) {
|
size_t needlelen) {
|
||||||
|
long *T;
|
||||||
|
size_t i;
|
||||||
const char *haystack, *needle, *h;
|
const char *haystack, *needle, *h;
|
||||||
haystack = haystack_;
|
needle = needlep;
|
||||||
needle = needle_;
|
haystack = haystackp;
|
||||||
if (needlelen > haystacklen) return NULL;
|
if (needlelen > haystacklen) return NULL;
|
||||||
if (!needlelen) return (/*unconst*/ void *)haystack;
|
if (!needlelen) return haystack;
|
||||||
h = memchr(haystack, *needle, haystacklen);
|
h = memchr(haystack, *needle, haystacklen);
|
||||||
if (!h || needlelen == 1) return (/*unconst*/ void *)h;
|
if (!h || needlelen == 1) return h;
|
||||||
haystacklen -= h - haystack;
|
haystacklen -= h - haystack;
|
||||||
long stacktmp[16];
|
if (needlelen < haystacklen && memcmp(h, needle, needlelen) == 0) {
|
||||||
void *freeme = NULL;
|
return h;
|
||||||
long *T = (needlelen + 1 < ARRAYLEN(stacktmp))
|
|
||||||
? &stacktmp[0]
|
|
||||||
: (freeme = malloc((needlelen + 1) * sizeof(long)));
|
|
||||||
KnuthMorrisPrattInit(needlelen, T, needle);
|
|
||||||
size_t i = KnuthMorrisPratt(needlelen, T, needle, haystacklen, h);
|
|
||||||
free(freeme);
|
|
||||||
if (i < haystacklen) {
|
|
||||||
return (/*unconst*/ char *)h + i * sizeof(char);
|
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
T = malloc((needlelen + 1) * sizeof(long));
|
||||||
|
KnuthMorrisPrattInit(needlelen, T, needle);
|
||||||
|
i = KnuthMorrisPratt(needlelen, T, needle, haystacklen, h);
|
||||||
|
free(T);
|
||||||
|
return i < haystacklen ? h + i * sizeof(char) : NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,7 @@
|
||||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||||
│ 02110-1301 USA │
|
│ 02110-1301 USA │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/bits.h"
|
|
||||||
#include "libc/calls/internal.h"
|
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/runtime/internal.h"
|
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ int clock_gettime(int clockid, struct timespec *out_ts) {
|
||||||
} else {
|
} else {
|
||||||
struct NtFileTime ft;
|
struct NtFileTime ft;
|
||||||
GetSystemTimeAsFileTime(&ft);
|
GetSystemTimeAsFileTime(&ft);
|
||||||
*out_ts = filetimetotimespec(ft);
|
*out_ts = FileTimeToTimeSpec(ft);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,9 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) {
|
||||||
: (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) |
|
: (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) |
|
||||||
((filetype == kNtFileTypeChar) ? S_IFCHR : 0) |
|
((filetype == kNtFileTypeChar) ? S_IFCHR : 0) |
|
||||||
((filetype == kNtFileTypePipe) ? S_IFIFO : 0))));
|
((filetype == kNtFileTypePipe) ? S_IFIFO : 0))));
|
||||||
st->st_atim = filetimetotimespec(wst.ftLastAccessFileTime);
|
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
|
||||||
st->st_mtim = filetimetotimespec(wst.ftLastWriteFileTime);
|
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
|
||||||
st->st_ctim = filetimetotimespec(wst.ftCreationFileTime);
|
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
|
||||||
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
|
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
|
||||||
st->st_blksize = PAGESIZE;
|
st->st_blksize = PAGESIZE;
|
||||||
st->st_dev = wst.dwVolumeSerialNumber;
|
st->st_dev = wst.dwVolumeSerialNumber;
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
static int g_pid;
|
static int g_pid;
|
||||||
|
|
||||||
static void __updatepid(void) {
|
static void __updatepid(void) {
|
||||||
atfork(__updatepid, NULL);
|
|
||||||
g_pid = __getpid();
|
g_pid = __getpid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,8 +46,9 @@ int __getpid(void) {
|
||||||
int getpid(void) {
|
int getpid(void) {
|
||||||
static bool once;
|
static bool once;
|
||||||
if (!once) {
|
if (!once) {
|
||||||
once = true;
|
|
||||||
__updatepid();
|
__updatepid();
|
||||||
|
atfork(__updatepid, NULL);
|
||||||
|
once = true;
|
||||||
}
|
}
|
||||||
return g_pid;
|
return g_pid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ textwindows int getrusage$nt(int who, struct rusage *usage) {
|
||||||
if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
|
if ((who == RUSAGE_SELF ? GetProcessTimes : GetThreadTimes)(
|
||||||
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
|
(who == RUSAGE_SELF ? GetCurrentProcess : GetCurrentThread)(),
|
||||||
&CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) {
|
&CreationFileTime, &ExitFileTime, &KernelFileTime, &UserFileTime)) {
|
||||||
filetimetotimeval(&usage->ru_utime, UserFileTime);
|
FileTimeToTimeVal(&usage->ru_utime, UserFileTime);
|
||||||
filetimetotimeval(&usage->ru_stime, KernelFileTime);
|
FileTimeToTimeVal(&usage->ru_stime, KernelFileTime);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return winerr();
|
return winerr();
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
int gettimeofday$nt(struct timeval *tv, struct timezone *tz) {
|
int gettimeofday$nt(struct timeval *tv, struct timezone *tz) {
|
||||||
struct NtFileTime ft;
|
struct NtFileTime ft;
|
||||||
GetSystemTimeAsFileTime(&ft);
|
GetSystemTimeAsFileTime(&ft);
|
||||||
filetimetotimeval(tv, ft);
|
FileTimeToTimeVal(tv, ft);
|
||||||
if (tz) memset(tz, 0, sizeof(*tz));
|
if (tz) memset(tz, 0, sizeof(*tz));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
* @param mode is an octal user/group/other permission signifier, that's
|
* @param mode is an octal user/group/other permission signifier, that's
|
||||||
* ignored if O_CREAT or O_TMPFILE weren't passed
|
* ignored if O_CREAT or O_TMPFILE weren't passed
|
||||||
* @return number needing close(), or -1 w/ errno
|
* @return number needing close(), or -1 w/ errno
|
||||||
* @asyncsignalsafe
|
* @note don't call open() from signal handlers
|
||||||
*/
|
*/
|
||||||
nodiscard int open(const char *file, int flags, ...) {
|
nodiscard int open(const char *file, int flags, ...) {
|
||||||
va_list va;
|
va_list va;
|
||||||
|
|
|
@ -155,7 +155,7 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
|
||||||
ap->sa_restorer = &__restore_rt;
|
ap->sa_restorer = &__restore_rt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rva >= 0) {
|
if (rva >= kSigactionMinRva) {
|
||||||
ap->sa_sigaction = (sigaction_f)__sigenter;
|
ap->sa_sigaction = (sigaction_f)__sigenter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ textwindows int utimensat$nt(int dirfd, const char *path,
|
||||||
} else if (ts[i].tv_nsec == UTIME_OMIT) {
|
} else if (ts[i].tv_nsec == UTIME_OMIT) {
|
||||||
ftp[i] = NULL;
|
ftp[i] = NULL;
|
||||||
} else {
|
} else {
|
||||||
ft[i] = timespectofiletime(ts[i]);
|
ft[i] = TimeSpecToFileTime(ts[i]);
|
||||||
ftp[i] = &ft[i];
|
ftp[i] = &ft[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,8 @@ textwindows int wait4$nt(int pid, int *opt_out_wstatus, int options,
|
||||||
memset(opt_out_rusage, 0, sizeof(*opt_out_rusage));
|
memset(opt_out_rusage, 0, sizeof(*opt_out_rusage));
|
||||||
GetProcessTimes(GetCurrentProcess(), &createfiletime, &exitfiletime,
|
GetProcessTimes(GetCurrentProcess(), &createfiletime, &exitfiletime,
|
||||||
&kernelfiletime, &userfiletime);
|
&kernelfiletime, &userfiletime);
|
||||||
filetimetotimeval(&opt_out_rusage->ru_utime, userfiletime);
|
FileTimeToTimeVal(&opt_out_rusage->ru_utime, userfiletime);
|
||||||
filetimetotimeval(&opt_out_rusage->ru_stime, kernelfiletime);
|
FileTimeToTimeVal(&opt_out_rusage->ru_stime, kernelfiletime);
|
||||||
}
|
}
|
||||||
return pid;
|
return pid;
|
||||||
} else if (options & WNOHANG) {
|
} else if (options & WNOHANG) {
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
* of the UNIX operation system signalled the end of modernity. Windows
|
* of the UNIX operation system signalled the end of modernity. Windows
|
||||||
* timestamps are living proof.
|
* timestamps are living proof.
|
||||||
*/
|
*/
|
||||||
#define MODERNITYSECONDS 11644473600
|
#define MODERNITYSECONDS 11644473600ull
|
||||||
#define HECTONANOSECONDS 10000000
|
#define HECTONANOSECONDS 10000000ull
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
@ -46,12 +46,13 @@ long strtol(const char *, char **, int)
|
||||||
│ cosmopolitan § conversion » time ─╬─│┼
|
│ cosmopolitan § conversion » time ─╬─│┼
|
||||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||||
|
|
||||||
struct timespec filetimetotimespec(struct NtFileTime);
|
int64_t DosDateTimeToUnix(unsigned, unsigned);
|
||||||
struct NtFileTime timespectofiletime(struct timespec);
|
struct timespec FileTimeToTimeSpec(struct NtFileTime);
|
||||||
struct NtFileTime timetofiletime(int64_t) nothrow pureconst;
|
struct NtFileTime TimeSpecToFileTime(struct timespec);
|
||||||
|
struct NtFileTime TimeToFileTime(int64_t) nothrow pureconst;
|
||||||
int64_t filetimetotime(struct NtFileTime) nothrow pureconst;
|
int64_t filetimetotime(struct NtFileTime) nothrow pureconst;
|
||||||
void filetimetotimeval(struct timeval *, struct NtFileTime) nothrow;
|
void FileTimeToTimeVal(struct timeval *, struct NtFileTime) nothrow;
|
||||||
struct NtFileTime timevaltofiletime(const struct timeval *) nosideeffect;
|
struct NtFileTime TimeValToFileTime(const struct timeval *) nosideeffect;
|
||||||
long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect;
|
long convertmicros(const struct timeval *, long) paramsnonnull() nosideeffect;
|
||||||
|
|
||||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||||
|
|
|
@ -52,6 +52,7 @@ $(LIBC_CONV_A).pkg: \
|
||||||
$(LIBC_CONV_A_OBJS) \
|
$(LIBC_CONV_A_OBJS) \
|
||||||
$(foreach x,$(LIBC_CONV_A_DIRECTDEPS),$($(x)_A).pkg)
|
$(foreach x,$(LIBC_CONV_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||||
|
|
||||||
|
o/$(MODE)/libc/conv/dosdatetimetounix.o \
|
||||||
o/$(MODE)/libc/conv/itoa64radix10.greg.o \
|
o/$(MODE)/libc/conv/itoa64radix10.greg.o \
|
||||||
o/$(MODE)/libc/conv/timetofiletime.o \
|
o/$(MODE)/libc/conv/timetofiletime.o \
|
||||||
o/$(MODE)/libc/conv/filetimetotime.o \
|
o/$(MODE)/libc/conv/filetimetotime.o \
|
||||||
|
|
|
@ -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.
|
* Converts Windows COBOL timestamp to UNIX epoch in nanoseconds.
|
||||||
*/
|
*/
|
||||||
struct timespec filetimetotimespec(struct NtFileTime ft) {
|
struct timespec FileTimeToTimeSpec(struct NtFileTime ft) {
|
||||||
uint64_t x;
|
uint64_t x;
|
||||||
x = ft.dwHighDateTime;
|
x = ft.dwHighDateTime;
|
||||||
x <<= 32;
|
x <<= 32;
|
||||||
x |= ft.dwLowDateTime;
|
x |= ft.dwLowDateTime;
|
||||||
x -= MODERNITYSECONDS;
|
return (struct timespec){x / HECTONANOSECONDS - MODERNITYSECONDS,
|
||||||
return (struct timespec){x / HECTONANOSECONDS, x % HECTONANOSECONDS * 100};
|
x % HECTONANOSECONDS * 100};
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,11 @@
|
||||||
#include "libc/conv/conv.h"
|
#include "libc/conv/conv.h"
|
||||||
#include "libc/nt/struct/filetime.h"
|
#include "libc/nt/struct/filetime.h"
|
||||||
|
|
||||||
void filetimetotimeval(struct timeval *tv, struct NtFileTime ft) {
|
void FileTimeToTimeVal(struct timeval *tv, struct NtFileTime ft) {
|
||||||
uint64_t t = (uint64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
|
uint64_t x;
|
||||||
uint64_t x = t - MODERNITYSECONDS * HECTONANOSECONDS;
|
x = ft.dwHighDateTime;
|
||||||
tv->tv_sec = x / HECTONANOSECONDS;
|
x <<= 32;
|
||||||
|
x |= ft.dwLowDateTime;
|
||||||
|
tv->tv_sec = x / HECTONANOSECONDS - MODERNITYSECONDS;
|
||||||
tv->tv_usec = x % HECTONANOSECONDS / 10;
|
tv->tv_usec = x % HECTONANOSECONDS / 10;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* Converts UNIX nanosecond timestamp to Windows COBOL timestamp.
|
||||||
*/
|
*/
|
||||||
struct NtFileTime timespectofiletime(struct timespec ts) {
|
struct NtFileTime TimeSpecToFileTime(struct timespec ts) {
|
||||||
uint64_t x;
|
uint64_t x;
|
||||||
x = MODERNITYSECONDS;
|
x = MODERNITYSECONDS;
|
||||||
x += ts.tv_sec * HECTONANOSECONDS;
|
x += ts.tv_sec * HECTONANOSECONDS;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "libc/conv/conv.h"
|
#include "libc/conv/conv.h"
|
||||||
#include "libc/nt/struct/filetime.h"
|
#include "libc/nt/struct/filetime.h"
|
||||||
|
|
||||||
struct NtFileTime timetofiletime(int64_t t) {
|
struct NtFileTime TimeToFileTime(int64_t t) {
|
||||||
uint64_t t2 = (t + MODERNITYSECONDS) * HECTONANOSECONDS;
|
uint64_t t2 = (t + MODERNITYSECONDS) * HECTONANOSECONDS;
|
||||||
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
|
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include "libc/nt/struct/filetime.h"
|
#include "libc/nt/struct/filetime.h"
|
||||||
#include "libc/time/time.h"
|
#include "libc/time/time.h"
|
||||||
|
|
||||||
struct NtFileTime timevaltofiletime(const struct timeval *tv) {
|
struct NtFileTime TimeValToFileTime(const struct timeval *tv) {
|
||||||
uint64_t t2 = tv->tv_sec * HECTONANOSECONDS + tv->tv_usec * 10 +
|
uint64_t t2 = tv->tv_sec * HECTONANOSECONDS + tv->tv_usec * 10 +
|
||||||
MODERNITYSECONDS * HECTONANOSECONDS;
|
MODERNITYSECONDS * HECTONANOSECONDS;
|
||||||
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
|
return (struct NtFileTime){(uint32_t)t2, (uint32_t)(t2 >> 32)};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_DOS_H_
|
#ifndef COSMOPOLITAN_LIBC_DOS_H_
|
||||||
#define COSMOPOLITAN_LIBC_DOS_H_
|
#define COSMOPOLITAN_LIBC_DOS_H_
|
||||||
|
#include "libc/macros.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview MS-DOS Data Structures.
|
* @fileoverview MS-DOS Data Structures.
|
||||||
|
|
|
@ -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 strerror_r(int, char *, size_t) nothrow nocallback;
|
||||||
int palandprintf(void *, void *, const char *, va_list) hidden;
|
int palandprintf(void *, void *, const char *, va_list) hidden;
|
||||||
char *itoa(int, char *, int) compatfn;
|
char *itoa(int, char *, int) compatfn;
|
||||||
|
char *fcvt(double, int, int *, int *);
|
||||||
|
char *ecvt(double, int, int *, int *);
|
||||||
|
|
||||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||||
│ cosmopolitan § string formatting » optimizations ─╬─│┼
|
│ cosmopolitan § string formatting » optimizations ─╬─│┼
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
* @see xdtoa() for higher precision at the cost of bloat
|
* @see xdtoa() for higher precision at the cost of bloat
|
||||||
* @see palandprintf() which is intended caller
|
* @see palandprintf() which is intended caller
|
||||||
*/
|
*/
|
||||||
int ftoa(int out(int, void *), void *arg, long double value, int prec,
|
int ftoa(int out(long, void *), void *arg, long double value, int prec,
|
||||||
unsigned long width, unsigned long flags) {
|
unsigned long width, unsigned long flags) {
|
||||||
long whole, frac;
|
long whole, frac;
|
||||||
long double tmp, diff;
|
long double tmp, diff;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *);
|
uintmax_t __udivmodti4(uintmax_t, uintmax_t, uintmax_t *);
|
||||||
|
|
||||||
static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len,
|
static int ntoaformat(int out(long, void *), void *arg, char *buf, unsigned len,
|
||||||
bool negative, unsigned log2base, unsigned prec,
|
bool negative, unsigned log2base, unsigned prec,
|
||||||
unsigned width, unsigned char flags) {
|
unsigned width, unsigned char flags) {
|
||||||
unsigned i, idx;
|
unsigned i, idx;
|
||||||
|
@ -103,7 +103,7 @@ static int ntoaformat(int out(int, void *), void *arg, char *buf, unsigned len,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ntoa2(int out(int, void *), void *arg, uintmax_t value, bool neg,
|
int ntoa2(int out(long, void *), void *arg, uintmax_t value, bool neg,
|
||||||
unsigned log2base, unsigned prec, unsigned width, unsigned flags,
|
unsigned log2base, unsigned prec, unsigned width, unsigned flags,
|
||||||
const char *alphabet) {
|
const char *alphabet) {
|
||||||
uintmax_t remainder;
|
uintmax_t remainder;
|
||||||
|
@ -135,7 +135,7 @@ int ntoa2(int out(int, void *), void *arg, uintmax_t value, bool neg,
|
||||||
return ntoaformat(out, arg, buf, len, neg, log2base, prec, width, flags);
|
return ntoaformat(out, arg, buf, len, neg, log2base, prec, width, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ntoa(int out(int, void *), void *arg, va_list va, unsigned char signbit,
|
int ntoa(int out(long, void *), void *arg, va_list va, unsigned char signbit,
|
||||||
unsigned long log2base, unsigned long prec, unsigned long width,
|
unsigned long log2base, unsigned long prec, unsigned long width,
|
||||||
unsigned char flags, const char *lang) {
|
unsigned char flags, const char *lang) {
|
||||||
bool neg;
|
bool neg;
|
||||||
|
|
|
@ -75,7 +75,7 @@ static int ppatoi(const char **str) {
|
||||||
* - `%Lf` long double
|
* - `%Lf` long double
|
||||||
* - `%p` pointer (48-bit hexadecimal)
|
* - `%p` pointer (48-bit hexadecimal)
|
||||||
*
|
*
|
||||||
* Length Modifiers
|
* Size Modifiers
|
||||||
*
|
*
|
||||||
* - `%hhd` char (8-bit)
|
* - `%hhd` char (8-bit)
|
||||||
* - `%hd` short (16-bit)
|
* - `%hd` short (16-bit)
|
||||||
|
@ -89,7 +89,11 @@ static int ppatoi(const char **str) {
|
||||||
* - `%08d` fixed columns w/ zero leftpadding
|
* - `%08d` fixed columns w/ zero leftpadding
|
||||||
* - `%8d` fixed columns w/ space leftpadding
|
* - `%8d` fixed columns w/ space leftpadding
|
||||||
* - `%*s` variable column string (thompson-pike)
|
* - `%*s` variable column string (thompson-pike)
|
||||||
* - `%.*s` variable column data (ignore nul terminator)
|
*
|
||||||
|
* Precision Modifiers
|
||||||
|
*
|
||||||
|
* - `%.8s` supplied character length (ignore nul terminator)
|
||||||
|
* - `%.*s` supplied character length argument (ignore nul terminator)
|
||||||
*
|
*
|
||||||
* Formatting Modifiers
|
* Formatting Modifiers
|
||||||
*
|
*
|
||||||
|
@ -112,9 +116,9 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
|
||||||
long double ldbl;
|
long double ldbl;
|
||||||
wchar_t charbuf[3];
|
wchar_t charbuf[3];
|
||||||
const char *alphabet;
|
const char *alphabet;
|
||||||
int (*out)(int, void *);
|
int (*out)(long, void *);
|
||||||
unsigned char signbit, log2base;
|
unsigned char signbit, log2base;
|
||||||
int w, rc, flags, width, lasterr, precision;
|
int w, flags, width, lasterr, precision;
|
||||||
|
|
||||||
lasterr = errno;
|
lasterr = errno;
|
||||||
out = fn ? fn : (int (*)(int, void *))missingno;
|
out = fn ? fn : (int (*)(int, void *))missingno;
|
||||||
|
@ -255,7 +259,8 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
|
||||||
case 'u': {
|
case 'u': {
|
||||||
flags &= ~FLAGS_HASH; /* no hash for dec format */
|
flags &= ~FLAGS_HASH; /* no hash for dec format */
|
||||||
DoNumber:
|
DoNumber:
|
||||||
if (weaken(ntoa)(out, arg, va, signbit, log2base, precision, width,
|
if (!weaken(ntoa) ||
|
||||||
|
weaken(ntoa)(out, arg, va, signbit, log2base, precision, width,
|
||||||
flags, alphabet) == -1) {
|
flags, alphabet) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -269,7 +274,8 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
|
||||||
} else {
|
} else {
|
||||||
ldbl = va_arg(va, double);
|
ldbl = va_arg(va, double);
|
||||||
}
|
}
|
||||||
if (weaken(ftoa)(out, arg, ldbl, precision, width, flags) == -1) {
|
if (!weaken(ftoa) ||
|
||||||
|
weaken(ftoa)(out, arg, ldbl, precision, width, flags) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -297,8 +303,10 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) {
|
||||||
case 's':
|
case 's':
|
||||||
p = va_arg(va, void *);
|
p = va_arg(va, void *);
|
||||||
showstr:
|
showstr:
|
||||||
rc = weaken(stoa)(out, arg, p, flags, precision, width, signbit, qchar);
|
if (!weaken(stoa) || weaken(stoa)(out, arg, p, flags, precision, width,
|
||||||
if (rc == -1) return -1;
|
signbit, qchar) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '%':
|
case '%':
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
int spacepad(int(int, void *), void *, unsigned long) hidden;
|
int spacepad(int(long, void *), void *, unsigned long) hidden;
|
||||||
int ftoa(int(int, void *), void *, long double, int, unsigned long,
|
int ftoa(int(long, void *), void *, long double, int, unsigned long,
|
||||||
unsigned long) hidden;
|
unsigned long) hidden;
|
||||||
int stoa(int(int, void *), void *, void *, unsigned long, unsigned long,
|
int stoa(int(long, void *), void *, void *, unsigned long, unsigned long,
|
||||||
unsigned long, unsigned char, unsigned char) hidden;
|
unsigned long, unsigned char, unsigned char) hidden;
|
||||||
int ntoa(int(int, void *), void *, va_list, unsigned char, unsigned long,
|
int ntoa(int(long, void *), void *, va_list, unsigned char, unsigned long,
|
||||||
unsigned long, unsigned long, unsigned char, const char *) hidden;
|
unsigned long, unsigned long, unsigned char, const char *) hidden;
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/fmt/palandprintf.h"
|
#include "libc/fmt/palandprintf.h"
|
||||||
|
|
||||||
int spacepad(int out(int, void *), void *arg, unsigned long n) {
|
int spacepad(int out(long, void *), void *arg, unsigned long n) {
|
||||||
int i, rc;
|
int i, rc;
|
||||||
for (rc = i = 0; i < n; ++i) rc |= out(' ', arg);
|
for (rc = i = 0; i < n; ++i) rc |= out(' ', arg);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
194
libc/fmt/stoa.c
194
libc/fmt/stoa.c
|
@ -17,47 +17,54 @@
|
||||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||||
│ 02110-1301 USA │
|
│ 02110-1301 USA │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/bits/bits.h"
|
|
||||||
#include "libc/bits/safemacros.h"
|
|
||||||
#include "libc/bits/weaken.h"
|
#include "libc/bits/weaken.h"
|
||||||
#include "libc/escape/escape.h"
|
#include "libc/escape/escape.h"
|
||||||
#include "libc/fmt/fmt.h"
|
|
||||||
#include "libc/fmt/paland.inc"
|
#include "libc/fmt/paland.inc"
|
||||||
#include "libc/fmt/palandprintf.h"
|
#include "libc/fmt/palandprintf.h"
|
||||||
#include "libc/nexgen32e/tinystrlen.h"
|
#include "libc/nexgen32e/tinystrlen.h"
|
||||||
#include "libc/str/internal.h"
|
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/str/tpdecode.h"
|
#include "libc/str/thompike.h"
|
||||||
#include "libc/str/tpencode.h"
|
#include "libc/str/tpenc.h"
|
||||||
|
#include "libc/str/utf16.h"
|
||||||
#include "libc/unicode/unicode.h"
|
#include "libc/unicode/unicode.h"
|
||||||
|
|
||||||
forceinline unsigned long tpiencode(wint_t wc) {
|
typedef int (*emit_f)(int (*)(long, void *), void *, wint_t);
|
||||||
char buf[8];
|
|
||||||
memset(buf, 0, sizeof(buf));
|
static int StoaEmitByte(int f(long, void *), void *a, wint_t c) {
|
||||||
tpencode(buf, sizeof(buf), wc, false);
|
return f(c, a);
|
||||||
return read64le(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline int emitwc(int out(int, void *), void *arg, unsigned flags,
|
static int StoaEmitWordEncodedString(int f(long, void *), void *a, uint64_t w) {
|
||||||
wint_t wc) {
|
|
||||||
unsigned long pending;
|
|
||||||
if (flags & FLAGS_QUOTE) {
|
|
||||||
if (wc > 127) {
|
|
||||||
pending = tpiencode(wc);
|
|
||||||
} else {
|
|
||||||
pending = cescapec(wc);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pending = tpiencode(wc);
|
|
||||||
}
|
|
||||||
do {
|
do {
|
||||||
if (out(pending & 0xff, arg) == -1) return -1;
|
if (f(w & 0xff, a) == -1) {
|
||||||
} while ((pending >>= 8));
|
return -1;
|
||||||
|
}
|
||||||
|
} while ((w >>= 8));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
|
static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) {
|
||||||
char ch, unsigned char signbit) {
|
if (0 <= c && c <= 127) {
|
||||||
|
return f(c, a);
|
||||||
|
} else {
|
||||||
|
return StoaEmitWordEncodedString(f, a, tpenc(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int StoaEmitQuoted(int f(long, void *), void *a, wint_t c) {
|
||||||
|
if (0 <= c && c <= 127) {
|
||||||
|
return StoaEmitWordEncodedString(f, a, cescapec(c));
|
||||||
|
} else {
|
||||||
|
return StoaEmitWordEncodedString(f, a, tpenc(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int StoaEmitVisualized(int f(long, void *), void *a, wint_t c) {
|
||||||
|
return StoaEmitUnicode(f, a, (*weaken(kCp437))[c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int StoaEmitQuote(int out(long, void *), void *arg, unsigned flags,
|
||||||
|
char ch, unsigned char signbit) {
|
||||||
if (flags & FLAGS_REPR) {
|
if (flags & FLAGS_REPR) {
|
||||||
if (signbit == 63) {
|
if (signbit == 63) {
|
||||||
if (out('L', arg) == -1) return -1;
|
if (out('L', arg) == -1) return -1;
|
||||||
|
@ -78,13 +85,15 @@ forceinline int emitquote(int out(int, void *), void *arg, unsigned flags,
|
||||||
*
|
*
|
||||||
* @see palandprintf()
|
* @see palandprintf()
|
||||||
*/
|
*/
|
||||||
int stoa(int out(int, void *), void *arg, void *data, unsigned long flags,
|
int stoa(int out(long, void *), void *arg, void *data, unsigned long flags,
|
||||||
unsigned long precision, unsigned long width, unsigned char signbit,
|
unsigned long precision, unsigned long width, unsigned char signbit,
|
||||||
unsigned char qchar) {
|
unsigned char qchar) {
|
||||||
char *p;
|
char *p;
|
||||||
wint_t wc;
|
wint_t wc;
|
||||||
unsigned w, c;
|
unsigned n;
|
||||||
bool ignorenul;
|
emit_f emit;
|
||||||
|
bool justdobytes;
|
||||||
|
unsigned w, c, pad;
|
||||||
|
|
||||||
p = data;
|
p = data;
|
||||||
if (!p) {
|
if (!p) {
|
||||||
|
@ -93,89 +102,102 @@ int stoa(int out(int, void *), void *arg, void *data, unsigned long flags,
|
||||||
flags |= FLAGS_NOQUOTE;
|
flags |= FLAGS_NOQUOTE;
|
||||||
signbit = 0;
|
signbit = 0;
|
||||||
} else {
|
} else {
|
||||||
if (emitquote(out, arg, flags, qchar, signbit) == -1) return -1;
|
if (StoaEmitQuote(out, arg, flags, qchar, signbit) == -1) return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
w = precision ? precision : -1;
|
if (!(flags & FLAGS_PRECISION)) {
|
||||||
|
if (signbit == 63) {
|
||||||
|
precision = tinywcsnlen((const wchar_t *)p, -1);
|
||||||
|
} else if (signbit == 15) {
|
||||||
|
precision = tinystrnlen16((const char16_t *)p, -1);
|
||||||
|
} else {
|
||||||
|
precision = strlen(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pad = 0;
|
||||||
if (width) {
|
if (width) {
|
||||||
|
w = precision;
|
||||||
if (signbit == 63) {
|
if (signbit == 63) {
|
||||||
if (weaken(wcsnwidth)) {
|
if (weaken(wcsnwidth)) {
|
||||||
w = weaken(wcsnwidth)((const wchar_t *)p, w);
|
w = weaken(wcsnwidth)((const wchar_t *)p, precision);
|
||||||
} else {
|
|
||||||
w = tinywcsnlen((const wchar_t *)p, w);
|
|
||||||
}
|
}
|
||||||
} else if (signbit == 15) {
|
} else if (signbit == 15) {
|
||||||
if (weaken(strnwidth16)) {
|
if (weaken(strnwidth16)) {
|
||||||
w = weaken(strnwidth16)((const char16_t *)p, w);
|
w = weaken(strnwidth16)((const char16_t *)p, precision);
|
||||||
} else {
|
|
||||||
w = tinystrnlen16((const char16_t *)p, w);
|
|
||||||
}
|
}
|
||||||
} else if (weaken(strnwidth)) {
|
} else if (weaken(strnwidth)) {
|
||||||
w = weaken(strnwidth)(p, w);
|
w = weaken(strnwidth)(p, precision);
|
||||||
} else {
|
}
|
||||||
w = strnlen(p, w);
|
if (w < width) {
|
||||||
|
pad = width - w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FLAGS_PRECISION) {
|
if (pad && !(flags & FLAGS_LEFT)) {
|
||||||
w = MIN(w, precision);
|
if (spacepad(out, arg, pad) == -1) return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w < width && !(flags & FLAGS_LEFT)) {
|
justdobytes = false;
|
||||||
if (spacepad(out, arg, width - w) == -1) return -1;
|
if (signbit == 15 || signbit == 63) {
|
||||||
|
if (flags & FLAGS_QUOTE) {
|
||||||
|
emit = StoaEmitQuoted;
|
||||||
|
} else {
|
||||||
|
emit = StoaEmitUnicode;
|
||||||
|
}
|
||||||
|
} else if ((flags & FLAGS_HASH) && weaken(kCp437)) {
|
||||||
|
justdobytes = true;
|
||||||
|
emit = StoaEmitVisualized;
|
||||||
|
} else if (flags & FLAGS_QUOTE) {
|
||||||
|
emit = StoaEmitQuoted;
|
||||||
|
} else {
|
||||||
|
justdobytes = true;
|
||||||
|
emit = StoaEmitByte;
|
||||||
}
|
}
|
||||||
|
|
||||||
ignorenul = (flags & FLAGS_PRECISION) && (flags & (FLAGS_HASH | FLAGS_QUOTE));
|
if (justdobytes) {
|
||||||
for (; !(flags & FLAGS_PRECISION) || precision; --precision) {
|
while (precision--) {
|
||||||
if (signbit == 15) {
|
wc = *p++ & 0xff;
|
||||||
if ((wc = *(const char16_t *)p) || ignorenul) {
|
if (emit(out, arg, wc) == -1) return -1;
|
||||||
if ((1 <= wc && wc <= 0xD7FF)) {
|
}
|
||||||
|
} else {
|
||||||
|
while (precision--) {
|
||||||
|
if (signbit == 15) {
|
||||||
|
wc = *(const char16_t *)p;
|
||||||
|
if (IsUcs2(wc)) {
|
||||||
p += sizeof(char16_t);
|
p += sizeof(char16_t);
|
||||||
} else if ((wc & UTF16_MASK) == UTF16_CONT) {
|
} else if (IsUtf16Cont(wc)) {
|
||||||
p += sizeof(char16_t);
|
p += sizeof(char16_t);
|
||||||
continue;
|
continue;
|
||||||
|
} else if (!precision) {
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
char16_t buf[4] = {wc};
|
--precision;
|
||||||
if (!(flags & FLAGS_PRECISION) || precision > 1) {
|
wc = MergeUtf16(wc, *(const char16_t *)p);
|
||||||
buf[1] = ((const char16_t *)p)[1];
|
}
|
||||||
|
} else if (signbit == 63) {
|
||||||
|
wc = *(const wint_t *)p;
|
||||||
|
p += sizeof(wint_t);
|
||||||
|
if (!wc) break;
|
||||||
|
} else {
|
||||||
|
wc = *p++ & 0xff;
|
||||||
|
if (!isascii(wc)) {
|
||||||
|
if (ThomPikeCont(wc)) continue;
|
||||||
|
n = ThomPikeLen(wc) - 1;
|
||||||
|
wc = ThomPikeByte(wc);
|
||||||
|
if (n > precision) break;
|
||||||
|
precision -= n;
|
||||||
|
while (n--) {
|
||||||
|
wc = ThomPikeMerge(wc, *p++);
|
||||||
}
|
}
|
||||||
p += max(1, getutf16((const char16_t *)p, &wc)) * sizeof(char16_t);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (signbit == 63) {
|
|
||||||
wc = *(const wint_t *)p;
|
|
||||||
p += sizeof(wint_t);
|
|
||||||
if (!wc) break;
|
|
||||||
} else if (flags & FLAGS_HASH) {
|
|
||||||
c = *p & 0xff;
|
|
||||||
if (!c && !ignorenul) break;
|
|
||||||
wc = (*weaken(kCp437))[c];
|
|
||||||
p++;
|
|
||||||
} else {
|
|
||||||
if ((wc = *p & 0xff) || ignorenul) {
|
|
||||||
if (1 <= wc && wc <= 0x7f) {
|
|
||||||
++p;
|
|
||||||
} else if (iscont(wc & 0xff)) {
|
|
||||||
++p;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
char buf[8];
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
memcpy(buf, p,
|
|
||||||
!(flags & FLAGS_PRECISION) ? 7 : MIN(7, precision - 1));
|
|
||||||
p += max(1, tpdecode(p, &wc));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (emit(out, arg, wc) == -1) return -1;
|
||||||
}
|
}
|
||||||
if (emitwc(out, arg, flags, wc) == -1) return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w <= width && (flags & FLAGS_LEFT)) {
|
if (pad && (flags & FLAGS_LEFT)) {
|
||||||
if (spacepad(out, arg, width - w) == -1) return -1;
|
if (spacepad(out, arg, pad) == -1) return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {
|
if (!(flags & FLAGS_NOQUOTE) && (flags & FLAGS_REPR)) {
|
||||||
|
|
|
@ -25,17 +25,13 @@
|
||||||
|
|
||||||
struct SprintfStr {
|
struct SprintfStr {
|
||||||
char *p;
|
char *p;
|
||||||
size_t i, n;
|
size_t i;
|
||||||
|
size_t n;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
|
static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
|
||||||
if (str->i < str->n) {
|
if (str->i < str->n) str->p[str->i] = c;
|
||||||
if (str->p) str->p[str->i] = c;
|
str->i++;
|
||||||
str->i++;
|
|
||||||
} else {
|
|
||||||
if (!IsTrustworthy() && str->i >= INT_MAX) abort();
|
|
||||||
str->i++;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,15 +44,11 @@ static int vsnprintfputchar(unsigned char c, struct SprintfStr *str) {
|
||||||
* @return number of bytes written, excluding the NUL terminator; or,
|
* @return number of bytes written, excluding the NUL terminator; or,
|
||||||
* if the output buffer wasn't passed, or was too short, then the
|
* if the output buffer wasn't passed, or was too short, then the
|
||||||
* number of characters that *would* have been written is returned
|
* number of characters that *would* have been written is returned
|
||||||
* @throw EOVERFLOW when a formatted field exceeds its limit, which can
|
|
||||||
* be checked by setting errno to 0 before calling
|
|
||||||
* @see palandprintf() and printf() for detailed documentation
|
* @see palandprintf() and printf() for detailed documentation
|
||||||
*/
|
*/
|
||||||
int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) {
|
int(vsnprintf)(char *buf, size_t size, const char *fmt, va_list va) {
|
||||||
struct SprintfStr str = {buf, 0, size};
|
struct SprintfStr str = {buf, 0, size};
|
||||||
palandprintf(vsnprintfputchar, &str, fmt, va);
|
palandprintf(vsnprintfputchar, &str, fmt, va);
|
||||||
if (str.p && str.n) {
|
if (str.n) str.p[min(str.i, str.n - 1)] = '\0';
|
||||||
str.p[min(str.i, str.n - 1)] = '\0';
|
|
||||||
}
|
|
||||||
return str.i;
|
return str.i;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BIGPAGESIZE 0x200000
|
#define BIGPAGESIZE 0x200000
|
||||||
#define STACKSIZE 0x80000 /* todo: zlib's fault? */
|
#define STACKSIZE 0x10000
|
||||||
#define FRAMESIZE 0x10000 /* 8086 */
|
#define FRAMESIZE 0x10000 /* 8086 */
|
||||||
#define PAGESIZE 0x1000 /* i386+ */
|
#define PAGESIZE 0x1000 /* i386+ */
|
||||||
#define BUFSIZ 0x1000 /* best stdio default */
|
#define BUFSIZ 0x1000 /* best stdio default */
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
PKGS += LIBC_LINUX
|
PKGS += LIBC_LINUX
|
||||||
|
|
||||||
LIBC_LINUX_HDRS = $(filter %.h,$(LIBC_FILES))
|
LIBC_LINUX_HDRS = $(filter %.h,$(LIBC_LINUX_FILES))
|
||||||
LIBC_LINUX_FILES := $(wildcard libc/linux/*)
|
LIBC_LINUX_FILES := $(wildcard libc/linux/*)
|
||||||
LIBC_LINUX_CHECKS = $(LIBC_LINUX_HDRS:%=o/$(MODE)/%.ok)
|
LIBC_LINUX_CHECKS = $(LIBC_LINUX_HDRS:%=o/$(MODE)/%.ok)
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
#include "third_party/dlmalloc/dlmalloc.h"
|
#include "third_party/dlmalloc/dlmalloc.h"
|
||||||
|
|
||||||
|
STATIC_YOINK("_init_asan");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Cosmopolitan Address Sanitizer Runtime.
|
* @fileoverview Cosmopolitan Address Sanitizer Runtime.
|
||||||
*
|
*
|
||||||
|
|
|
@ -31,6 +31,10 @@
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/auxv.h"
|
#include "libc/sysv/consts/auxv.h"
|
||||||
|
|
||||||
|
STATIC_YOINK("ntoa");
|
||||||
|
STATIC_YOINK("stoa");
|
||||||
|
STATIC_YOINK("ftoa");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles failure of CHECK_xx() macros.
|
* Handles failure of CHECK_xx() macros.
|
||||||
*/
|
*/
|
||||||
|
@ -47,30 +51,30 @@ relegated void __check_fail(const char *suffix, const char *opstr,
|
||||||
if (!memccpy(sufbuf, suffix, '\0', sizeof(sufbuf))) strcpy(sufbuf, "?");
|
if (!memccpy(sufbuf, suffix, '\0', sizeof(sufbuf))) strcpy(sufbuf, "?");
|
||||||
strtoupper(sufbuf);
|
strtoupper(sufbuf);
|
||||||
|
|
||||||
fprintf(stderr,
|
(fprintf)(stderr,
|
||||||
"check failed\n"
|
"check failed\n"
|
||||||
"\tCHECK_%s(%s, %s);\n"
|
"\tCHECK_%s(%s, %s);\n"
|
||||||
"\t\t → %#lx (%s)\n"
|
"\t\t → %#lx (%s)\n"
|
||||||
"\t\t%s %#lx (%s)\n",
|
"\t\t%s %#lx (%s)\n",
|
||||||
sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr);
|
sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr);
|
||||||
|
|
||||||
if (!isempty(fmt)) {
|
if (!isempty(fmt)) {
|
||||||
fputc('\t', stderr);
|
fputc('\t', stderr);
|
||||||
va_start(va, fmt);
|
va_start(va, fmt);
|
||||||
vfprintf(stderr, fmt, va);
|
(vfprintf)(stderr, fmt, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
fputc('\n', stderr);
|
fputc('\n', stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
|
(fprintf)(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE,
|
||||||
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
|
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
|
||||||
|
|
||||||
for (i = 1; i < g_argc; ++i) {
|
for (i = 1; i < g_argc; ++i) {
|
||||||
fprintf(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : "");
|
(fprintf)(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsTiny() && lasterr == ENOMEM) {
|
if (!IsTiny() && lasterr == ENOMEM) {
|
||||||
fprintf(stderr, "\n");
|
(fprintf)(stderr, "\n");
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
PrintMemoryIntervals(fileno(stderr), &_mmi);
|
PrintMemoryIntervals(fileno(stderr), &_mmi);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* Log level for compile-time DCE.
|
* Log level for compile-time DCE.
|
||||||
*/
|
*/
|
||||||
#ifndef LOGGABLELEVEL
|
#ifndef LOGGABLELEVEL
|
||||||
#ifndef NDEBUG
|
#ifndef TINY
|
||||||
#define LOGGABLELEVEL kLogDebug
|
#define LOGGABLELEVEL kLogDebug
|
||||||
/* #elif IsTiny() */
|
/* #elif IsTiny() */
|
||||||
/* #define LOGGABLELEVEL kLogInfo */
|
/* #define LOGGABLELEVEL kLogInfo */
|
||||||
|
|
|
@ -65,6 +65,11 @@ $(LIBC_LOG_A_OBJS): \
|
||||||
$(NO_MAGIC) \
|
$(NO_MAGIC) \
|
||||||
-fwrapv
|
-fwrapv
|
||||||
|
|
||||||
|
ifeq (,$(MODE))
|
||||||
|
LIBC_LOG_ASAN = o/$(MODE)/libc/log/asan.o
|
||||||
|
endif
|
||||||
|
LIBC_LOG_ASAN_A = o/$(MODE)/libc/log/log.a
|
||||||
|
|
||||||
LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)))
|
LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)))
|
||||||
LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS))
|
LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS))
|
||||||
LIBC_LOG_HDRS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_HDRS))
|
LIBC_LOG_HDRS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_HDRS))
|
||||||
|
|
|
@ -37,6 +37,10 @@
|
||||||
|
|
||||||
#define kNontrivialSize (8 * 1000 * 1000)
|
#define kNontrivialSize (8 * 1000 * 1000)
|
||||||
|
|
||||||
|
STATIC_YOINK("ntoa");
|
||||||
|
STATIC_YOINK("stoa");
|
||||||
|
STATIC_YOINK("ftoa");
|
||||||
|
|
||||||
static int loglevel2char(unsigned level) {
|
static int loglevel2char(unsigned level) {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case kLogInfo:
|
case kLogInfo:
|
||||||
|
@ -47,6 +51,8 @@ static int loglevel2char(unsigned level) {
|
||||||
return 'W';
|
return 'W';
|
||||||
case kLogFatal:
|
case kLogFatal:
|
||||||
return 'F';
|
return 'F';
|
||||||
|
case kLogVerbose:
|
||||||
|
return 'V';
|
||||||
default:
|
default:
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
|
@ -80,8 +86,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
||||||
long double t2;
|
long double t2;
|
||||||
const char *prog;
|
const char *prog;
|
||||||
int64_t secs, nsec, dots;
|
int64_t secs, nsec, dots;
|
||||||
char zonebuf[8], *zonebufp;
|
char timebuf[32], *timebufp;
|
||||||
char timebuf[24], *timebufp;
|
|
||||||
if (!f) f = g_logfile;
|
if (!f) f = g_logfile;
|
||||||
if (fileno(f) == -1) return;
|
if (fileno(f) == -1) return;
|
||||||
t2 = nowl();
|
t2 = nowl();
|
||||||
|
@ -89,22 +94,19 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
||||||
nsec = rem1000000000int64(t2 * 1e9L);
|
nsec = rem1000000000int64(t2 * 1e9L);
|
||||||
if (secs > ts.tv_sec) {
|
if (secs > ts.tv_sec) {
|
||||||
localtime_r(&secs, &tm);
|
localtime_r(&secs, &tm);
|
||||||
strftime(timebuf, sizeof(timebuf), "%Y%m%dT%H%M%S", &tm);
|
strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%S.", &tm);
|
||||||
strftime(zonebuf, sizeof(zonebuf), "%Z", &tm);
|
|
||||||
timebufp = timebuf;
|
timebufp = timebuf;
|
||||||
zonebufp = zonebuf;
|
|
||||||
dots = nsec;
|
dots = nsec;
|
||||||
} else {
|
} else {
|
||||||
timebufp = "---------------";
|
timebufp = "--------------------";
|
||||||
zonebufp = "---";
|
|
||||||
dots = nsec - ts.tv_nsec;
|
dots = nsec - ts.tv_nsec;
|
||||||
}
|
}
|
||||||
ts.tv_sec = secs;
|
ts.tv_sec = secs;
|
||||||
ts.tv_nsec = nsec;
|
ts.tv_nsec = nsec;
|
||||||
prog = basename(program_invocation_name);
|
prog = basename(program_invocation_name);
|
||||||
if (fprintf(f, "%c%s%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp,
|
if ((fprintf)(f, "%c%s%06ld:%s:%d:%.*s:%d] ", loglevel2char(level), timebufp,
|
||||||
zonebufp, rem1000000int64(div1000int64(dots)), file, line,
|
rem1000000int64(div1000int64(dots)), file, line,
|
||||||
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
||||||
vflogf_onfail(f);
|
vflogf_onfail(f);
|
||||||
}
|
}
|
||||||
(vfprintf)(f, fmt, va);
|
(vfprintf)(f, fmt, va);
|
||||||
|
@ -112,7 +114,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
||||||
fputc('\n', f);
|
fputc('\n', f);
|
||||||
if (level == kLogFatal) {
|
if (level == kLogFatal) {
|
||||||
startfatal(file, line);
|
startfatal(file, line);
|
||||||
fprintf(stderr, "fatal error see logfile\n");
|
(fprintf)(stderr, "fatal error see logfile\n");
|
||||||
die();
|
die();
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_
|
#ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_
|
||||||
#define COSMOPOLITAN_LIBC_NT_WINSOCK_H_
|
#define COSMOPOLITAN_LIBC_NT_WINSOCK_H_
|
||||||
#include "libc/nt/struct/overlapped.h"
|
#include "libc/nt/struct/overlapped.h"
|
||||||
|
#include "libc/nt/struct/pollfd.h"
|
||||||
|
#include "libc/sock/sock.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -55,10 +57,6 @@ COSMOPOLITAN_C_START_
|
||||||
#define kNtTfUseSystemThread 0x10
|
#define kNtTfUseSystemThread 0x10
|
||||||
#define kNtTfUseKernelApc 0x20
|
#define kNtTfUseKernelApc 0x20
|
||||||
|
|
||||||
struct sockaddr;
|
|
||||||
struct sockaddr_in;
|
|
||||||
struct pollfd$nt;
|
|
||||||
|
|
||||||
enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS };
|
enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS };
|
||||||
|
|
||||||
enum NtWsaCompletionType {
|
enum NtWsaCompletionType {
|
||||||
|
|
|
@ -53,8 +53,7 @@ _executive:
|
||||||
call _setstack
|
call _setstack
|
||||||
mov %eax,%edi
|
mov %eax,%edi
|
||||||
call exit
|
call exit
|
||||||
9: .endfn _executive,weak,hidden
|
.endfn _executive,weak,hidden
|
||||||
|
|
||||||
ud2
|
ud2
|
||||||
|
|
||||||
#ifdef __PG__
|
#ifdef __PG__
|
||||||
|
|
|
@ -28,9 +28,9 @@
|
||||||
*/
|
*/
|
||||||
void *_getstack(void) {
|
void *_getstack(void) {
|
||||||
char *p;
|
char *p;
|
||||||
p = mmap((char *)0x700000000000 - STACKSIZE, STACKSIZE,
|
p = mmap((char *)0x700000000000 /* IMAGE_BASE_VIRTUAL */ - STACKSIZE,
|
||||||
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1,
|
STACKSIZE, PROT_READ | PROT_WRITE,
|
||||||
0);
|
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
if (p == MAP_FAILED) abort();
|
if (p == MAP_FAILED) abort();
|
||||||
return p + STACKSIZE;
|
return p + STACKSIZE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,11 @@
|
||||||
*/
|
*/
|
||||||
bool isheap(void *p) {
|
bool isheap(void *p) {
|
||||||
int x, i;
|
int x, i;
|
||||||
|
#if 1
|
||||||
register intptr_t rsp asm("rsp");
|
register intptr_t rsp asm("rsp");
|
||||||
if ((intptr_t)p >= rsp) return false;
|
if ((intptr_t)p >= rsp) return false;
|
||||||
|
#endif
|
||||||
|
if ((intptr_t)p <= (intptr_t)_end) return false;
|
||||||
x = (intptr_t)p >> 16;
|
x = (intptr_t)p >> 16;
|
||||||
i = FindMemoryInterval(&_mmi, x);
|
i = FindMemoryInterval(&_mmi, x);
|
||||||
return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y;
|
return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y;
|
||||||
|
|
|
@ -17,11 +17,9 @@
|
||||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||||
│ 02110-1301 USA │
|
│ 02110-1301 USA │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/fmt/fmt.h"
|
#include "libc/conv/itoa.h"
|
||||||
#include "libc/sock/internal.h"
|
|
||||||
#include "libc/sock/sock.h"
|
|
||||||
#include "libc/sysv/errfuns.h"
|
|
||||||
#include "libc/sysv/consts/af.h"
|
#include "libc/sysv/consts/af.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats internet address to string.
|
* Formats internet address to string.
|
||||||
|
@ -33,11 +31,21 @@
|
||||||
* @return dst on success or NULL w/ errno
|
* @return dst on success or NULL w/ errno
|
||||||
*/
|
*/
|
||||||
const char *inet_ntop(int af, const void *src, char *dst, uint32_t size) {
|
const char *inet_ntop(int af, const void *src, char *dst, uint32_t size) {
|
||||||
|
char *p;
|
||||||
|
unsigned char *ip4;
|
||||||
if (src) {
|
if (src) {
|
||||||
if (af == AF_INET) {
|
if (af == AF_INET) {
|
||||||
unsigned char *p = (unsigned char *)src;
|
if (size >= 16) {
|
||||||
if (snprintf(dst, size, "%hhu.%hhu.%hhu.%hhu", p[0], p[1], p[2], p[3]) <
|
p = dst;
|
||||||
size) {
|
ip4 = src;
|
||||||
|
p += uint64toarray_radix10(ip4[0], p);
|
||||||
|
*p++ = '.';
|
||||||
|
p += uint64toarray_radix10(ip4[1], p);
|
||||||
|
*p++ = '.';
|
||||||
|
p += uint64toarray_radix10(ip4[2], p);
|
||||||
|
*p++ = '.';
|
||||||
|
p += uint64toarray_radix10(ip4[3], p);
|
||||||
|
*p = '\0';
|
||||||
return dst;
|
return dst;
|
||||||
} else {
|
} else {
|
||||||
enospc();
|
enospc();
|
||||||
|
|
|
@ -37,6 +37,7 @@ int fwritebuf(FILE *f) {
|
||||||
unsigned bytes;
|
unsigned bytes;
|
||||||
bytes = (f->beg < f->end ? f->end : f->size) - f->beg;
|
bytes = (f->beg < f->end ? f->end : f->size) - f->beg;
|
||||||
if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) {
|
if ((put = write(f->fd, &f->buf[f->beg], bytes)) == -1) {
|
||||||
|
if (errno == EINTR) return 0;
|
||||||
return (int)fseterrno(f);
|
return (int)fseterrno(f);
|
||||||
}
|
}
|
||||||
f->beg = (unsigned)((f->beg + put) & (f->size - 1));
|
f->beg = (unsigned)((f->beg + put) & (f->size - 1));
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/str/utf16.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes UTF-16 character.
|
* Decodes UTF-16 character.
|
||||||
|
@ -30,16 +31,15 @@
|
||||||
*/
|
*/
|
||||||
forcealignargpointer unsigned(getutf16)(const char16_t *p, wint_t *wc) {
|
forcealignargpointer unsigned(getutf16)(const char16_t *p, wint_t *wc) {
|
||||||
unsigned skip = 0;
|
unsigned skip = 0;
|
||||||
while ((p[skip] & UTF16_MASK) == UTF16_CONT) skip++;
|
while (IsUtf16Cont(p[skip])) skip++;
|
||||||
if ((p[skip] & UTF16_MASK) != UTF16_MOAR) {
|
if (IsUcs2(p[skip])) {
|
||||||
*wc = p[skip];
|
*wc = p[skip];
|
||||||
return skip + 1;
|
return skip + 1;
|
||||||
} else if ((p[skip + 1] & UTF16_MASK) != UTF16_CONT) {
|
} else if (IsUtf16Cont(p[skip + 1])) {
|
||||||
*wc = INVALID_CODEPOINT;
|
*wc = INVALID_CODEPOINT;
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
*wc = 0x10000 + ((((unsigned)p[skip + 0] - 0xd800) << 10) +
|
*wc = MergeUtf16(p[skip], p[skip + 1]);
|
||||||
((unsigned)p[skip + 1] - 0xdc00));
|
|
||||||
return skip + 2;
|
return skip + 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,6 @@ void *isnotplaintext(const void *, size_t) nothrow nocallback nosideeffect;
|
||||||
|
|
||||||
#define INVALID_CODEPOINT 0xfffd
|
#define INVALID_CODEPOINT 0xfffd
|
||||||
|
|
||||||
#define UTF16_MASK 0b1111110000000000
|
|
||||||
#define UTF16_MOAR 0b1101100000000000 /* 0xD800..0xDBFF */
|
|
||||||
#define UTF16_CONT 0b1101110000000000 /* 0xDC00..0xDBFF */
|
|
||||||
|
|
||||||
unsigned getutf16(const char16_t *, wint_t *);
|
unsigned getutf16(const char16_t *, wint_t *);
|
||||||
int pututf16(char16_t *, size_t, wint_t, bool);
|
int pututf16(char16_t *, size_t, wint_t, bool);
|
||||||
int iswalnum(wint_t);
|
int iswalnum(wint_t);
|
||||||
|
|
|
@ -18,11 +18,14 @@
|
||||||
│ 02110-1301 USA │
|
│ 02110-1301 USA │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "libc/str/utf16.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns number of characters in UTF-16 or UCS-2 string.
|
* Returns number of characters in UTF-16 or UCS-2 string.
|
||||||
*/
|
*/
|
||||||
size_t strclen16(const char16_t *s) { return strnclen16(s, -1ull); }
|
size_t strclen16(const char16_t *s) {
|
||||||
|
return strnclen16(s, -1ull);
|
||||||
|
}
|
||||||
|
|
||||||
noinline size_t strnclen16(const char16_t *p, size_t n) {
|
noinline size_t strnclen16(const char16_t *p, size_t n) {
|
||||||
size_t l = 0;
|
size_t l = 0;
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
#define COSMOPOLITAN_LIBC_STR_THOMPIKE_H_
|
#define COSMOPOLITAN_LIBC_STR_THOMPIKE_H_
|
||||||
#include "libc/nexgen32e/bsr.h"
|
#include "libc/nexgen32e/bsr.h"
|
||||||
|
|
||||||
#define ThomPikeCont(x) ((x & 0b11000000) == 0b10000000)
|
#define ThomPikeCont(x) (((x)&0b11000000) == 0b10000000)
|
||||||
#define ThomPikeByte(x) (x & (((1 << ThomPikeMsb(x)) - 1) | 3))
|
#define ThomPikeByte(x) ((x) & (((1 << ThomPikeMsb(x)) - 1) | 3))
|
||||||
#define ThomPikeLen(x) (7 - ThomPikeMsb(x))
|
#define ThomPikeLen(x) (7 - ThomPikeMsb(x))
|
||||||
#define ThomPikeMsb(x) (x < 252 ? bsr(~x & 0xff) : 1)
|
#define ThomPikeMsb(x) ((x) < 252 ? bsr(~(x)&0xff) : 1)
|
||||||
|
#define ThomPikeMerge(x, y) ((x) << 6 | (y)&0b00111111)
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */
|
#endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */
|
||||||
|
|
|
@ -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.
|
** where, to make the math easy, the answer for year zero is defined as zero.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pureconst static int
|
pureconst optimizespeed static int
|
||||||
leaps_thru_end_of(y)
|
leaps_thru_end_of(y)
|
||||||
register const int y;
|
register const int y;
|
||||||
{
|
{
|
||||||
return (y >= 0) ? (y / 4 - div100int64(y) + y / 400) :
|
return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
|
||||||
-(leaps_thru_end_of(-(y + 1)) + 1);
|
-(leaps_thru_end_of(-(y + 1)) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1605,15 +1605,19 @@ timesub(timep, offset, sp, tmp)
|
||||||
** Simplified normalize logic courtesy Paul Eggert.
|
** Simplified normalize logic courtesy Paul Eggert.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static inline int
|
||||||
increment_overflow(number, delta)
|
increment_overflow(number, delta)
|
||||||
int * number;
|
int * number;
|
||||||
int delta;
|
int delta;
|
||||||
{
|
{
|
||||||
|
#ifdef __GNUC__
|
||||||
|
return __builtin_add_overflow(*number, delta, number);
|
||||||
|
#else
|
||||||
int number0;
|
int number0;
|
||||||
number0 = *number;
|
number0 = *number;
|
||||||
*number += delta;
|
*number += delta;
|
||||||
return (*number < number0) != (delta < 0);
|
return (*number < number0) != (delta < 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -12,6 +12,7 @@ extern const char kWeekdayNameShort[7][4];
|
||||||
extern const char kWeekdayName[7][10];
|
extern const char kWeekdayName[7][10];
|
||||||
extern const char kMonthNameShort[12][4];
|
extern const char kMonthNameShort[12][4];
|
||||||
extern const char kMonthName[12][10];
|
extern const char kMonthName[12][10];
|
||||||
|
extern const unsigned short kMonthYearDay[2][12];
|
||||||
|
|
||||||
extern char *tzname[2];
|
extern char *tzname[2];
|
||||||
extern long CLOCKS_PER_SEC;
|
extern long CLOCKS_PER_SEC;
|
||||||
|
|
|
@ -47,9 +47,9 @@ static noinline long times2(struct tms *out_times, struct rusage *ru) {
|
||||||
&KernelTime, &UserTime)) {
|
&KernelTime, &UserTime)) {
|
||||||
return winerr();
|
return winerr();
|
||||||
}
|
}
|
||||||
filetimetotimeval(&tv, UserTime);
|
FileTimeToTimeVal(&tv, UserTime);
|
||||||
out_times->tms_utime = convertmicros(&tv, tick);
|
out_times->tms_utime = convertmicros(&tv, tick);
|
||||||
filetimetotimeval(&tv, KernelTime);
|
FileTimeToTimeVal(&tv, KernelTime);
|
||||||
out_times->tms_stime = convertmicros(&tv, tick);
|
out_times->tms_stime = convertmicros(&tv, tick);
|
||||||
out_times->tms_cutime = 0;
|
out_times->tms_cutime = 0;
|
||||||
out_times->tms_cstime = 0;
|
out_times->tms_cstime = 0;
|
||||||
|
|
18
libc/zip.h
18
libc/zip.h
|
@ -57,6 +57,7 @@
|
||||||
#define kZipCfileOffsetLastmodifieddate 14
|
#define kZipCfileOffsetLastmodifieddate 14
|
||||||
#define kZipCfileOffsetCrc32 16
|
#define kZipCfileOffsetCrc32 16
|
||||||
#define kZipCfileOffsetCompressedsize 20
|
#define kZipCfileOffsetCompressedsize 20
|
||||||
|
#define kZipCfileOffsetUncompressedsize 24
|
||||||
#define kZipCfileOffsetExternalattributes 38
|
#define kZipCfileOffsetExternalattributes 38
|
||||||
#define kZipCfileOffsetOffset 42
|
#define kZipCfileOffsetOffset 42
|
||||||
|
|
||||||
|
@ -71,10 +72,10 @@
|
||||||
|
|
||||||
#define kZipGflagUtf8 0x800
|
#define kZipGflagUtf8 0x800
|
||||||
|
|
||||||
#define kZipExtraHdrSize 4
|
#define kZipExtraHdrSize 4
|
||||||
#define kZipExtraZip64 0x0001
|
#define kZipExtraZip64 0x0001
|
||||||
#define kZipExtraNtfs 0x000a
|
#define kZipExtraNtfs 0x000a
|
||||||
#define kZipExtraNtfsFiletimes 0x0001
|
#define kZipExtraExtendedTimestamp 0x5455
|
||||||
|
|
||||||
#define kZipCfileMagic "PK\001\002"
|
#define kZipCfileMagic "PK\001\002"
|
||||||
|
|
||||||
|
@ -105,9 +106,10 @@
|
||||||
READ16LE((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */
|
READ16LE((P) + kZipCfileOffsetLastmodifiedtime) /* @see DOS_TIME() */
|
||||||
#define ZIP_CFILE_LASTMODIFIEDDATE(P) \
|
#define ZIP_CFILE_LASTMODIFIEDDATE(P) \
|
||||||
READ16LE((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */
|
READ16LE((P) + kZipCfileOffsetLastmodifieddate) /* @see DOS_DATE() */
|
||||||
#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32)
|
#define ZIP_CFILE_CRC32(P) READ32LE((P) + kZipCfileOffsetCrc32)
|
||||||
#define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize)
|
#define ZIP_CFILE_COMPRESSEDSIZE(P) READ32LE(P + kZipCfileOffsetCompressedsize)
|
||||||
#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) READ32LE((P) + 24)
|
#define ZIP_CFILE_UNCOMPRESSEDSIZE(P) \
|
||||||
|
READ32LE((P) + kZipCfileOffsetUncompressedsize)
|
||||||
#define ZIP_CFILE_NAMESIZE(P) READ16LE((P) + 28)
|
#define ZIP_CFILE_NAMESIZE(P) READ16LE((P) + 28)
|
||||||
#define ZIP_CFILE_EXTRASIZE(P) READ16LE((P) + 30)
|
#define ZIP_CFILE_EXTRASIZE(P) READ16LE((P) + 30)
|
||||||
#define ZIP_CFILE_COMMENTSIZE(P) READ16LE((P) + 32)
|
#define ZIP_CFILE_COMMENTSIZE(P) READ16LE((P) + 32)
|
||||||
|
@ -124,7 +126,7 @@
|
||||||
(ZIP_CFILE_NAMESIZE(P) + ZIP_CFILE_EXTRASIZE(P) + ZIP_CFILE_COMMENTSIZE(P) + \
|
(ZIP_CFILE_NAMESIZE(P) + ZIP_CFILE_EXTRASIZE(P) + ZIP_CFILE_COMMENTSIZE(P) + \
|
||||||
kZipCfileHdrMinSize)
|
kZipCfileHdrMinSize)
|
||||||
|
|
||||||
/* central directory file header */
|
/* local file header */
|
||||||
#define ZIP_LFILE_MAGIC(P) READ32LE(P)
|
#define ZIP_LFILE_MAGIC(P) READ32LE(P)
|
||||||
#define ZIP_LFILE_VERSIONNEED(P) ((P)[4])
|
#define ZIP_LFILE_VERSIONNEED(P) ((P)[4])
|
||||||
#define ZIP_LFILE_OSNEED(P) ((P)[5])
|
#define ZIP_LFILE_OSNEED(P) ((P)[5])
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
*/
|
*/
|
||||||
int __zipos_close(struct ZiposHandle *h) {
|
int __zipos_close(struct ZiposHandle *h) {
|
||||||
if (h) {
|
if (h) {
|
||||||
munmap(h->map, h->mapsize);
|
free(h->map);
|
||||||
free(h);
|
free(h);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -27,16 +27,14 @@
|
||||||
|
|
||||||
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
|
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
|
||||||
size_t i, cf;
|
size_t i, cf;
|
||||||
if ((zipos = __zipos_get())) {
|
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
|
||||||
assert(ZIP_CDIR_MAGIC(zipos->cdir) == kZipCdirHdrMagic);
|
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
|
||||||
for (i = 0, cf = ZIP_CDIR_OFFSET(zipos->cdir);
|
i < ZIP_CDIR_RECORDS(zipos->cdir);
|
||||||
i < ZIP_CDIR_RECORDS(zipos->cdir);
|
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
|
||||||
++i, cf += ZIP_CFILE_HDRSIZE(zipos->map + cf)) {
|
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
|
||||||
assert(ZIP_CFILE_MAGIC(zipos->map + cf) == kZipCfileHdrMagic);
|
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
|
||||||
if (name->len == ZIP_CFILE_NAMESIZE(zipos->map + cf) &&
|
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
|
||||||
memcmp(name->path, ZIP_CFILE_NAME(zipos->map + cf), name->len) == 0) {
|
return cf;
|
||||||
return cf;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -99,9 +99,9 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
|
||||||
if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf))) {
|
if ((h->size = ZIP_LFILE_UNCOMPRESSEDSIZE(zipos->map + lf))) {
|
||||||
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||||
assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf));
|
assert(ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf));
|
||||||
h->mapsize = ROUNDUP(h->size + FRAMESIZE, FRAMESIZE);
|
h->mapsize = h->size;
|
||||||
if ((h->map = mapanon(h->mapsize)) != MAP_FAILED) {
|
if ((h->map = malloc(h->mapsize)) != MAP_FAILED) {
|
||||||
h->mem = h->map + FRAMESIZE / 2;
|
h->mem = h->map;
|
||||||
if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)(
|
if ((IsTiny() ? __zipos_inflate_tiny : __zipos_inflate_fast)(
|
||||||
h, ZIP_LFILE_CONTENT(zipos->map + lf),
|
h, ZIP_LFILE_CONTENT(zipos->map + lf),
|
||||||
ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) == -1) {
|
ZIP_LFILE_COMPRESSEDSIZE(zipos->map + lf)) == -1) {
|
||||||
|
@ -132,7 +132,7 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
|
||||||
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
|
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
|
||||||
*
|
*
|
||||||
* @param uri is obtained via __zipos_parseuri()
|
* @param uri is obtained via __zipos_parseuri()
|
||||||
* @asyncsignalsafe
|
* @note don't call open() from signal handlers
|
||||||
*/
|
*/
|
||||||
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -140,7 +140,6 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
||||||
sigset_t oldmask;
|
sigset_t oldmask;
|
||||||
struct Zipos *zipos;
|
struct Zipos *zipos;
|
||||||
assert((flags & O_ACCMODE) == O_RDONLY);
|
assert((flags & O_ACCMODE) == O_RDONLY);
|
||||||
sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
|
|
||||||
if ((zipos = __zipos_get())) {
|
if ((zipos = __zipos_get())) {
|
||||||
if ((cf = __zipos_find(zipos, name)) != -1) {
|
if ((cf = __zipos_find(zipos, name)) != -1) {
|
||||||
fd = __zipos_load(zipos, cf, flags, mode);
|
fd = __zipos_load(zipos, cf, flags, mode);
|
||||||
|
@ -150,6 +149,5 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
||||||
} else {
|
} else {
|
||||||
fd = enoexec();
|
fd = enoexec();
|
||||||
}
|
}
|
||||||
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
Server, kHttpServer
|
||||||
Vary, kHttpVary
|
Vary, kHttpVary
|
||||||
Warning, kHttpWarning
|
Warning, kHttpWarning
|
||||||
WWW-Authenticate, kHttpWwwAuthenticate
|
Www-Authenticate, kHttpWwwAuthenticate
|
||||||
Last-Modified, kHttpLastModified
|
Last-Modified, kHttpLastModified
|
||||||
|
|
|
@ -216,7 +216,7 @@ LookupHttpHeader (register const char *str, register size_t len)
|
||||||
#line 43 "gethttpheader.gperf"
|
#line 43 "gethttpheader.gperf"
|
||||||
{"Keep-Alive", kHttpKeepAlive},
|
{"Keep-Alive", kHttpKeepAlive},
|
||||||
#line 61 "gethttpheader.gperf"
|
#line 61 "gethttpheader.gperf"
|
||||||
{"WWW-Authenticate", kHttpWwwAuthenticate},
|
{"Www-Authenticate", kHttpWwwAuthenticate},
|
||||||
#line 35 "gethttpheader.gperf"
|
#line 35 "gethttpheader.gperf"
|
||||||
{"Expires", kHttpExpires},
|
{"Expires", kHttpExpires},
|
||||||
#line 46 "gethttpheader.gperf"
|
#line 46 "gethttpheader.gperf"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
%readonly-tables
|
%readonly-tables
|
||||||
%struct-type
|
%struct-type
|
||||||
%define lookup-function-name LookupHttpMethod
|
%define lookup-function-name LookupHttpMethod
|
||||||
struct HttpMethodSlot { char *name; char code; };
|
struct HttpMethodSlot { char name[8]; char code; };
|
||||||
%%
|
%%
|
||||||
DELETE, kHttpDelete
|
DELETE, kHttpDelete
|
||||||
GET, kHttpGet
|
GET, kHttpGet
|
||||||
|
@ -20,17 +20,11 @@ CONNECT, kHttpConnect
|
||||||
OPTIONS, kHttpOptions
|
OPTIONS, kHttpOptions
|
||||||
TRACE, kHttpTrace
|
TRACE, kHttpTrace
|
||||||
COPY, kHttpCopy
|
COPY, kHttpCopy
|
||||||
CHECKOUT, kHttpCheckout
|
|
||||||
LOCK, kHttpLock
|
LOCK, kHttpLock
|
||||||
MERGE, kHttpMerge
|
MERGE, kHttpMerge
|
||||||
MKACTIVITY, kHttpMkactivity
|
|
||||||
MKCOL, kHttpMkcol
|
MKCOL, kHttpMkcol
|
||||||
MOVE, kHttpMove
|
MOVE, kHttpMove
|
||||||
NOTIFY, kHttpNotify
|
NOTIFY, kHttpNotify
|
||||||
PATCH, kHttpPatch
|
PATCH, kHttpPatch
|
||||||
PROPFIND, kHttpPropfind
|
|
||||||
PROPPATCH, kHttpProppatch
|
|
||||||
REPORT, kHttpReport
|
REPORT, kHttpReport
|
||||||
SUBSCRIBE, kHttpSubscribe
|
|
||||||
UNLOCK, kHttpUnlock
|
UNLOCK, kHttpUnlock
|
||||||
UNSUBSCRIBE, kHttpUnsubscribe
|
|
||||||
|
|
|
@ -35,14 +35,14 @@
|
||||||
#include "net/http/http.h"
|
#include "net/http/http.h"
|
||||||
#define GPERF_DOWNCASE
|
#define GPERF_DOWNCASE
|
||||||
#line 12 "gethttpmethod.gperf"
|
#line 12 "gethttpmethod.gperf"
|
||||||
struct HttpMethodSlot { char *name; char code; };
|
struct HttpMethodSlot { char name[8]; char code; };
|
||||||
|
|
||||||
#define TOTAL_KEYWORDS 23
|
#define TOTAL_KEYWORDS 17
|
||||||
#define MIN_WORD_LENGTH 3
|
#define MIN_WORD_LENGTH 3
|
||||||
#define MAX_WORD_LENGTH 11
|
#define MAX_WORD_LENGTH 7
|
||||||
#define MIN_HASH_VALUE 4
|
#define MIN_HASH_VALUE 3
|
||||||
#define MAX_HASH_VALUE 34
|
#define MAX_HASH_VALUE 25
|
||||||
/* maximum key range = 31, duplicates = 0 */
|
/* maximum key range = 23, duplicates = 0 */
|
||||||
|
|
||||||
#ifndef GPERF_DOWNCASE
|
#ifndef GPERF_DOWNCASE
|
||||||
#define GPERF_DOWNCASE 1
|
#define GPERF_DOWNCASE 1
|
||||||
|
@ -101,32 +101,32 @@ hash (register const char *str, register size_t len)
|
||||||
{
|
{
|
||||||
static const unsigned char asso_values[] =
|
static const unsigned char asso_values[] =
|
||||||
{
|
{
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 0, 35, 5, 10, 10,
|
26, 26, 26, 26, 26, 0, 26, 5, 15, 0,
|
||||||
35, 5, 10, 35, 35, 0, 30, 15, 0, 0,
|
26, 5, 0, 26, 26, 10, 15, 10, 0, 5,
|
||||||
0, 35, 5, 15, 0, 5, 35, 35, 35, 35,
|
0, 26, 10, 26, 5, 0, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 0, 35, 5,
|
26, 26, 26, 26, 26, 26, 26, 0, 26, 5,
|
||||||
10, 10, 35, 5, 10, 35, 35, 0, 30, 15,
|
15, 0, 26, 5, 0, 26, 26, 10, 15, 10,
|
||||||
0, 0, 0, 35, 5, 15, 0, 5, 35, 35,
|
0, 5, 0, 26, 10, 26, 5, 0, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
|
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
|
||||||
35, 35, 35, 35, 35, 35
|
26, 26, 26, 26, 26, 26
|
||||||
};
|
};
|
||||||
return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]];
|
return len + asso_values[(unsigned char)str[1]] + asso_values[(unsigned char)str[0]];
|
||||||
}
|
}
|
||||||
|
@ -136,58 +136,46 @@ LookupHttpMethod (register const char *str, register size_t len)
|
||||||
{
|
{
|
||||||
static const struct HttpMethodSlot wordlist[] =
|
static const struct HttpMethodSlot wordlist[] =
|
||||||
{
|
{
|
||||||
{""}, {""}, {""}, {""},
|
{""}, {""}, {""},
|
||||||
#line 17 "gethttpmethod.gperf"
|
|
||||||
{"POST", kHttpPost},
|
|
||||||
#line 30 "gethttpmethod.gperf"
|
|
||||||
{"PATCH", kHttpPatch},
|
|
||||||
#line 29 "gethttpmethod.gperf"
|
|
||||||
{"NOTIFY", kHttpNotify},
|
|
||||||
#line 20 "gethttpmethod.gperf"
|
|
||||||
{"OPTIONS", kHttpOptions},
|
|
||||||
#line 18 "gethttpmethod.gperf"
|
#line 18 "gethttpmethod.gperf"
|
||||||
{"PUT", kHttpPut},
|
{"PUT", kHttpPut},
|
||||||
#line 22 "gethttpmethod.gperf"
|
#line 16 "gethttpmethod.gperf"
|
||||||
{"COPY", kHttpCopy},
|
{"HEAD", kHttpHead},
|
||||||
#line 21 "gethttpmethod.gperf"
|
#line 28 "gethttpmethod.gperf"
|
||||||
{"TRACE", kHttpTrace},
|
{"PATCH", kHttpPatch},
|
||||||
#line 35 "gethttpmethod.gperf"
|
#line 30 "gethttpmethod.gperf"
|
||||||
{"UNLOCK", kHttpUnlock},
|
{"UNLOCK", kHttpUnlock},
|
||||||
#line 19 "gethttpmethod.gperf"
|
|
||||||
{"CONNECT", kHttpConnect},
|
|
||||||
#line 31 "gethttpmethod.gperf"
|
|
||||||
{"PROPFIND", kHttpPropfind},
|
|
||||||
#line 32 "gethttpmethod.gperf"
|
|
||||||
{"PROPPATCH", kHttpProppatch},
|
|
||||||
{""},
|
|
||||||
#line 36 "gethttpmethod.gperf"
|
|
||||||
{"UNSUBSCRIBE", kHttpUnsubscribe},
|
|
||||||
{""},
|
{""},
|
||||||
#line 15 "gethttpmethod.gperf"
|
#line 15 "gethttpmethod.gperf"
|
||||||
{"GET", kHttpGet},
|
{"GET", kHttpGet},
|
||||||
#line 28 "gethttpmethod.gperf"
|
#line 17 "gethttpmethod.gperf"
|
||||||
{"MOVE", kHttpMove},
|
{"POST", kHttpPost},
|
||||||
#line 27 "gethttpmethod.gperf"
|
{""},
|
||||||
{"MKCOL", kHttpMkcol},
|
#line 27 "gethttpmethod.gperf"
|
||||||
#line 33 "gethttpmethod.gperf"
|
{"NOTIFY", kHttpNotify},
|
||||||
{"REPORT", kHttpReport},
|
#line 20 "gethttpmethod.gperf"
|
||||||
|
{"OPTIONS", kHttpOptions},
|
||||||
|
{""},
|
||||||
|
#line 22 "gethttpmethod.gperf"
|
||||||
|
{"COPY", kHttpCopy},
|
||||||
|
#line 24 "gethttpmethod.gperf"
|
||||||
|
{"MERGE", kHttpMerge},
|
||||||
|
#line 29 "gethttpmethod.gperf"
|
||||||
|
{"REPORT", kHttpReport},
|
||||||
|
#line 19 "gethttpmethod.gperf"
|
||||||
|
{"CONNECT", kHttpConnect},
|
||||||
{""},
|
{""},
|
||||||
#line 23 "gethttpmethod.gperf"
|
|
||||||
{"CHECKOUT", kHttpCheckout},
|
|
||||||
#line 16 "gethttpmethod.gperf"
|
|
||||||
{"HEAD", kHttpHead},
|
|
||||||
#line 26 "gethttpmethod.gperf"
|
#line 26 "gethttpmethod.gperf"
|
||||||
{"MKACTIVITY", kHttpMkactivity},
|
{"MOVE", kHttpMove},
|
||||||
|
#line 21 "gethttpmethod.gperf"
|
||||||
|
{"TRACE", kHttpTrace},
|
||||||
#line 14 "gethttpmethod.gperf"
|
#line 14 "gethttpmethod.gperf"
|
||||||
{"DELETE", kHttpDelete},
|
{"DELETE", kHttpDelete},
|
||||||
{""}, {""},
|
{""}, {""},
|
||||||
#line 34 "gethttpmethod.gperf"
|
#line 23 "gethttpmethod.gperf"
|
||||||
{"SUBSCRIBE", kHttpSubscribe},
|
{"LOCK", kHttpLock},
|
||||||
#line 25 "gethttpmethod.gperf"
|
#line 25 "gethttpmethod.gperf"
|
||||||
{"MERGE", kHttpMerge},
|
{"MKCOL", kHttpMkcol}
|
||||||
{""}, {""}, {""},
|
|
||||||
#line 24 "gethttpmethod.gperf"
|
|
||||||
{"LOCK", kHttpLock}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
|
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
|
||||||
|
|
|
@ -1,30 +1,25 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
#ifndef COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
||||||
#define COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
#define COSMOPOLITAN_LIBC_HTTP_HTTP_H_
|
||||||
#include "libc/alg/alg.h"
|
#include "libc/alg/alg.h"
|
||||||
|
#include "libc/time/struct/tm.h"
|
||||||
|
|
||||||
#define kHttpGet 0
|
#define kHttpGet 0
|
||||||
#define kHttpHead 1
|
#define kHttpHead 1
|
||||||
#define kHttpPost 2
|
#define kHttpPost 2
|
||||||
#define kHttpPut 3
|
#define kHttpPut 3
|
||||||
#define kHttpDelete 4
|
#define kHttpDelete 4
|
||||||
#define kHttpConnect 5
|
#define kHttpConnect 5
|
||||||
#define kHttpOptions 6
|
#define kHttpOptions 6
|
||||||
#define kHttpTrace 7
|
#define kHttpTrace 7
|
||||||
#define kHttpCopy 8
|
#define kHttpCopy 8
|
||||||
#define kHttpCheckout 9
|
#define kHttpLock 9
|
||||||
#define kHttpLock 10
|
#define kHttpMerge 10
|
||||||
#define kHttpMerge 11
|
#define kHttpMkcol 11
|
||||||
#define kHttpMkactivity 12
|
#define kHttpMove 12
|
||||||
#define kHttpMkcol 13
|
#define kHttpNotify 13
|
||||||
#define kHttpMove 14
|
#define kHttpPatch 14
|
||||||
#define kHttpNotify 15
|
#define kHttpReport 15
|
||||||
#define kHttpPatch 16
|
#define kHttpUnlock 16
|
||||||
#define kHttpPropfind 17
|
|
||||||
#define kHttpProppatch 18
|
|
||||||
#define kHttpReport 19
|
|
||||||
#define kHttpSubscribe 20
|
|
||||||
#define kHttpUnlock 21
|
|
||||||
#define kHttpUnsubscribe 22
|
|
||||||
|
|
||||||
#define kHttpAccept 0
|
#define kHttpAccept 0
|
||||||
#define kHttpAcceptCharset 1
|
#define kHttpAcceptCharset 1
|
||||||
|
@ -86,17 +81,24 @@ struct HttpRequestSlice {
|
||||||
|
|
||||||
struct HttpRequest {
|
struct HttpRequest {
|
||||||
int method;
|
int method;
|
||||||
|
int length;
|
||||||
struct HttpRequestSlice uri;
|
struct HttpRequestSlice uri;
|
||||||
struct HttpRequestSlice version;
|
struct HttpRequestSlice version;
|
||||||
struct HttpRequestSlice scratch;
|
struct HttpRequestSlice scratch;
|
||||||
struct HttpRequestSlice headers[kHttpHeadersMax];
|
struct HttpRequestSlice headers[kHttpHeadersMax];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern const char kHttpMethod[17][8];
|
||||||
|
|
||||||
int GetHttpHeader(const char *, size_t);
|
int GetHttpHeader(const char *, size_t);
|
||||||
int GetHttpMethod(const char *, size_t);
|
int GetHttpMethod(const char *, size_t);
|
||||||
int ParseHttpRequest(struct HttpRequest *, const char *, size_t);
|
int ParseHttpRequest(struct HttpRequest *, const char *, size_t);
|
||||||
int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *,
|
int NegotiateHttpRequest(int, const char *, uint32_t *, char *, uint32_t *,
|
||||||
uint32_t *, bool, long double);
|
uint32_t *, bool, long double);
|
||||||
|
char *FormatHttpDateTime(char[hasatleast 30], struct tm *);
|
||||||
|
bool ParseHttpRange(const char *, size_t, long, long *, long *);
|
||||||
|
unsigned ParseHttpVersion(const char *, size_t);
|
||||||
|
int64_t ParseHttpDateTime(const char *, size_t);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -12,65 +12,70 @@ NET_HTTP_A_SRCS_S = $(filter %.S,$(NET_HTTP_A_FILES))
|
||||||
NET_HTTP_A_SRCS_C = $(filter %.c,$(NET_HTTP_A_FILES))
|
NET_HTTP_A_SRCS_C = $(filter %.c,$(NET_HTTP_A_FILES))
|
||||||
NET_HTTP_A_SRCS_R = $(filter %.rl,$(NET_HTTP_A_FILES))
|
NET_HTTP_A_SRCS_R = $(filter %.rl,$(NET_HTTP_A_FILES))
|
||||||
|
|
||||||
NET_HTTP_A_SRCS = \
|
NET_HTTP_A_SRCS = \
|
||||||
$(NET_HTTP_A_SRCS_S) \
|
$(NET_HTTP_A_SRCS_S) \
|
||||||
$(NET_HTTP_A_SRCS_C) \
|
$(NET_HTTP_A_SRCS_C) \
|
||||||
$(NET_HTTP_A_SRCS_R)
|
$(NET_HTTP_A_SRCS_R)
|
||||||
|
|
||||||
NET_HTTP_A_OBJS = \
|
NET_HTTP_A_OBJS = \
|
||||||
$(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \
|
$(NET_HTTP_A_SRCS:%=o/$(MODE)/%.zip.o) \
|
||||||
$(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
$(NET_HTTP_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||||
$(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \
|
$(NET_HTTP_A_SRCS_C:%.c=o/$(MODE)/%.o) \
|
||||||
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.o)
|
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.o)
|
||||||
|
|
||||||
NET_HTTP_A_CHECKS = \
|
NET_HTTP_A_CHECKS = \
|
||||||
$(NET_HTTP_A).pkg \
|
$(NET_HTTP_A).pkg \
|
||||||
$(NET_HTTP_A_HDRS:%=o/$(MODE)/%.ok)
|
$(NET_HTTP_A_HDRS:%=o/$(MODE)/%.ok)
|
||||||
|
|
||||||
NET_HTTP_A_DIRECTDEPS = \
|
NET_HTTP_A_DIRECTDEPS = \
|
||||||
LIBC_ALG \
|
LIBC_ALG \
|
||||||
LIBC_CALLS \
|
LIBC_CALLS \
|
||||||
LIBC_CONV \
|
LIBC_CONV \
|
||||||
LIBC_FMT \
|
LIBC_FMT \
|
||||||
LIBC_LOG \
|
LIBC_LOG \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
LIBC_RUNTIME \
|
LIBC_RUNTIME \
|
||||||
LIBC_SOCK \
|
LIBC_SOCK \
|
||||||
LIBC_STDIO \
|
LIBC_STDIO \
|
||||||
LIBC_STUBS \
|
LIBC_STUBS \
|
||||||
LIBC_SYSV \
|
LIBC_SYSV \
|
||||||
LIBC_TIME \
|
LIBC_TIME \
|
||||||
LIBC_X
|
LIBC_X
|
||||||
|
|
||||||
NET_HTTP_A_DEPS := \
|
NET_HTTP_A_DEPS := \
|
||||||
$(call uniq,$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x))))
|
$(call uniq,$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x))))
|
||||||
|
|
||||||
$(NET_HTTP_A): net/http/ \
|
$(NET_HTTP_A): net/http/ \
|
||||||
$(NET_HTTP_A).pkg \
|
$(NET_HTTP_A).pkg \
|
||||||
$(NET_HTTP_A_OBJS)
|
$(NET_HTTP_A_OBJS)
|
||||||
|
|
||||||
$(NET_HTTP_A).pkg: \
|
$(NET_HTTP_A).pkg: \
|
||||||
$(NET_HTTP_A_OBJS) \
|
$(NET_HTTP_A_OBJS) \
|
||||||
$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x)_A).pkg)
|
$(foreach x,$(NET_HTTP_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||||
|
|
||||||
|
o/$(MODE)/net/http/formathttpdatetime.o: \
|
||||||
|
OVERRIDE_CFLAGS += \
|
||||||
|
-O3
|
||||||
|
|
||||||
|
ifeq (,$(MODE))
|
||||||
|
$(NET_HTTP_A_OBJS): \
|
||||||
|
OVERRIDE_CFLAGS += \
|
||||||
|
-fsanitize=address
|
||||||
|
endif
|
||||||
|
|
||||||
NET_HTTP_LIBS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)))
|
NET_HTTP_LIBS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)))
|
||||||
NET_HTTP_SRCS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_SRCS))
|
NET_HTTP_SRCS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_SRCS))
|
||||||
NET_HTTP_HDRS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_HDRS))
|
NET_HTTP_HDRS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_HDRS))
|
||||||
NET_HTTP_CHECKS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_CHECKS))
|
NET_HTTP_CHECKS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_CHECKS))
|
||||||
NET_HTTP_OBJS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_OBJS))
|
NET_HTTP_OBJS = $(foreach x,$(NET_HTTP_ARTIFACTS),$($(x)_OBJS))
|
||||||
|
|
||||||
$(NET_HTTP_OBJS): $(BUILD_FILES) net/http/http.mk
|
.PRECIOUS: \
|
||||||
|
$(NET_HTTP_A_SRCS_R:%.rl=o/$(MODE)/%.c) \
|
||||||
.PRECIOUS: \
|
o/$(MODE)/net/http/uricspn.s \
|
||||||
$(NET_HTTP_A_SRCS_R:%.rl=build/bootstrap/%.c) \
|
o/$(MODE)/net/http/uricspn.i \
|
||||||
o/$(MODE)/net/http/uricspn.s \
|
|
||||||
o/$(MODE)/net/http/uriparse.s \
|
|
||||||
o/$(MODE)/net/http/uricspn.i \
|
|
||||||
o/$(MODE)/net/http/uriparse.i \
|
|
||||||
o/$(MODE)/net/http/uriparse.c \
|
|
||||||
o/$(MODE)/net/http/uricspn.c
|
o/$(MODE)/net/http/uricspn.c
|
||||||
|
|
||||||
.PHONY: o/$(MODE)/net/http
|
.PHONY: o/$(MODE)/net/http
|
||||||
o/$(MODE)/net/http: \
|
o/$(MODE)/net/http: \
|
||||||
$(NET_HTTP_CHECKS) \
|
$(NET_HTTP_CHECKS) \
|
||||||
$(NET_HTTP_A_SRCS_R:%.rl=%.svgz)
|
$(NET_HTTP_A_SRCS_R:%.rl=%.svgz)
|
||||||
|
|
|
@ -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;
|
break;
|
||||||
case LF2:
|
case LF2:
|
||||||
if (c == '\n') return 0;
|
if (c == '\n') {
|
||||||
|
req->length = i + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return ebadmsg();
|
return ebadmsg();
|
||||||
default:
|
default:
|
||||||
unreachable;
|
unreachable;
|
||||||
|
|
|
@ -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 = \
|
TEST_LIBC_CONV_DIRECTDEPS = \
|
||||||
LIBC_CONV \
|
LIBC_CONV \
|
||||||
|
LIBC_FMT \
|
||||||
|
LIBC_LOG \
|
||||||
|
LIBC_STDIO \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
LIBC_STUBS \
|
LIBC_STUBS \
|
||||||
LIBC_TINYMATH \
|
LIBC_TINYMATH \
|
||||||
|
|
|
@ -23,11 +23,11 @@
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/time/time.h"
|
#include "libc/time/time.h"
|
||||||
|
|
||||||
TEST(timevaltofiletime, roundTrip) {
|
TEST(TimeValToFileTime, roundTrip) {
|
||||||
struct timeval tv1, tv2;
|
struct timeval tv1, tv2;
|
||||||
tv1.tv_sec = 31337;
|
tv1.tv_sec = 31337;
|
||||||
tv1.tv_usec = 1337;
|
tv1.tv_usec = 1337;
|
||||||
filetimetotimeval(&tv2, timevaltofiletime(&tv1));
|
FileTimeToTimeVal(&tv2, TimeValToFileTime(&tv1));
|
||||||
EXPECT_EQ(31337, tv2.tv_sec);
|
EXPECT_EQ(31337, tv2.tv_sec);
|
||||||
EXPECT_EQ(1337, tv2.tv_usec);
|
EXPECT_EQ(1337, tv2.tv_usec);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
BENCH(palandprintf, bench) {
|
||||||
EZBENCH2("snprintf %x", donothing, Format("%x", VEIL("r", INT_MIN)));
|
EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23)));
|
||||||
EZBENCH2("snprintf %d", donothing, Format("%d", VEIL("r", INT_MIN)));
|
EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23)));
|
||||||
EZBENCH2("snprintf %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯")));
|
EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN)));
|
||||||
|
EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN)));
|
||||||
|
EZBENCH2("ascii %s", donothing, Format("%s", VEIL("r", "hiuhcreohucreo")));
|
||||||
|
EZBENCH2("utf8 %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯")));
|
||||||
EZBENCH2("snprintf %hs", donothing, Format("%hs", VEIL("r", u"hi (╯°□°)╯")));
|
EZBENCH2("snprintf %hs", donothing, Format("%hs", VEIL("r", u"hi (╯°□°)╯")));
|
||||||
EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯")));
|
EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯")));
|
||||||
EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer));
|
EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer));
|
||||||
|
|
|
@ -72,6 +72,22 @@ TEST(SUITE(snprintf), testStringPrecision_showsTrueBinary) {
|
||||||
EXPECT_STREQ("\3\4\0", Format("%.*s", 3, "\3\4\0"));
|
EXPECT_STREQ("\3\4\0", Format("%.*s", 3, "\3\4\0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SUITE(snprintf), testPrecision_usesCodepointCount) {
|
TEST(SUITE(snprintf), testPrecision_usesByteCount) {
|
||||||
EXPECT_STREQ("ちゃぶ台返し", Format("%.*s", 6, "ちゃぶ台返し"));
|
EXPECT_STREQ("ちゃ", Format("%.*s", 6, "ちゃぶ台返し"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SUITE(snprintf), testReprChar16) {
|
||||||
|
EXPECT_STREQ("u'♥'", Format("%`'hc", u'♥'));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SUITE(snprintf), testReprChar32) {
|
||||||
|
EXPECT_STREQ("L'♥'", Format("%`'Lc", L'♥'));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SUITE(snprintf), testReprUtf8) {
|
||||||
|
EXPECT_STREQ("\"♥\"", Format("%`'s", u8"♥"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SUITE(snprintf), testReprUtf8Precision_countsBytes) {
|
||||||
|
EXPECT_STREQ("\"♥\"", Format("%`'.*s", 3, u8"♥"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,10 +54,3 @@ TEST(inet_ntop, testNoSpace) {
|
||||||
ASSERT_STREQ("", buf);
|
ASSERT_STREQ("", buf);
|
||||||
tfree(buf);
|
tfree(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(inet_ntop, testSqueeze) {
|
|
||||||
char *buf = memcpy(tmalloc(8), "hi", 3);
|
|
||||||
uint8_t localhost[4] = {0, 0, 0, 0};
|
|
||||||
ASSERT_STREQ("0.0.0.0", inet_ntop(AF_INET, localhost, buf, 8));
|
|
||||||
tfree(buf);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 │
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||||
│ 02110-1301 USA │
|
│ 02110-1301 USA │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/rand/lcg.h"
|
||||||
|
#include "libc/rand/rand.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/testlib/ezbench.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "tool/build/lib/dis.h"
|
#include "tool/build/lib/dis.h"
|
||||||
#include "tool/build/lib/modrm.h"
|
#include "tool/build/lib/modrm.h"
|
||||||
|
@ -227,7 +231,7 @@ TEST(DisInst, testOrImmCode16gcc) {
|
||||||
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
|
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
|
||||||
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
|
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
|
||||||
DisInst(d, b1, DisSpec(d->xedd, b2));
|
DisInst(d, b1, DisSpec(d->xedd, b2));
|
||||||
EXPECT_STREQ("or $0xc00,12(%esp)", b1);
|
EXPECT_STREQ("orw $0xc00,12(%esp)", b1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(DisInst, testPause) {
|
TEST(DisInst, testPause) {
|
||||||
|
@ -269,3 +273,19 @@ TEST(DisInst, testJmpEq) {
|
||||||
DisInst(d, b1, DisSpec(d->xedd, b2));
|
DisInst(d, b1, DisSpec(d->xedd, b2));
|
||||||
EXPECT_STREQ("jmp %rax", b1);
|
EXPECT_STREQ("jmp %rax", b1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(DisInst, testMovswSs) {
|
||||||
|
uint8_t op[] = {0x36, 0xA5};
|
||||||
|
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
|
||||||
|
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
|
||||||
|
DisInst(d, b1, DisSpec(d->xedd, b2));
|
||||||
|
EXPECT_STREQ("movs %ss:(%si),(%di)", b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DisInst, testRealModrm_sibOverlap_showsNoDisplacement) {
|
||||||
|
uint8_t op[] = {0x8b, 0b00100101};
|
||||||
|
xed_decoded_inst_zero_set_mode(d->xedd, XED_MACHINE_MODE_REAL);
|
||||||
|
ASSERT_EQ(0, xed_instruction_length_decode(d->xedd, op, sizeof(op)));
|
||||||
|
DisInst(d, b1, DisSpec(d->xedd, b2));
|
||||||
|
EXPECT_STREQ("mov (%di),%sp", b1);
|
||||||
|
}
|
||||||
|
|
|
@ -57,6 +57,12 @@ o/$(MODE)/third_party/zlib/adler32.o: \
|
||||||
-ffunction-sections \
|
-ffunction-sections \
|
||||||
-fdata-sections
|
-fdata-sections
|
||||||
|
|
||||||
|
ifeq (,$(MODE))
|
||||||
|
$(THIRD_PARTY_ZLIB_A_OBJS): \
|
||||||
|
OVERRIDE_CFLAGS += \
|
||||||
|
-fsanitize=address
|
||||||
|
endif
|
||||||
|
|
||||||
THIRD_PARTY_ZLIB_LIBS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)))
|
THIRD_PARTY_ZLIB_LIBS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)))
|
||||||
THIRD_PARTY_ZLIB_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_SRCS))
|
THIRD_PARTY_ZLIB_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_SRCS))
|
||||||
THIRD_PARTY_ZLIB_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_HDRS))
|
THIRD_PARTY_ZLIB_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_ARTIFACTS),$($(x)_HDRS))
|
||||||
|
|
|
@ -35,6 +35,7 @@ TOOL_BUILD_DIRECTDEPS = \
|
||||||
LIBC_ELF \
|
LIBC_ELF \
|
||||||
LIBC_FMT \
|
LIBC_FMT \
|
||||||
LIBC_LOG \
|
LIBC_LOG \
|
||||||
|
LIBC_LOG_ASAN \
|
||||||
LIBC_TINYMATH \
|
LIBC_TINYMATH \
|
||||||
LIBC_MEM \
|
LIBC_MEM \
|
||||||
LIBC_NEXGEN32E \
|
LIBC_NEXGEN32E \
|
||||||
|
|
|
@ -27,6 +27,7 @@ TOOL_BUILD_EMUBIN_CHECKS = \
|
||||||
|
|
||||||
TOOL_BUILD_EMUBIN_DIRECTDEPS = \
|
TOOL_BUILD_EMUBIN_DIRECTDEPS = \
|
||||||
LIBC_STUBS \
|
LIBC_STUBS \
|
||||||
|
LIBC_NEXGEN32E \
|
||||||
LIBC_TINYMATH
|
LIBC_TINYMATH
|
||||||
|
|
||||||
TOOL_BUILD_EMUBIN_DEPS := \
|
TOOL_BUILD_EMUBIN_DEPS := \
|
||||||
|
@ -57,10 +58,10 @@ o/dbg/tool/build/emubin/lisp.real.com.dbg: \
|
||||||
$(APE)
|
$(APE)
|
||||||
-@$(APELINK)
|
-@$(APELINK)
|
||||||
|
|
||||||
o/tiny/tool/build/emubin/lisp.bin.dbg: \
|
o/$(MODE)/tool/build/emubin/lisp.bin.dbg: \
|
||||||
$(TOOL_BUILD_EMUBIN_DEPS) \
|
$(TOOL_BUILD_EMUBIN_DEPS) \
|
||||||
o/tiny/tool/build/emubin/lisp.real.o \
|
o/$(MODE)/tool/build/emubin/lisp.real.o \
|
||||||
o/tiny/tool/build/emubin/lispstart.o \
|
o/$(MODE)/tool/build/emubin/lispstart.o \
|
||||||
tool/build/emubin/lisp.lds
|
tool/build/emubin/lisp.lds
|
||||||
@$(ELFLINK) -z max-page-size=0x10
|
@$(ELFLINK) -z max-page-size=0x10
|
||||||
|
|
||||||
|
|
|
@ -3,36 +3,35 @@
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||||
│ │
|
│ │
|
||||||
│ This program is free software; you can redistribute it and/or modify │
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
│ it under the terms of the GNU General Public License as published by │
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
│ the Free Software Foundation; version 2 of the License. │
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
│ │
|
│ │
|
||||||
│ This program is distributed in the hope that it will be useful, but │
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
│ General Public License for more details. │
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
│ │
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
│ You should have received a copy of the GNU General Public License │
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
│ along with this program; if not, write to the Free Software │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
│ 02110-1301 USA │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
|
||||||
#define TRACE 0
|
#define TRACE 0
|
||||||
#define ERRORS 1
|
#define ERRORS 1
|
||||||
#define LONG long
|
#define LONG long
|
||||||
#define WORD short
|
#define WORD short
|
||||||
#define WORDS 2048
|
#define WORDS 8192
|
||||||
|
|
||||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||||
│ The LISP Challenge § 8086 PC BIOS / x86_64 Linux System Integration ─╬─│┼
|
│ The LISP Challenge § Impure x86_64 Linux 8086 PC BIOS System Integration ─╬─│┼
|
||||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||||
|
|
||||||
#define ATOM(x) /* a.k.a. !(x&1) */ \
|
#define TYPE(x) /* a.k.a. x&1 */ \
|
||||||
({ \
|
({ \
|
||||||
char IsAtom; \
|
char IsAtom; \
|
||||||
asm("test%z1\t$1,%1" : "=@ccz"(IsAtom) : "Qm"((char)x)); \
|
asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \
|
||||||
IsAtom; \
|
IsAtom; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define OBJECT(t, v) /* a.k.a. v<<1|t */ \
|
#define OBJECT(t, v) /* a.k.a. v<<1|t */ \
|
||||||
|
@ -57,71 +56,109 @@
|
||||||
c; \
|
c; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
|
#define REAL_READ_(REG, BASE, INDEX, DISP) \
|
||||||
({ \
|
({ \
|
||||||
__typeof(*(BASE)) Reg; \
|
__typeof(*(BASE)) Reg; \
|
||||||
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
|
if (__builtin_constant_p(INDEX) && !(INDEX)) { \
|
||||||
asm("mov\t%c2(%1),%0" \
|
asm("mov\t%c2(%1),%0" \
|
||||||
: "=Q"(Reg) \
|
: REG(Reg) \
|
||||||
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE)))); \
|
: "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \
|
||||||
} else { \
|
"m"(BASE[(INDEX) + (DISP)])); \
|
||||||
asm("mov\t%c3(%1,%2),%0" \
|
} else { \
|
||||||
: "=Q"(Reg) \
|
asm("mov\t%c3(%1,%2),%0" \
|
||||||
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
|
: REG(Reg) \
|
||||||
"i"((DISP) * sizeof(*(BASE)))); \
|
: "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \
|
||||||
} \
|
"i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \
|
||||||
Reg; \
|
} \
|
||||||
|
Reg; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
|
/* #ifdef __REAL_MODE__ */
|
||||||
|
#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \
|
||||||
|
(sizeof(*(BASE)) == 1 ? REAL_READ_("=Q", BASE, INDEX, DISP) \
|
||||||
|
: REAL_READ_("=r", BASE, INDEX, DISP))
|
||||||
|
/* #else */
|
||||||
|
/* #define REAL_READ(BASE, INDEX, DISP) BASE[INDEX + DISP] */
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
#define REAL_READ_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP) \
|
||||||
({ \
|
({ \
|
||||||
__typeof(*(OBJECT->MEMBER)) Reg; \
|
__typeof(*(OBJECT->MEMBER)) Reg; \
|
||||||
if (!(OBJECT)) { \
|
if (!(OBJECT)) { \
|
||||||
asm("mov\t%c2(%1),%0" \
|
asm("mov\t%c2(%1),%0" \
|
||||||
: "=Q"(Reg) \
|
: REG(Reg) \
|
||||||
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
: "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||||
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
|
||||||
|
"m"(OBJECT->MEMBER)); \
|
||||||
} else { \
|
} else { \
|
||||||
asm("mov\t%c3(%1,%2),%0" \
|
asm("mov\t%c3(%1,%2),%0" \
|
||||||
: "=Q"(Reg) \
|
: REG(Reg) \
|
||||||
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
: "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||||
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
sizeof(*(OBJECT->MEMBER)) * (DISP)), \
|
||||||
|
"m"(OBJECT->MEMBER)); \
|
||||||
} \
|
} \
|
||||||
Reg; \
|
Reg; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \
|
/* #ifdef __REAL_MODE__ */
|
||||||
do { \
|
#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \
|
||||||
__typeof(*(OBJECT->MEMBER)) Reg; \
|
(sizeof(*(OBJECT->MEMBER)) == 1 \
|
||||||
if (!(OBJECT)) { \
|
? REAL_READ_ARRAY_FIELD_("=Q", OBJECT, MEMBER, INDEX, DISP) \
|
||||||
asm volatile("mov\t%0,%c2(%1)" \
|
: REAL_READ_ARRAY_FIELD_("=r", OBJECT, MEMBER, INDEX, DISP))
|
||||||
: /* manual output */ \
|
/* #else */
|
||||||
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
|
/* #define REAL_READ_ARRAY_FIELD(o, m, i, d) o->m[i + d] */
|
||||||
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
/* #endif */
|
||||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
|
||||||
sizeof(*(OBJECT->MEMBER)) * (DISP)) \
|
#define REAL_WRITE_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \
|
||||||
: "memory"); \
|
do { \
|
||||||
} else { \
|
if (!(OBJECT)) { \
|
||||||
asm volatile("mov\t%0,%c3(%1,%2)" \
|
asm("mov\t%1,%c3(%2)" \
|
||||||
: /* manual output */ \
|
: "=m"(OBJECT->MEMBER) \
|
||||||
: "Q"((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
|
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \
|
||||||
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
"bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||||
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||||
sizeof(*(OBJECT->MEMBER)) * (DISP)) \
|
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
||||||
: "memory"); \
|
} else { \
|
||||||
} \
|
asm("mov\t%1,%c4(%2,%3)" \
|
||||||
|
: "=m"(OBJECT->MEMBER) \
|
||||||
|
: REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \
|
||||||
|
"DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \
|
||||||
|
"i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \
|
||||||
|
sizeof(*(OBJECT->MEMBER)) * (DISP))); \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static void *SetMemory(void *di, int al, unsigned long cx) {
|
/* #ifdef __REAL_MODE__ */
|
||||||
|
#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \
|
||||||
|
do { \
|
||||||
|
__typeof(*(OBJECT->MEMBER)) Reg; \
|
||||||
|
switch (sizeof(*(OBJECT->MEMBER))) { \
|
||||||
|
case 1: \
|
||||||
|
REAL_WRITE_ARRAY_FIELD_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \
|
||||||
|
break; \
|
||||||
|
default: \
|
||||||
|
REAL_WRITE_ARRAY_FIELD_("ri", OBJECT, MEMBER, INDEX, DISP, VALUE); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
/* #else */
|
||||||
|
/* #define REAL_WRITE_ARRAY_FIELD(o, m, i, d, v) o->m[i + d] = v */
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
long jb[8];
|
||||||
|
int setjmp(void *) __attribute__((__returns_twice__));
|
||||||
|
int longjmp(void *, int) __attribute__((__noreturn__));
|
||||||
|
|
||||||
|
static inline void *SetMemory(void *di, int al, unsigned long cx) {
|
||||||
asm("rep stosb"
|
asm("rep stosb"
|
||||||
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
|
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
|
||||||
: "0"(di), "1"(cx), "a"(al));
|
: "0"(di), "1"(cx), "a"(al));
|
||||||
return di;
|
return di;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *CopyMemory(void *di, void *si, unsigned long cx) {
|
static inline void *CopyMemory(void *di, void *si, unsigned long cx) {
|
||||||
asm("rep movsb"
|
asm("rep movsb"
|
||||||
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
|
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
|
||||||
: "0"(di), "1"(si), "2"(cx));
|
: "0"(di), "1"(si), "2"(cx));
|
||||||
|
@ -147,14 +184,13 @@ static void RawMode(void) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__noinline__)) static int PrintChar(LONG c) {
|
__attribute__((__noinline__)) static void PrintChar(LONG c) {
|
||||||
#ifdef __REAL_MODE__
|
#ifdef __REAL_MODE__
|
||||||
asm volatile("mov\t$0x0E,%%ah\n\t"
|
asm volatile("mov\t$0x0E,%%ah\n\t"
|
||||||
"int\t$0x10"
|
"int\t$0x10"
|
||||||
: /* no outputs */
|
: /* no outputs */
|
||||||
: "a"(c), "b"(7)
|
: "a"(c), "b"(7)
|
||||||
: "memory");
|
: "memory");
|
||||||
return 0;
|
|
||||||
#else
|
#else
|
||||||
static short buf;
|
static short buf;
|
||||||
int rc;
|
int rc;
|
||||||
|
@ -163,7 +199,6 @@ __attribute__((__noinline__)) static int PrintChar(LONG c) {
|
||||||
: "=a"(rc)
|
: "=a"(rc)
|
||||||
: "0"(1), "D"(1), "S"(&buf), "d"(1)
|
: "0"(1), "D"(1), "S"(&buf), "d"(1)
|
||||||
: "rcx", "r11", "memory");
|
: "rcx", "r11", "memory");
|
||||||
return rc;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +212,7 @@ static void PrintString(char *s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int XlatChar(LONG c) {
|
static int XlatChar(LONG c) {
|
||||||
|
if (c == 0x7F) return '\b';
|
||||||
if (c >= 'a') {
|
if (c >= 'a') {
|
||||||
asm volatile("" ::: "memory");
|
asm volatile("" ::: "memory");
|
||||||
if (c <= 'z') c -= 'a' - 'A';
|
if (c <= 'z') c -= 'a' - 'A';
|
||||||
|
@ -185,19 +221,16 @@ static int XlatChar(LONG c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int EchoChar(LONG c) {
|
static int EchoChar(LONG c) {
|
||||||
if (c == '\b' || c == 0x7F) {
|
if (c != '\b') {
|
||||||
PrintString("\b \b");
|
|
||||||
return '\b';
|
|
||||||
} else {
|
|
||||||
PrintChar(c);
|
PrintChar(c);
|
||||||
if (c == '\r') {
|
if (c == '\r') {
|
||||||
PrintChar('\n');
|
PrintChar('\n');
|
||||||
}
|
}
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static noinline int ReadChar(void) {
|
__attribute__((__noinline__)) static noinline int ReadChar(void) {
|
||||||
int c;
|
int c;
|
||||||
#ifdef __REAL_MODE__
|
#ifdef __REAL_MODE__
|
||||||
asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory");
|
asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory");
|
||||||
|
@ -213,13 +246,13 @@ static noinline int ReadChar(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||||
│ The LISP Challenge § LISP Machine ─╬─│┼
|
│ The LISP Challenge § Pure Original LISP Machine ─╬─│┼
|
||||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||||
|
|
||||||
#define TYPE_ATOM 0
|
#define ATOM 0
|
||||||
#define TYPE_CONS 1
|
#define CONS 1
|
||||||
|
|
||||||
#define ATOM_NIL 0
|
#define NIL 0
|
||||||
#define ATOM_T 8
|
#define ATOM_T 8
|
||||||
#define ATOM_QUOTE 12
|
#define ATOM_QUOTE 12
|
||||||
#define ATOM_ATOM 24
|
#define ATOM_ATOM 24
|
||||||
|
@ -234,7 +267,7 @@ static noinline int ReadChar(void) {
|
||||||
#define ATOM_DEFUN 110
|
#define ATOM_DEFUN 110
|
||||||
|
|
||||||
#define Quote(x) List(ATOM_QUOTE, x)
|
#define Quote(x) List(ATOM_QUOTE, x)
|
||||||
#define List(x, y) Cons(x, Cons(y, ATOM_NIL))
|
#define List(x, y) Cons(x, Cons(y, NIL))
|
||||||
#define Caar(x) Car(Car(x)) // ((A B C D) (E F G) H I) → A
|
#define Caar(x) Car(Car(x)) // ((A B C D) (E F G) H I) → A
|
||||||
#define Cdar(x) Cdr(Car(x)) // ((A B C D) (E F G) H I) → (B C D)
|
#define Cdar(x) Cdr(Car(x)) // ((A B C D) (E F G) H I) → (B C D)
|
||||||
#define Cadar(x) Cadr(Car(x)) // ((A B C D) (E F G) H I) → B
|
#define Cadar(x) Cadr(Car(x)) // ((A B C D) (E F G) H I) → B
|
||||||
|
@ -242,22 +275,40 @@ static noinline int ReadChar(void) {
|
||||||
#define Cadr(x) Car(Cdr(x)) // ((A B C D) (E F G) H I) → (E F G)
|
#define Cadr(x) Car(Cdr(x)) // ((A B C D) (E F G) H I) → (E F G)
|
||||||
#define Caddr(x) Cadr(Cdr(x)) // ((A B C D) (E F G) H I) → H
|
#define Caddr(x) Cadr(Cdr(x)) // ((A B C D) (E F G) H I) → H
|
||||||
|
|
||||||
#define BOOL(x) ((x) ? ATOM_T : ATOM_NIL)
|
#define BOOL(x) ((x) ? ATOM_T : NIL)
|
||||||
#define VALUE(x) ((x) >> 1)
|
#define VALUE(x) ((x) >> 1)
|
||||||
|
#define PTR(i) ((i) << 1 | CONS)
|
||||||
|
|
||||||
|
#define ARRAYLEN(A) \
|
||||||
|
((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A)))))
|
||||||
|
|
||||||
struct Lisp {
|
struct Lisp {
|
||||||
WORD memory[WORDS];
|
WORD mem[WORDS];
|
||||||
unsigned char syntax[256];
|
unsigned char syntax[256];
|
||||||
unsigned char look;
|
WORD look;
|
||||||
char token[16];
|
|
||||||
WORD globals;
|
WORD globals;
|
||||||
int index;
|
WORD index;
|
||||||
|
char token[128];
|
||||||
char str[WORDS];
|
char str[WORDS];
|
||||||
};
|
};
|
||||||
|
|
||||||
const char kSymbols[] aligned(1) = "\
|
_Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600,
|
||||||
|
"LISP Machine too large for real mode");
|
||||||
|
|
||||||
|
_Alignas(char) const char kSymbols[] = "\
|
||||||
NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0";
|
NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0";
|
||||||
|
|
||||||
|
_Alignas(WORD) const WORD kGlobals[] = {
|
||||||
|
[0] = PTR(2), // ((T . T) (NIL . NIL))
|
||||||
|
[1] = PTR(4), //
|
||||||
|
[2] = ATOM_T, // (T . T)
|
||||||
|
[3] = ATOM_T, //
|
||||||
|
[4] = PTR(6), // ((NIL . NIL))
|
||||||
|
[5] = NIL, //
|
||||||
|
[6] = NIL, // (NIL . NIL)
|
||||||
|
[7] = NIL, //
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __REAL_MODE__
|
#ifdef __REAL_MODE__
|
||||||
static struct Lisp *const q;
|
static struct Lisp *const q;
|
||||||
#else
|
#else
|
||||||
|
@ -281,28 +332,39 @@ static void SetupSyntax(void) {
|
||||||
q->syntax['\''] = '\'';
|
q->syntax['\''] = '\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline WORD Car(LONG x) {
|
static inline WORD Car(LONG x) {
|
||||||
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 0);
|
return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline WORD Cdr(LONG x) {
|
static inline WORD Cdr(LONG x) {
|
||||||
return REAL_READ_ARRAY_FIELD(q, memory, VALUE(x), 1);
|
return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Cons(WORD car, WORD cdr) {
|
static WORD Cons(WORD car, WORD cdr) {
|
||||||
int i, c;
|
#if TRACE
|
||||||
|
PrintString("CONS->");
|
||||||
|
Print(car);
|
||||||
|
PrintString(" ");
|
||||||
|
Print(cdr);
|
||||||
|
#endif
|
||||||
|
int i, cell;
|
||||||
i = q->index;
|
i = q->index;
|
||||||
REAL_WRITE_ARRAY_FIELD(q, memory, i, 0, car);
|
REAL_WRITE_ARRAY_FIELD(q, mem, i, 0, car);
|
||||||
REAL_WRITE_ARRAY_FIELD(q, memory, i, 1, cdr);
|
REAL_WRITE_ARRAY_FIELD(q, mem, i, 1, cdr);
|
||||||
q->index += 2;
|
q->index = i + 2;
|
||||||
c = OBJECT(TYPE_CONS, i);
|
cell = OBJECT(CONS, i);
|
||||||
return c;
|
#if TRACE
|
||||||
|
PrintString("CONS<-");
|
||||||
|
Print(cell);
|
||||||
|
#endif
|
||||||
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetupBuiltins(void) {
|
static void SetupBuiltins(void) {
|
||||||
CopyMemory(q->str, kSymbols, sizeof(kSymbols));
|
CopyMemory(q->str, kSymbols, sizeof(kSymbols));
|
||||||
q->globals =
|
CopyMemory(q->mem, kGlobals, sizeof(kGlobals));
|
||||||
Cons(Cons(ATOM_NIL, ATOM_NIL), Cons(Cons(ATOM_T, ATOM_T), ATOM_NIL));
|
q->index = ARRAYLEN(kGlobals);
|
||||||
|
q->globals = PTR(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *StpCpy(char *d, char *s) {
|
static char *StpCpy(char *d, char *s) {
|
||||||
|
@ -314,7 +376,7 @@ static char *StpCpy(char *d, char *s) {
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Intern(char *s) {
|
WORD Intern(char *s) {
|
||||||
int j, cx;
|
int j, cx;
|
||||||
char c, *z, *t;
|
char c, *z, *t;
|
||||||
z = q->str;
|
z = q->str;
|
||||||
|
@ -325,7 +387,7 @@ static WORD Intern(char *s) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!c) {
|
if (!c) {
|
||||||
return OBJECT(TYPE_ATOM, z - q->str - j - 1);
|
return OBJECT(ATOM, z - q->str - j - 1);
|
||||||
}
|
}
|
||||||
c = LODS(z);
|
c = LODS(z);
|
||||||
}
|
}
|
||||||
|
@ -334,11 +396,11 @@ static WORD Intern(char *s) {
|
||||||
}
|
}
|
||||||
--z;
|
--z;
|
||||||
StpCpy(z, s);
|
StpCpy(z, s);
|
||||||
return OBJECT(TYPE_ATOM, SUB((long)z, q->str));
|
return OBJECT(ATOM, SUB((long)z, q->str));
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline unsigned char XlatSyntax(unsigned char b) {
|
forceinline unsigned char XlatSyntax(unsigned char b) {
|
||||||
return REAL_READ_ARRAY_FIELD(q, syntax, b, 0); /* a.k.a. q->syntax[b] */
|
return REAL_READ_ARRAY_FIELD(q, syntax, b, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GetToken(void) {
|
static void GetToken(void) {
|
||||||
|
@ -357,7 +419,8 @@ static void GetToken(void) {
|
||||||
if (b != '\b') {
|
if (b != '\b') {
|
||||||
STOS(t, b);
|
STOS(t, b);
|
||||||
} else if (t > q->token) {
|
} else if (t > q->token) {
|
||||||
--t;
|
PrintString("\b \b");
|
||||||
|
if (t > q->token) --t;
|
||||||
}
|
}
|
||||||
b = ReadChar();
|
b = ReadChar();
|
||||||
}
|
}
|
||||||
|
@ -387,7 +450,7 @@ static WORD GetList(void) {
|
||||||
case '\'':
|
case '\'':
|
||||||
return AddList(GetQuote());
|
return AddList(GetQuote());
|
||||||
case ')':
|
case ')':
|
||||||
return ATOM_NIL;
|
return NIL;
|
||||||
case '.':
|
case '.':
|
||||||
return ConsumeObject();
|
return ConsumeObject();
|
||||||
}
|
}
|
||||||
|
@ -422,7 +485,7 @@ static void PrintList(LONG x) {
|
||||||
PrintChar('(');
|
PrintChar('(');
|
||||||
PrintObject(Car(x));
|
PrintObject(Car(x));
|
||||||
while ((x = Cdr(x))) {
|
while ((x = Cdr(x))) {
|
||||||
if (!ATOM(x)) {
|
if (TYPE(x) == CONS) {
|
||||||
PrintChar(' ');
|
PrintChar(' ');
|
||||||
PrintObject(Car(x));
|
PrintObject(Car(x));
|
||||||
} else {
|
} else {
|
||||||
|
@ -434,7 +497,7 @@ static void PrintList(LONG x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintObject(LONG x) {
|
static void PrintObject(LONG x) {
|
||||||
if (ATOM(x)) {
|
if (TYPE(x) == ATOM) {
|
||||||
PrintAtom(x);
|
PrintAtom(x);
|
||||||
} else {
|
} else {
|
||||||
PrintList(x);
|
PrintList(x);
|
||||||
|
@ -447,8 +510,7 @@ static void Print(LONG i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__noreturn__)) static void Reset(void) {
|
__attribute__((__noreturn__)) static void Reset(void) {
|
||||||
asm volatile("jmp\tRepl");
|
longjmp(jb, 1);
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__noreturn__)) static void OnUndefined(LONG x) {
|
__attribute__((__noreturn__)) static void OnUndefined(LONG x) {
|
||||||
|
@ -472,7 +534,7 @@ __attribute__((__noreturn__)) static void OnArity(void) {
|
||||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||||
|
|
||||||
static WORD Atom(LONG x) {
|
static WORD Atom(LONG x) {
|
||||||
return BOOL(ATOM(x));
|
return BOOL(TYPE(x) == ATOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Null(LONG x) {
|
static WORD Null(LONG x) {
|
||||||
|
@ -480,24 +542,32 @@ static WORD Null(LONG x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Eq(LONG x, LONG y) {
|
static WORD Eq(LONG x, LONG y) {
|
||||||
return BOOL(x == y); /* undefiled if !ATOM(x)||!ATOM(y) */
|
return BOOL(x == y); /* undefined if !Atom(x)||!Atom(y) */
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Assoc(LONG x, LONG y) {
|
static WORD Assoc(LONG x, LONG y) {
|
||||||
for (;;) {
|
if (Null(y)) OnUndefined(x);
|
||||||
if (!y) OnUndefined(x);
|
if (Eq(Caar(y), x)) return Cdar(y);
|
||||||
if (Eq(Caar(y), x)) break;
|
return Assoc(x, Cdr(y));
|
||||||
y = Cdr(y);
|
|
||||||
}
|
|
||||||
return Cdar(y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Append(LONG x, LONG y) {
|
static WORD Append(LONG x, LONG y) {
|
||||||
if (x) {
|
#if TRACE
|
||||||
return Cons(Car(x), Append(Cdr(x), y));
|
PrintString("APPEND->");
|
||||||
|
Print(x);
|
||||||
|
PrintString(" ");
|
||||||
|
Print(y);
|
||||||
|
#endif
|
||||||
|
if (!Null(x)) {
|
||||||
|
x = Cons(Car(x), Append(Cdr(x), y));
|
||||||
} else {
|
} else {
|
||||||
return y;
|
x = y;
|
||||||
}
|
}
|
||||||
|
#if TRACE
|
||||||
|
PrintString("APPEND<-");
|
||||||
|
Print(x);
|
||||||
|
#endif
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -506,26 +576,41 @@ static WORD Append(LONG x, LONG y) {
|
||||||
* @note recoded to make lists in dot notation
|
* @note recoded to make lists in dot notation
|
||||||
* @note it's zip() basically
|
* @note it's zip() basically
|
||||||
*/
|
*/
|
||||||
static WORD Pair(LONG x, LONG y) {
|
static WORD Pair_(LONG x, LONG y) {
|
||||||
if (!x && !y) {
|
if (Null(x) && Null(y)) {
|
||||||
return ATOM_NIL;
|
return NIL;
|
||||||
} else if (!ATOM(x) && !ATOM(y)) {
|
} else if (TYPE(x) == CONS && TYPE(y) == CONS) {
|
||||||
return Cons(Cons(Car(x), Car(y)), Pair(Cdr(x), Cdr(y)));
|
return Cons(Cons(Car(x), Car(y)), Pair_(Cdr(x), Cdr(y)));
|
||||||
} else {
|
} else {
|
||||||
OnArity();
|
OnArity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WORD Pair(LONG x, LONG y) {
|
||||||
|
#if TRACE
|
||||||
|
PrintString("PAIR->");
|
||||||
|
Print(x);
|
||||||
|
PrintString(" ");
|
||||||
|
Print(y);
|
||||||
|
#endif
|
||||||
|
x = Pair_(x, y);
|
||||||
|
#if TRACE
|
||||||
|
PrintString("PAIR<-");
|
||||||
|
Print(x);
|
||||||
|
#endif
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
static WORD Appq(long m) {
|
static WORD Appq(long m) {
|
||||||
if (m) {
|
if (m) {
|
||||||
return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m)));
|
return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m)));
|
||||||
} else {
|
} else {
|
||||||
return ATOM_NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Apply(long f, long a) {
|
static WORD Apply(long f, long a) {
|
||||||
return Eval(Cons(f, Appq(a)), ATOM_NIL);
|
return Eval(Cons(f, Appq(a)), NIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Evcon(LONG c, LONG a) {
|
static WORD Evcon(LONG c, LONG a) {
|
||||||
|
@ -536,14 +621,29 @@ static WORD Evcon(LONG c, LONG a) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Evlis(LONG m, LONG a) {
|
static WORD Evlis_(LONG m, LONG a) {
|
||||||
if (m) {
|
if (m) {
|
||||||
return Cons(Eval(Car(m), a), Evlis(Cdr(m), a));
|
return Cons(Eval(Car(m), a), Evlis_(Cdr(m), a));
|
||||||
} else {
|
} else {
|
||||||
return ATOM_NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WORD Evlis(LONG m, LONG a) {
|
||||||
|
#if TRACE
|
||||||
|
PrintString("EVLIS->");
|
||||||
|
Print(m);
|
||||||
|
PrintString(" ");
|
||||||
|
Print(a);
|
||||||
|
#endif
|
||||||
|
m = Evlis_(m, a);
|
||||||
|
#if TRACE
|
||||||
|
PrintString("EVLIS<-");
|
||||||
|
Print(m);
|
||||||
|
#endif
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
static WORD Set(LONG e) {
|
static WORD Set(LONG e) {
|
||||||
WORD name, value;
|
WORD name, value;
|
||||||
name = Car(e);
|
name = Car(e);
|
||||||
|
@ -563,9 +663,9 @@ static WORD Defun(LONG e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static WORD Evaluate(LONG e, LONG a) {
|
static WORD Evaluate(LONG e, LONG a) {
|
||||||
if (ATOM(e)) {
|
if (Atom(e)) {
|
||||||
return Assoc(e, a);
|
return Assoc(e, a);
|
||||||
} else if (ATOM(Car(e))) {
|
} else if (Atom(Car(e))) {
|
||||||
switch (Car(e)) {
|
switch (Car(e)) {
|
||||||
case ATOM_QUOTE:
|
case ATOM_QUOTE:
|
||||||
return Cadr(e);
|
return Cadr(e);
|
||||||
|
@ -601,6 +701,8 @@ static WORD Eval(LONG e, LONG a) {
|
||||||
#if TRACE
|
#if TRACE
|
||||||
PrintString("->");
|
PrintString("->");
|
||||||
Print(e);
|
Print(e);
|
||||||
|
PrintString(" ");
|
||||||
|
Print(a);
|
||||||
#endif
|
#endif
|
||||||
e = Evaluate(e, a);
|
e = Evaluate(e, a);
|
||||||
#if TRACE
|
#if TRACE
|
||||||
|
@ -615,6 +717,7 @@ static WORD Eval(LONG e, LONG a) {
|
||||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||||
|
|
||||||
void Repl(void) {
|
void Repl(void) {
|
||||||
|
setjmp(jb);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
PrintString("* ");
|
PrintString("* ");
|
||||||
Print(Eval(Read(), q->globals));
|
Print(Eval(Read(), q->globals));
|
||||||
|
@ -622,7 +725,7 @@ void Repl(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
RawMode();
|
/* RawMode(); */
|
||||||
SetupSyntax();
|
SetupSyntax();
|
||||||
SetupBuiltins();
|
SetupBuiltins();
|
||||||
PrintString("THE LISP CHALLENGE V1\r\n"
|
PrintString("THE LISP CHALLENGE V1\r\n"
|
|
@ -29,6 +29,7 @@ SECTIONS {
|
||||||
. = 0x1fe;
|
. = 0x1fe;
|
||||||
SHORT(0xaa55);
|
SHORT(0xaa55);
|
||||||
*(.text .text.*)
|
*(.text .text.*)
|
||||||
|
_etext = .;
|
||||||
. = ALIGN(512);
|
. = ALIGN(512);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +44,11 @@ SECTIONS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
syntax = 0x600+2048*2;
|
boot = 0x7c00;
|
||||||
look = 0x600+2048*2+256;
|
q.syntax = 8192*2;
|
||||||
token = 0x600+2048*2+256+1;
|
q.look = 8192*2+256;
|
||||||
globals = 0x600+2048*2+256+1+16;
|
q.globals = 8192*2+256+2;
|
||||||
index = 0x600+2048*2+256+1+16+2;
|
q.index = 8192*2+256+2+2;
|
||||||
str = 0x600+2048*2+256+1+16+2+4;
|
q.token = 8192*2+256+2+2+2;
|
||||||
v_sectors = SIZEOF(.text) / 512;
|
q.str = 8192*2+256+2+2+2+128;
|
||||||
|
v_sectors = SIZEOF(.text) / 512;
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
.code16
|
.code16
|
||||||
.section .start,"ax",@progbits
|
.section .start,"ax",@progbits
|
||||||
_start: jmp 1f
|
_start: jmp 1f
|
||||||
1: ljmp $0x600>>4,$2f
|
1: ljmp $0x600>>4,$_begin
|
||||||
2: push %cs
|
_begin: push %cs
|
||||||
pop %ds
|
pop %ds
|
||||||
push %cs
|
push %cs
|
||||||
pop %es
|
pop %es
|
||||||
|
@ -50,3 +50,45 @@ _start: jmp 1f
|
||||||
.globl _start
|
.globl _start
|
||||||
.globl v_sectors
|
.globl v_sectors
|
||||||
.globl main
|
.globl main
|
||||||
|
|
||||||
|
setjmp: mov %sp,%ax
|
||||||
|
stosw # sp
|
||||||
|
xchg %ax,%si
|
||||||
|
movsw %ss:(%si),(%di) # ip
|
||||||
|
mov %bp,%ax
|
||||||
|
stosw # bp
|
||||||
|
ret
|
||||||
|
.type setjmp,@function
|
||||||
|
.size setjmp,.-setjmp
|
||||||
|
.globl setjmp
|
||||||
|
|
||||||
|
longjmp:
|
||||||
|
mov (%di),%sp
|
||||||
|
mov 2(%di),%dx
|
||||||
|
mov 4(%di),%bp
|
||||||
|
pop %ax
|
||||||
|
mov %si,%ax
|
||||||
|
jmp *%dx
|
||||||
|
.type longjmp,@function
|
||||||
|
.size longjmp,.-longjmp
|
||||||
|
.globl longjmp
|
||||||
|
|
||||||
|
.globl q.syntax
|
||||||
|
.type q.syntax,@function
|
||||||
|
.globl q.look
|
||||||
|
.type q.look,@function
|
||||||
|
.globl q.globals
|
||||||
|
.type q.globals,@function
|
||||||
|
.globl q.index
|
||||||
|
.type q.index,@function
|
||||||
|
.globl q.token
|
||||||
|
.type q.token,@function
|
||||||
|
.globl q.str
|
||||||
|
.type q.str,@function
|
||||||
|
|
||||||
|
.globl boot
|
||||||
|
.type boot,@function
|
||||||
|
.globl bss
|
||||||
|
.type bss,@function
|
||||||
|
.globl rodata
|
||||||
|
.type rodata,@function
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
#include "libc/sysv/consts/sig.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
#include "libc/sysv/consts/termios.h"
|
#include "libc/sysv/consts/termios.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
#include "libc/time/time.h"
|
||||||
#include "libc/unicode/unicode.h"
|
#include "libc/unicode/unicode.h"
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
#include "third_party/dtoa/dtoa.h"
|
#include "third_party/dtoa/dtoa.h"
|
||||||
|
@ -91,8 +92,7 @@ DESCRIPTION\n\
|
||||||
\n\
|
\n\
|
||||||
FLAGS\n\
|
FLAGS\n\
|
||||||
\n\
|
\n\
|
||||||
-h\n\
|
-h help\n\
|
||||||
-? help\n\
|
|
||||||
-v verbosity\n\
|
-v verbosity\n\
|
||||||
-r real mode\n\
|
-r real mode\n\
|
||||||
-s statistics\n\
|
-s statistics\n\
|
||||||
|
@ -137,8 +137,9 @@ COMPLETENESS\n\
|
||||||
#define QUIT 0x200
|
#define QUIT 0x200
|
||||||
#define EXIT 0x400
|
#define EXIT 0x400
|
||||||
|
|
||||||
#define CTRL(C) ((C) ^ 0100)
|
#define CTRL(C) ((C) ^ 0100)
|
||||||
#define ALT(C) (('\e' << 010) | (C))
|
#define ALT(C) (('\e' << 010) | (C))
|
||||||
|
#define SEX(x, b) ((x) | ((x) & (1ull << (b)) ? -(1ull << (b)) : 0))
|
||||||
|
|
||||||
struct Panels {
|
struct Panels {
|
||||||
union {
|
union {
|
||||||
|
@ -215,16 +216,6 @@ static struct Breakpoints breakpoints;
|
||||||
static void SetupDraw(void);
|
static void SetupDraw(void);
|
||||||
static void Redraw(void);
|
static void Redraw(void);
|
||||||
|
|
||||||
static uint64_t SignExtend(uint64_t x, uint8_t b) {
|
|
||||||
uint64_t s;
|
|
||||||
s = 1;
|
|
||||||
b -= 1;
|
|
||||||
b &= 63;
|
|
||||||
s <<= b;
|
|
||||||
if (x & s) x |= ~(s - 1);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *FormatDouble(char *b, double x) {
|
static char *FormatDouble(char *b, double x) {
|
||||||
return g_fmt(b, x);
|
return g_fmt(b, x);
|
||||||
}
|
}
|
||||||
|
@ -234,16 +225,15 @@ static void SetCarry(bool cf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsCall(void) {
|
static bool IsCall(void) {
|
||||||
return m->xedd->op.map == XED_ILD_MAP0 &&
|
return (m->xedd->op.dispatch == 0x0E8 ||
|
||||||
(m->xedd->op.opcode == 0xE8 ||
|
(m->xedd->op.dispatch == 0x0FF && m->xedd->op.reg == 2));
|
||||||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsLongBranch(void) {
|
static bool IsLongBranch(void) {
|
||||||
return m->xedd && m->xedd->op.map == XED_ILD_MAP0 &&
|
return m->mode != XED_MODE_LONG &&
|
||||||
(m->xedd->op.opcode == 0xEA || m->xedd->op.opcode == 0x9A ||
|
(m->xedd->op.dispatch == 0x0EA || m->xedd->op.dispatch == 0x09A ||
|
||||||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 3) ||
|
(m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 3) ||
|
||||||
(m->xedd->op.opcode == 0xFF && m->xedd->op.reg == 5));
|
(m->xedd->op.opcode == 0x0FF && m->xedd->op.reg == 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsDebugBreak(void) {
|
static bool IsDebugBreak(void) {
|
||||||
|
@ -370,13 +360,16 @@ static void GetTtySize(void) {
|
||||||
static void TuiRejuvinate(void) {
|
static void TuiRejuvinate(void) {
|
||||||
GetTtySize();
|
GetTtySize();
|
||||||
ttyhidecursor(STDOUT_FILENO);
|
ttyhidecursor(STDOUT_FILENO);
|
||||||
ttyraw(0);
|
ttyraw(kTtySigs);
|
||||||
xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL);
|
xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnCtrlC(void) {
|
static void OnCtrlC(void) {
|
||||||
LOGF("OnCtrlC");
|
if (tuimode) {
|
||||||
action |= INT;
|
action |= INT;
|
||||||
|
} else {
|
||||||
|
HaltMachine(m, kMachineExit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnQ(void) {
|
static void OnQ(void) {
|
||||||
|
@ -404,7 +397,7 @@ static void OnCont(void) {
|
||||||
static void TuiCleanup(void) {
|
static void TuiCleanup(void) {
|
||||||
sigaction(SIGWINCH, oldsig + 0, NULL);
|
sigaction(SIGWINCH, oldsig + 0, NULL);
|
||||||
sigaction(SIGCONT, oldsig + 2, NULL);
|
sigaction(SIGCONT, oldsig + 2, NULL);
|
||||||
ttyraw((enum TtyRawFlags)(-1u));
|
ttyraw(-1);
|
||||||
ttyshowcursor(STDOUT_FILENO);
|
ttyshowcursor(STDOUT_FILENO);
|
||||||
CHECK_NE(-1, close(ttyfd));
|
CHECK_NE(-1, close(ttyfd));
|
||||||
tuimode = false;
|
tuimode = false;
|
||||||
|
@ -447,6 +440,7 @@ void TuiSetup(void) {
|
||||||
CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR)));
|
CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR)));
|
||||||
xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0);
|
xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0);
|
||||||
xsigaction(SIGCONT, OnCont, SA_RESTART, 0, oldsig + 2);
|
xsigaction(SIGCONT, OnCont, SA_RESTART, 0, oldsig + 2);
|
||||||
|
xsigaction(SIGINT, OnCtrlC, 0 /* SA_NODEFER */, 0, oldsig + 3);
|
||||||
memcpy(&m[1], &m[0], sizeof(m[0]));
|
memcpy(&m[1], &m[0], sizeof(m[0]));
|
||||||
TuiRejuvinate();
|
TuiRejuvinate();
|
||||||
}
|
}
|
||||||
|
@ -660,12 +654,19 @@ static void SetupDraw(void) {
|
||||||
pan.display.right - pan.display.left);
|
pan.display.right - pan.display.left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long Disassemble(void) {
|
||||||
|
long lines, current;
|
||||||
|
lines = pan.disassembly.bottom - pan.disassembly.top * 2;
|
||||||
|
CHECK_NE(-1, Dis(dis, m, GetIp(), m->ip, lines));
|
||||||
|
current = DisFind(dis, GetIp());
|
||||||
|
CHECK_NE(-1, current);
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
static long GetDisIndex(void) {
|
static long GetDisIndex(void) {
|
||||||
long i;
|
long i;
|
||||||
if ((i = DisFind(dis, GetIp())) == -1 || IsLongBranch()) {
|
if ((i = DisFind(dis, GetIp())) == -1) {
|
||||||
Dis(dis, m, GetIp(), m->ip,
|
i = Disassemble();
|
||||||
pan.disassembly.bottom - pan.disassembly.top * 2);
|
|
||||||
CHECK_NE(-1, (i = DisFind(dis, GetIp())));
|
|
||||||
}
|
}
|
||||||
while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++i;
|
while (i + 1 < dis->ops.i && !dis->ops.p[i].size) ++i;
|
||||||
return i;
|
return i;
|
||||||
|
@ -864,7 +865,7 @@ static void DrawXmm(struct Panel *p, long i, long r) {
|
||||||
if (0 && ssewidth == 1 && (040 <= ival && ival < 0200 - 1)) {
|
if (0 && ssewidth == 1 && (040 <= ival && ival < 0200 - 1)) {
|
||||||
sprintf(buf, "%`'c", ival);
|
sprintf(buf, "%`'c", ival);
|
||||||
} else {
|
} else {
|
||||||
int64toarray_radix10(SignExtend(ival, ssewidth * 8), buf);
|
int64toarray_radix10(SEX(ival, ssewidth * 8), buf);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint64toarray_fixed16(ival, buf, ssewidth * 8);
|
uint64toarray_fixed16(ival, buf, ssewidth * 8);
|
||||||
|
@ -977,7 +978,7 @@ static void DrawBreakpoints(struct Panel *p) {
|
||||||
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
|
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
|
||||||
s = buf;
|
s = buf;
|
||||||
s += sprintf(s, "%p ", addr);
|
s += sprintf(s, "%p ", addr);
|
||||||
CHECK_LT(Demangle(s, name), buf + ARRAYLEN(buf));
|
CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf));
|
||||||
AppendPanel(p, line, buf);
|
AppendPanel(p, line, buf);
|
||||||
if (sym != -1 && addr != dis->syms.p[sym].addr) {
|
if (sym != -1 && addr != dis->syms.p[sym].addr) {
|
||||||
snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr);
|
snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr);
|
||||||
|
@ -1015,7 +1016,7 @@ static void DrawTrace(struct Panel *p) {
|
||||||
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
|
name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN";
|
||||||
s = line;
|
s = line;
|
||||||
s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp);
|
s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp);
|
||||||
s = Demangle(s, name);
|
s = Demangle(s, name, DIS_MAX_SYMBOL_LENGTH);
|
||||||
AppendPanel(p, i, line);
|
AppendPanel(p, i, line);
|
||||||
if (sym != -1 && rp != dis->syms.p[sym].addr) {
|
if (sym != -1 && rp != dis->syms.p[sym].addr) {
|
||||||
snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr);
|
snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr);
|
||||||
|
@ -1417,7 +1418,6 @@ static void OnVidyaServiceTeletypeOutput(void) {
|
||||||
char buf[12];
|
char buf[12];
|
||||||
n = FormatCga(m->bx[0], buf);
|
n = FormatCga(m->bx[0], buf);
|
||||||
n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false);
|
n += tpencode(buf + n, 6, VidyaServiceXlatTeletype(m->ax[0]), false);
|
||||||
LOGF("teletype output %`'.*s", n, buf);
|
|
||||||
MachinePtyWrite(pty, buf, n);
|
MachinePtyWrite(pty, buf, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1520,6 +1520,7 @@ static void OnInt15h(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool OnHalt(int interrupt) {
|
static bool OnHalt(int interrupt) {
|
||||||
|
LOGF("OnHalt(%d)", interrupt);
|
||||||
ReactiveDraw();
|
ReactiveDraw();
|
||||||
switch (interrupt) {
|
switch (interrupt) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -1577,15 +1578,24 @@ static void OnPageDown(void) {
|
||||||
|
|
||||||
static void OnUpArrow(void) {
|
static void OnUpArrow(void) {
|
||||||
if (action & CONTINUE) {
|
if (action & CONTINUE) {
|
||||||
speed = MIN(0x40000000, MAX(1, speed) << 1);
|
if (speed >= -1) {
|
||||||
|
speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip
|
||||||
|
} else {
|
||||||
|
speed >>= 1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
--opstart;
|
--opstart;
|
||||||
}
|
}
|
||||||
|
LOGF("speed %d", speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnDownArrow(void) {
|
static void OnDownArrow(void) {
|
||||||
if (action & CONTINUE) {
|
if (action & CONTINUE) {
|
||||||
speed >>= 1;
|
if (speed > 0) {
|
||||||
|
speed >>= 1;
|
||||||
|
} else {
|
||||||
|
speed = MAX(-(5 * 1000), MIN(-10, speed) << 1); // 10ms..5s delay
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
++opstart;
|
++opstart;
|
||||||
}
|
}
|
||||||
|
@ -1897,6 +1907,9 @@ static void Tui(void) {
|
||||||
action &= ~WINCHED;
|
action &= ~WINCHED;
|
||||||
}
|
}
|
||||||
interactive = ++tick > speed;
|
interactive = ++tick > speed;
|
||||||
|
if (interactive && speed < 0) {
|
||||||
|
dsleep(.001L * -speed);
|
||||||
|
}
|
||||||
if (!(action & CONTINUE) || interactive) {
|
if (!(action & CONTINUE) || interactive) {
|
||||||
tick = 0;
|
tick = 0;
|
||||||
GetDisIndex();
|
GetDisIndex();
|
||||||
|
@ -1907,7 +1920,8 @@ static void Tui(void) {
|
||||||
LOGF("TUI FAILURE");
|
LOGF("TUI FAILURE");
|
||||||
PrintMessageBox(ttyfd, systemfailure, tyn, txn);
|
PrintMessageBox(ttyfd, systemfailure, tyn, txn);
|
||||||
ReadKeyboard();
|
ReadKeyboard();
|
||||||
} else if (!IsExecuting() || (interactive && HasPendingKeyboard())) {
|
} else if (!IsExecuting() || ((interactive || !(action & CONTINUE)) &&
|
||||||
|
!(action & INT) && HasPendingKeyboard())) {
|
||||||
ReadKeyboard();
|
ReadKeyboard();
|
||||||
}
|
}
|
||||||
if (action & INT) {
|
if (action & INT) {
|
||||||
|
@ -1961,6 +1975,9 @@ static void Tui(void) {
|
||||||
}
|
}
|
||||||
if (!IsDebugBreak()) {
|
if (!IsDebugBreak()) {
|
||||||
ExecuteInstruction(m);
|
ExecuteInstruction(m);
|
||||||
|
if (IsLongBranch()) {
|
||||||
|
Disassemble();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m->ip += m->xedd->length;
|
m->ip += m->xedd->length;
|
||||||
action &= ~NEXT;
|
action &= ~NEXT;
|
||||||
|
@ -1997,7 +2014,7 @@ static void Tui(void) {
|
||||||
static void GetOpts(int argc, char *argv[]) {
|
static void GetOpts(int argc, char *argv[]) {
|
||||||
int opt;
|
int opt;
|
||||||
stpcpy(stpcpy(stpcpy(logpath, kTmpPath), basename(argv[0])), ".log");
|
stpcpy(stpcpy(stpcpy(logpath, kTmpPath), basename(argv[0])), ".log");
|
||||||
while ((opt = getopt(argc, argv, "?hvtrRsb:HL:")) != -1) {
|
while ((opt = getopt(argc, argv, "hvtrRsb:HL:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 't':
|
case 't':
|
||||||
tuimode = true;
|
tuimode = true;
|
||||||
|
@ -2007,6 +2024,7 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
m->mode = XED_MACHINE_MODE_REAL;
|
m->mode = XED_MACHINE_MODE_REAL;
|
||||||
|
g_disisprog_disable = true;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
printstats = true;
|
printstats = true;
|
||||||
|
@ -2024,7 +2042,6 @@ static void GetOpts(int argc, char *argv[]) {
|
||||||
strcpy(logpath, optarg);
|
strcpy(logpath, optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
|
||||||
PrintUsage(EXIT_SUCCESS, stdout);
|
PrintUsage(EXIT_SUCCESS, stdout);
|
||||||
default:
|
default:
|
||||||
PrintUsage(EX_USAGE, stderr);
|
PrintUsage(EX_USAGE, stderr);
|
||||||
|
|
|
@ -64,13 +64,12 @@ ssize_t WriteBuffer(struct Buffer *b, int fd) {
|
||||||
p = b->p;
|
p = b->p;
|
||||||
n = b->i;
|
n = b->i;
|
||||||
do {
|
do {
|
||||||
TryAgain:
|
|
||||||
if ((rc = write(fd, p, n)) != -1) {
|
if ((rc = write(fd, p, n)) != -1) {
|
||||||
wrote = rc;
|
wrote = rc;
|
||||||
p += wrote;
|
p += wrote;
|
||||||
n -= wrote;
|
n -= wrote;
|
||||||
} else if (errno == EINTR) {
|
} else if (errno == EINTR) {
|
||||||
goto TryAgain;
|
break;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,14 @@
|
||||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||||
│ 02110-1301 USA │
|
│ 02110-1301 USA │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/assert.h"
|
||||||
|
#include "libc/bits/safemacros.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/hefty/spawn.h"
|
#include "libc/calls/hefty/spawn.h"
|
||||||
|
#include "libc/calls/struct/iovec.h"
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/sock/sock.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "tool/build/lib/demangle.h"
|
#include "tool/build/lib/demangle.h"
|
||||||
|
|
||||||
|
@ -37,12 +41,14 @@ void CloseCxxFilt(void) {
|
||||||
|
|
||||||
void SpawnCxxFilt(void) {
|
void SpawnCxxFilt(void) {
|
||||||
int pid;
|
int pid;
|
||||||
|
const char *cxxfilt;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
if (commandv("c++filt", path)) {
|
cxxfilt = firstnonnull(getenv("CXXFILT"), "c++filt");
|
||||||
|
if (commandv(cxxfilt, path)) {
|
||||||
g_cxxfilt.fds[0] = -1;
|
g_cxxfilt.fds[0] = -1;
|
||||||
g_cxxfilt.fds[1] = -1;
|
g_cxxfilt.fds[1] = -1;
|
||||||
g_cxxfilt.fds[2] = 2;
|
g_cxxfilt.fds[2] = 2;
|
||||||
if ((pid = spawnve(0, g_cxxfilt.fds, path, (char *const[]){"c++filt", NULL},
|
if ((pid = spawnve(0, g_cxxfilt.fds, path, (char *const[]){cxxfilt, NULL},
|
||||||
environ)) != -1) {
|
environ)) != -1) {
|
||||||
atexit(CloseCxxFilt);
|
atexit(CloseCxxFilt);
|
||||||
}
|
}
|
||||||
|
@ -52,30 +58,50 @@ void SpawnCxxFilt(void) {
|
||||||
g_cxxfilt.pid = pid;
|
g_cxxfilt.pid = pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *DemangleCxxFilt(char *p, const char *symbol) {
|
char *CopySymbol(char *p, size_t pn, const char *s, size_t sn) {
|
||||||
int n;
|
size_t extra;
|
||||||
char buf[512];
|
bool showdots, iscomplicated;
|
||||||
bool iscomplicated;
|
assert(pn >= 1 + 3 + 1 + 1);
|
||||||
|
iscomplicated = memchr(s, ' ', sn) || memchr(s, '(', sn);
|
||||||
|
extra = 1;
|
||||||
|
if (iscomplicated) extra += 2;
|
||||||
|
if (sn + extra > pn) {
|
||||||
|
sn = pn - extra - 3;
|
||||||
|
showdots = true;
|
||||||
|
} else {
|
||||||
|
showdots = false;
|
||||||
|
}
|
||||||
|
if (iscomplicated) *p++ = '"';
|
||||||
|
p = mempcpy(p, s, sn);
|
||||||
|
if (showdots) p = stpcpy(p, "...");
|
||||||
|
if (iscomplicated) *p++ = '"';
|
||||||
|
*p = '\0';
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *DemangleCxxFilt(char *p, size_t pn, const char *s, size_t sn) {
|
||||||
|
ssize_t rc;
|
||||||
|
size_t got;
|
||||||
|
struct iovec iov[2];
|
||||||
|
static char buf[PAGESIZE];
|
||||||
if (!g_cxxfilt.pid) SpawnCxxFilt();
|
if (!g_cxxfilt.pid) SpawnCxxFilt();
|
||||||
if (g_cxxfilt.pid == -1) return NULL;
|
if (g_cxxfilt.pid == -1) return NULL;
|
||||||
if ((n = strlen(symbol)) >= ARRAYLEN(buf)) return NULL;
|
buf[0] = '\n';
|
||||||
memcpy(buf, symbol, n);
|
iov[0].iov_base = s;
|
||||||
buf[n] = '\n';
|
iov[0].iov_len = sn;
|
||||||
write(g_cxxfilt.fds[0], buf, n + 1);
|
iov[1].iov_base = buf;
|
||||||
n = read(g_cxxfilt.fds[1], buf, ARRAYLEN(buf));
|
iov[1].iov_len = 1;
|
||||||
if (n > 1 && buf[n - 1] == '\n') {
|
writev(g_cxxfilt.fds[0], iov, ARRAYLEN(iov));
|
||||||
if (buf[n - 2] == '\r') --n;
|
if ((rc = read(g_cxxfilt.fds[1], buf, sizeof(buf))) != -1) {
|
||||||
--n;
|
got = rc;
|
||||||
iscomplicated = memchr(buf, ' ', n) || memchr(buf, '(', n);
|
if (got >= 2 && buf[got - 1] == '\n') {
|
||||||
if (iscomplicated) *p++ = '"';
|
if (buf[got - 2] == '\r') --got;
|
||||||
p = mempcpy(p, buf, n);
|
--got;
|
||||||
if (iscomplicated) *p++ = '"';
|
return CopySymbol(p, pn, buf, got);
|
||||||
*p = '\0';
|
}
|
||||||
return p;
|
|
||||||
} else {
|
|
||||||
CloseCxxFilt();
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
CloseCxxFilt();
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,10 +111,12 @@ char *DemangleCxxFilt(char *p, const char *symbol) {
|
||||||
* x86_64 disassembler. That's just for the GNU encoding scheme. So
|
* x86_64 disassembler. That's just for the GNU encoding scheme. So
|
||||||
* what we'll do, is just offload this work to the c++filt program.
|
* what we'll do, is just offload this work to the c++filt program.
|
||||||
*/
|
*/
|
||||||
char *Demangle(char *p, const char *symbol) {
|
char *Demangle(char *p, const char *symbol, size_t n) {
|
||||||
char *r;
|
char *r;
|
||||||
|
size_t sn;
|
||||||
|
sn = strlen(symbol);
|
||||||
if (startswith(symbol, "_Z")) {
|
if (startswith(symbol, "_Z")) {
|
||||||
if ((r = DemangleCxxFilt(p, symbol))) return r;
|
if ((r = DemangleCxxFilt(p, n, symbol, sn))) return r;
|
||||||
}
|
}
|
||||||
return stpcpy(p, symbol);
|
return CopySymbol(p, n, symbol, sn);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
char *Demangle(char *, const char *);
|
char *Demangle(char *, const char *, size_t);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -150,7 +150,7 @@ static char *DisLineData(struct Dis *d, char *p, const uint8_t *b, size_t n) {
|
||||||
static char *DisLabel(struct Dis *d, char *p, const char *name) {
|
static char *DisLabel(struct Dis *d, char *p, const char *name) {
|
||||||
p = DisColumn(DisAddr(d, p), p, ADDRLEN);
|
p = DisColumn(DisAddr(d, p), p, ADDRLEN);
|
||||||
p = HighStart(p, g_high.label);
|
p = HighStart(p, g_high.label);
|
||||||
p = Demangle(p, name);
|
p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
|
||||||
p = HighEnd(p);
|
p = HighEnd(p);
|
||||||
*p++ = ':';
|
*p++ = ':';
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
|
#define DIS_MAX_SYMBOL_LENGTH 32
|
||||||
|
|
||||||
struct Dis {
|
struct Dis {
|
||||||
struct DisOps {
|
struct DisOps {
|
||||||
size_t i, n;
|
size_t i, n;
|
||||||
|
@ -50,6 +52,8 @@ struct Dis {
|
||||||
char buf[512];
|
char buf[512];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern bool g_disisprog_disable;
|
||||||
|
|
||||||
long Dis(struct Dis *, struct Machine *, uint64_t, uint64_t, int);
|
long Dis(struct Dis *, struct Machine *, uint64_t, uint64_t, int);
|
||||||
long DisFind(struct Dis *, int64_t);
|
long DisFind(struct Dis *, int64_t);
|
||||||
void DisFree(struct Dis *);
|
void DisFree(struct Dis *);
|
||||||
|
|
|
@ -143,21 +143,30 @@ static char *DisInt(char *p, int64_t x) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *DisSym(struct Dis *d, char *p, int64_t addr, int64_t ip) {
|
static char *DisSymImpl(struct Dis *d, char *p, int64_t x, long sym) {
|
||||||
long sym;
|
|
||||||
int64_t addend;
|
int64_t addend;
|
||||||
const char *name;
|
const char *name;
|
||||||
if ((sym = DisFindSym(d, ip)) != -1 && d->syms.p[sym].name) {
|
addend = x - d->syms.p[sym].addr;
|
||||||
addend = ip - d->syms.p[sym].addr;
|
name = d->syms.stab + d->syms.p[sym].name;
|
||||||
name = d->syms.stab + d->syms.p[sym].name;
|
p = Demangle(p, name, DIS_MAX_SYMBOL_LENGTH);
|
||||||
p = Demangle(p, name);
|
if (addend) {
|
||||||
if (addend) {
|
*p++ = '+';
|
||||||
*p++ = '+';
|
p = DisInt(p, addend);
|
||||||
p = DisInt(p, addend);
|
}
|
||||||
}
|
return p;
|
||||||
return p;
|
}
|
||||||
|
|
||||||
|
static char *DisSym(struct Dis *d, char *p, int64_t x1, int64_t x2,
|
||||||
|
bool isrelative) {
|
||||||
|
long sym;
|
||||||
|
if ((sym = DisFindSym(d, x2)) != -1 && d->syms.p[sym].name &&
|
||||||
|
(d->syms.p[sym].isabs ^ isrelative)) {
|
||||||
|
return DisSymImpl(d, p, x2, sym);
|
||||||
|
} else if ((sym = DisFindSym(d, x1)) != -1 && d->syms.p[sym].name &&
|
||||||
|
(d->syms.p[sym].isabs ^ isrelative)) {
|
||||||
|
return DisSymImpl(d, p, x1, sym);
|
||||||
} else {
|
} else {
|
||||||
return DisInt(p, addr);
|
return DisInt(p, x1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +174,7 @@ static char *DisSymLiteral(struct Dis *d, uint32_t rde, char *p, uint64_t addr,
|
||||||
uint64_t ip) {
|
uint64_t ip) {
|
||||||
*p++ = '$';
|
*p++ = '$';
|
||||||
p = HighStart(p, g_high.literal);
|
p = HighStart(p, g_high.literal);
|
||||||
p = DisSym(d, p, addr, ip);
|
p = DisSym(d, p, addr, addr, false);
|
||||||
p = HighEnd(p);
|
p = HighEnd(p);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
@ -196,27 +205,36 @@ static bool IsRealModrmAbsolute(uint32_t rde) {
|
||||||
return Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde);
|
return Eamode(rde) == XED_MODE_REAL && ModrmRm(rde) == 6 && !ModrmMod(rde);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *DisM(struct Dis *d, uint32_t rde, char *p) {
|
static char *DisDisp(struct Dis *d, uint32_t rde, char *p) {
|
||||||
|
bool rela;
|
||||||
int64_t disp;
|
int64_t disp;
|
||||||
const char *base, *index, *scale;
|
|
||||||
p = DisSego(d, rde, p);
|
|
||||||
base = index = scale = NULL;
|
|
||||||
if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) ||
|
if (ModrmMod(rde) == 0b01 || ModrmMod(rde) == 0b10 || IsRipRelative(rde) ||
|
||||||
IsRealModrmAbsolute(rde) ||
|
IsRealModrmAbsolute(rde) ||
|
||||||
(ModrmMod(rde) == 0b00 && ModrmRm(rde) == 0b100 &&
|
(Eamode(rde) != XED_MODE_REAL && ModrmMod(rde) == 0b00 &&
|
||||||
SibBase(d->xedd) == 0b101)) {
|
ModrmRm(rde) == 0b100 && SibBase(d->xedd) == 0b101)) {
|
||||||
disp = d->xedd->op.disp;
|
disp = d->xedd->op.disp;
|
||||||
if (IsRipRelative(rde)) {
|
if (IsRipRelative(rde)) {
|
||||||
if (Mode(rde) == XED_MODE_LONG) {
|
if (Mode(rde) == XED_MODE_LONG) {
|
||||||
disp = RipRelative(d, disp);
|
disp = RipRelative(d, disp);
|
||||||
|
rela = true;
|
||||||
} else {
|
} else {
|
||||||
disp = Unrelative(rde, disp);
|
disp = Unrelative(rde, disp);
|
||||||
|
rela = false;
|
||||||
}
|
}
|
||||||
} else if (IsRealModrmAbsolute(rde)) {
|
} else if (IsRealModrmAbsolute(rde)) {
|
||||||
disp = Unrelative(rde, disp);
|
disp = Unrelative(rde, disp);
|
||||||
|
rela = false;
|
||||||
|
} else {
|
||||||
|
rela = true;
|
||||||
}
|
}
|
||||||
p = DisSym(d, p, disp, disp);
|
p = DisSym(d, p, disp, disp, rela);
|
||||||
}
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *DisBis(struct Dis *d, uint32_t rde, char *p) {
|
||||||
|
const char *base, *index, *scale;
|
||||||
|
base = index = scale = NULL;
|
||||||
if (Eamode(rde) != XED_MODE_REAL) {
|
if (Eamode(rde) != XED_MODE_REAL) {
|
||||||
if (!SibExists(rde)) {
|
if (!SibExists(rde)) {
|
||||||
DCHECK(!d->xedd->op.has_sib);
|
DCHECK(!d->xedd->op.has_sib);
|
||||||
|
@ -290,6 +308,13 @@ static char *DisM(struct Dis *d, uint32_t rde, char *p) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *DisM(struct Dis *d, uint32_t rde, char *p) {
|
||||||
|
p = DisSego(d, rde, p);
|
||||||
|
p = DisDisp(d, rde, p);
|
||||||
|
p = DisBis(d, rde, p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
static char *DisRegMem(struct Dis *d, uint32_t rde, char *p,
|
static char *DisRegMem(struct Dis *d, uint32_t rde, char *p,
|
||||||
char *f(struct Dis *, uint32_t, char *)) {
|
char *f(struct Dis *, uint32_t, char *)) {
|
||||||
if (IsModrmRegister(rde)) {
|
if (IsModrmRegister(rde)) {
|
||||||
|
@ -452,11 +477,11 @@ static char *DisJb(struct Dis *d, uint32_t rde, char *p) {
|
||||||
|
|
||||||
static char *DisJvds(struct Dis *d, uint32_t rde, char *p) {
|
static char *DisJvds(struct Dis *d, uint32_t rde, char *p) {
|
||||||
return DisSym(d, p, RipRelative(d, d->xedd->op.disp),
|
return DisSym(d, p, RipRelative(d, d->xedd->op.disp),
|
||||||
RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs));
|
RipRelative(d, d->xedd->op.disp) - Read64(d->m->cs), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *DisAbs(struct Dis *d, uint32_t rde, char *p) {
|
static char *DisAbs(struct Dis *d, uint32_t rde, char *p) {
|
||||||
return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp);
|
return DisSym(d, p, d->xedd->op.disp, d->xedd->op.disp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *DisSw(struct Dis *d, uint32_t rde, char *p) {
|
static char *DisSw(struct Dis *d, uint32_t rde, char *p) {
|
||||||
|
@ -477,12 +502,12 @@ static char *DisY(struct Dis *d, uint32_t rde, char *p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *DisX(struct Dis *d, uint32_t rde, char *p) {
|
static char *DisX(struct Dis *d, uint32_t rde, char *p) {
|
||||||
DisSego(d, rde, p);
|
p = DisSego(d, rde, p);
|
||||||
return DisSpecialAddr(d, rde, p, 6); // ds:si
|
return DisSpecialAddr(d, rde, p, 6); // ds:si
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *DisBBb(struct Dis *d, uint32_t rde, char *p) {
|
static char *DisBBb(struct Dis *d, uint32_t rde, char *p) {
|
||||||
DisSego(d, rde, p);
|
p = DisSego(d, rde, p);
|
||||||
return DisSpecialAddr(d, rde, p, 3); // ds:bx
|
return DisSpecialAddr(d, rde, p, 3); // ds:bx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,13 @@
|
||||||
#include "libc/elf/elf.h"
|
#include "libc/elf/elf.h"
|
||||||
#include "libc/elf/struct/sym.h"
|
#include "libc/elf/struct/sym.h"
|
||||||
#include "libc/log/check.h"
|
#include "libc/log/check.h"
|
||||||
|
#include "libc/log/log.h"
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
#include "tool/build/lib/dis.h"
|
#include "tool/build/lib/dis.h"
|
||||||
|
|
||||||
|
bool g_disisprog_disable;
|
||||||
|
|
||||||
static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) {
|
static int DisSymCompare(const struct DisSym *a, const struct DisSym *b) {
|
||||||
if (a->addr != b->addr) {
|
if (a->addr != b->addr) {
|
||||||
if (a->addr < b->addr) return -1;
|
if (a->addr < b->addr) return -1;
|
||||||
|
@ -72,6 +76,8 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
|
||||||
if (!st[i].st_name) continue;
|
if (!st[i].st_name) continue;
|
||||||
if (!(0 <= st[i].st_name && st[i].st_name < stablen)) continue;
|
if (!(0 <= st[i].st_name && st[i].st_name < stablen)) continue;
|
||||||
if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION) continue;
|
if (ELF64_ST_TYPE(st[i].st_info) == STT_SECTION) continue;
|
||||||
|
if (ELF64_ST_TYPE(st[i].st_info) == STT_FILE) continue;
|
||||||
|
if (startswith(d->syms.stab + st[i].st_name, "v_")) continue;
|
||||||
isabs = st[i].st_shndx == SHN_ABS;
|
isabs = st[i].st_shndx == SHN_ABS;
|
||||||
isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK;
|
isweak = ELF64_ST_BIND(st[i].st_info) == STB_WEAK;
|
||||||
islocal = ELF64_ST_BIND(st[i].st_info) == STB_LOCAL;
|
islocal = ELF64_ST_BIND(st[i].st_info) == STB_LOCAL;
|
||||||
|
@ -85,6 +91,7 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
|
||||||
t.addr = st[i].st_value;
|
t.addr = st[i].st_value;
|
||||||
t.rank = -islocal + -isweak + -isabs + isprotected + isobject + isfunc;
|
t.rank = -islocal + -isweak + -isabs + isprotected + isobject + isfunc;
|
||||||
t.iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc;
|
t.iscode = DisIsText(d, st[i].st_value) ? !isobject : isfunc;
|
||||||
|
t.isabs = isabs;
|
||||||
APPEND(&d->syms.p, &d->syms.i, &d->syms.n, &t);
|
APPEND(&d->syms.p, &d->syms.i, &d->syms.n, &t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +100,7 @@ static void DisLoadElfSyms(struct Dis *d, struct Elf *elf) {
|
||||||
|
|
||||||
bool DisIsProg(struct Dis *d, int64_t addr) {
|
bool DisIsProg(struct Dis *d, int64_t addr) {
|
||||||
long i;
|
long i;
|
||||||
|
if (g_disisprog_disable) return true;
|
||||||
for (i = 0; i < d->loads.i; ++i) {
|
for (i = 0; i < d->loads.i; ++i) {
|
||||||
if (addr >= d->loads.p[i].addr &&
|
if (addr >= d->loads.p[i].addr &&
|
||||||
addr < d->loads.p[i].addr + d->loads.p[i].size) {
|
addr < d->loads.p[i].addr + d->loads.p[i].size) {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "libc/log/check.h"
|
#include "libc/log/check.h"
|
||||||
#include "libc/nexgen32e/tinystrcmp.h"
|
#include "libc/nexgen32e/tinystrcmp.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
#include "third_party/zlib/zlib.h"
|
||||||
#include "tool/build/lib/dis.h"
|
#include "tool/build/lib/dis.h"
|
||||||
#include "tool/build/lib/high.h"
|
#include "tool/build/lib/high.h"
|
||||||
#include "tool/build/lib/modrm.h"
|
#include "tool/build/lib/modrm.h"
|
||||||
|
@ -163,7 +164,7 @@ static char *DisName(struct Dis *d, char *bp, const char *name,
|
||||||
} else if (wantsuffix || (ambiguous && !startswith(name, "f") &&
|
} else if (wantsuffix || (ambiguous && !startswith(name, "f") &&
|
||||||
!startswith(name, "set"))) {
|
!startswith(name, "set"))) {
|
||||||
if (Osz(rde)) {
|
if (Osz(rde)) {
|
||||||
if (Mode(rde) != XED_MODE_REAL) {
|
if (ambiguous || Mode(rde) != XED_MODE_REAL) {
|
||||||
*p++ = 'w';
|
*p++ = 'w';
|
||||||
}
|
}
|
||||||
} else if (Rexw(rde)) {
|
} else if (Rexw(rde)) {
|
||||||
|
@ -187,8 +188,8 @@ static char *DisName(struct Dis *d, char *bp, const char *name,
|
||||||
*/
|
*/
|
||||||
char *DisInst(struct Dis *d, char *p, const char *spec) {
|
char *DisInst(struct Dis *d, char *p, const char *spec) {
|
||||||
long i, n;
|
long i, n;
|
||||||
char sbuf[300];
|
char sbuf[64];
|
||||||
char args[4][300];
|
char args[4][256];
|
||||||
char *s, *name, *state;
|
char *s, *name, *state;
|
||||||
bool hasarg, hasmodrm, hasregister, hasmemory;
|
bool hasarg, hasmodrm, hasregister, hasmemory;
|
||||||
CHECK_EQ(0, (int)d->xedd->op.error);
|
CHECK_EQ(0, (int)d->xedd->op.error);
|
||||||
|
|
|
@ -48,7 +48,7 @@ COSMOPOLITAN_C_START_
|
||||||
#define SibHasIndex(x) (SibIndex(x) != 4 || Rexx(x))
|
#define SibHasIndex(x) (SibIndex(x) != 4 || Rexx(x))
|
||||||
#define SibHasBase(x, r) (SibBase(x) != 5 || ModrmMod(r))
|
#define SibHasBase(x, r) (SibBase(x) != 5 || ModrmMod(r))
|
||||||
#define SibIsAbsolute(x, r) (!SibHasBase(x, r) && !SibHasIndex(x))
|
#define SibIsAbsolute(x, r) (!SibHasBase(x, r) && !SibHasIndex(x))
|
||||||
#define IsRipRelative(x) (ModrmRm(x) == 5 && !ModrmMod(x))
|
#define IsRipRelative(x) (Eamode(x) && ModrmRm(x) == 5 && !ModrmMod(x))
|
||||||
|
|
||||||
struct AddrSeg {
|
struct AddrSeg {
|
||||||
int64_t addr;
|
int64_t addr;
|
||||||
|
|
|
@ -474,11 +474,8 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) {
|
||||||
break;
|
break;
|
||||||
case kMachinePtyUtf8:
|
case kMachinePtyUtf8:
|
||||||
if (ThomPikeCont(p[i])) {
|
if (ThomPikeCont(p[i])) {
|
||||||
pty->u8 <<= 6;
|
pty->u8 = ThomPikeMerge(pty->u8, p[i]);
|
||||||
pty->u8 |= p[i] & 0b00111111;
|
if (--pty->n8) break;
|
||||||
if (--pty->n8) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SetMachinePtyCell(pty, pty->u8);
|
SetMachinePtyCell(pty, pty->u8);
|
||||||
pty->state = kMachinePtyAscii;
|
pty->state = kMachinePtyAscii;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue