1 #include "rc.h" 2 #include <windows.h> 3 4 enum { 5 Nchild = 100, 6 }; 7 8 typedef struct Child Child; 9 10 struct Child { 11 int pid; 12 HANDLE handle; 13 }; 14 15 static Child child[Nchild]; 16 17 static void 18 winerror(void) 19 { 20 int e, r; 21 char buf[100], *p, *q; 22 23 e = GetLastError(); 24 25 r = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 26 0, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 27 buf, sizeof(buf), 0); 28 29 if(r == 0) 30 snprint(buf, sizeof(buf), "windows error %d", e); 31 32 for(p=q=buf; *p; p++) { 33 if(*p == '\r') 34 continue; 35 if(*p == '\n') 36 *q++ = ' '; 37 else 38 *q++ = *p; 39 } 40 *q = 0; 41 errstr(buf, sizeof buf); 42 } 43 44 static int 45 badentry(char *filename) 46 { 47 if(*filename == 0) 48 return 1; 49 if(filename[0] == '.'){ 50 if(filename[1] == 0) 51 return 1; 52 if(filename[1] == '.' && filename[2] == 0) 53 return 1; 54 } 55 return 0; 56 } 57 58 Direntry* 59 readdirect(char *path) 60 { 61 long n; 62 HANDLE h; 63 Direntry *d; 64 char fullpath[MAX_PATH]; 65 WIN32_FIND_DATA data; 66 67 snprint(fullpath, MAX_PATH, "%s\\*.*", path); 68 69 h = FindFirstFile(fullpath, &data); 70 if(h == INVALID_HANDLE_VALUE) 71 return 0; 72 73 n = 0; 74 d = 0; 75 for(;;){ 76 if(!badentry(data.cFileName)){ 77 d = realloc(d, (n+2)*sizeof(Direntry)); 78 if(d == 0){ 79 werrstr("memory allocation"); 80 return 0; 81 } 82 d[n].name = malloc(strlen(data.cFileName)+1); 83 if(d[n].name == 0){ 84 werrstr("memory allocation"); 85 return 0; 86 } 87 strcpy(d[n].name, data.cFileName); 88 if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 89 d[n].isdir = 1; 90 else 91 d[n].isdir = 0; 92 n++; 93 } 94 if(FindNextFile(h, &data) == 0) 95 break; 96 } 97 FindClose(h); 98 if(d){ 99 d[n].name = 0; 100 d[n].isdir = 0; 101 } 102 return d; 103 } 104 105 void 106 fatal(char *fmt, ...) 107 { 108 char buf[512]; 109 va_list arg; 110 111 va_start(arg, fmt); 112 vseprint(buf, buf+sizeof(buf), fmt, arg); 113 va_end(arg); 114 115 fprint(2, "rc: %s\n", buf); 116 _exits(buf); 117 } 118 119 static int 120 tas(int *p) 121 { 122 int v; 123 124 _asm { 125 mov eax, p 126 mov ebx, 1 127 xchg ebx, [eax] 128 mov v, ebx 129 } 130 131 return v; 132 } 133 134 static void 135 lock(Lock *lk) 136 { 137 int i; 138 139 /* easy case */ 140 if(!tas(&lk->val)) 141 return; 142 143 /* for muli processor machines */ 144 for(i=0; i<100; i++) 145 if(!tas(&lk->val)) 146 return; 147 148 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); 149 for(;;) { 150 for(i=0; i<10000; i++) { 151 Sleep(0); 152 if(!tas(&lk->val)) { 153 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); 154 return; 155 } 156 } 157 } 158 } 159 160 static void 161 unlock(Lock *lk) 162 { 163 lk->val = 0; 164 } 165 166 int 167 refinc(Ref *r) 168 { 169 int i; 170 171 lock(&r->lk); 172 i = r->ref; 173 r->ref++; 174 unlock(&r->lk); 175 return i; 176 } 177 178 int 179 refdec(Ref *r) 180 { 181 int i; 182 183 lock(&r->lk); 184 r->ref--; 185 i = r->ref; 186 unlock(&r->lk); 187 188 return i; 189 } 190 191 /* 192 * windows quoting rules - I think 193 * Words are seperated by space or tab 194 * Words containing a space or tab can be quoted using " 195 * 2N backslashes + " ==> N backslashes and end quote 196 * 2N+1 backslashes + " ==> N backslashes + literal " 197 * N backslashes not followed by " ==> N backslashes 198 */ 199 static char * 200 dblquote(char *cmd, char *s) 201 { 202 int nb; 203 char *p; 204 205 for(p=s; *p; p++) 206 if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"') 207 break; 208 209 if(*p == 0){ /* easy case */ 210 strcpy(cmd, s); 211 return cmd+(p-s); 212 } 213 214 *cmd++ = '"'; 215 for(;;) { 216 for(nb=0; *s=='\\'; nb++) 217 *cmd++ = *s++; 218 219 if(*s == 0) { /* trailing backslashes -> 2N */ 220 while(nb-- > 0) 221 *cmd++ = '\\'; 222 break; 223 } 224 225 if(*s == '"') { /* literal quote -> 2N+1 backslashes */ 226 while(nb-- > 0) 227 *cmd++ = '\\'; 228 *cmd++ = '\\'; /* escape the quote */ 229 } 230 *cmd++ = *s++; 231 } 232 233 *cmd++ = '"'; 234 *cmd = 0; 235 236 return cmd; 237 } 238 239 static char * 240 proccmd(char **argv) 241 { 242 int i, n; 243 char *cmd, *p; 244 245 /* conservatively calculate length of command; 246 * backslash expansion can cause growth in dblquote(). 247 */ 248 for(i=0,n=0; argv[i]; i++) { 249 n += 2*strlen(argv[i]); 250 } 251 n++; 252 253 cmd = malloc(n); 254 for(i=0,p=cmd; argv[i]; i++) { 255 p = dblquote(p, argv[i]); 256 *p++ = ' '; 257 } 258 if(p != cmd) 259 p--; 260 *p = 0; 261 262 return cmd; 263 } 264 265 static char * 266 exportenv(char **e) 267 { 268 int i, j, n; 269 char *buf; 270 271 if(e == 0 || *e == 0) 272 return 0; 273 274 buf = 0; 275 n = 0; 276 for(i = 0; *e; e++, i++) { 277 j = strlen(*e)+1; 278 buf = realloc(buf, n+j); 279 strcpy(buf+n, *e); 280 n += j; 281 } 282 /* final null */ 283 buf = realloc(buf, n+1); 284 buf[n] = 0; 285 286 return buf; 287 } 288 289 static int 290 setpath(char *path, char *file) 291 { 292 char *p, *last, tmp[MAX_PATH+1]; 293 int n; 294 295 if(strlen(file) >= MAX_PATH){ 296 werrstr("file name too long"); 297 return -1; 298 } 299 strcpy(tmp, file); 300 301 for(p=tmp; *p; p++) { 302 if(*p == '/') 303 *p = '\\'; 304 } 305 306 if(tmp[0] != 0 && tmp[1] == ':') { 307 if(tmp[2] == 0) { 308 tmp[2] = '\\'; 309 tmp[3] = 0; 310 } else if(tmp[2] != '\\') { 311 /* don't allow c:foo - only c:\foo */ 312 werrstr("illegal file name"); 313 return -1; 314 } 315 } 316 317 path[0] = 0; 318 n = GetFullPathName(tmp, MAX_PATH, path, &last); 319 if(n >= MAX_PATH) { 320 werrstr("file name too long"); 321 return -1; 322 } 323 if(n == 0 && tmp[0] == '\\' && tmp[1] == '\\' && tmp[2] != 0) { 324 strcpy(path, tmp); 325 return -1; 326 } 327 328 if(n == 0) { 329 werrstr("bad file name"); 330 return -1; 331 } 332 333 for(p=path; *p; p++) { 334 if(*p < 32 || *p == '*' || *p == '?') { 335 werrstr("file not found"); 336 return -1; 337 } 338 } 339 340 /* get rid of trailling \ */ 341 if(path[n-1] == '\\') { 342 if(n <= 2) { 343 werrstr("illegal file name"); 344 return -1; 345 } 346 path[n-1] = 0; 347 n--; 348 } 349 350 if(path[1] == ':' && path[2] == 0) { 351 path[2] = '\\'; 352 path[3] = '.'; 353 path[4] = 0; 354 return -1; 355 } 356 357 if(path[0] != '\\' || path[1] != '\\') 358 return 0; 359 360 for(p=path+2,n=0; *p; p++) 361 if(*p == '\\') 362 n++; 363 if(n == 0) 364 return -1; 365 if(n == 1) 366 return -1; 367 return 0; 368 } 369 370 371 static int 372 execpath(char *path, char *file) 373 { 374 int n; 375 376 if(setpath(path, file) < 0) 377 return 0; 378 379 n = strlen(path)-4; 380 if(path[n] == '.') { 381 if(GetFileAttributes(path) != -1) 382 return 1; 383 } 384 strncat(path, ".exe", MAX_PATH); 385 path[MAX_PATH-1] = 0; 386 if(GetFileAttributes(path) != -1) 387 return 1; 388 return 0; 389 } 390 391 static HANDLE 392 fdexport(int fd) 393 { 394 HANDLE h, r; 395 396 if(fd < 0) 397 return INVALID_HANDLE_VALUE; 398 399 h = (HANDLE)_get_osfhandle(fd); 400 if(h < 0) 401 return INVALID_HANDLE_VALUE; 402 403 if(!DuplicateHandle(GetCurrentProcess(), h, 404 GetCurrentProcess(), &r, DUPLICATE_SAME_ACCESS, 405 1, DUPLICATE_SAME_ACCESS)) 406 return INVALID_HANDLE_VALUE; 407 return r; 408 } 409 410 static int 411 addchild(int pid, HANDLE handle) 412 { 413 int i; 414 415 for(i=0; i<Nchild; i++) { 416 if(child[i].handle == 0) { 417 child[i].handle = handle; 418 child[i].pid = pid; 419 return 1; 420 } 421 } 422 werrstr("child table full"); 423 return 0; 424 } 425 426 int 427 procwait(uint pid) 428 { 429 HANDLE h; 430 int i, exit; 431 432 if(pid == 0) 433 return 0; 434 435 h = 0; 436 for(i = 0; i < Nchild; i++){ 437 if(child[i].pid == pid){ 438 h = child[i].handle; 439 child[i].pid = 0; 440 child[i].handle = 0; 441 break; 442 } 443 } 444 445 if(h == 0){ /* we don't know about this one - let the system try to find it */ 446 h = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); 447 if(h == 0) 448 return 0; /* can't find it */ 449 } 450 451 if(WaitForSingleObject(h, INFINITE) == WAIT_FAILED) { 452 winerror(); 453 fatal("procwait: "); 454 } 455 456 if(!GetExitCodeProcess(h, &exit)) { 457 winerror(); 458 exit = 1; 459 } 460 461 CloseHandle(h); 462 return exit; 463 } 464 465 uint 466 proc(char **argv, int stdin, int stdout, int stderr) 467 { 468 char *p, *arg0, *q, buf[MAX_PATH], path[MAX_PATH], *cmd, *eb; 469 STARTUPINFO si; 470 PROCESS_INFORMATION pi; 471 int r, found, full; 472 extern char **_environ; 473 Word *w; 474 475 arg0 = argv[0]; 476 if(arg0 == 0) { 477 werrstr("null argv[0]"); 478 return 0; 479 } 480 481 full = arg0[0] == '\\' || arg0[0] == '/' || arg0[0] == '.'; 482 found = execpath(path, arg0); 483 484 if(!found && !full) { 485 w = vlook("path")->val; 486 if(w) 487 p = w->word; 488 else 489 p = getenv("path"); 490 for(; p && *p; p = q){ 491 q = strchr(p, ';'); 492 if(q) 493 *q = 0; 494 snprint(buf, sizeof(buf), "%s/%s", p, arg0); 495 if(q) 496 *q++ = ';'; 497 found = execpath(path, buf); 498 if(found) 499 break; 500 } 501 } 502 503 if(!found) { 504 werrstr("file not found"); 505 return 0; 506 } 507 508 memset(&si, 0, sizeof(si)); 509 si.cb = sizeof(si); 510 si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; 511 si.wShowWindow = SW_SHOW; 512 si.hStdInput = fdexport(stdin); 513 si.hStdOutput = fdexport(stdout); 514 si.hStdError = fdexport(stderr); 515 516 eb = exportenv(_environ); 517 518 cmd = proccmd(argv); 519 520 r = CreateProcess(path, cmd, 0, 0, 1, 0, eb, 0, &si, &pi); 521 522 /* allow child to run */ 523 Sleep(0); 524 525 free(cmd); 526 free(eb); 527 528 CloseHandle(si.hStdInput); 529 CloseHandle(si.hStdOutput); 530 CloseHandle(si.hStdError); 531 532 if(!r) { 533 winerror(); 534 return 0; 535 } 536 537 CloseHandle(pi.hThread); 538 539 if(addchild(pi.dwProcessId, pi.hProcess) == 0) 540 return 0; 541 542 return pi.dwProcessId; 543 } 544 545 int 546 pipe(int *fd) 547 { 548 return _pipe(fd, 0, _O_BINARY); 549 } 550