mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
Add raw memory visualization tool to redbean
This change introduces a `-W /dev/pts/1` flag to redbean. What it does is use the mincore() system call to create a dual-screen terminal display that lets you troubleshoot the virtual address space. This is useful since page faults are an important thing to consider when using a forking web server. Now we have a colorful visualization of which pages are going to fault and which ones are resident in memory. The memory monitor, if enabled, spawns as a thread that just outputs ANSI codes to the second terminal in a loop. In order to make this happen using the new clone() polyfill, stdio is now thread safe. This change also introduces some new demo pages to redbean. It also polishes the demos we already have, to look a bit nicer and more presentable for the upcoming release, with better explanations too.
This commit is contained in:
parent
578cb21591
commit
80b211e314
106 changed files with 1483 additions and 592 deletions
|
@ -21,14 +21,16 @@
|
||||||
#define _POSIX2_VERSION _POSIX_VERSION
|
#define _POSIX2_VERSION _POSIX_VERSION
|
||||||
#define _XOPEN_VERSION 700
|
#define _XOPEN_VERSION 700
|
||||||
|
|
||||||
#define EOF -1 /* end of file */
|
#define EOF -1 /* end of file */
|
||||||
#define WEOF -1u /* end of file (multibyte) */
|
#define WEOF -1u /* end of file (multibyte) */
|
||||||
#define _IOFBF 0 /* fully buffered */
|
#define _IOFBF 0 /* fully buffered */
|
||||||
#define _IOLBF 1 /* line buffered */
|
#define _IOLBF 1 /* line buffered */
|
||||||
#define _IONBF 2 /* no buffering */
|
#define _IONBF 2 /* no buffering */
|
||||||
#define SEEK_SET 0 /* relative to beginning */
|
#define SEEK_SET 0 /* relative to beginning */
|
||||||
#define SEEK_CUR 1 /* relative to current position */
|
#define SEEK_CUR 1 /* relative to current position */
|
||||||
#define SEEK_END 2 /* relative to end */
|
#define SEEK_END 2 /* relative to end */
|
||||||
|
#define __WALL 0x40000000 /* Wait on all children, regardless of type */
|
||||||
|
#define __WCLONE 0x80000000 /* Wait only on non-SIGCHLD children */
|
||||||
|
|
||||||
#define SIG_ERR ((void (*)(int))(-1))
|
#define SIG_ERR ((void (*)(int))(-1))
|
||||||
#define SIG_DFL ((void *)0)
|
#define SIG_DFL ((void *)0)
|
||||||
|
@ -147,6 +149,7 @@ int linkat(int, const char *, int, const char *, int);
|
||||||
int lstat(const char *, struct stat *);
|
int lstat(const char *, struct stat *);
|
||||||
int lutimes(const char *, const struct timeval[2]);
|
int lutimes(const char *, const struct timeval[2]);
|
||||||
int madvise(void *, uint64_t, int);
|
int madvise(void *, uint64_t, int);
|
||||||
|
int mincore(void *, size_t, unsigned char *);
|
||||||
int mkdir(const char *, uint32_t);
|
int mkdir(const char *, uint32_t);
|
||||||
int mkdirat(int, const char *, uint32_t);
|
int mkdirat(int, const char *, uint32_t);
|
||||||
int mkfifo(const char *, uint32_t);
|
int mkfifo(const char *, uint32_t);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
#include "libc/sysv/consts/at.h"
|
#include "libc/sysv/consts/at.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,8 +29,11 @@
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
* @see /etc/passwd for user ids
|
* @see /etc/passwd for user ids
|
||||||
* @see /etc/group for group ids
|
* @see /etc/group for group ids
|
||||||
|
* @raises ENOSYS on Windows
|
||||||
*/
|
*/
|
||||||
int fchown(int fd, uint32_t uid, uint32_t gid) {
|
int fchown(int fd, uint32_t uid, uint32_t gid) {
|
||||||
/* TODO(jart): Windows? */
|
int rc;
|
||||||
return sys_fchown(fd, uid, gid);
|
rc = sys_fchown(fd, uid, gid);
|
||||||
|
STRACE("fchown(%d, %d, %d) → %d% m", fd, uid, gid, rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,7 @@ i32 sys_lseek(i32, i64, i64, i64) hidden;
|
||||||
i32 sys_lutimes(const char *, const struct timeval *) hidden;
|
i32 sys_lutimes(const char *, const struct timeval *) hidden;
|
||||||
i32 sys_madvise(void *, size_t, i32) hidden;
|
i32 sys_madvise(void *, size_t, i32) hidden;
|
||||||
i32 sys_memfd_create(const char *, u32) hidden;
|
i32 sys_memfd_create(const char *, u32) hidden;
|
||||||
|
i32 sys_mincore(void *, u64, unsigned char *) hidden;
|
||||||
i32 sys_mkdirat(i32, const char *, u32) hidden;
|
i32 sys_mkdirat(i32, const char *, u32) hidden;
|
||||||
i32 sys_mkfifo(const char *, u32) hidden;
|
i32 sys_mkfifo(const char *, u32) hidden;
|
||||||
i32 sys_mknod(const char *, u32, u64) hidden;
|
i32 sys_mknod(const char *, u32, u64) hidden;
|
||||||
|
|
31
libc/calls/mincore.c
Normal file
31
libc/calls/mincore.c
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/internal.h"
|
||||||
|
#include "libc/calls/strace.internal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells you which pages are resident in memory.
|
||||||
|
*/
|
||||||
|
int mincore(void *addr, size_t length, unsigned char *vec) {
|
||||||
|
int rc;
|
||||||
|
rc = sys_mincore(addr, length, vec);
|
||||||
|
POLLTRACE("mincore(%p, %'zu, %p) → %d% m", addr, length, vec, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
|
@ -45,9 +45,9 @@ noinstrument int nanosleep(const struct timespec *req, struct timespec *rem) {
|
||||||
rc = sys_nanosleep_nt(req, rem);
|
rc = sys_nanosleep_nt(req, rem);
|
||||||
}
|
}
|
||||||
if (!__time_critical) {
|
if (!__time_critical) {
|
||||||
STRACE("nanosleep(%s, [%s]) → %d% m",
|
POLLTRACE("nanosleep(%s, [%s]) → %d% m",
|
||||||
DescribeTimespec(buf[0], sizeof(buf[0]), rc, req),
|
DescribeTimespec(buf[0], sizeof(buf[0]), rc, req),
|
||||||
DescribeTimespec(buf[1], sizeof(buf[1]), rc, rem), rc);
|
DescribeTimespec(buf[1], sizeof(buf[1]), rc, rem), rc);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,6 @@ privileged static char *kemitquote(char *p, char *e, signed char t,
|
||||||
|
|
||||||
privileged static unsigned long long kgetint(va_list va, signed char t,
|
privileged static unsigned long long kgetint(va_list va, signed char t,
|
||||||
bool s) {
|
bool s) {
|
||||||
#ifdef __LP64__
|
|
||||||
int bits;
|
int bits;
|
||||||
unsigned long long x;
|
unsigned long long x;
|
||||||
x = va_arg(va, unsigned long);
|
x = va_arg(va, unsigned long);
|
||||||
|
@ -119,35 +118,6 @@ privileged static unsigned long long kgetint(va_list va, signed char t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
#else
|
|
||||||
if (s) {
|
|
||||||
switch (t) {
|
|
||||||
case -2:
|
|
||||||
return (signed char)va_arg(va, int);
|
|
||||||
case -1:
|
|
||||||
return (signed short)va_arg(va, int);
|
|
||||||
default:
|
|
||||||
return va_arg(va, signed int);
|
|
||||||
case 1:
|
|
||||||
return va_arg(va, signed long);
|
|
||||||
case 2:
|
|
||||||
return va_arg(va, signed long long);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (t) {
|
|
||||||
case -2:
|
|
||||||
return (unsigned char)va_arg(va, int);
|
|
||||||
case -1:
|
|
||||||
return (unsigned short)va_arg(va, int);
|
|
||||||
default:
|
|
||||||
return va_arg(va, unsigned int);
|
|
||||||
case 1:
|
|
||||||
return va_arg(va, unsigned long);
|
|
||||||
case 2:
|
|
||||||
return va_arg(va, unsigned long long);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
privileged static inline bool kiskernelpointer(const void *p) {
|
privileged static inline bool kiskernelpointer(const void *p) {
|
||||||
|
|
|
@ -1,34 +1,65 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
#ifndef COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
||||||
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
#define COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_
|
||||||
|
#include "libc/bits/weaken.h"
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/log/backtrace.internal.h"
|
||||||
|
#include "libc/log/log.h"
|
||||||
|
|
||||||
#ifdef TINY
|
#if defined(_SPINLOCK_DEBUG)
|
||||||
#define _spinlock(lock) \
|
#define _spinlock(lock) _spinlock_debug(lock)
|
||||||
do { \
|
#elif defined(TINY)
|
||||||
while (__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \
|
#define _spinlock(lock) _spinlock_tiny(lock)
|
||||||
__builtin_ia32_pause(); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
#else
|
#else
|
||||||
#define _spinlock(lock) \
|
#define _spinlock(lock) _spinlock_optimistic(lock)
|
||||||
do { \
|
|
||||||
for (;;) { \
|
|
||||||
typeof(*(lock)) x; \
|
|
||||||
__atomic_load(lock, &x, __ATOMIC_RELAXED); \
|
|
||||||
if (!x && !__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \
|
|
||||||
break; \
|
|
||||||
} else { \
|
|
||||||
__builtin_ia32_pause(); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define _spunlock(lock) __atomic_clear(lock, __ATOMIC_RELAXED)
|
#define _spunlock(lock) __atomic_clear(lock, __ATOMIC_RELAXED)
|
||||||
|
|
||||||
|
#define _trylock(lock) __atomic_test_and_set(lock, __ATOMIC_SEQ_CST)
|
||||||
|
|
||||||
#define _seizelock(lock) \
|
#define _seizelock(lock) \
|
||||||
do { \
|
do { \
|
||||||
typeof(*(lock)) x = 1; \
|
typeof(*(lock)) x = 1; \
|
||||||
__atomic_store(lock, &x, __ATOMIC_SEQ_CST); \
|
__atomic_store(lock, &x, __ATOMIC_SEQ_CST); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define _spinlock_tiny(lock) \
|
||||||
|
do { \
|
||||||
|
while (_trylock(lock)) { \
|
||||||
|
__builtin_ia32_pause(); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define _spinlock_optimistic(lock) \
|
||||||
|
do { \
|
||||||
|
for (;;) { \
|
||||||
|
typeof(*(lock)) x; \
|
||||||
|
__atomic_load(lock, &x, __ATOMIC_RELAXED); \
|
||||||
|
if (!x && !_trylock(lock)) { \
|
||||||
|
break; \
|
||||||
|
} else { \
|
||||||
|
__builtin_ia32_pause(); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define _spinlock_debug(lock) \
|
||||||
|
do { \
|
||||||
|
typeof(*(lock)) me, owner; \
|
||||||
|
me = gettid(); \
|
||||||
|
if (_trylock(lock)) { \
|
||||||
|
__atomic_load(lock, &owner, __ATOMIC_RELAXED); \
|
||||||
|
if (owner == me) { \
|
||||||
|
kprintf("%s:%d: warning: possible spinlock re-entry in %s()\n", \
|
||||||
|
__FILE__, __LINE__, __FUNCTION__); \
|
||||||
|
if (weaken(ShowBacktrace)) { \
|
||||||
|
weaken(ShowBacktrace)(2, 0); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
_spinlock_optimistic(lock); \
|
||||||
|
} \
|
||||||
|
*lock = me; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */
|
#endif /* COSMOPOLITAN_LIBC_INTRIN_SPINLOCK_H_ */
|
||||||
|
|
|
@ -174,15 +174,12 @@ static int PrintBacktrace(int fd, const struct StackFrame *bp) {
|
||||||
void ShowBacktrace(int fd, const struct StackFrame *bp) {
|
void ShowBacktrace(int fd, const struct StackFrame *bp) {
|
||||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||||
/* asan runtime depends on this function */
|
/* asan runtime depends on this function */
|
||||||
int st, ft;
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED);
|
||||||
st = __strace, __strace = 0;
|
__atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
ft = g_ftrace, g_ftrace = 0;
|
|
||||||
if (!bp) bp = __builtin_frame_address(0);
|
if (!bp) bp = __builtin_frame_address(0);
|
||||||
|
|
||||||
PrintBacktrace(fd, bp);
|
PrintBacktrace(fd, bp);
|
||||||
|
__atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
__strace = st;
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED);
|
||||||
g_ftrace = ft;
|
|
||||||
#else
|
#else
|
||||||
(fprintf)(stderr, "ShowBacktrace() needs these flags to show C backtrace:\n"
|
(fprintf)(stderr, "ShowBacktrace() needs these flags to show C backtrace:\n"
|
||||||
"\t-D__FNO_OMIT_FRAME_POINTER__\n"
|
"\t-D__FNO_OMIT_FRAME_POINTER__\n"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_LOG_LOG_H_
|
#ifndef COSMOPOLITAN_LIBC_LOG_LOG_H_
|
||||||
#define COSMOPOLITAN_LIBC_LOG_LOG_H_
|
#define COSMOPOLITAN_LIBC_LOG_LOG_H_
|
||||||
#include "libc/bits/likely.h"
|
#include "libc/bits/likely.h"
|
||||||
|
#include "libc/bits/weaken.h"
|
||||||
#include "libc/calls/struct/rusage.h"
|
#include "libc/calls/struct/rusage.h"
|
||||||
#include "libc/calls/struct/sigset.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/winsize.h"
|
#include "libc/calls/struct/winsize.h"
|
||||||
|
@ -77,23 +78,24 @@ extern unsigned __log_level; /* log level for runtime check */
|
||||||
// log a message with the specified log level (not checking if LOGGABLE)
|
// log a message with the specified log level (not checking if LOGGABLE)
|
||||||
#define LOGF(LEVEL, FMT, ...) \
|
#define LOGF(LEVEL, FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// die with an error message without backtrace and debugger invocation
|
// die with an error message without backtrace and debugger invocation
|
||||||
#define DIEF(FMT, ...) \
|
#define DIEF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
|
if (weaken(__die)) weaken(__die)(); \
|
||||||
exit(1); \
|
exit(1); \
|
||||||
unreachable; \
|
unreachable; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define FATALF(FMT, ...) \
|
#define FATALF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
ffatalf(kLogFatal, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
ffatalf(kLogFatal, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
unreachable; \
|
unreachable; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -101,78 +103,78 @@ extern unsigned __log_level; /* log level for runtime check */
|
||||||
#define ERRORF(FMT, ...) \
|
#define ERRORF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOGGABLE(kLogError)) { \
|
if (LOGGABLE(kLogError)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define WARNF(FMT, ...) \
|
#define WARNF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOGGABLE(kLogWarn)) { \
|
if (LOGGABLE(kLogWarn)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define INFOF(FMT, ...) \
|
#define INFOF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOGGABLE(kLogInfo)) { \
|
if (LOGGABLE(kLogInfo)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define VERBOSEF(FMT, ...) \
|
#define VERBOSEF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOGGABLE(kLogVerbose)) { \
|
if (LOGGABLE(kLogVerbose)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define DEBUGF(FMT, ...) \
|
#define DEBUGF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (UNLIKELY(LOGGABLE(kLogDebug))) { \
|
if (UNLIKELY(LOGGABLE(kLogDebug))) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define NOISEF(FMT, ...) \
|
#define NOISEF(FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (UNLIKELY(LOGGABLE(kLogNoise))) { \
|
if (UNLIKELY(LOGGABLE(kLogNoise))) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define FLOGF(F, FMT, ...) \
|
#define FLOGF(F, FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOGGABLE(kLogInfo)) { \
|
if (LOGGABLE(kLogInfo)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define FWARNF(F, FMT, ...) \
|
#define FWARNF(F, FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (LOGGABLE(kLogWarn)) { \
|
if (LOGGABLE(kLogWarn)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define FFATALF(F, FMT, ...) \
|
#define FFATALF(F, FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
ffatalf(kLogFatal, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
ffatalf(kLogFatal, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||||
unreachable; \
|
unreachable; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -180,18 +182,18 @@ extern unsigned __log_level; /* log level for runtime check */
|
||||||
#define FDEBUGF(F, FMT, ...) \
|
#define FDEBUGF(F, FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (UNLIKELY(LOGGABLE(kLogDebug))) { \
|
if (UNLIKELY(LOGGABLE(kLogDebug))) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define FNOISEF(F, FMT, ...) \
|
#define FNOISEF(F, FMT, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (UNLIKELY(LOGGABLE(kLogNoise))) { \
|
if (UNLIKELY(LOGGABLE(kLogNoise))) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -204,25 +206,25 @@ extern unsigned __log_level; /* log level for runtime check */
|
||||||
int e = errno; \
|
int e = errno; \
|
||||||
autotype(FORM) Ax = (FORM); \
|
autotype(FORM) Ax = (FORM); \
|
||||||
if (UNLIKELY(Ax == (typeof(Ax))(-1)) && LOGGABLE(kLogWarn)) { \
|
if (UNLIKELY(Ax == (typeof(Ax))(-1)) && LOGGABLE(kLogWarn)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
errno = e; \
|
errno = e; \
|
||||||
} \
|
} \
|
||||||
Ax; \
|
Ax; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#define LOGIFNULL(FORM) \
|
#define LOGIFNULL(FORM) \
|
||||||
({ \
|
({ \
|
||||||
int e = errno; \
|
int e = errno; \
|
||||||
autotype(FORM) Ax = (FORM); \
|
autotype(FORM) Ax = (FORM); \
|
||||||
if (Ax == NULL && LOGGABLE(kLogWarn)) { \
|
if (Ax == NULL && LOGGABLE(kLogWarn)) { \
|
||||||
--g_ftrace; \
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||||
++g_ftrace; \
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED); \
|
||||||
errno = e; \
|
errno = e; \
|
||||||
} \
|
} \
|
||||||
Ax; \
|
Ax; \
|
||||||
})
|
})
|
||||||
|
|
||||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||||
|
|
|
@ -275,10 +275,10 @@ static wontreturn relegated noinstrument void __minicrash(int sig,
|
||||||
relegated noinstrument void __oncrash(int sig, struct siginfo *si,
|
relegated noinstrument void __oncrash(int sig, struct siginfo *si,
|
||||||
ucontext_t *ctx) {
|
ucontext_t *ctx) {
|
||||||
intptr_t rip;
|
intptr_t rip;
|
||||||
int gdbpid, err, st, ft;
|
int gdbpid, err;
|
||||||
static bool noreentry, notpossible;
|
static bool noreentry, notpossible;
|
||||||
st = __strace, __strace = 0;
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED);
|
||||||
ft = g_ftrace, g_ftrace = 0;
|
__atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
if (_lockcmpxchg(&noreentry, false, true)) {
|
if (_lockcmpxchg(&noreentry, false, true)) {
|
||||||
if (!__vforked) {
|
if (!__vforked) {
|
||||||
rip = ctx ? ctx->uc_mcontext.rip : 0;
|
rip = ctx ? ctx->uc_mcontext.rip : 0;
|
||||||
|
@ -306,9 +306,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si,
|
||||||
}
|
}
|
||||||
} else if (sig == SIGTRAP) {
|
} else if (sig == SIGTRAP) {
|
||||||
/* chances are IsDebuggerPresent() confused strace w/ gdb */
|
/* chances are IsDebuggerPresent() confused strace w/ gdb */
|
||||||
g_ftrace = ft;
|
goto ItsATrap;
|
||||||
__strace = st;
|
|
||||||
return;
|
|
||||||
} else if (_lockcmpxchg(¬possible, false, true)) {
|
} else if (_lockcmpxchg(¬possible, false, true)) {
|
||||||
__minicrash(sig, si, ctx, "WHILE CRASHING");
|
__minicrash(sig, si, ctx, "WHILE CRASHING");
|
||||||
} else {
|
} else {
|
||||||
|
@ -317,5 +315,7 @@ relegated noinstrument void __oncrash(int sig, struct siginfo *si,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
noreentry = false;
|
noreentry = false;
|
||||||
++g_ftrace;
|
ItsATrap:
|
||||||
|
__atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
#include "libc/fmt/fmt.h"
|
#include "libc/fmt/fmt.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/log/internal.h"
|
#include "libc/log/internal.h"
|
||||||
#include "libc/log/log.h"
|
#include "libc/log/log.h"
|
||||||
#include "libc/math.h"
|
#include "libc/math.h"
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
#define kNontrivialSize (8 * 1000 * 1000)
|
#define kNontrivialSize (8 * 1000 * 1000)
|
||||||
|
|
||||||
static struct timespec vflogf_ts;
|
static struct timespec vflogf_ts;
|
||||||
|
_Alignas(64) static char vflogf_lock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes corrective action if logging is on the fritz.
|
* Takes corrective action if logging is on the fritz.
|
||||||
|
@ -77,17 +79,18 @@ void vflogf_onfail(FILE *f) {
|
||||||
*/
|
*/
|
||||||
void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
||||||
const char *fmt, va_list va) {
|
const char *fmt, va_list va) {
|
||||||
|
int bufmode;
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
long double t2;
|
long double t2;
|
||||||
int st, bufmode;
|
|
||||||
const char *prog;
|
const char *prog;
|
||||||
bool issamesecond;
|
bool issamesecond;
|
||||||
char buf32[32];
|
char buf32[32];
|
||||||
int64_t secs, nsec, dots;
|
int64_t secs, nsec, dots;
|
||||||
if (!f) f = __log_file;
|
if (!f) f = __log_file;
|
||||||
if (!f) return;
|
if (!f) return;
|
||||||
st = __strace;
|
_spinlock(&vflogf_lock);
|
||||||
__strace = 0;
|
__atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
|
|
||||||
t2 = nowl();
|
t2 = nowl();
|
||||||
secs = t2;
|
secs = t2;
|
||||||
nsec = (t2 - secs) * 1e9L;
|
nsec = (t2 - secs) * 1e9L;
|
||||||
|
@ -95,11 +98,13 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
||||||
dots = issamesecond ? nsec - vflogf_ts.tv_nsec : nsec;
|
dots = issamesecond ? nsec - vflogf_ts.tv_nsec : nsec;
|
||||||
vflogf_ts.tv_sec = secs;
|
vflogf_ts.tv_sec = secs;
|
||||||
vflogf_ts.tv_nsec = nsec;
|
vflogf_ts.tv_nsec = nsec;
|
||||||
|
|
||||||
localtime_r(&secs, &tm);
|
localtime_r(&secs, &tm);
|
||||||
strcpy(iso8601(buf32, &tm), issamesecond ? "+" : ".");
|
strcpy(iso8601(buf32, &tm), issamesecond ? "+" : ".");
|
||||||
prog = basename(firstnonnull(program_invocation_name, "unknown"));
|
prog = basename(firstnonnull(program_invocation_name, "unknown"));
|
||||||
bufmode = f->bufmode;
|
bufmode = f->bufmode;
|
||||||
if (bufmode == _IOLBF) f->bufmode = _IOFBF;
|
if (bufmode == _IOLBF) f->bufmode = _IOFBF;
|
||||||
|
|
||||||
if ((fprintf)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ", "FEWIVDNT"[level & 7], buf32,
|
if ((fprintf)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ", "FEWIVDNT"[level & 7], buf32,
|
||||||
rem1000000int64(div1000int64(dots)), file, line,
|
rem1000000int64(div1000int64(dots)), file, line,
|
||||||
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
||||||
|
@ -111,13 +116,17 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
||||||
f->bufmode = _IOLBF;
|
f->bufmode = _IOLBF;
|
||||||
fflush(f);
|
fflush(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level == kLogFatal) {
|
if (level == kLogFatal) {
|
||||||
__start_fatal(file, line);
|
__start_fatal(file, line);
|
||||||
strcpy(buf32, "unknown");
|
strcpy(buf32, "unknown");
|
||||||
gethostname(buf32, sizeof(buf32));
|
gethostname(buf32, sizeof(buf32));
|
||||||
(dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid());
|
(dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid());
|
||||||
|
_spunlock(&vflogf_lock);
|
||||||
__die();
|
__die();
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
__strace = st;
|
|
||||||
|
__atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
|
_spunlock(&vflogf_lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -438,7 +438,7 @@ static int CloneLinux(int (*func)(void *), char *stk, size_t stksz, int flags,
|
||||||
* @param flags usually has one of
|
* @param flags usually has one of
|
||||||
* - `SIGCHLD` will delegate to fork()
|
* - `SIGCHLD` will delegate to fork()
|
||||||
* - `CLONE_VFORK|CLONE_VM|SIGCHLD` means vfork()
|
* - `CLONE_VFORK|CLONE_VM|SIGCHLD` means vfork()
|
||||||
* - `CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND` for threads
|
* - `CLONE_THREAD|CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND`
|
||||||
* as part high bytes, and the low order byte may optionally contain
|
* as part high bytes, and the low order byte may optionally contain
|
||||||
* a signal e.g. SIGCHLD, to enable parent notification on terminate
|
* a signal e.g. SIGCHLD, to enable parent notification on terminate
|
||||||
* although the signal isn't supported on non-Linux and non-NetBSD
|
* although the signal isn't supported on non-Linux and non-NetBSD
|
||||||
|
@ -504,11 +504,11 @@ int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg,
|
||||||
|
|
||||||
// we now assume we're creating a thread
|
// we now assume we're creating a thread
|
||||||
// these platforms can't do signals the way linux does
|
// these platforms can't do signals the way linux does
|
||||||
else if (!IsTiny() &&
|
else if (!IsTiny() && ((stksz < PAGESIZE || (stksz & (PAGESIZE - 1))) ||
|
||||||
((stksz < PAGESIZE || (stksz & (PAGESIZE - 1))) ||
|
(flags & ~(CLONE_SETTLS | CLONE_PARENT_SETTID |
|
||||||
(flags &
|
CLONE_CHILD_SETTID)) !=
|
||||||
~(CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID)) !=
|
(CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||||
(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND))) {
|
CLONE_SIGHAND))) {
|
||||||
rc = einval();
|
rc = einval();
|
||||||
} else if (IsXnu()) {
|
} else if (IsXnu()) {
|
||||||
rc = CloneXnu(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
rc = CloneXnu(func, stk, stksz, flags, arg, ptid, tls, tlssz, ctid);
|
||||||
|
|
|
@ -77,6 +77,16 @@ cosmo: push %rbp
|
||||||
.endfn cosmo,weak
|
.endfn cosmo,weak
|
||||||
|
|
||||||
#if !IsTiny()
|
#if !IsTiny()
|
||||||
|
// Creates deterministically addressed stack we can use
|
||||||
|
//
|
||||||
|
// This helps debugging be more comprehensible, because
|
||||||
|
// when diagnosing low-level problems when error report
|
||||||
|
// isn't working, sometimes numbers are all you have to
|
||||||
|
// go on, and we can't use them if kernel hardening has
|
||||||
|
// configured that meaningful data to be randomized.
|
||||||
|
//
|
||||||
|
// Having deterministic addresses is also key to ensure
|
||||||
|
// builds, execution, and other things are reproducible
|
||||||
.init.start 304,_init_stack
|
.init.start 304,_init_stack
|
||||||
testb IsWindows()
|
testb IsWindows()
|
||||||
jnz 9f
|
jnz 9f
|
||||||
|
|
|
@ -137,18 +137,18 @@ textstartup void __printargs(const char *prologue) {
|
||||||
char **env;
|
char **env;
|
||||||
sigset_t ss;
|
sigset_t ss;
|
||||||
unsigned i, n;
|
unsigned i, n;
|
||||||
|
int e, x, flags;
|
||||||
uintptr_t *auxp;
|
uintptr_t *auxp;
|
||||||
struct utsname uts;
|
struct utsname uts;
|
||||||
struct termios termios;
|
struct termios termios;
|
||||||
int e, x, st, ft, flags;
|
|
||||||
struct AuxiliaryValue *auxinfo;
|
struct AuxiliaryValue *auxinfo;
|
||||||
union {
|
union {
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
struct pollfd pfds[128];
|
struct pollfd pfds[128];
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
st = __strace, __strace = 0;
|
__atomic_fetch_sub(&g_ftrace, 1, __ATOMIC_RELAXED);
|
||||||
ft = g_ftrace, g_ftrace = 0;
|
__atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
e = errno;
|
e = errno;
|
||||||
|
|
||||||
PRINT("");
|
PRINT("");
|
||||||
|
@ -454,13 +454,13 @@ textstartup void __printargs(const char *prologue) {
|
||||||
kprintf("\n");
|
kprintf("\n");
|
||||||
PRINT(" c_ispeed = %u", termios.c_ispeed);
|
PRINT(" c_ispeed = %u", termios.c_ispeed);
|
||||||
PRINT(" c_ospeed = %u", termios.c_ospeed);
|
PRINT(" c_ospeed = %u", termios.c_ospeed);
|
||||||
|
PRINT(" c_cc[VMIN] = %d", termios.c_cc[VMIN]);
|
||||||
|
PRINT(" c_cc[VTIME] = %d", termios.c_cc[VTIME]);
|
||||||
PRINT(" c_cc[VINTR] = CTRL-%c", CTRL(termios.c_cc[VINTR]));
|
PRINT(" c_cc[VINTR] = CTRL-%c", CTRL(termios.c_cc[VINTR]));
|
||||||
PRINT(" c_cc[VQUIT] = CTRL-%c", CTRL(termios.c_cc[VQUIT]));
|
PRINT(" c_cc[VQUIT] = CTRL-%c", CTRL(termios.c_cc[VQUIT]));
|
||||||
PRINT(" c_cc[VERASE] = CTRL-%c", CTRL(termios.c_cc[VERASE]));
|
PRINT(" c_cc[VERASE] = CTRL-%c", CTRL(termios.c_cc[VERASE]));
|
||||||
PRINT(" c_cc[VKILL] = CTRL-%c", CTRL(termios.c_cc[VKILL]));
|
PRINT(" c_cc[VKILL] = CTRL-%c", CTRL(termios.c_cc[VKILL]));
|
||||||
PRINT(" c_cc[VEOF] = CTRL-%c", CTRL(termios.c_cc[VEOF]));
|
PRINT(" c_cc[VEOF] = CTRL-%c", CTRL(termios.c_cc[VEOF]));
|
||||||
PRINT(" c_cc[VTIME] = CTRL-%c", CTRL(termios.c_cc[VTIME]));
|
|
||||||
PRINT(" c_cc[VMIN] = CTRL-%c", CTRL(termios.c_cc[VMIN]));
|
|
||||||
PRINT(" c_cc[VSTART] = CTRL-%c", CTRL(termios.c_cc[VSTART]));
|
PRINT(" c_cc[VSTART] = CTRL-%c", CTRL(termios.c_cc[VSTART]));
|
||||||
PRINT(" c_cc[VSTOP] = CTRL-%c", CTRL(termios.c_cc[VSTOP]));
|
PRINT(" c_cc[VSTOP] = CTRL-%c", CTRL(termios.c_cc[VSTOP]));
|
||||||
PRINT(" c_cc[VSUSP] = CTRL-%c", CTRL(termios.c_cc[VSUSP]));
|
PRINT(" c_cc[VSUSP] = CTRL-%c", CTRL(termios.c_cc[VSUSP]));
|
||||||
|
@ -547,7 +547,7 @@ textstartup void __printargs(const char *prologue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PRINT("");
|
PRINT("");
|
||||||
__strace = st;
|
__atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED);
|
||||||
g_ftrace = ft;
|
__atomic_fetch_add(&g_ftrace, 1, __ATOMIC_RELAXED);
|
||||||
errno = e;
|
errno = e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,6 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
|
||||||
void clearerr(FILE *f) {
|
void clearerr_unlocked(FILE *f) {
|
||||||
f->state = 0;
|
f->state = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdio/internal.h"
|
#include "libc/stdio/internal.h"
|
||||||
|
|
|
@ -21,6 +21,6 @@
|
||||||
/**
|
/**
|
||||||
* Returns true if stream is in end-of-file state.
|
* Returns true if stream is in end-of-file state.
|
||||||
*/
|
*/
|
||||||
int feof(FILE *f) {
|
int feof_unlocked(FILE *f) {
|
||||||
return f->state == -1;
|
return f->state == -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
* @note EOF doesn't count
|
* @note EOF doesn't count
|
||||||
* @see feof()
|
* @see feof()
|
||||||
*/
|
*/
|
||||||
errno_t ferror(FILE *f) {
|
errno_t ferror_unlocked(FILE *f) {
|
||||||
return f->state > 0 ? f->state : 0;
|
return f->state > 0 ? f->state : 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "libc/bits/pushpop.h"
|
#include "libc/bits/pushpop.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
@ -35,49 +36,63 @@
|
||||||
* @param f is the stream handle
|
* @param f is the stream handle
|
||||||
* @return is 0 on success or -1 on error
|
* @return is 0 on success or -1 on error
|
||||||
*/
|
*/
|
||||||
int fflush(FILE *f) {
|
int fflush_unlocked(FILE *f) {
|
||||||
|
int rc = 0;
|
||||||
size_t i;
|
size_t i;
|
||||||
if (!f) {
|
if (!f) {
|
||||||
|
_spinlock(&__fflush.lock);
|
||||||
for (i = __fflush.handles.i; i; --i) {
|
for (i = __fflush.handles.i; i; --i) {
|
||||||
if ((f = __fflush.handles.p[i - 1])) {
|
if ((f = __fflush.handles.p[i - 1])) {
|
||||||
if (fflush(f) == -1) return -1;
|
if (fflush(f) == -1) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_spunlock(&__fflush.lock);
|
||||||
} else if (f->fd != -1) {
|
} else if (f->fd != -1) {
|
||||||
if (__fflush_impl(f) == -1) return -1;
|
if (__fflush_impl(f) == -1) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
} else if (f->beg && f->beg < f->size) {
|
} else if (f->beg && f->beg < f->size) {
|
||||||
f->buf[f->beg] = 0;
|
f->buf[f->beg] = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
textstartup int __fflush_register(FILE *f) {
|
textstartup int __fflush_register(FILE *f) {
|
||||||
|
int rc;
|
||||||
size_t i;
|
size_t i;
|
||||||
struct StdioFlush *sf;
|
struct StdioFlush *sf;
|
||||||
|
_spinlock(&__fflush.lock);
|
||||||
sf = &__fflush;
|
sf = &__fflush;
|
||||||
if (!sf->handles.p) {
|
if (!sf->handles.p) {
|
||||||
sf->handles.p = sf->handles_initmem;
|
sf->handles.p = sf->handles_initmem;
|
||||||
pushmov(&sf->handles.n, ARRAYLEN(sf->handles_initmem));
|
pushmov(&sf->handles.n, ARRAYLEN(sf->handles_initmem));
|
||||||
__cxa_atexit(fflush, 0, 0);
|
__cxa_atexit(fflush_unlocked, 0, 0);
|
||||||
}
|
}
|
||||||
for (i = sf->handles.i; i; --i) {
|
for (i = sf->handles.i; i; --i) {
|
||||||
if (!sf->handles.p[i - 1]) {
|
if (!sf->handles.p[i - 1]) {
|
||||||
sf->handles.p[i - 1] = f;
|
sf->handles.p[i - 1] = f;
|
||||||
|
_spunlock(&__fflush.lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return append(&sf->handles, &f);
|
rc = append(&sf->handles, &f);
|
||||||
|
_spunlock(&__fflush.lock);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __fflush_unregister(FILE *f) {
|
void __fflush_unregister(FILE *f) {
|
||||||
size_t i;
|
size_t i;
|
||||||
struct StdioFlush *sf;
|
struct StdioFlush *sf;
|
||||||
|
_spinlock(&__fflush.lock);
|
||||||
sf = &__fflush;
|
sf = &__fflush;
|
||||||
sf = pushpop(sf);
|
sf = pushpop(sf);
|
||||||
for (i = sf->handles.i; i; --i) {
|
for (i = sf->handles.i; i; --i) {
|
||||||
if (sf->handles.p[i - 1] == f) {
|
if (sf->handles.p[i - 1] == f) {
|
||||||
pushmov(&sf->handles.p[i - 1], 0);
|
pushmov(&sf->handles.p[i - 1], 0);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_spunlock(&__fflush.lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,12 @@ struct StdioFlushHandles {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StdioFlush {
|
struct StdioFlush {
|
||||||
|
char lock;
|
||||||
struct StdioFlushHandles handles;
|
struct StdioFlushHandles handles;
|
||||||
FILE *handles_initmem[8];
|
FILE *handles_initmem[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct StdioFlush __fflush hidden;
|
hidden extern struct StdioFlush __fflush;
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -22,12 +22,12 @@
|
||||||
* Reads byte from stream.
|
* Reads byte from stream.
|
||||||
* @return byte in range 0..255, or -1 w/ errno
|
* @return byte in range 0..255, or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int fgetc(FILE *f) {
|
int fgetc_unlocked(FILE *f) {
|
||||||
unsigned char b[1];
|
unsigned char b[1];
|
||||||
if (f->beg < f->end) {
|
if (f->beg < f->end) {
|
||||||
return f->buf[f->beg++] & 0xff;
|
return f->buf[f->beg++] & 0xff;
|
||||||
} else {
|
} else {
|
||||||
if (!fread(b, 1, 1, f)) return -1;
|
if (!fread_unlocked(b, 1, 1, f)) return -1;
|
||||||
return b[0];
|
return b[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,36 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads content from stream.
|
* Reads line from stream.
|
||||||
*
|
*
|
||||||
* This function is similar to getline() except it'll truncate lines
|
* This function is similar to getline() except it'll truncate lines
|
||||||
* exceeding size. The line ending marker is included and may be removed
|
* exceeding size. The line ending marker is included and may be removed
|
||||||
* using _chomp().
|
* using _chomp().
|
||||||
|
*
|
||||||
|
* @param s is output buffer
|
||||||
|
* @param size is capacity of s
|
||||||
|
* @param f is non-null file oject stream pointer
|
||||||
|
* @return s on success, NULL on error, or NULL if EOF happens when
|
||||||
|
* zero characters have been read
|
||||||
*/
|
*/
|
||||||
char *fgets(char *s, int size, FILE *f) {
|
char *fgets_unlocked(char *s, int size, FILE *f) {
|
||||||
int c;
|
int c;
|
||||||
char *p;
|
char *p;
|
||||||
p = s;
|
p = s;
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
while (--size > 0) {
|
while (--size > 0) {
|
||||||
if ((c = getc(f)) == -1) {
|
if ((c = fgetc_unlocked(f)) == -1) {
|
||||||
if (ferror(f) == EINTR) continue;
|
if (ferror_unlocked(f) == EINTR) {
|
||||||
break;
|
continue;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*p++ = c & 0xff;
|
*p++ = c & 255;
|
||||||
if (c == '\n') break;
|
if (c == '\n') break;
|
||||||
}
|
}
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
|
@ -24,12 +24,12 @@
|
||||||
* Reads UTF-8 character from stream.
|
* Reads UTF-8 character from stream.
|
||||||
* @return wide character or -1 on EOF or error
|
* @return wide character or -1 on EOF or error
|
||||||
*/
|
*/
|
||||||
wint_t fgetwc(FILE *f) {
|
wint_t fgetwc_unlocked(FILE *f) {
|
||||||
int c, n;
|
int c, n;
|
||||||
wint_t b, x, y;
|
wint_t b, x, y;
|
||||||
if (f->beg < f->end) {
|
if (f->beg < f->end) {
|
||||||
b = f->buf[f->beg++] & 0xff;
|
b = f->buf[f->beg++] & 0xff;
|
||||||
} else if ((c = fgetc(f)) != -1) {
|
} else if ((c = fgetc_unlocked(f)) != -1) {
|
||||||
b = c;
|
b = c;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -38,12 +38,12 @@ wint_t fgetwc(FILE *f) {
|
||||||
n = ThomPikeLen(b);
|
n = ThomPikeLen(b);
|
||||||
x = ThomPikeByte(b);
|
x = ThomPikeByte(b);
|
||||||
while (--n) {
|
while (--n) {
|
||||||
if ((c = fgetc(f)) == -1) return -1;
|
if ((c = fgetc_unlocked(f)) == -1) return -1;
|
||||||
y = c;
|
y = c;
|
||||||
if (ThomPikeCont(y)) {
|
if (ThomPikeCont(y)) {
|
||||||
x = ThomPikeMerge(x, y);
|
x = ThomPikeMerge(x, y);
|
||||||
} else {
|
} else {
|
||||||
ungetc(y, f);
|
ungetc_unlocked(y, f);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,13 +23,13 @@
|
||||||
/**
|
/**
|
||||||
* Reads UTF-8 content from stream into UTF-32 buffer.
|
* Reads UTF-8 content from stream into UTF-32 buffer.
|
||||||
*/
|
*/
|
||||||
wchar_t *fgetws(wchar_t *s, int size, FILE *f) {
|
wchar_t *fgetws_unlocked(wchar_t *s, int size, FILE *f) {
|
||||||
wint_t c;
|
wint_t c;
|
||||||
wchar_t *p = s;
|
wchar_t *p = s;
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
while (--size > 0) {
|
while (--size > 0) {
|
||||||
if ((c = fgetwc(f)) == -1) {
|
if ((c = fgetwc_unlocked(f)) == -1) {
|
||||||
if (ferror(f) == EINTR) continue;
|
if (ferror_unlocked(f) == EINTR) continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*p++ = c;
|
*p++ = c;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
/**
|
/**
|
||||||
* Returns file descriptor associated with stream.
|
* Returns file descriptor associated with stream.
|
||||||
*/
|
*/
|
||||||
int fileno(FILE *f) {
|
int fileno_unlocked(FILE *f) {
|
||||||
if (f->fd != -1) {
|
if (f->fd != -1) {
|
||||||
return f->fd;
|
return f->fd;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,23 +16,12 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does nothing since Cosmopolitan currently doesn't support threads.
|
* Acquires lock on stdio object, blocking if needed.
|
||||||
*/
|
*/
|
||||||
void flockfile(FILE *f) {
|
void flockfile(FILE *f) {
|
||||||
}
|
_spinlock(&f->lock);
|
||||||
|
|
||||||
/**
|
|
||||||
* Does nothing since Cosmopolitan currently doesn't support threads.
|
|
||||||
*/
|
|
||||||
void funlockfile(FILE *f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does nothing since Cosmopolitan currently doesn't support threads.
|
|
||||||
*/
|
|
||||||
int ftrylockfile(FILE *f) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/stdio/fflush.internal.h"
|
#include "libc/stdio/fflush.internal.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/stdio/stdio_ext.h"
|
#include "libc/stdio/stdio_ext.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,9 +27,16 @@
|
||||||
*/
|
*/
|
||||||
void _flushlbf(void) {
|
void _flushlbf(void) {
|
||||||
int i;
|
int i;
|
||||||
|
FILE *f;
|
||||||
|
_spinlock(&__fflush.lock);
|
||||||
for (i = 0; i < __fflush.handles.i; ++i) {
|
for (i = 0; i < __fflush.handles.i; ++i) {
|
||||||
if (__fflush.handles.p[i]->bufmode == _IOLBF) {
|
if ((f = __fflush.handles.p[i])) {
|
||||||
fflush(__fflush.handles.p[i]);
|
_spinlock(&f->lock);
|
||||||
|
if (f->bufmode == _IOLBF) {
|
||||||
|
fflush_unlocked(f);
|
||||||
|
}
|
||||||
|
_spunlock(&f->lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_spunlock(&__fflush.lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,14 @@
|
||||||
* @param c is byte to buffer or write, which is masked
|
* @param c is byte to buffer or write, which is masked
|
||||||
* @return c as unsigned char if written or -1 w/ errno
|
* @return c as unsigned char if written or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int fputc(int c, FILE *f) {
|
int fputc_unlocked(int c, FILE *f) {
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
if (c != '\n' && f->beg < f->size && f->bufmode != _IONBF) {
|
if (c != '\n' && f->beg < f->size && f->bufmode != _IONBF) {
|
||||||
f->buf[f->beg++] = c;
|
f->buf[f->beg++] = c;
|
||||||
return c & 0xff;
|
return c & 0xff;
|
||||||
} else {
|
} else {
|
||||||
b = c;
|
b = c;
|
||||||
if (!fwrite(&b, 1, 1, f)) return -1;
|
if (!fwrite_unlocked(&b, 1, 1, f)) return -1;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,10 @@
|
||||||
* @param f is an open stream
|
* @param f is an open stream
|
||||||
* @return bytes written, or -1 w/ errno
|
* @return bytes written, or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int fputs(const char *s, FILE *f) {
|
int fputs_unlocked(const char *s, FILE *f) {
|
||||||
size_t n, r;
|
size_t n, r;
|
||||||
n = strlen(s);
|
n = strlen(s);
|
||||||
r = fwrite(s, 1, n, f);
|
r = fwrite_unlocked(s, 1, n, f);
|
||||||
if (!r && n) return -1;
|
if (!r && n) return -1;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,12 @@
|
||||||
*
|
*
|
||||||
* @return wc if written or -1 w/ errno
|
* @return wc if written or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
wint_t fputwc(wchar_t wc, FILE *f) {
|
wint_t fputwc_unlocked(wchar_t wc, FILE *f) {
|
||||||
uint64_t w;
|
uint64_t w;
|
||||||
if (wc != -1) {
|
if (wc != -1) {
|
||||||
w = tpenc(wc);
|
w = tpenc(wc);
|
||||||
do {
|
do {
|
||||||
if (fputc(w, f) == -1) {
|
if (fputc_unlocked(w, f) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} while ((w >>= 8));
|
} while ((w >>= 8));
|
||||||
|
|
|
@ -30,12 +30,16 @@
|
||||||
* @param f is an open stream
|
* @param f is an open stream
|
||||||
* @return strlen(s) or -1 w/ errno on error
|
* @return strlen(s) or -1 w/ errno on error
|
||||||
*/
|
*/
|
||||||
int fputws(const wchar_t *s, FILE *f) {
|
int fputws_unlocked(const wchar_t *s, FILE *f) {
|
||||||
int res = 0;
|
int res = 0;
|
||||||
while (*s) {
|
while (*s) {
|
||||||
if (fputwc(*s++, f) == -1) {
|
if (fputwc_unlocked(*s++, f) == -1) {
|
||||||
if (ferror(f) == EINTR) continue;
|
if (ferror_unlocked(f) == EINTR) {
|
||||||
if (feof(f)) errno = f->state = EPIPE;
|
continue;
|
||||||
|
}
|
||||||
|
if (feof_unlocked(f)) {
|
||||||
|
errno = f->state = EPIPE;
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
++res;
|
++res;
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
* @param count is the number of strides to fetch
|
* @param count is the number of strides to fetch
|
||||||
* @return count on success, [0,count) on eof, or 0 on error or count==0
|
* @return count on success, [0,count) on eof, or 0 on error or count==0
|
||||||
*/
|
*/
|
||||||
size_t fread(void *buf, size_t stride, size_t count, FILE *f) {
|
size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
|
||||||
char *p;
|
char *p;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
size_t n, m;
|
size_t n, m;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/sysv/consts/f.h"
|
#include "libc/sysv/consts/f.h"
|
||||||
#include "libc/sysv/consts/fd.h"
|
#include "libc/sysv/consts/fd.h"
|
||||||
|
@ -37,9 +38,11 @@
|
||||||
*/
|
*/
|
||||||
FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
||||||
int fd;
|
int fd;
|
||||||
|
FILE *res;
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
flags = fopenflags(mode);
|
flags = fopenflags(mode);
|
||||||
fflush(stream);
|
_spinlock(&stream->lock);
|
||||||
|
fflush_unlocked(stream);
|
||||||
if (pathname) {
|
if (pathname) {
|
||||||
/* open new stream, overwriting existing alloc */
|
/* open new stream, overwriting existing alloc */
|
||||||
if ((fd = open(pathname, flags, 0666)) != -1) {
|
if ((fd = open(pathname, flags, 0666)) != -1) {
|
||||||
|
@ -48,13 +51,15 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
||||||
stream->iomode = flags;
|
stream->iomode = flags;
|
||||||
stream->beg = 0;
|
stream->beg = 0;
|
||||||
stream->end = 0;
|
stream->end = 0;
|
||||||
return stream;
|
res = stream;
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
res = NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fcntl(stream->fd, F_SETFD, !!(flags & O_CLOEXEC));
|
fcntl(stream->fd, F_SETFD, !!(flags & O_CLOEXEC));
|
||||||
fcntl(stream->fd, F_SETFL, flags & ~O_CLOEXEC);
|
fcntl(stream->fd, F_SETFL, flags & ~O_CLOEXEC);
|
||||||
return stream;
|
res = stream;
|
||||||
}
|
}
|
||||||
|
_spunlock(&stream->lock);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/stdio/internal.h"
|
#include "libc/stdio/internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
@ -36,8 +37,10 @@
|
||||||
* @returns 0 on success or -1 on error
|
* @returns 0 on success or -1 on error
|
||||||
*/
|
*/
|
||||||
int fseeko(FILE *f, int64_t offset, int whence) {
|
int fseeko(FILE *f, int64_t offset, int whence) {
|
||||||
|
int res;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
int64_t pos;
|
int64_t pos;
|
||||||
|
_spinlock(&f->lock);
|
||||||
if (f->fd != -1) {
|
if (f->fd != -1) {
|
||||||
if (__fflush_impl(f) == -1) return -1;
|
if (__fflush_impl(f) == -1) return -1;
|
||||||
if (whence == SEEK_CUR && f->beg < f->end) {
|
if (whence == SEEK_CUR && f->beg < f->end) {
|
||||||
|
@ -46,10 +49,10 @@ int fseeko(FILE *f, int64_t offset, int whence) {
|
||||||
if (lseek(f->fd, offset, whence) != -1) {
|
if (lseek(f->fd, offset, whence) != -1) {
|
||||||
f->beg = 0;
|
f->beg = 0;
|
||||||
f->end = 0;
|
f->end = 0;
|
||||||
return 0;
|
res = 0;
|
||||||
} else {
|
} else {
|
||||||
f->state = errno == ESPIPE ? EBADF : errno;
|
f->state = errno == ESPIPE ? EBADF : errno;
|
||||||
return -1;
|
res = -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (whence) {
|
switch (whence) {
|
||||||
|
@ -68,10 +71,12 @@ int fseeko(FILE *f, int64_t offset, int whence) {
|
||||||
}
|
}
|
||||||
if (0 <= pos && pos <= f->end) {
|
if (0 <= pos && pos <= f->end) {
|
||||||
f->beg = pos;
|
f->beg = pos;
|
||||||
return 0;
|
res = 0;
|
||||||
} else {
|
} else {
|
||||||
f->state = errno = EINVAL;
|
f->state = errno = EINVAL;
|
||||||
return -1;
|
res = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_spunlock(&f->lock);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
#include "libc/stdio/stdio_ext.h"
|
#include "libc/stdio/stdio_ext.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does nothing and returns `FSETLOCKING_BYCALLER`.
|
* Does nothing and returns `FSETLOCKING_INTERNAL`.
|
||||||
*/
|
*/
|
||||||
int __fsetlocking(FILE *f, int type) {
|
int __fsetlocking(FILE *f, int type) {
|
||||||
return FSETLOCKING_BYCALLER;
|
return FSETLOCKING_INTERNAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdio/internal.h"
|
#include "libc/stdio/internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
|
||||||
/**
|
static int64_t ftello_unlocked(FILE *f) {
|
||||||
* Returns current position of stream.
|
|
||||||
*
|
|
||||||
* @param stream is a non-null stream handle
|
|
||||||
* @returns current byte offset from beginning, or -1 w/ errno
|
|
||||||
*/
|
|
||||||
int64_t ftello(FILE *f) {
|
|
||||||
int64_t pos;
|
int64_t pos;
|
||||||
uint32_t skew;
|
uint32_t skew;
|
||||||
if (f->fd != -1) {
|
if (f->fd != -1) {
|
||||||
|
@ -45,3 +40,17 @@ int64_t ftello(FILE *f) {
|
||||||
return f->beg;
|
return f->beg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current position of stream.
|
||||||
|
*
|
||||||
|
* @param stream is a non-null stream handle
|
||||||
|
* @returns current byte offset from beginning, or -1 w/ errno
|
||||||
|
*/
|
||||||
|
int64_t ftello(FILE *f) {
|
||||||
|
int64_t rc;
|
||||||
|
_spinlock(&f->lock);
|
||||||
|
rc = ftello_unlocked(f);
|
||||||
|
_spunlock(&f->lock);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
28
libc/stdio/ftrylockfile.c
Normal file
28
libc/stdio/ftrylockfile.c
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to acquire stdio object lock.
|
||||||
|
* @return 0 for success or non-zero if someone else has the lock
|
||||||
|
*/
|
||||||
|
int ftrylockfile(FILE *f) {
|
||||||
|
return _trylock(&f->lock);
|
||||||
|
}
|
27
libc/stdio/funlockfile.c
Normal file
27
libc/stdio/funlockfile.c
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*-*- 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 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases lock on stdio object.
|
||||||
|
*/
|
||||||
|
void funlockfile(FILE *f) {
|
||||||
|
_spunlock(&f->lock);
|
||||||
|
}
|
|
@ -36,7 +36,7 @@
|
||||||
* @param count is the number of strides to write
|
* @param count is the number of strides to write
|
||||||
* @return count on success, [0,count) on EOF, 0 on error or count==0
|
* @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||||
*/
|
*/
|
||||||
size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) {
|
size_t fwrite_unlocked(const void *data, size_t stride, size_t count, FILE *f) {
|
||||||
ldiv_t d;
|
ldiv_t d;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
size_t n, m;
|
size_t n, m;
|
||||||
|
|
|
@ -22,6 +22,6 @@
|
||||||
* Reads byte from stream.
|
* Reads byte from stream.
|
||||||
* @return byte in range 0..255, or -1 w/ errno
|
* @return byte in range 0..255, or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int(getc)(FILE *f) {
|
int(getc_unlocked)(FILE *f) {
|
||||||
return fgetc(f);
|
return fgetc_unlocked(f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads byte from stream.
|
* Reads byte from stdin.
|
||||||
* @return byte in range 0..255, or -1 w/ errno
|
* @return byte in range 0..255, or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int getchar(void) {
|
int getchar_unlocked(void) {
|
||||||
return fgetc(stdin);
|
return fgetc_unlocked(stdin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
@ -26,19 +27,7 @@
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
|
||||||
/**
|
static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) {
|
||||||
* Reads string from stream.
|
|
||||||
*
|
|
||||||
* @param s is the caller's buffer (in/out) which is extended or
|
|
||||||
* allocated automatically, also NUL-terminated is guaranteed
|
|
||||||
* @param n is the capacity of s (in/out)
|
|
||||||
* @param delim is the stop char (and NUL is implicitly too)
|
|
||||||
* @return number of bytes read >0, including delim, excluding NUL,
|
|
||||||
* or -1 w/ errno on EOF or error; see ferror() and feof()
|
|
||||||
* @note this function can't punt EINTR to caller
|
|
||||||
* @see getline(), _chomp(), gettok_r()
|
|
||||||
*/
|
|
||||||
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
|
||||||
char *p;
|
char *p;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
size_t i, m;
|
size_t i, m;
|
||||||
|
@ -83,3 +72,23 @@ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads string from stream.
|
||||||
|
*
|
||||||
|
* @param s is the caller's buffer (in/out) which is extended or
|
||||||
|
* allocated automatically, also NUL-terminated is guaranteed
|
||||||
|
* @param n is the capacity of s (in/out)
|
||||||
|
* @param delim is the stop char (and NUL is implicitly too)
|
||||||
|
* @return number of bytes read >0, including delim, excluding NUL,
|
||||||
|
* or -1 w/ errno on EOF or error; see ferror() and feof()
|
||||||
|
* @note this function can't punt EINTR to caller
|
||||||
|
* @see getline(), _chomp(), gettok_r()
|
||||||
|
*/
|
||||||
|
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
||||||
|
ssize_t rc;
|
||||||
|
_spinlock(&f->lock);
|
||||||
|
rc = getdelim_unlocked(s, n, delim, f);
|
||||||
|
_spunlock(&f->lock);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,6 @@
|
||||||
* Reads UTF-8 character from stream.
|
* Reads UTF-8 character from stream.
|
||||||
* @return wide character or -1 on EOF or error
|
* @return wide character or -1 on EOF or error
|
||||||
*/
|
*/
|
||||||
wint_t(getwc)(FILE *f) {
|
wint_t(getwc_unlocked)(FILE *f) {
|
||||||
return fgetwc(f);
|
return fgetwc_unlocked(f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,6 @@
|
||||||
* Reads UTF-8 character from stream.
|
* Reads UTF-8 character from stream.
|
||||||
* @return wide character or -1 on EOF or error
|
* @return wide character or -1 on EOF or error
|
||||||
*/
|
*/
|
||||||
wint_t getwchar(void) {
|
wint_t getwchar_unlocked(void) {
|
||||||
return fgetwc(stdin);
|
return fgetwc_unlocked(stdin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
* @param c is byte to buffer or write, which is masked
|
* @param c is byte to buffer or write, which is masked
|
||||||
* @return c as unsigned char if written or -1 w/ errno
|
* @return c as unsigned char if written or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int(putc)(int c, FILE *f) {
|
int(putc_unlocked)(int c, FILE *f) {
|
||||||
return fputc(c, f);
|
return fputc_unlocked(c, f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,4 +23,6 @@
|
||||||
*
|
*
|
||||||
* @return c (as unsigned char) if written or -1 w/ errno
|
* @return c (as unsigned char) if written or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
int putchar(int c) { return fputc(c, stdout); }
|
int putchar_unlocked(int c) {
|
||||||
|
return fputc_unlocked(c, stdout);
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,6 @@
|
||||||
*
|
*
|
||||||
* @return wc if written or -1 w/ errno
|
* @return wc if written or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
wint_t(putwc)(wchar_t wc, FILE *f) {
|
wint_t(putwc_unlocked)(wchar_t wc, FILE *f) {
|
||||||
return fputwc(wc, f);
|
return fputwc_unlocked(wc, f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,6 @@
|
||||||
* Writes wide character to stdout.
|
* Writes wide character to stdout.
|
||||||
* @return wc if written or -1 w/ errno
|
* @return wc if written or -1 w/ errno
|
||||||
*/
|
*/
|
||||||
wint_t putwchar(wchar_t wc) {
|
wint_t putwchar_unlocked(wchar_t wc) {
|
||||||
return fputwc(wc, stdout);
|
return fputwc_unlocked(wc, stdout);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ typedef struct FILE {
|
||||||
uint32_t size; /* 0x20 */
|
uint32_t size; /* 0x20 */
|
||||||
uint32_t nofree; /* 0x24 */
|
uint32_t nofree; /* 0x24 */
|
||||||
int pid; /* 0x28 */
|
int pid; /* 0x28 */
|
||||||
char *getln;
|
char lock; /* 0x2c */
|
||||||
|
char *getln; /* 0x30 */
|
||||||
} FILE;
|
} FILE;
|
||||||
|
|
||||||
extern FILE *stdin;
|
extern FILE *stdin;
|
||||||
|
@ -135,11 +136,48 @@ int fwide(FILE *, int);
|
||||||
#define vfscanf(F, FMT, VA) (vfscanf)(F, SFLINK(FMT), VA)
|
#define vfscanf(F, FMT, VA) (vfscanf)(F, SFLINK(FMT), VA)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
|
||||||
|
|
||||||
#define stdin SYMBOLIC(stdin)
|
#define stdin SYMBOLIC(stdin)
|
||||||
#define stdout SYMBOLIC(stdout)
|
#define stdout SYMBOLIC(stdout)
|
||||||
#define stderr SYMBOLIC(stderr)
|
#define stderr SYMBOLIC(stderr)
|
||||||
|
|
||||||
|
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||||
|
│ cosmopolitan § standard i/o » without mutexes ─╬─│┼
|
||||||
|
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||||
|
|
||||||
|
void flockfile(FILE *);
|
||||||
|
void funlockfile(FILE *);
|
||||||
|
int ftrylockfile(FILE *);
|
||||||
|
int getc_unlocked(FILE *) paramsnonnull();
|
||||||
|
int getchar_unlocked(void);
|
||||||
|
int putc_unlocked(int, FILE *) paramsnonnull();
|
||||||
|
int putchar_unlocked(int);
|
||||||
|
void clearerr_unlocked(FILE *);
|
||||||
|
int feof_unlocked(FILE *);
|
||||||
|
int ferror_unlocked(FILE *);
|
||||||
|
int fileno_unlocked(FILE *);
|
||||||
|
int fflush_unlocked(FILE *);
|
||||||
|
int fgetc_unlocked(FILE *);
|
||||||
|
int fputc_unlocked(int, FILE *);
|
||||||
|
size_t fread_unlocked(void *, size_t, size_t, FILE *);
|
||||||
|
size_t fwrite_unlocked(const void *, size_t, size_t, FILE *);
|
||||||
|
char *fgets_unlocked(char *, int, FILE *);
|
||||||
|
int fputs_unlocked(const char *, FILE *);
|
||||||
|
wint_t getwc_unlocked(FILE *);
|
||||||
|
wint_t getwchar_unlocked(void);
|
||||||
|
wint_t fgetwc_unlocked(FILE *);
|
||||||
|
wint_t fputwc_unlocked(wchar_t, FILE *);
|
||||||
|
wint_t putwc_unlocked(wchar_t, FILE *);
|
||||||
|
wint_t putwchar_unlocked(wchar_t);
|
||||||
|
wchar_t *fgetws_unlocked(wchar_t *, int, FILE *);
|
||||||
|
int fputws_unlocked(const wchar_t *, FILE *);
|
||||||
|
wint_t ungetwc_unlocked(wint_t, FILE *) paramsnonnull();
|
||||||
|
int ungetc_unlocked(int, FILE *) paramsnonnull();
|
||||||
|
|
||||||
|
#define getc_unlocked(f) fgetc_unlocked(f)
|
||||||
|
#define getwc_unlocked(f) fgetwc_unlocked(f)
|
||||||
|
#define putc_unlocked(c, f) fputc_unlocked(c, f)
|
||||||
|
#define putwc_unlocked(c, f) fputwc_unlocked(c, f)
|
||||||
|
|
||||||
|
COSMOPOLITAN_C_END_
|
||||||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
#endif /* COSMOPOLITAN_LIBC_STDIO_STDIO_H_ */
|
#endif /* COSMOPOLITAN_LIBC_STDIO_STDIO_H_ */
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
/**
|
/**
|
||||||
* Pushes byte back to stream.
|
* Pushes byte back to stream.
|
||||||
*/
|
*/
|
||||||
int ungetc(int c, FILE *f) {
|
int ungetc_unlocked(int c, FILE *f) {
|
||||||
if (c == -1) return -1;
|
if (c == -1) return -1;
|
||||||
if (f->beg) {
|
if (f->beg) {
|
||||||
f->buf[--f->beg] = c;
|
f->buf[--f->beg] = c;
|
||||||
|
@ -32,5 +32,5 @@ int ungetc(int c, FILE *f) {
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return c & 0xff;
|
return c & 255;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
/**
|
/**
|
||||||
* Pushes wide character back to stream.
|
* Pushes wide character back to stream.
|
||||||
*/
|
*/
|
||||||
wint_t ungetwc(wint_t c, FILE *f) {
|
wint_t ungetwc_unlocked(wint_t c, FILE *f) {
|
||||||
char b[6];
|
char b[6];
|
||||||
unsigned n;
|
unsigned n;
|
||||||
uint64_t w;
|
uint64_t w;
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_STDIO_UNLOCKED_H_
|
|
||||||
#define COSMOPOLITAN_LIBC_STDIO_UNLOCKED_H_
|
|
||||||
#include "libc/stdio/stdio.h"
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
|
||||||
COSMOPOLITAN_C_START_
|
|
||||||
|
|
||||||
void flockfile(FILE *);
|
|
||||||
void funlockfile(FILE *);
|
|
||||||
int ftrylockfile(FILE *);
|
|
||||||
int getc_unlocked(FILE *) paramsnonnull();
|
|
||||||
int getchar_unlocked(void);
|
|
||||||
int putc_unlocked(int, FILE *) paramsnonnull();
|
|
||||||
int putchar_unlocked(int);
|
|
||||||
void clearerr_unlocked(FILE *);
|
|
||||||
int feof_unlocked(FILE *);
|
|
||||||
int ferror_unlocked(FILE *);
|
|
||||||
int fileno_unlocked(FILE *);
|
|
||||||
int fflush_unlocked(FILE *);
|
|
||||||
int fgetc_unlocked(FILE *);
|
|
||||||
int fputc_unlocked(int, FILE *);
|
|
||||||
size_t fread_unlocked(void *, size_t, size_t, FILE *);
|
|
||||||
size_t fwrite_unlocked(const void *, size_t, size_t, FILE *);
|
|
||||||
char *fgets_unlocked(char *, int, FILE *);
|
|
||||||
int fputs_unlocked(const char *, FILE *);
|
|
||||||
wint_t getwc_unlocked(FILE *);
|
|
||||||
wint_t getwchar_unlocked(void);
|
|
||||||
wint_t fgetwc_unlocked(FILE *);
|
|
||||||
wint_t fputwc_unlocked(wchar_t, FILE *);
|
|
||||||
wint_t putwc_unlocked(wchar_t, FILE *);
|
|
||||||
wint_t putwchar_unlocked(wchar_t);
|
|
||||||
wchar_t *fgetws_unlocked(wchar_t *, int, FILE *);
|
|
||||||
int fputws_unlocked(const wchar_t *, FILE *);
|
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
|
||||||
#endif /* COSMOPOLITAN_LIBC_STDIO_UNLOCKED_H_ */
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
clearerr_unlocked:
|
// Clears error state on stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has stream pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @see clearerr_unlocked()
|
||||||
call clearerr
|
clearerr:
|
||||||
pop %rbp
|
mov %rdi,%r11
|
||||||
ret
|
ezlea clearerr_unlocked,ax
|
||||||
.endfn clearerr_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn clearerr,globl
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
feof_unlocked:
|
// Returns true if stream is in end-of-file state.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has file stream object pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @note EOF doesn't count
|
||||||
call feof
|
// @see feof_unlocked()
|
||||||
pop %rbp
|
feof: mov %rdi,%r11
|
||||||
ret
|
ezlea feof_unlocked,ax
|
||||||
.endfn feof_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn feof,globl
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
ferror_unlocked:
|
// Returns nonzero if stream is in error state.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has file stream object pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @note EOF doesn't count
|
||||||
call ferror
|
// @see ferror_unlocked()
|
||||||
pop %rbp
|
ferror: mov %rdi,%r11
|
||||||
ret
|
ezlea ferror_unlocked,ax
|
||||||
.endfn ferror_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn ferror,globl
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fflush_unlocked:
|
// Blocks until data from stream buffer is written out.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi is the stream handle
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @return 0 on success or -1 w/ errno
|
||||||
call fflush
|
// @see fflush_unlocked()
|
||||||
pop %rbp
|
fflush: mov %rdi,%r11
|
||||||
ret
|
ezlea fflush_unlocked,ax
|
||||||
.endfn fflush_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn fflush,globl
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fgetc_unlocked:
|
// Reads byte from stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has stream object pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @return byte in range 0..255, or -1 w/ errno
|
||||||
call fgetc
|
// @see fgetc_unlocked()
|
||||||
pop %rbp
|
fgetc: mov %rdi,%r11
|
||||||
ret
|
ezlea fgetc_unlocked,ax
|
||||||
.endfn fgetc_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn fgetc,globl
|
||||||
|
|
|
@ -18,11 +18,19 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fgets_unlocked:
|
// Reads line from stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// This function is similar to getline() except it'll truncate
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// lines exceeding size. The line ending marker is included
|
||||||
call fgets
|
// and may be removed using _chomp().
|
||||||
pop %rbp
|
//
|
||||||
ret
|
// @param rdi is output buffer
|
||||||
.endfn fgets_unlocked,globl
|
// @param rsi is size of rdi buffer
|
||||||
|
// @param rdx is file stream object pointer
|
||||||
|
// @return rax has rdi on success, NULL on error or
|
||||||
|
// NULL if EOF happens with zero chars read
|
||||||
|
// @see fgets_unlocked()
|
||||||
|
fgets: mov %rdx,%r11
|
||||||
|
ezlea fgets_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fgets,globl
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fgetwc_unlocked:
|
// Reads UTF-8 wide character from stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has stream object pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @return wide character or -1 on EOF or error
|
||||||
call fgetwc
|
// @see fgetwc_unlocked()
|
||||||
pop %rbp
|
fgetwc: mov %rdi,%r11
|
||||||
ret
|
ezlea fgetwc_unlocked,ax
|
||||||
.endfn fgetwc_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn fgetwc,globl
|
||||||
|
|
|
@ -18,11 +18,17 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fgetws_unlocked:
|
// Reads UTF-8 content from stream into UTF-32 buffer.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// This function is similar to getline() except it'll truncate lines
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// exceeding size. The line ending marker is included and may be removed
|
||||||
call fgetws
|
// using _chomp().
|
||||||
pop %rbp
|
//
|
||||||
ret
|
// @param rdi is nul-terminated string that's non-null
|
||||||
.endfn fgetws_unlocked,globl
|
// @param rsi is size of rdi buffer
|
||||||
|
// @param rsi is file stream object pointer
|
||||||
|
// @see fgetws_unlocked()
|
||||||
|
fgetws: mov %rdx,%r11
|
||||||
|
ezlea fgetws_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fgetws,globl
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fileno_unlocked:
|
// Returns file descriptor associated with stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has file stream object pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @see fileno_unlocked()
|
||||||
call fileno
|
fileno: mov %rdi,%r11
|
||||||
pop %rbp
|
ezlea fileno_unlocked,ax
|
||||||
ret
|
jmp stdio_unlock
|
||||||
.endfn fileno_unlocked,globl
|
.endfn fileno,globl
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fputc_unlocked:
|
// Writes character to stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi c is byte to buffer or write, which is masked
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @param rsi has stream object pointer
|
||||||
call fputc
|
// @return c as unsigned char if written or -1 w/ errno
|
||||||
pop %rbp
|
// @see fputc_unlocked()
|
||||||
ret
|
fputc: mov %rsi,%r11
|
||||||
.endfn fputc_unlocked,globl
|
ezlea fputc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fputc,globl
|
||||||
|
|
|
@ -18,11 +18,17 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fputs_unlocked:
|
// Writes string to stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// Writing stops at the NUL-terminator, which isn't included in output.
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// This function blocks until the full string is written, unless an
|
||||||
call fputs
|
// unrecoverable error happens.
|
||||||
pop %rbp
|
//
|
||||||
ret
|
// @param rdi is nul-terminated string that's non-null
|
||||||
.endfn fputs_unlocked,globl
|
// @param rsi is file object stream pointer
|
||||||
|
// @return strlen(rdi) on success or -1 w/ errno
|
||||||
|
// @see fputs_unlocked()
|
||||||
|
fputs: mov %rsi,%r11
|
||||||
|
ezlea fputs_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fputs,globl
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fputwc_unlocked:
|
// Writes wide character to stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has wide character
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @param rsi has file object stream pointer
|
||||||
call fputwc
|
// @return rax is wide character if written or -1 w/ errno
|
||||||
pop %rbp
|
// @see fputwc_unlocked()
|
||||||
ret
|
fputwc: mov %rsi,%r11
|
||||||
.endfn fputwc_unlocked,globl
|
ezlea fputwc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fputwc,globl
|
||||||
|
|
|
@ -18,11 +18,17 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fputws_unlocked:
|
// Writes wide character string to stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// Writing stops at the NUL-terminator, which isn't included in output.
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// This function blocks until the full string is written, unless an
|
||||||
call fputws
|
// unrecoverable error happens.
|
||||||
pop %rbp
|
//
|
||||||
ret
|
// @param rdi is nul-terminated string that's non-null
|
||||||
.endfn fputws_unlocked,globl
|
// @param rsi is file object stream pointer
|
||||||
|
// @return strlen(rdi) on success or -1 w/ errno
|
||||||
|
// @see fputws_unlocked()
|
||||||
|
fputws: mov %rsi,%r11
|
||||||
|
ezlea fputws_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fputws,globl
|
||||||
|
|
|
@ -18,11 +18,15 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fread_unlocked:
|
// Reads data from stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has pointer to data to read
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @param rsi stride specifies the size of individual items
|
||||||
call fread
|
// @param rdx count is the number of strides to read
|
||||||
pop %rbp
|
// @param rcx has file object stream pointer
|
||||||
ret
|
// @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||||
.endfn fread_unlocked,globl
|
// @see fread_unlocked()
|
||||||
|
fread: mov %rcx,%r11
|
||||||
|
ezlea fread_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fread,globl
|
||||||
|
|
|
@ -18,11 +18,15 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
fwrite_unlocked:
|
// Writes data to stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has pointer to data to write
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @param rsi stride specifies the size of individual items
|
||||||
call fwrite
|
// @param rdx count is the number of strides to write
|
||||||
pop %rbp
|
// @param rcx has file object stream pointer
|
||||||
ret
|
// @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||||
.endfn fwrite_unlocked,globl
|
// @see fwrite_unlocked()
|
||||||
|
fwrite: mov %rcx,%r11
|
||||||
|
ezlea fwrite_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn fwrite,globl
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
getc_unlocked:
|
// Reads character from stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has file stream object pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @return byte in range 0..255, or -1 w/ errno
|
||||||
call getc
|
// @see fgetc_unlocked()
|
||||||
pop %rbp
|
getc: mov %rdi,%r11
|
||||||
ret
|
ezlea fgetwc_unlocked,ax
|
||||||
.endfn getc_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn getc,globl
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
getchar_unlocked:
|
// Reads character from stdin.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @return byte in range 0..255, or -1 w/ errno
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @see fgetc_unlocked()
|
||||||
call getchar
|
getchar:
|
||||||
pop %rbp
|
lea stdin(%rip),%rdi
|
||||||
ret
|
mov %rdi,%r11
|
||||||
.endfn getchar_unlocked,globl
|
ezlea fgetc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn getchar,globl
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
getwc_unlocked:
|
// Reads UTF-8 character from stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has file stream object pointer
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @return wide character or -1 on EOF or error
|
||||||
call fgetwc_unlocked
|
// @see fgetwc_unlocked()
|
||||||
pop %rbp
|
getwc: mov %rdi,%r11
|
||||||
ret
|
ezlea fgetwc_unlocked,ax
|
||||||
.endfn getwc_unlocked,globl
|
jmp stdio_unlock
|
||||||
|
.endfn getwc,globl
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
getwchar_unlocked:
|
// Reads UTF-8 character from stdin.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @return wide character or -1 on EOF or error
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @see fgetwc_unlocked()
|
||||||
call getwchar
|
getwchar:
|
||||||
pop %rbp
|
lea stdin(%rip),%rdi
|
||||||
ret
|
mov %rdi,%r11
|
||||||
.endfn getwchar_unlocked,globl
|
ezlea fgetwc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn getwchar,globl
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
putc_unlocked:
|
// Writes character to stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi c is byte to buffer or write, which is masked
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @param rsi has stream object pointer
|
||||||
call putc
|
// @return c as unsigned char if written or -1 w/ errno
|
||||||
pop %rbp
|
// @see fputc_unlocked()
|
||||||
ret
|
putc: mov %rsi,%r11
|
||||||
.endfn putc_unlocked,globl
|
ezlea fputc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn putc,globl
|
||||||
|
|
|
@ -18,11 +18,14 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
putchar_unlocked:
|
// Writes character to stdout.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has character
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @return c (as unsigned char) if written or -1 w/ errno
|
||||||
call putchar
|
// @see fputc_unlocked()
|
||||||
pop %rbp
|
putchar:
|
||||||
ret
|
lea stdout(%rip),%rsi
|
||||||
.endfn putchar_unlocked,globl
|
mov %rsi,%r11
|
||||||
|
ezlea fputc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn putchar,globl
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
putwc_unlocked:
|
// Writes wide character to stream.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has wide character
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @param rsi has file object
|
||||||
call fputwc_unlocked
|
// @return wc if written or -1 w/ errno
|
||||||
pop %rbp
|
// @see putwc_unlocked()
|
||||||
ret
|
putwc: mov %rsi,%r11
|
||||||
.endfn putwc_unlocked,globl
|
ezlea fputwc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn putwc,globl
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
@ -19,11 +18,14 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
putwchar_unlocked:
|
// Writes wide character to stdout.
|
||||||
push %rbp
|
//
|
||||||
mov %rsp,%rbp
|
// @param rdi has wide character
|
||||||
.profilable # note: no consensus for threads exists in abis
|
// @return wc if written or -1 w/ errno
|
||||||
call putwchar
|
// @see fputwc_unlocked()
|
||||||
pop %rbp
|
putwchar:
|
||||||
ret
|
lea stdout(%rip),%rsi
|
||||||
.endfn putwchar_unlocked,globl
|
mov %rsi,%r11
|
||||||
|
ezlea fputwc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn putwchar,globl
|
||||||
|
|
70
libc/stdio/unlocked/stdio_unlock.S
Normal file
70
libc/stdio/unlocked/stdio_unlock.S
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
#define LOCK 0x2c /* see struct file in stdio.h */
|
||||||
|
|
||||||
|
// Wrapper for applying locking to stdio functions.
|
||||||
|
//
|
||||||
|
// This function is intended to be called by thunks.
|
||||||
|
//
|
||||||
|
// @param rax has the delegate function pointer
|
||||||
|
// @param rdi is passed along as an arg
|
||||||
|
// @param rsi is passed along as an arg
|
||||||
|
// @param rdx is passed along as an arg
|
||||||
|
// @param rcx is passed along as an arg
|
||||||
|
// @param r8 is passed along as an arg
|
||||||
|
// @param r9 is passed along as an arg
|
||||||
|
// @param r10 is passed along as an arg
|
||||||
|
// @param r11 has the FILE* obj pointer
|
||||||
|
// @return rax is passed along as result
|
||||||
|
// @return rdx is passed along as result
|
||||||
|
stdio_unlock:
|
||||||
|
push %rbp
|
||||||
|
mov %rsp,%rbp
|
||||||
|
|
||||||
|
// acquires mutex
|
||||||
|
push %rcx
|
||||||
|
push %rdx
|
||||||
|
mov $1,%cl
|
||||||
|
0: mov LOCK(%r11),%dl # optimistic
|
||||||
|
test %dl,%dl
|
||||||
|
je 2f
|
||||||
|
1: pause # hyperyield
|
||||||
|
jmp 0b
|
||||||
|
2: mov %ecx,%edx
|
||||||
|
xchg LOCK(%r11),%dl # locks bus!
|
||||||
|
test %dl,%dl
|
||||||
|
jne 1b
|
||||||
|
pop %rdx
|
||||||
|
pop %rcx
|
||||||
|
|
||||||
|
// calls delegate
|
||||||
|
push %rsi
|
||||||
|
push %r11
|
||||||
|
call *%rax
|
||||||
|
pop %r11
|
||||||
|
pop %rsi
|
||||||
|
|
||||||
|
// releases mutex
|
||||||
|
movb $0,LOCK(%r11)
|
||||||
|
|
||||||
|
pop %rbp
|
||||||
|
ret
|
||||||
|
.endfn stdio_unlock,globl
|
30
libc/stdio/unlocked/ungetc_unlocked.S
Normal file
30
libc/stdio/unlocked/ungetc_unlocked.S
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
// Pushes byte back to stream.
|
||||||
|
//
|
||||||
|
// @param rdi has character to push
|
||||||
|
// @param rds has stream object pointer
|
||||||
|
// @return rax has rdi on success or -1 w/ errno
|
||||||
|
// @see ungetc_unlocked()
|
||||||
|
ungetc: mov %rsi,%r11
|
||||||
|
ezlea ungetc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn ungetc,globl
|
31
libc/stdio/unlocked/ungetwc_unlocked.S
Normal file
31
libc/stdio/unlocked/ungetwc_unlocked.S
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
|
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
|
|
||||||
|
// Pushes byte back to stream.
|
||||||
|
//
|
||||||
|
// @param rdi has character to push
|
||||||
|
// @param rds has stream object pointer
|
||||||
|
// @return rax has rdi on success or -1 w/ errno
|
||||||
|
// @see ungetwc_unlocked()
|
||||||
|
ungetwc:
|
||||||
|
mov %rsi,%r11
|
||||||
|
ezlea ungetwc_unlocked,ax
|
||||||
|
jmp stdio_unlock
|
||||||
|
.endfn ungetwc,globl
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/fmt/fmt.h"
|
#include "libc/fmt/fmt.h"
|
||||||
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
@ -28,14 +29,23 @@ struct state {
|
||||||
};
|
};
|
||||||
|
|
||||||
static int vfprintfputchar(const char *s, struct state *t, size_t n) {
|
static int vfprintfputchar(const char *s, struct state *t, size_t n) {
|
||||||
|
int rc;
|
||||||
if (n) {
|
if (n) {
|
||||||
|
_spinlock(&t->f->lock);
|
||||||
if (n == 1 && *s != '\n' && t->f->beg < t->f->size &&
|
if (n == 1 && *s != '\n' && t->f->beg < t->f->size &&
|
||||||
t->f->bufmode != _IONBF) {
|
t->f->bufmode != _IONBF) {
|
||||||
t->f->buf[t->f->beg++] = *s;
|
t->f->buf[t->f->beg++] = *s;
|
||||||
} else if (!fwrite(s, 1, n, t->f)) {
|
t->n += n;
|
||||||
return -1;
|
rc = 0;
|
||||||
|
} else if (!fwrite_unlocked(s, 1, n, t->f)) {
|
||||||
|
rc = -1;
|
||||||
|
} else {
|
||||||
|
t->n += n;
|
||||||
|
rc = 0;
|
||||||
}
|
}
|
||||||
t->n += n;
|
_spunlock(&t->f->lock);
|
||||||
|
} else {
|
||||||
|
rc = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall mincore,0x04e04e04e204e01b,globl
|
|
2
libc/sysv/calls/sys_mincore.s
Normal file
2
libc/sysv/calls/sys_mincore.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall sys_mincore,0x04e04e04e204e01b,globl,hidden
|
|
@ -62,7 +62,7 @@ scall pselect 0x1b406e20a218afff globl
|
||||||
scall pselect6 0xfffffffffffff10e globl
|
scall pselect6 0xfffffffffffff10e globl
|
||||||
scall sys_sched_yield 0x15e12a14b103c018 globl hidden # swtch() on xnu
|
scall sys_sched_yield 0x15e12a14b103c018 globl hidden # swtch() on xnu
|
||||||
scall __sys_mremap 0x19bffffffffff019 globl hidden
|
scall __sys_mremap 0x19bffffffffff019 globl hidden
|
||||||
scall mincore 0x04e04e04e204e01b globl
|
scall sys_mincore 0x04e04e04e204e01b globl hidden
|
||||||
scall sys_madvise 0x04b04b04b204b01c globl hidden
|
scall sys_madvise 0x04b04b04b204b01c globl hidden
|
||||||
scall shmget 0x0e71210e7210901d globl # consider mmap
|
scall shmget 0x0e71210e7210901d globl # consider mmap
|
||||||
scall shmat 0x0e40e40e4210601e globl # consider mmap
|
scall shmat 0x0e40e40e4210601e globl # consider mmap
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
/*-*- mode:c; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/time/tz.internal.h"
|
#include "libc/time/tz.internal.h"
|
||||||
// clang-format off
|
// clang-format off
|
||||||
/* Return the difference between two timestamps. */
|
/* Return the difference between two timestamps. */
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#define LOCALTIME_IMPLEMENTATION
|
#define LOCALTIME_IMPLEMENTATION
|
||||||
|
#include "libc/bits/bits.h"
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/intrin/spinlock.h"
|
#include "libc/intrin/spinlock.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
@ -241,50 +242,12 @@ ttunspecified(struct state const *sp, int i)
|
||||||
return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0;
|
return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int_fast32_t
|
forceinline int_fast32_t detzcode(const char *const codep) {
|
||||||
detzcode(const char *const codep)
|
return READ32BE(codep);
|
||||||
{
|
|
||||||
register int_fast32_t result;
|
|
||||||
register int i;
|
|
||||||
int_fast32_t one = 1;
|
|
||||||
int_fast32_t halfmaxval = one << (32 - 2);
|
|
||||||
int_fast32_t maxval = halfmaxval - 1 + halfmaxval;
|
|
||||||
int_fast32_t minval = -1 - maxval;
|
|
||||||
|
|
||||||
result = codep[0] & 0x7f;
|
|
||||||
for (i = 1; i < 4; ++i)
|
|
||||||
result = (result << 8) | (codep[i] & 0xff);
|
|
||||||
|
|
||||||
if (codep[0] & 0x80) {
|
|
||||||
/* Do two's-complement negation even on non-two's-complement machines.
|
|
||||||
If the result would be minval - 1, return minval. */
|
|
||||||
result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0;
|
|
||||||
result += minval;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int_fast64_t
|
forceinline int_fast64_t detzcode64(const char *const codep) {
|
||||||
detzcode64(const char *const codep)
|
return READ64BE(codep);
|
||||||
{
|
|
||||||
register int_fast64_t result;
|
|
||||||
register int i;
|
|
||||||
int_fast64_t one = 1;
|
|
||||||
int_fast64_t halfmaxval = one << (64 - 2);
|
|
||||||
int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
|
|
||||||
int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval;
|
|
||||||
|
|
||||||
result = codep[0] & 0x7f;
|
|
||||||
for (i = 1; i < 8; ++i)
|
|
||||||
result = (result << 8) | (codep[i] & 0xff);
|
|
||||||
|
|
||||||
if (codep[0] & 0x80) {
|
|
||||||
/* Do two's-complement negation even on non-two's-complement machines.
|
|
||||||
If the result would be minval - 1, return minval. */
|
|
||||||
result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
|
|
||||||
result += minval;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/inttypes.h"
|
#include "libc/inttypes.h"
|
||||||
#include "libc/limits.h"
|
#include "libc/limits.h"
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/consts/ok.h"
|
#include "libc/sysv/consts/ok.h"
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
@ -376,8 +377,6 @@ int64_t time2posix_z(timezone_t, int64_t) nosideeffect;
|
||||||
** Finally, some convenience items.
|
** Finally, some convenience items.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
|
|
||||||
#define TYPE_SIGNED(type) (((type) -1) < 0)
|
|
||||||
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
|
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
|
||||||
|
|
||||||
/* Max and min values of the integer type T, of which only the bottom
|
/* Max and min values of the integer type T, of which only the bottom
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include "libc/zip.h"
|
#include "libc/zip.h"
|
||||||
#include "libc/zipos/zipos.internal.h"
|
#include "libc/zipos/zipos.internal.h"
|
||||||
|
|
||||||
|
// TODO(jart): improve time complexity here
|
||||||
|
|
||||||
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
|
ssize_t __zipos_find(struct Zipos *zipos, const struct ZiposUri *name) {
|
||||||
const char *zname;
|
const char *zname;
|
||||||
size_t i, n, c, znamesize;
|
size_t i, n, c, znamesize;
|
||||||
|
|
|
@ -91,9 +91,10 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
||||||
for (i = 0; i < THREADS; ++i) {
|
for (i = 0; i < THREADS; ++i) {
|
||||||
stacks[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
stacks[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||||
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||||
tid[i] = clone(Thrasher, stacks[i], GetStackSize(),
|
tid[i] =
|
||||||
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
clone(Thrasher, stacks[i], GetStackSize(),
|
||||||
(void *)(intptr_t)i, 0, 0, 0, 0);
|
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
||||||
|
(void *)(intptr_t)i, 0, 0, 0, 0);
|
||||||
ASSERT_NE(-1, tid[i]);
|
ASSERT_NE(-1, tid[i]);
|
||||||
}
|
}
|
||||||
ready = true;
|
ready = true;
|
||||||
|
|
|
@ -60,7 +60,8 @@ TEST(clone, test1) {
|
||||||
int tid;
|
int tid;
|
||||||
_spinlock(&lock);
|
_spinlock(&lock);
|
||||||
ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(),
|
ASSERT_NE(-1, (tid = clone(CloneTest1, stack, GetStackSize(),
|
||||||
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
|
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
|
||||||
|
CLONE_SIGHAND,
|
||||||
(void *)23, 0, 0, 0, 0)));
|
(void *)23, 0, 0, 0, 0)));
|
||||||
_spinlock(&lock);
|
_spinlock(&lock);
|
||||||
ASSERT_EQ(42, x);
|
ASSERT_EQ(42, x);
|
||||||
|
|
2
third_party/lua/liolib.c
vendored
2
third_party/lua/liolib.c
vendored
|
@ -30,8 +30,8 @@
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/calls/weirdtypes.h"
|
#include "libc/calls/weirdtypes.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/stdio/temp.h"
|
#include "libc/stdio/temp.h"
|
||||||
#include "libc/stdio/unlocked.h"
|
|
||||||
#include "third_party/lua/lauxlib.h"
|
#include "third_party/lua/lauxlib.h"
|
||||||
#include "third_party/lua/lprefix.h"
|
#include "third_party/lua/lprefix.h"
|
||||||
#include "third_party/lua/lua.h"
|
#include "third_party/lua/lua.h"
|
||||||
|
|
2
third_party/lua/lmem.c
vendored
2
third_party/lua/lmem.c
vendored
|
@ -27,6 +27,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#define lmem_c
|
#define lmem_c
|
||||||
#define LUA_CORE
|
#define LUA_CORE
|
||||||
|
#include "libc/log/log.h"
|
||||||
#include "third_party/lua/ldebug.h"
|
#include "third_party/lua/ldebug.h"
|
||||||
#include "third_party/lua/ldo.h"
|
#include "third_party/lua/ldo.h"
|
||||||
#include "third_party/lua/lgc.h"
|
#include "third_party/lua/lgc.h"
|
||||||
|
@ -170,6 +171,7 @@ static void *tryagain (lua_State *L, void *block,
|
||||||
size_t osize, size_t nsize) {
|
size_t osize, size_t nsize) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
if (completestate(g) && !g->gcstopem) {
|
if (completestate(g) && !g->gcstopem) {
|
||||||
|
WARNF("reacting to malloc() failure by running lua garbage collector...");
|
||||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||||
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||||
}
|
}
|
||||||
|
|
1
third_party/lz4cli/lz4cli.c
vendored
1
third_party/lz4cli/lz4cli.c
vendored
|
@ -46,6 +46,7 @@ asm(".include \"third_party/lz4cli/COPYING\"");
|
||||||
#include "third_party/lz4cli/lz4hc.h" /* LZ4HC_CLEVEL_MAX */
|
#include "third_party/lz4cli/lz4hc.h" /* LZ4HC_CLEVEL_MAX */
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/log/log.h"
|
#include "libc/log/log.h"
|
||||||
|
#include "libc/stdio/stdio.h"
|
||||||
#include "third_party/lz4cli/lz4.h" /* LZ4_VERSION_STRING */
|
#include "third_party/lz4cli/lz4.h" /* LZ4_VERSION_STRING */
|
||||||
|
|
||||||
|
|
||||||
|
|
1
third_party/python/Objects/fileobject.c
vendored
1
third_party/python/Objects/fileobject.c
vendored
|
@ -7,7 +7,6 @@
|
||||||
#define PY_SSIZE_T_CLEAN
|
#define PY_SSIZE_T_CLEAN
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
#include "libc/stdio/unlocked.h"
|
|
||||||
#include "third_party/python/Include/abstract.h"
|
#include "third_party/python/Include/abstract.h"
|
||||||
#include "third_party/python/Include/boolobject.h"
|
#include "third_party/python/Include/boolobject.h"
|
||||||
#include "third_party/python/Include/bytesobject.h"
|
#include "third_party/python/Include/bytesobject.h"
|
||||||
|
|
6
third_party/python/pyconfig.h
vendored
6
third_party/python/pyconfig.h
vendored
|
@ -150,9 +150,9 @@
|
||||||
/* #define HAVE_INITGROUPS 1 */
|
/* #define HAVE_INITGROUPS 1 */
|
||||||
/* #define HAVE_GETGROUPLIST 1 */
|
/* #define HAVE_GETGROUPLIST 1 */
|
||||||
|
|
||||||
#define HAVE_FSEEKO 1
|
#define HAVE_FSEEKO 1
|
||||||
#define HAVE_FTELLO 1
|
#define HAVE_FTELLO 1
|
||||||
/* #undef HAVE_GETC_UNLOCKED */
|
#define HAVE_GETC_UNLOCKED 1
|
||||||
|
|
||||||
#define HAVE_GETADDRINFO 1
|
#define HAVE_GETADDRINFO 1
|
||||||
#define HAVE_GAI_STRERROR 1
|
#define HAVE_GAI_STRERROR 1
|
||||||
|
|
2
third_party/sqlite3/os_unix.c
vendored
2
third_party/sqlite3/os_unix.c
vendored
|
@ -317,6 +317,7 @@ static pid_t randomnessPid = 0;
|
||||||
#define threadid 0
|
#define threadid 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
/*
|
/*
|
||||||
** HAVE_MREMAP defaults to true on Linux and false everywhere else.
|
** HAVE_MREMAP defaults to true on Linux and false everywhere else.
|
||||||
*/
|
*/
|
||||||
|
@ -327,6 +328,7 @@ static pid_t randomnessPid = 0;
|
||||||
# define HAVE_MREMAP 0
|
# define HAVE_MREMAP 0
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
/*
|
/*
|
||||||
|
|
1
third_party/sqlite3/sqlite3.mk
vendored
1
third_party/sqlite3/sqlite3.mk
vendored
|
@ -108,6 +108,7 @@ THIRD_PARTY_SQLITE3_FLAGS = \
|
||||||
-DBUILD_sqlite \
|
-DBUILD_sqlite \
|
||||||
-DHAVE_USLEEP \
|
-DHAVE_USLEEP \
|
||||||
-DHAVE_READLINK \
|
-DHAVE_READLINK \
|
||||||
|
-DHAVE_FCHOWN \
|
||||||
-DHAVE_LSTAT \
|
-DHAVE_LSTAT \
|
||||||
-DHAVE_GMTIME_R \
|
-DHAVE_GMTIME_R \
|
||||||
-DHAVE_FDATASYNC \
|
-DHAVE_FDATASYNC \
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
local mymodule = {}
|
local mymodule = {}
|
||||||
|
|
||||||
function mymodule.hello()
|
function mymodule.hello()
|
||||||
SetStatus(200)
|
|
||||||
SetHeader('Content-Type', 'text/html; charset=US-ASCII')
|
|
||||||
Write("<!doctype html>\r\n")
|
|
||||||
Write("<b>Hello World!</b>\r\n")
|
Write("<b>Hello World!</b>\r\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
189
tool/net/demo/binarytrees.lua
Normal file
189
tool/net/demo/binarytrees.lua
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
-- binary trees: benchmark game
|
||||||
|
-- with resource limit tutorial
|
||||||
|
-- by justine tunney
|
||||||
|
|
||||||
|
local outofcpu = false
|
||||||
|
local oldsigxcpufunc = nil
|
||||||
|
local oldsigxcpuflags = nil
|
||||||
|
local oldsigxcpumask = nil
|
||||||
|
|
||||||
|
-- This is our signal handler.
|
||||||
|
function OnSigxcpu(sig)
|
||||||
|
-- Please note, it's dangerous to do complicated things from inside
|
||||||
|
-- asynchronous signal handlers. Even Log() isn't async signal safe
|
||||||
|
-- The best possible practice is to have them simply set a variable
|
||||||
|
outofcpu = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- These two functions are what's being benchmarked. It's a popular
|
||||||
|
-- torture test for interpreted languages since it generates a lot of
|
||||||
|
-- garbage. Lua does well on this test, going faster than many other
|
||||||
|
-- languages, e.g. Racket, Python.
|
||||||
|
|
||||||
|
local function MakeTree(depth)
|
||||||
|
if outofcpu then
|
||||||
|
error({reason='MakeTree() caught SIGXCPU'})
|
||||||
|
end
|
||||||
|
if depth > 0 then
|
||||||
|
depth = depth - 1
|
||||||
|
left, right = MakeTree(depth), MakeTree(depth)
|
||||||
|
return { left, right }
|
||||||
|
else
|
||||||
|
return { }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function CheckTree(tree)
|
||||||
|
if outofcpu then
|
||||||
|
error({reason='CheckTree() caught SIGXCPU'})
|
||||||
|
end
|
||||||
|
if tree[1] then
|
||||||
|
return 1 + CheckTree(tree[1]) + CheckTree(tree[2])
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Now for our redbean web server code...
|
||||||
|
|
||||||
|
local function WriteForm(depth, suggestion)
|
||||||
|
Write([[<!doctype html>
|
||||||
|
|
||||||
|
<title>redbean binary trees</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body { padding: 1em; }
|
||||||
|
h1 a { color: inherit; text-decoration: none; }
|
||||||
|
h1 img { border: none; vertical-align: middle; }
|
||||||
|
input { margin: 1em; padding: .5em; }
|
||||||
|
p { word-break: break-word; max-width: 650px; }
|
||||||
|
dt { font-weight: bold; }
|
||||||
|
dd { margin-top: 1em; margin-bottom: 1em; }
|
||||||
|
.hdr { text-indent: -1em; padding-left: 1em; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h1>
|
||||||
|
<a href="/"><img src="/redbean.png"></a>
|
||||||
|
<a href="fetch.lua">binary trees benchmark demo</a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This demo is from the computer language benchmark game. It's a
|
||||||
|
torture test for the Lua garbage collector. ProTip: Try loading
|
||||||
|
this page while the terminal memory monitor feature is active, so
|
||||||
|
you can see memory shuffle around in a dual screen display. We
|
||||||
|
use setrlimit to set a 512mb memory limit. So if you specify a
|
||||||
|
number higher than 23 (a good meaty benchmark) then the Lua GC
|
||||||
|
should panic a bit trying to recover (due to malloc failure) and
|
||||||
|
ultimately the worker process should die. However the server and
|
||||||
|
your system will survive.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form action="binarytrees.lua" method="post">
|
||||||
|
<input type="text" id="depth" name="depth" size="70"
|
||||||
|
value="%s" placeholder="%d" onfocus="this.select()"
|
||||||
|
autofocus>
|
||||||
|
<input type="submit" value="run">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
]] % {EscapeHtml(depth), suggestion})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function BinaryTreesDemo()
|
||||||
|
if GetMethod() == 'GET' or GetMethod() == 'HEAD' then
|
||||||
|
WriteForm("18", 18)
|
||||||
|
elseif GetMethod() == 'POST' and not HasParam('depth') then
|
||||||
|
ServeError(400)
|
||||||
|
elseif GetMethod() == 'POST' then
|
||||||
|
|
||||||
|
-- write out the page so user can submit again
|
||||||
|
WriteForm(GetParam('depth'), 18)
|
||||||
|
Write('<dl>\r\n')
|
||||||
|
Write('<dt>Output\r\n')
|
||||||
|
Write('<dd>\r\n')
|
||||||
|
|
||||||
|
-- impose quotas to protect the server
|
||||||
|
unix.setrlimit(unix.RLIMIT_AS, 1024 * 1024 * 1024)
|
||||||
|
unix.setrlimit(unix.RLIMIT_CPU, 2, 4)
|
||||||
|
|
||||||
|
-- when RLIMIT_AS (virtual address space) runs out of memory, then
|
||||||
|
-- mmap() and malloc() start to fail, and lua reacts by trying to
|
||||||
|
-- run the garbage collector over and over again. so we also place
|
||||||
|
-- a limit on the number of "cpu time" seconds too. the behavior
|
||||||
|
-- when we hit the soft limit, is the kernel gives us a friendly
|
||||||
|
-- warning by sending the signal SIGXCPU which we must catch here
|
||||||
|
-- once we catch it, we've got 2 seconds of grace time to clean up
|
||||||
|
-- before we hit the "hard limit" and the kernel sends SIGKILL (9)
|
||||||
|
oldsigxcpufunc,
|
||||||
|
oldsigxcpuflags,
|
||||||
|
oldsigxcpumask =
|
||||||
|
unix.sigaction(unix.SIGXCPU, OnSigxcpu)
|
||||||
|
|
||||||
|
-- get the user-supplied parameter
|
||||||
|
depth = tonumber(GetParam('depth'))
|
||||||
|
|
||||||
|
-- run the benchmark, recording how long it takes
|
||||||
|
secs1, nanos1 = unix.clock_gettime()
|
||||||
|
res = CheckTree(MakeTree(depth))
|
||||||
|
secs2, nanos2 = unix.clock_gettime()
|
||||||
|
|
||||||
|
-- turn 128-bit timestamps into 64-bit nanosecond interval
|
||||||
|
if secs2 == secs1 then
|
||||||
|
nanos = nanos2 - nanos1
|
||||||
|
else
|
||||||
|
nanos = (secs2 - secs1) * 1000000000 + (1000000000 - nanos1) + nanos2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- write out result of benchmark
|
||||||
|
Write('0%o\r\n' % {res})
|
||||||
|
Write('<dt>Time Elapsed\r\n')
|
||||||
|
Write('<dd>\r\n')
|
||||||
|
Write('%g seconds, or<br>\r\n' % {nanos / 1e9})
|
||||||
|
Write('%g milliseconds, or<br>\r\n' % {nanos / 1e6})
|
||||||
|
Write('%g microseconds, or<br>\r\n' % {nanos / 1e6})
|
||||||
|
Write('%d nanoseconds\r\n' % {nanos})
|
||||||
|
Write('<dt>unix.clock_gettime() #1\r\n')
|
||||||
|
Write('<dd>%d, %d\r\n' % {secs1, nanos1})
|
||||||
|
Write('<dt>unix.clock_gettime() #2\r\n')
|
||||||
|
Write('<dd>%d, %d\r\n' % {secs2, nanos2})
|
||||||
|
Write('</dl>\r\n')
|
||||||
|
|
||||||
|
else
|
||||||
|
ServeError(405)
|
||||||
|
SetHeader('Allow', 'GET, HEAD, POST')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function main()
|
||||||
|
-- catch exceptions
|
||||||
|
ok, err = pcall(BinaryTreesDemo)
|
||||||
|
|
||||||
|
-- we don't need to restore the old handler
|
||||||
|
-- but it's generally a good practice to clean up
|
||||||
|
if oldsigxcpufunc then
|
||||||
|
unix.sigaction(unix.SIGXCPU,
|
||||||
|
oldsigxcpufunc,
|
||||||
|
oldsigxcpuflags,
|
||||||
|
oldsigxcpumask)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle exceptions
|
||||||
|
if not ok then
|
||||||
|
|
||||||
|
-- whenever anything, at all, goes wrong, with anything
|
||||||
|
-- always with few exceptions close the connection asap
|
||||||
|
SetHeader('Connection', 'close')
|
||||||
|
|
||||||
|
if err.reason then
|
||||||
|
-- show our error message withoun internal code leaking out
|
||||||
|
Write(err.reason)
|
||||||
|
Write('\r\n')
|
||||||
|
else
|
||||||
|
-- just rethrow exception
|
||||||
|
error('unexpected failure: ' .. tostring(err))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
main()
|
49
tool/net/demo/call-lua-module.lua
Normal file
49
tool/net/demo/call-lua-module.lua
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
-- Call Lua Module Demo
|
||||||
|
--
|
||||||
|
-- Your Lua modules may be stored in the /.lua/ folder.
|
||||||
|
--
|
||||||
|
-- In our /.init.lua global scope earlier, we ran the code:
|
||||||
|
--
|
||||||
|
-- mymodule = require "mymodule"
|
||||||
|
--
|
||||||
|
-- Which preloaded the /.lua/mymodule.lua module once into the server
|
||||||
|
-- global memory template from which all request handlers are forked.
|
||||||
|
-- Therefore, we can just immediately use that module from our Lua
|
||||||
|
-- server pages.
|
||||||
|
|
||||||
|
Write[[<!doctype html>
|
||||||
|
<title>redbean call lua module demo</title>
|
||||||
|
<style>
|
||||||
|
body { padding: 1em; }
|
||||||
|
h1 a { color: inherit; text-decoration: none; }
|
||||||
|
h1 img { border: none; vertical-align: middle; }
|
||||||
|
pre { margin-left: 2em; }
|
||||||
|
p { word-break: break-word; max-width: 650px; }
|
||||||
|
dt { font-weight: bold; }
|
||||||
|
dd { margin-top: 1em; margin-bottom: 1em; }
|
||||||
|
.hdr { text-indent: -1em; padding-left: 1em; }
|
||||||
|
</style>
|
||||||
|
<h1>
|
||||||
|
<a href="/"><img src="/redbean.png"></a>
|
||||||
|
<a href="call-lua-module.lua">call lua module demo</a>
|
||||||
|
</h1>
|
||||||
|
<p>Your Lua modules may be stored in the /.lua/ folder.
|
||||||
|
<p>In our <code>/.init.lua</code> global scope earlier, we ran the code:
|
||||||
|
<pre>mymodule = require "mymodule"</pre>
|
||||||
|
<p>Which preloaded the <code>/.lua/mymodule.lua</code> module once into
|
||||||
|
the server global memory template from which all request handlers are
|
||||||
|
forked. Therefore, we can just immediately use that module from our
|
||||||
|
Lua Server Pages.
|
||||||
|
<p>
|
||||||
|
Your <code>mymodule.hello()</code> output is as follows:
|
||||||
|
<blockquote>
|
||||||
|
]]
|
||||||
|
|
||||||
|
mymodule.hello()
|
||||||
|
|
||||||
|
Write[[
|
||||||
|
</blockquote>
|
||||||
|
<p>
|
||||||
|
<a href="/">go back</a>
|
||||||
|
</p>
|
||||||
|
]]
|
|
@ -1,41 +1,29 @@
|
||||||
-- Fetch() API Demo
|
-- Fetch() API Demo
|
||||||
|
|
||||||
local function WriteForm(url)
|
local function WriteForm(url)
|
||||||
Write('<!doctype html>\r\n')
|
Write([[<!doctype html>
|
||||||
Write([[
|
|
||||||
<title>redbean fetch demo</title>
|
<title>redbean fetch demo</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body { padding: 1em; }
|
||||||
padding: 1em;
|
h1 a { color: inherit; text-decoration: none; }
|
||||||
}
|
h1 img { border: none; vertical-align: middle; }
|
||||||
h1 a {
|
input { margin: 1em; padding: .5em; }
|
||||||
color: inherit;
|
pre { margin-left: 2em; }
|
||||||
text-decoration: none;
|
p { word-break: break-word; max-width: 650px; }
|
||||||
}
|
dt { font-weight: bold; }
|
||||||
h1 img {
|
dd { margin-top: 1em; margin-bottom: 1em; }
|
||||||
border: none;
|
.hdr { text-indent: -1em; padding-left: 1em; }
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
margin: 1em;
|
|
||||||
padding: .5em;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
.hdr {
|
|
||||||
text-indent: -1em;
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<h1>
|
<h1>
|
||||||
<a href="/"><img src="/redbean.png"></a>
|
<a href="/"><img src="/redbean.png"></a>
|
||||||
<a href="fetch.lua">redbean fetch demo</a>
|
<a href="fetch.lua">redbean fetch demo</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
<p>
|
||||||
|
Your redbean is able to function as an HTTP client too.
|
||||||
|
Lua server pages can use the <code>Fetch()</code> API to
|
||||||
|
to send outgoing HTTP and HTTPS requests to other web
|
||||||
|
servers. All it takes is a line of code!
|
||||||
|
</p>
|
||||||
<form action="fetch.lua" method="post">
|
<form action="fetch.lua" method="post">
|
||||||
<input type="text" id="url" name="url" size="70"
|
<input type="text" id="url" name="url" size="70"
|
||||||
value="%s" placeholder="uri" autofocus>
|
value="%s" placeholder="uri" autofocus>
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
if not IsPublicIp(GetClientAddr()) then
|
|
||||||
StoreAsset('/hi', 'sup')
|
|
||||||
end
|
|
||||||
mymodule.hello()
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue