Further refine documentation
parent
1bc3a25505
commit
548dcb9f08
8
Makefile
8
Makefile
|
@ -312,8 +312,12 @@ o/cosmopolitan.h: \
|
|||
$(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS))
|
||||
@ACTION=ROLLUP TARGET=$@ build/do $^ >$@
|
||||
|
||||
o/cosmopolitan.html: o/$(MODE)/third_party/chibicc/chibicc.com.dbg
|
||||
o/$(MODE)/third_party/chibicc/chibicc.com.dbg -J -fno-common -include libc/integral/normalize.inc -o $@ $(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))
|
||||
o/cosmopolitan.html: \
|
||||
o/$(MODE)/third_party/chibicc/chibicc.com.dbg \
|
||||
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))
|
||||
o/$(MODE)/third_party/chibicc/chibicc.com.dbg -J \
|
||||
-fno-common -include libc/integral/normalize.inc -o $@ \
|
||||
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS)))
|
||||
|
||||
# UNSPECIFIED PREREQUISITES TUTORIAL
|
||||
#
|
||||
|
|
|
@ -54,6 +54,7 @@ EXAMPLES_DIRECTDEPS = \
|
|||
LIBC_NT_KERNELBASE \
|
||||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_OHMYPLUS \
|
||||
LIBC_RAND \
|
||||
LIBC_RUNTIME \
|
||||
|
|
120
examples/fld.c
120
examples/fld.c
|
@ -1,120 +0,0 @@
|
|||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/inttypes.h"
|
||||
#include "libc/literal.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static const char kHeaderBin[] = "\
|
||||
/\t┌sign\n\
|
||||
/\t│ ┌exponent\n\
|
||||
/\t│ │ ┌intpart\n\
|
||||
/\t│ │ │ ┌fraction\n\
|
||||
/\t│ │ │ │\n\
|
||||
/\t│┌┴────────────┐│┌┴────────────────────────────────────────────────────────────┐\n";
|
||||
|
||||
static const char kHeaderHex[] = "\
|
||||
/\t ┌sign/exponent\n\
|
||||
/\t │ ┌intpart/fraction\n\
|
||||
/\t │ │\n\
|
||||
/\t┌┴─┐┌┴─────────────┐\n";
|
||||
|
||||
void dobin(const char *op, long double x, FILE *f) {
|
||||
uint16_t hi;
|
||||
uint64_t lo;
|
||||
uint8_t buf[16];
|
||||
memcpy(buf, &x, sizeof(x));
|
||||
memcpy(&lo, &buf[0], sizeof(lo));
|
||||
memcpy(&hi, &buf[8], sizeof(hi));
|
||||
fprintf(f, "/\t%016" PRIb16 "%064" PRIb64 " %-8s % 17.14Lf\n", hi, lo, op, x);
|
||||
}
|
||||
|
||||
void dohex(const char *op, long double x, FILE *f) {
|
||||
uint16_t hi;
|
||||
uint64_t lo;
|
||||
uint8_t buf[16];
|
||||
memcpy(buf, &x, sizeof(x));
|
||||
memcpy(&lo, &buf[0], sizeof(lo));
|
||||
memcpy(&hi, &buf[8], sizeof(hi));
|
||||
fprintf(f, "/\t%04" PRIx16 "%016" PRIx64 " %-8s % 17.14Lf\n", hi, lo, op, x);
|
||||
}
|
||||
|
||||
#define DOBIN(OP) \
|
||||
dobin(" " #OP, OP(), stdout); \
|
||||
dobin("-" #OP, -OP(), stdout)
|
||||
|
||||
#define DOHEX(OP) \
|
||||
dohex(" " #OP, OP(), stdout); \
|
||||
dohex("-" #OP, -OP(), stdout)
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
fputs(kHeaderBin, stdout);
|
||||
DOBIN(fldz);
|
||||
DOBIN(fld1);
|
||||
DOBIN(fldpi);
|
||||
DOBIN(fldl2t);
|
||||
DOBIN(fldlg2);
|
||||
DOBIN(fldln2);
|
||||
DOBIN(fldl2e);
|
||||
fputc('\n', stdout);
|
||||
fputs(kHeaderHex, stdout);
|
||||
DOHEX(fldz);
|
||||
DOHEX(fld1);
|
||||
DOHEX(fldpi);
|
||||
DOHEX(fldl2t);
|
||||
DOHEX(fldlg2);
|
||||
DOHEX(fldln2);
|
||||
DOHEX(fldl2e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
//
|
||||
// ┌sign
|
||||
// │ ┌exponent
|
||||
// │ │ ┌intpart
|
||||
// │ │ │ ┌fraction
|
||||
// │ │ │ │
|
||||
// │┌┴────────────┐│┌┴────────────────────────────────────────────────────────────┐
|
||||
// 00000000000000000000000000000000000000000000000000000000000000000000000000000000 fldz 0.0000000000000000000
|
||||
// 10000000000000000000000000000000000000000000000000000000000000000000000000000000 -fldz -0.0000000000000000000
|
||||
// 00111111111111111000000000000000000000000000000000000000000000000000000000000000 fld1 1.0000000000000000000
|
||||
// 10111111111111111000000000000000000000000000000000000000000000000000000000000000 -fld1 -1.0000000000000000000
|
||||
// 01000000000000001100100100001111110110101010001000100001011010001100001000110101 fldpi 3.1415926540000000000
|
||||
// 11000000000000001100100100001111110110101010001000100001011010001100001000110101 -fldpi -3.1415926540000000000
|
||||
// 01000000000000001101010010011010011110000100101111001101000110111000101011111110 fldl2t 3.3219280950000000000
|
||||
// 11000000000000001101010010011010011110000100101111001101000110111000101011111110 -fldl2t -3.3219280950000000000
|
||||
// 00111111111111011001101000100000100110101000010011111011110011111111011110011001 fldlg2 0.3010299960000000000
|
||||
// 10111111111111011001101000100000100110101000010011111011110011111111011110011001 -fldlg2 -0.3010299960000000000
|
||||
// 00111111111111101011000101110010000101111111011111010001110011110111100110101100 fldln2 0.6931471810000000000
|
||||
// 10111111111111101011000101110010000101111111011111010001110011110111100110101100 -fldln2 -0.6931471810000000000
|
||||
// 00111111111111111011100010101010001110110010100101011100000101111111000010111100 fldl2e 1.4426950410000000000
|
||||
// 10111111111111111011100010101010001110110010100101011100000101111111000010111100 -fldl2e -1.4426950410000000000
|
||||
//
|
||||
// ┌sign/exponent
|
||||
// │ ┌intpart/fraction
|
||||
// │ │
|
||||
// ┌┴─┐┌┴─────────────┐
|
||||
// 00000000000000000000 fldz 0.0000000000000000000
|
||||
// 80000000000000000000 -fldz -0.0000000000000000000
|
||||
// 3fff8000000000000000 fld1 1.0000000000000000000
|
||||
// bfff8000000000000000 -fld1 -1.0000000000000000000
|
||||
// 4000c90fdaa22168c235 fldpi 3.1415926540000000000
|
||||
// c000c90fdaa22168c235 -fldpi -3.1415926540000000000
|
||||
// 4000d49a784bcd1b8afe fldl2t 3.3219280950000000000
|
||||
// c000d49a784bcd1b8afe -fldl2t -3.3219280950000000000
|
||||
// 3ffd9a209a84fbcff799 fldlg2 0.3010299960000000000
|
||||
// bffd9a209a84fbcff799 -fldlg2 -0.3010299960000000000
|
||||
// 3ffeb17217f7d1cf79ac fldln2 0.6931471810000000000
|
||||
// bffeb17217f7d1cf79ac -fldln2 -0.6931471810000000000
|
||||
// 3fffb8aa3b295c17f0bc fldl2e 1.4426950410000000000
|
||||
// bfffb8aa3b295c17f0bc -fldl2e -1.4426950410000000000
|
|
@ -98,25 +98,37 @@ typedef float __m128_u _Vector_size(16) forcealign(1) mayalias;
|
|||
│ cosmopolitan § it's a trap! » sse » scalar ops ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
forceinline __m128 _mm_add_ss(__m128 m128_0, __m128 m128_1) {
|
||||
m128_0[0] += m128_1[0];
|
||||
return m128_0;
|
||||
}
|
||||
#define _mm_add_ss(m128_0, m128_1) \
|
||||
({ \
|
||||
__m128 a = m128_0; \
|
||||
__m128 b = m128_0; \
|
||||
a[0] += b[0]; \
|
||||
a; \
|
||||
})
|
||||
|
||||
forceinline __m128 _mm_sub_ss(__m128 m128_0, __m128 m128_1) {
|
||||
m128_0[0] -= m128_1[0];
|
||||
return m128_0;
|
||||
}
|
||||
#define _mm_sub_ss(m128_0, m128_1) \
|
||||
({ \
|
||||
__m128 a = m128_0; \
|
||||
__m128 b = m128_0; \
|
||||
a[0] -= b[0]; \
|
||||
a; \
|
||||
})
|
||||
|
||||
forceinline __m128 _mm_mul_ss(__m128 m128_0, __m128 m128_1) {
|
||||
m128_0[0] *= m128_1[0];
|
||||
return m128_0;
|
||||
}
|
||||
#define _mm_mul_ss(m128_0, m128_1) \
|
||||
({ \
|
||||
__m128 a = m128_0; \
|
||||
__m128 b = m128_0; \
|
||||
a[0] *= b[0]; \
|
||||
a; \
|
||||
})
|
||||
|
||||
forceinline __m128 _mm_div_ss(__m128 m128_0, __m128 m128_1) {
|
||||
m128_0[0] /= m128_1[0];
|
||||
return m128_0;
|
||||
}
|
||||
#define _mm_div_ss(m128_0, m128_1) \
|
||||
({ \
|
||||
__m128 a = m128_0; \
|
||||
__m128 b = m128_0; \
|
||||
a[0] /= b[0]; \
|
||||
a; \
|
||||
})
|
||||
|
||||
#define _mm_rcp_ss(M128) __builtin_ia32_rcpss((__v4sf)(M128)) /*~1/x*/
|
||||
#define _mm_sqrt_ss(M128) __builtin_ia32_sqrtss((__v4sf)(M128)) /*sqrt𝑥*/
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
|
||||
#define va_end(AP)
|
||||
#define va_copy(DST, SRC) ((DST)[0] = (SRC)[0])
|
||||
#define va_start(AP, LAST) \
|
||||
do { \
|
||||
*(AP) = *(struct __va *)__va_area__; \
|
||||
#define va_start(AP, LAST) \
|
||||
do { \
|
||||
*(AP) = *(struct __va_list *)__va_area__; \
|
||||
} while (0)
|
||||
|
||||
#define va_arg(AP, TYPE) \
|
||||
(*(TYPE *)__va_arg(AP, sizeof(TYPE), _Alignof(TYPE), \
|
||||
__builtin_reg_class(TYPE)))
|
||||
|
||||
typedef struct __va va_list[1];
|
||||
typedef struct __va_list va_list[1];
|
||||
|
|
|
@ -73,7 +73,7 @@ void vflogf_onfail(FILE *f) {
|
|||
fseek(f, SEEK_SET, 0);
|
||||
f->beg = f->end = 0;
|
||||
clearerr(f);
|
||||
(fprintf)(f, "performed emergency log truncation: %s\r\n", strerror(err));
|
||||
(fprintf)(f, "performed emergency log truncation: %s\n", strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,10 +112,10 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
}
|
||||
(vfprintf)(f, fmt, va);
|
||||
va_end(va);
|
||||
fputs("\r\n", f);
|
||||
fputs("\n", f);
|
||||
if (level == kLogFatal) {
|
||||
__start_fatal(file, line);
|
||||
(dprintf)(STDERR_FILENO, "fatal error see logfile\r\n");
|
||||
(dprintf)(STDERR_FILENO, "fatal error see logfile\n");
|
||||
__die();
|
||||
unreachable;
|
||||
}
|
||||
|
|
22
libc/math.h
22
libc/math.h
|
@ -285,28 +285,6 @@ void sincos(double, double *, double *);
|
|||
void sincosf(float, float *, float *);
|
||||
void sincosl(long double, long double *, long double *);
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § mathematics » x87 ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
#define fldz() __X87_CONST(fldz, 0x0p+0)
|
||||
#define fld1() __X87_CONST(fld1, 0x8p-3)
|
||||
#define fldpi() __X87_CONST(fldpi, M_PI)
|
||||
#define fldl2t() __X87_CONST(fldl2t, M_LOG2_10)
|
||||
#define fldlg2() __X87_CONST(fldlg2, M_LOG10_2)
|
||||
#define fldln2() __X87_CONST(fldln2, M_LN2)
|
||||
#define fldl2e() __X87_CONST(fldl2e, M_LOG2E)
|
||||
#ifdef __x86__
|
||||
#define __X87_CONST(OP, VALUE) \
|
||||
({ \
|
||||
long double St0##OP; \
|
||||
asm(#OP : "=t"(St0##OP)); \
|
||||
St0##OP; \
|
||||
})
|
||||
#else
|
||||
#define __X87_CONST(OP, VALUE) VALUE
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_MATH_H_ */
|
||||
|
|
|
@ -21,15 +21,15 @@
|
|||
|
||||
/ Returns binary logarithm of integer 𝑥.
|
||||
/
|
||||
/ 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
|
||||
/ 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
|
||||
/
|
||||
/ @param rsi:rdi is 128-bit unsigned 𝑥 value
|
||||
/ @return eax number in range [0,128) or undef if 𝑥 is 0
|
||||
|
|
|
@ -409,7 +409,7 @@ int WSARecvFrom(uint64_t s, const struct NtIovec *out_lpBuffers,
|
|||
uint32_t *inout_fromsockaddrlen,
|
||||
struct NtOverlapped *opt_inout_lpOverlapped,
|
||||
const NtWsaOverlappedCompletionRoutine opt_lpCompletionRoutine)
|
||||
paramsnonnull((2, 5, 6, 7));
|
||||
paramsnonnull((2, 5));
|
||||
|
||||
int WSARecvDisconnect(uint64_t s,
|
||||
const struct NtIovec *opt_lpInboundDisconnectData);
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
#include "libc/macros.h"
|
||||
#include "libc/runtime/valist.h"
|
||||
|
||||
static void *__va_arg_mem(struct __va *ap, size_t sz, size_t align) {
|
||||
static void *__va_arg_mem(struct __va_list *ap, size_t sz, size_t align) {
|
||||
void *r = (void *)ROUNDUP((intptr_t)ap->overflow_arg_area, align);
|
||||
ap->overflow_arg_area = (void *)ROUNDUP((intptr_t)r + sz, 8);
|
||||
return r;
|
||||
}
|
||||
|
||||
void *__va_arg(struct __va *ap, size_t sz, unsigned align, unsigned k) {
|
||||
void *__va_arg(struct __va_list *ap, size_t sz, unsigned align, unsigned k) {
|
||||
void *r;
|
||||
switch (k) {
|
||||
case 0:
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct __va {
|
||||
struct __va_list {
|
||||
uint32_t gp_offset;
|
||||
uint32_t fp_offset;
|
||||
void *overflow_arg_area;
|
||||
void *reg_save_area;
|
||||
};
|
||||
|
||||
void *__va_arg(struct __va *, size_t, unsigned, unsigned);
|
||||
void *__va_arg(struct __va_list *, size_t, unsigned, unsigned);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/chibicc/file.h"
|
||||
#include "third_party/gdtoa/gdtoa.h"
|
||||
#include "tool/build/lib/elfwriter.h"
|
||||
|
||||
|
@ -443,49 +445,6 @@ static unsigned Hash(const void *p, unsigned long n) {
|
|||
return MAX(1, h);
|
||||
}
|
||||
|
||||
static bool StartsWith(const char *s, const char *prefix) {
|
||||
for (;;) {
|
||||
if (!*prefix) return true;
|
||||
if (!*s) return false;
|
||||
if (*s++ != *prefix++) return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool EndsWith(const char *s, const char *suffix) {
|
||||
size_t n, m;
|
||||
n = strlen(s);
|
||||
m = strlen(suffix);
|
||||
if (m > n) return false;
|
||||
return !memcmp(s + n - m, suffix, m);
|
||||
}
|
||||
|
||||
static char *Format(const char *fmt, ...) {
|
||||
char *res;
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
vasprintf(&res, fmt, va);
|
||||
va_end(va);
|
||||
return res;
|
||||
}
|
||||
|
||||
static char *DirName(const char *path) {
|
||||
return dirname(strdup(path));
|
||||
}
|
||||
|
||||
static char *JoinPaths(const char *path, const char *other) {
|
||||
if (!*other) {
|
||||
return strdup(path);
|
||||
} else if (!*path) {
|
||||
return strdup(other);
|
||||
} else if (StartsWith(other, "/") || !strcmp(path, ".")) {
|
||||
return strdup(other);
|
||||
} else if (EndsWith(path, "/")) {
|
||||
return Format("%s%s", path, other);
|
||||
} else {
|
||||
return Format("%s/%s", path, other);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsPunctMergeable(int c) {
|
||||
switch (c) {
|
||||
case ';':
|
||||
|
@ -613,11 +572,11 @@ static void ReadFlags(struct As *a, int argc, char *argv[]) {
|
|||
for (i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "-o")) {
|
||||
a->outpath = StrDup(a, argv[++i]);
|
||||
} else if (StartsWith(argv[i], "-o")) {
|
||||
} else if (startswith(argv[i], "-o")) {
|
||||
a->outpath = StrDup(a, argv[i] + 2);
|
||||
} else if (!strcmp(argv[i], "-I")) {
|
||||
SaveString(&a->incpaths, strdup(argv[++i]));
|
||||
} else if (StartsWith(argv[i], "-I")) {
|
||||
} else if (startswith(argv[i], "-I")) {
|
||||
SaveString(&a->incpaths, strdup(argv[i] + 2));
|
||||
} else if (!strcmp(argv[i], "-Z")) {
|
||||
a->inhibiterr = true;
|
||||
|
@ -677,72 +636,6 @@ static int ReadCharLiteral(struct Slice *buf, int c, char *p, int *i) {
|
|||
}
|
||||
}
|
||||
|
||||
static void CanonicalizeNewline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
while (p[i]) {
|
||||
if (p[i] == '\r' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
p[j++] = '\n';
|
||||
} else if (p[i] == '\r') {
|
||||
i++;
|
||||
p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
static void RemoveBackslashNewline(char *p) {
|
||||
int i, j, n;
|
||||
for (i = j = n = 0; p[i];) {
|
||||
if (p[i] == '\\' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
n++;
|
||||
} else if (p[i] == '\n') {
|
||||
p[j++] = p[i++];
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
static char *ReadFile(const char *path) {
|
||||
char *p;
|
||||
FILE *fp;
|
||||
int buflen, nread, end, n;
|
||||
if (!strcmp(path, "-")) {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) return NULL;
|
||||
}
|
||||
buflen = 4096;
|
||||
nread = 0;
|
||||
p = calloc(1, buflen);
|
||||
for (;;) {
|
||||
end = buflen - 2;
|
||||
n = fread(p + nread, 1, end - nread, fp);
|
||||
if (n == 0) break;
|
||||
nread += n;
|
||||
if (nread == end) {
|
||||
buflen *= 2;
|
||||
p = realloc(p, buflen);
|
||||
}
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
if (nread > 0 && p[nread - 1] == '\\') {
|
||||
p[nread - 1] = '\n';
|
||||
} else if (nread == 0 || p[nread - 1] != '\n') {
|
||||
p[nread++] = '\n';
|
||||
}
|
||||
p[nread] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
static void PrintLocation(struct As *a) {
|
||||
fprintf(stderr,
|
||||
"%s:%d:: ", a->strings.p[a->sauces.p[a->things.p[a->i].s].path],
|
||||
|
@ -768,7 +661,7 @@ static char *FindInclude(struct As *a, const char *file) {
|
|||
char *path;
|
||||
struct stat st;
|
||||
for (i = 0; i < a->incpaths.n; ++i) {
|
||||
path = JoinPaths(a->incpaths.p[i], file);
|
||||
path = xjoinpaths(a->incpaths.p[i], file);
|
||||
if (stat(path, &st) != -1 && S_ISREG(st.st_mode)) return path;
|
||||
free(path);
|
||||
}
|
||||
|
@ -780,10 +673,10 @@ static void Tokenize(struct As *a, int path) {
|
|||
char *p, *path2;
|
||||
struct Slice buf;
|
||||
bool bol, isfloat, isfpu;
|
||||
p = SaveString(&a->strings, ReadFile(a->strings.p[path]));
|
||||
if (!memcmp(p, "\357\273\277", 3)) p += 3;
|
||||
CanonicalizeNewline(p);
|
||||
RemoveBackslashNewline(p);
|
||||
p = SaveString(&a->strings, read_file(a->strings.p[path]));
|
||||
p = skip_bom(p);
|
||||
canonicalize_newline(p);
|
||||
remove_backslash_newline(p);
|
||||
line = 1;
|
||||
bol = true;
|
||||
while ((c = *p)) {
|
||||
|
@ -1031,7 +924,7 @@ static void OnSymbol(struct As *a, int name) {
|
|||
static void OnLocalLabel(struct As *a, int id) {
|
||||
int i;
|
||||
char *name;
|
||||
name = Format(".Label.%d", a->counter++);
|
||||
name = xasprintf(".Label.%d", a->counter++);
|
||||
SaveString(&a->strings, name);
|
||||
AppendSlice(a);
|
||||
a->slices.p[a->slices.n - 1].p = name;
|
||||
|
@ -1796,13 +1689,13 @@ static int GrabSection(struct As *a, int name, int flags, int type) {
|
|||
static void OnSection(struct As *a, struct Slice s) {
|
||||
int name, flags, type;
|
||||
name = SliceDup(a, GetSlice(a));
|
||||
if (StartsWith(a->strings.p[name], ".text")) {
|
||||
if (startswith(a->strings.p[name], ".text")) {
|
||||
flags = SHF_ALLOC | SHF_EXECINSTR;
|
||||
type = SHT_PROGBITS;
|
||||
} else if (StartsWith(a->strings.p[name], ".data")) {
|
||||
} else if (startswith(a->strings.p[name], ".data")) {
|
||||
flags = SHF_ALLOC | SHF_WRITE;
|
||||
type = SHT_PROGBITS;
|
||||
} else if (StartsWith(a->strings.p[name], ".bss")) {
|
||||
} else if (startswith(a->strings.p[name], ".bss")) {
|
||||
flags = SHF_ALLOC | SHF_WRITE;
|
||||
type = SHT_NOBITS;
|
||||
} else {
|
||||
|
@ -2372,7 +2265,7 @@ static noinline void OpBsu(struct As *a, struct Slice opname, int op) {
|
|||
OpBsuImpl(a, opname, op);
|
||||
}
|
||||
|
||||
static int OpF6(struct As *a, struct Slice s, int reg) {
|
||||
static noinline int OpF6Impl(struct As *a, struct Slice s, int reg) {
|
||||
int modrm, imm, disp;
|
||||
modrm = ParseModrm(a, &disp);
|
||||
reg |= GetOpSize(a, s, modrm, 1) << 3;
|
||||
|
@ -2380,6 +2273,10 @@ static int OpF6(struct As *a, struct Slice s, int reg) {
|
|||
return reg;
|
||||
}
|
||||
|
||||
static noinline int OpF6(struct As *a, struct Slice s, int reg) {
|
||||
return OpF6Impl(a, s, reg);
|
||||
}
|
||||
|
||||
static void OnTest(struct As *a, struct Slice s) {
|
||||
int reg, modrm, imm, disp;
|
||||
if (IsPunct(a, a->i, '$')) {
|
||||
|
@ -2571,8 +2468,8 @@ static bool HasXmmOnLine(struct As *a) {
|
|||
int i;
|
||||
for (i = 0; !IsPunct(a, a->i + i, ';'); ++i) {
|
||||
if (IsSlice(a, a->i + i) && a->slices.p[a->things.p[a->i + i].i].n >= 4 &&
|
||||
(StartsWith(a->slices.p[a->things.p[a->i + i].i].p, "xmm") ||
|
||||
StartsWith(a->slices.p[a->things.p[a->i + i].i].p, "%xmm"))) {
|
||||
(startswith(a->slices.p[a->things.p[a->i + i].i].p, "xmm") ||
|
||||
startswith(a->slices.p[a->things.p[a->i + i].i].p, "%xmm"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -4002,7 +3899,7 @@ void Assembler(int argc, char *argv[]) {
|
|||
a = NewAssembler();
|
||||
ReadFlags(a, argc, argv);
|
||||
SaveString(&a->incpaths, strdup("."));
|
||||
SaveString(&a->incpaths, DirName(a->strings.p[a->inpath]));
|
||||
SaveString(&a->incpaths, xdirname(a->strings.p[a->inpath]));
|
||||
Tokenize(a, a->inpath);
|
||||
/* PrintThings(a); */
|
||||
Assemble(a);
|
||||
|
|
|
@ -130,9 +130,6 @@ Token *tokenize_string_literal(Token *, Type *);
|
|||
bool consume(Token **, Token *, char *, size_t);
|
||||
bool equal(Token *, char *, size_t);
|
||||
void convert_pp_tokens(Token *);
|
||||
void remove_backslash_newline(char *);
|
||||
void canonicalize_newline(char *);
|
||||
char *read_file(char *);
|
||||
int read_escaped_char(char **, char *);
|
||||
|
||||
#define UNREACHABLE() error("internal error at %s:%d", __FILE__, __LINE__)
|
||||
|
@ -450,6 +447,7 @@ struct Type {
|
|||
int align; // alignment
|
||||
bool is_unsigned; // unsigned or signed
|
||||
bool is_atomic; // true if _Atomic
|
||||
bool is_const; // const
|
||||
bool is_ms_abi; // microsoft abi
|
||||
Type *origin; // for type compatibility check
|
||||
// Pointer-to or array-of type. We intentionally use the same member
|
||||
|
|
|
@ -79,7 +79,8 @@ static void SerializeJavadown(struct Buffer *buf, struct Javadown *jd) {
|
|||
}
|
||||
|
||||
static char *DescribeScalar(struct Type *ty, char *name) {
|
||||
return xasprintf("%s%s%s", ty->is_atomic ? "_Atomic " : "",
|
||||
return xasprintf("%s%s%s%s", ty->is_atomic ? "_Atomic " : "",
|
||||
ty->is_const ? "const " : "",
|
||||
ty->is_unsigned ? "unsigned " : "", name);
|
||||
}
|
||||
|
||||
|
@ -134,7 +135,7 @@ static char *DescribeType(struct Type *ty) {
|
|||
return strdup("ANONYMOUS-UNION");
|
||||
}
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
return strdup("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,6 +179,7 @@ static void SerializeDox(struct DoxWriter *dox, Obj *prog) {
|
|||
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]->ty->is_variadic);
|
||||
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);
|
||||
|
@ -248,6 +250,7 @@ static void SerializeAsmdown(struct DoxWriter *dox, struct Asmdown *ad,
|
|||
SerializeStr(&dox->buf, filename);
|
||||
SerializeInt(&dox->buf, ad->symbols.p[i].line);
|
||||
SerializeInt(&dox->buf, true); // TODO: is_function
|
||||
SerializeInt(&dox->buf, false); // is_variadic
|
||||
SerializeInt(&dox->buf, false); // TODO: is_weak
|
||||
SerializeInt(&dox->buf, false); // is_inline
|
||||
SerializeInt(&dox->buf, false); // is_noreturn
|
||||
|
|
|
@ -47,6 +47,7 @@ struct Dox {
|
|||
char *path;
|
||||
int line;
|
||||
bool is_function;
|
||||
bool is_variadic;
|
||||
bool is_weak;
|
||||
bool is_inline;
|
||||
bool is_noreturn;
|
||||
|
@ -177,6 +178,7 @@ static void DeserializeObject(struct Dox *dox, struct DoxObject *o) {
|
|||
o->path = DeserializeStr(dox);
|
||||
o->line = DeserializeInt(dox);
|
||||
o->is_function = DeserializeInt(dox);
|
||||
o->is_variadic = DeserializeInt(dox);
|
||||
o->is_weak = DeserializeInt(dox);
|
||||
o->is_inline = DeserializeInt(dox);
|
||||
o->is_noreturn = DeserializeInt(dox);
|
||||
|
@ -210,7 +212,7 @@ static void DeserializeMacro(struct Dox *dox, struct DoxMacro *m) {
|
|||
}
|
||||
}
|
||||
|
||||
static void DeserializeDox(struct Dox *dox) {
|
||||
static void DeserializeDox(struct Dox *dox, const char *path) {
|
||||
int i, j, n;
|
||||
i = dox->objects.n;
|
||||
n = DeserializeInt(dox);
|
||||
|
@ -242,7 +244,7 @@ static void ReadDox(struct Dox *dox, const StringArray *files) {
|
|||
CHECK_NE(MAP_FAILED,
|
||||
(map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||
dox->p = map;
|
||||
DeserializeDox(dox);
|
||||
DeserializeDox(dox, files->data[i]);
|
||||
munmap(map, st.st_size);
|
||||
}
|
||||
close(fd);
|
||||
|
@ -317,15 +319,19 @@ static void IndexDox(struct Dox *dox) {
|
|||
CompareDoxIndexEntry, dox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes HTML entities and interprets basic Markdown syntax.
|
||||
*/
|
||||
static void PrintText(FILE *f, const char *s) {
|
||||
int c;
|
||||
bool bol, pre, ul0, ul2, bt1, bt2;
|
||||
for (bt1 = bt2 = ul2 = ul0 = pre = false, bol = true;;) {
|
||||
bool bol, pre, ul0, ul2, ol0, ol2, bt1, bt2;
|
||||
for (bt1 = bt2 = ul2 = ul0 = ol2 = ol0 = pre = false, bol = true;;) {
|
||||
switch ((c = *s++)) {
|
||||
case '\0':
|
||||
if (bt1 || bt2) fprintf(f, "</code>");
|
||||
if (pre) fprintf(f, "</pre>");
|
||||
if (ul0 || ul2) fprintf(f, "</ul>");
|
||||
if (ol0 || ol2) fprintf(f, "</ol>");
|
||||
return;
|
||||
case '&':
|
||||
fprintf(f, "&");
|
||||
|
@ -368,14 +374,19 @@ static void PrintText(FILE *f, const char *s) {
|
|||
bol = false;
|
||||
break;
|
||||
case '\n':
|
||||
if (!pre && !ul0 && !ul2 && *s == '\n') {
|
||||
if (!pre && !ul0 && !ul2 && !ol0 && !ol2 && *s == '\n') {
|
||||
fprintf(f, "\n<p>");
|
||||
bol = true;
|
||||
} else if (pre && s[0] != '\n' &&
|
||||
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
|
||||
fprintf(f, "</pre>\n");
|
||||
pre = false;
|
||||
bol = true;
|
||||
} else if (pre && s[0] != '\n') {
|
||||
if (s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ') {
|
||||
fprintf(f, "</pre>\n");
|
||||
pre = false;
|
||||
bol = true;
|
||||
} else {
|
||||
fprintf(f, "\n");
|
||||
bol = false;
|
||||
s += 4;
|
||||
}
|
||||
} else if (ul0 && s[0] == '-' && s[1] == ' ') {
|
||||
fprintf(f, "\n<li>");
|
||||
s += 2;
|
||||
|
@ -394,29 +405,65 @@ static void PrintText(FILE *f, const char *s) {
|
|||
fprintf(f, "\n</ul>\n");
|
||||
bol = true;
|
||||
ul2 = false;
|
||||
} else if (ol0 && ('0' <= s[0] && s[0] <= '9') && s[1] == '.' &&
|
||||
s[2] == ' ') {
|
||||
fprintf(f, "\n<li>");
|
||||
s += 3;
|
||||
bol = false;
|
||||
} else if (ol2 && s[0] == ' ' && s[1] == ' ' &&
|
||||
('0' <= s[2] && s[2] <= '9') && s[3] == '.' && s[3] == ' ') {
|
||||
fprintf(f, "\n<li>");
|
||||
s += 5;
|
||||
bol = false;
|
||||
} else if (ol0 && s[0] != '\n' && (s[0] != ' ' || s[1] != ' ')) {
|
||||
fprintf(f, "\n</ol>\n");
|
||||
bol = true;
|
||||
ol0 = false;
|
||||
} else if (ol2 && s[0] != '\n' &&
|
||||
(s[0] != ' ' || s[1] != ' ' || s[2] != ' ' || s[3] != ' ')) {
|
||||
fprintf(f, "\n</ol>\n");
|
||||
bol = true;
|
||||
ol2 = false;
|
||||
} else {
|
||||
fprintf(f, "\n");
|
||||
bol = true;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
if (bol && !ul0 && !ul2 && s[0] == ' ') {
|
||||
if (bol && !ul0 && !ul2 && !ol0 && !ol2 && s[0] == ' ') {
|
||||
ul0 = true;
|
||||
fprintf(f, "<ul><li>");
|
||||
++s;
|
||||
} else {
|
||||
fprintf(f, "-");
|
||||
}
|
||||
bol = false;
|
||||
break;
|
||||
case '1':
|
||||
if (bol && !ol0 && !ol2 && !ul0 && !ul2 && s[0] == '.' && s[1] == ' ') {
|
||||
ol0 = true;
|
||||
fprintf(f, "<ol><li>");
|
||||
s += 2;
|
||||
} else {
|
||||
fprintf(f, "1");
|
||||
}
|
||||
bol = false;
|
||||
break;
|
||||
case ' ':
|
||||
if (bol && !pre && s[0] == ' ' && s[1] == ' ' && s[2] == ' ') {
|
||||
pre = true;
|
||||
fprintf(f, "<pre> ");
|
||||
} else if (bol && !ul0 && !ul2 && s[0] == ' ' && s[1] == '-' &&
|
||||
s[2] == ' ') {
|
||||
fprintf(f, "<pre>");
|
||||
s += 3;
|
||||
} else if (bol && !ul0 && !ul2 && !ol0 && !ol2 && s[0] == ' ' &&
|
||||
s[1] == '-' && s[2] == ' ') {
|
||||
ul2 = true;
|
||||
fprintf(f, "<ul><li>");
|
||||
s += 3;
|
||||
} else if (bol && !ul0 && !ul2 && !ol0 && !ol2 && s[0] == ' ' &&
|
||||
('0' <= s[1] && s[1] <= '9') && s[2] == '.' && s[3] == ' ') {
|
||||
ol2 = true;
|
||||
fprintf(f, "<ol><li>");
|
||||
s += 4;
|
||||
} else {
|
||||
fprintf(f, " ");
|
||||
}
|
||||
|
@ -469,6 +516,9 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
.nav {\n\
|
||||
margin-bottom: 0;\n\
|
||||
}\n\
|
||||
.toc {\n\
|
||||
overflow-x: auto;\n\
|
||||
}\n\
|
||||
.toc a {\n\
|
||||
text-decoration: none;\n\
|
||||
}\n\
|
||||
|
@ -514,7 +564,10 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
</style>\n\
|
||||
\n\
|
||||
<header>\n\
|
||||
<img width=\"196\" height=\"105\" src=\"//storage.googleapis.com/justine/cosmopolitan/cosmopolitan.png\" alt=\"honeybadger\">\n\
|
||||
<img width=\"196\" height=\"105\"\n\
|
||||
src=\"//storage.googleapis.com/justine/cosmopolitan/cosmopolitan.png\"\n\
|
||||
title=\"cosmopolitan honeybadger\"\n\
|
||||
alt=\"honeybadger\">\n\
|
||||
<h1>cosmopolitan libc</h1>\n\
|
||||
<span>build-once run-anywhere c without devops</span>\n\
|
||||
</header>\n\
|
||||
|
@ -533,7 +586,7 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
\n\
|
||||
<table class=\"dox\" width=\"960\">\n\
|
||||
<tr>\n\
|
||||
<td width=\"320\" valign=\"top\" class=\"toc\">\n\
|
||||
<td width=\"283\" valign=\"top\" class=\"toc\">\n\
|
||||
");
|
||||
|
||||
/* // lefthand index: objects */
|
||||
|
@ -581,7 +634,7 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
|
||||
// righthand contents
|
||||
fprintf(f, "<td width=\"640\" valign=\"top\">\n");
|
||||
fprintf(f, "<td width=\"667\" valign=\"top\">\n");
|
||||
for (i = 0; i < dox->index.n; ++i) {
|
||||
if (dox->index.p[i].t == kObject) {
|
||||
o = dox->objects.p + dox->index.p[i].i;
|
||||
|
@ -606,7 +659,8 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
|
||||
// parameters
|
||||
if (o->is_function && (o->params.n || HasTag(o->javadown, "param"))) {
|
||||
if (o->is_function &&
|
||||
(o->is_variadic || o->params.n || HasTag(o->javadown, "param"))) {
|
||||
fprintf(f, "<div class=\"tag\">\n");
|
||||
fprintf(f, "<span class=\"tagname\">@param</span>\n");
|
||||
fprintf(f, "<dl>\n");
|
||||
|
@ -641,6 +695,9 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (o->is_variadic) {
|
||||
fprintf(f, "<dt>...\n");
|
||||
}
|
||||
fprintf(f, "</dl>\n");
|
||||
fprintf(f, "</div>\n"); // .tag
|
||||
}
|
||||
|
@ -676,6 +733,17 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
}
|
||||
}
|
||||
|
||||
// type
|
||||
if (!o->is_function) {
|
||||
fprintf(f, "<div class=\"tag\">\n");
|
||||
fprintf(f, "<span class=\"tagname\">@type</span>\n");
|
||||
fprintf(f, "<dl>\n");
|
||||
fprintf(f, "<dt>");
|
||||
PrintText(f, o->type);
|
||||
fprintf(f, "</dl>\n");
|
||||
fprintf(f, "</div>\n"); // .tag
|
||||
}
|
||||
|
||||
// tags
|
||||
if (o->javadown) {
|
||||
for (k = 0; k < o->javadown->tags.n; ++k) {
|
||||
|
@ -714,21 +782,18 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
if (i) fprintf(f, "<hr>");
|
||||
fprintf(f, "<div id=\"%s\" class=\"api\">\n", m->name);
|
||||
fprintf(f, "<h3><a href=\"#%s\">%s</a></h3>", m->name, m->name);
|
||||
|
||||
// title
|
||||
if (m->javadown && *m->javadown->title) {
|
||||
fprintf(f, "<p>");
|
||||
PrintText(f, m->javadown->title);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
// text
|
||||
if (m->javadown && *m->javadown->text) {
|
||||
fprintf(f, "<p>");
|
||||
PrintText(f, m->javadown->text);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
// parameters
|
||||
if (!m->is_objlike && (m->params.n || HasTag(m->javadown, "param"))) {
|
||||
fprintf(f, "<div class=\"tag\">\n");
|
||||
|
@ -767,7 +832,6 @@ static void PrintDox(struct Dox *dox, FILE *f) {
|
|||
fprintf(f, "</dl>\n");
|
||||
fprintf(f, "</div>\n"); // .tag
|
||||
}
|
||||
|
||||
fprintf(f, "</div>\n"); /* class=".api" */
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
#include "third_party/chibicc/chibicc.h"
|
||||
|
||||
// Slurps contents of file.
|
||||
char *read_file(const char *path) {
|
||||
char *p;
|
||||
FILE *fp;
|
||||
int buflen, nread, end, n;
|
||||
if (!strcmp(path, "-")) {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) return NULL;
|
||||
}
|
||||
buflen = 4096;
|
||||
nread = 0;
|
||||
p = calloc(1, buflen);
|
||||
for (;;) {
|
||||
end = buflen - 2;
|
||||
n = fread(p + nread, 1, end - nread, fp);
|
||||
if (n == 0) break;
|
||||
nread += n;
|
||||
if (nread == end) {
|
||||
buflen *= 2;
|
||||
p = realloc(p, buflen);
|
||||
}
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
if (nread > 0 && p[nread - 1] == '\\') {
|
||||
p[nread - 1] = '\n';
|
||||
} else if (nread == 0 || p[nread - 1] != '\n') {
|
||||
p[nread++] = '\n';
|
||||
}
|
||||
p[nread] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
char *skip_bom(char *p) {
|
||||
// UTF-8 texts may start with a 3-byte "BOM" marker sequence.
|
||||
// If exists, just skip them because they are useless bytes.
|
||||
// (It is actually not recommended to add BOM markers to UTF-8
|
||||
// texts, but it's not uncommon particularly on Windows.)
|
||||
if (!memcmp(p, "\357\273\277", 3)) p += 3;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Replaces \r or \r\n with \n.
|
||||
void canonicalize_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
while (p[i]) {
|
||||
if (p[i] == '\r' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
p[j++] = '\n';
|
||||
} else if (p[i] == '\r') {
|
||||
i++;
|
||||
p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
// Removes backslashes followed by a newline.
|
||||
void remove_backslash_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
// We want to keep the number of newline characters so that
|
||||
// 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++;
|
||||
} else if (p[i] == '\n') {
|
||||
p[j++] = p[i++];
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
p[j] = '\0';
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_CHIBICC_FILE_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_CHIBICC_FILE_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
char *skip_bom(char *);
|
||||
char *read_file(const char *);
|
||||
void remove_backslash_newline(char *);
|
||||
void canonicalize_newline(char *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_CHIBICC_FILE_H_ */
|
|
@ -48,7 +48,6 @@ typedef struct {
|
|||
bool is_static;
|
||||
bool is_extern;
|
||||
bool is_inline;
|
||||
bool is_const;
|
||||
bool is_tls;
|
||||
bool is_weak;
|
||||
bool is_ms_abi;
|
||||
|
@ -233,6 +232,8 @@ static Node *new_ulong(long val, Token *tok) {
|
|||
}
|
||||
|
||||
static Node *new_var_node(Obj *var, Token *tok) {
|
||||
if (!var) DebugBreak();
|
||||
CHECK_NOTNULL(var);
|
||||
Node *node = new_node(ND_VAR, tok);
|
||||
node->var = var;
|
||||
return node;
|
||||
|
@ -647,6 +648,7 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
|
|||
};
|
||||
Type *ty = copy_type(ty_int);
|
||||
int counter = 0;
|
||||
bool is_const = false;
|
||||
bool is_atomic = false;
|
||||
while (is_typename(tok)) {
|
||||
// Handle storage class specifiers.
|
||||
|
@ -682,7 +684,7 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
|
|||
goto Continue;
|
||||
}
|
||||
if (CONSUME(&tok, tok, "const")) {
|
||||
if (attr) attr->is_const = true;
|
||||
is_const = true;
|
||||
goto Continue;
|
||||
}
|
||||
// These keywords are recognized but ignored.
|
||||
|
@ -841,6 +843,10 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
|
|||
ty = copy_type(ty);
|
||||
ty->is_atomic = true;
|
||||
}
|
||||
/* if (attr && is_const) { */
|
||||
/* ty = copy_type(ty); */
|
||||
/* ty->is_const = true; */
|
||||
/* } */
|
||||
*rest = tok;
|
||||
return ty;
|
||||
}
|
||||
|
@ -873,6 +879,7 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) {
|
|||
Type head = {};
|
||||
Type *cur = &head;
|
||||
bool is_variadic = false;
|
||||
enter_scope();
|
||||
while (!EQUAL(tok, ")")) {
|
||||
if (cur != &head) tok = skip(tok, ',');
|
||||
if (EQUAL(tok, "...")) {
|
||||
|
@ -884,6 +891,9 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) {
|
|||
Type *ty2 = declspec(&tok, tok, NULL);
|
||||
ty2 = declarator(&tok, tok, ty2);
|
||||
Token *name = ty2->name;
|
||||
if (name) {
|
||||
new_lvar(strndup(name->loc, name->len), ty2);
|
||||
}
|
||||
if (ty2->kind == TY_ARRAY) {
|
||||
// "array of T" is converted to "pointer to T" only in the parameter
|
||||
// context. For example, *argv[] is converted to **argv by this.
|
||||
|
@ -897,6 +907,7 @@ static Type *func_params(Token **rest, Token *tok, Type *ty) {
|
|||
}
|
||||
cur = cur->next = copy_type(ty2);
|
||||
}
|
||||
leave_scope();
|
||||
if (cur == &head) is_variadic = true;
|
||||
ty = func_type(ty);
|
||||
ty->params = head.next;
|
||||
|
@ -2471,6 +2482,16 @@ static Node *shift(Token **rest, Token *tok) {
|
|||
}
|
||||
}
|
||||
|
||||
static Node *get_vla_size(Type *ty, Token *tok) {
|
||||
if (ty->vla_size) {
|
||||
return new_var_node(ty->vla_size, tok);
|
||||
} else {
|
||||
Node *lhs = compute_vla_size(ty, tok);
|
||||
Node *rhs = new_var_node(ty->vla_size, tok);
|
||||
return new_binary(ND_COMMA, lhs, rhs, tok);
|
||||
}
|
||||
}
|
||||
|
||||
// In C, `+` operator is overloaded to perform the pointer arithmetic.
|
||||
// If p is a pointer, p+n adds not n but sizeof(*p)*n to the value of p,
|
||||
// so that p+n points to the location n elements (not bytes) ahead of p.
|
||||
|
@ -2491,8 +2512,7 @@ static Node *new_add(Node *lhs, Node *rhs, Token *tok) {
|
|||
}
|
||||
// VLA + num
|
||||
if (lhs->ty->base->kind == TY_VLA) {
|
||||
rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok),
|
||||
tok);
|
||||
rhs = new_binary(ND_MUL, rhs, get_vla_size(lhs->ty->base, tok), tok);
|
||||
return new_binary(ND_ADD, lhs, rhs, tok);
|
||||
}
|
||||
// ptr + num
|
||||
|
@ -2509,8 +2529,7 @@ static Node *new_sub(Node *lhs, Node *rhs, Token *tok) {
|
|||
return new_binary(ND_SUB, lhs, rhs, tok);
|
||||
// VLA + num
|
||||
if (lhs->ty->base->kind == TY_VLA) {
|
||||
rhs = new_binary(ND_MUL, rhs, new_var_node(lhs->ty->base->vla_size, tok),
|
||||
tok);
|
||||
rhs = new_binary(ND_MUL, rhs, get_vla_size(lhs->ty->base, tok), tok);
|
||||
add_type(rhs);
|
||||
Node *node = new_binary(ND_SUB, lhs, rhs, tok);
|
||||
node->ty = lhs->ty;
|
||||
|
@ -3044,7 +3063,9 @@ static Node *primary(Token **rest, Token *tok) {
|
|||
if (EQUAL(tok, "sizeof")) {
|
||||
Node *node = unary(rest, tok->next);
|
||||
add_type(node);
|
||||
if (node->ty->kind == TY_VLA) return new_var_node(node->ty->vla_size, tok);
|
||||
if (node->ty->kind == TY_VLA) {
|
||||
return get_vla_size(node->ty, tok);
|
||||
}
|
||||
return new_ulong(node->ty->size, tok);
|
||||
}
|
||||
if ((EQUAL(tok, "_Alignof") || EQUAL(tok, "__alignof__")) &&
|
||||
|
@ -3416,8 +3437,9 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
|
|||
fn->is_no_instrument_function |= attr->is_no_instrument_function;
|
||||
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->javadown = fn->javadown ?: current_javadown;
|
||||
fn->is_root = !(fn->is_static && fn->is_inline);
|
||||
fn->javadown = fn->javadown ?: current_javadown;
|
||||
current_javadown = NULL;
|
||||
if (consume_attribute(&tok, tok, "asm")) {
|
||||
tok = skip(tok, '(');
|
||||
fn->asmname = ConsumeStringLiteral(&tok, tok);
|
||||
|
@ -3526,16 +3548,20 @@ static void scan_globals(void) {
|
|||
globals = head.next;
|
||||
}
|
||||
|
||||
static char *prefix_builtin(const char *name) {
|
||||
return xstrcat("__builtin_", name);
|
||||
}
|
||||
|
||||
static Obj *declare0(char *name, Type *ret) {
|
||||
if (!opt_no_builtin) new_gvar(name, func_type(ret));
|
||||
return new_gvar(xstrcat("__builtin_", name), func_type(ret));
|
||||
return new_gvar(prefix_builtin(name), func_type(ret));
|
||||
}
|
||||
|
||||
static Obj *declare1(char *name, Type *ret, Type *p1) {
|
||||
Type *ty = func_type(ret);
|
||||
ty->params = copy_type(p1);
|
||||
if (!opt_no_builtin) new_gvar(name, ty);
|
||||
return new_gvar(xstrcat("__builtin_", name), ty);
|
||||
return new_gvar(prefix_builtin(name), ty);
|
||||
}
|
||||
|
||||
static Obj *declare2(char *name, Type *ret, Type *p1, Type *p2) {
|
||||
|
@ -3543,7 +3569,7 @@ static Obj *declare2(char *name, Type *ret, Type *p1, Type *p2) {
|
|||
ty->params = copy_type(p1);
|
||||
ty->params->next = copy_type(p2);
|
||||
if (!opt_no_builtin) new_gvar(name, ty);
|
||||
return new_gvar(xstrcat("__builtin_", name), ty);
|
||||
return new_gvar(prefix_builtin(name), ty);
|
||||
}
|
||||
|
||||
static Obj *declare3(char *s, Type *r, Type *a, Type *b, Type *c) {
|
||||
|
@ -3552,7 +3578,7 @@ static Obj *declare3(char *s, Type *r, Type *a, Type *b, Type *c) {
|
|||
ty->params->next = copy_type(b);
|
||||
ty->params->next->next = copy_type(c);
|
||||
if (!opt_no_builtin) new_gvar(s, ty);
|
||||
return new_gvar(xstrcat("__builtin_", s), ty);
|
||||
return new_gvar(prefix_builtin(s), ty);
|
||||
}
|
||||
|
||||
static void math0(char *name) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
asm(".ident\t\"hello\"");
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
|
|
|
@ -1,7 +1,30 @@
|
|||
#include "libc/macros.h"
|
||||
#include "third_party/chibicc/test/test.h"
|
||||
|
||||
int index1d(int xn, int p[xn], int x) {
|
||||
return p[x];
|
||||
}
|
||||
|
||||
int index2d(int yn, int xn, int (*p)[yn][xn], int y, int x) {
|
||||
return (*p)[y][x];
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
ASSERT(30, ({
|
||||
int a[5] = {00, 10, 20, 30, 40, 50};
|
||||
index1d(5, a, 3);
|
||||
}));
|
||||
|
||||
/* ASSERT(210, ({ */
|
||||
/* int a[3][3] = { */
|
||||
/* {000, 010, 020}, */
|
||||
/* {100, 110, 120}, */
|
||||
/* {200, 210, 220}, */
|
||||
/* }; */
|
||||
/* index2d(3, 3, a, 2, 1); */
|
||||
/* })); */
|
||||
|
||||
ASSERT(20, ({
|
||||
int n = 5;
|
||||
int x[n];
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "third_party/chibicc/chibicc.h"
|
||||
#include "third_party/chibicc/file.h"
|
||||
|
||||
#define LOOKINGAT(TOK, OP) (!memcmp(TOK, OP, sizeof(OP) - 1))
|
||||
|
||||
|
@ -610,40 +611,6 @@ Token *tokenize(File *file) {
|
|||
return head.next;
|
||||
}
|
||||
|
||||
// Returns the contents of a given file.
|
||||
char *read_file(char *path) {
|
||||
FILE *fp;
|
||||
if (strcmp(path, "-") == 0) {
|
||||
// By convention, read from stdin if a given filename is "-".
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) return NULL;
|
||||
}
|
||||
int buflen = 4096;
|
||||
int nread = 0;
|
||||
char *buf = calloc(1, buflen);
|
||||
// Read the entire file.
|
||||
for (;;) {
|
||||
int end = buflen - 2; // extra 2 bytes for the trailing "\n\0"
|
||||
int n = fread(buf + nread, 1, end - nread, fp);
|
||||
if (n == 0) break;
|
||||
nread += n;
|
||||
if (nread == end) {
|
||||
buflen *= 2;
|
||||
buf = realloc(buf, buflen);
|
||||
}
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
// Make sure that the last logical line is properly terminated with '\n'.
|
||||
if (nread > 0 && buf[nread - 1] == '\\')
|
||||
buf[nread - 1] = '\n';
|
||||
else if (nread == 0 || buf[nread - 1] != '\n')
|
||||
buf[nread++] = '\n';
|
||||
buf[nread] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
File **get_input_files(void) {
|
||||
return input_files;
|
||||
}
|
||||
|
@ -657,68 +624,6 @@ File *new_file(char *name, int file_no, char *contents) {
|
|||
return file;
|
||||
}
|
||||
|
||||
// Replaces \r or \r\n with \n.
|
||||
void canonicalize_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
while (p[i]) {
|
||||
if (p[i] == '\r' && p[i + 1] == '\n') {
|
||||
i += 2;
|
||||
p[j++] = '\n';
|
||||
} else if (p[i] == '\r') {
|
||||
i++;
|
||||
p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
// Removes backslashes followed by a newline.
|
||||
void remove_backslash_newline(char *p) {
|
||||
int i = 0, j = 0;
|
||||
// We want to keep the number of newline characters so that
|
||||
// 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++;
|
||||
} else if (p[i] == '\n') {
|
||||
p[j++] = p[i++];
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
} else {
|
||||
p[j++] = p[i++];
|
||||
}
|
||||
}
|
||||
for (; n > 0; n--) p[j++] = '\n';
|
||||
p[j] = '\0';
|
||||
}
|
||||
|
||||
static uint32_t read_universal_char(char *p, int len) {
|
||||
uint32_t c = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
@ -761,11 +666,7 @@ static void convert_universal_chars(char *p) {
|
|||
Token *tokenize_file(char *path) {
|
||||
char *p = read_file(path);
|
||||
if (!p) return NULL;
|
||||
// UTF-8 texts may start with a 3-byte "BOM" marker sequence.
|
||||
// If exists, just skip them because they are useless bytes.
|
||||
// (It is actually not recommended to add BOM markers to UTF-8
|
||||
// texts, but it's not uncommon particularly on Windows.)
|
||||
if (!memcmp(p, "\xef\xbb\xbf", 3)) p += 3;
|
||||
p = skip_bom(p);
|
||||
canonicalize_newline(p);
|
||||
remove_backslash_newline(p);
|
||||
convert_universal_chars(p);
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
asm(".ident\t\"\\n\\n\
|
||||
getopt (BSD-3)\\n\
|
||||
Copyright 1987, 1993, 1994 The Regents of the University of California\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
|
||||
/* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
|
@ -36,13 +30,16 @@ asm(".include \"libc/disclaimer.inc\"");
|
|||
* @(#)getopt.c 8.3 (Berkeley) 4/27/95
|
||||
* $FreeBSD: src/lib/libc/stdlib/getopt.c,v 1.8 2007/01/09 00:28:10 imp Exp $
|
||||
* $DragonFly: src/lib/libc/stdlib/getopt.c,v 1.7 2005/11/20 12:37:48 swildner
|
||||
*Exp $
|
||||
*/
|
||||
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
getopt (BSD-3)\\n\
|
||||
Copyright 1987, 1993, 1994 The Regents of the University of California\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
|
||||
STATIC_YOINK("_init_getopt");
|
||||
|
||||
#define BADCH (int)'?'
|
||||
|
@ -50,48 +47,65 @@ STATIC_YOINK("_init_getopt");
|
|||
|
||||
/**
|
||||
* If error message should be printed.
|
||||
* @see getopt()
|
||||
*/
|
||||
int opterr;
|
||||
|
||||
/**
|
||||
* Index into parent argv vector.
|
||||
* @see getopt()
|
||||
*/
|
||||
int optind;
|
||||
|
||||
/**
|
||||
* Character checked for validity.
|
||||
* @see getopt()
|
||||
*/
|
||||
int optopt;
|
||||
|
||||
/**
|
||||
* Reset getopt.
|
||||
* @see getopt()
|
||||
*/
|
||||
int optreset;
|
||||
|
||||
/**
|
||||
* Argument associated with option.
|
||||
* @see getopt()
|
||||
*/
|
||||
char *optarg;
|
||||
|
||||
/**
|
||||
* Option letter processing.
|
||||
*/
|
||||
char *getopt_place;
|
||||
|
||||
hidden char *getopt_place;
|
||||
char kGetoptEmsg[1] hidden;
|
||||
|
||||
/**
|
||||
* Parses argc/argv argument vector.
|
||||
* Parses argc/argv argument vector, e.g.
|
||||
*
|
||||
* while ((opt = getopt(argc, argv, "hvx:")) != -1) {
|
||||
* switch (opt) {
|
||||
* case 'x':
|
||||
* x = atoi(optarg);
|
||||
* break;
|
||||
* case 'v':
|
||||
* ++verbose;
|
||||
* break;
|
||||
* case 'h':
|
||||
* PrintUsage(EXIT_SUCCESS, stdout);
|
||||
* default:
|
||||
* PrintUsage(EX_USAGE, stderr);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @see optind
|
||||
* @see optarg
|
||||
*/
|
||||
int getopt(int nargc, char *const nargv[], const char *ostr) {
|
||||
char *oli; /* option letter list index */
|
||||
|
||||
/*
|
||||
* Some programs like cvs expect optind = 0 to trigger
|
||||
* a reset of getopt.
|
||||
*/
|
||||
if (optind == 0) optind = 1;
|
||||
|
||||
if (optreset || *getopt_place == 0) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
getopt_place = nargv[optind];
|
||||
|
@ -117,7 +131,6 @@ int getopt(int nargc, char *const nargv[], const char *ostr) {
|
|||
} else {
|
||||
optopt = *getopt_place++;
|
||||
}
|
||||
|
||||
/* See if option letter is one the caller wanted... */
|
||||
if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
|
||||
if (*getopt_place == 0) ++optind;
|
||||
|
@ -127,7 +140,6 @@ int getopt(int nargc, char *const nargv[], const char *ostr) {
|
|||
}
|
||||
return (BADCH);
|
||||
}
|
||||
|
||||
/* Does this option need an argument? */
|
||||
if (oli[1] != ':') {
|
||||
/* don't need argument */
|
||||
|
@ -152,5 +164,5 @@ int getopt(int nargc, char *const nargv[], const char *ostr) {
|
|||
getopt_place = kGetoptEmsg;
|
||||
++optind;
|
||||
}
|
||||
return (optopt); /* return option letter */
|
||||
return optopt; /* return option letter */
|
||||
}
|
||||
|
|
|
@ -1,444 +0,0 @@
|
|||
.\" $OpenBSD: getopt_long.3,v 1.21 2016/01/04 19:43:13 tb Exp $
|
||||
.\" $NetBSD: getopt_long.3,v 1.11 2002/10/02 10:54:19 wiz Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1988, 1991, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\" 3. Neither the name of the University nor the names of its contributors
|
||||
.\" may be used to endorse or promote products derived from this software
|
||||
.\" without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" @(#)getopt.3 8.5 (Berkeley) 4/27/95
|
||||
.\"
|
||||
.Dd $Mdocdate: January 4 2016 $
|
||||
.Dt GETOPT_LONG 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm getopt_long ,
|
||||
.Nm getopt_long_only
|
||||
.Nd get long options from command line argument list
|
||||
.Sh SYNOPSIS
|
||||
.In getopt.h
|
||||
.Vt extern char *optarg;
|
||||
.Vt extern int optind;
|
||||
.Vt extern int optopt;
|
||||
.Vt extern int opterr;
|
||||
.Vt extern int optreset;
|
||||
.Ft int
|
||||
.Fn getopt_long "int argc" "char * const *argv" "const char *optstring" "const struct option *longopts" "int *longindex"
|
||||
.Ft int
|
||||
.Fn getopt_long_only "int argc" "char * const *argv" "const char *optstring" "const struct option *longopts" "int *longindex"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn getopt_long
|
||||
function is similar to
|
||||
.Xr getopt 3
|
||||
but it accepts options in two forms: words and characters.
|
||||
The
|
||||
.Fn getopt_long
|
||||
function provides a superset of the functionality of
|
||||
.Xr getopt 3 .
|
||||
.Fn getopt_long
|
||||
can be used in two ways.
|
||||
In the first way, every long option understood by the program has a
|
||||
corresponding short option, and the option structure is only used to
|
||||
translate from long options to short options.
|
||||
When used in this fashion,
|
||||
.Fn getopt_long
|
||||
behaves identically to
|
||||
.Xr getopt 3 .
|
||||
This is a good way to add long option processing to an existing program
|
||||
with the minimum of rewriting.
|
||||
.Pp
|
||||
In the second mechanism, a long option sets a flag in the
|
||||
.Fa option
|
||||
structure passed, or will store a pointer to the command line argument
|
||||
in the
|
||||
.Fa option
|
||||
structure passed to it for options that take arguments.
|
||||
Additionally, the long option's argument may be specified as a single
|
||||
argument with an equal sign, e.g.
|
||||
.Bd -literal -offset indent
|
||||
$ myprogram --myoption=somevalue
|
||||
.Ed
|
||||
.Pp
|
||||
When a long option is processed, the call to
|
||||
.Fn getopt_long
|
||||
will return 0.
|
||||
For this reason, long option processing without
|
||||
shortcuts is not backwards compatible with
|
||||
.Xr getopt 3 .
|
||||
.Pp
|
||||
It is possible to combine these methods, providing for long options
|
||||
processing with short option equivalents for some options.
|
||||
Less frequently used options would be processed as long options only.
|
||||
.Pp
|
||||
Abbreviated long option names are accepted when
|
||||
.Fn getopt_long
|
||||
processes long options if the abbreviation is unique.
|
||||
An exact match is always preferred for a defined long option.
|
||||
.Pp
|
||||
The
|
||||
.Fn getopt_long
|
||||
call requires an array to be initialized describing the long
|
||||
options.
|
||||
Each element of the array is a structure:
|
||||
.Bd -literal -offset indent
|
||||
struct option {
|
||||
char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Fa name
|
||||
field should contain the option name without the leading double dash.
|
||||
.Pp
|
||||
The
|
||||
.Fa has_arg
|
||||
field should be one of:
|
||||
.Pp
|
||||
.Bl -tag -width "optional_argument" -compact -offset indent
|
||||
.It Dv no_argument
|
||||
no argument to the option is expected.
|
||||
.It Dv required_argument
|
||||
an argument to the option is required.
|
||||
.It Dv optional_argument
|
||||
an argument to the option may be presented.
|
||||
.El
|
||||
.Pp
|
||||
If
|
||||
.Fa flag
|
||||
is not
|
||||
.Dv NULL ,
|
||||
then the integer pointed to by it will be set to the value in the
|
||||
.Fa val
|
||||
field.
|
||||
If the
|
||||
.Fa flag
|
||||
field is
|
||||
.Dv NULL ,
|
||||
then the
|
||||
.Fa val
|
||||
field will be returned.
|
||||
Setting
|
||||
.Fa flag
|
||||
to
|
||||
.Dv NULL
|
||||
and setting
|
||||
.Fa val
|
||||
to the corresponding short option will make this function act just
|
||||
like
|
||||
.Xr getopt 3 .
|
||||
.Pp
|
||||
If the
|
||||
.Fa longindex
|
||||
field is not
|
||||
.Dv NULL ,
|
||||
then the integer pointed to by it will be set to the index of the long
|
||||
option relative to
|
||||
.Fa longopts .
|
||||
.Pp
|
||||
The last element of the
|
||||
.Fa longopts
|
||||
array has to be filled with zeroes.
|
||||
.Pp
|
||||
The
|
||||
.Fn getopt_long_only
|
||||
function behaves identically to
|
||||
.Fn getopt_long
|
||||
with the exception that long options may start with
|
||||
.Sq -
|
||||
in addition to
|
||||
.Sq -- .
|
||||
If an option starting with
|
||||
.Sq -
|
||||
does not match a long option but does match a single-character option,
|
||||
the single-character option is returned.
|
||||
.Sh RETURN VALUES
|
||||
If the
|
||||
.Fa flag
|
||||
field in
|
||||
.Li struct option
|
||||
is
|
||||
.Dv NULL ,
|
||||
.Fn getopt_long
|
||||
and
|
||||
.Fn getopt_long_only
|
||||
return the value specified in the
|
||||
.Fa val
|
||||
field, which is usually just the corresponding short option.
|
||||
If
|
||||
.Fa flag
|
||||
is not
|
||||
.Dv NULL ,
|
||||
these functions return 0 and store
|
||||
.Fa val
|
||||
in the location pointed to by
|
||||
.Fa flag .
|
||||
These functions return
|
||||
.Sq \&:
|
||||
if there was a missing option argument,
|
||||
.Sq \&?
|
||||
if the user specified an unknown or ambiguous option, and
|
||||
\-1 when the argument list has been exhausted.
|
||||
.Sh IMPLEMENTATION DIFFERENCES
|
||||
This section describes differences to the GNU implementation
|
||||
found in glibc-2.1.3:
|
||||
.Bl -bullet
|
||||
.It
|
||||
handling of
|
||||
.Ql -
|
||||
within the option string (not the first character):
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It GNU
|
||||
treats a
|
||||
.Ql -
|
||||
on the command line as a non-argument.
|
||||
.It OpenBSD
|
||||
a
|
||||
.Ql -
|
||||
within the option string matches a
|
||||
.Ql -
|
||||
(single dash) on the command line.
|
||||
This functionality is provided for backward compatibility with
|
||||
programs, such as
|
||||
.Xr su 1 ,
|
||||
that use
|
||||
.Ql -
|
||||
as an option flag.
|
||||
This practice is wrong, and should not be used in any current development.
|
||||
.El
|
||||
.It
|
||||
handling of
|
||||
.Ql ::
|
||||
in the option string in the presence of
|
||||
.Ev POSIXLY_CORRECT :
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It Both
|
||||
GNU and
|
||||
.Ox
|
||||
ignore
|
||||
.Ev POSIXLY_CORRECT
|
||||
here and take
|
||||
.Ql ::
|
||||
to mean the preceding option takes an optional argument.
|
||||
.El
|
||||
.It
|
||||
return value in case of missing argument if first character
|
||||
(after
|
||||
.Ql +
|
||||
or
|
||||
.Ql - )
|
||||
in the option string is not
|
||||
.Ql \&: :
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It GNU
|
||||
returns
|
||||
.Ql \&?
|
||||
.It OpenBSD
|
||||
returns
|
||||
.Ql \&:
|
||||
(since
|
||||
.Ox Ns 's
|
||||
.Xr getopt 3
|
||||
does).
|
||||
.El
|
||||
.It
|
||||
handling of
|
||||
.Ql --a
|
||||
in
|
||||
.Xr getopt 3 :
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It GNU
|
||||
parses this as option
|
||||
.Ql - ,
|
||||
option
|
||||
.Ql a .
|
||||
.It OpenBSD
|
||||
parses this as
|
||||
.Ql -- ,
|
||||
and returns \-1 (ignoring the
|
||||
.Ql a )
|
||||
(because the original
|
||||
.Fn getopt
|
||||
did.)
|
||||
.El
|
||||
.It
|
||||
setting of
|
||||
.Va optopt
|
||||
for long options with
|
||||
.Va flag
|
||||
.No non- Ns Dv NULL :
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It GNU
|
||||
sets
|
||||
.Va optopt
|
||||
to
|
||||
.Va val .
|
||||
.It OpenBSD
|
||||
sets
|
||||
.Va optopt
|
||||
to 0 (since
|
||||
.Va val
|
||||
would never be returned).
|
||||
.El
|
||||
.It
|
||||
handling of
|
||||
.Ql -W
|
||||
with
|
||||
.Ql W;
|
||||
in the option string in
|
||||
.Xr getopt 3
|
||||
(not
|
||||
.Fn getopt_long ) :
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It GNU
|
||||
causes a segmentation fault.
|
||||
.It OpenBSD
|
||||
no special handling is done;
|
||||
.Ql W;
|
||||
is interpreted as two separate options, neither of which take an argument.
|
||||
.El
|
||||
.It
|
||||
setting of
|
||||
.Va optarg
|
||||
for long options without an argument that are invoked via
|
||||
.Ql -W
|
||||
(with
|
||||
.Ql W;
|
||||
in the option string):
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It GNU
|
||||
sets
|
||||
.Va optarg
|
||||
to the option name (the argument of
|
||||
.Ql -W ) .
|
||||
.It OpenBSD
|
||||
sets
|
||||
.Va optarg
|
||||
to
|
||||
.Dv NULL
|
||||
(the argument of the long option).
|
||||
.El
|
||||
.It
|
||||
handling of
|
||||
.Ql -W
|
||||
with an argument that is not (a prefix to) a known long option
|
||||
(with
|
||||
.Ql W;
|
||||
in the option string):
|
||||
.Bl -tag -width "OpenBSD"
|
||||
.It GNU
|
||||
returns
|
||||
.Ql -W
|
||||
with
|
||||
.Va optarg
|
||||
set to the unknown option.
|
||||
.It OpenBSD
|
||||
treats this as an error (unknown option) and returns
|
||||
.Ql \&?
|
||||
with
|
||||
.Va optopt
|
||||
set to 0 and
|
||||
.Va optarg
|
||||
set to
|
||||
.Dv NULL
|
||||
(as GNU's man page documents).
|
||||
.El
|
||||
.It
|
||||
The error messages are different.
|
||||
.It
|
||||
.Ox
|
||||
does not permute the argument vector at the same points in
|
||||
the calling sequence as GNU does.
|
||||
The aspects normally used by the caller
|
||||
(ordering after \-1 is returned, value of
|
||||
.Va optind
|
||||
relative to current positions) are the same, though.
|
||||
(We do fewer variable swaps.)
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width Ev
|
||||
.It Ev POSIXLY_CORRECT
|
||||
If set, option processing stops when the first non-option is found and
|
||||
a leading
|
||||
.Sq +
|
||||
in the
|
||||
.Ar optstring
|
||||
is ignored.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
.Bd -literal
|
||||
int bflag, ch, fd;
|
||||
int daggerset;
|
||||
|
||||
/* options descriptor */
|
||||
static struct option longopts[] = {
|
||||
{ "buffy", no_argument, NULL, 'b' },
|
||||
{ "fluoride", required_argument, NULL, 'f' },
|
||||
{ "daggerset", no_argument, &daggerset, 1 },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
bflag = 0;
|
||||
while ((ch = getopt_long(argc, argv, "bf:", longopts, NULL)) != -1)
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
bflag = 1;
|
||||
break;
|
||||
case 'f':
|
||||
if ((fd = open(optarg, O_RDONLY, 0)) == -1)
|
||||
err(1, "unable to open %s", optarg);
|
||||
break;
|
||||
case 0:
|
||||
if (daggerset)
|
||||
fprintf(stderr, "Buffy will use her dagger to "
|
||||
"apply fluoride to dracula's teeth\en");
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr getopt 3
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Fn getopt_long
|
||||
and
|
||||
.Fn getopt_long_only
|
||||
functions first appeared in GNU libiberty.
|
||||
This implementation first appeared in
|
||||
.Ox 3.3 .
|
||||
.Sh BUGS
|
||||
The
|
||||
.Ar argv
|
||||
argument is not really
|
||||
.Dv const
|
||||
as its elements may be permuted (unless
|
||||
.Ev POSIXLY_CORRECT
|
||||
is set).
|
|
@ -1,553 +0,0 @@
|
|||
/**
|
||||
* This file has an inappropriate amount of legal text for its
|
||||
* complexity and should be rewritten. Consider using getopt().
|
||||
*/
|
||||
asm(".ident\t\"\\n\\n\
|
||||
getopt_long (Licensed BSD-4, MIT)\\n\
|
||||
Copyright (c) 2000 The NetBSD Foundation, Inc.\\n\
|
||||
Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>\\n\
|
||||
The getopt_long code is derived from software contributed to The\\n\
|
||||
NetBSD Foundation by Dieter Baron and Thomas Klausner. This code\\n\
|
||||
has received sponsorship from the U.S. Military. Special thanks\\n\
|
||||
to the OpenBSD, NetBSD, FreeBSD, and DragonFlyBSD teams.\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
|
||||
#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
|
||||
|
||||
/* clang-format off */
|
||||
/* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $*/
|
||||
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and 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.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the NetBSD
|
||||
* Foundation, Inc. and its contributors.
|
||||
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD: src/lib/libc/stdlib/getopt_long.c,v 1.15 2006/09/23 14:48:31 ache Exp $
|
||||
* $DragonFly: src/lib/libc/stdlib/getopt_long.c,v 1.14 2005/11/20 12:37:48 swildner Exp $
|
||||
*/
|
||||
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/log/bsd.h"
|
||||
|
||||
STATIC_YOINK("stoa");
|
||||
|
||||
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||
|
||||
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||
|
||||
/* return values */
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG2 ((*options == ':') ? (int)':' : (int)'?')
|
||||
#define INORDER (int)1
|
||||
|
||||
#define EMSG ""
|
||||
|
||||
#define NO_PREFIX (-1)
|
||||
#define D_PREFIX 0
|
||||
#define DD_PREFIX 1
|
||||
#define W_PREFIX 2
|
||||
|
||||
static int getopt_internal(int, char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int parse_long_options(char * const *, const char *,
|
||||
const struct option *, int *, int, int);
|
||||
static int gcd(int, int);
|
||||
static void permute_args(int, int, int, char * const *);
|
||||
|
||||
static char *place = EMSG; /* option letter processing */
|
||||
|
||||
/* XXX: set optreset to 1 rather than these two */
|
||||
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||
|
||||
/* Error messages */
|
||||
static int dash_prefix = NO_PREFIX;
|
||||
#define recargchar "option requires an argument -- %c"
|
||||
#define illoptchar "illegal option -- %c" /* From P1003.2 */
|
||||
#define gnuoptchar "invalid option -- %c"
|
||||
#define recargstring "option `%s%s' requires an argument"
|
||||
#define ambig "option `%s%.*s' is ambiguous"
|
||||
#define noarg "option `%s%.*s' doesn't allow an argument"
|
||||
#define illoptstring "unrecognized option `%s%s'"
|
||||
|
||||
/*
|
||||
* Compute the greatest common divisor of a and b.
|
||||
*/
|
||||
static int
|
||||
gcd(int a, int b)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = a % b;
|
||||
while (c != 0) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||
* in each block).
|
||||
*/
|
||||
static void
|
||||
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||
char * const *nargv)
|
||||
{
|
||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||
char *swap;
|
||||
|
||||
/*
|
||||
* compute lengths of blocks and number and size of cycles
|
||||
*/
|
||||
nnonopts = panonopt_end - panonopt_start;
|
||||
nopts = opt_end - panonopt_end;
|
||||
ncycle = gcd(nnonopts, nopts);
|
||||
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||
|
||||
for (i = 0; i < ncycle; i++) {
|
||||
cstart = panonopt_end+i;
|
||||
pos = cstart;
|
||||
for (j = 0; j < cyclelen; j++) {
|
||||
if (pos >= panonopt_end)
|
||||
pos -= nnonopts;
|
||||
else
|
||||
pos += nopts;
|
||||
swap = nargv[pos];
|
||||
/* LINTED const cast */
|
||||
(__DECONST(char **, nargv))[pos] = nargv[cstart];
|
||||
/* LINTED const cast */
|
||||
(__DECONST(char **, nargv))[cstart] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_long_options --
|
||||
* Parse long options in argc/argv argument vector.
|
||||
* Returns -1 if short_too is set and the option does not match long_options.
|
||||
*/
|
||||
static int
|
||||
parse_long_options(char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int short_too, int flags)
|
||||
{
|
||||
char *current_argv, *has_equal;
|
||||
char *current_dash;
|
||||
size_t current_argv_len;
|
||||
int i, match, exact_match, second_partial_match;
|
||||
|
||||
current_argv = place;
|
||||
switch (dash_prefix) {
|
||||
case D_PREFIX:
|
||||
current_dash = "-";
|
||||
break;
|
||||
case DD_PREFIX:
|
||||
current_dash = "--";
|
||||
break;
|
||||
case W_PREFIX:
|
||||
current_dash = "-W ";
|
||||
break;
|
||||
default:
|
||||
current_dash = "";
|
||||
break;
|
||||
}
|
||||
match = -1;
|
||||
exact_match = 0;
|
||||
second_partial_match = 0;
|
||||
|
||||
optind++;
|
||||
|
||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||
/* argument found (--option=arg) */
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
/* find matching long option */
|
||||
if (strncmp(current_argv, long_options[i].name,
|
||||
current_argv_len))
|
||||
continue;
|
||||
|
||||
if (strlen(long_options[i].name) == current_argv_len) {
|
||||
/* exact match */
|
||||
match = i;
|
||||
exact_match = 1;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is a known short option, don't allow
|
||||
* a partial match of a single character.
|
||||
*/
|
||||
if (short_too && current_argv_len == 1)
|
||||
continue;
|
||||
|
||||
if (match == -1) /* first partial match */
|
||||
match = i;
|
||||
else if ((flags & FLAG_LONGONLY) ||
|
||||
long_options[i].has_arg !=
|
||||
long_options[match].has_arg ||
|
||||
long_options[i].flag != long_options[match].flag ||
|
||||
long_options[i].val != long_options[match].val)
|
||||
second_partial_match = 1;
|
||||
}
|
||||
if (!exact_match && second_partial_match) {
|
||||
/* ambiguous abbreviation */
|
||||
if (PRINT_ERROR)
|
||||
(warnx)(ambig,
|
||||
current_dash,
|
||||
(int)current_argv_len,
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (match != -1) { /* option found */
|
||||
if (long_options[match].has_arg == no_argument
|
||||
&& has_equal) {
|
||||
if (PRINT_ERROR)
|
||||
(warnx)(noarg,
|
||||
current_dash,
|
||||
(int)current_argv_len,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else if (long_options[match].has_arg ==
|
||||
required_argument) {
|
||||
/*
|
||||
* optional argument doesn't use next nargv
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == NULL)) {
|
||||
/*
|
||||
* Missing argument; leading ':' indicates no error
|
||||
* should be generated.
|
||||
*/
|
||||
if (PRINT_ERROR)
|
||||
(warnx)(recargstring,
|
||||
current_dash,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
--optind;
|
||||
return (BADARG2);
|
||||
}
|
||||
} else { /* unknown option */
|
||||
if (short_too) {
|
||||
--optind;
|
||||
return (-1);
|
||||
}
|
||||
if (PRINT_ERROR)
|
||||
(warnx)(illoptstring,
|
||||
current_dash,
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (idx)
|
||||
*idx = match;
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
return (0);
|
||||
} else
|
||||
return (long_options[match].val);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_internal --
|
||||
* Parse argc/argv argument vector. Called by user level routines.
|
||||
*/
|
||||
static int
|
||||
getopt_internal(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int flags)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
int posixly_correct; /* no static, can be changed on the fly */
|
||||
|
||||
if (options == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||
* string begins with a '+'.
|
||||
*/
|
||||
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||
if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
else if (posixly_correct || *options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
if (*options == '+' || *options == '-')
|
||||
options++;
|
||||
|
||||
/*
|
||||
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||
* XXX using optreset. Work around this braindamage.
|
||||
*/
|
||||
if (optind == 0)
|
||||
optind = optreset = 1;
|
||||
|
||||
optarg = NULL;
|
||||
if (optreset)
|
||||
nonopt_start = nonopt_end = -1;
|
||||
start:
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc) { /* end of argument vector */
|
||||
place = EMSG;
|
||||
if (nonopt_end != -1) {
|
||||
/* do permutation, if we have to */
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
else if (nonopt_start != -1) {
|
||||
/*
|
||||
* If we skipped non-options, set optind
|
||||
* to the first of them.
|
||||
*/
|
||||
optind = nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
if (*(place = nargv[optind]) != '-' ||
|
||||
place[1] == '\0') {
|
||||
place = EMSG; /* found non-option */
|
||||
if (flags & FLAG_ALLARGS) {
|
||||
/*
|
||||
* GNU extension:
|
||||
* return non-option as argument to option 1
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
return (INORDER);
|
||||
}
|
||||
if (!(flags & FLAG_PERMUTE)) {
|
||||
/*
|
||||
* If no permutation wanted, stop parsing
|
||||
* at first non-option.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
/* do permutation */
|
||||
if (nonopt_start == -1)
|
||||
nonopt_start = optind;
|
||||
else if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
nonopt_start = optind -
|
||||
(nonopt_end - nonopt_start);
|
||||
nonopt_end = -1;
|
||||
}
|
||||
optind++;
|
||||
/* process next argument */
|
||||
goto start;
|
||||
}
|
||||
if (nonopt_start != -1 && nonopt_end == -1)
|
||||
nonopt_end = optind;
|
||||
|
||||
/*
|
||||
* If we have "-" do nothing, if "--" we are done.
|
||||
*/
|
||||
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||
optind++;
|
||||
place = EMSG;
|
||||
/*
|
||||
* We found an option (--), so if we skipped
|
||||
* non-options, we have to permute.
|
||||
*/
|
||||
if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check long options if:
|
||||
* 1) we were passed some
|
||||
* 2) the arg is not just "-"
|
||||
* 3) either the arg starts with -- we are getopt_long_only()
|
||||
*/
|
||||
if (long_options != NULL && place != nargv[optind] &&
|
||||
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||
short_too = 0;
|
||||
dash_prefix = D_PREFIX;
|
||||
if (*place == '-') {
|
||||
place++; /* --foo long option */
|
||||
dash_prefix = DD_PREFIX;
|
||||
} else if (*place != ':' && strchr(options, *place) != NULL)
|
||||
short_too = 1; /* could be short option too */
|
||||
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, short_too, flags);
|
||||
if (optchar != -1) {
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optchar = (int)*place++) == (int)':' ||
|
||||
(optchar == (int)'-' && *place != '\0') ||
|
||||
(oli = strchr(options, optchar)) == NULL) {
|
||||
/*
|
||||
* If the user specified "-" and '-' isn't listed in
|
||||
* options, return -1 (non-option) as per POSIX.
|
||||
* Otherwise, it is an unknown option character (or ':').
|
||||
*/
|
||||
if (optchar == (int)'-' && *place == '\0')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
if (PRINT_ERROR)
|
||||
(warnx)(posixly_correct ? illoptchar : gnuoptchar,
|
||||
optchar);
|
||||
optopt = optchar;
|
||||
return (BADCH);
|
||||
}
|
||||
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||
/* -W long-option */
|
||||
if (*place) /* no space */
|
||||
/* NOTHING */;
|
||||
else if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
(warnx)(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG2);
|
||||
} else /* white space */
|
||||
place = nargv[optind];
|
||||
dash_prefix = W_PREFIX;
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, 0, flags);
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
if (*++oli != ':') { /* doesn't take argument */
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* takes (optional) argument */
|
||||
optarg = NULL;
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (oli[1] != ':') { /* arg not optional */
|
||||
if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
(warnx)(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG2);
|
||||
} else
|
||||
optarg = nargv[optind];
|
||||
}
|
||||
place = EMSG;
|
||||
++optind;
|
||||
}
|
||||
/* dump back option letter */
|
||||
return (optchar);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE));
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long_only --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE|FLAG_LONGONLY));
|
||||
}
|
|
@ -25,6 +25,5 @@
|
|||
pop %rax
|
||||
mov %eax,opterr(%rip)
|
||||
mov %eax,optind(%rip)
|
||||
lea kGetoptEmsg(%rip),%rax
|
||||
mov %rax,getopt_place(%rip)
|
||||
movl $kGetoptEmsg,getopt_place(%rip)
|
||||
.init.end 201,_init_getopt
|
||||
|
|
|
@ -40,15 +40,15 @@ M(0, i, "true", True, 1, "1")
|
|||
M(0, i, "intmin", IntMin, INT128_MIN, "native integer minimum")
|
||||
M(0, i, "intmax", IntMax, INT128_MAX, "native integer maximum")
|
||||
M(0, f, "e", Euler, M_E, "𝑒")
|
||||
M(0, f, "pi", Fldpi, fldpi(), "π")
|
||||
M(0, f, "pi", Fldpi, M_PI, "π")
|
||||
M(0, f, "epsilon", Epsilon, EPSILON, "ɛ")
|
||||
M(0, f, "inf", Inf, INFINITY, "∞")
|
||||
M(0, f, "nan", Nan, NAN, "NAN")
|
||||
M(0, f, "-0", Negzero, -0., "wut")
|
||||
M(0, f, "l2t", Fldl2t, fldl2t(), "log₂10")
|
||||
M(0, f, "lg2", Fldlg2, fldlg2(), "log₁₀2")
|
||||
M(0, f, "ln2", Fldln2, fldln2(), "logₑ2")
|
||||
M(0, f, "l2e", Fldl2e, fldl2e(), "logₑ10")
|
||||
M(0, f, "l2t", Fldl2t, M_LOG2_10, "log₂10")
|
||||
M(0, f, "lg2", Fldlg2, M_LOG10_2, "log₁₀2")
|
||||
M(0, f, "ln2", Fldln2, M_LN2, "logₑ2")
|
||||
M(0, f, "l2e", Fldl2e, M_LOG2E, "logₑ10")
|
||||
|
||||
M(1, f, "sqrt", Sqrt, sqrtl(x), "√𝑥")
|
||||
M(1, f, "exp", Exp, expl(x), "𝑒ˣ")
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/shut.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/sysv/consts/splice.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
|
@ -251,7 +250,6 @@ static const struct NamedVector kLightings[] = {
|
|||
};
|
||||
|
||||
static plm_t *plm_;
|
||||
static FILE *fsock_;
|
||||
static float gamma_;
|
||||
static int volscale_;
|
||||
static enum Blur blur_;
|
||||
|
@ -1210,8 +1208,6 @@ static void PerformBestEffortIo(void) {
|
|||
struct pollfd fds[] = {
|
||||
{infd_, POLLIN},
|
||||
{outfd_, f1_ && f1_->n ? POLLOUT : 0},
|
||||
{fsock_ ? fileno(fsock_) : -1,
|
||||
fsock_ && favail(fsock_) < (NETBUFSIZ >> 1) ? POLLIN : 0},
|
||||
};
|
||||
pollms = MAX(0, AsMilliseconds(GetGraceTime()));
|
||||
DEBUGF("poll() ms=%,d", pollms);
|
||||
|
@ -1221,12 +1217,6 @@ static void PerformBestEffortIo(void) {
|
|||
if (toto) {
|
||||
if (fds[0].revents & (POLLIN | POLLERR)) ReadKeyboard();
|
||||
if (fds[1].revents & (POLLOUT | POLLERR)) WriteVideo();
|
||||
if (fds[2].revents & (POLLHUP | POLLERR)) {
|
||||
LOGIFNEG1(shutdown(fsock_->fd, SHUT_RD));
|
||||
fsock_ = NULL; /* plm destroys it */
|
||||
} else if (fds[2].revents & POLLIN) {
|
||||
freplenish(fsock_);
|
||||
}
|
||||
}
|
||||
} else if (errno == EINTR) {
|
||||
DEBUGF("poll() → EINTR");
|
||||
|
|
Loading…
Reference in New Issue