Add epoll and do more release readiness changes

This change also pays off some of the remaining technical debt with
stdio, file descriptors, and memory managemnt polyfills.
main
Justine Tunney 2 years ago
parent a9ea949df8
commit 3e4fd4b0ad
  1. 2
      Makefile
  2. 203
      ape/ape.S
  3. 2
      ape/lib/apelib.mk
  4. 197
      examples/hangman.c
  5. 2
      examples/nesemu1.cc
  6. 51
      libc/bits/bits.h
  7. 2
      libc/calls/chdir-nt.c
  8. 6
      libc/calls/close-nt.c
  9. 24
      libc/calls/close.c
  10. 14
      libc/calls/dup-nt.c
  11. 4
      libc/calls/fadvise-nt.c
  12. 4
      libc/calls/fallocate.c
  13. 25
      libc/calls/fcntl-nt.c
  14. 4
      libc/calls/fdatasync-nt.c
  15. 2
      libc/calls/fdkind.c
  16. 4
      libc/calls/flock-nt.c
  17. 2
      libc/calls/fstat-nt.c
  18. 6
      libc/calls/fstat.c
  19. 6
      libc/calls/ftruncate-nt.c
  20. 26
      libc/calls/g_fds.c
  21. 2
      libc/calls/getcwd-nt.c
  22. 2
      libc/calls/getdomainname.c
  23. 13
      libc/calls/getemptyfd.c
  24. 2
      libc/calls/gethostname.c
  25. 2
      libc/calls/getpriority-nt.c
  26. 2
      libc/calls/getrusage-nt.c
  27. 10
      libc/calls/growfds.c
  28. 2
      libc/calls/hefty/copyfile.c
  29. 4
      libc/calls/hefty/dirstream.c
  30. 14
      libc/calls/hefty/fork-nt.c
  31. 2
      libc/calls/hefty/ntspawn.c
  32. 4
      libc/calls/hefty/spawnve-nt.c
  33. 2
      libc/calls/hefty/ttyname_r.c
  34. 32
      libc/calls/internal.h
  35. 4
      libc/calls/ioctl-default.c
  36. 4
      libc/calls/ioctl-tiocgwinsz-nt.c
  37. 4
      libc/calls/ioctl-tiocswinsz-nt.c
  38. 2
      libc/calls/isatty-nt.c
  39. 2
      libc/calls/ischardev.c
  40. 4
      libc/calls/isfdkind.c
  41. 4
      libc/calls/isfdopen.c
  42. 6
      libc/calls/link-nt.c
  43. 4
      libc/calls/lseek-nt.c
  44. 4
      libc/calls/madvise-nt.c
  45. 2
      libc/calls/mkdir.c
  46. 2
      libc/calls/mprotect.greg.c
  47. 1
      libc/calls/nanosleep-xnu.c
  48. 2
      libc/calls/ntaccesscheck.c
  49. 8
      libc/calls/open-nt.c
  50. 2
      libc/calls/open.c
  51. 9
      libc/calls/openanon.c
  52. 9
      libc/calls/pipe-nt.c
  53. 6
      libc/calls/pread.c
  54. 2
      libc/calls/pwrite.c
  55. 2
      libc/calls/raise.c
  56. 2
      libc/calls/read-nt.c
  57. 32
      libc/calls/read.c
  58. 56
      libc/calls/readv-serial.c
  59. 20
      libc/calls/readv.c
  60. 4
      libc/calls/removefd.c
  61. 2
      libc/calls/rename-nt.c
  62. 2
      libc/calls/rmdir-nt.c
  63. 2
      libc/calls/sched_setaffinity.c
  64. 2
      libc/calls/setitimer-nt.c
  65. 2
      libc/calls/setpriority-nt.c
  66. 4
      libc/calls/sigaction.c
  67. 2
      libc/calls/stat-nt.c
  68. 2
      libc/calls/stat.c
  69. 2
      libc/calls/symlink-nt.c
  70. 2
      libc/calls/sysinfo-nt.c
  71. 2
      libc/calls/truncate-nt.c
  72. 2
      libc/calls/unlink-nt.c
  73. 4
      libc/calls/utimensat-nt.c
  74. 8
      libc/calls/wait4-nt.c
  75. 2
      libc/calls/winerr.greg.c
  76. 2
      libc/calls/write-nt.c
  77. 40
      libc/calls/write.c
  78. 40
      libc/calls/writev-serial.c
  79. 20
      libc/calls/writev.c
  80. 2
      libc/dns/getntnameservers.c
  81. 3
      libc/fmt/fmt.h
  82. 12
      libc/fmt/vcscanf.c
  83. 2
      libc/fmt/vsscanf.c
  84. 143
      libc/isystem/windows.h
  85. 2
      libc/libc.mk
  86. 31
      libc/nexgen32e/uart.internal.h
  87. 0
      libc/notice.internal.h
  88. 15
      libc/nt/KernelBase/AcquireSRWLockExclusive.s
  89. 15
      libc/nt/KernelBase/AcquireSRWLockShared.s
  90. 15
      libc/nt/KernelBase/DeleteCriticalSection.s
  91. 15
      libc/nt/KernelBase/EnterCriticalSection.s
  92. 12
      libc/nt/KernelBase/GetTickCount64.s
  93. 15
      libc/nt/KernelBase/InitializeCriticalSection.s
  94. 2
      libc/nt/KernelBase/InitializeCriticalSectionAndSpinCount.s
  95. 15
      libc/nt/KernelBase/InitializeSRWLock.s
  96. 15
      libc/nt/KernelBase/LeaveCriticalSection.s
  97. 15
      libc/nt/KernelBase/ReleaseSRWLockExclusive.s
  98. 15
      libc/nt/KernelBase/ReleaseSRWLockShared.s
  99. 12
      libc/nt/KernelBase/SetCriticalSectionSpinCount.s
  100. 15
      libc/nt/KernelBase/TryAcquireSRWLockExclusive.s
  101. Some files were not shown because too many files have changed in this diff Show More

@ -316,7 +316,7 @@ COSMOPOLITAN_HEADERS = \
o/$(MODE)/cosmopolitan.a: $(filter-out o/libc/stubs/exit11.o,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_OBJS)))
o/cosmopolitan.h: \
o/$(MODE)/tool/build/rollup.com.dbg \
o/$(MODE)/tool/build/rollup.com \
libc/integral/normalize.inc \
$(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS))
@ACTION=ROLLUP TARGET=$@ build/do $^ >$@

@ -34,7 +34,6 @@
αcτµαlly pδrταblε εxεcµταblε § program header
*/
#include "ape/config.h"
#include "ape/lib/apm.h"
#include "ape/lib/pc.h"
#include "ape/macros.h"
#include "ape/notice.inc"
@ -49,7 +48,7 @@
#define USE_SYMBOL_HACK 0
.source "NOTICE"
.source "LICENSE"
.source "ape/ape.S"
.source "ape/ape.lds"
.section .text,"ax",@progbits
@ -360,85 +359,9 @@ pcread: push %ax
xor %ax,%ax # try disk reset on error
int $0x13
pop %ax
jmp 1b
jmp pcread
.endfn pcread
/ Waits for serial lines to become idle.
/
/ @param di short array of serial ports (0 means not present)
/ @param si number of items in array
/ @mode long,legacy,real
sflush: mov %si,%cx
mov %di,%si
xor %dx,%dx
0: lodsb
mov %al,%dl
lodsb
mov %al,%dh
test %ax,%ax
jz 2f
add $UART_LSR,%dx
mov $UART_TTYIDL,%ah
1: in %dx,%al
and %ah,%al
rep
nop
jz 1b
loop 0b
2: ret
.endfn sflush,globl
/ Transmits byte over serial line.
/
/ This is both blocking and asynchronous.
/
/ @param di character to send
/ @param si serial port
/ @mode long,legacy,real
/ @see ttytxr
sputc: push %ax
push %cx
push %dx
mov %si,%dx
add $UART_LSR,%dx
mov $UART_TTYTXR,%ah
1: in %dx,%al
and %ah,%al
jnz 2f
rep
nop
jmp 1b
2: mov %di,%ax
mov %si,%dx
out %al,%dx
pop %dx
pop %cx
pop %ax
ret
.endfn sputc,globl
/ Shuts down personal computer.
/
/ @mode real
/ @noreturn
apmoff: mov $0x5300,%ax # apm installation check
xor %bx,%bx # for the apm bios itself
int $APM_SERVICE
jc 1f
cmp $'P<<8|'M,%bx # did apm bios service interrupt?
jne 1f
mov $0x5301,%ax # real mode interface connect
xor %bx,%bx # to apm bios device
int $APM_SERVICE # ignore errors e.g. already connected
xor %bx,%bx
xor %cx,%cx
mov $0x5307,%ax # set power state
mov $1,%bl # for all devices within my dominion
mov $3,%cl # to off
int $APM_SERVICE
1: call panic
.endfn apmoff,globl
/*
αcτµαlly pδrταblε εxεcµταblε § partition table
*/
@ -1156,115 +1079,41 @@ sinit: mov %di,%dx
2: ret
.endfn sinit,global,hidden
/ Abnormally exits program.
/
/ @param di message
/ @mode real
/ @noreturn
rldie: call rlpute
call rloff
.endfn rldie,globl,hidden
/ Shuts down machine.
/
/ @mode real
/ @noreturn
rloff: mov $kBiosDataAreaXlm+COM1,%di
mov $4,%si
call sflush
call apmoff
.endfn rloff,globl,hidden
/ Prints error message.
/
/ @param di message
/ @mode real
rlpute: mov kBiosDataAreaXlm+METAL_STDERR(%bx),%si
test %si,%si
jnz 1f
mov kBiosDataAreaXlm+METAL_STDOUT,%si
1: xor %ax,%ax
push %ax
push %si
mov $REAL(.Lstr.crlf),%ax
push %ax
push %si
push %di
push %si
mov $REAL(.Lstr.error),%ax
push %ax
1: pop %di
test %di,%di
jz 2f
pop %si
call rlput2
jmp 1b
2: ret
.endfn rlpute,globl,hidden
/ Prints string to both video and serial.
/
/ @param di NUL-terminated string
/ @param si serial port
/ @mode real
rlput2: push %di
push %si
call rvputs
pop %si
pop %di
test %si,%si
jz 1f
call sputs
1: ret
.endfn rlput2,globl,hidden
/ Writes string to serial line.
/
/ @param di NUL-terminated string
/ @param si serial port
/ @mode long,legacy,real
sputs: push %bx
mov %di,%bx
1: xchg %bx,%si
lodsb
xchg %bx,%si
test %al,%al
jz 2f
mov %ax,%di
push %si
rlcall sputc
pop %si
jmp 1b
2: pop %bx
ret
.endfn sputs,globl
/ Video put string.
/
/ @param di is the string
/ @mode real
rvputs: mov %di,%si
rvputs: push %bp
push %bx
mov %di,%si
0: lodsb
test %al,%al
je 1f
rlcall rvputc
mov $7,%bx # normal mda/cga style page zero
mov $0x0e,%ah # teletype output al cp437
int $0x10 # vidya service
jmp 0b
1: ret
1: pop %bx
pop %bp
ret
.endfn rvputs,globl,hidden
/ Video put char.
/ Abnormally halts startup.
/
/ @param al is the char
/ @param di message
/ @mode real
rvputc: push %bx # don't clobber bp,bx,di,si,cx
push %bp # original ibm pc scroll up bug
mov $7,%bx # normal mda/cga style page zero
mov $0x0e,%ah # teletype output al cp437
int $0x10 # vidya service
pop %bp # preserves al
pop %bx
ret
.endfn rvputc
/ @noreturn
rldie: push %di
mov $REAL(.Lstr.error),%di
call rvputs
pop %di
call rvputs
mov $REAL(.Lstr.crlf),%di
call rvputs
xor %ax,%ax # get keystroke
int $0x16 # keyboard service
jmp triplf
.endfn rldie,globl,hidden
/*
@ -1321,6 +1170,7 @@ longmodeloader:
jc 9f
call unreal
/ call hiload
call pinit
jmp golong
9: mov $REAL(.Lstr.e820),%ax
call rldie
@ -1568,7 +1418,6 @@ pinit: push %ds
/ @see Intel Manual V3A §4.1.2
golong: cli
lidt XLM(BADIDT)
call pinit
mov %cr4,%eax
or $CR4_PAE|CR4_PGE|CR4_OSFXSR,%eax
mov %eax,%cr4

@ -21,7 +21,7 @@ APE_LIB_A_OBJS = \
$(APE_LIB_A_SRCS:%=o/$(MODE)/%.zip.o) \
o/$(MODE)/ape/ape.lds.zip.o \
o/$(MODE)/ape/ape.S.zip.o \
o/$(MODE)/NOTICE.zip.o
o/$(MODE)/LICENSE.zip.o
APE_LIB_A_CHECKS = $(APE_LIB_A_HDRS:%=o/$(MODE)/%.ok)
APE_LIB_A_DIRECTDEPS = LIBC_STR LIBC_STUBS

@ -0,0 +1,197 @@
/* UNIX v7 usr/src/games/hangman.c
*
* Copyright 2002 Caldera International Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code and documentation must retain the
* above copyright notice, this list of conditions and the following
* disclaimer. 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.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
*
* This product includes software developed or owned by Caldera
* International, Inc.
*
* Neither the name of Caldera International, Inc. nor the names of
* other contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENCE BY CALDERA
* INTERNATIONAL, 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 CALDERA INTERNATIONAL, 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.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/rand/rand.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/time/time.h"
#include "third_party/zlib/zlib.h"
/* clang-format off */
#define DICT "usr/share/dict/hangman"
#define MAXERR 7
#define MINSCORE 0
#define MINLEN 7
char *dictfile;
int alive,lost;
FILE *dict;
long int dictlen;
float errors=0, words=0;
char word[26],alph[26],realword[26];
void fatal(s)
char *s;
{
fprintf(stderr,"%s\n",s);
exit(1);
}
void setup()
{
long tvec;
struct stat statb;
time(&tvec);
srand(tvec);
if((dict=fopen(dictfile,"r"))==NULL) fatal("no dictionary");
if(stat(dictfile,&statb)<0) fatal("can't stat");
dictlen=statb.st_size;
}
double frand()
{
return(rand()/32768.);
}
void pscore()
{
if(words!=0) printf("(%4.2f/%.0f) ",errors/words,words);
}
void getword()
{ char wbuf[128],c;
int i,j;
loop:
if(fscanf(dict,"%s\n",wbuf)==EOF)
{ fseek(dict,0L,0);
goto loop;
}
if((c=wbuf[0])>'z' || c<'a') goto loop;
for(i=j=0;wbuf[j]!=0;i++,j++)
{ if(wbuf[j]=='-') j++;
wbuf[i]=wbuf[j];
}
wbuf[i]=0;
if(i<MINLEN) goto loop;
for(j=0;j<i;j++)
if((c=wbuf[j])<'a' || c>'z') goto loop;
pscore();
strcpy(realword,wbuf);
for(j=0;j<i;word[j++]='.');
}
void startnew()
{ int i;
long int pos;
char buf[128];
for(i=0;i<26;i++) word[i]=alph[i]=realword[i]=0;
pos=frand()*dictlen;
pos%=dictlen;
fseek(dict,pos,0);
fscanf(dict,"%s\n",buf);
getword();
alive=MAXERR;
lost=0;
}
void stateout()
{ int i;
printf("guesses: ");
for(i=0;i<26;i++)
if(alph[i]!=0) putchar(alph[i]);
printf(" word: %s ",word);
printf("errors: %d/%d\n",MAXERR-alive,MAXERR);
}
void getguess()
{ char gbuf[128],c;
int ok=0,i;
loop:
printf("guess: ");
if(gets(gbuf)==NULL)
{ putchar('\n');
exit(0);
}
if((c=gbuf[0])<'a' || c>'z')
{ printf("lower case\n");
goto loop;
}
if(alph[c-'a']!=0)
{ printf("you guessed that\n");
goto loop;
}
else alph[c-'a']=c;
for(i=0;realword[i]!=0;i++)
if(realword[i]==c)
{ word[i]=c;
ok=1;
}
if(ok==0)
{ alive--;
errors=errors+1;
if(alive<=0) lost=1;
return;
}
for(i=0;word[i]!=0;i++)
if(word[i]=='.') return;
alive=0;
lost=0;
return;
}
void wordout()
{
errors=errors+2;
printf("the answer was %s, you blew it\n",realword);
}
void youwon()
{
printf("you win, the word is %s\n",realword);
}
main(argc,argv)
char **argv;
{
if(argc==1) dictfile=DICT;
else dictfile=argv[1];
setup();
for(;;)
{ startnew();
while(alive>0)
{ stateout();
getguess();
}
words=words+1;
if(lost) wordout();
else youwon();
}
}

@ -43,7 +43,7 @@
#include "libc/time/time.h"
#include "libc/x/x.h"
#include "libc/zip.h"
#include "libc/zipos/zipos.h"
#include "libc/zipos/zipos.internal.h"
#include "third_party/getopt/getopt.h"
#include "tool/viz/lib/knobs.h"

@ -277,27 +277,17 @@ unsigned long hamming(unsigned long, unsigned long) pureconst;
* @return true if value was exchanged, otherwise false
* @see lockcmpxchg()
*/
#define cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
asm(ZFLAG_ASM("cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*(IFTHING)), "+a"(*(ISEQUALTOME)) \
: "r"((typeof(*(IFTHING)))(REPLACEITWITHME)) \
: "cc"); \
DidIt; \
})
#define ezcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
autotype(IFTHING) IfThing = (IFTHING); \
typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \
typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \
asm(ZFLAG_ASM("cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \
: "r"(ReplaceItWithMe) \
: "cc"); \
DidIt; \
#define cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
autotype(IFTHING) IfThing = (IFTHING); \
typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \
typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \
asm volatile(ZFLAG_ASM("cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \
: "r"(ReplaceItWithMe) \
: "cc"); \
DidIt; \
})
/**
@ -307,14 +297,17 @@ unsigned long hamming(unsigned long, unsigned long) pureconst;
* @return true if value was exchanged, otherwise false
* @see lockcmpxchg()
*/
#define lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
asm(ZFLAG_ASM("lock cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*(IFTHING)), "+a"(*(ISEQUALTOME)) \
: "r"((typeof(*(IFTHING)))(REPLACEITWITHME)) \
: "cc"); \
DidIt; \
#define lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \
({ \
bool DidIt; \
autotype(IFTHING) IfThing = (IFTHING); \
typeof(*IfThing) IsEqualToMe = (ISEQUALTOME); \
typeof(*IfThing) ReplaceItWithMe = (REPLACEITWITHME); \
asm volatile(ZFLAG_ASM("lock cmpxchg\t%3,%1") \
: ZFLAG_CONSTRAINT(DidIt), "+m"(*IfThing), "+a"(IsEqualToMe) \
: "r"(ReplaceItWithMe) \
: "cc"); \
DidIt; \
})
/**

@ -33,6 +33,6 @@ textwindows int chdir$nt(const char *path) {
if (SetCurrentDirectory(path16)) {
return 0;
} else {
return winerr();
return __winerr();
}
}

@ -24,7 +24,7 @@
textwindows int close$nt(int fd) {
bool32 ok;
if (isfdopen(fd)) {
if (__isfdopen(fd)) {
if (g_fds.p[fd].kind == kFdFile) {
/*
* Like Linux, closing a file on Windows doesn't guarantee it's
@ -37,8 +37,8 @@ textwindows int close$nt(int fd) {
if (g_fds.p[fd].kind == kFdConsole) {
ok &= CloseHandle(g_fds.p[fd].extra);
}
removefd(fd);
return ok ? 0 : winerr();
__removefd(fd);
return ok ? 0 : __winerr();
} else {
return ebadf();
}

@ -20,9 +20,10 @@
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/macros.h"
#include "libc/sock/internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.h"
#include "libc/zipos/zipos.internal.h"
/**
* Closes file descriptor.
@ -32,17 +33,24 @@
*/
int close(int fd) {
int rc;
if (fd == -1) return einval();
if (isfdkind(fd, kFdZip)) {
rc = weaken(__zipos_close)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle);
if (fd < 0) return einval();
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = weaken(__zipos_close)(fd);
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdEpoll) {
rc = weaken(close$epoll)(fd);
} else if (!IsWindows()) {
rc = close$sysv(fd);
} else if (isfdkind(fd, kFdSocket)) {
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdSocket) {
rc = weaken(closesocket$nt)(fd);
} else {
} else if (fd < g_fds.n &&
(g_fds.p[fd].kind == kFdFile || g_fds.p[fd].kind == kFdConsole)) {
rc = close$nt(fd);
} else {
rc = ebadf();
}
if (fd < g_fds.n) {
g_fds.p[fd].kind = kFdEmpty;
g_fds.f = MIN(g_fds.f, fd);
}
removefd(fd);
return rc;
}

@ -30,17 +30,17 @@
* Implements dup(), dup2(), and dup3() for Windows NT.
*/
textwindows int dup$nt(int oldfd, int newfd, int flags) {
if (!isfdkind(oldfd, kFdFile)) return ebadf();
if (!__isfdkind(oldfd, kFdFile)) return ebadf();
if (newfd == -1) {
if ((newfd = createfd()) == -1) return -1;
} else if (isfdindex(newfd)) {
if ((newfd = __getemptyfd()) == -1) {
return -1;
}
} else if (__ensurefds(newfd) != -1) {
if (g_fds.p[newfd].kind != kFdEmpty) {
close(newfd);
}
} else {
do {
if (growfds() == -1) return -1;
} while (newfd >= g_fds.n);
return -1;
}
if (DuplicateHandle(GetCurrentProcess(), g_fds.p[oldfd].handle,
GetCurrentProcess(), &g_fds.p[newfd].handle, 0,
@ -49,6 +49,6 @@ textwindows int dup$nt(int oldfd, int newfd, int flags) {
g_fds.p[newfd].flags = flags;
return newfd;
} else {
return winerr();
return __winerr();
}
}

@ -38,7 +38,7 @@ textwindows int fadvise$nt(int fd, uint64_t offset, uint64_t len, int advice) {
struct NtIoStatusBlock iostatus;
struct NtFileBasicInformation basicinfo;
struct NtFileAccessInformation accessinfo;
if (!isfdkind(fd, kFdFile)) return ebadf();
if (!__isfdkind(fd, kFdFile)) return ebadf();
sharemode = /* xxx: no clue how to query this */
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
/* TODO(jart): can we do it in one call w/ NtQueryObject? */
@ -56,7 +56,7 @@ textwindows int fadvise$nt(int fd, uint64_t offset, uint64_t len, int advice) {
}
return 0;
}
return winerr();
return __winerr();
} else if (status == kNtStatusDllNotFound) {
return enosys();
} else {

@ -51,7 +51,7 @@ int fallocate(int fd, int32_t mode, int64_t offset, int64_t length) {
} else if (!IsWindows()) {
return posix_fallocate$sysv(fd, offset, length);
} else if (IsWindows()) {
if (!isfdkind(fd, kFdFile)) return ebadf();
if (!__isfdkind(fd, kFdFile)) return ebadf();
if (mode == FALLOC_FL_ZERO_RANGE) {
if (DeviceIoControl(
g_fds.p[fd].handle, kNtFsctlSetZeroData,
@ -59,7 +59,7 @@ int fallocate(int fd, int32_t mode, int64_t offset, int64_t length) {
sizeof(struct NtFileZeroDataInformation), NULL, 0, &br, NULL)) {
return 0;
} else {
return winerr();
return __winerr();
}
} else if (!mode && !offset) {
/*

@ -21,20 +21,31 @@
#include "libc/nt/files.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
textwindows int fcntl$nt(int fd, int cmd, unsigned arg) {
uint32_t flags;
if (!isfdkind(fd, kFdFile)) return ebadf();
if (!__isfdkind(fd, kFdFile)) return ebadf();
switch (cmd) {
case F_GETFL:
return g_fds.p[fd].flags;
case F_SETFL:
return (g_fds.p[fd].flags = arg);
case F_GETFD:
if (!GetHandleInformation(g_fds.p[fd].handle, &flags)) return -1;
arg = (flags & FD_CLOEXEC) ^ FD_CLOEXEC;
return arg;
if (g_fds.p[fd].flags & O_CLOEXEC) {
return FD_CLOEXEC;
} else {
return 0;
}
case F_SETFD:
arg ^= FD_CLOEXEC;
if (!SetHandleInformation(g_fds.p[fd].handle, FD_CLOEXEC, arg)) return -1;
return 0;
if (arg & O_CLOEXEC) {
g_fds.p[fd].flags |= O_CLOEXEC;
return FD_CLOEXEC;
} else {
g_fds.p[fd].flags &= ~O_CLOEXEC;
return 0;
}
default:
return 0; /* TODO(jart): Implement me. */
}

@ -22,11 +22,11 @@
#include "libc/sysv/errfuns.h"
textwindows int fdatasync$nt(int fd) {
if (!isfdkind(fd, kFdFile)) return ebadf();
if (!__isfdkind(fd, kFdFile)) return ebadf();
/*
* XXX: On Windows NT this might be more analagous to fflush() and
* Microsoft docs say to do manual block i/o for database-ish
* guarantees on disk persistence. Consider: Really good UPS.
*/
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : winerr();
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : __winerr();
}

@ -20,7 +20,7 @@
#include "libc/calls/internal.h"
enum FdKind fdkind(int fd) {
if (isfdindex(fd)) {
if (0 <= fd && fd <= g_fds.n) {
return g_fds.p[fd].kind;
} else {
return kFdEmpty;

@ -29,7 +29,7 @@
textwindows int flock$nt(int fd, int op) {
struct NtOverlapped ov;
struct NtByHandleFileInformation info;
if (!isfdkind(fd, kFdFile)) return ebadf();
if (!__isfdkind(fd, kFdFile)) return ebadf();
memset(&ov, 0, sizeof(ov));
if (GetFileInformationByHandle(g_fds.p[fd].handle, &info) &&
((!(op & LOCK_UN) &&
@ -39,6 +39,6 @@ textwindows int flock$nt(int fd, int op) {
info.nFileSizeHigh, &ov)))) {
return 0;
} else {
return winerr();
return __winerr();
}
}

@ -66,6 +66,6 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) {
st->st_blocks = roundup(actualsize, PAGESIZE) / 512;
return 0;
} else {
return winerr();
return __winerr();
}
}

@ -22,20 +22,20 @@
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.h"
#include "libc/zipos/zipos.internal.h"
/**
* Returns information about file, via open()'d descriptor.
* @asyncsignalsafe
*/
int fstat(int fd, struct stat *st) {
if (isfdkind(fd, kFdZip)) {
if (__isfdkind(fd, kFdZip)) {
return weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
} else if (!IsWindows()) {
return fstat$sysv(fd, st);
} else {
if (!isfdkind(fd, kFdFile)) return ebadf();
if (!__isfdkind(fd, kFdFile)) return ebadf();
return fstat$nt(g_fds.p[fd].handle, st);
}
}

@ -25,14 +25,14 @@
textwindows int ftruncate$nt(int fd, uint64_t length) {
bool32 ok;
int64_t tell;
if (!isfdkind(fd, kFdFile)) return ebadf();
if (!__isfdkind(fd, kFdFile)) return ebadf();
tell = -1;
if (SetFilePointerEx(g_fds.p[fd].handle, 0, &tell, kNtFileCurrent)) {
ok = SetFilePointerEx(g_fds.p[fd].handle, length, NULL, kNtFileBegin) &&
SetEndOfFile(g_fds.p[fd].handle);
SetFilePointerEx(g_fds.p[fd].handle, tell, NULL, kNtFileBegin);
return ok ? 0 : winerr();
return ok ? 0 : __winerr();
} else {
return winerr();
return __winerr();
}
}

@ -33,12 +33,22 @@ void InitializeFileDescriptors(void) {
pushmov(&fds->f, 3ul);
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
fds->p = fds->__init_p;
fds->__init_p[STDIN_FILENO].kind = pushpop(kFdFile);
fds->__init_p[STDOUT_FILENO].kind = pushpop(kFdFile);
fds->__init_p[STDERR_FILENO].kind = pushpop(kFdFile);
fds->__init_p[STDIN_FILENO].handle = GetStdHandle(pushpop(kNtStdInputHandle));
fds->__init_p[STDOUT_FILENO].handle =
GetStdHandle(pushpop(kNtStdOutputHandle));
fds->__init_p[STDERR_FILENO].handle =
GetStdHandle(pushpop(kNtStdErrorHandle));
if (!IsMetal()) {
fds->__init_p[STDIN_FILENO].kind = pushpop(kFdFile);
fds->__init_p[STDOUT_FILENO].kind = pushpop(kFdFile);
fds->__init_p[STDERR_FILENO].kind = pushpop(kFdFile);
fds->__init_p[STDIN_FILENO].handle =
GetStdHandle(pushpop(kNtStdInputHandle));
fds->__init_p[STDOUT_FILENO].handle =
GetStdHandle(pushpop(kNtStdOutputHandle));
fds->__init_p[STDERR_FILENO].handle =
GetStdHandle(pushpop(kNtStdErrorHandle));
} else {
fds->__init_p[STDIN_FILENO].kind = pushpop(kFdSerial);
fds->__init_p[STDOUT_FILENO].kind = pushpop(kFdSerial);
fds->__init_p[STDERR_FILENO].kind = pushpop(kFdSerial);
fds->__init_p[STDIN_FILENO].handle = 0x3F8;
fds->__init_p[STDOUT_FILENO].handle = 0x3F8;
fds->__init_p[STDERR_FILENO].handle = 0x3F8;
}
}

@ -31,7 +31,7 @@ textwindows char *getcwd$nt(char *buf, size_t size) {
erange();
}
} else {
winerr();
__winerr();
}
return NULL;
}

@ -45,7 +45,7 @@ int getdomainname(char *name, size_t len) {
} else {
nSize = ARRAYLEN(name16);
if (!GetComputerNameEx(kNtComputerNameDnsFullyQualified, name16, &nSize)) {
return winerr();
return __winerr();
}
tprecode16to8(name, MIN(MIN(ARRAYLEN(name16), nSize + 1), len), name16);
return 0;

@ -25,14 +25,11 @@
/**
* Finds open file descriptor slot.
*/
ssize_t createfd(void) {
size_t fd;
for (;;) {
while (g_fds.f < g_fds.n) {
if (g_fds.p[(fd = g_fds.f++)].kind == kFdEmpty) {
return fd;
}
ssize_t __getemptyfd(void) {
for (; g_fds.f < g_fds.n; ++g_fds.f) {
if (g_fds.p[g_fds.f].kind == kFdEmpty) {
return g_fds.f;
}
if (growfds() == -1) return -1;
}
return __ensurefds(g_fds.f);
}

@ -50,7 +50,7 @@ int gethostname(char *name, size_t len) {
} else {
nSize = ARRAYLEN(name16);
if (!GetComputerNameEx(kNtComputerNameDnsHostname, name16, &nSize)) {
return winerr();
return __winerr();
}
tprecode16to8(name, MIN(MIN(ARRAYLEN(name16), nSize + 1), len), name16);
return 0;

@ -46,6 +46,6 @@ textwindows int getpriority$nt(int ignored) {
}
abort();
} else {
return winerr();
return __winerr();
}
}

@ -40,6 +40,6 @@ textwindows int getrusage$nt(int who, struct rusage *usage) {
FileTimeToTimeVal(&usage->ru_stime, KernelFileTime);
return 0;
} else {
return winerr();
return __winerr();
}
}

@ -23,18 +23,20 @@
#include "libc/mem/mem.h"
#include "libc/sysv/errfuns.h"
int growfds(void) {
int __ensurefds(int fd) {
size_t i, n;
struct Fd *p;
if (fd < g_fds.n) return fd;
if (weaken(realloc)) {
if ((p = weaken(realloc)(g_fds.p != g_fds.__init_p ? g_fds.p : NULL,
(n = (i = g_fds.n) << 1) * sizeof(*p)))) {
if ((p = weaken(realloc)(
g_fds.p != g_fds.__init_p ? g_fds.p : NULL,
(n = MAX(fd + 1, (i = g_fds.n) << 1)) * sizeof(*p)))) {
do {
p[i++].kind = kFdEmpty;
} while (i < n);
g_fds.p = p;
g_fds.n = n;
return 0;
return fd;
} else {
return enomem();
}

@ -55,7 +55,7 @@ static textwindows int copyfile$nt(const char *src, const char *dst,
}
return 0;
} else {
return winerr();
return __winerr();
}
}

@ -74,7 +74,7 @@ static textwindows noinline DIR *opendir$nt(const char *name) {
if ((res->fd = FindFirstFile(name16, &res->windata)) != -1) {
return res;
} else {
winerr();
__winerr();
free(res);
return NULL;
}
@ -220,7 +220,7 @@ int closedir(DIR *dir) {
if (!IsWindows()) {
rc = close(dir->fd);
} else {
rc = FindClose(dir->fd) ? 0 : winerr();
rc = FindClose(dir->fd) ? 0 : __winerr();
}
free(dir);
} else {

@ -67,7 +67,7 @@ textwindows void WinMainForked(void) {
char16_t *p;
uint64_t size;
char16_t var[21 + 1 + 21 + 1];
uint32_t i, varlen, protect, access;
uint32_t i, varlen, protect, access, oldprot;
varlen = GetEnvironmentVariable(u"_FORK", var, ARRAYLEN(var));
if (!varlen || varlen >= ARRAYLEN(var)) return;
p = var;
@ -98,11 +98,13 @@ textwindows void WinMainForked(void) {
break;
}
if (_mmi.p[i].flags & MAP_PRIVATE) {
MapViewOfFileExNuma(
(_mmi.p[i].h = CreateFileMappingNuma(-1, NULL, protect, 0, size, NULL,
kNtNumaNoPreferredNode)),
access, 0, 0, size, addr, kNtNumaNoPreferredNode);
MapViewOfFileExNuma((_mmi.p[i].h = CreateFileMappingNuma(
-1, NULL, kNtPageExecuteReadwrite, 0, size, NULL,
kNtNumaNoPreferredNode)),
kNtFileMapRead | kNtFileMapWrite | kNtFileMapExecute,
0, 0, size, addr, kNtNumaNoPreferredNode);
ReadAll(h, addr, size);
VirtualProtect(addr, size, protect, &oldprot);
} else {
MapViewOfFileExNuma(_mmi.p[i].h, access, 0, 0, size, addr,
kNtNumaNoPreferredNode);
@ -148,7 +150,7 @@ textwindows int fork$nt(void) {
}
unsetenv("_FORK");
} else {
rc = winerr();
rc = __winerr();
}
} else {
rc = 0;

@ -78,7 +78,7 @@ textwindows int ntspawn(
opt_out_lpProcessInformation)) {
rc = 0;
} else {
rc = winerr();
rc = __winerr();
}
} else {
rc = -1;

@ -49,14 +49,14 @@ textwindows int spawnve$nt(unsigned flags, int stdiofds[3], const char *program,
sti.cb = sizeof(sti);
sti.dwFlags = kNtStartfUsestdhandles;
if ((pid = createfd()) == -1) return -1;
if ((pid = __getemptyfd()) == -1) return -1;
for (i = 0; i < 3; ++i) {
if (stdiofds[i] == -1) {
x = &h;
y = &sti.stdiofds[i];
if (kIoMotion[i]) xchg(&x, &y);
if ((tubes[i] = createfd()) != -1 &&
if ((tubes[i] = __getemptyfd()) != -1 &&
CreatePipe(x, y, &kNtIsInheritable, 0)) {
g_fds.p[tubes[i]].handle = h;
} else {

@ -76,7 +76,7 @@ int ttyname_r(int fd, char *buf, size_t size) {
} else if (IsFreebsd()) {
return ttyname$freebsd(fd, buf, size);
} else if (IsWindows()) {
if (isfdkind(fd, kFdFile)) {
if (__isfdkind(fd, kFdFile)) {
return ttyname$nt(fd, buf, size);
} else {
return ebadf();

@ -2,6 +2,7 @@
#define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
#ifndef __STRICT_ANSI__
#include "libc/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
@ -37,7 +38,7 @@ struct IoctlPtmGet {
struct Fds {
size_t f; // arbitrary free slot start search index
size_t n; // capacity
size_t n; // monotonic capacity
struct Fd {
int64_t handle;
int64_t extra;
@ -47,7 +48,9 @@ struct Fds {
kFdSocket,