mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Get GNU GMP test suite fully passing
- Fix stdio fmemopen() buffer behaviors - Fix scanf() to return EOF when appropriate - Prefer fseek/ftell names over fseeko/ftello - Ensure locale field is always set in the TIB - Fix recent regression in vfprintf() return count - Make %n directive in scanf() have standard behavior
This commit is contained in:
parent
755ae64e73
commit
63a1636e1f
20 changed files with 228 additions and 51 deletions
|
@ -29,7 +29,8 @@
|
||||||
/**
|
/**
|
||||||
* Sets current directory.
|
* Sets current directory.
|
||||||
*
|
*
|
||||||
* This does *not* update the `PWD` environment variable.
|
* The current directory is shared by all threads in a process. This
|
||||||
|
* does not update the `PWD` environment variable.
|
||||||
*
|
*
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
* @raise ELOOP if a loop was detected resolving components of `path`
|
* @raise ELOOP if a loop was detected resolving components of `path`
|
||||||
|
|
|
@ -35,5 +35,4 @@ o/$(MODE)/libc: o/$(MODE)/libc/calls \
|
||||||
o/$(MODE)/libc/tinymath \
|
o/$(MODE)/libc/tinymath \
|
||||||
o/$(MODE)/libc/vga \
|
o/$(MODE)/libc/vga \
|
||||||
o/$(MODE)/libc/x \
|
o/$(MODE)/libc/x \
|
||||||
o/$(MODE)/libc/zipos \
|
|
||||||
$(LIBC_CHECKS)
|
$(LIBC_CHECKS)
|
||||||
|
|
|
@ -59,11 +59,11 @@ static void vflogf_onfail(FILE *f) {
|
||||||
(err == ENOSPC || err == EDQUOT || err == EFBIG) &&
|
(err == ENOSPC || err == EDQUOT || err == EFBIG) &&
|
||||||
(fstat(fileno_unlocked(f), &st) == -1 || st.st_size > kNontrivialSize)) {
|
(fstat(fileno_unlocked(f), &st) == -1 || st.st_size > kNontrivialSize)) {
|
||||||
ftruncate(fileno_unlocked(f), 0);
|
ftruncate(fileno_unlocked(f), 0);
|
||||||
fseeko_unlocked(f, SEEK_SET, 0);
|
fseek_unlocked(f, SEEK_SET, 0);
|
||||||
f->beg = f->end = 0;
|
f->beg = f->end = 0;
|
||||||
clearerr_unlocked(f);
|
clearerr_unlocked(f);
|
||||||
(fprintf_unlocked)(f, "performed emergency log truncation: %s\n",
|
fprintf_unlocked(f, "performed emergency log truncation: %s\n",
|
||||||
strerror(err));
|
strerror(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "libc/macros.internal.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/str/locale.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/thread/posixthread.internal.h"
|
#include "libc/thread/posixthread.internal.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
@ -191,6 +192,7 @@ textstartup void __enable_tls(void) {
|
||||||
tib->tib_errno = __errno;
|
tib->tib_errno = __errno;
|
||||||
tib->tib_strace = __strace;
|
tib->tib_strace = __strace;
|
||||||
tib->tib_ftrace = __ftrace;
|
tib->tib_ftrace = __ftrace;
|
||||||
|
tib->tib_locale = (intptr_t)&__c_dot_utf8_locale;
|
||||||
tib->tib_pthread = (pthread_t)&_pthread_main;
|
tib->tib_pthread = (pthread_t)&_pthread_main;
|
||||||
if (IsLinux() || IsXnuSilicon()) {
|
if (IsLinux() || IsXnuSilicon()) {
|
||||||
// gnu/systemd guarantees pid==tid for the main thread so we can
|
// gnu/systemd guarantees pid==tid for the main thread so we can
|
||||||
|
|
|
@ -25,8 +25,6 @@
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
// TODO(jart): POSIX says buffer needs to grow in write modes?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens buffer as stream.
|
* Opens buffer as stream.
|
||||||
*
|
*
|
||||||
|
@ -63,7 +61,9 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) {
|
||||||
}
|
}
|
||||||
f->fd = -1;
|
f->fd = -1;
|
||||||
f->buf = buf;
|
f->buf = buf;
|
||||||
f->end = size;
|
if (!(iomode & O_TRUNC)) {
|
||||||
|
f->end = size;
|
||||||
|
}
|
||||||
f->size = size;
|
f->size = size;
|
||||||
f->iomode = iomode;
|
f->iomode = iomode;
|
||||||
if (iomode & O_APPEND) {
|
if (iomode & O_APPEND) {
|
||||||
|
|
|
@ -35,14 +35,14 @@
|
||||||
* @returns 0 on success or -1 on error
|
* @returns 0 on success or -1 on error
|
||||||
* @threadsafe
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
int fseeko(FILE *f, int64_t offset, int whence) {
|
int fseek(FILE *f, int64_t offset, int whence) {
|
||||||
int rc;
|
int rc;
|
||||||
flockfile(f);
|
flockfile(f);
|
||||||
rc = fseeko_unlocked(f, offset, whence);
|
rc = fseek_unlocked(f, offset, whence);
|
||||||
STDIOTRACE("fseeko(%p, %'ld, %s) → %d %s", f, offset, DescribeWhence(whence),
|
STDIOTRACE("fseek(%p, %'ld, %s) → %d %s", f, offset, DescribeWhence(whence),
|
||||||
rc, DescribeStdioState(f->state));
|
rc, DescribeStdioState(f->state));
|
||||||
funlockfile(f);
|
funlockfile(f);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
__strong_reference(fseeko, fseek);
|
__strong_reference(fseek, fseeko);
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/errno.h"
|
#include "libc/errno.h"
|
||||||
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/stdio/internal.h"
|
#include "libc/stdio/internal.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
* @param whence can be SEET_SET, SEEK_CUR, or SEEK_END
|
* @param whence can be SEET_SET, SEEK_CUR, or SEEK_END
|
||||||
* @returns 0 on success or -1 on error
|
* @returns 0 on success or -1 on error
|
||||||
*/
|
*/
|
||||||
int fseeko_unlocked(FILE *f, int64_t offset, int whence) {
|
int fseek_unlocked(FILE *f, int64_t offset, int whence) {
|
||||||
int res;
|
int res;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
int64_t pos;
|
int64_t pos;
|
||||||
|
@ -68,6 +69,7 @@ int fseeko_unlocked(FILE *f, int64_t offset, int whence) {
|
||||||
pos = -1;
|
pos = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
f->end = MAX(f->beg, f->end);
|
||||||
if (0 <= pos && pos <= f->end) {
|
if (0 <= pos && pos <= f->end) {
|
||||||
f->beg = pos;
|
f->beg = pos;
|
||||||
f->state = 0;
|
f->state = 0;
|
|
@ -24,7 +24,7 @@
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/sysv/consts/o.h"
|
#include "libc/sysv/consts/o.h"
|
||||||
|
|
||||||
static inline int64_t ftello_unlocked(FILE *f) {
|
static inline int64_t ftell_unlocked(FILE *f) {
|
||||||
int64_t pos;
|
int64_t pos;
|
||||||
uint32_t skew;
|
uint32_t skew;
|
||||||
if (f->fd != -1) {
|
if (f->fd != -1) {
|
||||||
|
@ -48,12 +48,12 @@ static inline int64_t ftello_unlocked(FILE *f) {
|
||||||
* @returns current byte offset from beginning, or -1 w/ errno
|
* @returns current byte offset from beginning, or -1 w/ errno
|
||||||
* @threadsafe
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
int64_t ftello(FILE *f) {
|
int64_t ftell(FILE *f) {
|
||||||
int64_t rc;
|
int64_t rc;
|
||||||
flockfile(f);
|
flockfile(f);
|
||||||
rc = ftello_unlocked(f);
|
rc = ftell_unlocked(f);
|
||||||
funlockfile(f);
|
funlockfile(f);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
__strong_reference(ftello, ftell);
|
__strong_reference(ftell, ftello);
|
|
@ -29,7 +29,7 @@
|
||||||
*/
|
*/
|
||||||
void rewind(FILE *f) {
|
void rewind(FILE *f) {
|
||||||
flockfile(f);
|
flockfile(f);
|
||||||
fseeko_unlocked(f, 0, SEEK_SET);
|
fseek_unlocked(f, 0, SEEK_SET);
|
||||||
f->state = 0;
|
f->state = 0;
|
||||||
funlockfile(f);
|
funlockfile(f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,7 @@ wchar_t *fgetws_unlocked(wchar_t *, int, FILE *);
|
||||||
int fputws_unlocked(const wchar_t *, FILE *);
|
int fputws_unlocked(const wchar_t *, FILE *);
|
||||||
wint_t ungetwc_unlocked(wint_t, FILE *) paramsnonnull();
|
wint_t ungetwc_unlocked(wint_t, FILE *) paramsnonnull();
|
||||||
int ungetc_unlocked(int, FILE *) paramsnonnull();
|
int ungetc_unlocked(int, FILE *) paramsnonnull();
|
||||||
int fseeko_unlocked(FILE *, int64_t, int) paramsnonnull();
|
int fseek_unlocked(FILE *, int64_t, int) paramsnonnull();
|
||||||
ssize_t getdelim_unlocked(char **, size_t *, int, FILE *) paramsnonnull();
|
ssize_t getdelim_unlocked(char **, size_t *, int, FILE *) paramsnonnull();
|
||||||
int fprintf_unlocked(FILE *, const char *, ...) printfesque(2)
|
int fprintf_unlocked(FILE *, const char *, ...) printfesque(2)
|
||||||
paramsnonnull((1, 2)) dontthrow nocallback;
|
paramsnonnull((1, 2)) dontthrow nocallback;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/fmt/conv.h"
|
#include "libc/fmt/conv.h"
|
||||||
#include "libc/fmt/fmt.h"
|
#include "libc/fmt/fmt.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
@ -27,6 +28,13 @@
|
||||||
#include "libc/str/utf16.h"
|
#include "libc/str/utf16.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
|
|
||||||
|
#define READ \
|
||||||
|
({ \
|
||||||
|
int c = callback(arg); \
|
||||||
|
if (c != -1) ++consumed; \
|
||||||
|
c; \
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String / file / stream decoder.
|
* String / file / stream decoder.
|
||||||
*
|
*
|
||||||
|
@ -54,10 +62,12 @@ int __vcscanf(int callback(void *), //
|
||||||
void *ptr;
|
void *ptr;
|
||||||
} *freeme = NULL;
|
} *freeme = NULL;
|
||||||
const unsigned char *p = (const unsigned char *)fmt;
|
const unsigned char *p = (const unsigned char *)fmt;
|
||||||
unsigned i = 0;
|
int *n_ptr;
|
||||||
int items = 0;
|
int items = 0;
|
||||||
int c = callback(arg);
|
int consumed = 0;
|
||||||
while (c != -1) {
|
unsigned i = 0;
|
||||||
|
int c = READ;
|
||||||
|
for (;;) {
|
||||||
switch (p[i++]) {
|
switch (p[i++]) {
|
||||||
case '\0':
|
case '\0':
|
||||||
if (c != -1 && unget) {
|
if (c != -1 && unget) {
|
||||||
|
@ -70,7 +80,7 @@ int __vcscanf(int callback(void *), //
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\v':
|
case '\v':
|
||||||
while (isspace(c)) {
|
while (isspace(c)) {
|
||||||
c = callback(arg);
|
c = READ;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '%': {
|
case '%': {
|
||||||
|
@ -115,8 +125,11 @@ int __vcscanf(int callback(void *), //
|
||||||
case 'c':
|
case 'c':
|
||||||
rawmode = true;
|
rawmode = true;
|
||||||
if (!width) width = 1;
|
if (!width) width = 1;
|
||||||
/* εpsilon transition */
|
// fallthrough
|
||||||
case 's':
|
case 's':
|
||||||
|
while (isspace(c)) {
|
||||||
|
c = READ;
|
||||||
|
}
|
||||||
goto DecodeString;
|
goto DecodeString;
|
||||||
case '\'':
|
case '\'':
|
||||||
thousands = true;
|
thousands = true;
|
||||||
|
@ -144,38 +157,61 @@ int __vcscanf(int callback(void *), //
|
||||||
case 'b': /* binary */
|
case 'b': /* binary */
|
||||||
base = 2;
|
base = 2;
|
||||||
prefix = 'b';
|
prefix = 'b';
|
||||||
|
while (isspace(c)) {
|
||||||
|
c = READ;
|
||||||
|
}
|
||||||
goto ConsumeBasePrefix;
|
goto ConsumeBasePrefix;
|
||||||
case 'p': /* pointer (NexGen32e) */
|
case 'p': /* pointer (NexGen32e) */
|
||||||
bits = 48;
|
bits = 48;
|
||||||
|
while (isspace(c)) {
|
||||||
|
c = READ;
|
||||||
|
}
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case 'x':
|
case 'x':
|
||||||
case 'X': /* hexadecimal */
|
case 'X': /* hexadecimal */
|
||||||
base = 16;
|
base = 16;
|
||||||
prefix = 'x';
|
prefix = 'x';
|
||||||
|
while (isspace(c)) {
|
||||||
|
c = READ;
|
||||||
|
}
|
||||||
goto ConsumeBasePrefix;
|
goto ConsumeBasePrefix;
|
||||||
case 'o': /* octal */
|
case 'o': /* octal */
|
||||||
base = 8;
|
base = 8;
|
||||||
goto DecodeNumber;
|
while (isspace(c)) {
|
||||||
case 'd': /* decimal */
|
c = READ;
|
||||||
case 'n': /* TODO(jart): flexidecimal */
|
|
||||||
issigned = true;
|
|
||||||
if (c == '+' || (isneg = c == '-')) {
|
|
||||||
c = callback(arg);
|
|
||||||
}
|
}
|
||||||
/* εpsilon transition */
|
goto DecodeNumber;
|
||||||
|
case 'n':
|
||||||
|
goto ReportConsumed;
|
||||||
|
case 'd': // decimal
|
||||||
|
issigned = true;
|
||||||
|
while (isspace(c)) {
|
||||||
|
c = READ;
|
||||||
|
}
|
||||||
|
if (c == '+' || (isneg = c == '-')) {
|
||||||
|
c = READ;
|
||||||
|
}
|
||||||
|
// fallthrough
|
||||||
case 'u':
|
case 'u':
|
||||||
base = 10;
|
base = 10;
|
||||||
|
while (isspace(c)) {
|
||||||
|
c = READ;
|
||||||
|
}
|
||||||
goto DecodeNumber;
|
goto DecodeNumber;
|
||||||
default:
|
default:
|
||||||
items = einval();
|
items = einval();
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ReportConsumed:
|
||||||
|
n_ptr = va_arg(va, int *);
|
||||||
|
*n_ptr = consumed - 1; // minus lookahead
|
||||||
|
continue;
|
||||||
ConsumeBasePrefix:
|
ConsumeBasePrefix:
|
||||||
if (c == '0') {
|
if (c == '0') {
|
||||||
c = callback(arg);
|
c = READ;
|
||||||
if (c == prefix || c == prefix + ('a' - 'A')) {
|
if (c == prefix || c == prefix + ('a' - 'A')) {
|
||||||
c = callback(arg);
|
c = READ;
|
||||||
} else if (c == -1) {
|
} else if (c == -1) {
|
||||||
c = '0';
|
c = '0';
|
||||||
}
|
}
|
||||||
|
@ -195,7 +231,7 @@ int __vcscanf(int callback(void *), //
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while ((c = callback(arg)) != -1 && width > 0);
|
} while ((c = READ) != -1 && width > 0);
|
||||||
if (!discard) {
|
if (!discard) {
|
||||||
uint128_t bane = (uint128_t)1 << (bits - 1);
|
uint128_t bane = (uint128_t)1 << (bits - 1);
|
||||||
if (!(number & ~((bane - 1) | (issigned ? 0 : bane))) ||
|
if (!(number & ~((bane - 1) | (issigned ? 0 : bane))) ||
|
||||||
|
@ -228,7 +264,13 @@ int __vcscanf(int callback(void *), //
|
||||||
*(uint8_t *)out = (uint8_t)number;
|
*(uint8_t *)out = (uint8_t)number;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (!items && c == -1) {
|
||||||
|
items = -1;
|
||||||
|
goto Done;
|
||||||
}
|
}
|
||||||
|
} else if (!items) {
|
||||||
|
items = -1;
|
||||||
|
goto Done;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
DecodeString:
|
DecodeString:
|
||||||
|
@ -256,7 +298,7 @@ int __vcscanf(int callback(void *), //
|
||||||
if (c != -1 && j + !rawmode < bufsize && (rawmode || !isspace(c))) {
|
if (c != -1 && j + !rawmode < bufsize && (rawmode || !isspace(c))) {
|
||||||
if (charbytes == 1) {
|
if (charbytes == 1) {
|
||||||
((unsigned char *)buf)[j++] = (unsigned char)c;
|
((unsigned char *)buf)[j++] = (unsigned char)c;
|
||||||
c = callback(arg);
|
c = READ;
|
||||||
} else if (tpdecodecb((wint_t *)&c, c, (void *)callback, arg) !=
|
} else if (tpdecodecb((wint_t *)&c, c, (void *)callback, arg) !=
|
||||||
-1) {
|
-1) {
|
||||||
if (charbytes == sizeof(char16_t)) {
|
if (charbytes == sizeof(char16_t)) {
|
||||||
|
@ -270,10 +312,13 @@ int __vcscanf(int callback(void *), //
|
||||||
} else {
|
} else {
|
||||||
((wchar_t *)buf)[j++] = (wchar_t)c;
|
((wchar_t *)buf)[j++] = (wchar_t)c;
|
||||||
}
|
}
|
||||||
c = callback(arg);
|
c = READ;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!rawmode && j < bufsize) {
|
if (!j && c == -1 && !items) {
|
||||||
|
items = -1;
|
||||||
|
goto Done;
|
||||||
|
} else if (!rawmode && j < bufsize) {
|
||||||
if (charbytes == sizeof(char)) {
|
if (charbytes == sizeof(char)) {
|
||||||
((unsigned char *)buf)[j] = '\0';
|
((unsigned char *)buf)[j] = '\0';
|
||||||
} else if (charbytes == sizeof(char16_t)) {
|
} else if (charbytes == sizeof(char16_t)) {
|
||||||
|
@ -292,13 +337,13 @@ int __vcscanf(int callback(void *), //
|
||||||
} else {
|
} else {
|
||||||
do {
|
do {
|
||||||
if (isspace(c)) break;
|
if (isspace(c)) break;
|
||||||
} while ((c = callback(arg)) != -1);
|
} while ((c = READ) != -1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
NonDirectiveCharacter:
|
NonDirectiveCharacter:
|
||||||
c = (c == p[i - 1]) ? callback(arg) : -1;
|
c = (c == p[i - 1]) ? READ : -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,6 @@ static int __vfprintf_flbuf(const char *s, struct state *t, size_t n) {
|
||||||
if (n) {
|
if (n) {
|
||||||
if (n == 1 && *s != '\n' && t->f->beg < t->f->size) {
|
if (n == 1 && *s != '\n' && t->f->beg < t->f->size) {
|
||||||
t->f->buf[t->f->beg++] = *s;
|
t->f->buf[t->f->beg++] = *s;
|
||||||
t->n += n;
|
|
||||||
rc = 0;
|
rc = 0;
|
||||||
} else if (fwrite_unlocked(s, 1, n, t->f)) {
|
} else if (fwrite_unlocked(s, 1, n, t->f)) {
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
34
libc/str/locale.c
Normal file
34
libc/str/locale.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*-*- 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 2023 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/str/locale.h"
|
||||||
|
#include "libc/str/str.h"
|
||||||
|
|
||||||
|
static const uint32_t empty_mo[] = {0x950412de, 0, -1, -1, -1};
|
||||||
|
|
||||||
|
const struct __locale_map __c_dot_utf8 = {
|
||||||
|
.map = empty_mo,
|
||||||
|
.map_size = sizeof empty_mo,
|
||||||
|
.name = "C.UTF-8",
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct __locale_struct __c_locale;
|
||||||
|
|
||||||
|
const struct __locale_struct __c_dot_utf8_locale = {
|
||||||
|
.cat[LC_CTYPE] = &__c_dot_utf8,
|
||||||
|
};
|
|
@ -37,6 +37,10 @@ struct __locale_struct {
|
||||||
|
|
||||||
typedef struct __locale_struct *locale_t;
|
typedef struct __locale_struct *locale_t;
|
||||||
|
|
||||||
|
extern const struct __locale_map __c_dot_utf8;
|
||||||
|
extern const struct __locale_struct __c_locale;
|
||||||
|
extern const struct __locale_struct __c_dot_utf8_locale;
|
||||||
|
|
||||||
char *nl_langinfo_l(int, locale_t);
|
char *nl_langinfo_l(int, locale_t);
|
||||||
char *setlocale(int, const char *);
|
char *setlocale(int, const char *);
|
||||||
double strtod_l(const char *, char **, locale_t);
|
double strtod_l(const char *, char **, locale_t);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/internal.h"
|
#include "libc/runtime/internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/str/locale.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/thread/spawn.h"
|
#include "libc/thread/spawn.h"
|
||||||
#include "libc/thread/tls.h"
|
#include "libc/thread/tls.h"
|
||||||
|
@ -41,6 +42,7 @@ static char *_mktls_finish(struct CosmoTib **out_tib, char *mem,
|
||||||
tib->tib_ftrace = old->tib_ftrace;
|
tib->tib_ftrace = old->tib_ftrace;
|
||||||
tib->tib_strace = old->tib_strace;
|
tib->tib_strace = old->tib_strace;
|
||||||
tib->tib_sigmask = old->tib_sigmask;
|
tib->tib_sigmask = old->tib_sigmask;
|
||||||
|
tib->tib_locale = (intptr_t)&__c_dot_utf8_locale;
|
||||||
atomic_store_explicit(&tib->tib_tid, -1, memory_order_relaxed);
|
atomic_store_explicit(&tib->tib_tid, -1, memory_order_relaxed);
|
||||||
if (out_tib) {
|
if (out_tib) {
|
||||||
*out_tib = tib;
|
*out_tib = tib;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/mem/gc.h"
|
#include "libc/mem/gc.h"
|
||||||
|
#include "libc/mem/gc.internal.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
|
@ -28,7 +29,7 @@
|
||||||
|
|
||||||
TEST(fgetln, test) {
|
TEST(fgetln, test) {
|
||||||
FILE *f;
|
FILE *f;
|
||||||
f = fmemopen(_gc(strdup(kHyperion)), kHyperionSize, "r+");
|
f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+");
|
||||||
EXPECT_STREQ("The fall of Hyperion - a Dream\n", fgetln(f, 0));
|
EXPECT_STREQ("The fall of Hyperion - a Dream\n", fgetln(f, 0));
|
||||||
EXPECT_STREQ("John Keats\n", fgetln(f, 0));
|
EXPECT_STREQ("John Keats\n", fgetln(f, 0));
|
||||||
EXPECT_STREQ("\n", fgetln(f, 0));
|
EXPECT_STREQ("\n", fgetln(f, 0));
|
||||||
|
@ -81,7 +82,7 @@ TEST(fgetln, testReadingFromStdin_doesntLeakMemory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCH(fgetln, bench) {
|
BENCH(fgetln, bench) {
|
||||||
FILE *f = fmemopen(_gc(strdup(kHyperion)), kHyperionSize, "r+");
|
FILE *f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+");
|
||||||
EZBENCH2("fgetln", donothing, fgetln(f, 0));
|
EZBENCH2("fgetln", donothing, fgetln(f, 0));
|
||||||
EZBENCH2("xgetline", donothing, free(xgetline(f)));
|
EZBENCH2("xgetline", donothing, free(xgetline(f)));
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
|
@ -16,7 +16,12 @@
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/mem/gc.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
#include "libc/stdio/temp.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
TEST(fmemopen, testWriteRewindRead) {
|
TEST(fmemopen, testWriteRewindRead) {
|
||||||
|
@ -30,10 +35,43 @@ TEST(fmemopen, testWriteRewindRead) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TEST(fmemopen, testWriteRead_readsNothingButNotEof) { */
|
TEST(fmemopen_fprintf, test) {
|
||||||
/* char c; */
|
FILE *f = fmemopen(NULL, BUFSIZ, "w+");
|
||||||
/* FILE *f; */
|
EXPECT_EQ(1, fprintf(f, "%ld", 0L));
|
||||||
/* f = fmemopen(NULL, BUFSIZ, "w+"); */
|
rewind(f);
|
||||||
/* EXPECT_EQ(1, fwrite("c", 1, 1, f)); */
|
char buf[8] = {0};
|
||||||
/* EXPECT_EQ(0, fread(&c, 1, 1, f)); */
|
EXPECT_EQ(1, fread(&buf, 1, 8, f));
|
||||||
/* } */
|
EXPECT_STREQ("0", buf);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(fmemopen, seekEofRead) {
|
||||||
|
FILE *f = fmemopen("x", 1, "r+");
|
||||||
|
ASSERT_SYS(EINVAL, -1, fseek(f, -1, SEEK_SET));
|
||||||
|
ASSERT_SYS(EINVAL, -1, fseek(f, +1, SEEK_END));
|
||||||
|
ASSERT_EQ(0, fseek(f, 0, SEEK_END));
|
||||||
|
ASSERT_FALSE(feof(f));
|
||||||
|
ASSERT_EQ(-1, fgetc(f));
|
||||||
|
ASSERT_TRUE(feof(f));
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(tmpfile_fprintf, test) {
|
||||||
|
FILE *f = tmpfile();
|
||||||
|
EXPECT_EQ(1, fprintf(f, "%ld", 0L));
|
||||||
|
rewind(f);
|
||||||
|
char buf[8] = {0};
|
||||||
|
EXPECT_EQ(1, fread(&buf, 1, 8, f));
|
||||||
|
EXPECT_STREQ("0", buf);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(fmemopen, small) {
|
||||||
|
FILE *f = fmemopen(gc(malloc(1)), 1, "w+");
|
||||||
|
EXPECT_EQ(3, fprintf(f, "%ld", 123L));
|
||||||
|
rewind(f);
|
||||||
|
char buf[8] = {0};
|
||||||
|
EXPECT_EQ(1, fread(&buf, 1, 8, f));
|
||||||
|
EXPECT_STREQ("1", buf);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/calls.h"
|
||||||
#include "libc/mem/mem.h"
|
|
||||||
#include "libc/mem/gc.internal.h"
|
#include "libc/mem/gc.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
#include "libc/x/x.h"
|
#include "libc/x/x.h"
|
||||||
|
|
|
@ -42,7 +42,6 @@ TEST(sscanf, testMultiple) {
|
||||||
|
|
||||||
TEST(sscanf, testDecimal) {
|
TEST(sscanf, testDecimal) {
|
||||||
EXPECT_EQ(123, sscanf1("123", "%d"));
|
EXPECT_EQ(123, sscanf1("123", "%d"));
|
||||||
EXPECT_EQ(123, sscanf1("123", "%n"));
|
|
||||||
EXPECT_EQ(123, sscanf1("123", "%u"));
|
EXPECT_EQ(123, sscanf1("123", "%u"));
|
||||||
EXPECT_EQ((uint32_t)-123, sscanf1("-123", "%d"));
|
EXPECT_EQ((uint32_t)-123, sscanf1("-123", "%d"));
|
||||||
}
|
}
|
||||||
|
@ -258,3 +257,30 @@ TEST(sscanf, test0) {
|
||||||
ASSERT_EQ(sscanf("0", "%b", &v), 1);
|
ASSERT_EQ(sscanf("0", "%b", &v), 1);
|
||||||
ASSERT_EQ(v, 0);
|
ASSERT_EQ(v, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(sscanf, n) {
|
||||||
|
int x, y;
|
||||||
|
EXPECT_EQ(1, sscanf("7 2 3 4", "%d%n", &x, &y));
|
||||||
|
EXPECT_EQ(7, x);
|
||||||
|
EXPECT_EQ(1, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(sscanf, eofForNoMatching) {
|
||||||
|
int y = 666;
|
||||||
|
char x[8] = "hi";
|
||||||
|
EXPECT_EQ(-1, sscanf(" ", "%s%n", &x, &y));
|
||||||
|
EXPECT_STREQ("hi", x);
|
||||||
|
EXPECT_EQ(666, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(sscanf, eofConditions) {
|
||||||
|
int x = 666;
|
||||||
|
EXPECT_EQ(-1, sscanf("", "%d", &x));
|
||||||
|
EXPECT_EQ(666, x);
|
||||||
|
EXPECT_EQ(-1, sscanf(" ", "%d", &x));
|
||||||
|
EXPECT_EQ(666, x);
|
||||||
|
EXPECT_EQ(-1, sscanf("123", "%*d%d", &x));
|
||||||
|
EXPECT_EQ(666, x);
|
||||||
|
EXPECT_EQ(-1, sscanf("123", "%*d%n", &x));
|
||||||
|
EXPECT_EQ(666, x);
|
||||||
|
}
|
||||||
|
|
24
test/libc/str/locale_test.c
Normal file
24
test/libc/str/locale_test.c
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*-*- 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 2023 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/str/unicode.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
|
TEST(locale, test) {
|
||||||
|
EXPECT_STREQ(".", localeconv()->decimal_point);
|
||||||
|
}
|
Loading…
Reference in a new issue