diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index cd8a9ce..fd4100c 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 90 +#define MONTAUK_BUILD_NUMBER 91 diff --git a/programs/include/libc/dirent.h b/programs/include/libc/dirent.h index 5e90839..0c2f08a 100644 --- a/programs/include/libc/dirent.h +++ b/programs/include/libc/dirent.h @@ -29,6 +29,11 @@ struct dirent *readdir(DIR *dirp); int closedir(DIR *dirp); void rewinddir(DIR *dirp); +int scandir(const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)); +int alphasort(const struct dirent **a, const struct dirent **b); + #ifdef __cplusplus } #endif diff --git a/programs/include/libc/math.h b/programs/include/libc/math.h index 6f2b779..72f42a4 100644 --- a/programs/include/libc/math.h +++ b/programs/include/libc/math.h @@ -11,6 +11,24 @@ extern "C" { #define INFINITY __builtin_inff() #define NAN __builtin_nanf("") +/* + * X/Open math constants. Not in ISO C, but ported code assumes them freely + * (NetSurf's about:chart uses M_PI and M_PI_2). + */ +#define M_E 2.7182818284590452354 +#define M_LOG2E 1.4426950408889634074 +#define M_LOG10E 0.43429448190325182765 +#define M_LN2 0.69314718055994530942 +#define M_LN10 2.30258509299404568402 +#define M_PI 3.14159265358979323846 +#define M_PI_2 1.57079632679489661923 +#define M_PI_4 0.78539816339744830962 +#define M_1_PI 0.31830988618379067154 +#define M_2_PI 0.63661977236758134308 +#define M_2_SQRTPI 1.12837916709551257390 +#define M_SQRT2 1.41421356237309504880 +#define M_SQRT1_2 0.70710678118654752440 + /* C99 floating-point classification. */ #define FP_NAN 0 #define FP_INFINITE 1 diff --git a/programs/include/libc/string.h b/programs/include/libc/string.h index ba815d6..c570dab 100644 --- a/programs/include/libc/string.h +++ b/programs/include/libc/string.h @@ -25,6 +25,7 @@ char *strncpy(char *dest, const char *src, size_t n); char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, size_t n); char *strdup(const char *s); +char *strndup(const char *s, size_t n); char *strchr(const char *s, int c); char *strrchr(const char *s, int c); char *strpbrk(const char *s, const char *accept); diff --git a/programs/include/libc/unistd.h b/programs/include/libc/unistd.h index 7190705..b779f90 100644 --- a/programs/include/libc/unistd.h +++ b/programs/include/libc/unistd.h @@ -61,6 +61,8 @@ long sysconf(int name); extern char **environ; unsigned int sleep(unsigned int seconds); +int usleep(unsigned long usec); +int ftruncate(int fd, long length); #ifdef __cplusplus } diff --git a/programs/lib/libc/libc.c b/programs/lib/libc/libc.c index d6eb746..f437253 100644 --- a/programs/lib/libc/libc.c +++ b/programs/lib/libc/libc.c @@ -571,6 +571,18 @@ char *strdup(const char *s) { return d; } +char *strndup(const char *s, size_t n) { + size_t len = 0; + char *d; + if (s == NULL) return NULL; + while (len < n && s[len] != '\0') len++; + d = (char *)malloc(len + 1); + if (d == NULL) return NULL; + memcpy(d, s, len); + d[len] = '\0'; + return d; +} + const char *strerror(int errnum) { switch (errnum) { case 0: return "success"; @@ -2047,6 +2059,49 @@ int write(int fd, const void *buf, size_t count) { return ret; } +/* + * ftruncate: EXTEND ONLY. + * + * The kernel has no truncate syscall, so growth is emulated by writing zeros + * at the tail. Shrinking cannot be emulated and fails with ENOSYS rather than + * silently reporting success -- a caller that believes a file shrank when it + * did not is worse off than one told it is unsupported. + */ +int ftruncate(int fd, long length) { + unsigned long size; + static const unsigned char zeros[512] = { 0 }; + + if (fd < 0 || length < 0) { + errno = EINVAL; + return -1; + } + + size = (unsigned long)_zos_syscall1(SYS_GETSIZE, (long)fd); + + if ((unsigned long)length == size) + return 0; + + if ((unsigned long)length < size) { + errno = ENOSYS; /* no kernel truncate; cannot shrink */ + return -1; + } + + while (size < (unsigned long)length) { + unsigned long chunk = (unsigned long)length - size; + int written; + if (chunk > sizeof(zeros)) chunk = sizeof(zeros); + written = (int)_zos_syscall4(SYS_FWRITE, (long)fd, (long)zeros, + (long)size, (long)chunk); + if (written <= 0) { + errno = EIO; + return -1; + } + size += (unsigned long)written; + } + + return 0; +} + /* * Positioned I/O. SYS_READ/SYS_FWRITE already take an explicit file position, * so these pass the caller's offset straight through and never consult or @@ -2273,6 +2328,68 @@ struct dirent *readdir(DIR *dirp) { return &dirp->entry; } +/* + * scandir/alphasort, built on opendir/readdir. The array and every entry are + * malloc'd; the caller frees each entry then the array, as POSIX requires. + */ +int alphasort(const struct dirent **a, const struct dirent **b) { + return strcmp((*a)->d_name, (*b)->d_name); +} + +int scandir(const char *dirp, struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) { + DIR *d; + struct dirent *ent; + struct dirent **list = NULL; + size_t count = 0, cap = 0; + + if (dirp == NULL || namelist == NULL) { + errno = EINVAL; + return -1; + } + + d = opendir(dirp); + if (d == NULL) return -1; + + while ((ent = readdir(d)) != NULL) { + struct dirent *copy; + + if (filter != NULL && filter(ent) == 0) continue; + + if (count == cap) { + size_t newcap = (cap == 0) ? 16 : cap * 2; + struct dirent **grown = + (struct dirent **)realloc(list, newcap * sizeof(*list)); + if (grown == NULL) goto nomem; + list = grown; + cap = newcap; + } + + copy = (struct dirent *)malloc(sizeof(*copy)); + if (copy == NULL) goto nomem; + *copy = *ent; + list[count++] = copy; + } + + closedir(d); + + if (compar != NULL && count > 1) { + qsort(list, count, sizeof(*list), + (int (*)(const void *, const void *))compar); + } + + *namelist = list; + return (int)count; + +nomem: + while (count > 0) free(list[--count]); + free(list); + closedir(d); + errno = ENOMEM; + return -1; +} + int closedir(DIR *dirp) { if (dirp == NULL) { errno = EINVAL; @@ -3037,6 +3154,19 @@ unsigned int sleep(unsigned int seconds) { return 0; } +/* + * Sub-second sleeps. The kernel only offers millisecond granularity, so + * anything finer rounds UP to 1ms rather than to zero -- a caller asking for a + * short delay wants to yield, not to spin. + */ +int usleep(unsigned long usec) { + unsigned long ms = usec / 1000UL; + if (ms == 0 && usec != 0) + ms = 1; + _zos_syscall1(SYS_SLEEP_MS, (long)ms); + return 0; +} + /* ======================================================================== math.h: modf and C99 float variants (wrappers over the double implementations; assumed present by hosted libstdc++) diff --git a/programs/lib/libc/obj/libc.o b/programs/lib/libc/obj/libc.o index e8cb0ff..4682b36 100644 Binary files a/programs/lib/libc/obj/libc.o and b/programs/lib/libc/obj/libc.o differ