Auto-generate some documentation
parent
117d0111ab
commit
13437dd19b
5
Makefile
5
Makefile
|
@ -303,6 +303,7 @@ COSMOPOLITAN_HEADERS = \
|
|||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_ZLIB \
|
||||
THIRD_PARTY_REGEX
|
||||
|
||||
o/$(MODE)/cosmopolitan.a: $(filter-out o/libc/stubs/exit11.o,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_OBJS)))
|
||||
|
@ -312,6 +313,10 @@ o/cosmopolitan.h: \
|
|||
$(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS))
|
||||
@ACTION=ROLLUP TARGET=$@ build/do $^ >$@
|
||||
|
||||
o/cosmopolitan.html: \
|
||||
o//third_party/chibicc/chibicc.com.dbg
|
||||
o//third_party/chibicc/chibicc.com.dbg -J -fno-common -include libc/integral/normalize.inc -o $@ $(filter %.c,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))
|
||||
|
||||
# UNSPECIFIED PREREQUISITES TUTORIAL
|
||||
#
|
||||
# A build rule must exist for all files that make needs to consider in
|
||||
|
|
21
ape/config.h
21
ape/config.h
|
@ -3,11 +3,7 @@
|
|||
#include "ape/relocations.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* @fileverview αcτµαlly pδrταblε εxεcµταblε configuration.
|
||||
*/
|
||||
|
||||
/**
|
||||
/*
|
||||
* Post-Initialization Read-Only Code Size Threshold.
|
||||
*
|
||||
* An executable needs to have at least this much code, before the
|
||||
|
@ -22,9 +18,6 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* PC Standard I/O Configuration.
|
||||
*/
|
||||
#ifndef METAL_STDIN
|
||||
#define METAL_STDIN COM1
|
||||
#endif
|
||||
|
@ -35,11 +28,6 @@
|
|||
#define METAL_STDERR COM1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* PC Display Configuration (MDA/CGA)
|
||||
* @see www.lammertbies.nl/comm/info/serial-uart.html
|
||||
* @see ape/lib/vidya.internal.h
|
||||
*/
|
||||
#ifndef VIDYA_MODE
|
||||
#define VIDYA_MODE VIDYA_MODE_CGA
|
||||
#endif
|
||||
|
@ -66,10 +54,6 @@
|
|||
#define X87_DEFAULT X87_NORMAL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Serial Line Configuration (8250 UART 16550)
|
||||
* @see ape/lib/uart.h
|
||||
*/
|
||||
#ifndef UART_BAUD_RATE
|
||||
#define UART_BAUD_RATE 9600 /* bits per second ∈ [50,115200] */
|
||||
#endif
|
||||
|
@ -93,9 +77,6 @@
|
|||
#define UART_CONF_LCR 0b01000011
|
||||
#endif
|
||||
|
||||
/**
|
||||
* eXtreme Low Memory.
|
||||
*/
|
||||
#define XLM(VAR) (XLM_BASE_REAL + XLM_##VAR)
|
||||
#define XLMV(VAR) (__xlm + XLM_##VAR)
|
||||
#define XLM_BASE_REAL 0x1000
|
||||
|
|
|
@ -19,23 +19,23 @@
|
|||
#define IMAGE_BASE_PHYSICAL 0x100000
|
||||
#endif
|
||||
|
||||
#ifndef REAL_SCRATCH_AREA
|
||||
/**
|
||||
* Location of anything goes memory for real mode.
|
||||
*
|
||||
* The MBR won't load program content beyond this address, so we have
|
||||
* room for buffers, page tables, etc. before we reach the stack frame.
|
||||
*/
|
||||
#ifndef REAL_SCRATCH_AREA
|
||||
#define REAL_SCRATCH_AREA 0x40000
|
||||
#endif
|
||||
|
||||
#ifndef REAL_STACK_FRAME
|
||||
/**
|
||||
* Location of real mode 64kb stack frame.
|
||||
*
|
||||
* This address was chosen because memory beyond 0x80000 can't be
|
||||
* accessed safely without consulting e820.
|
||||
*/
|
||||
#ifndef REAL_STACK_FRAME
|
||||
#define REAL_STACK_FRAME 0x70000
|
||||
#endif
|
||||
|
||||
|
|
|
@ -21,12 +21,8 @@
|
|||
#include "libc/macros.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
/* clang-format off */
|
||||
|
||||
static void KnuthMorrisPrattInit(m, T, W)
|
||||
ssize_t m, T[m + 1];
|
||||
const char W[m];
|
||||
{
|
||||
static void KnuthMorrisPrattInit(ssize_t m, ssize_t *T, const char *W) {
|
||||
ssize_t i = 2;
|
||||
ssize_t j = 0;
|
||||
T[0] = -1;
|
||||
|
@ -43,10 +39,8 @@ static void KnuthMorrisPrattInit(m, T, W)
|
|||
T[m] = 0;
|
||||
}
|
||||
|
||||
static size_t KnuthMorrisPratt(m, T, W, n, S)
|
||||
const long n, m, T[m + 1];
|
||||
const char W[m], S[n];
|
||||
{
|
||||
static size_t KnuthMorrisPratt(long m, const long *T, const char *W, long n,
|
||||
const char *S) {
|
||||
long i = 0, j = 0;
|
||||
while (i + j < n) {
|
||||
if (W[i] == S[i + j]) {
|
||||
|
@ -60,8 +54,6 @@ static size_t KnuthMorrisPratt(m, T, W, n, S)
|
|||
return j;
|
||||
}
|
||||
|
||||
/* clang-format on */
|
||||
|
||||
/**
|
||||
* Searches for fixed-length substring in memory region.
|
||||
*
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#include "libc/fmt/conv.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* Returns absolute value of x.
|
||||
*/
|
||||
int(abs)(int x) {
|
||||
return ABS(x);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
|
||||
/**
|
||||
* Reverses bits in 16-bit word.
|
||||
*/
|
||||
uint16_t(bitreverse16)(uint16_t x) {
|
||||
return kReverseBits[x & 0x00FF] << 8 | kReverseBits[(x & 0xFF00) >> 8];
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/bswap.h"
|
||||
|
||||
/**
|
||||
* Reverses bits in 32-bit word.
|
||||
*/
|
||||
uint32_t(bitreverse32)(uint32_t x) {
|
||||
x = bswap_32(x);
|
||||
x = ((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1);
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/bswap.h"
|
||||
|
||||
/**
|
||||
* Reverses bits in 64-bit word.
|
||||
*/
|
||||
uint64_t bitreverse64(uint64_t x) {
|
||||
x = bswap_64(x);
|
||||
x = ((x & 0xaaaaaaaaaaaaaaaa) >> 1) | ((x & 0x5555555555555555) << 1);
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
|
||||
/**
|
||||
* Reverses bits in 8-bit word.
|
||||
*/
|
||||
uint8_t(bitreverse8)(uint8_t x) {
|
||||
return kReverseBits[x];
|
||||
}
|
||||
|
|
|
@ -17,5 +17,12 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
|
||||
const char *emptytonull(const char *s) { return s && !*s ? 0 : s; }
|
||||
/**
|
||||
* Returns string where empty string is made null.
|
||||
* @see nulltoempty()
|
||||
*/
|
||||
const char *(emptytonull)(const char *s) {
|
||||
return s && !*s ? 0 : s;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
* Returns a or b or aborts if both are null.
|
||||
*/
|
||||
const char *(firstnonnull)(const char *a, const char *b) {
|
||||
if (a) return a;
|
||||
if (b) return b;
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
|
||||
/**
|
||||
* Returns gray code for x.
|
||||
* @see https://en.wikipedia.org/wiki/Gray_code
|
||||
* @see ungray()
|
||||
*/
|
||||
uint32_t gray(uint32_t x) {
|
||||
return x ^ (x >> 1);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
/**
|
||||
* Counts number of different bits.
|
||||
* @see https://en.wikipedia.org/wiki/Hamming_code
|
||||
*/
|
||||
unsigned long hamming(unsigned long x, unsigned long y) {
|
||||
return popcnt(x ^ y);
|
||||
|
|
|
@ -36,7 +36,8 @@ static axdx_t RotateQuadrant(long n, long y, long x, long ry, long rx) {
|
|||
/**
|
||||
* Generates Hilbert space-filling curve.
|
||||
*
|
||||
* @see morton()
|
||||
* @see https://en.wikipedia.org/wiki/Hilbert_curve
|
||||
* @see unhilbert()
|
||||
*/
|
||||
long hilbert(long n, long y, long x) {
|
||||
axdx_t m;
|
||||
|
@ -56,7 +57,8 @@ long hilbert(long n, long y, long x) {
|
|||
/**
|
||||
* Decodes Hilbert space-filling curve.
|
||||
*
|
||||
* @see unmorton()
|
||||
* @see https://en.wikipedia.org/wiki/Hilbert_curve
|
||||
* @see hilbert()
|
||||
*/
|
||||
axdx_t unhilbert(long n, long i) {
|
||||
axdx_t m;
|
||||
|
|
|
@ -18,4 +18,9 @@
|
|||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
|
||||
bool isempty(const char *s) { return !s || !*s; }
|
||||
/**
|
||||
* Returns true if s is empty string or null.
|
||||
*/
|
||||
bool isempty(const char *s) {
|
||||
return !s || !*s;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
/**
|
||||
* Interleaves bits.
|
||||
* @see https://en.wikipedia.org/wiki/Z-order_curve
|
||||
* @see unmorton()
|
||||
*/
|
||||
unsigned long(morton)(unsigned long y, unsigned long x) {
|
||||
x = (x | x << 020) & 0x0000FFFF0000FFFF;
|
||||
|
|
|
@ -17,5 +17,12 @@
|
|||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/safemacros.internal.h"
|
||||
|
||||
const char *nulltoempty(const char *s) { return s ? s : ""; }
|
||||
/**
|
||||
* Returns 𝑠 converting null to empty string.
|
||||
* @see emptytonull()
|
||||
*/
|
||||
const char *(nulltoempty)(const char *s) {
|
||||
return s ? s : "";
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/popcnt.h"
|
||||
|
||||
/**
|
||||
* Returns number of bits set in integer.
|
||||
*/
|
||||
uint64_t(popcnt)(uint64_t x) {
|
||||
x = x - ((x >> 1) & 0x5555555555555555);
|
||||
x = ((x >> 2) & 0x3333333333333333) + (x & 0x3333333333333333);
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
#include "libc/macros.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
#if !defined(__GNUC__) || defined(__STRICT_ANSI__)
|
||||
#define pushpop(x) (x)
|
||||
#else
|
||||
/**
|
||||
* PushPop
|
||||
* An elegant weapon for a more civilized age.
|
||||
*/
|
||||
#if !defined(__GNUC__) || defined(__STRICT_ANSI__)
|
||||
#define pushpop(x) (x)
|
||||
#else
|
||||
#define pushpop(x) \
|
||||
({ \
|
||||
typeof(x) Popped; \
|
||||
|
|
|
@ -19,4 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
|
||||
long(rounddown)(long w, long k) { return ROUNDDOWN(w, k); }
|
||||
/**
|
||||
* Rounds down 𝑤 to next two power 𝑘.
|
||||
*/
|
||||
long(rounddown)(long w, long k) {
|
||||
return ROUNDDOWN(w, k);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* Rounds up 𝑤 to next two power 𝑘.
|
||||
*/
|
||||
long(roundup)(long w, long k) {
|
||||
return ROUNDUP(w, k);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/**
|
||||
* Returns 𝑥 rounded up to next two power and log'd.
|
||||
* @see roundup2pow
|
||||
* @see roundup2pow()
|
||||
*/
|
||||
unsigned long roundup2log(unsigned long x) {
|
||||
return x > 1 ? (bsrl(x - 1) + 1) : x ? 1 : 0;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* Returns 𝑥 rounded up to next two power.
|
||||
*
|
||||
* @define (𝑥>0→2^⌈log₂x⌉, x=0→0, 𝑇→⊥)
|
||||
* @see rounddown2pow)()
|
||||
* @see rounddown2pow()
|
||||
*/
|
||||
unsigned long roundup2pow(unsigned long x) {
|
||||
return x > 1 ? 1ul << (bsrl(x - 1) + 1) : x ? 1 : 0;
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
|
||||
/**
|
||||
* Decodes gray code.
|
||||
* @see https://en.wikipedia.org/wiki/Gray_code
|
||||
* @see gray()
|
||||
*/
|
||||
uint32_t ungray(uint32_t x) {
|
||||
x ^= x >> 16;
|
||||
x ^= x >> 8;
|
||||
|
|
|
@ -35,6 +35,7 @@ static unsigned long GetOddBits(unsigned long x) {
|
|||
* @param 𝑖 is interleaved index
|
||||
* @return deinterleaved coordinate {ax := 𝑦, dx := 𝑥}
|
||||
* @see en.wikipedia.org/wiki/Z-order_curve
|
||||
* @see morton()
|
||||
*/
|
||||
axdx_t(unmorton)(unsigned long i) {
|
||||
return (axdx_t){GetOddBits(i >> 1), GetOddBits(i)};
|
||||
|
|
|
@ -25,15 +25,15 @@
|
|||
/**
|
||||
* Changes file permissions via open()'d file descriptor, e.g.:
|
||||
*
|
||||
* CHECK_NE(-1, chmod("foo/bar.txt", 0644));
|
||||
* CHECK_NE(-1, chmod("o/default/program.com", 0755));
|
||||
* CHECK_NE(-1, chmod("privatefolder/", 0700));
|
||||
* CHECK_NE(-1, chmod("foo/bar.txt", 0644));
|
||||
* CHECK_NE(-1, chmod("o/default/program.com", 0755));
|
||||
* CHECK_NE(-1, chmod("privatefolder/", 0700));
|
||||
*
|
||||
* The esoteric bits generally available on System V are:
|
||||
*
|
||||
* CHECK_NE(-1, chmod("/opt/", 01000)); // sticky bit
|
||||
* CHECK_NE(-1, chmod("/usr/bin/sudo", 04755)); // setuid bit
|
||||
* CHECK_NE(-1, chmod("/usr/bin/wall", 02755)); // setgid bit
|
||||
* CHECK_NE(-1, chmod("/opt/", 01000)); // sticky bit
|
||||
* CHECK_NE(-1, chmod("/usr/bin/sudo", 04755)); // setuid bit
|
||||
* CHECK_NE(-1, chmod("/usr/bin/wall", 02755)); // setgid bit
|
||||
*
|
||||
* This works on Windows NT if you ignore the error ;-)
|
||||
*
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
/**
|
||||
* Does things with file descriptor, via re-imagined hourglass api, e.g.
|
||||
*
|
||||
* CHECK_NE(-1, fcntl(fd, F_SETFD, FD_CLOEXEC));
|
||||
* CHECK_NE(-1, fcntl(fd, F_SETFD, FD_CLOEXEC));
|
||||
*
|
||||
* @param cmd can be F_{GET,SET}{FD,FL}, etc.
|
||||
* @param arg can be FD_CLOEXEC, etc. depending
|
||||
|
|
|
@ -118,13 +118,13 @@ static textwindows noinline struct dirent *readdir$nt(DIR *dir) {
|
|||
/**
|
||||
* Opens directory, e.g.
|
||||
*
|
||||
* DIR *d;
|
||||
* struct dirent *e;
|
||||
* CHECK((d = opendir(path)));
|
||||
* while ((e = readdir(d))) {
|
||||
* printf("%s/%s\n", path, e->d_name);
|
||||
* }
|
||||
* LOGIFNEG1(closedir(d));
|
||||
* DIR *d;
|
||||
* struct dirent *e;
|
||||
* CHECK((d = opendir(path)));
|
||||
* while ((e = readdir(d))) {
|
||||
* printf("%s/%s\n", path, e->d_name);
|
||||
* }
|
||||
* LOGIFNEG1(closedir(d));
|
||||
*
|
||||
* @returns newly allocated DIR object, or NULL w/ errno
|
||||
* @errors ENOENT, ENOTDIR, EACCES, EMFILE, ENFILE, ENOMEM
|
||||
|
|
|
@ -27,18 +27,18 @@
|
|||
/**
|
||||
* Launches program, e.g.
|
||||
*
|
||||
* char buf[2];
|
||||
* int ws, pid, fds[3] = {-1, -1, STDERR_FILENO};
|
||||
* CHECK_NE(-1, (pid = spawnve(0, fds, commandv("ssh"),
|
||||
* (char *const[]){"ssh", hostname, "cat", NULL},
|
||||
* environ)));
|
||||
* CHECK_EQ(+2, write(fds[0], "hi", 2));
|
||||
* CHECK_NE(-1, close(fds[0]));
|
||||
* CHECK_EQ(+2, read(fds[1], buf, 2)));
|
||||
* CHECK_NE(-1, close(fds[1]));
|
||||
* CHECK_EQ(+0, memcmp(buf, "hi", 2)));
|
||||
* CHECK_NE(-1, waitpid(pid, &ws, 0));
|
||||
* CHECK_EQ(+0, WEXITSTATUS(ws));
|
||||
* char buf[2];
|
||||
* int ws, pid, fds[3] = {-1, -1, STDERR_FILENO};
|
||||
* CHECK_NE(-1, (pid = spawnve(0, fds, commandv("ssh"),
|
||||
* (char *const[]){"ssh", hostname, "cat", 0},
|
||||
* environ)));
|
||||
* CHECK_EQ(+2, write(fds[0], "hi", 2));
|
||||
* CHECK_NE(-1, close(fds[0]));
|
||||
* CHECK_EQ(+2, read(fds[1], buf, 2)));
|
||||
* CHECK_NE(-1, close(fds[1]));
|
||||
* CHECK_EQ(+0, memcmp(buf, "hi", 2)));
|
||||
* CHECK_NE(-1, waitpid(pid, &ws, 0));
|
||||
* CHECK_EQ(+0, WEXITSTATUS(ws));
|
||||
*
|
||||
* @param stdiofds may optionally be passed to customize standard i/o
|
||||
* @param stdiofds[𝑖] may be -1 to receive a pipe() fd
|
||||
|
@ -49,6 +49,7 @@
|
|||
* @param envp[0,n-2] specifies "foo=bar" environment variables
|
||||
* @param envp[n-1] is NULL
|
||||
* @return pid of child, or -1 w/ errno
|
||||
* @deprecated just use vfork() and execve()
|
||||
*/
|
||||
int spawnve(unsigned flags, int stdiofds[3], const char *program,
|
||||
char *const argv[], char *const envp[]) {
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
* @asyncsignalsafe
|
||||
*/
|
||||
bool32 isatty(int fd) {
|
||||
char buf[sizeof(uint16_t) * 4] forcealign(2);
|
||||
_Alignas(short) char buf[sizeof(uint16_t) * 4];
|
||||
if (!IsWindows()) {
|
||||
return ioctl$sysv(fd, TIOCGWINSZ, &buf) != -1;
|
||||
} else {
|
||||
|
|
|
@ -21,38 +21,37 @@
|
|||
#include "libc/calls/kntprioritycombos.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/nexgen32e/ffs.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/enum/threadpriority.h"
|
||||
|
||||
#define FFS(x) __builtin_ffs(x)
|
||||
|
||||
const struct NtPriorityCombo kNtPriorityCombos[] = {
|
||||
{-20, FFS(kNtHighPriorityClass), kNtThreadPriorityHighest, 15},
|
||||
{-18, FFS(kNtHighPriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-17, FFS(kNtNormalPriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-15, FFS(kNtIdlePriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-13, FFS(kNtHighPriorityClass), kNtThreadPriorityAboveNormal, 14},
|
||||
{-11, FFS(kNtHighPriorityClass), kNtThreadPriorityNormal, 13},
|
||||
{-9, FFS(kNtHighPriorityClass), kNtThreadPriorityBelowNormal, 12},
|
||||
{-7, FFS(kNtNormalPriorityClass), kNtThreadPriorityHighest, 11},
|
||||
{-5, FFS(kNtHighPriorityClass), kNtThreadPriorityLowest, 11},
|
||||
{-3, FFS(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 10},
|
||||
{-1, FFS(kNtNormalPriorityClass), kNtThreadPriorityHighest, 9},
|
||||
{0, FFS(kNtNormalPriorityClass), kNtThreadPriorityNormal, 9},
|
||||
{1, FFS(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 8},
|
||||
{2, FFS(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 8},
|
||||
{3, FFS(kNtNormalPriorityClass), kNtThreadPriorityNormal, 7},
|
||||
{4, FFS(kNtNormalPriorityClass), kNtThreadPriorityLowest, 7},
|
||||
{5, FFS(kNtIdlePriorityClass), kNtThreadPriorityHighest, 6},
|
||||
{6, FFS(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 6},
|
||||
{7, FFS(kNtIdlePriorityClass), kNtThreadPriorityAboveNormal, 5},
|
||||
{9, FFS(kNtNormalPriorityClass), kNtThreadPriorityLowest, 5},
|
||||
{11, FFS(kNtIdlePriorityClass), kNtThreadPriorityNormal, 4},
|
||||
{13, FFS(kNtIdlePriorityClass), kNtThreadPriorityBelowNormal, 3},
|
||||
{15, FFS(kNtIdlePriorityClass), kNtThreadPriorityLowest, 2},
|
||||
{17, FFS(kNtHighPriorityClass), kNtThreadPriorityIdle, 1},
|
||||
{18, FFS(kNtNormalPriorityClass), kNtThreadPriorityIdle, 1},
|
||||
{19, FFS(kNtIdlePriorityClass), kNtThreadPriorityIdle, 1},
|
||||
{-20, ffs(kNtHighPriorityClass), kNtThreadPriorityHighest, 15},
|
||||
{-18, ffs(kNtHighPriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-17, ffs(kNtNormalPriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-15, ffs(kNtIdlePriorityClass), kNtThreadPriorityTimeCritical, 15},
|
||||
{-13, ffs(kNtHighPriorityClass), kNtThreadPriorityAboveNormal, 14},
|
||||
{-11, ffs(kNtHighPriorityClass), kNtThreadPriorityNormal, 13},
|
||||
{-9, ffs(kNtHighPriorityClass), kNtThreadPriorityBelowNormal, 12},
|
||||
{-7, ffs(kNtNormalPriorityClass), kNtThreadPriorityHighest, 11},
|
||||
{-5, ffs(kNtHighPriorityClass), kNtThreadPriorityLowest, 11},
|
||||
{-3, ffs(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 10},
|
||||
{-1, ffs(kNtNormalPriorityClass), kNtThreadPriorityHighest, 9},
|
||||
{0, ffs(kNtNormalPriorityClass), kNtThreadPriorityNormal, 9},
|
||||
{1, ffs(kNtNormalPriorityClass), kNtThreadPriorityAboveNormal, 8},
|
||||
{2, ffs(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 8},
|
||||
{3, ffs(kNtNormalPriorityClass), kNtThreadPriorityNormal, 7},
|
||||
{4, ffs(kNtNormalPriorityClass), kNtThreadPriorityLowest, 7},
|
||||
{5, ffs(kNtIdlePriorityClass), kNtThreadPriorityHighest, 6},
|
||||
{6, ffs(kNtNormalPriorityClass), kNtThreadPriorityBelowNormal, 6},
|
||||
{7, ffs(kNtIdlePriorityClass), kNtThreadPriorityAboveNormal, 5},
|
||||
{9, ffs(kNtNormalPriorityClass), kNtThreadPriorityLowest, 5},
|
||||
{11, ffs(kNtIdlePriorityClass), kNtThreadPriorityNormal, 4},
|
||||
{13, ffs(kNtIdlePriorityClass), kNtThreadPriorityBelowNormal, 3},
|
||||
{15, ffs(kNtIdlePriorityClass), kNtThreadPriorityLowest, 2},
|
||||
{17, ffs(kNtHighPriorityClass), kNtThreadPriorityIdle, 1},
|
||||
{18, ffs(kNtNormalPriorityClass), kNtThreadPriorityIdle, 1},
|
||||
{19, ffs(kNtIdlePriorityClass), kNtThreadPriorityIdle, 1},
|
||||
};
|
||||
|
||||
const unsigned kNtPriorityCombosLen = ARRAYLEN(kNtPriorityCombos);
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
* how long each sequence is, so that each read consumes a single thing
|
||||
* from the underlying file descriptor, e.g.
|
||||
*
|
||||
* "a" ALFA
|
||||
* "\316\261" ALPHA
|
||||
* "\033[A" CURSOR UP
|
||||
* "\033[38;5;202m" ORANGERED
|
||||
* "\eOP" PF1
|
||||
* "a" ALFA
|
||||
* "\316\261" ALPHA
|
||||
* "\033[A" CURSOR UP
|
||||
* "\033[38;5;202m" ORANGERED
|
||||
* "\eOP" PF1
|
||||
*
|
||||
* This routine generalizes to ascii, utf-8, chorded modifier keys,
|
||||
* function keys, color codes, c0/c1 control codes, cursor movement,
|
||||
|
@ -48,9 +48,9 @@
|
|||
* tokenized as a single read. Lastly note, this function has limited
|
||||
* support for UNICODE representations of C0/C1 control codes, e.g.
|
||||
*
|
||||
* "\000" NUL
|
||||
* "\300\200" NUL
|
||||
* "\302\233A" CURSOR UP
|
||||
* "\000" NUL
|
||||
* "\300\200" NUL
|
||||
* "\302\233A" CURSOR UP
|
||||
*
|
||||
* @param buf is guaranteed to receive a NUL terminator if size>0
|
||||
* @return number of bytes read (helps differentiate "\0" vs. "")
|
||||
|
|
|
@ -28,27 +28,28 @@
|
|||
*
|
||||
* Raise SIGALRM every 1.5s:
|
||||
*
|
||||
* CHECK_NE(-1, sigaction(SIGALRM,
|
||||
* &(struct sigaction){.sa_sigaction = missingno},
|
||||
* NULL));
|
||||
* CHECK_NE(-1, setitimer(ITIMER_REAL,
|
||||
* &(const struct itimerval){{1, 500000}, {1, 500000}},
|
||||
* NULL));
|
||||
* CHECK_NE(-1, sigaction(SIGALRM,
|
||||
* &(struct sigaction){.sa_sigaction = missingno},
|
||||
* NULL));
|
||||
* CHECK_NE(-1, setitimer(ITIMER_REAL,
|
||||
* &(const struct itimerval){{1, 500000},
|
||||
* {1, 500000}},
|
||||
* NULL));
|
||||
*
|
||||
* Set single-shot 50ms timer callback to interrupt laggy connect():
|
||||
*
|
||||
* CHECK_NE(-1, sigaction(SIGALRM,
|
||||
* &(struct sigaction){.sa_sigaction = missingno,
|
||||
* .sa_flags = SA_RESETHAND},
|
||||
* NULL));
|
||||
* CHECK_NE(-1, setitimer(ITIMER_REAL,
|
||||
* &(const struct itimerval){{0, 0}, {0, 50000}},
|
||||
* NULL));
|
||||
* if (connect(...) == -1 && errno == EINTR) { ... }
|
||||
* CHECK_NE(-1, sigaction(SIGALRM,
|
||||
* &(struct sigaction){.sa_sigaction = missingno,
|
||||
* .sa_flags = SA_RESETHAND},
|
||||
* NULL));
|
||||
* CHECK_NE(-1, setitimer(ITIMER_REAL,
|
||||
* &(const struct itimerval){{0, 0}, {0, 50000}},
|
||||
* NULL));
|
||||
* if (connect(...) == -1 && errno == EINTR) { ... }
|
||||
*
|
||||
* Disarm timer:
|
||||
*
|
||||
* CHECK_NE(-1, setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL));
|
||||
* CHECK_NE(-1, setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL));
|
||||
*
|
||||
* Be sure to check for EINTR on your i/o calls, for best low latency.
|
||||
*
|
||||
|
|
|
@ -112,10 +112,10 @@ static void sigaction$native2cosmo(union metasigaction *sa) {
|
|||
/**
|
||||
* Installs handler for kernel interrupt, e.g.:
|
||||
*
|
||||
* void GotCtrlC(int sig, siginfo_t *si, ucontext_t *ctx);
|
||||
* struct sigaction sa = {.sa_sigaction = GotCtrlC,
|
||||
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
|
||||
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
|
||||
* void GotCtrlC(int sig, siginfo_t *si, ucontext_t *ctx);
|
||||
* struct sigaction sa = {.sa_sigaction = GotCtrlC,
|
||||
* .sa_flags = SA_RESETHAND|SA_RESTART|SA_SIGINFO};
|
||||
* CHECK_NE(-1, sigaction(SIGINT, &sa, NULL));
|
||||
*
|
||||
* @see xsigaction() for a much better api
|
||||
* @asyncsignalsafe
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
/**
|
||||
* Changes program signal blocking state, e.g.:
|
||||
*
|
||||
* sigset_t oldmask;
|
||||
* sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
|
||||
* sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
* sigset_t oldmask;
|
||||
* sigprocmask(SIG_BLOCK, &kSigsetFull, &oldmask);
|
||||
* sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
||||
*
|
||||
* @param how can be SIG_BLOCK (U), SIG_UNBLOCK (/), SIG_SETMASK (=)
|
||||
* @param set is the new mask content (optional)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
│ cosmopolitan § autotune » dead code elimination │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
|
||||
#ifndef SUPPORT_VECTOR
|
||||
/**
|
||||
* Supported Platforms Tuning Knob (Runtime & Compile-Time)
|
||||
*
|
||||
|
@ -17,7 +18,6 @@
|
|||
* offer marginal improvements in terms of code size and performance, at
|
||||
* the cost of portability.
|
||||
*/
|
||||
#ifndef SUPPORT_VECTOR
|
||||
#define SUPPORT_VECTOR 0b11111111
|
||||
#endif
|
||||
#define LINUX 1
|
||||
|
|
|
@ -10,7 +10,7 @@ typedef struct Elf64_Rela {
|
|||
* ELF64_R_TYPE(r_info) → R_X86_64_{64,PC32,GOTPCRELX,...}
|
||||
* ELF64_R_INFO(sym, type) → r_info
|
||||
*/
|
||||
/*u64*/ Elf64_Xword r_info; /** @see ELF64_R_{SYM,SIZE,INFO} */
|
||||
/*u64*/ Elf64_Xword r_info; /* ELF64_R_{SYM,SIZE,INFO} */
|
||||
/*i64*/ Elf64_Sxword r_addend;
|
||||
} Elf64_Rela;
|
||||
|
||||
|
|
|
@ -26,23 +26,23 @@
|
|||
*
|
||||
* Cosmopolitan displays RADIX-256 numbers using these digits:
|
||||
*
|
||||
* 0123456789abcdef
|
||||
* 0 ☺☻♥♦♣♠•◘○◙♂♀♪♫☼
|
||||
* 1►◄↕‼¶§▬↨↑↓→←∟↔▲▼
|
||||
* 2 !"#$%&'()*+,-./
|
||||
* 30123456789:;<=>?
|
||||
* 4@ABCDEFGHIJKLMNO
|
||||
* 5PQRSTUVWXYZ[\]^_
|
||||
* 6`abcdefghijklmno
|
||||
* 7pqrstuvwxyz{|}~⌂
|
||||
* 8ÇüéâäàåçêëèïîìÄÅ
|
||||
* 9ÉæÆôöòûùÿÖÜ¢£¥€ƒ
|
||||
* aáíóúñѪº¿⌐¬½¼¡«»
|
||||
* b░▒▓│┤╡╢╖╕╣║╗╝╜╛┐
|
||||
* c└┴┬├─┼╞╟╚╔╩╦╠═╬╧
|
||||
* d╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀
|
||||
* eαßΓπΣσμτΦΘΩδ∞φε∩
|
||||
* f≡±≥≤⌠⌡÷≈°∙·√ⁿ²■λ
|
||||
* 0123456789abcdef
|
||||
* 0 ☺☻♥♦♣♠•◘○◙♂♀♪♫☼
|
||||
* 1►◄↕‼¶§▬↨↑↓→←∟↔▲▼
|
||||
* 2 !"#$%&'()*+,-./
|
||||
* 30123456789:;<=>?
|
||||
* 4@ABCDEFGHIJKLMNO
|
||||
* 5PQRSTUVWXYZ[\]^_
|
||||
* 6`abcdefghijklmno
|
||||
* 7pqrstuvwxyz{|}~⌂
|
||||
* 8ÇüéâäàåçêëèïîìÄÅ
|
||||
* 9ÉæÆôöòûùÿÖÜ¢£¥€ƒ
|
||||
* aáíóúñѪº¿⌐¬½¼¡«»
|
||||
* b░▒▓│┤╡╢╖╕╣║╗╝╜╛┐
|
||||
* c└┴┬├─┼╞╟╚╔╩╦╠═╬╧
|
||||
* d╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀
|
||||
* eαßΓπΣσμτΦΘΩδ∞φε∩
|
||||
* f≡±≥≤⌠⌡÷≈°∙·√ⁿ²■λ
|
||||
*
|
||||
* IBM designed these glyphs for the PC to map onto the display bytes at
|
||||
* (char *)0xb8000. Because IBM authorized buyers of its PCs to inspect
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
#define __far
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets type of expression.
|
||||
* @see autotype() which is a better alternative for certain use cases
|
||||
*/
|
||||
#if !defined(__GNUC__) && __cplusplus + 0 >= 201103L
|
||||
#define typeof(x) decltype(x)
|
||||
#elif (defined(__STRICT_ANSI__) || !defined(__GNUC__)) && \
|
||||
|
@ -143,35 +139,10 @@ typedef __UINT32_TYPE__ uint32_t;
|
|||
typedef __INT64_TYPE__ int64_t;
|
||||
typedef __UINT64_TYPE__ uint64_t;
|
||||
|
||||
/**
|
||||
* AX:DX register pair.
|
||||
*
|
||||
* Every ABI we support permits functions to return two machine words.
|
||||
* Normally it's best to define a one-off struct. Sometimes we don't
|
||||
* want the boilerplate.
|
||||
*
|
||||
* @see System V Application Binary Interface NexGen32e Architecture
|
||||
* Processor Supplement, Version 1.0, December 5th, 2018
|
||||
* @see agner.org/optimize/calling_conventions.pdf (chapter 6)
|
||||
* @see LISP primitives CONS[CAR,CDR] w/ IBM 704 naming
|
||||
* @see int128_t
|
||||
*/
|
||||
typedef struct {
|
||||
intptr_t ax, dx;
|
||||
} axdx_t;
|
||||
|
||||
/*
|
||||
* GCC, Clang, and System V ABI all incorrectly define intmax_t.
|
||||
*
|
||||
* “[intmax_t] designates a signed integer type capable of
|
||||
* representing any value of any signed integer type.”
|
||||
* ──Quoth ISO/IEC 9899:201x 7.20.1.5
|
||||
*
|
||||
* This surprising contradiction is most likely due to Linux distro
|
||||
* practices of using dynamic shared objects which needs to change.
|
||||
*
|
||||
* http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2303.pdf
|
||||
*/
|
||||
#ifdef __SIZEOF_INTMAX__
|
||||
#undef __SIZEOF_INTMAX__
|
||||
#endif
|
||||
|
@ -210,11 +181,6 @@ typedef uint64_t uintmax_t;
|
|||
#define mallocesque reallocesque returnspointerwithnoaliases
|
||||
#define interruptfn nocallersavedregisters forcealignargpointer
|
||||
|
||||
/**
|
||||
* Declares combinator, i.e. never reads/writes global memory.
|
||||
* Thus enabling LICM, CSE, DCE, etc. optimizations.
|
||||
* @see nosideeffect
|
||||
*/
|
||||
#ifndef pureconst
|
||||
#ifndef __STRICT_ANSI__
|
||||
#define pureconst __attribute__((__const__))
|
||||
|
@ -223,9 +189,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Aligns automatic or static variable.
|
||||
*/
|
||||
#ifndef forcealign
|
||||
#ifndef __STRICT_ANSI__
|
||||
#define forcealign(bytes) __attribute__((__aligned__(bytes)))
|
||||
|
@ -234,21 +197,12 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Disables alignment.
|
||||
* @param opt_bytes defaults to __BIGGEST_ALIGNMENT__
|
||||
* @see nosideeffect
|
||||
*/
|
||||
#ifndef __STRICT_ANSI__
|
||||
#define thatispacked __attribute__((__packed__))
|
||||
#else
|
||||
#define thatispacked
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares prototype as using well-known format string DSL.
|
||||
* Thereby allowing compiler to identify certain bugs.
|
||||
*/
|
||||
#ifndef __STRICT_ANSI__
|
||||
#define printfesque(n) __attribute__((__format__(__gnu_printf__, n, n + 1)))
|
||||
#define scanfesque(n) __attribute__((__format__(__gnu_scanf__, n, n + 1)))
|
||||
|
@ -297,11 +251,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares prototype as never mutating global memory.
|
||||
* Thus enabling CSE, DCE, LICM [clang-only?], etc. optimizations.
|
||||
* @see pureconst
|
||||
*/
|
||||
#ifndef nosideeffect
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__pure__) || \
|
||||
|
@ -332,18 +281,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Makes function behave as much like macro as possible, meaning:
|
||||
*
|
||||
* 1. always inlined, i.e. even with -fno-inline
|
||||
* 2. unlinkable, i.e. elf data is not generated
|
||||
* 3. steppable, i.e. dwarf data is generated
|
||||
* 4. unprofilable
|
||||
* 5. unhookable
|
||||
*
|
||||
* @note consider static or writing a macro
|
||||
* @see externinline
|
||||
*/
|
||||
#ifndef forceinline
|
||||
#ifdef __cplusplus
|
||||
#define forceinline inline
|
||||
|
@ -372,19 +309,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif /* __cplusplus */
|
||||
#endif /* forceinline */
|
||||
|
||||
/**
|
||||
* Permits untyped or punned memory manipulation w/o asm.
|
||||
*
|
||||
* “The fundamental problem is that it is not possible to write real
|
||||
* programs using the X3J11 definition of C. The committee has created
|
||||
* an unreal language that no one can or will actually use. While the
|
||||
* problems of `const' may owe to careless drafting of the
|
||||
* specification, `noalias' is an altogether mistaken notion, and must
|
||||
* not survive.” ──Dennis Ritchie in 1988-03-20.
|
||||
*
|
||||
* @see asm(), memcpy(), memset(), read32be(), etc.
|
||||
* @see unsigned char
|
||||
*/
|
||||
#ifndef mayalias
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__may_alias__) || \
|
||||
|
@ -395,11 +319,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares prototype as returning freeable resource.
|
||||
* Compilation will fail if caller ignores return value.
|
||||
* @see gc(), free(), close(), etc.
|
||||
*/
|
||||
#ifndef nodiscard
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 304 || \
|
||||
|
@ -410,10 +329,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares variadic function as needing NULL sentinel argument.
|
||||
* @see execl() for example
|
||||
*/
|
||||
#ifndef nullterminated
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__sentinel__) || __GNUC__ >= 4)
|
||||
|
@ -448,10 +363,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Relocates function to .text.unlikely section of binary.
|
||||
* @note can be used to minimize page-faults and improve locality
|
||||
*/
|
||||
#ifndef relegated
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__cold__) || \
|
||||
|
@ -470,11 +381,6 @@ typedef uint64_t uintmax_t;
|
|||
#define warnifused(s)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Relocates function to .text.hot section of binary.
|
||||
* @note can be used to minimize page-faults w/ improved locality
|
||||
* @note most appropriately automated by profile-guided opts
|
||||
*/
|
||||
#ifndef firstclass
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__hot__) || \
|
||||
|
@ -485,12 +391,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares all or specific parameters as never receiving NULL.
|
||||
*
|
||||
* This can be checked at both compile-time (only for constexprs) and
|
||||
* runtime too (only in MODE=dbg mode) by synthetic Ubsan code.
|
||||
*/
|
||||
#ifndef paramsnonnull
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__nonnull__) || \
|
||||
|
@ -501,22 +401,12 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares array argument w/ minimum size contract, e.g.
|
||||
*
|
||||
* int foo(int bar[hasatleast 2]) { ... }
|
||||
*/
|
||||
#if __STDC_VERSION__ + 0 >= 199901L
|
||||
#define hasatleast static
|
||||
#else
|
||||
#define hasatleast
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Qualifies char pointer so it's treated like every other type.
|
||||
*
|
||||
* int foo(int bar[hasatleast 2]) { ... }
|
||||
*/
|
||||
#if __STDC_VERSION__ + 0 < 199901L && !defined(restrict)
|
||||
#if !defined(__STRICT_ANSI__) && !defined(__cplusplus) && \
|
||||
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 301 || defined(_MSC_VER))
|
||||
|
@ -527,10 +417,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares prototype that can't mutate caller's static variables.
|
||||
* @note consider more .c files or declare in function
|
||||
*/
|
||||
#ifndef nocallback
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__leaf__) || \
|
||||
|
@ -554,11 +440,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Asks compiler to not optimize function definition.
|
||||
*
|
||||
* @todo this is dangerous delete?
|
||||
*/
|
||||
#ifndef nooptimize
|
||||
#ifndef __STRICT_ANSI__
|
||||
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
|
||||
|
@ -572,13 +453,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Asks compiler to generate as little code as possible for function.
|
||||
*
|
||||
* This does the same thing as relegated, but without relocation.
|
||||
*
|
||||
* @todo this is dangerous delete?
|
||||
*/
|
||||
#ifndef optimizesize
|
||||
#ifndef __STRICT_ANSI__
|
||||
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
|
||||
|
@ -592,15 +466,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Asks compiler to always heavily optimize function.
|
||||
*
|
||||
* This keyword provides an alternative to build flag tuning, in cases
|
||||
* where the compiler is reluctant to vectorize mathematical code that's
|
||||
* written in standards-compliant C rather than GCC extensions.
|
||||
*
|
||||
* @todo this is dangerous delete?
|
||||
*/
|
||||
#ifndef optimizespeed
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
|
||||
|
@ -611,9 +476,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares prototype that behaves similar to setjmp() or vfork().
|
||||
*/
|
||||
#ifndef returnstwice
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__returns_twice__) || \
|
||||
|
@ -624,10 +486,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Asks compiler to not emit DWARF assembly for function.
|
||||
* @see artificial
|
||||
*/
|
||||
#ifndef nodebuginfo
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__nodebug__) || defined(__llvm__))
|
||||
|
@ -637,10 +495,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Associates debug information with call site.
|
||||
* @see nodebuginfo
|
||||
*/
|
||||
#ifndef artificial
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__artificial__) || \
|
||||
|
@ -651,11 +505,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Defines function as specially compiled for newer cpu model.
|
||||
* @see -ftree-vectorize and consider assembly
|
||||
* @see libc/dce.h
|
||||
*/
|
||||
#ifndef microarchitecture
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__target__) || \
|
||||
|
@ -666,10 +515,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Compiles function multiple times for different cpu models.
|
||||
* @see libc/dce.h
|
||||
*/
|
||||
#ifndef targetclones
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__target_clones__) || __GNUC__ >= 6)
|
||||
|
@ -679,10 +524,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Defines function with prologue that fixes misaligned stack.
|
||||
* @see nocallersavedregisters and consider assembly
|
||||
*/
|
||||
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 408 || \
|
||||
__has_attribute(__force_align_arg_pointer__)
|
||||
#define forcealignargpointer __attribute__((__force_align_arg_pointer__))
|
||||
|
@ -690,12 +531,6 @@ typedef uint64_t uintmax_t;
|
|||
#define forcealignargpointer "need modern compiler"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares prototype as never returning NULL.
|
||||
*
|
||||
* This is checked at compile-time for constexprs. It'll be checked at
|
||||
* runtime too by synthetic code, only in MODE=dbg mode.
|
||||
*/
|
||||
#ifndef returnsnonnull
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__returns_nonnull__) || \
|
||||
|
@ -706,13 +541,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Attests return value is aligned.
|
||||
*
|
||||
* @param (alignment)
|
||||
* @param (alignment, misalignment)
|
||||
* @see attributeallocalign(), returnspointerwithnoaliases, mallocesque
|
||||
*/
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__assume_aligned__) || \
|
||||
(__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 409)
|
||||
|
@ -721,10 +549,6 @@ typedef uint64_t uintmax_t;
|
|||
#define returnsaligned(x)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Declares prototype as behaving similar to malloc().
|
||||
* @see attributeallocsize(), attributeallocalign()
|
||||
*/
|
||||
#ifndef returnspointerwithnoaliases
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__has_attribute(__malloc__) || \
|
||||
|
@ -757,20 +581,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Defines variable as having same type as right-hand side.
|
||||
*
|
||||
* This enables safe, backwards-compatible, non-explosive macros, e.g.:
|
||||
*
|
||||
* #define bar(FOO) \
|
||||
* ({ \
|
||||
* autotype(FOO) Foo = (FOO); \
|
||||
* Foo + Foo * 2; \
|
||||
* })
|
||||
*
|
||||
* @param x must be identical to rhs
|
||||
* @note typeof goes back to gcc 2.x
|
||||
*/
|
||||
#if __cplusplus + 0 >= 201103L
|
||||
#define autotype(x) auto
|
||||
#elif ((__has_builtin(auto_type) || defined(__llvm__) || \
|
||||
|
@ -781,27 +591,16 @@ typedef uint64_t uintmax_t;
|
|||
#define autotype(x) typeof(x)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Defines interrupt handler that can call non-interrupt functions.
|
||||
* @see forcealignargpointer, -mgeneral-regs-only and consider assembly
|
||||
*/
|
||||
#if __GNUC__ >= 7 || __has_attribute(__no_caller_saved_registers__)
|
||||
#define nocallersavedregisters __attribute__((__no_caller_saved_registers__))
|
||||
#else
|
||||
#define nocallersavedregisters "need modern compiler"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Attests that execution of statement is impossible.
|
||||
*/
|
||||
#ifndef unreachable
|
||||
#define unreachable __builtin_unreachable()
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Statement that does nothing.
|
||||
* @note can help avoid drama w/ linters, warnings, formatters, etc.
|
||||
*/
|
||||
#define donothing \
|
||||
do { \
|
||||
} while (0)
|
||||
|
@ -857,9 +656,6 @@ typedef uint64_t uintmax_t;
|
|||
#define initarray _Section(".init_array")
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Systemic suppressions.
|
||||
*/
|
||||
#ifndef __STRICT_ANSI__
|
||||
#if defined(__GNUC__) || defined(__llvm__)
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare" /* lint needs to change */
|
||||
|
@ -908,17 +704,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif /* !GCC && LLVM */
|
||||
#endif /* ANSI */
|
||||
|
||||
/**
|
||||
* Elevate warnings of material consequence.
|
||||
*
|
||||
* These aren't stylistic in nature; but are perfectly fine to disable,
|
||||
* assuming we're ok with the compiler simply generating a runtime crash
|
||||
* instead. Otherwise what usually happens with these is that a weakness
|
||||
* is introduced, important optimizations can't be performed; or worst
|
||||
* of all: the code will need patching if ported to a toy or any machine
|
||||
* designed by an engineer who hadn't understood John von Neumann at the
|
||||
* time, e.g. 1's complement, big endian, under 32bit word size, etc.
|
||||
*/
|
||||
#ifndef __W__
|
||||
#ifndef __STRICT_ANSI__
|
||||
#if defined(__GNUC__) || defined(__llvm__)
|
||||
|
@ -965,10 +750,6 @@ typedef uint64_t uintmax_t;
|
|||
#endif /* ANSI */
|
||||
#endif /* -w */
|
||||
|
||||
/**
|
||||
* Sets manual breakpoint.
|
||||
* @see showcrashreports() for auto gdb attach
|
||||
*/
|
||||
#define DebugBreak() asm("int3")
|
||||
|
||||
#define VEIL(CONSTRAINT, EXPRESSION) \
|
||||
|
@ -991,10 +772,6 @@ typedef uint64_t uintmax_t;
|
|||
0; \
|
||||
})
|
||||
|
||||
/**
|
||||
* Pulls another module, by symbol, into linkage.
|
||||
* @note nop is discarded by ape/ape.lds
|
||||
*/
|
||||
#define YOINK(SYMBOL) \
|
||||
do { \
|
||||
_Static_assert(!__builtin_types_compatible_p(typeof(SYMBOL), char[]), \
|
||||
|
@ -1006,29 +783,15 @@ typedef uint64_t uintmax_t;
|
|||
: "X"(SYMBOL)); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Pulls another module into linkage from top-level scope.
|
||||
* @note nop is discarded by ape/ape.lds
|
||||
*/
|
||||
#define STATIC_YOINK(SYMBOLSTR) \
|
||||
asm(".pushsection .yoink\n\tnop\t\"" SYMBOLSTR "\"\n\t.popsection")
|
||||
|
||||
/**
|
||||
* Pulls source file into ZIP portion of binary.
|
||||
* @see build/rules.mk which defines the wildcard build rule %.zip.o
|
||||
*/
|
||||
#if !defined(IM_FEELING_NAUGHTY) && !defined(__STRICT_ANSI__)
|
||||
#define STATIC_YOINK_SOURCE(PATH) STATIC_YOINK(PATH)
|
||||
#else
|
||||
#define STATIC_YOINK_SOURCE(PATH)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Pulls source of object being compiled into zip.
|
||||
* @note automates most compliance with gpl terms
|
||||
* @see libc/zipos/zipcentraldir.S
|
||||
* @see ape/ape.lds
|
||||
*/
|
||||
#ifdef __BASE_FILE__
|
||||
STATIC_YOINK_SOURCE(__BASE_FILE__);
|
||||
#endif
|
||||
|
|
|
@ -67,4 +67,4 @@
|
|||
#define __INT_FAST64_TYPE__ __INT64_TYPE__
|
||||
#define __UINT_FAST64_TYPE__ __UINT64_TYPE__
|
||||
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
/**
|
||||
* @fileoverview Cosmopolitan Preprocessor / Language Normalization.
|
||||
*
|
||||
* This is our lowest-level header file. You don't need to include it,
|
||||
* since we require that compilers be configured to do so automatically,
|
||||
* and the -include flag is the safest bet. Further note polyfills here
|
||||
* shouldn't be taken as indicators of intent to support.
|
||||
*/
|
||||
|
||||
#define __COSMOPOLITAN__ 1
|
||||
|
||||
#ifndef __COUNTER__
|
||||
|
|
|
@ -289,11 +289,6 @@ void sincosl(long double, long double *, long double *);
|
|||
│ cosmopolitan § mathematics » x87 ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Asks FPU to push well-known numbers to its own stack.
|
||||
*/
|
||||
#endif
|
||||
#define fldz() __X87_CONST(fldz, 0x0p+0)
|
||||
#define fld1() __X87_CONST(fld1, 0x8p-3)
|
||||
#define fldpi() __X87_CONST(fldpi, M_PI)
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
* @see xasprintf() for a better API
|
||||
*/
|
||||
int(vasprintf)(char **strp, const char *fmt, va_list va) {
|
||||
/**
|
||||
/*
|
||||
* This implementation guarantees the smallest possible allocation,
|
||||
* using an optimistic approach w/o changing asymptotic complexity.
|
||||
*/
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
/ @asyncsignalsafe
|
||||
ffs: .leafprologue
|
||||
.profilable
|
||||
bsf %edi,%eax
|
||||
or $-1,%edx
|
||||
bsf %edi,%eax
|
||||
cmovz %edx,%eax
|
||||
inc %eax
|
||||
.leafepilogue
|
||||
|
|
|
@ -3,9 +3,29 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/*
|
||||
* BIT SCANNING 101
|
||||
* ctz(𝑥) 31^clz(𝑥) clz(𝑥)
|
||||
* uint32 𝑥 bsf(𝑥) tzcnt(𝑥) ffs(𝑥) bsr(𝑥) lzcnt(𝑥)
|
||||
* 0x00000000 wut 32 0 wut 32
|
||||
* 0x00000001 0 0 1 0 31
|
||||
* 0x80000001 0 0 1 31 0
|
||||
* 0x80000000 31 31 32 31 0
|
||||
* 0x00000010 4 4 5 4 27
|
||||
* 0x08000010 4 4 5 27 4
|
||||
* 0x08000000 27 27 28 27 4
|
||||
* 0xffffffff 0 0 1 31 0
|
||||
*/
|
||||
|
||||
int ffs(int) pureconst;
|
||||
int ffsl(long int) pureconst;
|
||||
int ffsll(long long int) pureconst;
|
||||
int ffsl(long) pureconst;
|
||||
int ffsll(long long) pureconst;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define ffs(u) __builtin_ffs(u)
|
||||
#define ffsl(u) __builtin_ffsl(u)
|
||||
#define ffsll(u) __builtin_ffsll(u)
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
/ @asyncsignalsafe
|
||||
ffsl: .leafprologue
|
||||
.profilable
|
||||
bsf %rdi,%rax
|
||||
or $-1,%edx
|
||||
bsf %rdi,%rax
|
||||
cmovz %edx,%eax
|
||||
inc %eax
|
||||
.leafepilogue
|
||||
|
|
|
@ -29,13 +29,6 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/**
|
||||
* Globally precomputed x86 CPUID values.
|
||||
*
|
||||
* @note Referencing Is Initialization (RII)
|
||||
* @note Protected with PIRO
|
||||
* @see X86_HAVE()
|
||||
*/
|
||||
extern const unsigned kCpuids[KCPUIDS_LEN][4];
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#define COSMOPOLITAN_LIBC_NT_THUNK_MSABI_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \
|
||||
(__has_attribute(__ms_abi__) || defined(__llvm__)))
|
||||
/**
|
||||
* Defines function as using Microsoft x64 calling convention.
|
||||
*
|
||||
|
@ -9,13 +12,10 @@
|
|||
* generate code that calls MS ABI functions directly, without needing
|
||||
* to jump through the assembly thunks.
|
||||
*/
|
||||
#if !defined(__STRICT_ANSI__) && \
|
||||
(__GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \
|
||||
(__has_attribute(__ms_abi__) || defined(__llvm__)))
|
||||
#define __msabi __attribute__((__ms_abi__))
|
||||
#endif
|
||||
|
||||
/**
|
||||
/*
|
||||
* Returns true if header should provide MS-ABI overrides.
|
||||
*/
|
||||
#ifndef ShouldUseMsabiAttribute
|
||||
|
|
|
@ -35,7 +35,7 @@ nodebuginfo uint32_t(rand32)(void) {
|
|||
} else {
|
||||
devrand(&res, sizeof(res));
|
||||
}
|
||||
hidden extern uint32_t g_rando32;
|
||||
extern uint32_t g_rando32 hidden;
|
||||
res ^= MarsagliaXorshift32(&g_rando32);
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -42,9 +42,9 @@
|
|||
/**
|
||||
* Beseeches system for page-table entries.
|
||||
*
|
||||
* char *p = mmap(NULL, 65536, PROT_READ | PROT_WRITE,
|
||||
* MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
* munmap(p, 65536);
|
||||
* char *p = mmap(NULL, 65536, PROT_READ | PROT_WRITE,
|
||||
* MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
* munmap(p, 65536);
|
||||
*
|
||||
* @param addr optionally requests a particular virtual base address,
|
||||
* which needs to be 64kb aligned if passed (for NT compatibility)
|
||||
|
|
|
@ -157,29 +157,29 @@ static textwindows wontreturn void WinMainNew(void) {
|
|||
* The Cosmopolitan Runtime provides the following services, which aim
|
||||
* to bring Windows NT behavior closer in harmony with System Five:
|
||||
*
|
||||
* 1. We configure CMD.EXE for UTF-8 and enable ANSI colors on Win10.
|
||||
* 1. We configure CMD.EXE for UTF-8 and enable ANSI colors on Win10.
|
||||
*
|
||||
* 2. Command line arguments are passed as a blob of UTF-16 text. We
|
||||
* chop them up into an char *argv[] UTF-8 data structure, in
|
||||
* accordance with the DOS conventions for argument quoting.
|
||||
* 2. Command line arguments are passed as a blob of UTF-16 text. We
|
||||
* chop them up into an char *argv[] UTF-8 data structure, in
|
||||
* accordance with the DOS conventions for argument quoting.
|
||||
*
|
||||
* 3. Environment variables are passed to us as a sorted UTF-16 double
|
||||
* NUL terminated list. We translate this to char ** using UTF-8.
|
||||
* 3. Environment variables are passed to us as a sorted UTF-16 double
|
||||
* NUL terminated list. We translate this to char ** using UTF-8.
|
||||
*
|
||||
* 4. Allocates new stack at a high address. NT likes to choose a
|
||||
* stack address that's beneath the program image. We want to be
|
||||
* able to assume that stack addresses are located at higher
|
||||
* addresses than heap and program memory.
|
||||
* 4. Allocates new stack at a high address. NT likes to choose a
|
||||
* stack address that's beneath the program image. We want to be
|
||||
* able to assume that stack addresses are located at higher
|
||||
* addresses than heap and program memory.
|
||||
*
|
||||
* 5. Windows users are afraid of "drive-by downloads" where someone
|
||||
* might accidentally an evil DLL to their Downloads folder which
|
||||
* then overrides the behavior of a legitimate EXE being run from
|
||||
* the downloads folder. Since we don't even use dynamic linking,
|
||||
* we've cargo culted some API calls, that may harden against it.
|
||||
* 5. Windows users are afraid of "drive-by downloads" where someone
|
||||
* might accidentally an evil DLL to their Downloads folder which
|
||||
* then overrides the behavior of a legitimate EXE being run from
|
||||
* the downloads folder. Since we don't even use dynamic linking,
|
||||
* we've cargo culted some API calls, that may harden against it.
|
||||
*
|
||||
* 6. Finally, we need fork. Microsoft designed Windows to prevent us
|
||||
* from having fork() so we pass pipe handles in an environment
|
||||
* variable literally copy all the memory.
|
||||
* 6. Finally, we need fork. Microsoft designed Windows to prevent us
|
||||
* from having fork() so we pass pipe handles in an environment
|
||||
* variable literally copy all the memory.
|
||||
*
|
||||
* @param hInstance call GetModuleHandle(NULL) from main if you need it
|
||||
*/
|
||||
|
|
|
@ -22,7 +22,11 @@
|
|||
|
||||
STATIC_YOINK("_init_g_stderr");
|
||||
|
||||
/**
|
||||
* Pointer to standard error stream.
|
||||
*/
|
||||
FILE *stderr;
|
||||
|
||||
hidden FILE g_stderr;
|
||||
hidden unsigned char g_stderr_buf[BUFSIZ] forcealign(PAGESIZE);
|
||||
|
||||
|
|
|
@ -22,7 +22,11 @@
|
|||
|
||||
STATIC_YOINK("_init_g_stdin");
|
||||
|
||||
/**
|
||||
* Pointer to standard input stream.
|
||||
*/
|
||||
FILE *stdin;
|
||||
|
||||
hidden FILE g_stdin;
|
||||
hidden unsigned char g_stdin_buf[BUFSIZ] forcealign(PAGESIZE);
|
||||
|
||||
|
|
|
@ -25,7 +25,11 @@
|
|||
|
||||
STATIC_YOINK("_init_g_stdout");
|
||||
|
||||
/**
|
||||
* Pointer to standard output stream.
|
||||
*/
|
||||
FILE *stdout;
|
||||
|
||||
hidden FILE g_stdout;
|
||||
hidden unsigned char g_stdout_buf[BUFSIZ] forcealign(PAGESIZE);
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is lower, alpha, or digit.
|
||||
*/
|
||||
int isalnum(int c) {
|
||||
return ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') ||
|
||||
('a' <= c && c <= 'z');
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is upper or lower.
|
||||
*/
|
||||
int isalpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is ascii.
|
||||
*/
|
||||
int isascii(int c) {
|
||||
return 0x00 <= c && c <= 0x7F;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is space or tab.
|
||||
*/
|
||||
int isblank(int c) {
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is C0 ASCII control code or DEL.
|
||||
*/
|
||||
int iscntrl(int c) {
|
||||
return (0x00 <= c && c <= 0x1F) || c == 0x7F;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is decimal digit.
|
||||
*/
|
||||
int isdigit(int c) {
|
||||
return '0' <= c && c <= '9';
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is lowercase alpha ascii character.
|
||||
*/
|
||||
int islower(int c) {
|
||||
return 'a' <= c && c <= 'z';
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns true if c is space, \t, \r, \n, \f, or \v.
|
||||
*/
|
||||
int isspace(int c) {
|
||||
return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' ||
|
||||
c == '\v';
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if c is uppercase alpha ascii character.
|
||||
*/
|
||||
int isupper(int c) {
|
||||
return 'A' <= c && c <= 'Z';
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns nonzero if wc is C0 or C1 control code.
|
||||
*/
|
||||
int iswcntrl(wint_t wc) {
|
||||
return (0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Returns true if c is hexadecimal digit.
|
||||
*/
|
||||
int isxdigit(int c) {
|
||||
return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') ||
|
||||
('a' <= c && c <= 'f');
|
||||
|
|
|
@ -28,17 +28,17 @@
|
|||
*
|
||||
* For example, strictly:
|
||||
*
|
||||
* char buf[16];
|
||||
* CHECK_NOTNULL(memccpy(buf, s, '\0', sizeof(buf)));
|
||||
* char buf[16];
|
||||
* CHECK_NOTNULL(memccpy(buf, s, '\0', sizeof(buf)));
|
||||
*
|
||||
* Or unstrictly:
|
||||
*
|
||||
* if (!memccpy(buf, s, '\0', sizeof(buf))) strcpy(buf, "?");
|
||||
* if (!memccpy(buf, s, '\0', sizeof(buf))) strcpy(buf, "?");
|
||||
*
|
||||
* Are usually more sensible than the following:
|
||||
*
|
||||
* char cstrbuf[16];
|
||||
* snprintf(cstrbuf, sizeof(cstrbuf), "%s", CSTR);
|
||||
* char cstrbuf[16];
|
||||
* snprintf(cstrbuf, sizeof(cstrbuf), "%s", CSTR);
|
||||
*
|
||||
* @return 𝑑 + idx(𝑐) + 1, or NULL if 𝑐 ∉ 𝑠₀․․ₙ₋₁
|
||||
* @note 𝑑 and 𝑠 can't overlap
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* Prepares static search buffer.
|
||||
*
|
||||
* 1. If SRC is too long, it's truncated and *not* NUL-terminated.
|
||||
*
|
||||
* 2. If SRC is too short, the remainder is zero-filled.
|
||||
*
|
||||
* Please note this function isn't designed to prevent untrustworthy
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* Prepares static search buffer.
|
||||
*
|
||||
* 1. If SRC is too long, it's truncated and *not* NUL-terminated.
|
||||
*
|
||||
* 2. If SRC is too short, the remainder is zero-filled.
|
||||
*
|
||||
* Please note this function isn't designed to prevent untrustworthy
|
||||
|
|
|
@ -46,7 +46,7 @@ wontreturn void testlib_abort(void) {
|
|||
*/
|
||||
testonly void testlib_runtestcases(testfn_t *start, testfn_t *end,
|
||||
testfn_t warmup) {
|
||||
/**
|
||||
/*
|
||||
* getpid() calls are inserted to help visually see tests in traces
|
||||
* which can be performed on Linux, FreeBSD, OpenBSD, and XNU:
|
||||
*
|
||||
|
|
|
@ -370,13 +370,13 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format,
|
|||
/**
|
||||
* Converts time to string, e.g.
|
||||
*
|
||||
* char b[64];
|
||||
* int64_t sec;
|
||||
* struct tm tm;
|
||||
* time(&sec);
|
||||
* localtime_r(&sec, &tm);
|
||||
* strftime(b, sizeof(b), "%Y-%m-%dT%H:%M:%S%z", &tm); // ISO8601
|
||||
* strftime(b, sizeof(b), "%a, %d %b %Y %H:%M:%S %Z", &tm); // RFC1123
|
||||
* char b[64];
|
||||
* int64_t sec;
|
||||
* struct tm tm;
|
||||
* time(&sec);
|
||||
* localtime_r(&sec, &tm);
|
||||
* strftime(b, sizeof(b), "%Y-%m-%dT%H:%M:%S%z", &tm); // ISO8601
|
||||
* strftime(b, sizeof(b), "%a, %d %b %Y %H:%M:%S %Z", &tm); // RFC1123
|
||||
*
|
||||
* @return bytes copied excluding nul, or 0 on error
|
||||
*/
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
* Returns monospace display width of UTF-8 string.
|
||||
*
|
||||
* - Control codes are discounted
|
||||
*
|
||||
* - ANSI escape sequences are discounted
|
||||
*
|
||||
* - East asian glyphs, emoji, etc. count as two
|
||||
*
|
||||
* @param s is NUL-terminated string
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
/**
|
||||
* Decodes human-readable CP437 glyphs into binary, e.g.
|
||||
*
|
||||
* char binged[5];
|
||||
* char golden[5] = "\0\1\2\3\4";
|
||||
* unbingbuf(binged, sizeof(binged), u" ☺☻♥♦", -1);
|
||||
* CHECK_EQ(0, memcmp(binged, golden, 5));
|
||||
* char binged[5];
|
||||
* char golden[5] = "\0\1\2\3\4";
|
||||
* unbingbuf(binged, sizeof(binged), u" ☺☻♥♦", -1);
|
||||
* CHECK_EQ(0, memcmp(binged, golden, 5));
|
||||
*
|
||||
* @param buf is caller owned
|
||||
* @param size is byte length of buf
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
/**
|
||||
* Decodes human-readable CP437 glyphs into binary, e.g.
|
||||
*
|
||||
* CHECK_EQ(0, memcmp(gc(unbingstr(u" ☺☻♥♦")), "\0\1\2\3\4", 5));
|
||||
* CHECK_EQ(0, memcmp(gc(unbingstr(u" ☺☻♥♦")), "\0\1\2\3\4", 5));
|
||||
*
|
||||
* @param buf is caller owned
|
||||
* @param size is byte length of buf
|
||||
|
|
|
@ -24,12 +24,12 @@
|
|||
/**
|
||||
* Joins paths, e.g.
|
||||
*
|
||||
* "a" + "b" → "a/b"
|
||||
* "a/" + "b" → "a/b"
|
||||
* "a" + "b/" → "a/b/"
|
||||
* "a" + "/b" → "/b"
|
||||
* "." + "b" → "b"
|
||||
* "" + "b" → "b"
|
||||
* "a" + "b" → "a/b"
|
||||
* "a/" + "b" → "a/b"
|
||||
* "a" + "b/" → "a/b/"
|
||||
* "a" + "/b" → "/b"
|
||||
* "." + "b" → "b"
|
||||
* "" + "b" → "b"
|
||||
*
|
||||
* @return newly allocated string of resulting path
|
||||
*/
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
/**
|
||||
* Installs handler for kernel interrupt, e.g.:
|
||||
*
|
||||
* onctrlc(sig) { exit(128+sig); }
|
||||
* CHECK_NE(-1, xsigaction(SIGINT, onctrlc, SA_RESETHAND, 0, 0));
|
||||
* onctrlc(sig) { exit(128+sig); }
|
||||
* CHECK_NE(-1, xsigaction(SIGINT, onctrlc, SA_RESETHAND, 0, 0));
|
||||
*
|
||||
* @param sig can be SIGINT, SIGTERM, etc.
|
||||
* @param handler is SIG_DFL, SIG_IGN, or a pointer to a 0≤arity≤3
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ This program is free software; you can redistribute it and/or modify │
|
||||
│ it under the terms of the GNU General Public License as published by │
|
||||
│ the Free Software Foundation; version 2 of the License. │
|
||||
│ │
|
||||
│ This program is distributed in the hope that it will be useful, but │
|
||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||||
│ General Public License for more details. │
|
||||
│ │
|
||||
│ You should have received a copy of the GNU General Public License │
|
||||
│ along with this program; if not, write to the Free Software │
|
||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/nexgen32e/ffs.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
TEST(ffs, test) {
|
||||
EXPECT_EQ(__builtin_ffs(0), (ffs)(0));
|
||||
EXPECT_EQ(__builtin_ffs(1), (ffs)(1));
|
||||
EXPECT_EQ(__builtin_ffs(0x00100000), (ffs)(0x00100000));
|
||||
EXPECT_EQ(__builtin_ffs(-1), (ffs)(-1));
|
||||
}
|
||||
|
||||
TEST(ffsl, test) {
|
||||
EXPECT_EQ(__builtin_ffsl(0), (ffsl)(0));
|
||||
EXPECT_EQ(__builtin_ffsl(1), (ffsl)(1));
|
||||
EXPECT_EQ(__builtin_ffsl(0x00100000), (ffsl)(0x00100000));
|
||||
EXPECT_EQ(__builtin_ffsl(0x0010000000000000), (ffsl)(0x0010000000000000));
|
||||
EXPECT_EQ(__builtin_ffsl(-1), (ffsl)(-1));
|
||||
}
|
||||
|
||||
TEST(ffsll, test) {
|
||||
EXPECT_EQ(__builtin_ffsll(0), (ffsll)(0));
|
||||
EXPECT_EQ(__builtin_ffsll(1), (ffsll)(1));
|
||||
EXPECT_EQ(__builtin_ffsll(0x00100000), (ffsll)(0x00100000));
|
||||
EXPECT_EQ(__builtin_ffsll(0x0010000000000000), (ffsll)(0x0010000000000000));
|
||||
EXPECT_EQ(__builtin_ffsll(-1), (ffsll)(-1));
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ This program is free software; you can redistribute it and/or modify │
|
||||
│ it under the terms of the GNU General Public License as published by │
|
||||
│ the Free Software Foundation; version 2 of the License. │
|
||||
│ │
|
||||
│ This program is distributed in the hope that it will be useful, but │
|
||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||||
│ General Public License for more details. │
|
||||
│ │
|
||||
│ You should have received a copy of the GNU General Public License │
|
||||
│ along with this program; if not, write to the Free Software │
|
||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "tool/build/lib/javadown.h"
|
||||
|
||||
TEST(ParseJavadown, testOneLiner) {
|
||||
const char *comment = "/** Parses javadown. */";
|
||||
struct Javadown *jd;
|
||||
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
|
||||
EXPECT_FALSE(jd->isfileoverview);
|
||||
EXPECT_STREQ("Parses javadown.", jd->title);
|
||||
EXPECT_STREQ("", jd->text);
|
||||
EXPECT_EQ(0, jd->tags.n);
|
||||
FreeJavadown(jd);
|
||||
}
|
||||
|
||||
TEST(ParseJavadown, testShortAndTerse) {
|
||||
const char *comment = "\
|
||||
/**\n\
|
||||
* Parses javadown.\n\
|
||||
* @see love\n\
|
||||
*/";
|
||||
const char *title = "Parses javadown and that is the bottom line.";
|
||||
struct Javadown *jd;
|
||||
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
|
||||
EXPECT_FALSE(jd->isfileoverview);
|
||||
EXPECT_STREQ("Parses javadown.", jd->title);
|
||||
EXPECT_STREQ("", jd->text);
|
||||
EXPECT_EQ(1, jd->tags.n);
|
||||
EXPECT_STREQ("see", jd->tags.p[0].tag);
|
||||
EXPECT_STREQ("love", jd->tags.p[0].text);
|
||||
FreeJavadown(jd);
|
||||
}
|
||||
|
||||
TEST(ParseJavadown, testBlankLineOmitted) {
|
||||
const char *comment = "\
|
||||
/**\n\
|
||||
* Parses javadown.\n\
|
||||
*\n\
|
||||
* Description.\n\
|
||||
* @see love\n\
|
||||
*/";
|
||||
const char *title = "Parses javadown and that is the bottom line.";
|
||||
struct Javadown *jd;
|
||||
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
|
||||
EXPECT_FALSE(jd->isfileoverview);
|
||||
EXPECT_STREQ("Parses javadown.", jd->title);
|
||||
EXPECT_STREQ("Description.", jd->text);
|
||||
EXPECT_EQ(1, jd->tags.n);
|
||||
EXPECT_STREQ("see", jd->tags.p[0].tag);
|
||||
EXPECT_STREQ("love", jd->tags.p[0].text);
|
||||
FreeJavadown(jd);
|
||||
}
|
||||
|
||||
TEST(ParseJavadown, testContentInterpretation) {
|
||||
const char *comment = "\
|
||||
/**\n\
|
||||
* Parses javadown \n\
|
||||
* and that is the bottom line.\n\
|
||||
*\n\
|
||||
* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
|
||||
* eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim \n\
|
||||
* ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
|
||||
* aliquip ex ea commodo consequat.\n\
|
||||
*\n\
|
||||
* Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
|
||||
* dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
|
||||
* non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
|
||||
* laborum\n\
|
||||
*\n\
|
||||
* @param data should point to text inside the slash star markers\n\
|
||||
* lorem ipsum dolla dollaz yo\n\
|
||||
* @param size is length of data in bytes\n\
|
||||
* @return object that should be passed to FreeJavadown()\n\
|
||||
* @asyncsignalsafe\n\
|
||||
*/";
|
||||
const char *title = "Parses javadown and that is the bottom line.";
|
||||
const char *description = "\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim\n\
|
||||
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
|
||||
aliquip ex ea commodo consequat.\n\
|
||||
\n\
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
|
||||
non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
|
||||
laborum\n";
|
||||
struct Javadown *jd;
|
||||
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
|
||||
EXPECT_FALSE(jd->isfileoverview);
|
||||
EXPECT_STREQ(title, jd->title);
|
||||
EXPECT_STREQ(description, jd->text);
|
||||
EXPECT_EQ(4, jd->tags.n);
|
||||
EXPECT_STREQ("param", jd->tags.p[0].tag);
|
||||
EXPECT_STREQ("data should point to text inside the slash star markers\n"
|
||||
"lorem ipsum dolla dollaz yo",
|
||||
jd->tags.p[0].text);
|
||||
EXPECT_STREQ("param", jd->tags.p[1].tag);
|
||||
EXPECT_STREQ("size is length of data in bytes", jd->tags.p[1].text);
|
||||
EXPECT_STREQ("return", jd->tags.p[2].tag);
|
||||
EXPECT_STREQ("object that should be passed to FreeJavadown()",
|
||||
jd->tags.p[2].text);
|
||||
EXPECT_STREQ("asyncsignalsafe", jd->tags.p[3].tag);
|
||||
FreeJavadown(jd);
|
||||
}
|
||||
|
||||
TEST(ParseJavadown, testTabFormatting1) {
|
||||
const char *comment = "\
|
||||
/**\n\
|
||||
\tParses javadown \n\
|
||||
\tand that is the bottom line.\n\
|
||||
\n\
|
||||
\tLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
|
||||
\teiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim \n\
|
||||
\tad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
|
||||
\taliquip ex ea commodo consequat.\n\
|
||||
\n\
|
||||
\tDuis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
|
||||
\tdolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
|
||||
\tnon proident, sunt in culpa qui officia deserunt mollit anim id est\n\
|
||||
\tlaborum\n\
|
||||
\n\
|
||||
\t@param data should point to text inside the slash star markers\n\
|
||||
\t\tlorem ipsum dolla dollaz yo\n\
|
||||
\t@param size is length of data in bytes\n\
|
||||
\t@return object that should be passed to FreeJavadown()\n\
|
||||
\t@asyncsignalsafe\n\
|
||||
*/";
|
||||
const char *title = "Parses javadown and that is the bottom line.";
|
||||
const char *description = "\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim\n\
|
||||
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
|
||||
aliquip ex ea commodo consequat.\n\
|
||||
\n\
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
|
||||
non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
|
||||
laborum\n";
|
||||
struct Javadown *jd;
|
||||
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
|
||||
EXPECT_FALSE(jd->isfileoverview);
|
||||
EXPECT_STREQ(title, jd->title);
|
||||
EXPECT_STREQ(description, jd->text);
|
||||
EXPECT_EQ(4, jd->tags.n);
|
||||
EXPECT_STREQ("param", jd->tags.p[0].tag);
|
||||
EXPECT_STREQ("data should point to text inside the slash star markers\n"
|
||||
"lorem ipsum dolla dollaz yo",
|
||||
jd->tags.p[0].text);
|
||||
EXPECT_STREQ("param", jd->tags.p[1].tag);
|
||||
EXPECT_STREQ("size is length of data in bytes", jd->tags.p[1].text);
|
||||
EXPECT_STREQ("return", jd->tags.p[2].tag);
|
||||
EXPECT_STREQ("object that should be passed to FreeJavadown()",
|
||||
jd->tags.p[2].text);
|
||||
EXPECT_STREQ("asyncsignalsafe", jd->tags.p[3].tag);
|
||||
FreeJavadown(jd);
|
||||
}
|
||||
|
||||
TEST(ParseJavadown, testTabFormatting2) {
|
||||
const char *comment = "\
|
||||
/**\n\
|
||||
\tParses javadown \n\
|
||||
\tand that is the bottom line.\n\
|
||||
\t\n\
|
||||
\tLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
|
||||
\t\teiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim \n\
|
||||
\t\tad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
|
||||
\t\taliquip ex ea commodo consequat.\n\
|
||||
\t\n\
|
||||
\tDuis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
|
||||
\tdolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
|
||||
\tnon proident, sunt in culpa qui officia deserunt mollit anim id est\n\
|
||||
\tlaborum\n\
|
||||
\t\n\
|
||||
\t@param data should point to text inside the slash star markers\n\
|
||||
\t\tlorem ipsum dolla dollaz yo\n\
|
||||
\t@param size is length of data in bytes\n\
|
||||
\t@return object that should be passed to FreeJavadown()\n\
|
||||
\t@asyncsignalsafe\n\
|
||||
*/";
|
||||
const char *title = "Parses javadown and that is the bottom line.";
|
||||
const char *description = "\
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do\n\
|
||||
\teiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim\n\
|
||||
\tad minim veniam, quis nostrud exercitation ullamco laboris nisi ut\n\
|
||||
\taliquip ex ea commodo consequat.\n\
|
||||
\n\
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum\n\
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat\n\
|
||||
non proident, sunt in culpa qui officia deserunt mollit anim id est\n\
|
||||
laborum\n";
|
||||
struct Javadown *jd;
|
||||
jd = ParseJavadown(comment + 3, strlen(comment) - 3 - 2);
|
||||
EXPECT_FALSE(jd->isfileoverview);
|
||||
EXPECT_STREQ(title, jd->title);
|
||||
EXPECT_STREQ(description, jd->text);
|
||||
EXPECT_EQ(4, jd->tags.n);
|
||||
EXPECT_STREQ("param", jd->tags.p[0].tag);
|
||||
EXPECT_STREQ("data should point to text inside the slash star markers\n"
|
||||
"lorem ipsum dolla dollaz yo",
|
||||
jd->tags.p[0].text);
|
||||
EXPECT_STREQ("param", jd->tags.p[1].tag);
|
||||
EXPECT_STREQ("size is length of data in bytes", jd->tags.p[1].text);
|
||||
EXPECT_STREQ("return", jd->tags.p[2].tag);
|
||||
EXPECT_STREQ("object that should be passed to FreeJavadown()",
|
||||
jd->tags.p[2].text);
|
||||
EXPECT_STREQ("asyncsignalsafe", jd->tags.p[3].tag);
|
||||
FreeJavadown(jd);
|
||||
}
|
|
@ -16,18 +16,22 @@ local enhancements
|
|||
- support __builtin_constant_p, __builtin_likely, etc.
|
||||
- support __builtin_isunordered, __builtin_islessgreater, etc.
|
||||
- support __builtin_ctz, __builtin_bswap, __builtin_popcount, etc.
|
||||
- support __force_align_arg_pointer__, __no_caller_saved_registers__, etc.
|
||||
- support __constructor__, __section__, __cold__, -ffunction-sections, etc.
|
||||
- support building -x assembler-with-cpp a.k.a. .S files
|
||||
- support profiling w/ -mcount / -mfentry / -mnop-mcount
|
||||
- improve error messages to trace macro expansions
|
||||
- reduce #lines of generated assembly by a third
|
||||
- reduce #bytes of generated binary by a third
|
||||
- report divide errors in constexprs
|
||||
|
||||
local bug fixes
|
||||
|
||||
- allow casted values to be lvalues
|
||||
- permit remainder operator in constexprs
|
||||
- permit parentheses around string-initializer
|
||||
- fix 64-bit bug in generated code for struct bitfields
|
||||
- fix struct_designator() so it won't crash on anonymous union members
|
||||
- fix bug where last statement in statement expression couldn't have label
|
||||
- print_tokens (chibicc -E) now works in the case of adjacent string literals
|
||||
- make enums unsigned (like gcc) so we don't suffer the msvc enum bitfield bug
|
||||
|
@ -35,6 +39,8 @@ local bug fixes
|
|||
local changes
|
||||
|
||||
- use tabs in generated output
|
||||
- parse javadoc-style markdown comments
|
||||
- don't fold backslash newline in comments
|
||||
- generated code no longer assumes red zone
|
||||
- emit .size directives for function definitions
|
||||
- use fisttp long double conversions if built w/ -msse3
|
||||
|
|
|
@ -177,8 +177,8 @@ struct As {
|
|||
struct Sauces {
|
||||
unsigned long n;
|
||||
struct Sauce {
|
||||
int path; // strings
|
||||
int line; // 1-indexed
|
||||
unsigned path; // strings
|
||||
unsigned line; // 1-indexed
|
||||
} * p;
|
||||
} sauces;
|
||||
struct Things {
|
||||
|
@ -192,14 +192,14 @@ struct As {
|
|||
TT_FORWARD,
|
||||
TT_BACKWARD,
|
||||
} t : 4;
|
||||
int s : 28; // sauces
|
||||
int i; // identity,ints,floats,slices
|
||||
unsigned s : 28; // sauces
|
||||
unsigned i; // identity,ints,floats,slices
|
||||
} * p;
|
||||
} things;
|
||||
struct Sections {
|
||||
unsigned long n;
|
||||
struct Section {
|
||||
int name; // strings
|
||||
unsigned name; // strings
|
||||
int flags;
|
||||
int type;
|
||||
int align;
|
||||
|
@ -210,11 +210,11 @@ struct As {
|
|||
unsigned long n;
|
||||
struct Symbol {
|
||||
bool isused;
|
||||
int name; // slices
|
||||
int section; // sections
|
||||
int stb; // STB_*
|
||||
int stv; // STV_*
|
||||
int type; // STT_*
|
||||
unsigned char stb; // STB_*
|
||||
unsigned char stv; // STV_*
|
||||
unsigned char type; // STT_*
|
||||
unsigned name; // slices
|
||||
unsigned section; // sections
|
||||
long offset;
|
||||
long size;
|
||||
struct ElfWriterSymRef ref;
|
||||
|
@ -223,25 +223,25 @@ struct As {
|
|||
struct HashTable {
|
||||
unsigned i, n;
|
||||
struct HashEntry {
|
||||
int h;
|
||||
int i;
|
||||
unsigned h;
|
||||
unsigned i;
|
||||
} * p;
|
||||
} symbolindex;
|
||||
struct Labels {
|
||||
unsigned long n;
|
||||
struct Label {
|
||||
int id;
|
||||
int tok; // things
|
||||
int symbol; // symbols
|
||||
unsigned id;
|
||||
unsigned tok; // things
|
||||
unsigned symbol; // symbols
|
||||
} * p;
|
||||
} labels;
|
||||
struct Relas {
|
||||
unsigned long n;
|
||||
struct Rela {
|
||||
bool isdead;
|
||||
int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...}
|
||||
int expr; // exprs
|
||||
int section; // sections
|
||||
int kind; // R_X86_64_{16,32,64,PC8,PC32,PLT32,GOTPCRELX,...}
|
||||
unsigned expr; // exprs
|
||||
unsigned section; // sections
|
||||
long offset;
|
||||
long addend;
|
||||
} * p;
|
||||
|
@ -251,7 +251,7 @@ struct As {
|
|||
struct Expr {
|
||||
enum ExprKind {
|
||||
EX_INT, // integer
|
||||
EX_SYM, // slice, forward, backward, then symbol
|
||||
EX_SYM, // things (then symbols after eval)
|
||||
EX_NEG, // unary -
|
||||
EX_NOT, // unary !
|
||||
EX_BITNOT, // unary ~
|
||||
|
@ -276,7 +276,7 @@ struct As {
|
|||
EM_DTPOFF,
|
||||
EM_TPOFF,
|
||||
} em;
|
||||
int tok;
|
||||
unsigned tok;
|
||||
int lhs;
|
||||
int rhs;
|
||||
long x;
|
||||
|
@ -456,7 +456,7 @@ static bool EndsWith(const char *s, const char *suffix) {
|
|||
n = strlen(s);
|
||||
m = strlen(suffix);
|
||||
if (m > n) return false;
|
||||
return memcmp(s + n - m, suffix, m) == 0;
|
||||
return !memcmp(s + n - m, suffix, m);
|
||||
}
|
||||
|
||||
static char *Format(const char *fmt, ...) {
|
||||
|
@ -1192,21 +1192,21 @@ static int ParseMul(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '*')) {
|
||||
y = ParseUnary(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x *= a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_MUL, x, y);
|
||||
}
|
||||
} else if (IsPunct(a, i, '/')) {
|
||||
y = ParseUnary(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x /= a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_DIV, x, y);
|
||||
}
|
||||
} else if (IsPunct(a, i, '%')) {
|
||||
y = ParseUnary(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x %= a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_REM, x, y);
|
||||
|
@ -1225,14 +1225,14 @@ static int ParseAdd(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '+')) {
|
||||
y = ParseMul(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x += a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_ADD, x, y);
|
||||
}
|
||||
} else if (IsPunct(a, i, '-')) {
|
||||
y = ParseMul(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x -= a->exprs.p[y].x;
|
||||
} else if (a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[y].x = -a->exprs.p[y].x;
|
||||
|
@ -1254,14 +1254,14 @@ static int ParseShift(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '<' << 8 | '<')) {
|
||||
y = ParseAdd(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x <<= a->exprs.p[y].x & 63;
|
||||
} else {
|
||||
x = NewBinary(a, EX_SHL, x, y);
|
||||
}
|
||||
} else if (IsPunct(a, i, '>' << 8 | '>')) {
|
||||
y = ParseAdd(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x >>= a->exprs.p[y].x & 63;
|
||||
} else {
|
||||
x = NewBinary(a, EX_SHR, x, y);
|
||||
|
@ -1280,28 +1280,28 @@ static int ParseRelational(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '<')) {
|
||||
y = ParseShift(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x = a->exprs.p[x].x < a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_LT, x, y);
|
||||
}
|
||||
} else if (IsPunct(a, i, '>')) {
|
||||
y = ParseShift(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x = a->exprs.p[y].x < a->exprs.p[x].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_LT, y, x);
|
||||
}
|
||||
} else if (IsPunct(a, i, '<' << 8 | '=')) {
|
||||
y = ParseShift(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x = a->exprs.p[x].x <= a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_LE, x, y);
|
||||
}
|
||||
} else if (IsPunct(a, i, '>' << 8 | '=')) {
|
||||
y = ParseShift(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x = a->exprs.p[y].x <= a->exprs.p[x].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_LE, y, x);
|
||||
|
@ -1320,14 +1320,14 @@ static int ParseEquality(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '=' << 8 | '=')) {
|
||||
y = ParseRelational(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x = a->exprs.p[x].x == a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_EQ, x, y);
|
||||
}
|
||||
} else if (IsPunct(a, i, '!' << 8 | '=')) {
|
||||
y = ParseRelational(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x = a->exprs.p[x].x != a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_NE, x, y);
|
||||
|
@ -1346,7 +1346,7 @@ static int ParseAnd(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '&')) {
|
||||
y = ParseEquality(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x &= a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_AND, x, y);
|
||||
|
@ -1365,7 +1365,7 @@ static int ParseXor(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '^')) {
|
||||
y = ParseAnd(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x ^= a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_XOR, x, y);
|
||||
|
@ -1384,7 +1384,7 @@ static int ParseOr(struct As *a, int *rest, int i) {
|
|||
for (;;) {
|
||||
if (IsPunct(a, i, '|')) {
|
||||
y = ParseXor(a, &i, i + 1);
|
||||
if (a->exprs.p[x].kind == EX_INT || a->exprs.p[y].kind == EX_INT) {
|
||||
if (a->exprs.p[x].kind == EX_INT && a->exprs.p[y].kind == EX_INT) {
|
||||
a->exprs.p[x].x |= a->exprs.p[y].x;
|
||||
} else {
|
||||
x = NewBinary(a, EX_OR, x, y);
|
||||
|
@ -3745,8 +3745,7 @@ static int ResolveSymbol(struct As *a, int i) {
|
|||
case TT_FORWARD:
|
||||
return FindLabelForward(a, a->ints.p[a->things.p[i].i]);
|
||||
default:
|
||||
DebugBreak();
|
||||
Fail(a, "this corruption");
|
||||
Fail(a, "this corruption %d", a->things.p[i].t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3777,15 +3776,6 @@ static void Write32(char b[4], int x) {
|
|||
b[3] = x >> 030;
|
||||
}
|
||||
|
||||
static void MarkUndefinedSymbolsGlobal(struct As *a) {
|
||||
int i;
|
||||
for (i = 0; i < a->symbols.n; ++i) {
|
||||
if (!a->symbols.p[i].section && a->symbols.p[i].stb == STB_LOCAL) {
|
||||
a->symbols.p[i].stb = STB_GLOBAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MarkUsedSymbols(struct As *a, int i) {
|
||||
if (i == -1) return;
|
||||
MarkUsedSymbols(a, a->exprs.p[i].lhs);
|
||||
|
@ -3818,6 +3808,16 @@ static void Evaluate(struct As *a) {
|
|||
}
|
||||
}
|
||||
|
||||
static void MarkUndefinedSymbolsGlobal(struct As *a) {
|
||||
int i;
|
||||
for (i = 0; i < a->symbols.n; ++i) {
|
||||
if (a->symbols.p[i].isused && !a->symbols.p[i].section &&
|
||||
a->symbols.p[i].stb == STB_LOCAL) {
|
||||
a->symbols.p[i].stb = STB_GLOBAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsLocal(struct As *a, int name) {
|
||||
if (name < 0) return true;
|
||||
return a->slices.p[name].n >= 2 && !memcmp(a->slices.p[name].p, ".L", 2);
|
||||
|
@ -3829,17 +3829,18 @@ static bool IsLiveSymbol(struct As *a, int i) {
|
|||
}
|
||||
|
||||
static void Objectify(struct As *a, int path) {
|
||||
int i, j, s;
|
||||
char *p;
|
||||
int i, j, s, e;
|
||||
struct ElfWriter *elf;
|
||||
elf = elfwriter_open(a->strings.p[path], 0644);
|
||||
for (i = 0; i < a->symbols.n; ++i) {
|
||||
if (!IsLiveSymbol(a, i)) continue;
|
||||
p = strndup(a->slices.p[a->symbols.p[i].name].p,
|
||||
a->slices.p[a->symbols.p[i].name].n);
|
||||
a->symbols.p[i].ref = elfwriter_appendsym(
|
||||
elf,
|
||||
strndup(a->slices.p[a->symbols.p[i].name].p,
|
||||
a->slices.p[a->symbols.p[i].name].n),
|
||||
ELF64_ST_INFO(a->symbols.p[i].stb, a->symbols.p[i].type),
|
||||
elf, p, ELF64_ST_INFO(a->symbols.p[i].stb, a->symbols.p[i].type),
|
||||
a->symbols.p[i].stv, a->symbols.p[i].offset, a->symbols.p[i].size);
|
||||
free(p);
|
||||
}
|
||||
for (i = 0; i < a->sections.n; ++i) {
|
||||
elfwriter_align(elf, a->sections.p[i].align, 0);
|
||||
|
@ -3853,24 +3854,24 @@ static void Objectify(struct As *a, int path) {
|
|||
for (j = 0; j < a->relas.n; ++j) {
|
||||
if (a->relas.p[j].isdead) continue;
|
||||
if (a->relas.p[j].section != i) continue;
|
||||
a->i = a->exprs.p[a->relas.p[j].expr].tok;
|
||||
switch (a->exprs.p[a->relas.p[j].expr].kind) {
|
||||
e = a->relas.p[j].expr;
|
||||
a->i = a->exprs.p[e].tok;
|
||||
switch (a->exprs.p[e].kind) {
|
||||
case EX_INT:
|
||||
break;
|
||||
case EX_SYM:
|
||||
elfwriter_appendrela(
|
||||
elf, a->relas.p[j].offset,
|
||||
a->symbols.p[a->exprs.p[a->relas.p[j].expr].x].ref,
|
||||
a->relas.p[j].kind, a->relas.p[j].addend);
|
||||
elfwriter_appendrela(elf, a->relas.p[j].offset,
|
||||
a->symbols.p[a->exprs.p[e].x].ref,
|
||||
a->relas.p[j].kind, a->relas.p[j].addend);
|
||||
break;
|
||||
case EX_ADD:
|
||||
if (a->exprs.p[a->exprs.p[j].lhs].kind == EX_SYM &&
|
||||
a->exprs.p[a->exprs.p[j].rhs].kind == EX_INT) {
|
||||
if (a->exprs.p[a->exprs.p[e].lhs].kind == EX_SYM &&
|
||||
a->exprs.p[a->exprs.p[e].rhs].kind == EX_INT) {
|
||||
elfwriter_appendrela(
|
||||
elf, a->relas.p[j].offset,
|
||||
a->symbols.p[a->exprs.p[a->exprs.p[j].lhs].x].ref,
|
||||
a->symbols.p[a->exprs.p[a->exprs.p[e].lhs].x].ref,
|
||||
a->relas.p[j].kind,
|
||||
a->relas.p[j].addend + a->exprs.p[a->exprs.p[j].rhs].x);
|
||||
a->relas.p[j].addend + a->exprs.p[a->exprs.p[e].rhs].x);
|
||||
} else {
|
||||
Fail(a, "bad addend");
|
||||
}
|
||||
|
@ -3887,6 +3888,58 @@ static void Objectify(struct As *a, int path) {
|
|||
elfwriter_close(elf);
|
||||
}
|
||||
|
||||
static void CheckIntegrity(struct As *a) {
|
||||
int i;
|
||||
for (i = 0; i < a->things.n; ++i) {
|
||||
CHECK_LT((int)a->things.p[i].s, a->sauces.n);
|
||||
switch (a->things.p[i].t) {
|
||||
case TT_INT:
|
||||
case TT_FORWARD:
|
||||
case TT_BACKWARD:
|
||||
CHECK_LT(a->things.p[i].i, a->ints.n);
|
||||
break;
|
||||
case TT_FLOAT:
|
||||
CHECK_LT(a->things.p[i].i, a->floats.n);
|
||||
break;
|
||||
case TT_SLICE:
|
||||
CHECK_LT(a->things.p[i].i, a->slices.n);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < a->sections.n; ++i) {
|
||||
CHECK_LT(a->sections.p[i].name, a->strings.n);
|
||||
}
|
||||
for (i = 0; i < a->symbols.n; ++i) {
|
||||
CHECK_LT(a->symbols.p[i].name, a->slices.n);
|
||||
CHECK_LT(a->symbols.p[i].section, a->sections.n);
|
||||
}
|
||||
for (i = 0; i < a->labels.n; ++i) {
|
||||
CHECK_LT(a->labels.p[i].tok, a->things.n);
|
||||
CHECK_LT(a->labels.p[i].symbol, a->symbols.n);
|
||||
}
|
||||
for (i = 0; i < a->relas.n; ++i) {
|
||||
CHECK_LT(a->relas.p[i].expr, a->exprs.n);
|
||||
CHECK_LT(a->relas.p[i].section, a->sections.n);
|
||||
}
|
||||
for (i = 0; i < a->exprs.n; ++i) {
|
||||
CHECK_LT(a->exprs.p[i].tok, a->things.n);
|
||||
if (a->exprs.p[i].lhs != -1) CHECK_LT(a->exprs.p[i].lhs, a->exprs.n);
|
||||
if (a->exprs.p[i].rhs != -1) CHECK_LT(a->exprs.p[i].rhs, a->exprs.n);
|
||||
switch (a->exprs.p[i].kind) {
|
||||
case EX_SYM:
|
||||
CHECK_LT(a->exprs.p[i].x, a->things.n);
|
||||
CHECK(a->things.p[a->exprs.p[i].x].t == TT_SLICE ||
|
||||
a->things.p[a->exprs.p[i].x].t == TT_FORWARD ||
|
||||
a->things.p[a->exprs.p[i].x].t == TT_BACKWARD);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintThings(struct As *a) {
|
||||
int i;
|
||||
char pbuf[4], fbuf[32];
|
||||
|
@ -3929,17 +3982,18 @@ void Assembler(int argc, char *argv[]) {
|
|||
Tokenize(a, a->inpath);
|
||||
/* PrintThings(a); */
|
||||
Assemble(a);
|
||||
/* CheckIntegrity(a); */
|
||||
Evaluate(a);
|
||||
MarkUndefinedSymbolsGlobal(a);
|
||||
Objectify(a, a->outpath);
|
||||
malloc_stats();
|
||||
/* malloc_stats(); */
|
||||
FreeAssembler(a);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
showcrashreports();
|
||||
if (argc == 1) {
|
||||
system("o//third_party/chibicc/as.com -o /tmp/o third_party/chibicc/hog.s");
|
||||
system("o//third_party/chibicc/as.com -o /tmp/o /home/jart/trash/hog.s");
|
||||
system("objdump -xwd /tmp/o");
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ bool opt_verbose;
|
|||
|
||||
static bool opt_A;
|
||||
static bool opt_E;
|
||||
static bool opt_J;
|
||||
static bool opt_M;
|
||||
static bool opt_MD;
|
||||
static bool opt_MMD;
|
||||
|
@ -202,6 +203,8 @@ static void parse_args(int argc, char **argv) {
|
|||
opt_c = true;
|
||||
} else if (!strcmp(argv[i], "-E")) {
|
||||
opt_E = true;
|
||||
} else if (!strcmp(argv[i], "-J")) {
|
||||
opt_J = true;
|
||||
} else if (!strcmp(argv[i], "-A")) {
|
||||
opt_A = true;
|
||||
} else if (!strcmp(argv[i], "-I")) {
|
||||
|
@ -364,7 +367,14 @@ static char *create_tmpfile(void) {
|
|||
return path;
|
||||
}
|
||||
|
||||
static void run_subprocess(char **argv) {
|
||||
static void handle_exit(bool ok) {
|
||||
if (!ok) {
|
||||
opt_save_temps = true;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static bool run_subprocess(char **argv) {
|
||||
// If -### is given, dump the subprocess's command line.
|
||||
if (opt_hash_hash_hash) {
|
||||
fprintf(stderr, "%s", argv[0]);
|
||||
|
@ -384,13 +394,10 @@ static void run_subprocess(char **argv) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (status != 0) {
|
||||
opt_save_temps = true;
|
||||
exit(1);
|
||||
}
|
||||
return !status;
|
||||
}
|
||||
|
||||
static void run_cc1(int argc, char **argv, char *input, char *output) {
|
||||
static bool run_cc1(int argc, char **argv, char *input, char *output) {
|
||||
char **args = calloc(argc + 10, sizeof(char *));
|
||||
memcpy(args, argv, argc * sizeof(char *));
|
||||
args[argc++] = "-cc1";
|
||||
|
@ -402,7 +409,7 @@ static void run_cc1(int argc, char **argv, char *input, char *output) {
|
|||
args[argc++] = "-cc1-output";
|
||||
args[argc++] = output;
|
||||
}
|
||||
run_subprocess(args);
|
||||
return run_subprocess(args);
|
||||
}
|
||||
|
||||
static void print_token(FILE *out, Token *tok) {
|
||||
|
@ -540,6 +547,10 @@ static void cc1(void) {
|
|||
print_ast(stdout, prog);
|
||||
return;
|
||||
}
|
||||
if (opt_J) {
|
||||
output_javadown(output_file, prog);
|
||||
return;
|
||||
}
|
||||
FILE *out = open_file(output_file);
|
||||
codegen(prog, out);
|
||||
fclose(out);
|
||||
|
@ -561,7 +572,7 @@ static void assemble(char *input, char *output) {
|
|||
strarray_push(&arr, "-o");
|
||||
strarray_push(&arr, output);
|
||||
strarray_push(&arr, NULL);
|
||||
run_subprocess(arr.data);
|
||||
handle_exit(run_subprocess(arr.data));
|
||||
}
|
||||
|
||||
static void run_linker(StringArray *inputs, char *output) {
|
||||
|
@ -591,7 +602,7 @@ static void run_linker(StringArray *inputs, char *output) {
|
|||
strarray_push(&arr, inputs->data[i]);
|
||||
}
|
||||
strarray_push(&arr, NULL);
|
||||
run_subprocess(arr.data);
|
||||
handle_exit(run_subprocess(arr.data));
|
||||
}
|
||||
|
||||
int chibicc(int argc, char **argv) {
|
||||
|
@ -608,6 +619,7 @@ int chibicc(int argc, char **argv) {
|
|||
error("cannot specify '-o' with '-c,' '-S' or '-E' with multiple files");
|
||||
}
|
||||
StringArray ld_args = {};
|
||||
StringArray dox_args = {};
|
||||
for (int i = 0; i < input_paths.len; i++) {
|
||||
char *input = input_paths.data[i];
|
||||
if (!strncmp(input, "-l", 2)) {
|
||||
|
@ -647,25 +659,33 @@ int chibicc(int argc, char **argv) {
|
|||
assert(type == FILE_C || type == FILE_ASM_CPP);
|
||||
// Just preprocess
|
||||
if (opt_E || opt_M) {
|
||||
run_cc1(argc, argv, input, NULL);
|
||||
handle_exit(run_cc1(argc, argv, input, NULL));
|
||||
continue;
|
||||
}
|
||||
// Compile
|
||||
if (opt_S) {
|
||||
run_cc1(argc, argv, input, output);
|
||||
handle_exit(run_cc1(argc, argv, input, output));
|
||||
continue;
|
||||
}
|
||||
// Compile and assemble
|
||||
if (opt_c) {
|
||||
char *tmp = create_tmpfile();
|
||||
run_cc1(argc, argv, input, tmp);
|
||||
handle_exit(run_cc1(argc, argv, input, tmp));
|
||||
assemble(tmp, output);
|
||||
continue;
|
||||
}
|
||||
// Dox
|
||||
if (opt_J) {
|
||||
char *tmp = create_tmpfile();
|
||||
if (run_cc1(argc, argv, input, tmp)) {
|
||||
strarray_push(&dox_args, tmp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Compile, assemble and link
|
||||
char *tmp1 = create_tmpfile();
|
||||
char *tmp2 = create_tmpfile();
|
||||
run_cc1(argc, argv, input, tmp1);
|
||||
handle_exit(run_cc1(argc, argv, input, tmp1));
|
||||
assemble(tmp1, tmp2);
|
||||
strarray_push(&ld_args, tmp2);
|
||||
continue;
|
||||
|
@ -673,5 +693,8 @@ int chibicc(int argc, char **argv) {
|
|||
if (ld_args.len > 0) {
|
||||
run_linker(&ld_args, opt_o ? opt_o : "a.out");
|
||||
}
|
||||
if (dox_args.len > 0) {
|
||||
drop_dox(&dox_args, opt_o ? opt_o : "/dev/stdout");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "libc/unicode/unicode.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/gdtoa/gdtoa.h"
|
||||
#include "tool/build/lib/javadown.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
|
@ -35,7 +36,10 @@ typedef struct Asm Asm;
|
|||
typedef struct AsmOperand AsmOperand;
|
||||
typedef struct File File;
|
||||
typedef struct FpClassify FpClassify;
|
||||
typedef struct HashMap HashMap;
|
||||
typedef struct Hideset Hideset;
|
||||
typedef struct Macro Macro;
|
||||
typedef struct MacroParam MacroParam;
|
||||
typedef struct Member Member;
|
||||
typedef struct Node Node;
|
||||
typedef struct Obj Obj;
|
||||
|
@ -47,6 +51,8 @@ typedef struct Token Token;
|
|||
typedef struct TokenStack TokenStack;
|
||||
typedef struct Type Type;
|
||||
|
||||
typedef Token *macro_handler_fn(Token *);
|
||||
|
||||
//
|
||||
// strarray.c
|
||||
//
|
||||
|
@ -64,13 +70,14 @@ void strarray_push(StringArray *, char *);
|
|||
//
|
||||
|
||||
typedef enum {
|
||||
TK_IDENT, // Identifiers
|
||||
TK_PUNCT, // Punctuators
|
||||
TK_KEYWORD, // Keywords
|
||||
TK_STR, // String literals
|
||||
TK_NUM, // Numeric literals
|
||||
TK_PP_NUM, // Preprocessing numbers
|
||||
TK_EOF, // End-of-file markers
|
||||
TK_IDENT, // Identifiers
|
||||
TK_PUNCT, // Punctuators
|
||||
TK_KEYWORD, // Keywords
|
||||
TK_STR, // String literals
|
||||
TK_NUM, // Numeric literals
|
||||
TK_PP_NUM, // Preprocessing numbers
|
||||
TK_JAVADOWN, // /** ... */ comments
|
||||
TK_EOF, // End-of-file markers
|
||||
} TokenKind;
|
||||
|
||||
struct File {
|
||||
|
@ -80,6 +87,7 @@ struct File {
|
|||
// For #line directive
|
||||
char *display_name;
|
||||
int line_delta;
|
||||
struct Javadown *javadown;
|
||||
};
|
||||
|
||||
struct thatispacked Token {
|
||||
|
@ -96,6 +104,7 @@ struct thatispacked Token {
|
|||
char *filename; // Filename
|
||||
Hideset *hideset; // For macro expansion
|
||||
Token *origin; // If this is expanded from a macro, the original token
|
||||
struct Javadown *javadown;
|
||||
union {
|
||||
int64_t val; // If kind is TK_NUM, its value
|
||||
long double fval; // If kind is TK_NUM, its value
|
||||
|
@ -134,6 +143,23 @@ int read_escaped_char(char **, char *);
|
|||
// preprocess.c
|
||||
//
|
||||
|
||||
struct MacroParam {
|
||||
MacroParam *next;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct Macro {
|
||||
char *name;
|
||||
bool is_objlike; // Object-like or function-like
|
||||
MacroParam *params;
|
||||
char *va_args_name;
|
||||
Token *body;
|
||||
macro_handler_fn *handler;
|
||||
Token *javadown;
|
||||
};
|
||||
|
||||
extern HashMap macros;
|
||||
|
||||
char *search_include_paths(char *);
|
||||
void init_macros(void);
|
||||
void define_macro(char *, char *);
|
||||
|
@ -232,6 +258,7 @@ struct Obj {
|
|||
char *asmname;
|
||||
char *section;
|
||||
char *visibility;
|
||||
Token *javadown;
|
||||
// Global variable
|
||||
bool is_tentative;
|
||||
bool is_string_literal;
|
||||
|
@ -244,6 +271,9 @@ struct Obj {
|
|||
bool is_noreturn;
|
||||
bool is_destructor;
|
||||
bool is_constructor;
|
||||
bool is_ms_abi; /* TODO */
|
||||
bool is_force_align_arg_pointer;
|
||||
bool is_no_caller_saved_registers;
|
||||
int stack_size;
|
||||
Obj *params;
|
||||
Node *body;
|
||||
|
@ -419,6 +449,7 @@ struct Type {
|
|||
int align; // alignment
|
||||
bool is_unsigned; // unsigned or signed
|
||||
bool is_atomic; // true if _Atomic
|
||||
bool is_ms_abi; // microsoft abi
|
||||
Type *origin; // for type compatibility check
|
||||
// Pointer-to or array-of type. We intentionally use the same member
|
||||
// to represent pointer/array duality in C.
|
||||
|
@ -534,11 +565,11 @@ typedef struct {
|
|||
void *val;
|
||||
} HashEntry;
|
||||
|
||||
typedef struct {
|
||||
struct HashMap {
|
||||
HashEntry *buckets;
|
||||
int capacity;
|
||||
int used;
|
||||
} HashMap;
|
||||
};
|
||||
|
||||
void *hashmap_get(HashMap *, char *);
|
||||
void *hashmap_get2(HashMap *, char *, int);
|
||||
|
@ -584,6 +615,13 @@ Token *alloc_token(void);
|
|||
Obj *alloc_obj(void);
|
||||
Type *alloc_type(void);
|
||||
|
||||
//
|
||||
// javadown.c
|
||||
//
|
||||
|
||||
void output_javadown(const char *, Obj *);
|
||||
void drop_dox(const StringArray *, const char *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_CHIBICC_CHIBICC_H_ */
|
||||
|
|
|
@ -49,6 +49,7 @@ THIRD_PARTY_CHIBICC_A_CHECKS = \
|
|||
$(THIRD_PARTY_CHIBICC_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
THIRD_PARTY_CHIBICC_A_DIRECTDEPS = \
|
||||
LIBC_ALG \
|
||||
LIBC_BITS \
|
||||
LIBC_CALLS \
|
||||
LIBC_CALLS_HEFTY \
|
||||
|
|
|
@ -2265,7 +2265,7 @@ static void emit_data(Obj *prog) {
|
|||
}
|
||||
}
|
||||
|
||||
static void store_fp(int r, int offset, int sz) {
|
||||
static void store_fp(Obj *fn, int r, int offset, int sz) {
|
||||
switch (sz) {
|
||||
case 4:
|
||||
println("\tmovss\t%%xmm%d,%d(%%rbp)", r, offset);
|
||||
|
@ -2274,7 +2274,8 @@ static void store_fp(int r, int offset, int sz) {
|
|||
println("\tmovsd\t%%xmm%d,%d(%%rbp)", r, offset);
|
||||
return;
|
||||
case 16:
|
||||
println("\tmovaps\t%%xmm%d,%d(%%rbp)", r, offset);
|
||||
println("\t%s\t%%xmm%d,%d(%%rbp)",
|
||||
fn->is_force_align_arg_pointer ? "movups" : "movaps", r, offset);
|
||||
return;
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -2381,13 +2382,13 @@ static void emit_text(Obj *prog) {
|
|||
case TY_UNION:
|
||||
assert(ty->size <= 16);
|
||||
if (has_flonum(ty, 0, 8, 0)) {
|
||||
store_fp(fp++, var->offset, MIN(8, ty->size));
|
||||
store_fp(fn, fp++, var->offset, MIN(8, ty->size));
|
||||
} else {
|
||||
store_gp(gp++, var->offset, MIN(8, ty->size));
|
||||
}
|
||||
if (ty->size > 8) {
|
||||
if (has_flonum(ty, 8, 16, 0)) {
|
||||
store_fp(fp++, var->offset + 8, ty->size - 8);
|
||||
store_fp(fn, fp++, var->offset + 8, ty->size - 8);
|
||||
} else {
|
||||
store_gp(gp++, var->offset + 8, ty->size - 8);
|
||||
}
|
||||
|
@ -2395,7 +2396,7 @@ static void emit_text(Obj *prog) {
|
|||
break;
|
||||
case TY_FLOAT:
|
||||
case TY_DOUBLE:
|
||||
store_fp(fp++, var->offset, ty->size);
|
||||
store_fp(fn, fp++, var->offset, ty->size);
|
||||
break;
|
||||
case TY_INT128:
|
||||
store_gp(gp++, var->offset + 0, 8);
|
||||
|
@ -2405,6 +2406,20 @@ static void emit_text(Obj *prog) {
|
|||
store_gp(gp++, var->offset, ty->size);
|
||||
}
|
||||
}
|
||||
if (fn->is_force_align_arg_pointer) {
|
||||
emitlin("\tand\t$-16,%rsp");
|
||||
}
|
||||
if (fn->is_no_caller_saved_registers) {
|
||||
emitlin("\
|
||||
\tpush\t%rdi\n\
|
||||
\tpush\t%rsi\n\
|
||||
\tpush\t%rdx\n\
|
||||
\tpush\t%rcx\n\
|
||||
\tpush\t%r8\n\
|
||||
\tpush\t%r9\n\
|
||||
\tpush\t%r10\n\
|
||||
\tpush\t%r11");
|
||||
}
|
||||
// Emit code
|
||||
gen_stmt(fn->body);
|
||||
assert(!depth);
|
||||
|
@ -2420,6 +2435,17 @@ static void emit_text(Obj *prog) {
|
|||
if (fn->is_noreturn) {
|
||||
emitlin("\tud2");
|
||||
} else {
|
||||
if (fn->is_no_caller_saved_registers) {
|
||||
emitlin("\
|
||||
\tpop\t%r11\n\
|
||||
\tpop\t%r10\n\
|
||||
\tpop\t%r9\n\
|
||||
\tpop\t%r8\n\
|
||||
\tpop\t%rcx\n\
|
||||
\tpop\t%rdx\n\
|
||||
\tpop\t%rsi\n\
|
||||
\tpop\t%rdi");
|
||||
}
|
||||
emitlin("\tleave");
|
||||
emitlin("\tret");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
/*-*- 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/runtime/gc.h"
|
||||
#include "third_party/chibicc/chibicc.h"
|
||||
|
||||
#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p))
|
||||
|
||||
struct DoxWriter {
|
||||
struct Buffer {
|
||||
size_t n;
|
||||
char *p;
|
||||
} buf;
|
||||
struct Macros {
|
||||
size_t n;
|
||||
Macro **p;
|
||||
} macros;
|
||||
struct Objects {
|
||||
size_t n;
|
||||
Obj **p;
|
||||
} objects;
|
||||
};
|
||||
|
||||
static void SerializeData(struct Buffer *buf, const void *p, unsigned long n) {
|
||||
struct Slice *s;
|
||||
buf->p = realloc(buf->p, buf->n + n);
|
||||
memcpy(buf->p + buf->n, p, n);
|
||||
buf->n += n;
|
||||
}
|
||||
|
||||
static int SerializeInt(struct Buffer *buf, int x) {
|
||||
unsigned char b[4];
|
||||
b[0] = x >> 000;
|
||||
b[1] = x >> 010;
|
||||
b[2] = x >> 020;
|
||||
b[3] = x >> 030;
|
||||
SerializeData(buf, b, 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void SerializeStr(struct Buffer *buf, const char *s) {
|
||||
size_t n;
|
||||
if (!s) s = "";
|
||||
n = strlen(s);
|
||||
n = MIN(INT_MAX, n);
|
||||
SerializeInt(buf, n);
|
||||
SerializeData(buf, s, n);
|
||||
}
|
||||
|
||||
static void SerializeJavadown(struct Buffer *buf, struct Javadown *jd) {
|
||||
int i;
|
||||
SerializeInt(buf, jd->isfileoverview);
|
||||
SerializeStr(buf, jd->title);
|
||||
SerializeStr(buf, jd->text);
|
||||
SerializeInt(buf, jd->tags.n);
|
||||
for (i = 0; i < jd->tags.n; ++i) {
|
||||
SerializeStr(buf, jd->tags.p[i].tag);
|
||||
SerializeStr(buf, jd->tags.p[i].text);
|
||||
}
|
||||
}
|
||||
|
||||
static char *DescribeScalar(struct Type *ty, char *name) {
|
||||
return xasprintf("%s%s%s", ty->is_atomic ? "_Atomic " : "",
|
||||
ty->is_unsigned ? "unsigned " : "", name);
|
||||
}
|
||||
|
||||
static char *DescribeType(struct Type *ty) {
|
||||
switch (ty->kind) {
|
||||
case TY_VOID:
|
||||
return strdup("void");
|
||||
case TY_BOOL:
|
||||
return strdup("_Bool");
|
||||
case TY_CHAR:
|
||||
return DescribeScalar(ty, "char");
|
||||
case TY_SHORT:
|
||||
return DescribeScalar(ty, "short");
|
||||
case TY_INT:
|
||||
return DescribeScalar(ty, "int");
|
||||
case TY_LONG:
|
||||
return DescribeScalar(ty, "long");
|
||||
case TY_INT128:
|
||||
return DescribeScalar(ty, "__int128");
|
||||
case TY_FLOAT:
|
||||
return DescribeScalar(ty, "float");
|
||||
case TY_DOUBLE:
|
||||
return DescribeScalar(ty, "double");
|
||||
case TY_LDOUBLE:
|
||||
return DescribeScalar(ty, "long double");
|
||||
case TY_PTR:
|
||||
return xasprintf("%s*", gc(DescribeType(ty->base)));
|
||||
case TY_ARRAY:
|
||||
return xasprintf("%s[%d]", gc(DescribeType(ty->base)), ty->array_len);
|
||||
case TY_ENUM:
|
||||
if (ty->name_pos) {
|
||||
return xasprintf("enum %.*s", ty->name_pos->len, ty->name_pos->loc);
|
||||
} else {
|
||||
return strdup("ANONYMOUS-ENUM");
|
||||
}
|
||||
case TY_STRUCT:
|
||||
if (ty->name_pos) {
|
||||
return xasprintf("struct %.*s", ty->name_pos->len, ty->name_pos->loc);
|
||||
} else {
|
||||
return strdup("ANONYMOUS-STRUCT");
|
||||
}
|
||||
case TY_UNION:
|
||||
if (ty->name_pos) {
|
||||
return xasprintf("union %.*s", ty->name_pos->len, ty->name_pos->loc);
|
||||
} else {
|
||||
return strdup("ANONYMOUS-UNION");
|
||||
}
|
||||
case TY_FUNC:
|
||||
return xasprintf("%s(*)()", gc(DescribeType(ty->return_ty)));
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static int CountParams(Obj *params) {
|
||||
int n;
|
||||
for (n = 0; params; params = params->next) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static const char *GetFileName(Obj *obj) {
|
||||
if (obj->javadown && obj->javadown->file) return obj->javadown->file->name;
|
||||
if (obj->tok && obj->tok->file) return obj->tok->file->name;
|
||||
return "missingno.c";
|
||||
}
|
||||
|
||||
static int GetLine(Obj *obj) {
|
||||
if (obj->javadown && obj->javadown->file) return obj->javadown->line_no;
|
||||
if (obj->tok && obj->tok->file) return obj->tok->line_no;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SerializeDox(struct DoxWriter *dox, Obj *prog) {
|
||||
int i;
|
||||
char *s;
|
||||
Obj *param;
|
||||
MacroParam *mparam;
|
||||
SerializeInt(&dox->buf, dox->objects.n);
|
||||
for (i = 0; i < dox->objects.n; ++i) {
|
||||
s = DescribeType(dox->objects.p[i]->ty);
|
||||
SerializeStr(&dox->buf, s);
|
||||
free(s);
|
||||
SerializeStr(&dox->buf, dox->objects.p[i]->name);
|
||||
SerializeStr(&dox->buf, GetFileName(dox->objects.p[i]));
|
||||
SerializeInt(&dox->buf, GetLine(dox->objects.p[i]));
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_function);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_weak);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_inline);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_noreturn);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_destructor);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_constructor);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_force_align_arg_pointer);
|
||||
SerializeInt(&dox->buf, dox->objects.p[i]->is_no_caller_saved_registers);
|
||||
SerializeStr(&dox->buf, dox->objects.p[i]->visibility);
|
||||
SerializeJavadown(&dox->buf, dox->objects.p[i]->javadown->javadown);
|
||||
SerializeInt(&dox->buf, CountParams(dox->objects.p[i]->params));
|
||||
for (param = dox->objects.p[i]->params; param; param = param->next) {
|
||||
s = DescribeType(param->ty);
|
||||
SerializeStr(&dox->buf, s);
|
||||
free(s);
|
||||
SerializeStr(&dox->buf, param->name);
|
||||
}
|
||||
}
|
||||
SerializeInt(&dox->buf, dox->macros.n);
|
||||
for (i = 0; i < dox->macros.n; ++i) {
|
||||
SerializeStr(&dox->buf, dox->macros.p[i]->name);
|
||||
SerializeStr(&dox->buf, dox->macros.p[i]->javadown->file->name);
|
||||
SerializeInt(&dox->buf, dox->macros.p[i]->javadown->line_no);
|
||||
SerializeJavadown(&dox->buf, dox->macros.p[i]->javadown->javadown);
|
||||
}
|
||||
SerializeInt(&dox->buf, 31337);
|
||||
}
|
||||
|
||||
static void LoadPublicDefinitions(struct DoxWriter *dox, Obj *prog) {
|
||||
int i;
|
||||
Obj *obj;
|
||||
Macro *macro;
|
||||
for (obj = prog; obj; obj = obj->next) {
|
||||
if (obj->is_static) continue;
|
||||
if (*obj->name == '_') continue;
|
||||
if (!obj->javadown) continue;
|
||||
if (obj->is_string_literal) continue;
|
||||
if (obj->visibility && !strcmp(obj->visibility, "hidden")) continue;
|
||||
if (strchr(obj->name, '$')) continue;
|
||||
APPEND(dox->objects);
|
||||
dox->objects.p[dox->objects.n - 1] = obj;
|
||||
}
|
||||
for (i = 0; i < macros.capacity; ++i) {
|
||||
if (!macros.buckets[i].key) continue;
|
||||
if (macros.buckets[i].key == (char *)-1) continue;
|
||||
macro = macros.buckets[i].val;
|
||||
if (!macro->javadown) continue;
|
||||
if (!macro->javadown->javadown) continue;
|
||||
if (*macro->name == '_') continue;
|
||||
if (strchr(macro->name, '$')) continue;
|
||||
APPEND(dox->macros);
|
||||
dox->macros.p[dox->macros.n - 1] = macro;
|
||||
}
|
||||
}
|
||||
|
||||
static struct DoxWriter *NewDoxWriter(void) {
|
||||
return calloc(1, sizeof(struct DoxWriter));
|
||||
}
|
||||
|
||||
static void FreeDoxWriter(struct DoxWriter *dox) {
|
||||
if (dox) {
|
||||
free(dox->buf.p);
|
||||
free(dox->macros.p);
|
||||
free(dox->objects.p);
|
||||
free(dox);
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteDox(struct DoxWriter *dox, const char *path) {
|
||||
int fd;
|
||||
CHECK_NE(-1, (fd = creat(path, 0644)));
|
||||
CHECK_EQ(dox->buf.n, write(fd, dox->buf.p, dox->buf.n));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits documentation datum for compilation unit just parsed.
|
||||
*/
|
||||
void output_javadown(const char *path, Obj *prog) {
|
||||
struct DoxWriter *dox = NewDoxWriter();
|
||||
LoadPublicDefinitions(dox, prog);
|
||||
SerializeDox(dox, prog);
|
||||
WriteDox(dox, path);
|
||||
FreeDoxWriter(dox);
|
||||
}
|
|
@ -0,0 +1,392 @@
|
|||
/*-*- 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/alg/alg.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "third_party/chibicc/chibicc.h"
|
||||
|
||||
#define APPEND(L) L.p = realloc(L.p, ++L.n * sizeof(*L.p))
|
||||
|
||||
struct Dox {
|
||||
unsigned char *p;
|
||||
struct Freelist {
|
||||
size_t n;
|
||||
void **p;
|
||||
} freelist;
|
||||
struct Set {
|
||||
size_t n;
|
||||
struct SetEntry {
|
||||
unsigned h;
|
||||
char *s;
|
||||
} * p;
|
||||
} names;
|
||||
struct DoxObjects {
|
||||
size_t n;
|
||||
struct DoxObject {
|
||||
bool ignore;
|
||||
char *type;
|
||||
char *name;
|
||||
char *path;
|
||||
int line;
|
||||
bool is_function;
|
||||
bool is_weak;
|
||||
bool is_inline;
|
||||
bool is_noreturn;
|
||||
bool is_destructor;
|
||||
bool is_constructor;
|
||||
bool is_force_align_arg_pointer;
|
||||
bool is_no_caller_saved_registers;
|
||||
char *visibility;
|
||||
struct Javadown *javadown;
|
||||
struct DoxObjectParams {
|
||||
size_t n;
|
||||
struct DoxObjectParam {
|
||||
char *type;
|
||||
char *name;
|
||||
} * p;
|
||||
} params;
|
||||
} * p;
|
||||
} objects;
|
||||
struct {
|
||||
size_t n;
|
||||
int *p;
|
||||
} objectindex;
|
||||
};
|
||||
|
||||
static unsigned Hash(const void *p, unsigned long n) {
|
||||
unsigned h, i;
|
||||
for (h = i = 0; i < n; i++) {
|
||||
h += ((unsigned char *)p)[i];
|
||||
h *= 0x9e3779b1;
|
||||
}
|
||||
return MAX(1, h);
|
||||
}
|
||||
|
||||
static struct Dox *NewDox(void) {
|
||||
return calloc(1, sizeof(struct Dox));
|
||||
}
|
||||
|
||||
static void FreeDox(struct Dox *dox) {
|
||||
int i;
|
||||
if (dox) {
|
||||
for (i = 0; i < dox->freelist.n; ++i) {
|
||||
free(dox->freelist.p[i]);
|
||||
}
|
||||
free(dox->names.p);
|
||||
free(dox->freelist.p);
|
||||
free(dox->objects.p);
|
||||
free(dox);
|
||||
}
|
||||
}
|
||||
|
||||
static void *FreeLater(struct Dox *dox, void *p) {
|
||||
APPEND(dox->freelist);
|
||||
dox->freelist.p[dox->freelist.n - 1] = p;
|
||||
return p;
|
||||
}
|
||||
|
||||
static int DeserializeInt(struct Dox *dox) {
|
||||
int x;
|
||||
x = (unsigned)dox->p[0] << 000 | (unsigned)dox->p[1] << 010 |
|
||||
(unsigned)dox->p[2] << 020 | (unsigned)dox->p[3] << 030;
|
||||
dox->p += 4;
|
||||
return x;
|
||||
}
|
||||
|
||||
static char *DeserializeStr(struct Dox *dox) {
|
||||
char *s;
|
||||
size_t n;
|
||||
n = DeserializeInt(dox);
|
||||
s = malloc(n + 1);
|
||||
memcpy(s, dox->p, n);
|
||||
s[n] = '\0';
|
||||
dox->p += n;
|
||||
return FreeLater(dox, s);
|
||||
}
|
||||
|
||||
static struct Javadown *DeserializeJavadown(struct Dox *dox) {
|
||||
int i;
|
||||
struct Javadown *jd;
|
||||
jd = FreeLater(dox, calloc(1, sizeof(struct Javadown)));
|
||||
jd->isfileoverview = DeserializeInt(dox);
|
||||
jd->title = DeserializeStr(dox);
|
||||
jd->text = DeserializeStr(dox);
|
||||
jd->tags.n = DeserializeInt(dox);
|
||||
jd->tags.p = FreeLater(dox, malloc(jd->tags.n * sizeof(*jd->tags.p)));
|
||||
for (i = 0; i < jd->tags.n; ++i) {
|
||||
jd->tags.p[i].tag = DeserializeStr(dox);
|
||||
jd->tags.p[i].text = DeserializeStr(dox);
|
||||
}
|
||||
return jd;
|
||||
}
|
||||
|
||||
static void DeserializeObject(struct Dox *dox, struct DoxObject *o) {
|
||||
int i;
|
||||
o->ignore = false;
|
||||
o->type = DeserializeStr(dox);
|
||||
o->name = DeserializeStr(dox);
|
||||
o->path = DeserializeStr(dox);
|
||||
o->line = DeserializeInt(dox);
|
||||
o->is_function = DeserializeInt(dox);
|
||||
o->is_weak = DeserializeInt(dox);
|
||||
o->is_inline = DeserializeInt(dox);
|
||||
o->is_noreturn = DeserializeInt(dox);
|
||||
o->is_destructor = DeserializeInt(dox);
|
||||
o->is_constructor = DeserializeInt(dox);
|
||||
o->is_force_align_arg_pointer = DeserializeInt(dox);
|
||||
o->is_no_caller_saved_registers = DeserializeInt(dox);
|
||||
o->visibility = DeserializeStr(dox);
|
||||
o->javadown = DeserializeJavadown(dox);
|
||||
o->params.n = DeserializeInt(dox);
|
||||
o->params.p = FreeLater(dox, malloc(o->params.n * sizeof(*o->params.p)));
|
||||
for (i = 0; i < o->params.n; ++i) {
|
||||
o->params.p[i].type = DeserializeStr(dox);
|
||||
o->params.p[i].name = DeserializeStr(dox);
|
||||
}
|
||||
}
|
||||
|
||||
static void DeserializeDox(struct Dox *dox) {
|
||||
int i, j, n;
|
||||
i = dox->objects.n;
|
||||
n = DeserializeInt(dox);
|
||||
dox->objects.p =
|
||||
realloc(dox->objects.p, (dox->objects.n + n) * sizeof(*dox->objects.p));
|
||||
for (j = 0; j < n; ++j) {
|
||||
DeserializeObject(dox, dox->objects.p + i + j);
|
||||
}
|
||||
dox->objects.n += n;
|
||||
}
|
||||
|
||||
static void ReadDox(struct Dox *dox, const StringArray *files) {
|
||||
int i, fd;
|
||||
void *map;
|
||||
struct stat st;
|
||||
for (i = 0; i < files->len; ++i) {
|
||||
CHECK_NE(-1, (fd = open(files->data[i], O_RDONLY)));
|
||||
CHECK_NE(-1, fstat(fd, &st));
|
||||
if (st.st_size) {
|
||||
CHECK_NE(MAP_FAILED,
|
||||
(map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
dox->p = map;
|
||||
DeserializeDox(dox);
|
||||
munmap(map, st.st_size);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static bool AddSet(struct Set *set, char *s) {
|
||||
unsigned i, h, k;
|
||||
h = Hash(s, strlen(s));
|
||||
k = 0;
|
||||
for (k = 0;; ++k) {
|
||||
i = (h + k + ((k + 1) >> 1)) & (set->n - 1);
|
||||
if (!set->p[i].h) {
|
||||
set->p[i].h = h;
|
||||
set->p[i].s = s;
|
||||
return true;
|
||||
}
|
||||
if (h == set->p[i].h && !strcmp(s, set->p[i].s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int CompareObjectNames(const void *a, const void *b, void *arg) {
|
||||
int *i1, *i2;
|
||||
struct Dox *dox;
|
||||
i1 = a, i2 = b, dox = arg;
|
||||
return strcmp(dox->objects.p[*i1].name, dox->objects.p[*i2].name);
|
||||
}
|
||||
|
||||
static void IndexDox(struct Dox *dox) {
|
||||
size_t i, j, n;
|
||||
dox->names.n = roundup2pow(dox->objects.n) << 1;
|
||||
dox->names.p = calloc(dox->names.n, sizeof(*dox->names.p));
|
||||
for (n = i = 0; i < dox->objects.n; ++i) {
|
||||
if (AddSet(&dox->names, dox->objects.p[i].name)) {
|
||||
++n;
|
||||
} else {
|
||||
dox->objects.p[i].ignore = true;
|
||||
}
|
||||
}
|
||||
dox->objectindex.n = n;
|
||||
dox->objectindex.p = malloc(n * sizeof(*dox->objectindex.p));
|
||||
for (j = i = 0; i < dox->objects.n; ++i) {
|
||||
if (dox->objects.p[i].ignore) continue;
|
||||
dox->objectindex.p[j++] = i;
|
||||
}
|
||||
qsort_r(dox->objectindex.p, dox->objectindex.n, sizeof(*dox->objectindex.p),
|
||||
CompareObjectNames, dox);
|
||||
}
|
||||
|
||||
static void PrintText(FILE *f, const char *s) {
|
||||
int c;
|
||||
bool bol, pre;
|
||||
for (pre = false, bol = true;;) {
|
||||
switch ((c = *s++)) {
|
||||
case '\0':
|
||||
if (pre) {
|
||||
fprintf(f, "</pre>");
|
||||
}
|
||||
return;
|
||||
case '&':
|
||||
fprintf(f, "&");
|
||||
bol = false;
|
||||
break;
|
||||
case '<':
|
||||
fprintf(f, "<");
|
||||
bol = false;
|
||||
break;
|
||||
case '>':
|
||||
fprintf(f, ">");
|
||||
bol = false;
|
||||
break;
|
||||
case '"':
|
||||
fprintf(f, """);
|
||||
bol = false;
|
||||
break;
|
||||
case '\'':
|
||||
fprintf(f, "'");
|
||||
bol = false;
|
||||
break;
|
||||
case '\n':
|
||||
if (!pre && *s == '\n') {
|
||||
++s;
|
||||
fprintf(f, "\n<p>");
|
||||
} else if (pre &&
|
||||
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
|
||||
fprintf(f, "</pre>\n");
|
||||
pre = false;
|
||||
} else {
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
bol = true;
|
||||
break;
|
||||
case ' ':
|
||||
if (bol && !pre && s[0] == ' ' && s[1] == ' ' && s[2] == ' ') {
|
||||
pre = true;
|
||||
fprintf(f, "<pre>");
|
||||
}
|
||||
fprintf(f, " ");
|
||||
bol = false;
|
||||
break;
|
||||
default:
|
||||
fprintf(f, "%c", c);
|
||||
bol = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintDox(struct Dox *dox, FILE *f) {
|
||||
int i, j, k;
|
||||
char *prefix;
|
||||
struct DoxObject *o;
|
||||
fprintf(f, "\
|
||||
<!doctype html>\n\
|
||||
<meta charset=\"utf-8\">\n\
|
||||
<style>\n\
|
||||
.indent {\n\
|
||||
padding-left: 1em;\n\
|
||||
}\n\
|
||||
</style>\n\
|
||||
\n\
|
||||
<table width=\"100%%\"><tr><td width=\"33%%\" valign=\"top\">\n\
|
||||
<p class=\"toc\">\n\
|
||||
");
|
||||
for (i = 0; i < dox->objectindex.n; ++i) {
|
||||
o = dox->objects.p + dox->objectindex.p[i];
|
||||
if (o->ignore || !o->is_function) continue;
|
||||
fprintf(f, "<a href=\"#%s\">%s</a><br>\n", o->name, o->name);
|
||||
fprintf(f, "<br>\n");
|
||||
}
|
||||
fprintf(f, "<td width=\"67%%\" valign=\"top\">\n");
|
||||
for (i = 0; i < dox->objectindex.n; ++i) {
|
||||
o = dox->objects.p + dox->objectindex.p[i];
|
||||
if (o->ignore || !o->is_function) continue;
|
||||
fprintf(f, "\n<div id=\"%s\" class=\"func\">\n", o->name, o->name);
|
||||
fprintf(f, "<h3><a href=\"#%s\">", o->name);
|
||||
fprintf(f, "<strong class=\"name\">%s</strong></a></h3>", o->name);
|
||||
fprintf(f, "<p>");
|
||||
PrintText(f, o->javadown->title);
|
||||
fprintf(f, "\n");
|
||||
if (*o->javadown->text) {
|
||||
fprintf(f, "<p>");
|
||||
PrintText(f, o->javadown->text);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fprintf(f, "<p><strong>@param</strong>\n");
|
||||
fprintf(f, "<div class=\"params indent\">\n");
|
||||
if (o->params.n) {
|
||||
fprintf(f, "<dl>\n");
|
||||
for (j = 0; j < o->params.n; ++j) {
|
||||
fprintf(f, "<dt>");
|
||||
PrintText(f, o->params.p[j].type);
|
||||
fprintf(f, " <em>");
|
||||
PrintText(f, o->params.p[j].name);
|
||||
fprintf(f, "</em>\n");
|
||||
prefix = xasprintf("%s ", o->params.p[j].name);
|
||||
for (k = 0; k < o->javadown->tags.n; ++k) {
|
||||
if (!strcmp(o->javadown->tags.p[k].tag, "param") &&
|
||||
startswith(o->javadown->tags.p[k].text, prefix)) {
|
||||
fprintf(f, "<dd>");
|
||||
PrintText(f, o->javadown->tags.p[k].text + strlen(prefix));
|
||||
fprintf(f, "\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(prefix);
|
||||
}
|
||||
fprintf(f, "</dl>\n");
|
||||
} else {
|
||||
fprintf(f, "<p>None.\n");
|
||||
}
|
||||
fprintf(f, "</div>\n");
|
||||
for (k = 0; k < o->javadown->tags.n; ++k) {
|
||||
if (!strcmp(o->javadown->tags.p[k].tag, "param")) continue;
|
||||
fprintf(f, "<p><strong>@");
|
||||
PrintText(f, o->javadown->tags.p[k].tag);
|
||||
fprintf(f, "</strong>\n");
|
||||
if (*o->javadown->tags.p[k].text) {
|
||||
PrintText(f, o->javadown->tags.p[k].text);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(f, "</div>\n");
|
||||
}
|
||||
fprintf(f, "</table>\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges documentation data and outputs HTML.
|
||||
*/
|
||||
void drop_dox(const StringArray *files, const char *path) {
|
||||
FILE *f;
|
||||
struct Dox *dox;
|
||||
dox = NewDox();
|
||||
ReadDox(dox, files);
|
||||
IndexDox(dox);
|
||||
f = fopen(path, "w");
|
||||
PrintDox(dox, f);
|
||||
fclose(f);
|
||||
FreeDox(dox);
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
// So it is very easy to lookahead arbitrary number of tokens in this
|
||||
// parser.
|
||||
|
||||
#include "libc/nexgen32e/ffs.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "third_party/chibicc/chibicc.h"
|
||||
|
||||
|
@ -50,11 +51,14 @@ typedef struct {
|
|||
bool is_const;
|
||||
bool is_tls;
|
||||
bool is_weak;
|
||||
bool is_ms_abi;
|
||||
bool is_aligned;
|
||||
bool is_noreturn;
|
||||
bool is_destructor;
|
||||
bool is_constructor;
|
||||
bool is_externally_visible;
|
||||
bool is_force_align_arg_pointer;
|
||||
bool is_no_caller_saved_registers;
|
||||
int align;
|
||||
char *section;
|
||||
char *visibility;
|
||||
|
@ -113,6 +117,8 @@ static Node *current_switch;
|
|||
|
||||
static Obj *builtin_alloca;
|
||||
|
||||
static Token *current_javadown;
|
||||
|
||||
static Initializer *initializer(Token **, Token *, Type *, Type **);
|
||||
static Member *get_struct_member(Type *, Token *);
|
||||
static Node *binor(Token **, Token *);
|
||||
|
@ -382,6 +388,10 @@ static Token *type_attributes(Token *tok, void *arg) {
|
|||
ty->is_packed = true;
|
||||
return tok;
|
||||
}
|
||||
if (consume_attribute(&tok, tok, "ms_abi")) {
|
||||
ty->is_ms_abi = true;
|
||||
return tok;
|
||||
}
|
||||
if (consume_attribute(&tok, tok, "aligned")) {
|
||||
ty->is_aligned = true;
|
||||
if (CONSUME(&tok, tok, "(")) {
|
||||
|
@ -466,6 +476,18 @@ static Token *thing_attributes(Token *tok, void *arg) {
|
|||
attr->is_externally_visible = true;
|
||||
return tok;
|
||||
}
|
||||
if (consume_attribute(&tok, tok, "force_align_arg_pointer")) {
|
||||
attr->is_force_align_arg_pointer = true;
|
||||
return tok;
|
||||
}
|
||||
if (consume_attribute(&tok, tok, "no_caller_saved_registers")) {
|
||||
attr->is_no_caller_saved_registers = true;
|
||||
return tok;
|
||||
}
|
||||
if (consume_attribute(&tok, tok, "ms_abi")) {
|
||||
attr->is_ms_abi = true;
|
||||
return tok;
|
||||
}
|
||||
if (consume_attribute(&tok, tok, "constructor")) {
|
||||
attr->is_constructor = true;
|
||||
if (CONSUME(&tok, tok, "(")) {
|
||||
|
@ -1080,6 +1102,7 @@ static Node *declaration(Token **rest, Token *tok, Type *basety,
|
|||
// even if ty is not VLA because ty may be a pointer to VLA
|
||||
// (e.g. int (*foo)[n][m] where n and m are variables.)
|
||||
cur = cur->next = new_unary(ND_EXPR_STMT, compute_vla_size(ty, tok), tok);
|
||||
tok = attribute_list(tok, attr, thing_attributes);
|
||||
if (ty->kind == TY_VLA) {
|
||||
if (EQUAL(tok, "="))
|
||||
error_tok(tok, "variable-sized object may not be initialized");
|
||||
|
@ -1198,7 +1221,8 @@ static Member *struct_designator(Token **rest, Token *tok, Type *ty) {
|
|||
if (tok->kind != TK_IDENT) error_tok(tok, "expected a field designator");
|
||||
for (Member *mem = ty->members; mem; mem = mem->next) {
|
||||
// Anonymous struct member
|
||||
if (mem->ty->kind == TY_STRUCT && !mem->name) {
|
||||
if ((mem->ty->kind == TY_STRUCT || mem->ty->kind == TY_UNION) &&
|
||||
!mem->name) {
|
||||
if (get_struct_member(mem->ty, tok)) {
|
||||
*rest = start;
|
||||
return mem;
|
||||
|
@ -1920,6 +1944,7 @@ int64_t eval(Node *node) {
|
|||
// number. The latter form is accepted only as an initialization
|
||||
// expression for a global variable.
|
||||
int64_t eval2(Node *node, char ***label) {
|
||||
int64_t x, y;
|
||||
add_type(node);
|
||||
if (is_flonum(node->ty)) return eval_double(node);
|
||||
switch (node->kind) {
|
||||
|
@ -1930,15 +1955,25 @@ int64_t eval2(Node *node, char ***label) {
|
|||
case ND_MUL:
|
||||
return eval(node->lhs) * eval(node->rhs);
|
||||
case ND_DIV:
|
||||
if (node->ty->is_unsigned)
|
||||
return (uint64_t)eval(node->lhs) / eval(node->rhs);
|
||||
return eval(node->lhs) / eval(node->rhs);
|
||||
y = eval(node->rhs);
|
||||
if (!y) error_tok(node->rhs->tok, "constexpr div by zero");
|
||||
if (node->ty->is_unsigned) {
|
||||
return (uint64_t)eval(node->lhs) / y;
|
||||
}
|
||||
x = eval(node->lhs);
|
||||
if (x == 0x8000000000000000 && y == -1) {
|
||||
error_tok(node->rhs->tok, "constexpr divide error");
|
||||
}
|
||||
return x / y;
|
||||
case ND_NEG:
|
||||
return -eval(node->lhs);
|
||||
case ND_REM:
|
||||
if (node->ty->is_unsigned)
|
||||
return (uint64_t)eval(node->lhs) % eval(node->rhs);
|
||||
return eval(node->lhs) % eval(node->rhs);
|
||||
y = eval(node->rhs);
|
||||
if (!y) error_tok(node->rhs->tok, "constexpr rem by zero");
|
||||
if (node->ty->is_unsigned) {
|
||||
return (uint64_t)eval(node->lhs) % y;
|
||||
}
|
||||
return eval(node->lhs) % y;
|
||||
case ND_BINAND:
|
||||
return eval(node->lhs) & eval(node->rhs);
|
||||
case ND_BINOR:
|
||||
|
@ -2044,8 +2079,9 @@ int64_t eval2(Node *node, char ***label) {
|
|||
static int64_t eval_rval(Node *node, char ***label) {
|
||||
switch (node->kind) {
|
||||
case ND_VAR:
|
||||
if (node->var->is_local)
|
||||
if (node->var->is_local) {
|
||||
error_tok(node->tok, "not a compile-time constant");
|
||||
}
|
||||
*label = &node->var->name;
|
||||
return 0;
|
||||
case ND_DEREF:
|
||||
|
@ -2063,6 +2099,7 @@ bool is_const_expr(Node *node) {
|
|||
case ND_SUB:
|
||||
case ND_MUL:
|
||||
case ND_DIV:
|
||||
case ND_REM:
|
||||
case ND_BINAND:
|
||||
case ND_BINOR:
|
||||
case ND_BINXOR:
|
||||
|
@ -2884,8 +2921,9 @@ static Node *postfix(Token **rest, Token *tok) {
|
|||
static Node *funcall(Token **rest, Token *tok, Node *fn) {
|
||||
add_type(fn);
|
||||
if (fn->ty->kind != TY_FUNC &&
|
||||
(fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC))
|
||||
(fn->ty->kind != TY_PTR || fn->ty->base->kind != TY_FUNC)) {
|
||||
error_tok(fn->tok, "not a function");
|
||||
}
|
||||
Type *ty = (fn->ty->kind == TY_FUNC) ? fn->ty : fn->ty->base;
|
||||
Type *param_ty = ty->params;
|
||||
Node head = {};
|
||||
|
@ -2896,8 +2934,9 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
|
|||
add_type(arg);
|
||||
if (!param_ty && !ty->is_variadic) error_tok(tok, "too many arguments");
|
||||
if (param_ty) {
|
||||
if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION)
|
||||
if (param_ty->kind != TY_STRUCT && param_ty->kind != TY_UNION) {
|
||||
arg = new_cast(arg, param_ty);
|
||||
}
|
||||
param_ty = param_ty->next;
|
||||
} else if (arg->ty->kind == TY_FLOAT) {
|
||||
// If parameter type is omitted (e.g. in "..."), float
|
||||
|
@ -2914,8 +2953,9 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
|
|||
node->args = head.next;
|
||||
// If a function returns a struct, it is caller's responsibility
|
||||
// to allocate a space for the return value.
|
||||
if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION)
|
||||
if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) {
|
||||
node->ret_buffer = new_lvar("", node->ty);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -3138,13 +3178,37 @@ static Node *primary(Token **rest, Token *tok) {
|
|||
*rest = skip(tok, ')');
|
||||
return node;
|
||||
}
|
||||
if (EQUAL(tok, "__builtin_popcount") || EQUAL(tok, "__builtin_popcountl") ||
|
||||
if (EQUAL(tok, "__builtin_popcount")) {
|
||||
Token *t = skip(tok->next, '(');
|
||||
Node *node = assign(&t, t);
|
||||
if (is_const_expr(node)) {
|
||||
*rest = skip(t, ')');
|
||||
return new_num(__builtin_popcount(eval(node)), t);
|
||||
}
|
||||
}
|
||||
if (EQUAL(tok, "__builtin_popcountl") ||
|
||||
EQUAL(tok, "__builtin_popcountll")) {
|
||||
Token *t = skip(tok->next, '(');
|
||||
Node *node = assign(&t, t);
|
||||
if (is_const_expr(node)) {
|
||||
*rest = skip(t, ')');
|
||||
return new_num(popcnt(eval(node)), t);
|
||||
return new_num(__builtin_popcountl(eval(node)), t);
|
||||
}
|
||||
}
|
||||
if (EQUAL(tok, "__builtin_ffs")) {
|
||||
Token *t = skip(tok->next, '(');
|
||||
Node *node = assign(&t, t);
|
||||
if (is_const_expr(node)) {
|
||||
*rest = skip(t, ')');
|
||||
return new_num(__builtin_ffs(eval(node)), t);
|
||||
}
|
||||
}
|
||||
if (EQUAL(tok, "__builtin_ffsl") || EQUAL(tok, "__builtin_ffsll")) {
|
||||
Token *t = skip(tok->next, '(');
|
||||
Node *node = assign(&t, t);
|
||||
if (is_const_expr(node)) {
|
||||
*rest = skip(t, ')');
|
||||
return new_num(__builtin_ffsl(eval(node)), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3328,15 +3392,19 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
|
|||
fn->is_static = attr->is_static || (attr->is_inline && !attr->is_extern);
|
||||
fn->is_inline = attr->is_inline;
|
||||
fn->is_weak = attr->is_weak;
|
||||
fn->is_ms_abi = attr->is_ms_abi;
|
||||
fn->is_aligned = attr->is_aligned;
|
||||
fn->is_noreturn = attr->is_noreturn;
|
||||
fn->is_destructor = attr->is_destructor;
|
||||
fn->is_constructor = attr->is_constructor;
|
||||
fn->is_externally_visible = attr->is_externally_visible;
|
||||
fn->is_force_align_arg_pointer = attr->is_force_align_arg_pointer;
|
||||
fn->is_no_caller_saved_registers = attr->is_no_caller_saved_registers;
|
||||
fn->align = attr->align;
|
||||
fn->section = attr->section;
|
||||
fn->visibility = attr->visibility;
|
||||
}
|
||||
fn->javadown = fn->javadown ?: current_javadown;
|
||||
fn->is_root = !(fn->is_static && fn->is_inline);
|
||||
if (consume_attribute(&tok, tok, "asm")) {
|
||||
tok = skip(tok, '(');
|
||||
|
@ -3352,11 +3420,13 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
|
|||
// A buffer for a struct/union return value is passed
|
||||
// as the hidden first parameter.
|
||||
Type *rty = ty->return_ty;
|
||||
if ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16)
|
||||
if ((rty->kind == TY_STRUCT || rty->kind == TY_UNION) && rty->size > 16) {
|
||||
new_lvar("", pointer_to(rty));
|
||||
}
|
||||
fn->params = locals;
|
||||
if (ty->is_variadic)
|
||||
if (ty->is_variadic) {
|
||||
fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136));
|
||||
}
|
||||
fn->alloca_bottom = new_lvar("__alloca_size__", pointer_to(ty_char));
|
||||
tok = skip(tok, '{');
|
||||
// [https://www.sigbus.info/n1570#6.4.2.2p1] "__func__" is
|
||||
|
@ -3382,6 +3452,7 @@ static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) {
|
|||
Type *ty = declarator(&tok, tok, basety);
|
||||
if (!ty->name) error_tok(ty->name_pos, "variable name omitted");
|
||||
Obj *var = new_gvar(get_ident(ty->name), ty);
|
||||
var->javadown = current_javadown;
|
||||
if (consume_attribute(&tok, tok, "asm")) {
|
||||
tok = skip(tok, '(');
|
||||
var->asmname = ConsumeStringLiteral(&tok, tok);
|
||||
|
@ -3528,6 +3599,12 @@ Obj *parse(Token *tok) {
|
|||
tok = static_assertion(tok);
|
||||
continue;
|
||||
}
|
||||
if (tok->kind == TK_JAVADOWN) {
|
||||
current_javadown = tok;
|
||||
tok = tok->next;
|
||||
} else {
|
||||
current_javadown = NULL;
|
||||
}
|
||||
VarAttr attr = {};
|
||||
tok = attribute_list(tok, &attr, thing_attributes);
|
||||
Type *basety = declspec(&tok, tok, &attr);
|
||||
|
|
|
@ -26,11 +26,7 @@
|
|||
|
||||
typedef struct CondIncl CondIncl;
|
||||
typedef struct Hideset Hideset;
|
||||
typedef struct Macro Macro;
|
||||
typedef struct MacroArg MacroArg;
|
||||
typedef struct MacroParam MacroParam;
|
||||
|
||||
typedef Token *macro_handler_fn(Token *);
|
||||
|
||||
typedef enum {
|
||||
STR_NONE,
|
||||
|
@ -40,11 +36,6 @@ typedef enum {
|
|||
STR_WIDE,
|
||||
} StringKind;
|
||||
|
||||
struct MacroParam {
|
||||
MacroParam *next;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct MacroArg {
|
||||
MacroArg *next;
|
||||
char *name;
|
||||
|
@ -52,15 +43,6 @@ struct MacroArg {
|
|||
Token *tok;
|
||||
};
|
||||
|
||||
struct Macro {
|
||||
char *name;
|
||||
bool is_objlike; // Object-like or function-like
|
||||
MacroParam *params;
|
||||
char *va_args_name;
|
||||
Token *body;
|
||||
macro_handler_fn *handler;
|
||||
};
|
||||
|
||||
// `#if` can be nested, so we use a stack to manage nested `#if`s.
|
||||
struct CondIncl {
|
||||
CondIncl *next;
|
||||
|
@ -74,7 +56,8 @@ struct Hideset {
|
|||
char *name;
|
||||
};
|
||||
|
||||
static HashMap macros;
|
||||
HashMap macros;
|
||||
|
||||
static CondIncl *cond_incl;
|
||||
static HashMap pragma_once;
|
||||
static int include_next_idx;
|
||||
|
@ -338,21 +321,24 @@ static MacroParam *read_macro_params(Token **rest, Token *tok,
|
|||
return head.next;
|
||||
}
|
||||
|
||||
static void read_macro_definition(Token **rest, Token *tok) {
|
||||
static Macro *read_macro_definition(Token **rest, Token *tok) {
|
||||
Macro *m;
|
||||
char *name;
|
||||
if (tok->kind != TK_IDENT) error_tok(tok, "macro name must be an identifier");
|
||||
char *name = strndup(tok->loc, tok->len);
|
||||
name = strndup(tok->loc, tok->len);
|
||||
tok = tok->next;
|
||||
if (!tok->has_space && EQUAL(tok, "(")) {
|
||||
// Function-like macro
|
||||
char *va_args_name = NULL;
|
||||
MacroParam *params = read_macro_params(&tok, tok->next, &va_args_name);
|
||||
Macro *m = add_macro(name, false, copy_line(rest, tok));
|
||||
m = add_macro(name, false, copy_line(rest, tok));
|
||||
m->params = params;
|
||||
m->va_args_name = va_args_name;
|
||||
} else {
|
||||
// Object-like macro
|
||||
add_macro(name, true, copy_line(rest, tok));
|
||||
m = add_macro(name, true, copy_line(rest, tok));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static MacroArg *read_macro_arg_one(Token **rest, Token *tok, bool read_rest) {
|
||||
|
@ -742,6 +728,12 @@ static Token *preprocess2(Token *tok) {
|
|||
while (tok->kind != TK_EOF) {
|
||||
// If it is a macro, expand it.
|
||||
if (expand_macro(&tok, tok)) continue;
|
||||
// make sure javadown is removed if it's for a macro definition
|
||||
if (tok->kind == TK_JAVADOWN && is_hash(tok->next) &&
|
||||
EQUAL(tok->next->next, "define")) {
|
||||
read_macro_definition(&tok, tok->next->next->next)->javadown = tok;
|
||||
continue;
|
||||
}
|
||||
// Pass through if it is not a "#".
|
||||
if (!is_hash(tok)) {
|
||||
tok->line_delta = tok->file->line_delta;
|
||||
|
@ -936,6 +928,8 @@ __GNUC_PATCHLEVEL__\000\
|
|||
0\000\
|
||||
__NO_INLINE__\000\
|
||||
16\000\
|
||||
__GNUC_STDC_INLINE__\000\
|
||||
1\000\
|
||||
__BIGGEST_ALIGNMENT__\000\
|
||||
16\000\
|
||||
__C99_MACRO_WITH_VA_ARGS\000\
|
||||
|
@ -1010,6 +1004,10 @@ __UINT32_MAX__\000\
|
|||
0xffffffffu\000\
|
||||
__INT64_MAX__\000\
|
||||
0x7fffffffffffffffl\000\
|
||||
__LONG_MAX__\000\
|
||||
0x7fffffffffffffffl\000\
|
||||
__LONG_LONG_MAX__\000\
|
||||
0x7fffffffffffffffl\000\
|
||||
__UINT64_MAX__\000\
|
||||
0xfffffffffffffffful\000\
|
||||
__SIZE_MAX__\000\
|
||||
|
|
|
@ -12,7 +12,8 @@ __attribute__((__nonnull__(1))) void cate2(char *);
|
|||
__attribute__((__section__(".data.var"))) int var2;
|
||||
__attribute__((__section__(".data.var"))) int ar2[4];
|
||||
|
||||
int main() {
|
||||
__attribute__((__force_align_arg_pointer__, __no_caller_saved_registers__)) int
|
||||
main() {
|
||||
int2 a;
|
||||
ASSERT(64, _Alignof(int2));
|
||||
ASSERT(64, _Alignof(a));
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "libc/macros.h"
|
||||
#include "third_party/chibicc/test/test.h"
|
||||
|
||||
int main() {
|
||||
|
@ -6,6 +7,11 @@ int main() {
|
|||
int x[n];
|
||||
sizeof(x);
|
||||
}));
|
||||
ASSERT(5, ({
|
||||
int n = 5;
|
||||
int x[n];
|
||||
ARRAYLEN(x);
|
||||
}));
|
||||
ASSERT((5 + 1) * (8 * 2) * 4, ({
|
||||
int m = 5, n = 8;
|
||||
int x[m + 1][n * 2];
|
||||
|
|
|
@ -470,6 +470,7 @@ Token *tokenize(File *file) {
|
|||
char *p = file->contents;
|
||||
Token head = {};
|
||||
Token *cur = &head;
|
||||
struct Javadown *javadown;
|
||||
at_bol = true;
|
||||
has_space = false;
|
||||
while (*p) {
|
||||
|
@ -480,6 +481,22 @@ Token *tokenize(File *file) {
|
|||
has_space = true;
|
||||
continue;
|
||||
}
|
||||
// Javadoc-style markdown comments.
|
||||
if (LOOKINGAT(p, "/**") && p[3] != '/' && p[3] != '*') {
|
||||
char *q = strstr(p + 3, "*/");
|
||||
if (!q) error_at(p, "unclosed javadown");
|
||||
javadown = ParseJavadown(p + 3, q - p - 3 - 2);
|
||||
if (javadown->isfileoverview) {
|
||||
FreeJavadown(file->javadown);
|
||||
file->javadown = javadown;
|
||||
} else {
|
||||
cur = cur->next = new_token(TK_JAVADOWN, p, q + 2);
|
||||
cur->javadown = javadown;
|
||||
}
|
||||
p = q + 2;
|
||||
has_space = true;
|
||||
continue;
|
||||
}
|
||||
// Skip block comments.
|
||||
if (LOOKINGAT(p, "/*")) {
|
||||
char *q = strstr(p + 2, "*/");
|
||||
|
@ -505,12 +522,13 @@ Token *tokenize(File *file) {
|
|||
if (isdigit(*p) || (*p == '.' && isdigit(p[1]))) {
|
||||
char *q = p++;
|
||||
for (;;) {
|
||||
if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1]))
|
||||
if (p[0] && p[1] && strchr("eEpP", p[0]) && strchr("+-", p[1])) {
|
||||
p += 2;
|
||||
else if (isalnum(*p) || *p == '.')
|
||||
} else if (isalnum(*p) || *p == '.') {
|
||||
p++;
|
||||
else
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cur = cur->next = new_token(TK_PP_NUM, q, p);
|
||||
continue;
|
||||
|
@ -663,7 +681,30 @@ void remove_backslash_newline(char *p) {
|
|||
// the logical line number matches the physical one.
|
||||
// This counter maintain the number of newlines we have removed.
|
||||
int n = 0;
|
||||
bool instring = false;
|
||||
while (p[i]) {
|
||||
if (instring) {
|
||||
if (p[i] == '"' && p[i - 1] != '\\') {
|
||||
instring = false;
|
||||
}
|
||||
} else {
|
||||
if (p[i] == '"') {
|
||||
instring = true;
|
||||
} else if (p[i] == '/' && p[i + 1] == '*') {
|
||||
p[j++] = p[i++];
|
||||
p[j++] = p[i++];
|
||||
while (p[i]) {
|
||||
if (p[i] == '*' && p[i + 1] == '/') {
|
||||
p[j++] = p[i++];
|
||||
p[j++] = p[i++];
|
||||
break;
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (p[i] == '\\' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
n++;
|
||||
|
|
|
@ -4,19 +4,28 @@
|
|||
/**
|
||||
* Returns (by copy) a struct containing various summary statistics:
|
||||
*
|
||||
* arena: current total non-mmapped bytes allocated from system
|
||||
* ordblks: the number of free chunks
|
||||
* smblks: always zero.
|
||||
* hblks: current number of mmapped regions
|
||||
* hblkhd: total bytes held in mmapped regions
|
||||
* usmblks: the maximum total allocated space. This will be greater
|
||||
* than current total if trimming has occurred.
|
||||
* fsmblks: always zero
|
||||
* uordblks: current total allocated space (normal or mmapped)
|
||||
* fordblks: total free space
|
||||
* keepcost: the maximum number of bytes that could ideally be released
|
||||
* back to system via malloc_trim. ("ideally" means that
|
||||
* it ignores page restrictions etc.)
|
||||
* - arena: current total non-mmapped bytes allocated from system
|
||||
*
|
||||
* - ordblks: the number of free chunks
|
||||
*
|
||||
* - smblks: always zero.
|
||||
*
|
||||
* - hblks: current number of mmapped regions
|
||||
*
|
||||
* - hblkhd: total bytes held in mmapped regions
|
||||
*
|
||||
* - usmblks: the maximum total allocated space. This will be greater
|
||||
* than current total if trimming has occurred.
|
||||
*
|
||||
* - fsmblks: always zero
|
||||
*
|
||||
* - uordblks: current total allocated space (normal or mmapped)
|
||||
*
|
||||
* - fordblks: total free space
|
||||
*
|
||||
* - keepcost: the maximum number of bytes that could ideally be
|
||||
* released back to system via malloc_trim. ("ideally" means that it
|
||||
* ignores page restrictions etc.)
|
||||
*
|
||||
* Because these fields are ints, but internal bookkeeping may
|
||||
* be kept as longs, the reported values may wrap around zero and
|
||||
|
|
|
@ -227,13 +227,13 @@ static int GlobPredicate(const void *a, const void *b) {
|
|||
*
|
||||
* For example:
|
||||
*
|
||||
* glob_t g = {.gl_offs = 2};
|
||||
* glob("*.*", GLOB_DOOFFS, NULL, &g);
|
||||
* glob("../.*", GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
|
||||
* g.gl_pathv[0] = "ls";
|
||||
* g.gl_pathv[1] = "-l";
|
||||
* execvp("ls", &g.gl_pathv[0]);
|
||||
* globfree(g);
|
||||
* glob_t g = {.gl_offs = 2};
|
||||
* glob("*.*", GLOB_DOOFFS, NULL, &g);
|
||||
* glob("../.*", GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
|
||||
* g.gl_pathv[0] = "ls";
|
||||
* g.gl_pathv[1] = "-l";
|
||||
* execvp("ls", &g.gl_pathv[0]);
|
||||
* globfree(g);
|
||||
*
|
||||
* @param pat can have star wildcard see fnmatch()
|
||||
* @param g will receive matching entries and needs globfree()
|
||||
|
|
|
@ -2387,10 +2387,10 @@ static reg_errcode_t tre_ast_to_tnfa(tre_ast_node_t *node,
|
|||
/**
|
||||
* Compiles regular expression, e.g.
|
||||
*
|
||||
* regex_t rx;
|
||||
* EXPECT_EQ(REG_OK, regcomp(&rx, "^[A-Za-z\x7f-\uFFFF]{2}$", REG_EXTENDED));
|
||||
* EXPECT_EQ(REG_OK, regexec(&rx, "→A", 0, NULL, 0));
|
||||
* regfree(&rx);
|
||||
* regex_t rx;
|
||||
* EXPECT_EQ(REG_OK, regcomp(&rx, "^[A-Za-z]{2}$", REG_EXTENDED));
|
||||
* EXPECT_EQ(REG_OK, regexec(&rx, "→A", 0, NULL, 0));
|
||||
* regfree(&rx);
|
||||
*
|
||||
* @param preg points to state, and needs regfree() afterwards
|
||||
* @param regex is utf-8 regular expression string
|
||||
|
|
|
@ -38,11 +38,11 @@
|
|||
* even in the case of corrupted input.
|
||||
*/
|
||||
|
||||
#define ZLIB_VERSION "1.2.11"
|
||||
#define ZLIB_VERNUM 0x12b0
|
||||
#define ZLIB_VER_MAJOR 1
|
||||
#define ZLIB_VER_MINOR 2
|
||||
#define ZLIB_VER_REVISION 11
|
||||
#define ZLIB_VERSION "1.2.11"
|
||||
#define ZLIB_VERNUM 0x12b0
|
||||
#define ZLIB_VER_MAJOR 1
|
||||
#define ZLIB_VER_MINOR 2
|
||||
#define ZLIB_VER_REVISION 11
|
||||
#define ZLIB_VER_SUBREVISION 0
|
||||
|
||||
/**
|
||||
|
@ -81,25 +81,25 @@
|
|||
* (particularly if the decompressor wants to decompress everything in a
|
||||
* single step).
|
||||
*/
|
||||
#define Z_NO_FLUSH 0
|
||||
#define Z_NO_FLUSH 0
|
||||
#define Z_PARTIAL_FLUSH 1
|
||||
#define Z_SYNC_FLUSH 2
|
||||
#define Z_FULL_FLUSH 3
|
||||
#define Z_FINISH 4
|
||||
#define Z_BLOCK 5
|
||||
#define Z_TREES 6
|
||||
#define Z_SYNC_FLUSH 2
|
||||
#define Z_FULL_FLUSH 3
|
||||
#define Z_FINISH 4
|
||||
#define Z_BLOCK 5
|
||||
#define Z_TREES 6
|
||||
|
||||
/**
|
||||
* Allowed flush values; see deflate() and inflate() below for details.
|
||||
*/
|
||||
#define Z_OK 0
|
||||
#define Z_STREAM_END 1
|
||||
#define Z_NEED_DICT 2
|
||||
#define Z_ERRNO (-1)
|
||||
#define Z_STREAM_ERROR (-2)
|
||||
#define Z_DATA_ERROR (-3)
|
||||
#define Z_MEM_ERROR (-4)
|
||||
#define Z_BUF_ERROR (-5)
|
||||
#define Z_OK 0
|
||||
#define Z_STREAM_END 1
|
||||
#define Z_NEED_DICT 2
|
||||
#define Z_ERRNO (-1)
|
||||
#define Z_STREAM_ERROR (-2)
|
||||
#define Z_DATA_ERROR (-3)
|
||||
#define Z_MEM_ERROR (-4)
|
||||
#define Z_BUF_ERROR (-5)
|
||||
#define Z_VERSION_ERROR (-6)
|
||||
|
||||
/**
|
||||
|
@ -107,26 +107,26 @@
|
|||
* values are errors, positive values are used for special but normal
|
||||
* events.
|
||||
*/
|
||||
#define Z_NO_COMPRESSION 0
|
||||
#define Z_BEST_SPEED 1
|
||||
#define Z_BEST_COMPRESSION 9
|
||||
#define Z_NO_COMPRESSION 0
|
||||
#define Z_BEST_SPEED 1
|
||||
#define Z_BEST_COMPRESSION 9
|
||||
#define Z_DEFAULT_COMPRESSION (-1)
|
||||
|
||||
/**
|
||||
* Compression levels.
|
||||
*/
|
||||
#define Z_FILTERED 1
|
||||
#define Z_HUFFMAN_ONLY 2
|
||||
#define Z_RLE 3
|
||||
#define Z_FIXED 4
|
||||
#define Z_FILTERED 1
|
||||
#define Z_HUFFMAN_ONLY 2
|
||||
#define Z_RLE 3
|
||||
#define Z_FIXED 4
|
||||
#define Z_DEFAULT_STRATEGY 0
|
||||
|
||||
/**
|
||||
* Compression strategy; see deflateInit2() below for details
|
||||
*/
|
||||
#define Z_BINARY 0
|
||||
#define Z_TEXT 1
|
||||
#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
|
||||
#define Z_BINARY 0
|
||||
#define Z_TEXT 1
|
||||
#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
|
||||
#define Z_UNKNOWN 2
|
||||
|
||||
/**
|
||||
|
@ -1139,7 +1139,7 @@ int inflateBack(z_streamp strm, in_func in, void *in_desc, out_func out,
|
|||
*/
|
||||
int inflateBackEnd(z_streamp strm);
|
||||
|
||||
/**
|
||||
/*
|
||||
* Return flags indicating compile-time options.
|
||||
*
|
||||
* Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
|
||||
|
@ -1160,10 +1160,13 @@ int inflateBackEnd(z_streamp strm);
|
|||
* 14,15: 0 (reserved)
|
||||
*
|
||||
* Library content (indicates missing functionality):
|
||||
*
|
||||
* 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
|
||||
* deflate code when not needed)
|
||||
* 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
|
||||
* and decode gzip streams (to avoid linking crc code)
|
||||
* deflate code when not needed)
|
||||
*
|
||||
* 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't
|
||||
* detect and decode gzip streams (to avoid linking crc code)
|
||||
*
|
||||
* 18-19: 0 (reserved)
|
||||
*
|
||||
* Operation variations (changes in library functionality):
|
||||
|
@ -1685,12 +1688,11 @@ uLong adler32_combine(uLong adler1, uLong adler2, int64_t len2);
|
|||
*
|
||||
* Usage example:
|
||||
*
|
||||
* uLong crc = crc32(0L, Z_NULL, 0);
|
||||
*
|
||||
* while (read_buffer(buffer, length) != EOF) {
|
||||
* crc = crc32(crc, buffer, length);
|
||||
* }
|
||||
* if (crc != original_crc) error();
|
||||
* uLong crc = crc32(0L, Z_NULL, 0);
|
||||
* while (read_buffer(buffer, length) != EOF) {
|
||||
* crc = crc32(crc, buffer, length);
|
||||
* }
|
||||
* if (crc != original_crc) error();
|
||||
*/
|
||||
uLong crc32(uLong crc, const Bytef *buf, uInt len);
|
||||
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ This program is free software; you can redistribute it and/or modify │
|
||||
│ it under the terms of the GNU General Public License as published by │
|
||||
│ the Free Software Foundation; version 2 of the License. │
|
||||
│ │
|
||||
│ This program is distributed in the hope that it will be useful, but │
|
||||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||||
│ General Public License for more details. │
|
||||
│ │
|
||||
│ You should have received a copy of the GNU General Public License │
|
||||
│ along with this program; if not, write to the Free Software │
|
||||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||||
│ 02110-1301 USA │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "tool/build/lib/javadown.h"
|
||||
|
||||
#define FILEOVERVIEW "@fileoverview"
|
||||
|
||||
struct Lines {
|
||||
size_t n;
|
||||
struct Line {
|
||||
char *p;
|
||||
size_t n;
|
||||
} * p;
|
||||
};
|
||||
|
||||
static char *SkipEmptyFirstLine(char *p) {
|
||||
int i = 0;
|
||||
while (p[i] == ' ' || p[i] == '\t') ++i;
|
||||
return p[i] == '\n' ? p + i + 1 : p;
|
||||
}
|
||||
|
||||
static void DeleteLastEmptyLine(char *p, size_t n) {
|
||||
while (n && (p[n - 1] == ' ' || p[n - 1] == '\t')) --n;
|
||||
if (n && p[n - 1] == '\n') p[n - 1] = '\0';
|
||||
}
|
||||
|
||||
static void AppendLine(struct Lines *lines) {
|
||||
lines->p = realloc(lines->p, ++lines->n * sizeof(*lines->p));
|
||||
memset(lines->p + lines->n - 1, 0, sizeof(*lines->p));
|
||||
}
|
||||
|
||||
static void AppendTag(struct JavadownTags *tags) {
|
||||
tags->p = realloc(tags->p, ++tags->n * sizeof(*tags->p));
|
||||
memset(tags->p + tags->n - 1, 0, sizeof(*tags->p));
|
||||
}
|
||||
|
||||
static unsigned GetSpacePrefixLen(const char *p, size_t n) {
|
||||
int i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (p[i] != ' ' && p[i] != '\t') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static unsigned GetSpaceStarPrefixLen(const char *p, size_t n) {
|
||||
int i;
|
||||
i = GetSpacePrefixLen(p, n);
|
||||
return i < n && (p[i] == '*') ? i + 1 : 0;
|
||||
}
|
||||
|
||||
static unsigned GetTagLen(const char *p, size_t n) {
|
||||
int i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (!islower(p[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static unsigned GetMinPrefixLen(struct Lines *lines,
|
||||
unsigned GetPrefixLen(const char *, size_t)) {
|
||||
int i;
|
||||
unsigned n, m;
|
||||
for (m = -1, i = 0; i < lines->n; ++i) {
|
||||
if (lines->p[i].n) {
|
||||
n = GetPrefixLen(lines->p[i].p, lines->p[i].n);
|
||||
if (n < m) m = n;
|
||||
}
|
||||
}
|
||||
return m == -1 ? 0 : m;
|
||||
}
|
||||
|
||||
static void RemovePrefixes(struct Lines *lines, unsigned m) {
|
||||
int i;
|
||||
for (i = 0; i < lines->n; ++i) {
|
||||
if (m <= lines->p[i].n) {
|
||||
lines->p[i].p += m;
|
||||
lines->p[i].n -= m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SplitLines(struct Lines *lines, char *p) {
|
||||
char *q;
|
||||
DeleteLastEmptyLine(p, strlen(p));
|
||||
p = SkipEmptyFirstLine(p);
|
||||
for (;;) {
|
||||
AppendLine(lines);
|
||||
lines->p[lines->n - 1].p = p;
|
||||
lines->p[lines->n - 1].n = (q = strchr(p, '\n')) ? q - p : strlen(p);
|
||||
if (!q) break;
|
||||
p = q + 1;
|
||||
}
|
||||
RemovePrefixes(lines, GetMinPrefixLen(lines, GetSpaceStarPrefixLen));
|
||||
RemovePrefixes(lines, GetMinPrefixLen(lines, GetSpacePrefixLen));
|
||||
}
|
||||
|
||||
static bool ConsumeFileOverview(struct Lines *lines) {
|
||||
int i;
|
||||
if (lines->n && lines->p[0].n >= strlen(FILEOVERVIEW) &&
|
||||
startswith(lines->p[0].p, FILEOVERVIEW)) {
|
||||
lines->p[0].p += strlen(FILEOVERVIEW);
|
||||
lines->p[0].n -= strlen(FILEOVERVIEW);
|
||||
while (lines->p[0].n &&
|
||||
(lines->p[0].p[0] == ' ' || lines->p[0].p[0] == '\t')) {
|
||||
lines->p[0].p += 1;
|
||||
lines->p[0].n -= 1;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void RemoveTrailingWhitespace(struct Lines *lines) {
|
||||
int i;
|
||||
for (i = 0; i < lines->n; ++i) {
|
||||
while (lines->p[i].n && isspace(lines->p[i].p[lines->p[i].n - 1])) {
|
||||
--lines->p[i].n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ExtractTitle(struct Javadown *jd, struct Lines *lines) {
|
||||
int i;
|
||||
char *p;
|
||||
size_t n;
|
||||
for (p = NULL, n = i = 0; i < lines->n; ++i) {
|
||||
if (!lines->p[i].n) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
if (*lines->p[i].p == '@') {
|
||||
break;
|
||||
}
|
||||
if (i) {
|
||||
p = realloc(p, ++n);
|
||||
p[n - 1] = ' ';
|
||||
}
|
||||
p = realloc(p, n + lines->p[i].n);
|
||||
memcpy(p + n, lines->p[i].p, lines->p[i].n);
|
||||
n += lines->p[i].n;
|
||||
}
|
||||
p = realloc(p, n + 1);
|
||||
p[n] = '\0';
|
||||
jd->title = p;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int ExtractText(struct Javadown *jd, struct Lines *lines, int i) {
|
||||
int j;
|
||||
char *p;
|
||||
size_t n;
|
||||
for (p = NULL, n = j = 0; i + j < lines->n; ++j) {
|
||||
if (lines->p[i + j].n && lines->p[i + j].p[0] == '@') break;
|
||||
if (j) {
|
||||
p = realloc(p, ++n);
|
||||
p[n - 1] = '\n';
|
||||
}
|
||||
p = realloc(p, n + lines->p[i + j].n);
|
||||
memcpy(p + n, lines->p[i + j].p, lines->p[i + j].n);
|
||||
n += lines->p[i + j].n;
|
||||
}
|
||||
p = realloc(p, n + 1);
|
||||
p[n] = '\0';
|
||||
jd->text = p;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void ExtractTags(struct Javadown *jd, struct Lines *lines, int i) {
|
||||
size_t n;
|
||||
char *p, *tag, *text, *p2;
|
||||
unsigned taglen, textlen, n2;
|
||||
for (p = NULL, n = 0; i < lines->n; ++i) {
|
||||
if (!lines->p[i].n) continue;
|
||||
if (lines->p[i].p[0] != '@') continue;
|
||||
tag = lines->p[i].p + 1;
|
||||
taglen = GetTagLen(tag, lines->p[i].n - 1);
|
||||
if (!taglen) continue;
|
||||
text = tag + taglen;
|
||||
tag = strndup(tag, taglen);
|
||||
textlen = lines->p[i].n - 1 - taglen;
|
||||
while (textlen && isspace(*text)) {
|
||||
++text;
|
||||
--textlen;
|
||||
}
|
||||
text = strndup(text, textlen);
|
||||
while (i + 1 < lines->n &&
|
||||
(!lines->p[i + 1].n || lines->p[i + 1].p[0] != '@')) {
|
||||
++i;
|
||||
p2 = lines->p[i].p;
|
||||
n2 = lines->p[i].n;
|
||||
if (n2 && *p2 == '\t') {
|
||||
p2 += 1;
|
||||
n2 -= 1;
|
||||
}
|
||||
if (n2 >= 4 && !memcmp(p2, " ", 4)) {
|
||||
p2 += 4;
|
||||
n2 -= 4;
|
||||
}
|
||||
text = realloc(text, textlen + 1 + n2 + 1);
|
||||
text[textlen] = '\n';
|
||||
memcpy(text + textlen + 1, p2, n2);
|
||||
textlen += 1 + n2;
|
||||
text[textlen] = '\0';
|
||||
}
|
||||
AppendTag(&jd->tags);
|
||||
jd->tags.p[jd->tags.n - 1].tag = tag;
|
||||
jd->tags.p[jd->tags.n - 1].text = text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses javadown.
|
||||
*
|
||||
* @param data should point to text inside the slash star markers
|
||||
* @param size is length of data in bytes
|
||||
* @return object that should be passed to FreeJavadown()
|
||||
*/
|
||||
struct Javadown *ParseJavadown(const char *data, size_t size) {
|
||||
int i;
|
||||
char *p;
|
||||
struct Lines lines;
|
||||
struct Javadown *jd;
|
||||
memset(&lines, 0, sizeof(lines));
|
||||
jd = calloc(1, sizeof(struct Javadown));
|
||||
p = strndup(data, size);
|
||||
SplitLines(&lines, p);
|
||||
RemoveTrailingWhitespace(&lines);
|
||||
jd->isfileoverview = ConsumeFileOverview(&lines);
|
||||
i = ExtractTitle(jd, &lines);
|
||||
i = ExtractText(jd, &lines, i);
|
||||
ExtractTags(jd, &lines, i);
|
||||
free(lines.p);
|
||||
free(p);
|
||||
return jd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees object returned by ParseJavadown().
|
||||
*/
|
||||
void FreeJavadown(struct Javadown *jd) {
|
||||
int i;
|
||||
if (jd) {
|
||||
for (i = 0; i < jd->tags.n; ++i) {
|
||||
free(jd->tags.p[i].tag);
|
||||
free(jd->tags.p[i].text);
|
||||
}
|
||||
free(jd->tags.p);
|
||||
free(jd->title);
|
||||
free(jd->text);
|
||||
free(jd);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_JAVADOWN_H_
|
||||
#define COSMOPOLITAN_TOOL_BUILD_LIB_JAVADOWN_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct Javadown {
|
||||
bool isfileoverview;
|
||||
char *title;
|
||||
char *text;
|
||||
struct JavadownTags {
|
||||
size_t n;
|
||||
struct JavadownTag {
|
||||
char *tag;
|
||||
char *text;
|
||||
} * p;
|
||||
} tags;
|
||||
};
|
||||
|
||||
struct Javadown *ParseJavadown(const char *, size_t);
|
||||
void FreeJavadown(struct Javadown *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_JAVADOWN_H_ */
|
|
@ -43,6 +43,27 @@
|
|||
"\\)*>")
|
||||
0 ,c-doc-markup-face-name prepend nil)))
|
||||
|
||||
;; (defconst javadown-font-lock-doc-comments
|
||||
;; `(("{@[a-z]+[^}\n\r]*}" ; "{@foo ...}" markup.
|
||||
;; 0 ,c-doc-markup-face-name prepend nil)
|
||||
;; ("^\\(/\\*\\)?\\(\\s \\|\\*\\)*\\(@[a-z]+\\)" ; "@foo ..." markup.
|
||||
;; 3 ,c-doc-markup-face-name prepend nil)
|
||||
;; (,(concat "</?\\sw" ; HTML tags.
|
||||
;; "\\("
|
||||
;; (concat "\\sw\\|\\s \\|[=\n\r*.:]\\|"
|
||||
;; "\"[^\"]*\"\\|'[^']*'")
|
||||
;; "\\)*>")
|
||||
;; 0 ,c-doc-markup-face-name prepend nil)
|
||||
;; ;; ("&\\(\\sw\\|[.:]\\)+;" ; HTML entities.
|
||||
;; ;; 0 ,c-doc-markup-face-name prepend nil)
|
||||
;; ;; Fontify remaining markup characters as invalid. Note
|
||||
;; ;; that the Javadoc spec is hazy about when "@" is
|
||||
;; ;; allowed in non-markup use. [jart: we like markdown]
|
||||
;; ;; (,(lambda (limit)
|
||||
;; ;; (c-find-invalid-doc-markup "[<>&]\\|{@" limit))
|
||||
;; ;; 0 'font-lock-warning-face prepend nil)
|
||||
;; ))
|
||||
|
||||
(defconst javadown-font-lock-keywords
|
||||
`((,(lambda (limit)
|
||||
(c-font-lock-doc-comments "/\\*\\*" limit
|
||||
|
|
Loading…
Reference in New Issue