mirror of
				https://github.com/jart/cosmopolitan.git
				synced 2025-10-26 19:16:41 +00:00 
			
		
		
		
	Make improvements
- Polyfill readlink("foo/") dir check on Windows
- Support asynchronous signal delivery on Windows
- Restore Windows Console from execve() daisy chain
- Work around bug in AARCH64 Optimized Routines memcmp()
- Disable unbourne.com shell completion on Windows for now
- Don't always set virtual terminal input state on console
- Remove Musl Libc's unusual preservation of realpath("//")
- Make realpath() strongly link malloc() to pass configure test
- Delete cosh.com shell, now that unbourne.com works on Windows!
			
			
This commit is contained in:
		
							parent
							
								
									f9c9a323fe
								
							
						
					
					
						commit
						425c055116
					
				
					 40 changed files with 581 additions and 706 deletions
				
			
		
							
								
								
									
										318
									
								
								examples/cosh.c
									
										
									
									
									
								
							
							
						
						
									
										318
									
								
								examples/cosh.c
									
										
									
									
									
								
							|  | @ -1,318 +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/calls/calls.h" | ||||
| #include "libc/calls/struct/dirent.h" | ||||
| #include "libc/calls/struct/rusage.h" | ||||
| #include "libc/calls/struct/sigaction.h" | ||||
| #include "libc/calls/struct/sigset.h" | ||||
| #include "libc/calls/struct/timespec.h" | ||||
| #include "libc/errno.h" | ||||
| #include "libc/fmt/fmt.h" | ||||
| #include "libc/fmt/itoa.h" | ||||
| #include "libc/limits.h" | ||||
| #include "libc/log/appendresourcereport.internal.h" | ||||
| #include "libc/log/internal.h" | ||||
| #include "libc/log/log.h" | ||||
| #include "libc/macros.internal.h" | ||||
| #include "libc/mem/mem.h" | ||||
| #include "libc/runtime/internal.h" | ||||
| #include "libc/runtime/runtime.h" | ||||
| #include "libc/stdio/append.h" | ||||
| #include "libc/stdio/stdio.h" | ||||
| #include "libc/str/str.h" | ||||
| #include "libc/sysv/consts/clock.h" | ||||
| #include "libc/sysv/consts/dt.h" | ||||
| #include "libc/sysv/consts/o.h" | ||||
| #include "libc/sysv/consts/sig.h" | ||||
| #include "libc/time/time.h" | ||||
| #include "libc/x/x.h" | ||||
| #include "third_party/linenoise/linenoise.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * @fileoverview Cosmopolitan Shell | ||||
|  * | ||||
|  * This doesn't have script language features like UNBOURNE.COM but it | ||||
|  * works on Windows and, unlike CMD.EXE, has CTRL-P, CTRL-R, and other | ||||
|  * GNU Emacs / Readline keyboard shortcuts. | ||||
|  * | ||||
|  * One day we'll have UNBOURNE.COM working on Windows but the code isn't | ||||
|  * very maintainable sadly. | ||||
|  */ | ||||
| 
 | ||||
| volatile int gotint; | ||||
| 
 | ||||
| static void OnInterrupt(int sig) { | ||||
|   gotint = sig; | ||||
| } | ||||
| 
 | ||||
| static void AddUniqueCompletion(linenoiseCompletions *c, char *s) { | ||||
|   size_t i; | ||||
|   if (!s) return; | ||||
|   for (i = 0; i < c->len; ++i) { | ||||
|     if (!strcmp(s, c->cvec[i])) { | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|   c->cvec = realloc(c->cvec, ++c->len * sizeof(*c->cvec)); | ||||
|   c->cvec[c->len - 1] = s; | ||||
| } | ||||
| 
 | ||||
| static void CompleteFilename(const char *p, const char *q, const char *b, | ||||
|                              linenoiseCompletions *c) { | ||||
|   DIR *d; | ||||
|   char *buf; | ||||
|   const char *g; | ||||
|   struct dirent *e; | ||||
|   if ((buf = malloc(512))) { | ||||
|     if ((g = memrchr(p, '/', q - p))) { | ||||
|       *(char *)mempcpy(buf, p, MIN(g - p, 511)) = 0; | ||||
|       p = ++g; | ||||
|     } else { | ||||
|       strcpy(buf, "."); | ||||
|     } | ||||
|     if ((d = opendir(buf))) { | ||||
|       while ((e = readdir(d))) { | ||||
|         if (!strcmp(e->d_name, ".")) continue; | ||||
|         if (!strcmp(e->d_name, "..")) continue; | ||||
|         if (!strncmp(e->d_name, p, q - p)) { | ||||
|           snprintf(buf, 512, "%.*s%s%s", p - b, b, e->d_name, | ||||
|                    e->d_type == DT_DIR ? "/" : ""); | ||||
|           AddUniqueCompletion(c, strdup(buf)); | ||||
|         } | ||||
|       } | ||||
|       closedir(d); | ||||
|     } | ||||
|     free(buf); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| static void ShellCompletion(const char *p, linenoiseCompletions *c) { | ||||
|   const char *q, *b; | ||||
|   for (b = p, q = (p += strlen(p)); p > b; --p) { | ||||
|     if (!isalnum(p[-1]) && | ||||
|         (p[-1] != '.' && p[-1] != '_' && p[-1] != '-' && p[-1] != '+' && | ||||
|          p[-1] != '[' && p[-1] != '/' && p[-1] != '\\')) { | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   CompleteFilename(p, q, b, c); | ||||
| } | ||||
| 
 | ||||
| static char *ShellHint(const char *p, const char **ansi1, const char **ansi2) { | ||||
|   char *h = 0; | ||||
|   linenoiseCompletions c = {0}; | ||||
|   ShellCompletion(p, &c); | ||||
|   if (c.len == 1) { | ||||
|     h = strdup(c.cvec[0] + strlen(p)); | ||||
|   } | ||||
|   linenoiseFreeCompletions(&c); | ||||
|   return h; | ||||
| } | ||||
| 
 | ||||
| static char *MakePrompt(char *p) { | ||||
|   char *s, buf[256]; | ||||
|   if (!gethostname(buf, sizeof(buf))) { | ||||
|     p = stpcpy(p, "\e[95m"); | ||||
|     if ((s = getenv("USER"))) { | ||||
|       p = stpcpy(p, s); | ||||
|       *p++ = '@'; | ||||
|     } | ||||
|     p = stpcpy(p, buf); | ||||
|     *p++ = ':'; | ||||
|   } | ||||
|   p = stpcpy(p, "\e[96m"); | ||||
|   if ((s = getcwd(buf, sizeof(buf)))) { | ||||
|     p = stpcpy(p, s); | ||||
|   } | ||||
|   return stpcpy(p, "\e[0m>: "); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) { | ||||
|   bool timeit; | ||||
|   int64_t nanos; | ||||
|   struct rusage ru; | ||||
|   struct timespec ts1, ts2; | ||||
|   char *prog, path[PATH_MAX]; | ||||
|   sigset_t chldmask, savemask; | ||||
|   int stdoutflags, stderrflags; | ||||
|   int n, rc, ws, child, killcount; | ||||
|   const char *stdoutpath, *stderrpath; | ||||
|   struct sigaction sa, saveint, savequit; | ||||
|   char *p, *line, **args, *arg, *start, *state, prompt[1024]; | ||||
|   linenoiseSetFreeHintsCallback(free); | ||||
|   linenoiseSetHintsCallback(ShellHint); | ||||
|   linenoiseSetCompletionCallback(ShellCompletion); | ||||
|   MakePrompt(prompt); | ||||
|   while ((line = linenoiseWithHistory(prompt, "cmd"))) { | ||||
|     n = 0; | ||||
|     start = line; | ||||
|     if (_startswith(start, "time ")) { | ||||
|       timeit = true; | ||||
|       start += 5; | ||||
|     } else { | ||||
|       timeit = false; | ||||
|     } | ||||
|     stdoutpath = 0; | ||||
|     stderrpath = 0; | ||||
|     stdoutflags = 0; | ||||
|     stderrflags = 0; | ||||
|     args = xcalloc(1, sizeof(*args)); | ||||
|     while ((arg = strtok_r(start, " \t\r\n", &state))) { | ||||
|       // cmd >>stdout.txt
 | ||||
|       if (arg[0] == '>' && arg[1] == '>') { | ||||
|         stdoutflags = O_WRONLY | O_APPEND | O_CREAT; | ||||
|         stdoutpath = arg + 2; | ||||
|       } else if (arg[0] == '>') { | ||||
|         // cmd >stdout.txt
 | ||||
|         stdoutflags = O_WRONLY | O_CREAT | O_TRUNC; | ||||
|         stdoutpath = arg + 1; | ||||
|       } else if (arg[0] == '2' && arg[1] == '>' && arg[2] == '>') { | ||||
|         // cmd 2>>stderr.txt
 | ||||
|         stderrflags = O_WRONLY | O_APPEND | O_CREAT; | ||||
|         stderrpath = arg + 3; | ||||
|       } else if (arg[0] == '2' && arg[1] == '>') { | ||||
|         // cmd 2>stderr.txt
 | ||||
|         stderrflags = O_WRONLY | O_CREAT | O_TRUNC; | ||||
|         stderrpath = arg + 2; | ||||
|       } else { | ||||
|         // arg
 | ||||
|         args = xrealloc(args, (++n + 1) * sizeof(*args)); | ||||
|         args[n - 1] = arg; | ||||
|         args[n - 0] = 0; | ||||
|       } | ||||
|       start = 0; | ||||
|     } | ||||
|     if (n > 0) { | ||||
|       if ((prog = commandv(args[0], path, sizeof(path)))) { | ||||
| 
 | ||||
|         // let keyboard interrupts kill child and not shell
 | ||||
|         gotint = 0; | ||||
|         killcount = 0; | ||||
|         sa.sa_flags = 0; | ||||
|         sa.sa_handler = SIG_IGN; | ||||
|         sigemptyset(&sa.sa_mask); | ||||
|         sigaction(SIGQUIT, &sa, &savequit); | ||||
|         sa.sa_handler = OnInterrupt; | ||||
|         sigaction(SIGINT, &sa, &saveint); | ||||
|         sigemptyset(&chldmask); | ||||
|         sigaddset(&chldmask, SIGCHLD); | ||||
|         sigprocmask(SIG_BLOCK, &chldmask, &savemask); | ||||
| 
 | ||||
|         // record timestamp
 | ||||
|         if (timeit) { | ||||
|           clock_gettime(CLOCK_REALTIME, &ts1); | ||||
|         } | ||||
| 
 | ||||
|         // launch process
 | ||||
|         if (!(child = vfork())) { | ||||
|           if (stdoutpath) { | ||||
|             close(1); | ||||
|             open(stdoutpath, stdoutflags, 0644); | ||||
|           } | ||||
|           if (stderrpath) { | ||||
|             close(2); | ||||
|             open(stderrpath, stderrflags, 0644); | ||||
|           } | ||||
|           sigaction(SIGINT, &saveint, 0); | ||||
|           sigaction(SIGQUIT, &savequit, 0); | ||||
|           sigprocmask(SIG_SETMASK, &savemask, 0); | ||||
|           execv(prog, args); | ||||
|           _Exit(127); | ||||
|         } | ||||
| 
 | ||||
|         // wait for process
 | ||||
|         for (;;) { | ||||
|           if (gotint) { | ||||
|             switch (killcount) { | ||||
|               case 0: | ||||
|                 // ctrl-c
 | ||||
|                 // we do nothing
 | ||||
|                 // terminals broadcast sigint to process group
 | ||||
|                 rc = 0; | ||||
|                 break; | ||||
|               case 1: | ||||
|                 // ctrl-c ctrl-c
 | ||||
|                 // we try sending sigterm
 | ||||
|                 rc = kill(child, SIGTERM); | ||||
|                 break; | ||||
|               default: | ||||
|                 // ctrl-c ctrl-c ctrl-c ...
 | ||||
|                 // we use kill -9 as our last resort
 | ||||
|                 rc = kill(child, SIGKILL); | ||||
|                 break; | ||||
|             } | ||||
|             if (rc == -1) { | ||||
|               fprintf(stderr, "kill failed: %m\n"); | ||||
|               exit(1); | ||||
|             } | ||||
|             ++killcount; | ||||
|             gotint = 0; | ||||
|           } | ||||
|           rc = wait4(0, &ws, 0, &ru); | ||||
|           if (rc != -1) { | ||||
|             break; | ||||
|           } else if (errno == EINTR) { | ||||
|             errno = 0; | ||||
|           } else { | ||||
|             fprintf(stderr, "wait failed: %m\n"); | ||||
|             exit(1); | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|         // print resource consumption for `time` pseudocommand
 | ||||
|         if (timeit) { | ||||
|           clock_gettime(CLOCK_REALTIME, &ts2); | ||||
|           if (ts2.tv_sec == ts1.tv_sec) { | ||||
|             nanos = ts2.tv_nsec - ts1.tv_nsec; | ||||
|           } else { | ||||
|             nanos = (ts2.tv_sec - ts1.tv_sec) * 1000000000LL; | ||||
|             nanos += 1000000000LL - ts1.tv_nsec; | ||||
|             nanos += ts2.tv_nsec; | ||||
|           } | ||||
|           printf("took %,ldµs wall time\n", nanos / 1000); | ||||
|           p = 0; | ||||
|           AppendResourceReport(&p, &ru, "\n"); | ||||
|           fputs(p, stdout); | ||||
|           free(p); | ||||
|         } | ||||
| 
 | ||||
|         // update prompt to reflect exit status
 | ||||
|         p = prompt; | ||||
|         if (WIFEXITED(ws)) { | ||||
|           if (WEXITSTATUS(ws)) { | ||||
|             if (!__nocolor) p = stpcpy(p, "\e[1;31m"); | ||||
|             p = stpcpy(p, "rc="); | ||||
|             p = FormatInt32(p, WEXITSTATUS(ws)); | ||||
|             if (128 < WEXITSTATUS(ws) && WEXITSTATUS(ws) <= 128 + 32) { | ||||
|               *p++ = ' '; | ||||
|               p = stpcpy(p, strsignal(WEXITSTATUS(ws) - 128)); | ||||
|             } | ||||
|             if (!__nocolor) p = stpcpy(p, "\e[0m"); | ||||
|             *p++ = ' '; | ||||
|           } | ||||
|         } else { | ||||
|           if (!__nocolor) p = stpcpy(p, "\e[1;31m"); | ||||
|           p = stpcpy(p, strsignal(WTERMSIG(ws))); | ||||
|           if (!__nocolor) p = stpcpy(p, "\e[0m"); | ||||
|           *p++ = ' '; | ||||
|         } | ||||
|         MakePrompt(p); | ||||
| 
 | ||||
|         sigprocmask(SIG_SETMASK, &savemask, 0); | ||||
|         sigaction(SIGINT, &saveint, 0); | ||||
|         sigaction(SIGQUIT, &savequit, 0); | ||||
|       } else { | ||||
|         fprintf(stderr, "%s: %s: command not found\n", argv[0], args[0]); | ||||
|       } | ||||
|     } | ||||
|     free(line); | ||||
|     free(args); | ||||
|   } | ||||
| } | ||||
|  | @ -10,14 +10,25 @@ | |||
| #include "libc/calls/calls.h" | ||||
| #include "libc/calls/struct/sigaction.h" | ||||
| #include "libc/errno.h" | ||||
| #include "libc/limits.h" | ||||
| #include "libc/runtime/runtime.h" | ||||
| #include "libc/sock/struct/pollfd.h" | ||||
| #include "libc/stdio/stdio.h" | ||||
| #include "libc/str/str.h" | ||||
| #include "libc/sysv/consts/limits.h" | ||||
| #include "libc/sysv/consts/poll.h" | ||||
| #include "libc/sysv/consts/sig.h" | ||||
| 
 | ||||
| volatile bool gotsig; | ||||
| 
 | ||||
| void SignalHandler(int sig) { | ||||
|   // we don't need to do anything in our signal handler since the signal
 | ||||
|   // delivery itself causes a visible state change, saying what happened
 | ||||
|   const char *s = "SignalHandler() called\n"; | ||||
|   write(1, s, strlen(s));  // notice both functions are @asyncsignalsafe
 | ||||
|   // however this will help if delivered asynchronously in cpubound code
 | ||||
|   // it's also necessary to discern spurious interrupts from real signal
 | ||||
|   gotsig = true; | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) { | ||||
|  | @ -31,6 +42,16 @@ int main(int argc, char *argv[]) { | |||
| 
 | ||||
|   for (;;) { | ||||
| 
 | ||||
|     // some programs are blocked on cpu rather than i/o
 | ||||
|     // such programs shall rely on asynchronous signals
 | ||||
|     printf("doing cpu task...\n"); | ||||
|     for (volatile int i = 0; i < INT_MAX / 20; ++i) { | ||||
|       if (gotsig) { | ||||
|         printf("\rgot ctrl+c asynchronously\n"); | ||||
|         exit(0); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // posix guarantees atomic i/o if you use pipe_buf sized buffers
 | ||||
|     // that way we don't need to worry about things like looping and
 | ||||
|     // we can also be assured that multiple actors wont have tearing
 | ||||
|  | @ -45,6 +66,7 @@ int main(int argc, char *argv[]) { | |||
|     // it's possible to be more precise if we were building library
 | ||||
|     // code. for example, you can block signals using sigprocmask()
 | ||||
|     // and then use pselect() to do the waiting.
 | ||||
|     printf("doing read i/o task...\n"); | ||||
|     int got = read(0, buf, sizeof(buf)); | ||||
| 
 | ||||
|     // check if the read operation failed
 | ||||
|  | @ -59,8 +81,13 @@ int main(int argc, char *argv[]) { | |||
|         // however EINTR is very useful, when we choose to use it
 | ||||
|         // the \r character is needed so when the line is printed
 | ||||
|         // it'll overwrite the ^C that got echo'd with the ctrl-c
 | ||||
|         printf("\rgot ctrl+c\n"); | ||||
|         exit(0); | ||||
|         if (gotsig) { | ||||
|           printf("\rgot ctrl+c via i/o eintr\n"); | ||||
|           exit(0); | ||||
|         } else { | ||||
|           printf("\rgot spurious eintr\n"); | ||||
|           continue; | ||||
|         } | ||||
|       } else { | ||||
|         // log it in the unlikely event something else went wrong
 | ||||
|         perror("<stdin>"); | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ int rawmode(void) { | |||
|   static bool once; | ||||
|   struct termios t; | ||||
|   if (!once) { | ||||
|     if (tcgetattr(1, &oldterm)) { | ||||
|     if (!tcgetattr(1, &oldterm)) { | ||||
|       atexit(restoretty); | ||||
|     } else { | ||||
|       perror("tcgetattr"); | ||||
|  |  | |||
|  | @ -5816,8 +5816,11 @@ static ssize_t preadfd(void) { | |||
| retry: | ||||
|   if (!parsefile->fd && isatty(0)) { | ||||
|     linenoiseSetFreeHintsCallback(free); | ||||
|     linenoiseSetHintsCallback(ShellHint); | ||||
|     linenoiseSetCompletionCallback(ShellCompletion); | ||||
|     if (!IsWindows()) { | ||||
|       // TODO(jart): Cache $PATH search.
 | ||||
|       linenoiseSetHintsCallback(ShellHint); | ||||
|       linenoiseSetCompletionCallback(ShellCompletion); | ||||
|     } | ||||
|     if ((p = linenoiseWithHistory(">: ", "unbourne"))) { | ||||
|       nr = min(strlen(p), IBUFSIZ - 2); | ||||
|       memcpy(buf, p, nr); | ||||
|  |  | |||
|  | @ -73,7 +73,7 @@ typedef int sig_atomic_t; | |||
| 
 | ||||
| bool32 isatty(int); | ||||
| char *getcwd(char *, size_t); | ||||
| char *realpath(const char *, char *); | ||||
| char *realpath(const char *, char *) __wur; | ||||
| char *ttyname(int); | ||||
| int access(const char *, int) dontthrow; | ||||
| int chdir(const char *); | ||||
|  |  | |||
|  | @ -65,6 +65,13 @@ $(LIBC_CALLS_A).pkg:					\ | |||
| 		$(LIBC_CALLS_A_OBJS)			\
 | ||||
| 		$(foreach x,$(LIBC_CALLS_A_DIRECTDEPS),$($(x)_A).pkg) | ||||
| 
 | ||||
| # we can't use sanitizers because:
 | ||||
| #   we're on a stack owned by win32 without tls
 | ||||
| o/$(MODE)/libc/calls/onntconsoleevent.o: private	\ | ||||
| 		COPTS +=				\
 | ||||
| 			-ffreestanding			\
 | ||||
| 			-fno-sanitize=all | ||||
| 
 | ||||
| # we can't use asan because:
 | ||||
| #   siginfo_t memory is owned by kernels
 | ||||
| o/$(MODE)/libc/calls/siginfo2cosmo.o: private		\ | ||||
|  |  | |||
|  | @ -38,6 +38,7 @@ | |||
| #include "libc/nt/synchronization.h" | ||||
| #include "libc/nt/thread.h" | ||||
| #include "libc/nt/thunk/msabi.h" | ||||
| #include "libc/runtime/internal.h" | ||||
| #include "libc/runtime/memtrack.internal.h" | ||||
| #include "libc/runtime/runtime.h" | ||||
| #include "libc/runtime/stack.h" | ||||
|  | @ -56,10 +57,10 @@ | |||
| extern long __klog_handle; | ||||
| 
 | ||||
| __msabi extern typeof(CloseHandle) *const __imp_CloseHandle; | ||||
| __msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; | ||||
| __msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess; | ||||
| __msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile; | ||||
| __msabi extern typeof(TerminateThread) *const __imp_TerminateThread; | ||||
| __msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile; | ||||
| __msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; | ||||
| 
 | ||||
| wontreturn void __switch_stacks(intptr_t, long, long, long, | ||||
|                                 void (*)(intptr_t, intptr_t, long, long), | ||||
|  | @ -70,9 +71,9 @@ __msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) { | |||
| } | ||||
| 
 | ||||
| static keywords void PurgeHandle(intptr_t h) { | ||||
|   if (h && h != -1) { | ||||
|     __imp_CloseHandle(h); | ||||
|   } | ||||
|   if (!h) return; | ||||
|   if (h == -1) return; | ||||
|   __imp_CloseHandle(h); | ||||
| } | ||||
| 
 | ||||
| static keywords void PurgeThread(intptr_t h) { | ||||
|  |  | |||
|  | @ -17,15 +17,14 @@ | |||
| │ PERFORMANCE OF THIS SOFTWARE.                                                │ | ||||
| ╚─────────────────────────────────────────────────────────────────────────────*/ | ||||
| #include "libc/calls/internal.h" | ||||
| #include "libc/nt/enum/filetype.h" | ||||
| #include "libc/nt/files.h" | ||||
| #include "libc/calls/syscall-nt.internal.h" | ||||
| #include "libc/nt/console.h" | ||||
| #include "libc/sysv/errfuns.h" | ||||
| 
 | ||||
| textwindows bool32 sys_isatty_nt(int fd) { | ||||
|   if (__isfdopen(fd)) { | ||||
|     if (__isfdkind(fd, kFdConsole) || | ||||
|         (__isfdkind(fd, kFdFile) && | ||||
|          GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar)) { | ||||
|     uint32_t mode; | ||||
|     if (GetConsoleMode(g_fds.p[fd].handle, &mode)) { | ||||
|       return true; | ||||
|     } else { | ||||
|       enotty(); | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
| #include "libc/calls/syscall_support-nt.internal.h" | ||||
| #include "libc/dce.h" | ||||
| #include "libc/intrin/asan.internal.h" | ||||
| #include "libc/intrin/kprintf.h" | ||||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/macros.internal.h" | ||||
| #include "libc/nt/systeminfo.h" | ||||
|  |  | |||
|  | @ -17,49 +17,143 @@ | |||
| │ PERFORMANCE OF THIS SOFTWARE.                                                │ | ||||
| ╚─────────────────────────────────────────────────────────────────────────────*/ | ||||
| #include "libc/calls/sig.internal.h" | ||||
| #include "libc/dce.h" | ||||
| #include "libc/errno.h" | ||||
| #include "libc/intrin/atomic.h" | ||||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/intrin/dll.h" | ||||
| #include "libc/log/libfatal.internal.h" | ||||
| #include "libc/nexgen32e/nt2sysv.h" | ||||
| #include "libc/nt/enum/context.h" | ||||
| #include "libc/nt/enum/ctrlevent.h" | ||||
| #include "libc/nt/enum/threadaccess.h" | ||||
| #include "libc/nt/runtime.h" | ||||
| #include "libc/nt/struct/context.h" | ||||
| #include "libc/nt/thread.h" | ||||
| #include "libc/nt/thunk/msabi.h" | ||||
| #include "libc/str/str.h" | ||||
| #include "libc/sysv/consts/sicode.h" | ||||
| #include "libc/sysv/consts/sig.h" | ||||
| #include "libc/thread/posixthread.internal.h" | ||||
| #include "libc/thread/thread.h" | ||||
| #include "libc/thread/tls.h" | ||||
| #include "libc/thread/tls2.internal.h" | ||||
| 
 | ||||
| #ifdef __x86_64__ | ||||
| 
 | ||||
| textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { | ||||
|   struct CosmoTib tib; | ||||
| // WIN32 doesn't have the System V red-zone. Microsoft says they reserve
 | ||||
| // the right to trample all over it. so we technically don't need to use
 | ||||
| // this value. it's just not clear how common it is for WIN32 to clobber
 | ||||
| // the red zone, which means broken code could seem to mostly work which
 | ||||
| // means it's better that we're not the ones responsible for breaking it
 | ||||
| #define kRedzoneSize 128 | ||||
| 
 | ||||
|   // win32 spawns a thread on its own just to deliver sigint
 | ||||
|   // TODO(jart): make signal code lockless so we can delete!
 | ||||
|   if (__tls_enabled && !__get_tls_win32()) { | ||||
|     bzero(&tib, sizeof(tib)); | ||||
|     tib.tib_self = &tib; | ||||
|     tib.tib_self2 = &tib; | ||||
|     atomic_store_explicit(&tib.tib_tid, GetCurrentThreadId(), | ||||
|                           memory_order_relaxed); | ||||
|     __set_tls_win32(&tib); | ||||
|   } | ||||
| // Both Microsoft and the Fifth Bell System agree on this one.
 | ||||
| #define kStackAlign 16 | ||||
| 
 | ||||
|   STRACE("__onntconsoleevent(%u)", dwCtrlType); | ||||
| __msabi extern typeof(CloseHandle) *const __imp_CloseHandle; | ||||
| __msabi extern typeof(GetLastError) *const __imp_GetLastError; | ||||
| __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; | ||||
| __msabi extern typeof(GetThreadContext) *const __imp_GetThreadContext; | ||||
| __msabi extern typeof(OpenThread) *const __imp_OpenThread; | ||||
| __msabi extern typeof(ResumeThread) *const __imp_ResumeThread; | ||||
| __msabi extern typeof(SuspendThread) *const __imp_SuspendThread; | ||||
| __msabi extern typeof(WriteFile) *const __imp_WriteFile; | ||||
| 
 | ||||
| int WinThreadLaunch(int, int, int (*)(int, int), intptr_t); | ||||
| 
 | ||||
| static unsigned long StrLen(const char *s) { | ||||
|   unsigned long n = 0; | ||||
|   while (*s++) ++n; | ||||
|   return n; | ||||
| } | ||||
| 
 | ||||
| static void Log(const char *s) { | ||||
| #ifndef NDEBUG | ||||
|   __imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static int GetSig(uint32_t dwCtrlType) { | ||||
|   switch (dwCtrlType) { | ||||
|     case kNtCtrlCEvent: | ||||
|       __sig_add(0, SIGINT, SI_KERNEL); | ||||
|       return true; | ||||
|       return SIGINT; | ||||
|     case kNtCtrlBreakEvent: | ||||
|       __sig_add(0, SIGQUIT, SI_KERNEL); | ||||
|       return true; | ||||
|       return SIGQUIT; | ||||
|     case kNtCtrlCloseEvent: | ||||
|     case kNtCtrlLogoffEvent:    // only received by services
 | ||||
|     case kNtCtrlShutdownEvent:  // only received by services
 | ||||
|       __sig_add(0, SIGHUP, SI_KERNEL); | ||||
|       return true; | ||||
|       return SIGHUP; | ||||
|     default: | ||||
|       return false; | ||||
|       __builtin_unreachable(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| __msabi textwindows dontinstrument dontasan dontubsan bool32 | ||||
| __onntconsoleevent(uint32_t dwCtrlType) { | ||||
| 
 | ||||
|   // the signal to be delivered
 | ||||
|   int sig = GetSig(dwCtrlType); | ||||
|   int sic = SI_KERNEL; | ||||
| 
 | ||||
|   // if we don't have tls, then we can't hijack a safe stack from a
 | ||||
|   // thread so just try our luck punting the signal to the next i/o
 | ||||
|   if (!__tls_enabled) { | ||||
|     goto PuntSignal; | ||||
|   } | ||||
| 
 | ||||
|   // we're on a stack that's owned by win32. to make matters worse,
 | ||||
|   // win32 spawns a totally new thread just to invoke this handler.
 | ||||
|   // that means most of the cosmo runtime is broken right now which
 | ||||
|   // means we can't call the user signal handler safely. what we'll
 | ||||
|   // do instead is pick a posix thread at random to hijack, pretend
 | ||||
|   // to be that thread, use its stack, and then deliver this signal
 | ||||
|   // asynchronously if it isn't blocked. hopefully it won't longjmp
 | ||||
|   // because once the handler returns, we'll restore the old thread
 | ||||
|   bool gotsome = false; | ||||
|   pthread_spin_lock(&_pthread_lock); | ||||
|   for (struct Dll *e = dll_first(_pthread_list); e && !gotsome; | ||||
|        e = dll_next(_pthread_list, e)) { | ||||
|     struct PosixThread *pt = POSIXTHREAD_CONTAINER(e); | ||||
|     int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); | ||||
|     if (tid <= 0) continue;  // -1 means spawning, 0 means terminated
 | ||||
|     if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue;  // blocked
 | ||||
|     intptr_t th; | ||||
|     if ((th = __imp_OpenThread(kNtThreadSuspendResume | kNtThreadGetContext, | ||||
|                                false, tid))) { | ||||
|       uint32_t old_suspend_count = __imp_SuspendThread(th); | ||||
|       if (old_suspend_count != -1u) { | ||||
|         if (!old_suspend_count && | ||||
|             atomic_load_explicit(&pt->status, memory_order_acquire) < | ||||
|                 kPosixThreadTerminated) { | ||||
|           struct NtContext ctx; | ||||
|           __repstosb(&ctx, 0, sizeof(ctx)); | ||||
|           ctx.ContextFlags = kNtContextControl; | ||||
|           if (__imp_GetThreadContext(th, &ctx)) { | ||||
|             gotsome = true; | ||||
|             pthread_spin_unlock(&_pthread_lock); | ||||
|             __set_tls_win32(pt->tib); | ||||
|             WinThreadLaunch(sig, sic, __sig_raise, | ||||
|                             ROUNDDOWN(ctx.Rsp - kRedzoneSize, kStackAlign) - 8); | ||||
|           } else { | ||||
|             Log("GetThreadContext failed\n"); | ||||
|           } | ||||
|         } | ||||
|         __imp_ResumeThread(th); | ||||
|       } else { | ||||
|         Log("SuspendThread failed\n"); | ||||
|       } | ||||
|       __imp_CloseHandle(th); | ||||
|     } else { | ||||
|       Log("OpenThread failed\n"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (!gotsome) { | ||||
|     pthread_spin_unlock(&_pthread_lock); | ||||
|   PuntSignal: | ||||
|     __sig_add(0, sig, sic); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| #endif /* __x86_64__ */ | ||||
|  |  | |||
|  | @ -18,17 +18,11 @@ | |||
| ╚─────────────────────────────────────────────────────────────────────────────*/ | ||||
| #include "libc/dce.h" | ||||
| #include "libc/macros.internal.h" | ||||
| .text.windows | ||||
| 
 | ||||
| __onntconsoleevent_nt: | ||||
| 	ezlea	__onntconsoleevent,ax | ||||
| 	jmp	__nt2sysv | ||||
| 	.endfn	__onntconsoleevent_nt,globl,hidden | ||||
| 
 | ||||
| 	.init.start 300,_init_onntconsoleevent | ||||
| 	testb	IsWindows() | ||||
| 	jz	1f | ||||
| 	ezlea	__onntconsoleevent_nt,cx | ||||
| 	ezlea	__onntconsoleevent,cx | ||||
| 	pushpop	1,%rdx | ||||
| 	ntcall	__imp_SetConsoleCtrlHandler | ||||
| 1:	.init.end 300,_init_onntconsoleevent,globl,hidden | ||||
|  |  | |||
|  | @ -28,7 +28,6 @@ | |||
| #include "libc/errno.h" | ||||
| #include "libc/intrin/kprintf.h" | ||||
| #include "libc/intrin/nomultics.internal.h" | ||||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/intrin/weaken.h" | ||||
| #include "libc/macros.internal.h" | ||||
| #include "libc/nt/console.h" | ||||
|  | @ -107,6 +106,7 @@ StartOver: | |||
|       // since for overlapped i/o, we always use GetOverlappedResult
 | ||||
|       ok = ReadFile(handle, targetdata, targetsize, 0, &overlap); | ||||
|       if (!ok && GetLastError() == kNtErrorIoPending) { | ||||
|       TryAgain: | ||||
|         // the i/o operation is in flight; blocking is unavoidable
 | ||||
|         // if we're in a non-blocking mode, then immediately abort
 | ||||
|         // if an interrupt is pending then we abort before waiting
 | ||||
|  | @ -137,6 +137,9 @@ StartOver: | |||
|         // overlapped is allocated on stack, so it's important we wait
 | ||||
|         // for windows to acknowledge that it's done using that memory
 | ||||
|         ok = GetOverlappedResult(handle, &overlap, &got, true); | ||||
|         if (!ok && GetLastError() == kNtErrorIoIncomplete) { | ||||
|           goto TryAgain; | ||||
|         } | ||||
|       } | ||||
|       CloseHandle(overlap.hEvent); | ||||
|     } else { | ||||
|  |  | |||
|  | @ -20,8 +20,11 @@ | |||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/mem/alloca.h" | ||||
| #include "libc/nt/createfile.h" | ||||
| #include "libc/nt/enum/accessmask.h" | ||||
| #include "libc/nt/enum/creationdisposition.h" | ||||
| #include "libc/nt/enum/fileflagandattributes.h" | ||||
| #include "libc/nt/enum/filesharemode.h" | ||||
| #include "libc/nt/enum/filetype.h" | ||||
| #include "libc/nt/enum/fsctl.h" | ||||
| #include "libc/nt/enum/io.h" | ||||
| #include "libc/nt/files.h" | ||||
|  | @ -34,28 +37,45 @@ | |||
| 
 | ||||
| textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, | ||||
|                                       size_t bufsiz) { | ||||
| 
 | ||||
|   char16_t path16[PATH_MAX]; | ||||
|   if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; | ||||
|   size_t len = strlen16(path16); | ||||
|   bool must_be_directory = len > 1 && path16[len - 1] == '\\'; | ||||
|   if (must_be_directory) path16[--len] = 0; | ||||
| 
 | ||||
|   int64_t h; | ||||
|   ssize_t rc; | ||||
|   uint64_t w; | ||||
|   wint_t x, y; | ||||
|   volatile char *memory; | ||||
|   uint32_t i, j, n, mem; | ||||
|   char16_t path16[PATH_MAX], *p; | ||||
|   struct NtReparseDataBuffer *rdb; | ||||
|   if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; | ||||
|   mem = 16384; | ||||
|   memory = alloca(mem); | ||||
|   uint32_t mem = 16384; | ||||
|   volatile char *memory = alloca(mem); | ||||
|   CheckLargeStackAllocation((char *)memory, mem); | ||||
|   rdb = (struct NtReparseDataBuffer *)memory; | ||||
|   if ((h = CreateFile(path16, 0, 0, 0, kNtOpenExisting, | ||||
|   struct NtReparseDataBuffer *rdb = (struct NtReparseDataBuffer *)memory; | ||||
|   if ((h = CreateFile(path16, kNtFileReadAttributes, | ||||
|                       kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, | ||||
|                       0, kNtOpenExisting, | ||||
|                       kNtFileFlagOpenReparsePoint | kNtFileFlagBackupSemantics, | ||||
|                       0)) != -1) { | ||||
|     if (DeviceIoControl(h, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &n, 0)) { | ||||
| 
 | ||||
|     // if path had trailing slash, assert last component is directory
 | ||||
|     if (must_be_directory) { | ||||
|       struct NtByHandleFileInformation wst; | ||||
|       if (GetFileType(h) != kNtFileTypeDisk || | ||||
|           (GetFileInformationByHandle(h, &wst) && | ||||
|            !(wst.dwFileAttributes & kNtFileAttributeDirectory))) { | ||||
|         return enotdir(); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     uint32_t bc; | ||||
|     if (DeviceIoControl(h, kNtFsctlGetReparsePoint, 0, 0, rdb, mem, &bc, 0)) { | ||||
|       if (rdb->ReparseTag == kNtIoReparseTagSymlink) { | ||||
|         i = 0; | ||||
|         j = 0; | ||||
|         n = rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t); | ||||
|         p = (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer + | ||||
| 
 | ||||
|         uint32_t i = 0; | ||||
|         uint32_t j = 0; | ||||
|         uint32_t n = | ||||
|             rdb->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(char16_t); | ||||
|         char16_t *p = | ||||
|             (char16_t *)((char *)rdb->SymbolicLinkReparseBuffer.PathBuffer + | ||||
|                          rdb->SymbolicLinkReparseBuffer.PrintNameOffset); | ||||
|         if (n >= 3 && isalpha(p[0]) && p[1] == ':' && p[2] == '\\') { | ||||
|           p[1] = p[0]; | ||||
|  | @ -63,15 +83,18 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, | |||
|           p[2] = '/'; | ||||
|         } | ||||
|         while (i < n) { | ||||
|           x = p[i++] & 0xffff; | ||||
| 
 | ||||
|           wint_t x = p[i++] & 0xffff; | ||||
|           if (!IsUcs2(x)) { | ||||
|             if (i < n) { | ||||
|               y = p[i++] & 0xffff; | ||||
|               wint_t y = p[i++] & 0xffff; | ||||
|               x = MergeUtf16(x, y); | ||||
|             } else { | ||||
|               x = 0xfffd; | ||||
|             } | ||||
|           } | ||||
| 
 | ||||
|           uint64_t w; | ||||
|           if (x < 0200) { | ||||
|             if (x == '\\') { | ||||
|               x = '/'; | ||||
|  | @ -95,6 +118,7 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, | |||
|     } else { | ||||
|       rc = -1; | ||||
|     } | ||||
| 
 | ||||
|     CloseHandle(h); | ||||
|   } else { | ||||
|     rc = __fix_enotdir(-1, path16); | ||||
|  |  | |||
|  | @ -24,8 +24,8 @@ | |||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/intrin/weaken.h" | ||||
| #include "libc/runtime/runtime.h" | ||||
| #include "libc/sysv/errfuns.h" | ||||
| #include "libc/runtime/zipos.internal.h" | ||||
| #include "libc/sysv/errfuns.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * Reads symbolic link. | ||||
|  | @ -40,12 +40,20 @@ | |||
|  *     and this buffer will *not* be nul-terminated | ||||
|  * @return number of bytes written to buf, or -1 w/ errno; if the | ||||
|  *     return is equal to bufsiz then truncation may have occurred | ||||
|  * @error EINVAL if path isn't a symbolic link | ||||
|  * @raise EINVAL if path isn't a symbolic link | ||||
|  * @raise ENOENT if `path` didn't exist | ||||
|  * @raise ENOTDIR if parent component existed that's not a directory | ||||
|  * @raise ENOTDIR if base component ends with slash and is not a dir | ||||
|  * @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX` | ||||
|  * @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX` | ||||
|  * @raise EBADF on relative `path` when `dirfd` isn't open or `AT_FDCWD` | ||||
|  * @raise ELOOP if a loop was detected resolving parent components | ||||
|  * @asyncsignalsafe | ||||
|  */ | ||||
| ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) { | ||||
|   ssize_t bytes; | ||||
|   if ((IsAsan() && !__asan_is_valid(buf, bufsiz)) || (bufsiz && !buf)) { | ||||
|   if ((bufsiz && !buf) || (IsAsan() && (!__asan_is_valid_str(path) || | ||||
|                                         !__asan_is_valid(buf, bufsiz)))) { | ||||
|     bytes = efault(); | ||||
|   } else if (_weaken(__zipos_notat) && | ||||
|              (bytes = __zipos_notat(dirfd, path)) == -1) { | ||||
|  |  | |||
|  | @ -24,7 +24,6 @@ | |||
| #include "libc/calls/struct/winsize.internal.h" | ||||
| #include "libc/dce.h" | ||||
| #include "libc/intrin/atomic.h" | ||||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/nt/console.h" | ||||
| #include "libc/nt/struct/consolescreenbufferinfoex.h" | ||||
| #include "libc/sysv/consts/sicode.h" | ||||
|  | @ -66,7 +65,6 @@ __attribute__((__constructor__)) static void sigwinch_init(void) { | |||
|   if (!IsWindows()) return; | ||||
|   unsigned ws = __get_console_size(); | ||||
|   atomic_store_explicit(&__win_winsize, ws, memory_order_release); | ||||
|   STRACE("sigwinch_init() → %08x", ws); | ||||
| } | ||||
| 
 | ||||
| #endif /* __x86_64__ */ | ||||
|  |  | |||
|  | @ -185,18 +185,23 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { | |||
|     if (opt == TCSAFLUSH) { | ||||
|       FlushConsoleInputBuffer(hInput); | ||||
|     } | ||||
|     inmode &= | ||||
|         ~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput); | ||||
|     inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | | ||||
|                 kNtEnableProcessedInput | kNtEnableVirtualTerminalInput); | ||||
|     inmode |= kNtEnableWindowInput; | ||||
|     __ttymagic = 0; | ||||
|     if (tio->c_lflag & ICANON) { | ||||
|       inmode |= kNtEnableLineInput; | ||||
|       inmode |= kNtEnableLineInput | kNtEnableProcessedInput; | ||||
|     } else { | ||||
|       __ttymagic |= kFdTtyMunging; | ||||
|       if (tio->c_cc[VMIN] != 1) { | ||||
|         STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows"); | ||||
|         return einval(); | ||||
|       } | ||||
|       if (IsAtLeastWindows10()) { | ||||
|         // - keys like f1, up, etc. get turned into \e ansi codes
 | ||||
|         // - totally destroys default console gui (e.g. up arrow)
 | ||||
|         inmode |= kNtEnableVirtualTerminalInput; | ||||
|       } | ||||
|     } | ||||
|     if (!(tio->c_iflag & ICRNL)) { | ||||
|       __ttymagic |= kFdTtyNoCr2Nl; | ||||
|  | @ -220,13 +225,11 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) { | |||
|     if (!(tio->c_lflag & ISIG)) { | ||||
|       __ttymagic |= kFdTtyNoIsigs; | ||||
|     } | ||||
|     if (IsAtLeastWindows10()) { | ||||
|       inmode |= kNtEnableVirtualTerminalInput; | ||||
|     } | ||||
|     __vintr = tio->c_cc[VINTR]; | ||||
|     __vquit = tio->c_cc[VQUIT]; | ||||
|     if ((tio->c_lflag & ISIG) &&  //
 | ||||
|         tio->c_cc[VINTR] == CTRL('C')) { | ||||
|       // allows ctrl-c to be delivered asynchronously via win32
 | ||||
|       inmode |= kNtEnableProcessedInput; | ||||
|     } | ||||
|     ok = SetConsoleMode(hInput, inmode); | ||||
|  |  | |||
|  | @ -20,7 +20,9 @@ | |||
| #include "libc/calls/internal.h" | ||||
| #include "libc/calls/syscall_support-nt.internal.h" | ||||
| #include "libc/dce.h" | ||||
| #include "libc/intrin/atomic.h" | ||||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/log/libfatal.internal.h" | ||||
| #include "libc/nt/createfile.h" | ||||
| #include "libc/nt/enum/accessmask.h" | ||||
| #include "libc/nt/enum/creationdisposition.h" | ||||
|  | @ -30,6 +32,8 @@ | |||
| #include "libc/nt/synchronization.h" | ||||
| #include "libc/nt/thread.h" | ||||
| #include "libc/nt/thunk/msabi.h" | ||||
| #include "libc/thread/tls.h" | ||||
| #include "libc/thread/tls2.internal.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * @fileoverview makes windows stdin handle capable of being poll'd | ||||
|  | @ -56,11 +60,24 @@ __msabi extern typeof(CreateFile) *const __imp_CreateFileW; | |||
| __msabi extern typeof(CreateNamedPipe) *const __imp_CreateNamedPipeW; | ||||
| __msabi extern typeof(CreateSemaphore) *const __imp_CreateSemaphoreW; | ||||
| __msabi extern typeof(CreateThread) *const __imp_CreateThread; | ||||
| __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId; | ||||
| __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; | ||||
| __msabi extern typeof(ReadFile) *const __imp_ReadFile; | ||||
| __msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; | ||||
| __msabi extern typeof(WriteFile) *const __imp_WriteFile; | ||||
| 
 | ||||
| static unsigned long StrLen(const char *s) { | ||||
|   unsigned long n = 0; | ||||
|   while (*s++) ++n; | ||||
|   return n; | ||||
| } | ||||
| 
 | ||||
| static void Log(const char *s) { | ||||
| #if 0 | ||||
|   __imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| __msabi static dontasan dontubsan dontinstrument textwindows uint32_t | ||||
| WinStdinThread(void *lpParameter) { | ||||
|   char buf[4096]; | ||||
|  | @ -75,19 +92,19 @@ WinStdinThread(void *lpParameter) { | |||
|   __imp_CloseHandle(g_fds.stdin.inisem); | ||||
| 
 | ||||
|   // relay stdin to process
 | ||||
|   NTTRACE("<stdin> activated"); | ||||
|   Log("<stdin> activated\n"); | ||||
|   for (;;) { | ||||
|     if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) { | ||||
|       NTTRACE("<stdin> read failed"); | ||||
|       Log("<stdin> read failed\n"); | ||||
|       goto Finish; | ||||
|     } | ||||
|     if (!got) { | ||||
|       NTTRACE("<stdin> end of file"); | ||||
|       Log("<stdin> end of file\n"); | ||||
|       goto Finish; | ||||
|     } | ||||
|     for (i = 0; i < got; i += wrote) { | ||||
|       if (!__imp_WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) { | ||||
|         NTTRACE("<stdin> failed to write to pipe"); | ||||
|         Log("<stdin> failed to write to pipe\n"); | ||||
|         goto Finish; | ||||
|       } | ||||
|     } | ||||
|  | @ -106,13 +123,13 @@ dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) { | |||
|   if (!SupportsWindows()) return; | ||||
|   hStdin = __imp_GetStdHandle(kNtStdInputHandle); | ||||
|   if (hStdin == kNtInvalidHandleValue) { | ||||
|     NTTRACE("<stdin> GetStdHandle failed"); | ||||
|     Log("<stdin> GetStdHandle failed\n"); | ||||
|     return; | ||||
|   } | ||||
|   // create non-inherited semaphore with initial value of 0
 | ||||
|   hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0); | ||||
|   if (!hSemaphore) { | ||||
|     NTTRACE("<stdin> CreateSemaphore failed"); | ||||
|     Log("<stdin> CreateSemaphore failed\n"); | ||||
|     return; | ||||
|   } | ||||
|   __create_pipe_name(pipename); | ||||
|  | @ -120,18 +137,18 @@ dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) { | |||
|       pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, | ||||
|       kNtPipeTypeByte | kNtPipeReadmodeByte, 1, 4096, 4096, 0, 0); | ||||
|   if (hReader == kNtInvalidHandleValue) { | ||||
|     NTTRACE("<stdin> CreateNamedPipe failed"); | ||||
|     Log("<stdin> CreateNamedPipe failed\n"); | ||||
|     return; | ||||
|   } | ||||
|   hWriter = __imp_CreateFileW(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, | ||||
|                               kNtFileFlagOverlapped, 0); | ||||
|   if (hWriter == kNtInvalidHandleValue) { | ||||
|     NTTRACE("<stdin> CreateFile failed"); | ||||
|     Log("<stdin> CreateFile failed\n"); | ||||
|     return; | ||||
|   } | ||||
|   hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0); | ||||
|   if (!hThread) { | ||||
|     NTTRACE("<stdin> CreateThread failed"); | ||||
|     Log("<stdin> CreateThread failed\n"); | ||||
|     return; | ||||
|   } | ||||
|   g_fds.stdin.handle = hStdin; | ||||
|  |  | |||
|  | @ -25,11 +25,11 @@ | |||
| //	This function switches us over, so that we can start using the | ||||
| //	runtime facilities. | ||||
| // | ||||
| //	@param	%rdi is arg
 | ||||
| //	@param	%rsi is tid
 | ||||
| //	@param	%rdi is arg1
 | ||||
| //	@param	%rsi is arg2
 | ||||
| //	@param	%rdx is func
 | ||||
| //	@param	%rcx is stack
 | ||||
| //	@return	%rax is exit code
 | ||||
| //	@return	%rax is res
 | ||||
| //	@see	clone()
 | ||||
| WinThreadLaunch: | ||||
| 	push	%rbx | ||||
|  | @ -1,218 +0,0 @@ | |||
| /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8     -*-│
 | ||||
| │vi: set et ft=asm ts=8 tw=8 fenc=utf-8                                     :vi│ | ||||
| ╚──────────────────────────────────────────────────────────────────────────────╝ | ||||
| │                                                                              │ | ||||
| │  Optimized Routines                                                          │ | ||||
| │  Copyright (c) 1999-2022, Arm Limited.                                       │ | ||||
| │                                                                              │ | ||||
| │  Permission is hereby granted, free of charge, to any person obtaining       │ | ||||
| │  a copy of this software and associated documentation files (the             │ | ||||
| │  "Software"), to deal in the Software without restriction, including         │ | ||||
| │  without limitation the rights to use, copy, modify, merge, publish,         │ | ||||
| │  distribute, sublicense, and/or sell copies of the Software, and to          │ | ||||
| │  permit persons to whom the Software is furnished to do so, subject to       │ | ||||
| │  the following conditions:                                                   │ | ||||
| │                                                                              │ | ||||
| │  The above copyright notice and this permission notice shall be              │ | ||||
| │  included in all copies or substantial portions of the Software.             │ | ||||
| │                                                                              │ | ||||
| │  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,             │ | ||||
| │  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF          │ | ||||
| │  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.      │ | ||||
| │  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY        │ | ||||
| │  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,        │ | ||||
| │  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE           │ | ||||
| │  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                      │ | ||||
| │                                                                              │ | ||||
| ╚─────────────────────────────────────────────────────────────────────────────*/ | ||||
| #include "libc/intrin/aarch64/asmdefs.internal.h" | ||||
| 
 | ||||
| #define __memcmp_aarch64 memcmp | ||||
| 
 | ||||
| .ident "\n\n\ | ||||
| Optimized Routines (MIT License)\n\ | ||||
| Copyright 2022 ARM Limited\n" | ||||
| .include "libc/disclaimer.inc" | ||||
| 
 | ||||
| /* Assumptions: | ||||
|  * | ||||
|  * ARMv8-a, AArch64, Advanced SIMD, unaligned accesses. | ||||
|  */ | ||||
| 
 | ||||
| #define src1	x0 | ||||
| #define src2	x1 | ||||
| #define limit	x2 | ||||
| #define result	w0 | ||||
| 
 | ||||
| #define data1	x3 | ||||
| #define data1w	w3 | ||||
| #define data2	x4 | ||||
| #define data2w	w4 | ||||
| #define data3	x5 | ||||
| #define data3w	w5 | ||||
| #define data4	x6 | ||||
| #define data4w	w6 | ||||
| #define tmp	x6 | ||||
| #define src1end	x7 | ||||
| #define src2end	x8 | ||||
| 
 | ||||
| 
 | ||||
| ENTRY (__memcmp_aarch64) | ||||
| 	PTR_ARG (0) | ||||
| 	PTR_ARG (1) | ||||
| 	SIZE_ARG (2) | ||||
| 
 | ||||
| 	cmp	limit, 16 | ||||
| 	b.lo	L(less16) | ||||
| 	ldp	data1, data3, [src1] | ||||
| 	ldp	data2, data4, [src2] | ||||
| 	ccmp	data1, data2, 0, ne | ||||
| 	ccmp	data3, data4, 0, eq | ||||
| 	b.ne	L(return2) | ||||
| 
 | ||||
| 	add	src1end, src1, limit | ||||
| 	add	src2end, src2, limit | ||||
| 	cmp	limit, 32 | ||||
| 	b.ls	L(last_bytes) | ||||
| 	cmp	limit, 160 | ||||
| 	b.hs	L(loop_align) | ||||
| 	sub	limit, limit, 32 | ||||
| 
 | ||||
| 	.p2align 4
 | ||||
| L(loop32): | ||||
| 	ldp	data1, data3, [src1, 16] | ||||
| 	ldp	data2, data4, [src2, 16] | ||||
| 	cmp	data1, data2 | ||||
| 	ccmp	data3, data4, 0, eq | ||||
| 	b.ne	L(return2) | ||||
| 	cmp	limit, 16 | ||||
| 	b.ls	L(last_bytes) | ||||
| 
 | ||||
| 	ldp	data1, data3, [src1, 32] | ||||
| 	ldp	data2, data4, [src2, 32] | ||||
| 	cmp	data1, data2 | ||||
| 	ccmp	data3, data4, 0, eq | ||||
| 	b.ne	L(return2) | ||||
| 	add	src1, src1, 32 | ||||
| 	add	src2, src2, 32 | ||||
| L(last64): | ||||
| 	subs	limit, limit, 32 | ||||
| 	b.hi	L(loop32) | ||||
| 
 | ||||
| 	/* Compare last 1-16 bytes using unaligned access.  */ | ||||
| L(last_bytes): | ||||
| 	ldp	data1, data3, [src1end, -16] | ||||
| 	ldp	data2, data4, [src2end, -16] | ||||
| L(return2): | ||||
| 	cmp	data1, data2 | ||||
| 	csel	data1, data1, data3, ne | ||||
| 	csel	data2, data2, data4, ne | ||||
| 
 | ||||
| 	/* Compare data bytes and set return value to 0, -1 or 1.  */ | ||||
| L(return): | ||||
| #ifndef __AARCH64EB__ | ||||
| 	rev	data1, data1 | ||||
| 	rev	data2, data2 | ||||
| #endif | ||||
| 	cmp	data1, data2 | ||||
| 	cset	result, ne | ||||
| 	cneg	result, result, lo | ||||
| 	ret | ||||
| 
 | ||||
| 	.p2align 4
 | ||||
| L(less16): | ||||
| 	add	src1end, src1, limit | ||||
| 	add	src2end, src2, limit | ||||
| 	tbz	limit, 3, L(less8) | ||||
| 	ldr	data1, [src1] | ||||
| 	ldr	data2, [src2] | ||||
| 	ldr	data3, [src1end, -8] | ||||
| 	ldr	data4, [src2end, -8] | ||||
| 	b	L(return2) | ||||
| 
 | ||||
| 	.p2align 4
 | ||||
| L(less8): | ||||
| 	tbz	limit, 2, L(less4) | ||||
| 	ldr	data1w, [src1] | ||||
| 	ldr	data2w, [src2] | ||||
| 	ldr	data3w, [src1end, -4] | ||||
| 	ldr	data4w, [src2end, -4] | ||||
| 	b	L(return2) | ||||
| 
 | ||||
| L(less4): | ||||
| 	tbz	limit, 1, L(less2) | ||||
| 	ldrh	data1w, [src1] | ||||
| 	ldrh	data2w, [src2] | ||||
| 	cmp	data1w, data2w | ||||
| 	b.ne	L(return) | ||||
| L(less2): | ||||
| 	mov	result, 0 | ||||
| 	tbz	limit, 0, L(return_zero) | ||||
| 	ldrb	data1w, [src1end, -1] | ||||
| 	ldrb	data2w, [src2end, -1] | ||||
| 	sub	result, data1w, data2w | ||||
| L(return_zero): | ||||
| 	ret | ||||
| 
 | ||||
| L(loop_align): | ||||
| 	ldp	data1, data3, [src1, 16] | ||||
| 	ldp	data2, data4, [src2, 16] | ||||
| 	cmp	data1, data2 | ||||
| 	ccmp	data3, data4, 0, eq | ||||
| 	b.ne	L(return2) | ||||
| 
 | ||||
| 	/* Align src2 and adjust src1, src2 and limit.  */ | ||||
| 	and	tmp, src2, 15 | ||||
| 	sub	tmp, tmp, 16 | ||||
| 	sub	src2, src2, tmp | ||||
| 	add	limit, limit, tmp | ||||
| 	sub	src1, src1, tmp | ||||
| 	sub	limit, limit, 64 + 16 | ||||
| 
 | ||||
| 	.p2align 4
 | ||||
| L(loop64): | ||||
| 	ldr	q0, [src1, 16] | ||||
| 	ldr	q1, [src2, 16] | ||||
| 	subs	limit, limit, 64 | ||||
| 	ldr	q2, [src1, 32] | ||||
| 	ldr	q3, [src2, 32] | ||||
| 	eor	v0.16b, v0.16b, v1.16b | ||||
| 	eor	v1.16b, v2.16b, v3.16b | ||||
| 	ldr	q2, [src1, 48] | ||||
| 	ldr	q3, [src2, 48] | ||||
| 	umaxp	v0.16b, v0.16b, v1.16b | ||||
| 	ldr	q4, [src1, 64]! | ||||
| 	ldr	q5, [src2, 64]! | ||||
| 	eor	v1.16b, v2.16b, v3.16b | ||||
| 	eor	v2.16b, v4.16b, v5.16b | ||||
| 	umaxp	v1.16b, v1.16b, v2.16b | ||||
| 	umaxp	v0.16b, v0.16b, v1.16b | ||||
| 	umaxp	v0.16b, v0.16b, v0.16b | ||||
| 	fmov	tmp, d0 | ||||
| 	ccmp	tmp, 0, 0, hi | ||||
| 	b.eq	L(loop64) | ||||
| 
 | ||||
| 	/* If equal, process last 1-64 bytes using scalar loop.  */ | ||||
| 	add	limit, limit, 64 + 16 | ||||
| 	cbz	tmp, L(last64) | ||||
| 
 | ||||
| 	/* Determine the 8-byte aligned offset of the first difference.  */ | ||||
| #ifdef __AARCH64EB__ | ||||
| 	rev16	tmp, tmp | ||||
| #endif | ||||
| 	rev	tmp, tmp | ||||
| 	clz	tmp, tmp | ||||
| 	bic	tmp, tmp, 7 | ||||
| 	sub	tmp, tmp, 48 | ||||
| 	ldr	data1, [src1, tmp] | ||||
| 	ldr	data2, [src2, tmp] | ||||
| #ifndef __AARCH64EB__ | ||||
| 	rev	data1, data1 | ||||
| 	rev	data2, data2 | ||||
| #endif | ||||
| 	mov	result, 1 | ||||
| 	cmp	data1, data2 | ||||
| 	cneg	result, result, lo | ||||
| 	ret | ||||
| 
 | ||||
| END (__memcmp_aarch64) | ||||
|  | @ -85,10 +85,6 @@ wontreturn void _Exit(int exitcode) { | |||
| #endif | ||||
|   } else if (IsWindows()) { | ||||
|     uint32_t waitstatus; | ||||
|     // Restoring the CMD.EXE program to its original state is critical.
 | ||||
|     if (_weaken(__restore_console_win32)) { | ||||
|       _weaken(__restore_console_win32)(); | ||||
|     } | ||||
|     // What Microsoft calls an exit code, POSIX calls a status code. See
 | ||||
|     // also the WEXITSTATUS() and WIFEXITED() macros that POSIX defines.
 | ||||
|     waitstatus = exitcode; | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ | |||
| #include "libc/intrin/likely.h" | ||||
| #include "libc/nexgen32e/x86feature.h" | ||||
| #include "libc/str/str.h" | ||||
| #ifndef __aarch64__ | ||||
| 
 | ||||
| typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); | ||||
| 
 | ||||
|  | @ -83,5 +82,3 @@ int memcmp(const void *a, const void *b, size_t n) { | |||
|   } | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| #endif /* __aarch64__ */ | ||||
|  |  | |||
							
								
								
									
										22
									
								
								libc/intrin/pthreadlist.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								libc/intrin/pthreadlist.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| /*-*- 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/intrin/dll.h" | ||||
| #include "libc/thread/posixthread.internal.h" | ||||
| 
 | ||||
| struct Dll *_pthread_list; | ||||
							
								
								
									
										22
									
								
								libc/intrin/pthreadlock.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								libc/intrin/pthreadlock.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| /*-*- 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/thread/posixthread.internal.h" | ||||
| #include "libc/thread/thread.h" | ||||
| 
 | ||||
| pthread_spinlock_t _pthread_lock; | ||||
|  | @ -30,8 +30,6 @@ | |||
| #include "libc/errno.h" | ||||
| #include "libc/intrin/bits.h" | ||||
| #include "libc/intrin/safemacros.internal.h" | ||||
| #include "libc/intrin/strace.internal.h" | ||||
| #include "libc/intrin/weaken.h" | ||||
| #include "libc/limits.h" | ||||
| #include "libc/log/backtrace.internal.h" | ||||
| #include "libc/mem/mem.h" | ||||
|  | @ -44,7 +42,7 @@ asm(".ident\t\"\\n\\n\ | |||
| Musl libc (MIT License)\\n\ | ||||
| Copyright 2005-2014 Rich Felker, et. al.\""); | ||||
| asm(".include \"libc/disclaimer.inc\""); | ||||
| /* clang-format off */ | ||||
| // clang-format off
 | ||||
| 
 | ||||
| static inline bool IsSlash(char c) | ||||
| { | ||||
|  | @ -60,7 +58,7 @@ static size_t GetSlashLen(const char *s) | |||
| 
 | ||||
| static char *ResolvePath(char *d, const char *s, size_t n) | ||||
| { | ||||
| 	if (d || (_weaken(malloc) && (d = _weaken(malloc)(n+1)))) { | ||||
| 	if (d || (d = malloc(n+1))) { | ||||
| 		return memmove(d, s, n+1); | ||||
| 	} else { | ||||
| 		enomem(); | ||||
|  | @ -71,11 +69,26 @@ static char *ResolvePath(char *d, const char *s, size_t n) | |||
| /**
 | ||||
|  * Returns absolute pathname. | ||||
|  * | ||||
|  * This function removes `/./` and `/../` components. IF the path is a | ||||
|  * symbolic link then it's resolved. | ||||
|  * This function removes `/./` and `/../` components. If any individual | ||||
|  * path component is a symbolic link, then it'll be resolved. Any slash | ||||
|  * characters that repeat (e.g. `//`) will collapse into one (i.e. `/`)
 | ||||
|  * | ||||
|  * @param resolved needs PATH_MAX bytes or NULL to use malloc() | ||||
|  * @return resolved or NULL w/ errno | ||||
|  * This implementation is consistent with glibc, in that `"//"` becomes | ||||
|  * `"/"` unlike Musl Libc, which considers that special (not sure why?) | ||||
|  * This is the only change Cosmopolitan Libc made vs. Musl's realpath() | ||||
|  * aside from also being permissive about backslashes, to help Windows. | ||||
|  * | ||||
|  * @param filename is the path that needs to be resolved | ||||
|  * @param resolved needs PATH_MAX bytes, or NULL to use malloc() | ||||
|  * @return resolved path, or NULL w/ errno | ||||
|  * @raise EINVAL if `filename` is NULL | ||||
|  * @raise ENOENT if `filename` is an empty string | ||||
|  * @raise ENOMEM if `resolved` is NULL and malloc() failed | ||||
|  * @raise ENOENT if `filename` didn't exist | ||||
|  * @raise ENOTDIR if directory component existed that's not a directory | ||||
|  * @raise ENOTDIR if base component ends with slash and is not a dir | ||||
|  * @raise ENAMETOOLONG if filename resolution exceeded `PATH_MAX` | ||||
|  * @raise ELOOP if too many symlinks were encountered | ||||
|  */ | ||||
| char *realpath(const char *filename, char *resolved) | ||||
| { | ||||
|  | @ -84,8 +97,6 @@ char *realpath(const char *filename, char *resolved) | |||
| 	size_t k, p, q, l, l0, cnt=0, nup=0; | ||||
| 	char output[PATH_MAX], stack[PATH_MAX+1], *z; | ||||
| 
 | ||||
| 	/* STRACE("realpath(%#s, %#s)", filename, resolved); */ | ||||
| 
 | ||||
| 	if (!filename) { | ||||
| 		einval(); | ||||
| 		return 0; | ||||
|  | @ -118,9 +129,6 @@ restart: | |||
| 			q=0; | ||||
| 			output[q++] = '/'; | ||||
| 			p++; | ||||
| 			/* Initial // is special. */ | ||||
| 			if (IsSlash(stack[p]) && !IsSlash(stack[p+1])) | ||||
| 				output[q++] = '/'; | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
							
								
								
									
										21
									
								
								libc/nt/enum/context.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								libc/nt/enum/context.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| #ifndef COSMOPOLITAN_LIBC_NT_ENUM_CONTEXT_H_ | ||||
| #define COSMOPOLITAN_LIBC_NT_ENUM_CONTEXT_H_ | ||||
| 
 | ||||
| #define kNtContextAmd64 0x00100000 | ||||
| 
 | ||||
| #define kNtContextControl        (kNtContextAmd64 | 0x00000001) | ||||
| #define kNtContextInteger        (kNtContextAmd64 | 0x00000002) | ||||
| #define kNtContextSegments       (kNtContextAmd64 | 0x00000004) | ||||
| #define kNtContextFloatingPoint  (kNtContextAmd64 | 0x00000008) | ||||
| #define kNtContextDebugRegisters (kNtContextAmd64 | 0x00000010) | ||||
| 
 | ||||
| #define kNtContextFull \ | ||||
|   (kNtContextControl | kNtContextInteger | kNtContextFloatingPoint) | ||||
| 
 | ||||
| #define kNtContextAll                                           \ | ||||
|   (kNtContextControl | kNtContextInteger | kNtContextSegments | \ | ||||
|    kNtContextFloatingPoint | kNtContextDebugRegisters) | ||||
| 
 | ||||
| #define kNtContextXstate (kNtContextAmd64 | 0x00000040) | ||||
| 
 | ||||
| #endif /* COSMOPOLITAN_LIBC_NT_ENUM_CONTEXT_H_ */ | ||||
							
								
								
									
										18
									
								
								libc/nt/kernel32/GetThreadContext.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								libc/nt/kernel32/GetThreadContext.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| #include "libc/nt/codegen.h" | ||||
| .imp	kernel32,__imp_GetThreadContext,GetThreadContext | ||||
| 
 | ||||
| 	.text.windows | ||||
|         .ftrace1 | ||||
| GetThreadContext: | ||||
|         .ftrace2 | ||||
| #ifdef __x86_64__ | ||||
| 	push	%rbp | ||||
| 	mov	%rsp,%rbp | ||||
| 	mov	__imp_GetThreadContext(%rip),%rax | ||||
| 	jmp	__sysv2nt | ||||
| #elif defined(__aarch64__) | ||||
| 	mov	x0,#0 | ||||
| 	ret | ||||
| #endif | ||||
| 	.endfn	GetThreadContext,globl | ||||
| 	.previous | ||||
							
								
								
									
										18
									
								
								libc/nt/kernel32/InitializeContext.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								libc/nt/kernel32/InitializeContext.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| #include "libc/nt/codegen.h" | ||||
| .imp	kernel32,__imp_InitializeContext,InitializeContext | ||||
| 
 | ||||
| 	.text.windows | ||||
|         .ftrace1 | ||||
| InitializeContext: | ||||
|         .ftrace2 | ||||
| #ifdef __x86_64__ | ||||
| 	push	%rbp | ||||
| 	mov	%rsp,%rbp | ||||
| 	mov	__imp_InitializeContext(%rip),%rax | ||||
| 	jmp	__sysv2nt | ||||
| #elif defined(__aarch64__) | ||||
| 	mov	x0,#0 | ||||
| 	ret | ||||
| #endif | ||||
| 	.endfn	InitializeContext,globl | ||||
| 	.previous | ||||
							
								
								
									
										20
									
								
								libc/nt/kernel32/ResumeThread.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								libc/nt/kernel32/ResumeThread.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| #include "libc/nt/codegen.h" | ||||
| .imp	kernel32,__imp_ResumeThread,ResumeThread | ||||
| 
 | ||||
| 	.text.windows | ||||
|         .ftrace1 | ||||
| ResumeThread: | ||||
|         .ftrace2 | ||||
| #ifdef __x86_64__ | ||||
| 	push	%rbp | ||||
| 	mov	%rsp,%rbp | ||||
| 	mov	%rdi,%rcx | ||||
| 	sub	$32,%rsp | ||||
| 	call	*__imp_ResumeThread(%rip) | ||||
| 	leave | ||||
| #elif defined(__aarch64__) | ||||
| 	mov	x0,#0 | ||||
| #endif | ||||
| 	ret | ||||
| 	.endfn	ResumeThread,globl | ||||
| 	.previous | ||||
|  | @ -11,9 +11,9 @@ | |||
| #	Name							Actual							DLL		Arity | ||||
| imp	''							CloseHandle						kernel32	1 | ||||
| imp	''							CreateDirectoryW					kernel32	2 | ||||
| imp	''							CreateFileA						kernel32	7 | ||||
| imp	''							CreateFileMappingNumaW					kernel32	7 | ||||
| imp	''							CreateFileMappingW					kernel32	6 | ||||
| imp	''							CreateFileA						kernel32	7 | ||||
| imp	''							CreateFileW						kernel32	7 | ||||
| imp	''							CreateNamedPipeW					kernel32	8 | ||||
| imp	''							CreatePipe						kernel32	4 | ||||
|  | @ -162,6 +162,7 @@ imp	'GetSystemTimePreciseAsFileTime'			GetSystemTimePreciseAsFileTime				kernel3 | |||
| imp	'GetSystemTimes'					GetSystemTimes						kernel32	3 | ||||
| imp	'GetTempPath'						GetTempPathW						kernel32	2 | ||||
| imp	'GetTempPathA'						GetTempPathA						kernel32	2 | ||||
| imp	'GetThreadContext'					GetThreadContext					kernel32	2 | ||||
| imp	'GetThreadIOPendingFlag'				GetThreadIOPendingFlag					kernel32	2 | ||||
| imp	'GetThreadId'						GetThreadId						kernel32	1 | ||||
| imp	'GetThreadPriority'					GetThreadPriority					kernel32	1 | ||||
|  | @ -182,6 +183,7 @@ imp	'HeapCreate'						HeapCreate						kernel32	3 | |||
| imp	'HeapDestroy'						HeapDestroy						kernel32	1 | ||||
| imp	'HeapFree'						HeapFree						kernel32	3 | ||||
| imp	'HeapReAlloc'						HeapReAlloc						kernel32	4 | ||||
| imp	'InitializeContext'					InitializeContext					kernel32	4	# Windows 7+ | ||||
| imp	'InitializeCriticalSection'				InitializeCriticalSection				kernel32	1 | ||||
| imp	'InitializeCriticalSectionAndSpinCount'			InitializeCriticalSectionAndSpinCount			kernel32	2 | ||||
| imp	'InitializeProcThreadAttributeList'			InitializeProcThreadAttributeList			kernel32	4 | ||||
|  | @ -223,6 +225,7 @@ imp	'ReleaseSemaphore'					ReleaseSemaphore					kernel32	3 | |||
| imp	'RemoveVectoredContinueHandler'				RemoveVectoredContinueHandler				kernel32	1 | ||||
| imp	'RemoveVectoredExceptionHandler'			RemoveVectoredExceptionHandler				kernel32	1 | ||||
| imp	'ResetEvent'						ResetEvent						kernel32	1 | ||||
| imp	'ResumeThread'						ResumeThread						kernel32	1 | ||||
| imp	'SetConsoleActiveScreenBuffer'				SetConsoleActiveScreenBuffer				kernel32	1	# TODO(jart): 6.2 and higher | ||||
| imp	'SetConsoleCP'						SetConsoleCP						kernel32	1	# TODO(jart): 6.2 and higher | ||||
| imp	'SetConsoleCtrlHandler'					SetConsoleCtrlHandler					kernel32	2 | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #ifndef COSMOPOLITAN_LIBC_NT_THREADS_H_ | ||||
| #define COSMOPOLITAN_LIBC_NT_THREADS_H_ | ||||
| #include "libc/nt/struct/context.h" | ||||
| #include "libc/nt/struct/overlapped.h" | ||||
| #include "libc/nt/struct/securityattributes.h" | ||||
| #include "libc/nt/thunk/msabi.h" | ||||
|  | @ -60,6 +61,10 @@ bool32 TlsFree(uint32_t); | |||
| bool32 TlsSetValue(uint32_t, void *); | ||||
| void *TlsGetValue(uint32_t); | ||||
| 
 | ||||
| uint32_t SuspendThread(int64_t hThread); | ||||
| uint32_t ResumeThread(int64_t hThread); | ||||
| bool32 GetThreadContext(int64_t hThread, struct NtContext *in_out_lpContext); | ||||
| 
 | ||||
| #if ShouldUseMsabiAttribute() | ||||
| #include "libc/nt/thunk/thread.inc" | ||||
| #endif /* ShouldUseMsabiAttribute() */ | ||||
|  |  | |||
|  | @ -40,8 +40,6 @@ | |||
| extern unsigned char __tls_mov_nt_rax[]; | ||||
| extern unsigned char __tls_add_nt_rax[]; | ||||
| 
 | ||||
| struct Dll *_pthread_list; | ||||
| pthread_spinlock_t _pthread_lock; | ||||
| static struct PosixThread _pthread_main; | ||||
| _Alignas(TLS_ALIGNMENT) static char __static_tls[6016]; | ||||
| 
 | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ | |||
| __static_yoink("_check_sigchld"); | ||||
| 
 | ||||
| extern int64_t __wincrashearly; | ||||
| bool32 __onntconsoleevent_nt(uint32_t); | ||||
| bool32 __onntconsoleevent(uint32_t); | ||||
| void sys_setitimer_nt_reset(void); | ||||
| void kmalloc_unlock(void); | ||||
| 
 | ||||
|  | @ -308,8 +308,8 @@ textwindows void WinMainForked(void) { | |||
|     } | ||||
|     AddVectoredExceptionHandler(1, (void *)_weaken(__wincrash_nt)); | ||||
|   } | ||||
|   if (_weaken(__onntconsoleevent_nt)) { | ||||
|     SetConsoleCtrlHandler(_weaken(__onntconsoleevent_nt), 1); | ||||
|   if (_weaken(__onntconsoleevent)) { | ||||
|     SetConsoleCtrlHandler(_weaken(__onntconsoleevent), 1); | ||||
|   } | ||||
| 
 | ||||
|   // jump back into function below
 | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ | |||
| #include "libc/runtime/stack.h" | ||||
| #include "libc/runtime/winargs.internal.h" | ||||
| #include "libc/sock/internal.h" | ||||
| #include "libc/str/str.h" | ||||
| #include "libc/sysv/consts/prot.h" | ||||
| 
 | ||||
| #ifdef __x86_64__ | ||||
|  | @ -66,6 +67,7 @@ __msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode; | |||
| __msabi extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess; | ||||
| __msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId; | ||||
| __msabi extern typeof(GetEnvironmentStrings) *const __imp_GetEnvironmentStringsW; | ||||
| __msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW; | ||||
| __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; | ||||
| __msabi extern typeof(MapViewOfFileEx) *const __imp_MapViewOfFileEx; | ||||
| __msabi extern typeof(SetConsoleCP) *const __imp_SetConsoleCP; | ||||
|  | @ -73,7 +75,6 @@ __msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode; | |||
| __msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP; | ||||
| __msabi extern typeof(SetStdHandle) *const __imp_SetStdHandle; | ||||
| __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; | ||||
| __msabi extern typeof(WriteFile) *const __imp_WriteFile; | ||||
| // clang-format on
 | ||||
| 
 | ||||
| extern void cosmo(int, char **, char **, long (*)[2]) wontreturn; | ||||
|  | @ -84,20 +85,6 @@ static const signed char kNtStdio[3] = { | |||
|     (signed char)kNtStdErrorHandle, | ||||
| }; | ||||
| 
 | ||||
| static const short kConsoleModes[3] = { | ||||
|     kNtEnableProcessedInput | kNtEnableLineInput | kNtEnableEchoInput | | ||||
|         kNtEnableMouseInput | kNtEnableQuickEditMode | kNtEnableExtendedFlags | | ||||
|         kNtEnableAutoPosition | kNtEnableInsertMode | | ||||
|         kNtEnableVirtualTerminalInput, | ||||
|     kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | | ||||
|         kNtEnableVirtualTerminalProcessing, | ||||
|     kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | | ||||
|         kNtEnableVirtualTerminalProcessing, | ||||
| }; | ||||
| 
 | ||||
| static uint32_t __init_pid; | ||||
| static uint32_t __console_mode[3]; | ||||
| 
 | ||||
| forceinline int IsAlpha(int c) { | ||||
|   return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); | ||||
| } | ||||
|  | @ -108,16 +95,6 @@ __msabi long __oops_win32(void) { | |||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| // called by _exit to undo our config changes to cmd.exe
 | ||||
| // it must never ever be called from forked subprocesses
 | ||||
| void __restore_console_win32(void) { | ||||
|   if (__imp_GetCurrentProcessId() == __init_pid) { | ||||
|     for (int i = 0; i < 3; ++i) { | ||||
|       __imp_SetConsoleMode(__imp_GetStdHandle(kNtStdio[i]), __console_mode[i]); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // https://nullprogram.com/blog/2022/02/18/
 | ||||
| __msabi static inline char16_t *MyCommandLine(void) { | ||||
|   void *cmd; | ||||
|  | @ -128,6 +105,15 @@ __msabi static inline char16_t *MyCommandLine(void) { | |||
|   return cmd; | ||||
| } | ||||
| 
 | ||||
| // returns true if utf-8 path is a win32-style path that exists
 | ||||
| __msabi static textwindows bool32 WinFileExists(const char *path) { | ||||
|   uint16_t path16[PATH_MAX]; | ||||
|   size_t z = ARRAYLEN(path16); | ||||
|   size_t n = tprecode8to16(path16, z, path).ax; | ||||
|   if (n >= z - 1) return false; | ||||
|   return __imp_GetFileAttributesW(path16) != -1u; | ||||
| } | ||||
| 
 | ||||
| // this ensures close(1) won't accidentally close(2) for example
 | ||||
| __msabi static textwindows void DeduplicateStdioHandles(void) { | ||||
|   for (long i = 0; i < 3; ++i) { | ||||
|  | @ -144,27 +130,30 @@ __msabi static textwindows void DeduplicateStdioHandles(void) { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { | ||||
|   size_t stacksize; | ||||
|   struct WinArgs *wa; | ||||
|   uintptr_t stackaddr; | ||||
|   __init_pid = __pid; | ||||
| // main function of windows init process
 | ||||
| // i.e. first process spawned that isn't forked
 | ||||
| __msabi static textwindows wontreturn void WinInit(const char16_t *cmdline) { | ||||
|   __oldstack = (intptr_t)__builtin_frame_address(0); | ||||
| 
 | ||||
|   // make console into utf-8 ansi/xterm style tty
 | ||||
|   if (NtGetPeb()->OSMajorVersion >= 10 && | ||||
|       (intptr_t)v_ntsubsystem == kNtImageSubsystemWindowsCui) { | ||||
|     __imp_SetConsoleCP(kNtCpUtf8); | ||||
|     __imp_SetConsoleOutputCP(kNtCpUtf8); | ||||
|     for (int i = 0; i < 3; ++i) { | ||||
|       int64_t hand = __imp_GetStdHandle(kNtStdio[i]); | ||||
|       __imp_GetConsoleMode(hand, __console_mode + i); | ||||
|       __imp_SetConsoleMode(hand, kConsoleModes[i]); | ||||
|     for (int i = 1; i <= 2; ++i) { | ||||
|       uint32_t m; | ||||
|       intptr_t h = __imp_GetStdHandle(kNtStdio[i]); | ||||
|       __imp_GetConsoleMode(h, &m); | ||||
|       __imp_SetConsoleMode(h, m | kNtEnableVirtualTerminalProcessing); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // allocate memory for stack and argument block
 | ||||
|   _Static_assert(sizeof(struct WinArgs) % FRAMESIZE == 0, ""); | ||||
|   _mmi.p = _mmi.s; | ||||
|   _mmi.n = ARRAYLEN(_mmi.s); | ||||
|   stackaddr = GetStaticStackAddr(0); | ||||
|   stacksize = GetStaticStackSize(); | ||||
|   uintptr_t stackaddr = GetStaticStackAddr(0); | ||||
|   size_t stacksize = GetStaticStackSize(); | ||||
|   __imp_MapViewOfFileEx((_mmi.p[0].h = __imp_CreateFileMappingW( | ||||
|                              -1, &kNtIsInheritable, kNtPageExecuteReadwrite, | ||||
|                              stacksize >> 32, stacksize, NULL)), | ||||
|  | @ -181,25 +170,47 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { | |||
|   _mmi.p[0].flags = 0x00000026;  // stack+anonymous
 | ||||
|   _mmi.p[0].size = stacksize; | ||||
|   _mmi.i = 1; | ||||
|   wa = (struct WinArgs *)(stackaddr + (stacksize - sizeof(struct WinArgs))); | ||||
|   struct WinArgs *wa = | ||||
|       (struct WinArgs *)(stackaddr + (stacksize - sizeof(struct WinArgs))); | ||||
| 
 | ||||
|   // parse utf-16 command into utf-8 argv array in argument block
 | ||||
|   int count = GetDosArgv(cmdline, wa->argblock, ARRAYLEN(wa->argblock), | ||||
|                          wa->argv, ARRAYLEN(wa->argv)); | ||||
|   for (int i = 0; wa->argv[0][i]; ++i) { | ||||
|     if (wa->argv[0][i] == '\\') { | ||||
|       wa->argv[0][i] = '/'; | ||||
| 
 | ||||
|   // munge argv so dos paths become cosmo paths
 | ||||
|   for (int i = 0; wa->argv[i]; ++i) { | ||||
|     if (wa->argv[i][0] == '\\' &&  //
 | ||||
|         wa->argv[i][1] == '\\') { | ||||
|       // don't munge new technology style paths
 | ||||
|       continue; | ||||
|     } | ||||
|     if (!WinFileExists(wa->argv[i])) { | ||||
|       // don't munge if we're not certain it's a file
 | ||||
|       continue; | ||||
|     } | ||||
|     // use forward slashes
 | ||||
|     for (int j = 0; wa->argv[i][j]; ++j) { | ||||
|       if (wa->argv[i][j] == '\\') { | ||||
|         wa->argv[i][j] = '/'; | ||||
|       } | ||||
|     } | ||||
|     // turn c:/... into /c/...
 | ||||
|     if (IsAlpha(wa->argv[i][0]) &&  //
 | ||||
|         wa->argv[i][1] == ':' &&    //
 | ||||
|         wa->argv[i][2] == '/') { | ||||
|       wa->argv[i][1] = wa->argv[i][0]; | ||||
|       wa->argv[i][0] = '/'; | ||||
|     } | ||||
|   } | ||||
|   if (IsAlpha(wa->argv[0][0]) &&  //
 | ||||
|       wa->argv[0][1] == ':' &&    //
 | ||||
|       wa->argv[0][2] == '/') { | ||||
|     wa->argv[0][1] = wa->argv[0][0]; | ||||
|     wa->argv[0][0] = '/'; | ||||
|   } | ||||
| 
 | ||||
|   // translate utf-16 win32 environment into utf-8 environment variables
 | ||||
|   char16_t *env16 = __imp_GetEnvironmentStringsW(); | ||||
|   GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp, | ||||
|                 ARRAYLEN(wa->envp) - 1); | ||||
|   __imp_FreeEnvironmentStringsW(env16); | ||||
|   __envp = &wa->envp[0]; | ||||
| 
 | ||||
|   // handover control to cosmopolitan runtime
 | ||||
|   _jmpstack((char *)(stackaddr + (stacksize - sizeof(struct WinArgs))), cosmo, | ||||
|             count, wa->argv, wa->envp, wa->auxv); | ||||
| } | ||||
|  | @ -227,7 +238,7 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, | |||
|   if (_weaken(WinMainForked)) { | ||||
|     _weaken(WinMainForked)(); | ||||
|   } | ||||
|   WinMainNew(cmdline); | ||||
|   WinInit(cmdline); | ||||
| } | ||||
| 
 | ||||
| #endif /* __x86_64__ */ | ||||
|  |  | |||
|  | @ -49,6 +49,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped, | |||
|     // our i/o operation never happened because it failed
 | ||||
|     return __winsockerr(); | ||||
|   } | ||||
| TryAgain: | ||||
|   // our i/o operation is in flight and it needs to block
 | ||||
|   abort_errno = EAGAIN; | ||||
|   if (fd->flags & O_NONBLOCK) { | ||||
|  | @ -87,10 +88,15 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped, | |||
|   // we wait for win32 to acknowledge that it's done using that memory.
 | ||||
|   if (WSAGetOverlappedResult(fd->handle, overlapped, &got, true, flags)) { | ||||
|     return got; | ||||
|   } else if (WSAGetLastError() == kNtErrorOperationAborted) { | ||||
|     errno = abort_errno; | ||||
|     return -1; | ||||
|   } else { | ||||
|     return -1; | ||||
|   } | ||||
|   switch (WSAGetLastError()) { | ||||
|     case kNtErrorIoIncomplete: | ||||
|       goto TryAgain; | ||||
|     case kNtErrorOperationAborted: | ||||
|       errno = abort_errno; | ||||
|       break; | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   return -1; | ||||
| } | ||||
|  |  | |||
|  | @ -94,6 +94,12 @@ o/$(MODE)/libc/fmt/strsignal.greg.o: private			\ | |||
| 			-ffreestanding				\
 | ||||
| 			$(NO_MAGIC) | ||||
| 
 | ||||
| # we can't use sanitizers because:
 | ||||
| #   WinMain calls this
 | ||||
| o/$(MODE)/libc/str/tprecode8to16.o: private			\ | ||||
| 		COPTS +=					\
 | ||||
| 			-fno-sanitize=all | ||||
| 
 | ||||
| o/$(MODE)/libc/str/eastasianwidth.bin:				\ | ||||
| 		libc/str/eastasianwidth.txt			\
 | ||||
| 		o/$(MODE)/tool/decode/mkwides.com | ||||
|  |  | |||
|  | @ -24,9 +24,9 @@ | |||
| #include "libc/str/thompike.h" | ||||
| #include "libc/str/utf16.h" | ||||
| 
 | ||||
| /* 34x speedup for ascii */ | ||||
| static inline dontasan axdx_t tprecode8to16_sse2(char16_t *dst, size_t dstsize, | ||||
|                                                  const char *src, axdx_t r) { | ||||
| // 34x speedup for ascii
 | ||||
| static inline axdx_t tprecode8to16_sse2(char16_t *dst, size_t dstsize, | ||||
|                                         const char *src, axdx_t r) { | ||||
|   uint8_t v1[16], v2[16], vz[16]; | ||||
|   memset(vz, 0, 16); | ||||
|   while (r.ax + 16 < dstsize) { | ||||
|  | @ -62,9 +62,11 @@ axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) { | |||
|   r.ax = 0; | ||||
|   r.dx = 0; | ||||
|   for (;;) { | ||||
| #ifdef __x86_64__ | ||||
|     if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { | ||||
|       r = tprecode8to16_sse2(dst, dstsize, src, r); | ||||
|     } | ||||
| #endif | ||||
|     x = src[r.dx++] & 0377; | ||||
|     if (x >= 0300) { | ||||
|       a = ThomPikeByte(x); | ||||
|  |  | |||
|  | @ -47,6 +47,8 @@ TEST(readlink, enoent) { | |||
| TEST(readlink, enotdir) { | ||||
|   char buf[32]; | ||||
|   ASSERT_SYS(0, 0, touch("o", 0644)); | ||||
|   ASSERT_SYS(ENOTDIR, -1, readlink("o/", buf, 32)); | ||||
|   ASSERT_SYS(ENOTDIR, -1, readlink("o/o/..", buf, 32)); | ||||
|   ASSERT_SYS(ENOTDIR, -1, readlink("o/doesnotexist", buf, 32)); | ||||
| } | ||||
| 
 | ||||
|  | @ -95,7 +97,7 @@ TEST(readlinkat, frootloop) { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| TEST(readlinkat, statReadsNameLength) { | ||||
| TEST(readlinkat, statReadsNameLength_countsUtf8Bytes) { | ||||
|   struct stat st; | ||||
|   ASSERT_SYS(0, 0, symlink("froÒt", "froÒt")); | ||||
|   ASSERT_SYS(0, 0, fstatat(AT_FDCWD, "froÒt", &st, AT_SYMLINK_NOFOLLOW)); | ||||
|  |  | |||
							
								
								
									
										75
									
								
								test/libc/mem/realpath_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								test/libc/mem/realpath_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| /*-*- 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/calls/calls.h" | ||||
| #include "libc/dce.h" | ||||
| #include "libc/errno.h" | ||||
| #include "libc/mem/gc.internal.h" | ||||
| #include "libc/mem/mem.h" | ||||
| #include "libc/runtime/runtime.h" | ||||
| #include "libc/str/str.h" | ||||
| #include "libc/testlib/testlib.h" | ||||
| 
 | ||||
| char testlib_enable_tmp_setup_teardown; | ||||
| 
 | ||||
| void SetUp(void) { | ||||
|   touch("conftest.a", 0644); | ||||
|   symlink("conftest.a", "conftest.l"); | ||||
|   mkdir("conftest.d", 0755); | ||||
| } | ||||
| 
 | ||||
| TEST(realpath, test1) { | ||||
|   char *name = gc(realpath("conftest.a", NULL)); | ||||
|   ASSERT_TRUE(name && *name == '/'); | ||||
| } | ||||
| 
 | ||||
| TEST(realpath, test2) { | ||||
|   ASSERT_EQ(NULL, gc(realpath("conftest.b/../conftest.a", NULL))); | ||||
| } | ||||
| 
 | ||||
| TEST(realpath, test3) { | ||||
|   char *name = gc(realpath("conftest.l/../conftest.a", NULL)); | ||||
|   if (IsWindows()) { | ||||
|     // WIN32 acts as a flat namespace, rather than linear inode crawl.
 | ||||
|     // GNU ./configure scripts consider this outcome to be acceptable.
 | ||||
|     ASSERT_NE(NULL, name); | ||||
|   } else { | ||||
|     // Every other OS FS is a UNIX inode crawl.
 | ||||
|     ASSERT_SYS(ENOTDIR, NULL, name); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| TEST(realpath, test4) { | ||||
|   ASSERT_SYS(ENOTDIR, NULL, gc(realpath("conftest.a/", NULL))); | ||||
| } | ||||
| 
 | ||||
| TEST(realpath, test5) { | ||||
|   char *name1 = gc(realpath(".", NULL)); | ||||
|   char *name2 = gc(realpath("conftest.d//./..", NULL)); | ||||
|   ASSERT_NE(NULL, name1); | ||||
|   ASSERT_NE(NULL, name2); | ||||
|   ASSERT_STREQ(name1, name2); | ||||
| } | ||||
| 
 | ||||
| TEST(realpath, test6) { | ||||
|   // musl libc fails this test
 | ||||
|   // comment say // is special but doesn't say why :/
 | ||||
|   char *name = gc(realpath("//", NULL)); | ||||
|   ASSERT_NE(NULL, name); | ||||
|   EXPECT_STREQ("/", name); | ||||
| } | ||||
							
								
								
									
										1
									
								
								third_party/finger/finger.c
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								third_party/finger/finger.c
									
										
									
									
										vendored
									
									
								
							|  | @ -45,6 +45,7 @@ | |||
| #include "libc/runtime/utmp.h" | ||||
| #include "libc/sock/sock.h" | ||||
| #include "libc/sock/struct/sockaddr.h" | ||||
| #include "libc/stdio/stdio.h" | ||||
| #include "libc/str/str.h" | ||||
| #include "libc/sysv/consts/fileno.h" | ||||
| #include "libc/time/time.h" | ||||
|  |  | |||
|  | @ -1458,9 +1458,10 @@ static char *SecondPass2(char *p, struct Input *in) { | |||
|     // the new file size. that's only possible if all the fat ape hdrs
 | ||||
|     // we generate are able to fit inside the prologue.
 | ||||
|     p = ALIGN(p, 8); | ||||
|     // TODO(jart): Figure out why not skewing corrupts pe import table
 | ||||
|     in->we_must_skew_pe_vaspace = | ||||
|         ROUNDUP(p - prologue + in->size_of_pe_headers, | ||||
|                 (int)in->pe->OptionalHeader.FileAlignment) > in->minload; | ||||
|         1 || ROUNDUP(p - prologue + in->size_of_pe_headers, | ||||
|                      (int)in->pe->OptionalHeader.FileAlignment) > in->minload; | ||||
|     if (!in->we_must_skew_pe_vaspace) { | ||||
|       in->pe_e_lfanew = p - prologue; | ||||
|       in->pe_SizeOfHeaders = in->pe->OptionalHeader.SizeOfHeaders; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue