Make improvements

- Add hierarchical auto-completion to redbean's repl
- Fetch latest localtime() and strftime() from Eggert
- Shave a few milliseconds off redbean start latency
- Fix redbean repl with multi-line statements
- Make the Lua unix module code more elegant
- Harden Lua data structure serialization
This commit is contained in:
Justine Tunney 2022-04-27 05:39:39 -07:00
parent d57b81aac7
commit 6a145a9262
44 changed files with 2987 additions and 1941 deletions

View file

@ -140,7 +140,7 @@ o/$(MODE)/examples/nesemu1.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/examples/.nesemu1/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/examples/.nesemu1/.symtab
o/$(MODE)/examples/hello.com.dbg: \

View file

@ -31,34 +31,35 @@
.align 4
.underrun
kOpenFlags:
.e O_RDWR,"RDWR" // order matters
.e O_RDONLY,"RDONLY" //
.e O_WRONLY,"WRONLY" //
.e O_ACCMODE,"ACCMODE" // mask of prev three
.e O_CREAT,"CREAT" //
.e O_EXCL,"EXCL" //
.e O_TRUNC,"TRUNC" //
.e O_CLOEXEC,"CLOEXEC" //
.e O_DIRECT,"DIRECT" // no-op on xnu/openbsd
.e O_APPEND,"APPEND" // weird on nt
.e O_TMPFILE,"TMPFILE" // linux, windows
.e O_NOFOLLOW,"NOFOLLOW" // unix
.e O_SYNC,"SYNC" // unix
.e O_ASYNC,"ASYNC" // unix
.e O_NOCTTY,"NOCTTY" // unix
.e O_NOATIME,"NOATIME" // linux
.e O_EXEC,"EXEC" // free/openbsd
.e O_SEARCH,"SEARCH" // free/netbsd
.e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd
.e O_RSYNC,"RSYNC" // linux/open/netbsd
.e O_PATH,"PATH" // linux
.e O_VERIFY,"VERIFY" // freebsd
.e O_SHLOCK,"SHLOCK" // bsd
.e O_EXLOCK,"EXLOCK" // bsd
.e O_RANDOM,"RANDOM" // windows
.e O_SEQUENTIAL,"SEQUENTIAL" // windows
.e O_COMPRESSED,"COMPRESSED" // windows
.e O_INDEXED,"INDEXED" // windows
.e O_RDWR,"RDWR" // order matters
.e O_RDONLY,"RDONLY" //
.e O_WRONLY,"WRONLY" //
.e O_ACCMODE,"ACCMODE" // mask of prev three
.e O_CREAT,"CREAT" //
.e O_EXCL,"EXCL" //
.e O_TRUNC,"TRUNC" //
.e O_CLOEXEC,"CLOEXEC" //
.e O_NONBLOCK,"NONBLOCK" //
.e O_DIRECT,"DIRECT" // no-op on xnu/openbsd
.e O_APPEND,"APPEND" // weird on nt
.e O_TMPFILE,"TMPFILE" // linux, windows
.e O_NOFOLLOW,"NOFOLLOW" // unix
.e O_SYNC,"SYNC" // unix
.e O_ASYNC,"ASYNC" // unix
.e O_NOCTTY,"NOCTTY" // unix
.e O_NOATIME,"NOATIME" // linux
.e O_EXEC,"EXEC" // free/openbsd
.e O_SEARCH,"SEARCH" // free/netbsd
.e O_DSYNC,"DSYNC" // linux/xnu/open/netbsd
.e O_RSYNC,"RSYNC" // linux/open/netbsd
.e O_PATH,"PATH" // linux
.e O_VERIFY,"VERIFY" // freebsd
.e O_SHLOCK,"SHLOCK" // bsd
.e O_EXLOCK,"EXLOCK" // bsd
.e O_RANDOM,"RANDOM" // windows
.e O_SEQUENTIAL,"SEQUENTIAL" // windows
.e O_COMPRESSED,"COMPRESSED" // windows
.e O_INDEXED,"INDEXED" // windows
.long MAGNUM_TERMINATOR
.endobj kOpenFlags,globl,hidden
.overrun

View file

@ -88,9 +88,9 @@ int __reservefd(int start) {
/**
* Closes non-stdio file descriptors to free dynamic memory.
*/
static void __freefds(void) {
static void FreeFds(void) {
int i;
NTTRACE("__freefds()");
NTTRACE("FreeFds()");
for (i = 3; i < g_fds.n; ++i) {
if (g_fds.p[i].kind) {
close(i);
@ -104,10 +104,10 @@ static void __freefds(void) {
}
}
static textstartup void __freefds_init(void) {
atexit(__freefds);
static textstartup void FreeFdsInit(void) {
atexit(FreeFds);
}
const void *const __freefds_ctor[] initarray = {
__freefds_init,
const void *const FreeFdsCtor[] initarray = {
FreeFdsInit,
};

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
@ -23,6 +24,7 @@
#include "libc/dce.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/asan.internal.h"
#include "libc/log/log.h"
#include "libc/nt/enum/computernameformat.h"
#include "libc/nt/struct/teb.h"
#include "libc/runtime/runtime.h"

View file

@ -48,6 +48,26 @@ typedef __UINT_FAST32_TYPE__ uint_fast32_t;
typedef __INT_FAST64_TYPE__ int_fast64_t;
typedef __UINT_FAST64_TYPE__ uint_fast64_t;
#define TIME_T_MAX __INT64_MAX__
#define UINT_FAST64_MAX __UINT_FAST64_MAX__
#define UINT_FAST8_MAX __UINT_FAST8_MAX__
#define INT_FAST32_MAX __INT_FAST32_MAX__
#define INT_FAST16_MAX __INT_FAST16_MAX__
#define UINT_FAST32_MAX __UINT_FAST32_MAX__
#define INT_FAST8_MAX __INT_FAST8_MAX__
#define INT_FAST64_MAX __INT_FAST64_MAX__
#define UINT_FAST16_MAX __UINT_FAST16_MAX__
#define TIME_T_MIN (-TIME_T_MAX - 1)
#define UINT_FAST64_MIN (-UINT_FAST64_MAX - 1)
#define UINT_FAST8_MIN (-UINT_FAST8_MAX - 1)
#define INT_FAST32_MIN (-INT_FAST32_MAX - 1)
#define INT_FAST16_MIN (-INT_FAST16_MAX - 1)
#define UINT_FAST32_MIN (-UINT_FAST32_MAX - 1)
#define INT_FAST8_MIN (-INT_FAST8_MAX - 1)
#define INT_FAST64_MIN (-INT_FAST64_MAX - 1)
#define UINT_FAST16_MIN (-UINT_FAST16_MAX - 1)
#define atomic_bool _Atomic(_Bool)
#define atomic_bool32 atomic_int_fast32_t
#define atomic_char _Atomic(char)

View file

@ -44,17 +44,17 @@ int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) {
if (size > 1) *buf++ = c;
if (size) *buf = 0;
} else if (!IsWindows() || err == winerr || !winerr) {
ksnprintf(buf, size, "%s:%d:%s", sym, err, msg);
ksnprintf(buf, size, "%s/%d/%s", sym, err, msg);
} else {
if ((n = FormatMessage(
kNtFormatMessageFromSystem | kNtFormatMessageIgnoreInserts, 0,
winerr, MAKELANGID(kNtLangNeutral, kNtSublangDefault), winmsg,
ARRAYLEN(winmsg), 0))) {
while ((n && winmsg[n - 1] <= ' ') || winmsg[n - 1] == '.') --n;
ksnprintf(buf, size, "%s:%d:%s:%d:%.*hs", sym, err, msg, winerr, n,
ksnprintf(buf, size, "%s/%d/%s/%d/%.*hs", sym, err, msg, winerr, n,
winmsg);
} else {
ksnprintf(buf, size, "%s:%d:%s:%d", sym, err, msg, winerr);
ksnprintf(buf, size, "%s/%d/%s/%d", sym, err, msg, winerr);
}
}
return 0;

View file

@ -54,7 +54,7 @@ static ssize_t FindSymtabInZip(struct Zipos *zipos) {
* @note This code can't depend on dlmalloc()
*/
static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) {
ssize_t cf, lf;
ssize_t rc, cf, lf;
size_t size, size2;
struct DeflateState ds;
struct SymbolTable *res = 0;
@ -67,14 +67,16 @@ static struct SymbolTable *GetSymbolTableFromZip(struct Zipos *zipos) {
case kZipCompressionNone:
memcpy(res, (void *)ZIP_LFILE_CONTENT(zipos->map + lf), size);
break;
#if 0
case kZipCompressionDeflate:
if (undeflate(res, size, (void *)ZIP_LFILE_CONTENT(zipos->map + lf),
GetZipLfileCompressedSize(zipos->map + lf),
&ds) == -1) {
rc = undeflate(res, size, (void *)ZIP_LFILE_CONTENT(zipos->map + lf),
GetZipLfileCompressedSize(zipos->map + lf), &ds);
if (rc == -1) {
munmap(res, size2);
res = 0;
}
break;
#endif
default:
munmap(res, size2);
res = 0;
@ -115,11 +117,8 @@ static struct SymbolTable *GetSymbolTableFromElf(void) {
* @return symbol table, or NULL w/ errno on first call
*/
struct SymbolTable *GetSymbolTable(void) {
int ft, st;
struct Zipos *z;
if (!g_symtab && !__isworker) {
ft = g_ftrace, g_ftrace = 0;
st = __strace, __strace = 0;
if (weaken(__zipos_get) && (z = weaken(__zipos_get)())) {
if ((g_symtab = GetSymbolTableFromZip(z))) {
g_symtab->names =
@ -131,8 +130,6 @@ struct SymbolTable *GetSymbolTable(void) {
if (!g_symtab) {
g_symtab = GetSymbolTableFromElf();
}
g_ftrace = ft;
__strace = st;
}
return g_symtab;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/macros.internal.h"
@ -80,4 +81,7 @@ void longsort(long *x, size_t n) {
longsort_pure(x, n, t);
}
}
if (n > 1000) {
STRACE("longsort(%p, %'zu)", x, n);
}
}

View file

@ -53,6 +53,8 @@ noasan static inline const char *strchrnul_sse(const char *s, unsigned char c) {
/**
* Returns pointer to first instance of character.
*
* If c is not found then a pointer to the nul byte is returned.
*
* @param s is a NUL-terminated string
* @param c is masked with 255 as byte to search for
* @return pointer to first instance of c, or pointer to

View file

@ -1110,9 +1110,6 @@ syscon pf PF_VSOCK 40 0 0 0 0 0
syscon pf PF_WANPIPE 25 0 0 0 0 0
syscon pf PF_X25 9 0 0 0 0 0
syscon exit EXIT_SUCCESS 0 0 0 0 0 0 # consensus
syscon exit EXIT_FAILURE 1 1 1 1 1 1 # consensus
# Eric Allman's exit() codes
#
# - Broadly supported style guideline;

View file

@ -1,16 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_
#include "libc/runtime/symbolic.h"
#define EXIT_FAILURE SYMBOLIC(EXIT_FAILURE)
#define EXIT_SUCCESS SYMBOLIC(EXIT_SUCCESS)
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern const long EXIT_FAILURE;
extern const long EXIT_SUCCESS;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_EXIT_H_ */

View file

@ -1,31 +1,114 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
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/fmt/fmt.h"
#include "libc/time/time.h"
#include "libc/time/tz.internal.h"
// clang-format off
/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */
static char g_asctime_buf[64];
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/**
* Converts date time to string.
*
* @return date time string in statically allocated buffer
* @see asctime_r for reentrant version
*/
char *asctime(const struct tm *date) {
return asctime_r(date, g_asctime_buf);
/*
** Avoid the temptation to punt entirely to strftime;
** the output of strftime is supposed to be locale specific
** whereas the output of asctime is supposed to be constant.
*/
/*
** Some systems only handle "%.2d"; others only handle "%02d";
** "%02.2d" makes (most) everybody happy.
** At least some versions of gcc warn about the %02.2d;
** we conditionalize below to avoid the warning.
*/
/*
** All years associated with 32-bit time_t values are exactly four digits long;
** some years associated with 64-bit time_t values are not.
** Vintage programs are coded for years that are always four digits long
** and may assume that the newline always lands in the same place.
** For years that are less than four digits, we pad the output with
** leading zeroes to get the newline in the traditional place.
** The -4 ensures that we get four characters of output even if
** we call a strftime variant that produces fewer characters for some years.
** The ISO C and POSIX standards prohibit padding the year,
** but many implementations pad anyway; most likely the standards are buggy.
*/
#ifdef __GNUC__
#define ASCTIME_FMT "%s %s%3d %2.2d:%2.2d:%2.2d %-4s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT "%s %s%3d %02.2d:%02.2d:%02.2d %-4s\n"
#endif /* !defined __GNUC__ */
/*
** For years that are more than four digits we put extra spaces before the year
** so that code trying to overwrite the newline won't end up overwriting
** a digit within a year and truncating the year (operating on the assumption
** that no output is better than wrong output).
*/
#ifdef __GNUC__
#define ASCTIME_FMT_B "%s %s%3d %2.2d:%2.2d:%2.2d %s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT_B "%s %s%3d %02.2d:%02.2d:%02.2d %s\n"
#endif /* !defined __GNUC__ */
#define STD_ASCTIME_BUF_SIZE 26
/*
** Big enough for something such as
** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
** (two three-character abbreviations, five strings denoting integers,
** seven explicit spaces, two explicit colons, a newline,
** and a trailing NUL byte).
** The values above are for systems where an int is 32 bits and are provided
** as an example; the define below calculates the maximum for the system at
** hand.
*/
#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
static char buf_asctime[MAX_ASCTIME_BUF_SIZE];
char *
asctime_r(register const struct tm *timeptr, char *buf)
{
register const char * wn;
register const char * mn;
char year[INT_STRLEN_MAXIMUM(int) + 2];
char result[MAX_ASCTIME_BUF_SIZE];
if (timeptr == NULL) {
errno = EINVAL;
return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
}
if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
wn = "???";
else wn = kWeekdayNameShort[timeptr->tm_wday];
if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
mn = "???";
else mn = kMonthNameShort[timeptr->tm_mon];
/*
** Use strftime's %Y to generate the year, to avoid overflow problems
** when computing timeptr->tm_year + TM_YEAR_BASE.
** Assume that strftime is unaffected by other out-of-range members
** (e.g., timeptr->tm_mday) when processing "%Y".
*/
strftime(year, sizeof year, "%Y", timeptr);
/*
** We avoid using snprintf since it's not available on all systems.
*/
(sprintf)(result,
((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
year);
if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime)
return strcpy(buf, result);
else {
errno = EOVERFLOW;
return NULL;
}
}
char *
asctime(register const struct tm *timeptr)
{
return asctime_r(timeptr, buf_asctime);
}

View file

@ -1,40 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
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/fmt/fmt.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
static unsigned clip(unsigned index, unsigned count) {
return index < count ? index : 0;
}
/**
* Converts date time to string.
*
* @param buf needs to have 64 bytes
* @return pointer to buf
* @see asctime_r for reentrant version
*/
char *asctime_r(const struct tm *date, char buf[hasatleast 64]) {
(snprintf)(buf, 64, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
kWeekdayNameShort[clip(date->tm_wday, 7)],
kMonthNameShort[clip(date->tm_mon, 12)], date->tm_mday,
date->tm_hour, date->tm_min, date->tm_sec, 1900 + date->tm_year);
return buf;
}

View file

@ -1,21 +1,13 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
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/weirdtypes.h"
#include "libc/time/time.h"
char *ctime(const int64_t *timep) { return asctime(localtime(timep)); }
char *ctime(const time_t *timep) {
/*
** Section 4.12.3.2 of X3.159-1989 requires that
** The ctime function converts the calendar time pointed to by timer
** to local time in the form of a string. It is equivalent to
** asctime(localtime(timer))
*/
struct tm *tmp = localtime(timep);
return tmp ? asctime(tmp) : NULL;
}

View file

@ -1,25 +1,9 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
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/weirdtypes.h"
#include "libc/time/struct/tm.h"
#include "libc/time/time.h"
char *ctime_r(const int64_t *timep, char buf[hasatleast 64]) {
struct tm date[1];
return asctime_r(localtime_r(timep, date), buf);
char *ctime_r(const time_t *timep, char *buf) {
struct tm mytm;
struct tm *tmp = localtime_r(timep, &mytm);
return tmp ? asctime_r(tmp, buf) : NULL;
}

View file

@ -1,21 +1,61 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
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.
/*-*- 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
*/
#include "libc/time/time.h"
#include "libc/time/tz.internal.h"
// clang-format off
/* Return the difference between two timestamps. */
double difftime(int64_t x, int64_t y) { return x - y; }
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/* Return -X as a double. Using this avoids casting to 'double'. */
static inline double
dminus(double x)
{
return -x;
}
double
difftime(time_t time1, time_t time0)
{
/*
** If double is large enough, simply convert and subtract
** (assuming that the larger type has more precision).
*/
if (sizeof(time_t) < sizeof(double)) {
double t1 = time1, t0 = time0;
return t1 - t0;
}
/*
** The difference of two unsigned values can't overflow
** if the minuend is greater than or equal to the subtrahend.
*/
if (!TYPE_SIGNED(time_t))
return time0 <= time1 ? time1 - time0 : dminus(time0 - time1);
/* Use uintmax_t if wide enough. */
if (sizeof(time_t) <= sizeof(uintmax_t)) {
uintmax_t t1 = time1, t0 = time0;
return time0 <= time1 ? t1 - t0 : dminus(t0 - t1);
}
/*
** Handle cases where both time1 and time0 have the same sign
** (meaning that their difference cannot overflow).
*/
if ((time1 < 0) == (time0 < 0))
return time1 - time0;
/*
** The values have opposite signs and uintmax_t is too narrow.
** This suffers from double rounding; attempt to lessen that
** by using long double temporaries.
*/
{
long double t1 = time1, t0 = time0;
return t1 - t0;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
/*-*- 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
/*-*- 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
Copyright (c) 1989 The Regents of the University of California.
All rights reserved.
@ -16,354 +16,529 @@
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/time/struct/tm.h"
#include "libc/inttypes.h"
#include "libc/stdio/stdio.h"
#include "libc/time/time.h"
#include "libc/time/tzfile.internal.h"
#include "libc/time/tz.internal.h"
#include "libc/unicode/locale.h"
// clang-format off
// converts broken-down timestamp to string
#define DIVISOR 100
asm(".ident\t\"\\n\\n\
strftime (BSD-3)\\n\
Copyright 1989 The Regents of the University of California\"");
asm(".include \"libc/disclaimer.inc\"");
static char *strftime_add(char *p, const char *pe, const char *str) {
while (p < pe && (*p = *str++)) ++p;
return p;
/*
** Based on the UCB version with the copyright notice appearing above.
**
** This is ANSIish only when "multibyte character == plain character".
*/
struct lc_time_T {
const char * mon[MONSPERYEAR];
const char * month[MONSPERYEAR];
const char * wday[DAYSPERWEEK];
const char * weekday[DAYSPERWEEK];
const char * X_fmt;
const char * x_fmt;
const char * c_fmt;
const char * am;
const char * pm;
const char * date_fmt;
};
#define Locale (&C_time_locale)
static const struct lc_time_T C_time_locale = {
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
}, {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
}, {
"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"
}, {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
},
/* X_fmt */
"%H:%M:%S",
/*
** x_fmt
** C99 and later require this format.
** Using just numbers (as here) makes Quakers happier;
** it's also compatible with SVR4.
*/
"%m/%d/%y",
/*
** c_fmt
** C99 and later require this format.
** Previously this code used "%D %X", but we now conform to C99.
** Note that
** "%a %b %d %H:%M:%S %Y"
** is used by Solaris 2.3.
*/
"%a %b %e %T %Y",
/* am */
"AM",
/* pm */
"PM",
/* date_fmt */
"%a %b %e %H:%M:%S %Z %Y"
};
enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL };
#ifndef YEAR_2000_NAME
#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
#endif /* !defined YEAR_2000_NAME */
static char *
_add(const char *str, char *pt, const char *ptlim)
{
while (pt < ptlim && (*pt = *str++) != '\0')
++pt;
return pt;
}
static char *strftime_conv(char *p, const char *pe, int n, const char *format) {
char buf[22];
(snprintf)(buf, sizeof(buf), format, n);
return strftime_add(p, pe, buf);
static char *
_conv(int n, const char *format, char *pt, const char *ptlim)
{
char buf[INT_STRLEN_MAXIMUM(int) + 1];
(sprintf)(buf, format, n);
return _add(buf, pt, ptlim);
}
static char *strftime_secs(char *p, const char *pe, const struct tm *t) {
char ibuf[21];
struct tm tmp;
int64_t s;
tmp = *t; /* Make a copy, mktime(3) modifies the tm struct. */
s = mktime(&tmp);
FormatInt64(ibuf, s);
return strftime_add(p, pe, ibuf);
/*
** POSIX and the C Standard are unclear or inconsistent about
** what %C and %y do if the year is negative or exceeds 9999.
** Use the convention that %C concatenated with %y yields the
** same output as %Y, and that %Y contains at least 4 bytes,
** with more only if necessary.
*/
static char *
_yconv(int a, int b, bool convert_top, bool convert_yy,
char *pt, const char *ptlim)
{
register int lead;
register int trail;
trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
trail %= DIVISOR;
if (trail < 0 && lead > 0) {
trail += DIVISOR;
--lead;
} else if (lead < 0 && trail > 0) {
trail -= DIVISOR;
++lead;
}
if (convert_top) {
if (lead == 0 && trail < 0)
pt = _add("-0", pt, ptlim);
else pt = _conv(lead, "%02d", pt, ptlim);
}
if (convert_yy)
pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
return pt;
}
static char *strftime_timefmt(char *p, const char *pe, const char *format,
const struct tm *t) {
int i;
long diff;
char const *sign;
/* size_t z1, z2, z3; */
for (; *format; ++format) {
if (*format == '%') {
label:
switch (*++format) {
case '\0':
--format;
break;
case 'A':
p = strftime_add(p, pe,
(t->tm_wday < 0 || t->tm_wday > 6)
? "?"
: kWeekdayName[t->tm_wday]);
continue;
case 'a':
p = strftime_add(p, pe,
(t->tm_wday < 0 || t->tm_wday > 6)
? "?"
: kWeekdayNameShort[t->tm_wday]);
continue;
case 'B':
p = strftime_add(
p, pe,
(t->tm_mon < 0 || t->tm_mon > 11) ? "?" : kMonthName[t->tm_mon]);
continue;
case 'b':
case 'h':
p = strftime_add(p, pe,
(t->tm_mon < 0 || t->tm_mon > 11)
? "?"
: kMonthNameShort[t->tm_mon]);
continue;
case 'c':
p = strftime_timefmt(p, pe, "%D %X", t);
continue;
case 'C':
/*
** %C used to do a...
** strftime_timefmt("%a %b %e %X %Y", t);
** ...whereas now POSIX 1003.2 calls for
** something completely different.
** (ado, 5/24/93)
*/
p = strftime_conv(p, pe, div100int64(t->tm_year + TM_YEAR_BASE),
"%02d");
continue;
case 'D':
p = strftime_timefmt(p, pe, "%m/%d/%y", t);
continue;
case 'x':
/*
** Version 3.0 of strftime from Arnold Robbins
** (arnold@skeeve.atl.ga.us) does the
** equivalent of...
** strftime_timefmt("%a %b %e %Y");
** ...for %x; since the X3J11 C language
** standard calls for "date, using locale's
** date format," anything goes. Using just
** numbers (as here) makes Quakers happier.
** Word from Paul Eggert (eggert@twinsun.com)
** is that %Y-%m-%d is the ISO standard date
** format, specified in ISO 2014 and later
** ISO 8601:1988, with a summary available in
** pub/doc/ISO/english/ISO8601.ps.Z on
** ftp.uni-erlangen.de.
** (ado, 5/30/93)
*/
p = strftime_timefmt(p, pe, "%m/%d/%y", t);
continue;
case 'd':
p = strftime_conv(p, pe, t->tm_mday, "%02d");
continue;
case 'E':
case 'O':
/*
** POSIX locale extensions, a la
** Arnold Robbins' strftime version 3.0.
** The sequences
** %Ec %EC %Ex %Ey %EY
** %Od %oe %OH %OI %Om %OM
** %OS %Ou %OU %OV %Ow %OW %Oy
** are supposed to provide alternate
** representations.
** (ado, 5/24/93)
*/
goto label;
case 'e':
p = strftime_conv(p, pe, t->tm_mday, "%2d");
continue;
case 'H':
p = strftime_conv(p, pe, t->tm_hour, "%02d");
continue;
case 'I':
p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12,
"%02d");
continue;
case 'j':
p = strftime_conv(p, pe, t->tm_yday + 1, "%03d");
continue;
case 'k':
/*
** This used to be...
** strftime_conv(t->tm_hour % 12 ?
** t->tm_hour % 12 : 12, 2, ' ');
** ...and has been changed to the below to
** match SunOS 4.1.1 and Arnold Robbins'
** strftime version 3.0. That is, "%k" and
** "%l" have been swapped.
** (ado, 5/24/93)
*/
p = strftime_conv(p, pe, t->tm_hour, "%2d");
continue;
static char *
_fmt(const char *format, const struct tm *t, char *pt,
const char *ptlim, enum warn *warnp)
{
for ( ; *format; ++format) {
if (*format == '%') {
label:
switch (*++format) {
case '\0':
--format;
break;
case 'A':
pt = _add((t->tm_wday < 0 ||
t->tm_wday >= DAYSPERWEEK) ?
"?" : Locale->weekday[t->tm_wday],
pt, ptlim);
continue;
case 'a':
pt = _add((t->tm_wday < 0 ||
t->tm_wday >= DAYSPERWEEK) ?
"?" : Locale->wday[t->tm_wday],
pt, ptlim);
continue;
case 'B':
pt = _add((t->tm_mon < 0 ||
t->tm_mon >= MONSPERYEAR) ?
"?" : Locale->month[t->tm_mon],
pt, ptlim);
continue;
case 'b':
case 'h':
pt = _add((t->tm_mon < 0 ||
t->tm_mon >= MONSPERYEAR) ?
"?" : Locale->mon[t->tm_mon],
pt, ptlim);
continue;
case 'C':
/*
** %C used to do a...
** _fmt("%a %b %e %X %Y", t);
** ...whereas now POSIX 1003.2 calls for
** something completely different.
** (ado, 1993-05-24)
*/
pt = _yconv(t->tm_year, TM_YEAR_BASE,
true, false, pt, ptlim);
continue;
case 'c':
{
enum warn warn2 = IN_SOME;
pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
*warnp = warn2;
}
continue;
case 'D':
pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
continue;
case 'd':
pt = _conv(t->tm_mday, "%02d", pt, ptlim);
continue;
case 'E':
case 'O':
/*
** Locale modifiers of C99 and later.
** The sequences
** %Ec %EC %Ex %EX %Ey %EY
** %Od %oe %OH %OI %Om %OM
** %OS %Ou %OU %OV %Ow %OW %Oy
** are supposed to provide alternative
** representations.
*/
goto label;
case 'e':
pt = _conv(t->tm_mday, "%2d", pt, ptlim);
continue;
case 'F':
pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
continue;
case 'H':
pt = _conv(t->tm_hour, "%02d", pt, ptlim);
continue;
case 'I':
pt = _conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
"%02d", pt, ptlim);
continue;
case 'j':
pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
continue;
case 'k':
/*
** This used to be...
** _conv(t->tm_hour % 12 ?
** t->tm_hour % 12 : 12, 2, ' ');
** ...and has been changed to the below to
** match SunOS 4.1.1 and Arnold Robbins'
** strftime version 3.0. That is, "%k" and
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
pt = _conv(t->tm_hour, "%2d", pt, ptlim);
continue;
#ifdef KITCHEN_SINK
case 'K':
/*
** After all this time, still unclaimed!
*/
p = strftime_add(p, pe, "kitchen sink");
continue;
case 'K':
/*
** After all this time, still unclaimed!
*/
pt = _add("kitchen sink", pt, ptlim);
continue;
#endif /* defined KITCHEN_SINK */
case 'l':
/*
** This used to be...
** strftime_conv(t->tm_hour, 2, ' ');
** ...and has been changed to the below to
** match SunOS 4.1.1 and Arnold Robbin's
** strftime version 3.0. That is, "%k" and
** "%l" have been swapped.
** (ado, 5/24/93)
*/
p = strftime_conv(p, pe, (t->tm_hour % 12) ? (t->tm_hour % 12) : 12,
"%2d");
continue;
case 'M':
p = strftime_conv(p, pe, t->tm_min, "%02d");
continue;
case 'm':
p = strftime_conv(p, pe, t->tm_mon + 1, "%02d");
continue;
case 'n':
p = strftime_add(p, pe, "\n");
continue;
case 'p':
p = strftime_add(p, pe, t->tm_hour >= 12 ? "PM" : "AM");
continue;
case 'R':
p = strftime_timefmt(p, pe, "%H:%M", t);
continue;
case 'r':
p = strftime_timefmt(p, pe, "%I:%M:%S %p", t);
continue;
case 'S':
p = strftime_conv(p, pe, t->tm_sec, "%02d");
continue;
case 's':
p = strftime_secs(p, pe, t);
continue;
case 'T':
case 'X':
p = strftime_timefmt(p, pe, "%H:%M:%S", t);
continue;
case 't':
p = strftime_add(p, pe, "\t");
continue;
case 'U':
p = strftime_conv(p, pe, (t->tm_yday + 7 - t->tm_wday) / 7, "%02d");
continue;
case 'u':
/*
** From Arnold Robbins' strftime version 3.0:
** "ISO 8601: Weekday as a decimal number
** [1 (Monday) - 7]"
** (ado, 5/24/93)
*/
p = strftime_conv(p, pe, (t->tm_wday == 0) ? 7 : t->tm_wday, "%d");
continue;
case 'V':
/*
** From Arnold Robbins' strftime version 3.0:
** "the week number of the year (the first
** Monday as the first day of week 1) as a
** decimal number (01-53). The method for
** determining the week number is as specified
** by ISO 8601 (to wit: if the week containing
** January 1 has four or more days in the new
** year, then it is week 1, otherwise it is
** week 53 of the previous year and the next
** week is week 1)."
** (ado, 5/24/93)
*/
/*
** XXX--If January 1 falls on a Friday,
** January 1-3 are part of week 53 of the
** previous year. By analogy, if January
** 1 falls on a Thursday, are December 29-31
** of the PREVIOUS year part of week 1???
** (ado 5/24/93)
**
** You are understood not to expect this.
*/
i = (t->tm_yday + 10 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7;
if (i == 0) {
/*
** What day of the week does
** January 1 fall on?
*/
i = t->tm_wday - (t->tm_yday - 1);
/*
** Fri Jan 1: 53
** Sun Jan 1: 52
** Sat Jan 1: 53 if previous
** year a leap
** year, else 52
*/
if (i == TM_FRIDAY)
i = 53;
else if (i == TM_SUNDAY)
i = 52;
else
i = isleap(t->tm_year + TM_YEAR_BASE) ? 53 : 52;
#ifdef XPG4_1994_04_09
/*
** As of 4/9/94, though,
** XPG4 calls for 53
** unconditionally.
*/
i = 53;
#endif /* defined XPG4_1994_04_09 */
}
p = strftime_conv(p, pe, i, "%02d");
continue;
case 'v':
/*
** From Arnold Robbins' strftime version 3.0:
** "date as dd-bbb-YYYY"
** (ado, 5/24/93)
*/
p = strftime_timefmt(p, pe, "%e-%b-%Y", t);
continue;
case 'W':
p = strftime_conv(
p, pe, (t->tm_yday + 7 - (t->tm_wday ? (t->tm_wday - 1) : 6)) / 7,
"%02d");
continue;
case 'w':
p = strftime_conv(p, pe, t->tm_wday, "%d");
continue;
case 'y':
p = strftime_conv(p, pe, (t->tm_year + TM_YEAR_BASE) % 100, "%02d");
continue;
case 'Y':
p = strftime_conv(p, pe, t->tm_year + TM_YEAR_BASE, "%04d");
continue;
case 'Z':
if (t->tm_zone) {
p = strftime_add(p, pe, t->tm_zone);
} else {
if (t->tm_isdst == 0 || t->tm_isdst == 1) {
p = strftime_add(p, pe, tzname[t->tm_isdst]);
} else {
p = strftime_add(p, pe, "?");
}
}
continue;
case 'z':
if (t->tm_isdst < 0) continue;
#ifdef TM_GMTOFF
diff = t->TM_GMTOFF;
#else /* !defined TM_GMTOFF */
if (t->tm_isdst == 0)
#ifdef USG_COMPAT
diff = -timezone;
#else /* !defined USG_COMPAT */
continue;
#endif /* !defined USG_COMPAT */
else
#ifdef ALTZONE
diff = -altzone;
#else /* !defined ALTZONE */
continue;
#endif /* !defined ALTZONE */
#endif /* !defined TM_GMTOFF */
if (diff < 0) {
sign = "-";
diff = -diff;
} else {
sign = "+";
}
p = strftime_add(p, pe, sign);
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
p = strftime_conv(p, pe, diff, "%04d");
continue;
case '%':
/*
* X311J/88-090 (4.12.3.5): if conversion char is
* undefined, behavior is undefined. Print out the
* character itself as printf(3) also does.
*/
default:
break;
}
}
if (p >= pe) break;
*p++ = *format;
}
return p;
case 'l':
/*
** This used to be...
** _conv(t->tm_hour, 2, ' ');
** ...and has been changed to the below to
** match SunOS 4.1.1 and Arnold Robbin's
** strftime version 3.0. That is, "%k" and
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
pt = _conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
"%2d", pt, ptlim);
continue;
case 'M':
pt = _conv(t->tm_min, "%02d", pt, ptlim);
continue;
case 'm':
pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
continue;
case 'n':
pt = _add("\n", pt, ptlim);
continue;
case 'p':
pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
Locale->pm :
Locale->am,
pt, ptlim);
continue;
case 'R':
pt = _fmt("%H:%M", t, pt, ptlim, warnp);
continue;
case 'r':
pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
continue;
case 'S':
pt = _conv(t->tm_sec, "%02d", pt, ptlim);
continue;
case 's':
{
struct tm tm;
char buf[INT_STRLEN_MAXIMUM(
time_t) + 1];
time_t mkt;
tm = *t;
tm.tm_yday = -1;
mkt = mktime(&tm);
if (mkt == (time_t) -1) {
/* Fail unless this -1 represents
a valid time. */
struct tm tm_1;
if (!localtime_r(&mkt, &tm_1))
return NULL;
if (!(tm.tm_year == tm_1.tm_year
&& tm.tm_yday == tm_1.tm_yday
&& tm.tm_hour == tm_1.tm_hour
&& tm.tm_min == tm_1.tm_min
&& tm.tm_sec == tm_1.tm_sec))
return NULL;
}
if (TYPE_SIGNED(time_t)) {
intmax_t n = mkt;
(sprintf)(buf, "%"PRIdMAX, n);
} else {
uintmax_t n = mkt;
(sprintf)(buf, "%"PRIuMAX, n);
}
pt = _add(buf, pt, ptlim);
}
continue;
case 'T':
pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
continue;
case 't':
pt = _add("\t", pt, ptlim);
continue;
case 'U':
pt = _conv((t->tm_yday + DAYSPERWEEK -
t->tm_wday) / DAYSPERWEEK,
"%02d", pt, ptlim);
continue;
case 'u':
/*
** From Arnold Robbins' strftime version 3.0:
** "ISO 8601: Weekday as a decimal number
** [1 (Monday) - 7]"
** (ado, 1993-05-24)
*/
pt = _conv((t->tm_wday == 0) ?
DAYSPERWEEK : t->tm_wday,
"%d", pt, ptlim);
continue;
case 'V': /* ISO 8601 week number */
case 'G': /* ISO 8601 year (four digits) */
case 'g': /* ISO 8601 year (two digits) */
/*
** From Arnold Robbins' strftime version 3.0: "the week number of the
** year (the first Monday as the first day of week 1) as a decimal number
** (01-53)."
** (ado, 1993-05-24)
**
** From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn:
** "Week 01 of a year is per definition the first week which has the
** Thursday in this year, which is equivalent to the week which contains
** the fourth day of January. In other words, the first week of a new year
** is the week which has the majority of its days in the new year. Week 01
** might also contain days from the previous year and the week before week
** 01 of a year is the last week (52 or 53) of the previous year even if
** it contains days from the new year. A week starts with Monday (day 1)
** and ends with Sunday (day 7). For example, the first week of the year
** 1997 lasts from 1996-12-30 to 1997-01-05..."
** (ado, 1996-01-02)
*/
{
int year;
int base;
int yday;
int wday;
int w;
year = t->tm_year;
base = TM_YEAR_BASE;
yday = t->tm_yday;
wday = t->tm_wday;
for ( ; ; ) {
int len;
int bot;
int top;
len = isleap_sum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
/*
** What yday (-3 ... 3) does
** the ISO year begin on?
*/
bot = ((yday + 11 - wday) %
DAYSPERWEEK) - 3;
/*
** What yday does the NEXT
** ISO year begin on?
*/
top = bot -
(len % DAYSPERWEEK);
if (top < -3)
top += DAYSPERWEEK;
top += len;
if (yday >= top) {
++base;
w = 1;
break;
}
if (yday >= bot) {
w = 1 + ((yday - bot) /
DAYSPERWEEK);
break;
}
--base;
yday += isleap_sum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
}
if (*format == 'V')
pt = _conv(w, "%02d",
pt, ptlim);
else if (*format == 'g') {
*warnp = IN_ALL;
pt = _yconv(year, base,
false, true,
pt, ptlim);
} else pt = _yconv(year, base,
true, true,
pt, ptlim);
}
continue;
case 'v':
/*
** From Arnold Robbins' strftime version 3.0:
** "date as dd-bbb-YYYY"
** (ado, 1993-05-24)
*/
pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
continue;
case 'W':
pt = _conv((t->tm_yday + DAYSPERWEEK -
(t->tm_wday ?
(t->tm_wday - 1) :
(DAYSPERWEEK - 1))) / DAYSPERWEEK,
"%02d", pt, ptlim);
continue;
case 'w':
pt = _conv(t->tm_wday, "%d", pt, ptlim);
continue;
case 'X':
pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
continue;
case 'x':
{
enum warn warn2 = IN_SOME;
pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
*warnp = warn2;
}
continue;
case 'y':
*warnp = IN_ALL;
pt = _yconv(t->tm_year, TM_YEAR_BASE,
false, true,
pt, ptlim);
continue;
case 'Y':
pt = _yconv(t->tm_year, TM_YEAR_BASE,
true, true,
pt, ptlim);
continue;
case 'Z':
pt = _add(t->tm_zone, pt, ptlim);
/*
** C99 and later say that %Z must be
** replaced by the empty string if the
** time zone abbreviation is not
** determinable.
*/
continue;
case 'z':
{
long diff;
char const * sign;
bool negative;
diff = t->tm_gmtoff;
negative = diff < 0;
if (diff == 0) {
negative = t->tm_zone[0] == '-';
}
if (negative) {
sign = "-";
diff = -diff;
} else sign = "+";
pt = _add(sign, pt, ptlim);
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 +
(diff % MINSPERHOUR);
pt = _conv(diff, "%04d", pt, ptlim);
}
continue;
case '+':
pt = _fmt(Locale->date_fmt, t, pt, ptlim,
warnp);
continue;
case '%':
/*
** X311J/88-090 (4.12.3.5): if conversion char is
** undefined, behavior is undefined. Print out the
** character itself as printf(3) also does.
*/
default:
break;
}
}
if (pt == ptlim)
break;
*pt++ = *format;
}
return pt;
}
/**
@ -379,14 +554,32 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format,
*
* @return bytes copied excluding nul, or 0 on error
*/
size_t strftime(char *s, size_t size, const char *f, const struct tm *t) {
char *p;
p = strftime_timefmt(s, s + size, f, t);
if (p < s + size) {
*p = '\0';
return p - s;
} else {
s[size - 1] = '\0';
return 0;
}
size_t
strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
{
char * p;
int saved_errno = errno;
enum warn warn = IN_NONE;
tzset();
p = _fmt(format, t, s, s + maxsize, &warn);
if (!p) {
errno = EOVERFLOW;
return 0;
}
if (p == s + maxsize) {
errno = ERANGE;
return 0;
}
*p = '\0';
errno = saved_errno;
return p - s;
}
size_t
strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
locale_t locale)
{
/* Just call strftime, as only the C locale is supported. */
return strftime(s, maxsize, format, t);
}

View file

@ -67,6 +67,10 @@ o/$(MODE)/libc/time/strftime.o: \
-fdata-sections \
-ffunction-sections
o/$(MODE)/libc/time/localtime.o: \
OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED
o/$(MODE)/libc/time/now.o: \
OVERRIDE_CFLAGS += \
-O3

542
libc/time/tz.internal.h Normal file
View file

@ -0,0 +1,542 @@
#ifndef COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_
#define COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_
#include "libc/calls/weirdtypes.h"
#include "libc/errno.h"
#include "libc/inttypes.h"
#include "libc/limits.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/ok.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/* clang-format off */
/* Private header for tzdb code. */
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/*
** This header is for use ONLY with the time conversion code.
** There is no guarantee that it will remain unchanged,
** or that it will remain at all.
** Do NOT copy it to any system include directory.
** Thank you!
*/
/*
** zdump has been made independent of the rest of the time
** conversion package to increase confidence in the verification it provides.
** You can use zdump to help in verifying other implementations.
** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
*/
#ifndef USE_LTZ
# define USE_LTZ 1
#endif
/* This string was in the Factory zone through version 2016f. */
#define GRANDPARENTED "Local time zone must be set--see zic manual page"
/*
** Defaults for preprocessor symbols.
** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'.
*/
#ifndef HAVE_DECL_ASCTIME_R
#define HAVE_DECL_ASCTIME_R 1
#endif
#if !defined HAVE_GENERIC && defined __has_extension
# if __has_extension(c_generic_selections)
# define HAVE_GENERIC 1
# else
# define HAVE_GENERIC 0
# endif
#endif
/* _Generic is buggy in pre-4.9 GCC. */
#if !defined HAVE_GENERIC && defined __GNUC__
# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
#endif
#ifndef HAVE_GENERIC
# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
#endif
#ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0
#endif /* !defined HAVE_GETTEXT */
#ifndef HAVE_INCOMPATIBLE_CTIME_R
#define HAVE_INCOMPATIBLE_CTIME_R 0
#endif
#ifndef HAVE_LINK
#define HAVE_LINK 1
#endif /* !defined HAVE_LINK */
#ifndef HAVE_MALLOC_ERRNO
#define HAVE_MALLOC_ERRNO 1
#endif
#ifndef HAVE_POSIX_DECLS
#define HAVE_POSIX_DECLS 1
#endif
#ifndef HAVE_STRTOLL
#define HAVE_STRTOLL 1
#endif
#ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */
#if HAVE_INCOMPATIBLE_CTIME_R
#define asctime_r _incompatible_asctime_r
#define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/*
** Nested includes
*/
/* Avoid clashes with NetBSD by renaming NetBSD's declarations.
If defining the 'timezone' variable, avoid a clash with FreeBSD's
'timezone' function by renaming its declaration. */
#define localtime_rz sys_localtime_rz
#define mktime_z sys_mktime_z
#define posix2time_z sys_posix2time_z
#define time2posix_z sys_time2posix_z
#if defined USG_COMPAT && USG_COMPAT == 2
# define timezone sys_timezone
#endif
#define timezone_t sys_timezone_t
#define tzalloc sys_tzalloc
#define tzfree sys_tzfree
#undef localtime_rz
#undef mktime_z
#undef posix2time_z
#undef time2posix_z
#if defined USG_COMPAT && USG_COMPAT == 2
# undef timezone
#endif
#undef timezone_t
#undef tzalloc
#undef tzfree
#if HAVE_GETTEXT
#include <libintl.h>
#endif /* HAVE_GETTEXT */
#ifndef HAVE_STRFTIME_L
# if _POSIX_VERSION < 200809
# define HAVE_STRFTIME_L 0
# else
# define HAVE_STRFTIME_L 1
# endif
#endif
#ifndef USG_COMPAT
# ifndef _XOPEN_VERSION
# define USG_COMPAT 0
# else
# define USG_COMPAT 1
# endif
#endif
#ifndef HAVE_TZNAME
# if _POSIX_VERSION < 198808 && !USG_COMPAT
# define HAVE_TZNAME 0
# else
# define HAVE_TZNAME 1
# endif
#endif
#ifndef ALTZONE
# if defined __sun || defined _M_XENIX
# define ALTZONE 1
# else
# define ALTZONE 0
# endif
#endif
#ifndef R_OK
#define R_OK 4
#endif /* !defined R_OK */
#if 3 <= __GNUC__
# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
#else
# define ATTRIBUTE_FORMAT(spec) /* empty */
#endif
/*
** Workarounds for compilers/systems.
*/
#ifndef EPOCH_LOCAL
# define EPOCH_LOCAL 0
#endif
#ifndef EPOCH_OFFSET
# define EPOCH_OFFSET 0
#endif
/*
** Compile with -Dtime_tz=T to build the tz package with a private
** int64_t type equivalent to T rather than the system-supplied int64_t.
** This debugging feature can test unusual design decisions
** (e.g., int64_t wider than 'long', or unsigned int64_t) even on
** typical platforms.
*/
#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
# define TZ_INT64_T 1
#else
# define TZ_INT64_T 0
#endif
#if defined LOCALTIME_IMPLEMENTATION && TZ_INT64_T
static int64_t sys_time(int64_t *x) { return time(x); }
#endif
#if TZ_INT64_T
typedef time_tz tz_int64_t;
# undef asctime
# define asctime tz_asctime
# undef asctime_r
# define asctime_r tz_asctime_r
# undef ctime
# define ctime tz_ctime
# undef ctime_r
# define ctime_r tz_ctime_r
# undef difftime
# define difftime tz_difftime
# undef gmtime
# define gmtime tz_gmtime
# undef gmtime_r
# define gmtime_r tz_gmtime_r
# undef localtime
# define localtime tz_localtime
# undef localtime_r
# define localtime_r tz_localtime_r
# undef localtime_rz
# define localtime_rz tz_localtime_rz
# undef mktime
# define mktime tz_mktime
# undef mktime_z
# define mktime_z tz_mktime_z
# undef offtime
# define offtime tz_offtime
# undef posix2time
# define posix2time tz_posix2time
# undef posix2time_z
# define posix2time_z tz_posix2time_z
# undef strftime
# define strftime tz_strftime
# undef time
# define time tz_time
# undef time2posix
# define time2posix tz_time2posix
# undef time2posix_z
# define time2posix_z tz_time2posix_z
# undef int64_t
# define int64_t tz_int64_t
# undef timegm
# define timegm tz_timegm
# undef timelocal
# define timelocal tz_timelocal
# undef timeoff
# define timeoff tz_timeoff
# undef tzalloc
# define tzalloc tz_tzalloc
# undef tzfree
# define tzfree tz_tzfree
# undef tzset
# define tzset tz_tzset
# if HAVE_STRFTIME_L
# undef strftime_l
# define strftime_l tz_strftime_l
# endif
# if HAVE_TZNAME
# undef tzname
# define tzname tz_tzname
# endif
# if USG_COMPAT
# undef daylight
# define daylight tz_daylight
# undef timezone
# define timezone tz_timezone
# endif
# if ALTZONE
# undef altzone
# define altzone tz_altzone
# endif
char *asctime(struct tm const *);
char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime(int64_t const *);
char *ctime_r(int64_t const *, char *);
double difftime(int64_t, int64_t) pureconst;
size_t strftime(char *restrict, size_t, char const *restrict,
struct tm const *restrict);
# if HAVE_STRFTIME_L
size_t strftime_l(char *restrict, size_t, char const *restrict,
struct tm const *restrict, locale_t);
# endif
struct tm *gmtime(int64_t const *);
struct tm *gmtime_r(int64_t const *restrict, struct tm *restrict);
struct tm *localtime(int64_t const *);
struct tm *localtime_r(int64_t const *restrict, struct tm *restrict);
int64_t mktime(struct tm *);
int64_t time(int64_t *);
void tzset(void);
#endif
#if !HAVE_DECL_ASCTIME_R && !defined asctime_r
extern char *asctime_r(struct tm const *restrict, char *restrict);
#endif
#ifndef HAVE_DECL_ENVIRON
# if defined environ || defined __USE_GNU
# define HAVE_DECL_ENVIRON 1
# else
# define HAVE_DECL_ENVIRON 0
# endif
#endif
#if 2 <= HAVE_TZNAME + (TZ_INT64_T || !HAVE_POSIX_DECLS)
extern char *tzname[];
#endif
#if 2 <= USG_COMPAT + (TZ_INT64_T || !HAVE_POSIX_DECLS)
extern long timezone;
extern int daylight;
#endif
#if 2 <= ALTZONE + (TZ_INT64_T || !HAVE_POSIX_DECLS)
extern long altzone;
#endif
/*
** The STD_INSPIRED functions are similar, but most also need
** declarations if time_tz is defined.
*/
#ifdef STD_INSPIRED
# if TZ_INT64_T || !defined offtime
struct tm *offtime(int64_t const *, long);
# endif
# if TZ_INT64_T || !defined timegm
int64_t timegm(struct tm *);
# endif
# if TZ_INT64_T || !defined timelocal
int64_t timelocal(struct tm *);
# endif
# if TZ_INT64_T || !defined timeoff
int64_t timeoff(struct tm *, long);
# endif
# if TZ_INT64_T || !defined time2posix
int64_t time2posix(int64_t);
# endif
# if TZ_INT64_T || !defined posix2time
int64_t posix2time(int64_t);
# endif
#endif
/* Infer TM_ZONE on systems where this information is known, but suppress
guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */
#define TM_GMTOFF tm_gmtoff
#define TM_ZONE tm_zone
/*
** Define functions that are ABI compatible with NetBSD but have
** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t
** and labors under the misconception that 'const timezone_t' is a
** pointer to a constant. This use of 'const' is ineffective, so it
** is not done here. What we call 'struct state' NetBSD calls
** 'struct __state', but this is a private name so it doesn't matter.
*/
#if NETBSD_INSPIRED
typedef struct state *timezone_t;
struct tm *localtime_rz(timezone_t restrict, int64_t const *restrict,
struct tm *restrict);
int64_t mktime_z(timezone_t restrict, struct tm *restrict);
timezone_t tzalloc(char const *);
void tzfree(timezone_t);
# ifdef STD_INSPIRED
# if TZ_INT64_T || !defined posix2time_z
int64_t posix2time_z(timezone_t, int64_t) nosideeffect;
# endif
# if TZ_INT64_T || !defined time2posix_z
int64_t time2posix_z(timezone_t, int64_t) nosideeffect;
# endif
# endif
#endif
/*
** 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)
/* Max and min values of the integer type T, of which only the bottom
B bits are used, and where the highest-order used bit is considered
to be a sign bit if T is signed. */
#define MAXVAL(t, b) \
((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
- 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
#define MINVAL(t, b) \
((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
/* The extreme time values, assuming no padding. */
#define INT64_T_MIN_NO_PADDING MINVAL(int64_t, TYPE_BIT(int64_t))
#define INT64_T_MAX_NO_PADDING MAXVAL(int64_t, TYPE_BIT(int64_t))
/* The extreme time values. These are macros, not constants, so that
any portability problems occur only when compiling .c files that use
the macros, which is safer for applications that need only zdump and zic.
This implementation assumes no padding if int64_t is signed and
either the compiler lacks support for _Generic or int64_t is not one
of the standard signed integer types. */
#if HAVE_GENERIC
# define INT64_T_MIN \
_Generic((int64_t) 0, \
signed char: SCHAR_MIN, short: SHRT_MIN, \
int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \
default: INT64_T_MIN_NO_PADDING)
# define INT64_T_MAX \
(TYPE_SIGNED(int64_t) \
? _Generic((int64_t) 0, \
signed char: SCHAR_MAX, short: SHRT_MAX, \
int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
default: INT64_T_MAX_NO_PADDING) \
: (int64_t) -1)
#else
# define INT64_T_MIN INT64_T_MIN_NO_PADDING
# define INT64_T_MAX INT64_T_MAX_NO_PADDING
#endif
/*
** 302 / 1000 is log10(2.0) rounded up.
** Subtract one for the sign bit if the type is signed;
** add one for integer division truncation;
** add one more for a minus sign if the type is signed.
*/
#define INT_STRLEN_MAXIMUM(type) \
((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
1 + TYPE_SIGNED(type))
/*
** INITIALIZE(x)
*/
#define INITIALIZE(x) ((x) = 0)
/* Whether memory access must strictly follow the C standard.
If 0, it's OK to read uninitialized storage so long as the value is
not relied upon. Defining it to 0 lets mktime access parts of
struct tm that might be uninitialized, as a heuristic when the
standard doesn't say what to return and when tm_gmtoff can help
mktime likely infer a better value. */
#ifndef UNINIT_TRAP
# define UNINIT_TRAP 0
#endif
#ifdef DEBUG
# define UNREACHABLE() abort()
#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
# define UNREACHABLE() __builtin_unreachable()
#elif defined __has_builtin
# if __has_builtin(__builtin_unreachable)
# define UNREACHABLE() __builtin_unreachable()
# endif
#endif
#ifndef UNREACHABLE
# define UNREACHABLE() ((void) 0)
#endif
/*
** For the benefit of GNU folk...
** '_(MSGID)' uses the current locale's message library string for MSGID.
** The default is to use gettext if available, and use MSGID otherwise.
*/
#if HAVE_GETTEXT
#define _(msgid) gettext(msgid)
#else /* !HAVE_GETTEXT */
#define _(msgid) msgid
#endif /* !HAVE_GETTEXT */
#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
# define TZ_DOMAIN "tz"
#endif
#if HAVE_INCOMPATIBLE_CTIME_R
#undef asctime_r
#undef ctime_r
char *asctime_r(struct tm const *, char *);
char *ctime_r(int64_t const *, char *);
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Handy macros that are independent of tzfile implementation. */
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
#define DAYSPERWEEK 7
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_JANUARY 0
#define TM_FEBRUARY 1
#define TM_MARCH 2
#define TM_APRIL 3
#define TM_MAY 4
#define TM_JUNE 5
#define TM_JULY 6
#define TM_AUGUST 7
#define TM_SEPTEMBER 8
#define TM_OCTOBER 9
#define TM_NOVEMBER 10
#define TM_DECEMBER 11
#define TM_YEAR_BASE 1900
#define TM_WDAY_BASE TM_MONDAY
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
** Since everything in isleap is modulo 400 (or a factor of 400), we know that
** isleap(y) == isleap(y % 400)
** and so
** isleap(a + b) == isleap((a + b) % 400)
** or
** isleap(a + b) == isleap(a % 400 + b % 400)
** This is true even if % means modulo rather than Fortran remainder
** (which is allowed by C89 but not by C99 or later).
** We use this to avoid addition overflow problems.
*/
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_THIRD_PARTY_TZ_PRIVATE_H_ */

View file

@ -1,46 +1,56 @@
#ifndef TZFILE_H
#define TZFILE_H
#define TM_ZONE tm_zone
#define TM_GMTOFF tm_gmtoff
/* clang-format off */
/* Layout and location of TZif files. */
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/*
** This header is for use ONLY with the time conversion code.
** There is no guarantee that it will remain unchanged,
** or that it will remain at all.
** Do NOT copy it to any system include directory.
** Thank you!
*/
/*
** Information about time zone files.
*/
#ifndef TZDIR
#define TZDIR "/zip/usr/share/zoneinfo"
#endif
#define TZDIR "/zip/usr/share/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
#define TZDEFAULT "GST"
#endif
#define TZDEFAULT "GST"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
#define TZDEFRULES "New_York"
#endif
#define TZDEFRULES "New_York"
#endif /* !defined TZDEFRULES */
/* See Internet RFC 8536 for more details about the following format. */
/*
** Each file begins with. . .
*/
#define TZ_MAGIC "TZif"
#define TZ_MAGIC "TZif"
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
char tzh_reserved[15]; /* reserved; must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */
char tzh_timecnt[4]; /* coded number of transition times */
char tzh_typecnt[4]; /* coded number of local time types */
char tzh_charcnt[4]; /* coded number of abbr. chars */
char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */
char tzh_reserved[15]; /* reserved; must be zero */
char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */
char tzh_timecnt[4]; /* coded number of transition times */
char tzh_typecnt[4]; /* coded number of local time types */
char tzh_charcnt[4]; /* coded number of abbr. chars */
};
/*
@ -58,14 +68,15 @@ struct tzhead {
** one (char [4]) total correction after above
** tzh_ttisstdcnt (char)s indexed by type; if 1, transition
** time is standard time, if 0,
** transition time is wall clock time
** if absent, transition times are
** assumed to be wall clock time
** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition
** time is UT, if 0,
** transition time is local time
** if absent, transition times are
** transition time is local (wall clock)
** time; if absent, transition times are
** assumed to be local time
** tzh_ttisutcnt (char)s indexed by type; if 1, transition
** time is UT, if 0, transition time is
** local time; if absent, transition
** times are assumed to be local time.
** When this is 1, the corresponding
** std/wall indicator must also be 1.
*/
/*
@ -91,73 +102,21 @@ struct tzhead {
*/
#ifndef TZ_MAX_TIMES
#define TZ_MAX_TIMES 1200
#define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
#define DAYSPERWEEK 7
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_JANUARY 0
#define TM_FEBRUARY 1
#define TM_MARCH 2
#define TM_APRIL 3
#define TM_MAY 4
#define TM_JUNE 5
#define TM_JULY 6
#define TM_AUGUST 7
#define TM_SEPTEMBER 8
#define TM_OCTOBER 9
#define TM_NOVEMBER 10
#define TM_DECEMBER 11
#define TM_YEAR_BASE 1900
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
** Since everything in isleap is modulo 400 (or a factor of 400), we know that
** isleap(y) == isleap(y % 400)
** and so
** isleap(a + b) == isleap((a + b) % 400)
** or
** isleap(a + b) == isleap(a % 400 + b % 400)
** This is true even if % means modulo rather than Fortran remainder
** (which is allowed by C89 but not C99).
** We use this to avoid addition overflow problems.
*/
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
#endif /* !defined TZFILE_H */

View file

@ -116,7 +116,7 @@ o/$(MODE)/third_party/chibicc/chibicc.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/third_party/chibicc/.chibicc/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/third_party/chibicc/.chibicc/.symtab
o/$(MODE)/third_party/chibicc/as.com.dbg: \

View file

@ -339,7 +339,7 @@ static int notwseparator(wint_t c) {
}
static int iswname(wint_t c) {
return !iswseparator(c) || c == '_' || c == '-' || c == '.';
return !iswseparator(c) || c == '_' || c == '-' || c == '.' || c == ':';
}
static int notwname(wint_t c) {
@ -1956,23 +1956,22 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
// handle tab and tab-tab completion
if (seq[0] == '\t' && completionCallback) {
size_t i, j, k, n, m, itemlen;
size_t i, n, m;
// we know that the user pressed tab once
rc = 0;
linenoiseFreeCompletions(&l->lc);
i = Backwards(l, l->pos, iswname);
j = l->pos;
{
char *s = strndup(l->buf + i, j - i);
char *s = strndup(l->buf + i, l->pos - i);
completionCallback(s, &l->lc);
free(s);
}
m = GetCommonPrefixLength(&l->lc);
if (m > j - i || (m == j - i && l->lc.len == 1)) {
if (m > l->pos - i || (m == l->pos - i && l->lc.len == 1)) {
// on common prefix (or single completion) we complete and return
n = i + m + (l->len - j);
n = i + m + (l->len - l->pos);
if (linenoiseGrow(l, n + 1)) {
memmove(l->buf + i + m, l->buf + i + j, l->len - j + 1);
memmove(l->buf + i + m, l->buf + l->pos, l->len - l->pos + 1);
memcpy(l->buf + i, l->lc.cvec[0], m);
l->pos = i + m;
l->len = n;
@ -1994,7 +1993,7 @@ ssize_t linenoiseEdit(struct linenoiseState *l, const char *prompt, char **obuf,
if (rc == 1 && seq[0] == '\t') {
const char **p;
struct abuf ab;
int i, x, y, xn, yn, xy;
int i, k, x, y, xn, yn, xy, itemlen;
itemlen = linenoiseMaxCompletionWidth(&l->lc) + 4;
xn = MAX(1, (l->ws.ws_col - 1) / itemlen);
yn = (l->lc.len + (xn - 1)) / xn;

View file

@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_
char *LuaFormatStack(lua_State *) dontdiscard;
int LuaCallWithTrace(lua_State *, int, int, lua_State *);
int LuaEncodeJsonData(lua_State *, char **, int, char *, int);
int LuaEncodeLuaData(lua_State *, char **, int, char *, int);
int LuaEncodeJsonData(lua_State *, char **, char *, int);
int LuaEncodeLuaData(lua_State *, char **, char *, int);
int LuaEncodeUrl(lua_State *);
int LuaParseUrl(lua_State *);
int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int);

View file

@ -27,6 +27,7 @@
*/
#define ldo_c
#define LUA_CORE
#include "libc/log/log.h"
#include "libc/runtime/gc.h"
#include "third_party/lua/lapi.h"
#include "third_party/lua/ldebug.h"
@ -148,7 +149,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
lua_unlock(L);
g->panic(L); /* call panic function (last chance to jump out) */
}
abort();
__die();
}
}
}

View file

@ -29,15 +29,18 @@
#include "libc/alg/alg.h"
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/log/check.h"
#include "libc/macros.internal.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
#include "libc/x/x.h"
#include "third_party/linenoise/linenoise.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lprefix.h"
#include "third_party/lua/lrepl.h"
@ -87,37 +90,74 @@ static const char *g_historypath;
#endif
static void lua_readline_addcompletion (linenoiseCompletions *c, const char *s) {
char **p, *t;
static void lua_readline_addcompletion (linenoiseCompletions *c, char *s) {
char **p;
if ((p = realloc(c->cvec, (c->len + 1) * sizeof(*p)))) {
c->cvec = p;
if ((t = strdup(s))) {
c->cvec[c->len++] = t;
}
c->cvec[c->len++] = s;
}
}
void lua_readline_completions (const char *p, linenoiseCompletions *c) {
int i;
bool found;
lua_State *L;
const char *name;
for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) {
if (startswithi(kKeywordHints[i], p)) {
lua_readline_addcompletion(c, kKeywordHints[i]);
}
}
char *a, *b, *component;
// start searching globals
L = globalL;
lua_pushglobaltable(L);
// traverse parent objects
// split foo.bar and foo:bar
a = p;
b = strpbrk(a, ".:");
while (b) {
component = strndup(a, b - a);
found = false;
lua_pushnil(L); // search key
while (lua_next(L, -2)) {
if (lua_type(L, -2) == LUA_TSTRING) {
name = lua_tostring(L, -2);
if (!strcmp(name, component)) {
lua_remove(L, -3); // remove table
lua_remove(L, -2); // remove key
found = true;
break;
}
}
lua_pop(L, 1); // pop value
}
free(component);
if (!found) {
lua_pop(L, 1); // pop table
return;
}
a = b + 1;
b = strpbrk(a, ".:");
}
// search final object
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
name = lua_tostring(L, -2);
if (startswithi(name, p)) {
lua_readline_addcompletion(c, name);
while (lua_next(L, -2)) {
if (lua_type(L, -2) == LUA_TSTRING) {
name = lua_tostring(L, -2);
if (startswithi(name, a)) {
lua_readline_addcompletion(c, xasprintf("%.*s%s", a - p, p, name));
}
}
lua_pop(L, 1);
}
lua_pop(L, 1);
for (i = 0; i < ARRAYLEN(kKeywordHints); ++i) {
if (startswithi(kKeywordHints[i], p)) {
lua_readline_addcompletion(c, xstrdup(kKeywordHints[i]));
}
}
if (lua_repl_completions_callback) {
lua_repl_completions_callback(p, c);
}
@ -191,7 +231,7 @@ static ssize_t pushline (lua_State *L, int firstline) {
prmt = strdup(get_prompt(L, firstline));
lua_pop(L, 1); /* remove prompt */
LUA_REPL_UNLOCK;
rc = linenoiseEdit(lua_repl_linenoise, 0, &b, !firstline || lua_repl_blocking);
rc = linenoiseEdit(lua_repl_linenoise, prmt, &b, !firstline || lua_repl_blocking);
free(prmt);
if (rc != -1) {
if (b && *b) {
@ -207,6 +247,9 @@ static ssize_t pushline (lua_State *L, int firstline) {
LUA_REPL_LOCK;
rc = b ? 1 : -1;
}
if (!(rc == -1 && errno == EAGAIN)) {
write(1, "\n", 1);
}
if (rc == -1 || (!rc && !b)) {
return rc;
}

View file

@ -1724,7 +1724,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
lua_error(L1);
}
else if EQ("abort") {
abort();
__die();
}
else if EQ("throw") {
#if defined(__cplusplus)

View file

@ -324,7 +324,6 @@ static void doREPL (lua_State *L) {
progname = oldprogname;
return;
}
lua_writeline();
if (status == LUA_OK)
status = lua_runchunk(L, 0, LUA_MULTRET);
if (status == LUA_OK) {
@ -335,7 +334,6 @@ static void doREPL (lua_State *L) {
}
lua_freerepl();
lua_settop(L, 0); /* clear stack */
lua_writeline();
progname = oldprogname;
}

View file

@ -77,7 +77,7 @@ o/$(MODE)/third_party/lua/lua.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/third_party/lua/.lua/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/third_party/lua/.lua/.symtab
o//third_party/lua/lgc.o: \

View file

@ -25,8 +25,8 @@
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h"
int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat,
int idx) {
static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
char *numformat, int idx) {
char *s;
bool isarray;
size_t tbllen, z;
@ -93,7 +93,7 @@ int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat,
for (size_t i = 1; i <= tbllen; i++) {
if (i > 1) appendw(buf, ',');
lua_rawgeti(L, -1, i); // table/-2, value/-1
LuaEncodeJsonData(L, buf, level - 1, numformat, -1);
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1);
lua_pop(L, 1);
}
} else {
@ -121,7 +121,7 @@ int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat,
lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1
}
appendw(buf, '"' | ':' << 010);
LuaEncodeJsonData(L, buf, level - 1, numformat, -1);
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1);
lua_pop(L, 1); // table/-2, key/-1
}
// stack: table/-1, as the key was popped by lua_next
@ -137,3 +137,9 @@ int LuaEncodeJsonData(lua_State *L, char **buf, int level, char *numformat,
unreachable;
}
}
int LuaEncodeJsonData(lua_State *L, char **buf, char *numformat, int idx) {
int rc;
rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx);
return rc;
}

View file

@ -16,42 +16,98 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/fmt/itoa.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/stdio/append.internal.h"
#include "libc/x/x.h"
#include "third_party/lua/cosmo.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h"
int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat,
int idx) {
struct Visited {
int n;
void **p;
};
static bool PushVisit(struct Visited *visited, void *p) {
int i;
for (i = 0; i < visited->n; ++i) {
if (visited->p[i] == p) {
return false;
}
}
visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p));
visited->p[visited->n - 1] = p;
return true;
}
static void PopVisit(struct Visited *visited) {
assert(visited->n > 0);
--visited->n;
}
static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
char *numformat, int idx,
struct Visited *visited) {
char *s;
int ktype;
bool didcomma;
lua_Integer i;
int ktype, vtype;
size_t tbllen, buflen, slen;
char ibuf[21], fmt[] = "%.14g";
if (level > 0) {
switch (lua_type(L, idx)) {
case LUA_TNIL:
appendw(buf, READ32LE("nil"));
return 0;
case LUA_TSTRING:
s = lua_tolstring(L, idx, &slen);
EscapeLuaString(s, slen, buf);
return 0;
case LUA_TFUNCTION:
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TUSERDATA:
appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TLIGHTUSERDATA:
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TTHREAD:
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TUSERDATA:
if (luaL_callmeta(L, idx, "__repr")) {
if (lua_type(L, -1) == LUA_TSTRING) {
s = lua_tolstring(L, -1, &slen);
appendd(buf, s, slen);
} else {
appendf(buf, "[[error %s returned a %s value]]", "__repr",
luaL_typename(L, -1));
}
lua_pop(L, 1);
return 0;
}
if (luaL_callmeta(L, idx, "__tostring")) {
if (lua_type(L, -1) == LUA_TSTRING) {
s = lua_tolstring(L, -1, &slen);
EscapeLuaString(s, slen, buf);
} else {
appendf(buf, "[[error %s returned a %s value]]", "__tostring",
luaL_typename(L, -1));
}
lua_pop(L, 1);
return 0;
}
appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
return 0;
case LUA_TNUMBER:
if (lua_isinteger(L, idx)) {
appendd(buf, ibuf,
@ -69,11 +125,13 @@ int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat,
fmt[4] = *numformat;
break;
default:
return luaL_error(L, "numformat string not allowed");
luaL_error(L, "numformat string not allowed");
unreachable;
}
appendf(buf, fmt, lua_tonumber(L, idx));
}
return 0;
case LUA_TBOOLEAN:
if (lua_toboolean(L, idx)) {
appendw(buf, READ32LE("true"));
@ -81,26 +139,47 @@ int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat,
appendw(buf, READ64LE("false\0\0"));
}
return 0;
case LUA_TTABLE:
i = 0;
didcomma = false;
appendw(buf, '{');
lua_pushvalue(L, idx);
lua_pushnil(L); // push the first key
while (lua_next(L, -2) != 0) {
while (lua_next(L, -2)) {
++i;
ktype = lua_type(L, -2);
if (i++ > 0) appendw(buf, ',');
vtype = lua_type(L, -1);
if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) {
appendw(buf, '[');
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
LuaEncodeLuaData(L, buf, level - 1, numformat, -1);
lua_remove(L, -1); // remove copied key: table/-3, key/-2, value/-1
appendw(buf, ']' | '=' << 010);
if (PushVisit(visited, lua_touserdata(L, -2))) {
if (i > 1) appendw(buf, ',' | ' ' << 8);
didcomma = true;
appendw(buf, '[');
LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -2, visited);
appendw(buf, ']' | '=' << 010);
PopVisit(visited);
} else {
// TODO: Strange Lua data structure, do nothing.
lua_pop(L, 1);
continue;
}
}
if (PushVisit(visited, lua_touserdata(L, -1))) {
if (!didcomma && i > 1) appendw(buf, ',' | ' ' << 8);
LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -1, visited);
PopVisit(visited);
} else {
// TODO: Strange Lua data structure, do nothing.
lua_pop(L, 1);
continue;
}
LuaEncodeLuaData(L, buf, level - 1, numformat, -1);
lua_pop(L, 1); // table/-2, key/-1
}
lua_pop(L, 1); // table
// stack: table/-1, as the key was popped by lua_next
appendw(buf, '}');
return 0;
default:
luaL_error(L, "can't serialize value of this type");
unreachable;
@ -110,3 +189,12 @@ int LuaEncodeLuaData(lua_State *L, char **buf, int level, char *numformat,
unreachable;
}
}
int LuaEncodeLuaData(lua_State *L, char **buf, char *numformat, int idx) {
int rc;
struct Visited visited = {0};
rc = LuaEncodeLuaDataImpl(L, buf, 64, numformat, idx, &visited);
assert(!visited.n);
free(visited.p);
return rc;
}

View file

@ -28,7 +28,7 @@ dontdiscard char *LuaFormatStack(lua_State *L) {
for (i = 1; i <= top; i++) {
if (i > 1) appendw(&b, '\n');
appendf(&b, "\t%d\t%s\t", i, luaL_typename(L, i));
LuaEncodeLuaData(L, &b, 64, "g", -1);
LuaEncodeLuaData(L, &b, "g", i);
}
return b;
}

View file

@ -131,7 +131,7 @@ o/$(MODE)/third_party/make/make.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/third_party/make/.make/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/third_party/make/.make/.symtab
$(THIRD_PARTY_MAKE_OBJS): \

View file

@ -4197,7 +4197,7 @@ o/$(MODE)/third_party/python/python.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/third_party/python/.python/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/third_party/python/.python/.symtab
################################################################################

View file

@ -154,7 +154,7 @@ o/$(MODE)/third_party/quickjs/qjs.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/third_party/quickjs/.qjs/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/third_party/quickjs/.qjs/.symtab
o/$(MODE)/third_party/quickjs/qjsc.com.dbg: \

View file

@ -83,7 +83,7 @@ o/$(MODE)/third_party/sqlite3/sqlite3.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/third_party/sqlite3/.sqlite3/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/third_party/sqlite3/.sqlite3/.symtab
$(THIRD_PARTY_SQLITE3_A): \

View file

@ -97,7 +97,7 @@ o/$(MODE)/tool/build/blinkenlights.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/build/.blinkenlights/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/build/.blinkenlights/.symtab
o/$(MODE)/tool/build/ar.com.dbg: \

View file

@ -585,7 +585,6 @@ FUNCTIONS
ignored if used outside of request handling code.
- numformat: sets numeric format to be used, which can be 'g',
'f', or 'a' [experimental api]
- maxdepth: (number=64) sets the max number of nested tables.
EncodeLua(value[,options:table]) → json:str
Turns passed Lua value into a Lua string. The following options
@ -593,7 +592,6 @@ FUNCTIONS
- useoutput: (bool=false) encodes the result directly to the
output buffer and returns `nil` value. This option is
ignored if used outside of request handling code.
- maxdepth: (number=64) sets the max number of nested tables.
EncodeLatin1(utf-8:str[,flags:int]) → iso-8859-1:str
Turns UTF-8 into ISO-8859-1 string.
@ -1421,11 +1419,12 @@ UNIX MODULE
The following values may also be OR'd into `flags`:
- `O_CREAT`: create file if it doesn't exist
- `O_CREAT` create file if it doesn't exist
- `O_TRUNC` automatic ftruncate(fd,0) if exists
- `O_CLOEXEC`: automatic close() upon execve()
- `O_EXCL`: exclusive access (see below)
- `O_APPEND`: open file for append only
- `O_CLOEXEC` automatic close() upon execve()
- `O_EXCL` exclusive access (see below)
- `O_APPEND` open file for append only
- `O_NONBLOCK` asks read/write to fail with EAGAIN rather than block
- `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
- `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
- `O_TMPFILE` try to make temp more secure (Linux and Windows only)
@ -1615,7 +1614,7 @@ UNIX MODULE
- `O_CLOEXEC`: Automatically close file descriptor upon execve()
- `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking.
- `O_NONBLOCK`: Request `EAGAIN` be raised rather than blocking
- `O_DIRECT`: Enable packet mode w/ atomic reads and writes, so long
as they're no larger than `PIPE_BUF` (guaranteed to be 512+ bytes)
@ -2634,6 +2633,52 @@ UNIX MODULE
Returns information about resource limit.
unix.gmtime(unixts:int)
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
└─→ nil,unix.Errno
Breaks down UNIX timestamp into Zulu Time numbers.
- `mon` 1 ≤ mon ≤ 12
- `mday` 1 ≤ mday ≤ 31
- `hour` 0 ≤ hour ≤ 23
- `min` 0 ≤ min ≤ 59
- `sec` 0 ≤ sec ≤ 60
- `gmtoff` ±93600 seconds
- `wday` 0 ≤ wday ≤ 6
- `yday` 0 ≤ yday ≤ 365
- `dst` 1 if daylight savings, 0 if not, -1 if not unknown
unix.localtime(unixts:int)
├─→ year,mon,mday,hour,min,sec,gmtoffsec,wday,yday,dst:int,zone:str
└─→ nil,unix.Errno
Breaks down UNIX timestamp into local time numbers.
This follows the same API as gmtime() which has further details.
Your redbean ships with a subset of the time zone database.
- `/zip/usr/share/zoneinfo/Honolulu`
- `/zip/usr/share/zoneinfo/Anchorage`
- `/zip/usr/share/zoneinfo/GST`
- `/zip/usr/share/zoneinfo/Boulder`
- `/zip/usr/share/zoneinfo/Chicago`
- `/zip/usr/share/zoneinfo/New_York`
- `/zip/usr/share/zoneinfo/UTC`
- `/zip/usr/share/zoneinfo/London`
- `/zip/usr/share/zoneinfo/Berlin`
- `/zip/usr/share/zoneinfo/Israel`
- `/zip/usr/share/zoneinfo/Beijing`
- `/zip/usr/share/zoneinfo/Japan`
- `/zip/usr/share/zoneinfo/Sydney`
You can control which timezone is used using the `TZ` environment
variable. If your time zone isn't included in the above list, you
can simply copy it inside your redbean. The same is also the case
for future updates to the database, which can be swapped out when
needed, without having to recompile.
unix.stat(path:str[, flags:int[, dirfd:int]])
├─→ unix.Stat
└─→ nil, unix.Errno
@ -2847,7 +2892,7 @@ UNIX MODULE
actually consumes. For example, for small file systems, your system
might report this number as being 8, which means 4096 bytes.
On Windows NT, if compression is enabled for a file, then this
On Windows NT, if `O_COMPRESSED` is used for a file, then this
number will reflect the size *after* compression. you can use:
st = assert(unix.stat("moby.txt"))

View file

@ -98,15 +98,9 @@ struct UnixDir {
DIR *dir;
};
struct UnixStat {
int refs;
struct stat st;
};
struct UnixErrno {
int refs;
uint16_t errno;
uint16_t winerr;
int errno;
int winerr;
const char *call;
};
@ -137,12 +131,17 @@ static void *LuaUnixAlloc(lua_State *L, size_t n) {
}
static void LuaPushSigset(lua_State *L, struct sigset set) {
struct sigset *sp;
sp = lua_newuserdatauv(L, sizeof(*sp), 1);
struct sigset *sp = lua_newuserdatauv(L, sizeof(*sp), 1);
luaL_setmetatable(L, "unix.Sigset");
*sp = set;
}
static void LuaPushStat(lua_State *L, struct stat *st) {
struct stat *stp = lua_newuserdatauv(L, sizeof(*stp), 1);
luaL_setmetatable(L, "unix.Stat");
*stp = *st;
}
static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, k);
@ -163,19 +162,8 @@ static dontinline int ReturnString(lua_State *L, const char *x) {
return 1;
}
static void LuaUnixPushErrno(lua_State *L, const char *sc, int uerr, int werr) {
struct UnixErrno *ue, **uep;
ue = LuaUnixAlloc(L, sizeof(*ue));
ue->refs = 1;
ue->call = sc;
ue->errno = uerr;
ue->winerr = werr;
uep = lua_newuserdatauv(L, sizeof(*uep), 1);
luaL_setmetatable(L, "unix.Errno");
*uep = ue;
}
static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) {
static int SysretErrno(lua_State *L, const char *call, int olderr) {
struct UnixErrno *ep;
int i, unixerr, winerr;
unixerr = errno;
winerr = GetLastError();
@ -183,7 +171,11 @@ static dontinline int SysretErrno(lua_State *L, const char *call, int olderr) {
WARNF("errno should not be %d", unixerr);
}
lua_pushnil(L);
LuaUnixPushErrno(L, call, unixerr, winerr);
ep = lua_newuserdatauv(L, sizeof(*ep), 1);
luaL_setmetatable(L, "unix.Errno");
ep->errno = unixerr;
ep->winerr = winerr;
ep->call = call;
errno = olderr;
return 2;
}
@ -936,49 +928,33 @@ static int LuaUnixWrite(lua_State *L) {
return SysretInteger(L, "write", olderr, rc);
}
static int ReturnStat(lua_State *L, struct UnixStat *ust) {
struct UnixStat **ustp;
ust->refs = 1;
ustp = lua_newuserdatauv(L, sizeof(*ustp), 1);
luaL_setmetatable(L, "unix.Stat");
*ustp = ust;
return 1;
}
// unix.stat(path:str[, flags:int[, dirfd:int]])
// ├─→ unix.Stat
// └─→ nil, unix.Errno
static int LuaUnixStat(lua_State *L) {
const char *path;
struct UnixStat *ust;
int dirfd, flags, olderr = errno;
path = luaL_checkstring(L, 1);
flags = luaL_optinteger(L, 2, 0);
dirfd = luaL_optinteger(L, 3, AT_FDCWD);
if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) {
if (!fstatat(dirfd, path, &ust->st, flags)) {
return ReturnStat(L, ust);
}
free(ust);
struct stat st;
int olderr = errno;
if (!fstatat(luaL_optinteger(L, 3, AT_FDCWD), luaL_checkstring(L, 1), &st,
luaL_optinteger(L, 2, 0))) {
LuaPushStat(L, &st);
return 1;
} else {
return SysretErrno(L, "stat", olderr);
}
return SysretErrno(L, "stat", olderr);
}
// unix.fstat(fd:int)
// ├─→ unix.Stat
// └─→ nil, unix.Errno
static int LuaUnixFstat(lua_State *L) {
int fd, olderr = errno;
struct UnixStat *ust;
olderr = errno;
fd = luaL_checkinteger(L, 1);
if ((ust = LuaUnixAllocRaw(L, sizeof(*ust)))) {
if (!fstat(fd, &ust->st)) {
return ReturnStat(L, ust);
}
free(ust);
struct stat st;
int olderr = errno;
if (!fstat(luaL_checkinteger(L, 1), &st)) {
LuaPushStat(L, &st);
return 1;
} else {
return SysretErrno(L, "fstat", olderr);
}
return SysretErrno(L, "fstat", olderr);
}
static bool IsSockoptBool(int l, int x) {
@ -1746,9 +1722,7 @@ static int LuaUnixMinor(lua_State *L) {
// unix.Stat object
static dontinline struct stat *GetUnixStat(lua_State *L) {
struct UnixStat **ust;
ust = luaL_checkudata(L, 1, "unix.Stat");
return &(*ust)->st;
return luaL_checkudata(L, 1, "unix.Stat");
}
// unix.Stat:size()
@ -1853,28 +1827,12 @@ static int LuaUnixStatFlags(lua_State *L) {
return ReturnInteger(L, GetUnixStat(L)->st_flags);
}
static void FreeUnixStat(struct UnixStat *stat) {
if (!--stat->refs) {
free(stat);
}
}
static int LuaUnixStatToString(lua_State *L) {
struct stat *st = GetUnixStat(L);
lua_pushstring(L, "unix.Stat{}");
return 1;
}
static int LuaUnixStatGc(lua_State *L) {
struct UnixStat **ust;
ust = luaL_checkudata(L, 1, "unix.Stat");
if (*ust) {
FreeUnixStat(*ust);
*ust = 0;
}
return 0;
}
static const luaL_Reg kLuaUnixStatMeth[] = {
{"atim", LuaUnixStatAtim}, //
{"birthtim", LuaUnixStatBirthtim}, //
@ -1897,7 +1855,6 @@ static const luaL_Reg kLuaUnixStatMeth[] = {
static const luaL_Reg kLuaUnixStatMeta[] = {
{"__tostring", LuaUnixStatToString}, //
{"__gc", LuaUnixStatGc}, //
{0}, //
};
@ -1913,10 +1870,8 @@ static void LuaUnixStatObj(lua_State *L) {
////////////////////////////////////////////////////////////////////////////////
// unix.Errno object
static dontinline struct UnixErrno *GetUnixErrno(lua_State *L) {
struct UnixErrno **ep;
ep = luaL_checkudata(L, 1, "unix.Errno");
return *ep;
static struct UnixErrno *GetUnixErrno(lua_State *L) {
return luaL_checkudata(L, 1, "unix.Errno");
}
static int LuaUnixErrnoErrno(lua_State *L) {
@ -1948,22 +1903,6 @@ static int LuaUnixErrnoToString(lua_State *L) {
return 1;
}
static void FreeUnixErrno(struct UnixErrno *errno) {
if (!--errno->refs) {
free(errno);
}
}
static int LuaUnixErrnoGc(lua_State *L) {
struct UnixErrno **ue;
ue = luaL_checkudata(L, 1, "unix.Errno");
if (*ue) {
FreeUnixErrno(*ue);
*ue = 0;
}
return 0;
}
static const luaL_Reg kLuaUnixErrnoMeth[] = {
{"strerror", LuaUnixErrnoToString}, //
{"errno", LuaUnixErrnoErrno}, //
@ -1975,7 +1914,6 @@ static const luaL_Reg kLuaUnixErrnoMeth[] = {
static const luaL_Reg kLuaUnixErrnoMeta[] = {
{"__tostring", LuaUnixErrnoToString}, //
{"__gc", LuaUnixErrnoGc}, //
{0}, //
};

View file

@ -117,9 +117,10 @@ o/$(MODE)/tool/net/redbean.com: \
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean/.ape \
o/$(MODE)/tool/net/.redbean/.symtab \
tool/net/help.txt \
tool/net/.init.lua \
tool/net/favicon.ico \
@ -262,9 +263,10 @@ o/$(MODE)/tool/net/redbean-demo.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-demo/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-demo/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-demo/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-demo/.ape \
o/$(MODE)/tool/net/.redbean-demo/.symtab \
tool/net/help.txt
# REDBEAN-STATIC.COM
@ -284,9 +286,10 @@ o/$(MODE)/tool/net/redbean-static.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-static/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-static/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-static/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-static/.ape \
o/$(MODE)/tool/net/.redbean-static/.symtab \
tool/net/help.txt \
tool/net/favicon.ico \
tool/net/redbean.png
@ -320,9 +323,10 @@ o/$(MODE)/tool/net/redbean-unsecure.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-unsecure/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-unsecure/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-unsecure/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-unsecure/.ape \
o/$(MODE)/tool/net/.redbean-unsecure/.symtab \
tool/net/help.txt \
tool/net/favicon.ico \
tool/net/redbean.png
@ -363,9 +367,10 @@ o/$(MODE)/tool/net/redbean-original.com: \
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-original/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/net/.redbean-original/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-original/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-original/.ape \
o/$(MODE)/tool/net/.redbean-original/.symtab \
tool/net/help.txt \
tool/net/favicon.ico \
tool/net/redbean.png
@ -436,6 +441,8 @@ o/$(MODE)/tool/net/redbean-assimilate.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-assimilate
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/net/.redbean-assimilate/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/net/.redbean-assimilate/.symtab
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
o/$(MODE)/tool/net/.redbean-assimilate/.symtab \
tool/net/help.txt \

View file

@ -4120,7 +4120,7 @@ static int LuaLog(lua_State *L) {
}
static int LuaEncodeSmth(lua_State *L,
int Encoder(lua_State *, char **, int, char *, int)) {
int Encoder(lua_State *, char **, char *, int)) {
int useoutput = false;
int maxdepth = 64;
char *numformat = "%.14g";
@ -4129,15 +4129,14 @@ static int LuaEncodeSmth(lua_State *L,
lua_settop(L, 2); // discard any extra arguments
lua_getfield(L, 2, "useoutput");
// ignore useoutput outside of request handling
if (ishandlingrequest && lua_isboolean(L, -1))
if (ishandlingrequest && lua_isboolean(L, -1)) {
useoutput = lua_toboolean(L, -1);
lua_getfield(L, 2, "maxdepth");
maxdepth = luaL_optinteger(L, -1, maxdepth);
}
lua_getfield(L, 2, "numformat");
numformat = luaL_optstring(L, -1, numformat);
}
lua_settop(L, 1); // keep the passed argument on top
Encoder(L, useoutput ? &outbuf : &p, maxdepth, numformat, -1);
Encoder(L, useoutput ? &outbuf : &p, numformat, -1);
if (useoutput) {
lua_pushnil(L);
} else {
@ -5185,7 +5184,7 @@ static void LuaPrint(lua_State *L) {
if (n > 0) {
for (i = 1; i <= n; i++) {
if (i > 1) appendw(&b, '\t');
LuaEncodeLuaData(L, &b, 64, "g", i);
LuaEncodeLuaData(L, &b, "g", i);
}
appendw(&b, '\n');
WRITE(1, b, appendz(b).i);
@ -5222,7 +5221,6 @@ static void LuaInterpreter(lua_State *L) {
}
for (;;) {
status = lua_loadline(L);
write(1, "\n", 1);
if (status == -1) break; // eof
if (status == -2) {
if (errno == EINTR) {
@ -6502,7 +6500,6 @@ static int HandleReadline(void) {
if (status == -1) {
OnTerm(SIGHUP); // eof
INFOF("got repl eof");
write(1, "\n", 1);
return -1;
} else if (errno == EINTR) {
errno = 0;
@ -6516,7 +6513,6 @@ static int HandleReadline(void) {
return -1;
}
}
write(1, "\n", 1);
linenoiseDisableRawMode();
LUA_REPL_LOCK;
if (status == LUA_OK) {

View file

@ -55,7 +55,7 @@ o/$(MODE)/tool/plinko/plinko.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/plinko/.redbean
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com -o o/$(MODE)/tool/plinko/.plinko/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/plinko/.plinko/.symtab
$(TOOL_PLINKO_OBJS): \

View file

@ -88,7 +88,7 @@ o/$(MODE)/tool/viz/printimage.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/viz/.printimage/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/viz/.printimage/.symtab
o/$(MODE)/tool/viz/printvideo.com: \
@ -98,7 +98,7 @@ o/$(MODE)/tool/viz/printvideo.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ASYMTAB o/$(MODE)/tool/build/symtab.com \
-o o/$(MODE)/tool/viz/.printvideo/.symtab $<
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -9qj $@ \
@$(COMPILE) -AZIP -T$@ o/$(MODE)/third_party/zip/zip.com -0qj $@ \
o/$(MODE)/tool/viz/.printvideo/.symtab
o/$(MODE)/tool/viz/derasterize.o: \

Binary file not shown.