|
|
1.1 ! root 1: /* ! 2: * UAE - The Un*x Amiga Emulator ! 3: * ! 4: * Win32 interface ! 5: * ! 6: * Copyright 1997 Mathias Ortmann ! 7: */ ! 8: ! 9: #ifdef __GNUC__ ! 10: #define __int64 long long ! 11: #include "machdep/winstuff.h" ! 12: #else ! 13: #include <windows.h> ! 14: #include <ddraw.h> ! 15: #include <stdlib.h> ! 16: #include <stdarg.h> ! 17: #include <commctrl.h> ! 18: #include <commdlg.h> ! 19: #include <stdio.h> ! 20: #include <fcntl.h> ! 21: #include <sys/stat.h> ! 22: #include <io.h> ! 23: #endif ! 24: ! 25: #include "config.h" ! 26: #include "sysconfig.h" ! 27: #include "sysdeps.h" ! 28: #include "options.h" ! 29: #include "memory.h" ! 30: #include "osdep/win32.h" ! 31: ! 32: /* stdioemu, posixemu, mallocemu, and various file system helper routines */ ! 33: static DWORD lasterror; ! 34: ! 35: static int isillegal (unsigned char *str) ! 36: { ! 37: unsigned char a = *str, b = str[1], c = str[2]; ! 38: ! 39: if (a >= 'a' && a <= 'z') ! 40: a &= ~' '; ! 41: if (b >= 'a' && b <= 'z') ! 42: b &= ~' '; ! 43: if (c >= 'a' && c <= 'z') ! 44: c &= ~' '; ! 45: ! 46: return (a == 'A' && b == 'U' && c == 'X' || ! 47: a == 'C' && b == 'O' && c == 'N' || ! 48: a == 'P' && b == 'R' && c == 'N' || ! 49: a == 'N' && b == 'U' && c == 'L'); ! 50: } ! 51: ! 52: static int checkspace (char *str, char s, char d) ! 53: { ! 54: char *ptr = str; ! 55: ! 56: while (*ptr && *ptr == s) ! 57: ptr++; ! 58: ! 59: if (!*ptr || *ptr == '/' || *ptr == '\\') { ! 60: while (str < ptr) ! 61: *(str++) = d; ! 62: return 0; ! 63: } ! 64: return 1; ! 65: } ! 66: ! 67: /* This is sick and incomplete... in the meantime, I have discovered six new illegal file name formats ! 68: * M$ sucks! */ ! 69: void fname_atow (char *src, char *dst, int size) ! 70: { ! 71: char *lastslash = dst, *strt = dst; ! 72: int i, j; ! 73: ! 74: while (size-- > 0) { ! 75: if (!(*dst = *src++)) ! 76: break; ! 77: ! 78: if (*dst == '~' || *dst == '|' || *dst == '*' || *dst == '?') { ! 79: if (size > 2) { ! 80: sprintf (dst, "~%02x", *dst); ! 81: size -= 2; ! 82: dst += 2; ! 83: } ! 84: } else if (*dst == '/') { ! 85: if (checkspace (lastslash, ' ', 0xa0) && (dst - lastslash == 3 || (dst - lastslash > 3 && lastslash[3] == '.')) && isillegal (lastslash)) { ! 86: i = dst - lastslash - 3; ! 87: dst++; ! 88: for (j = i + 1; j--; dst--) ! 89: *dst = dst[-1]; ! 90: *(dst++) = 0xa0; ! 91: dst += i; ! 92: size--; ! 93: } else if (*lastslash == '.' && (dst - lastslash == 1 || lastslash[1] == '.' && dst - lastslash == 2) && size) { ! 94: *(dst++) = 0xa0; ! 95: size--; ! 96: } ! 97: *dst = '\\'; ! 98: lastslash = dst + 1; ! 99: } ! 100: dst++; ! 101: } ! 102: ! 103: if (checkspace (lastslash, ' ', 0xa0) && (dst - lastslash == 3 || (dst - lastslash > 3 && lastslash[3] == '.')) && isillegal (lastslash) && size > 1) { ! 104: i = dst - lastslash - 3; ! 105: dst++; ! 106: for (j = i + 1; j--; dst--) ! 107: *dst = dst[-1]; ! 108: *(dst++) = 0xa0; ! 109: } else if (!strcmp (lastslash, ".") || !strcmp (lastslash, "..")) ! 110: strcat (lastslash, "\xa0"); ! 111: } ! 112: ! 113: int getdiskfreespace (char *name, int *f_blocks, int *f_bavail, int *f_bsize) ! 114: { ! 115: char buf1[1024]; ! 116: char buf2[1024]; ! 117: DWORD SectorsPerCluster; ! 118: DWORD BytesPerSector; ! 119: DWORD NumberOfFreeClusters; ! 120: DWORD TotalNumberOfClusters; ! 121: ! 122: fname_atow (name, buf1, sizeof buf1); ! 123: ! 124: GetFullPathName (buf1, sizeof buf2, buf2, NULL); ! 125: ! 126: buf2[3] = 0; ! 127: ! 128: if (!GetDiskFreeSpace (buf2, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters)) { ! 129: lasterror = GetLastError (); ! 130: return FALSE; ! 131: } ! 132: *f_blocks = TotalNumberOfClusters; ! 133: *f_bavail = NumberOfFreeClusters; ! 134: *f_bsize = SectorsPerCluster * BytesPerSector; ! 135: ! 136: return TRUE; ! 137: } ! 138: ! 139: /* Translate lasterror to valid AmigaDOS error code ! 140: * The mapping is probably not 100% correct yet */ ! 141: long dos_errno (void) ! 142: { ! 143: int i; ! 144: ! 145: static DWORD errtbl[][2] = ! 146: { ! 147: {ERROR_FILE_NOT_FOUND, 205}, /* 2 */ ! 148: {ERROR_PATH_NOT_FOUND, 205}, /* 3 */ ! 149: {ERROR_SHARING_VIOLATION, 202}, ! 150: {ERROR_ACCESS_DENIED, 223}, /* 5 */ ! 151: {ERROR_ARENA_TRASHED, 103}, /* 7 */ ! 152: {ERROR_NOT_ENOUGH_MEMORY, 103}, /* 8 */ ! 153: {ERROR_INVALID_BLOCK, 219}, /* 9 */ ! 154: {ERROR_INVALID_DRIVE, 204}, /* 15 */ ! 155: {ERROR_CURRENT_DIRECTORY, 214}, /* 16 */ ! 156: {ERROR_NO_MORE_FILES, 232}, /* 18 */ ! 157: {ERROR_LOCK_VIOLATION, 214}, /* 33 */ ! 158: {ERROR_BAD_NETPATH, 204}, /* 53 */ ! 159: {ERROR_NETWORK_ACCESS_DENIED, 214}, /* 65 */ ! 160: {ERROR_BAD_NET_NAME, 204}, /* 67 */ ! 161: {ERROR_FILE_EXISTS, 203}, /* 80 */ ! 162: {ERROR_CANNOT_MAKE, 214}, /* 82 */ ! 163: {ERROR_FAIL_I24, 223}, /* 83 */ ! 164: {ERROR_DRIVE_LOCKED, 202}, /* 108 */ ! 165: {ERROR_DISK_FULL, 221}, /* 112 */ ! 166: {ERROR_NEGATIVE_SEEK, 219}, /* 131 */ ! 167: {ERROR_SEEK_ON_DEVICE, 219}, /* 132 */ ! 168: {ERROR_DIR_NOT_EMPTY, 216}, /* 145 */ ! 169: {ERROR_ALREADY_EXISTS, 203}, /* 183 */ ! 170: {ERROR_FILENAME_EXCED_RANGE, 205}, /* 206 */ ! 171: {ERROR_NOT_ENOUGH_QUOTA, 221}, /* 1816 */ ! 172: {ERROR_DIRECTORY, 212}}; ! 173: ! 174: for (i = sizeof (errtbl) / sizeof (errtbl[0]); i--;) { ! 175: if (errtbl[i][0] == lasterror) ! 176: return errtbl[i][1]; ! 177: } ! 178: return 236; ! 179: } ! 180: ! 181: static DWORD getattr (char *name, LPFILETIME lpft, size_t * size) ! 182: { ! 183: HANDLE hFind; ! 184: WIN32_FIND_DATA fd; ! 185: ! 186: if ((hFind = FindFirstFile (name, &fd)) == INVALID_HANDLE_VALUE) { ! 187: lasterror = GetLastError (); ! 188: ! 189: fd.dwFileAttributes = GetFileAttributes (name); ! 190: ! 191: return fd.dwFileAttributes; ! 192: } ! 193: FindClose (hFind); ! 194: ! 195: if (lpft) ! 196: *lpft = fd.ftLastWriteTime; ! 197: if (size) ! 198: *size = fd.nFileSizeLow; ! 199: ! 200: return fd.dwFileAttributes; ! 201: } ! 202: ! 203: int posixemu_access (char *name, int mode) ! 204: { ! 205: DWORD attr; ! 206: char buf[1024]; ! 207: ! 208: fname_atow (name, buf, sizeof buf - 4); ! 209: ! 210: if ((attr = getattr (buf, NULL, NULL)) == ~0) ! 211: return -1; ! 212: ! 213: if (attr & FILE_ATTRIBUTE_READONLY && (mode & 4)) { ! 214: lasterror = ERROR_ACCESS_DENIED; ! 215: return -1; ! 216: } else ! 217: return 0; ! 218: } ! 219: ! 220: int posixemu_open (char *name, int oflag, int dummy) ! 221: { ! 222: DWORD fileaccess; ! 223: DWORD filecreate; ! 224: char buf[1024]; ! 225: ! 226: HANDLE hFile; ! 227: ! 228: switch (oflag & (O_RDONLY | O_WRONLY | O_RDWR)) { ! 229: case O_RDONLY: ! 230: fileaccess = GENERIC_READ; ! 231: break; ! 232: case O_WRONLY: ! 233: fileaccess = GENERIC_WRITE; ! 234: break; ! 235: case O_RDWR: ! 236: fileaccess = GENERIC_READ | GENERIC_WRITE; ! 237: break; ! 238: default: ! 239: return -1; ! 240: } ! 241: ! 242: switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) { ! 243: case 0: ! 244: case O_EXCL: ! 245: filecreate = OPEN_EXISTING; ! 246: break; ! 247: case O_CREAT: ! 248: filecreate = OPEN_ALWAYS; ! 249: break; ! 250: case O_CREAT | O_EXCL: ! 251: case O_CREAT | O_TRUNC | O_EXCL: ! 252: filecreate = CREATE_NEW; ! 253: break; ! 254: case O_TRUNC: ! 255: case O_TRUNC | O_EXCL: ! 256: filecreate = TRUNCATE_EXISTING; ! 257: break; ! 258: case O_CREAT | O_TRUNC: ! 259: filecreate = CREATE_ALWAYS; ! 260: break; ! 261: } ! 262: ! 263: fname_atow (name, buf, sizeof buf); ! 264: ! 265: if ((hFile = CreateFile (buf, ! 266: fileaccess, ! 267: FILE_SHARE_READ | FILE_SHARE_WRITE, ! 268: NULL, ! 269: filecreate, ! 270: FILE_ATTRIBUTE_NORMAL, ! 271: NULL)) == INVALID_HANDLE_VALUE) ! 272: lasterror = GetLastError (); ! 273: ! 274: return (int) hFile; ! 275: } ! 276: ! 277: void posixemu_close (int hFile) ! 278: { ! 279: CloseHandle ((HANDLE) hFile); ! 280: } ! 281: ! 282: int w32fopendel (char *name, char *mode, int delflag) ! 283: { ! 284: HANDLE hFile; ! 285: hFile = CreateFile (name, ! 286: mode[1] == '+' ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ, ! 287: FILE_SHARE_READ | FILE_SHARE_WRITE, ! 288: NULL, ! 289: OPEN_EXISTING, ! 290: delflag ? FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE : FILE_ATTRIBUTE_NORMAL, ! 291: NULL); ! 292: ! 293: if (hFile == INVALID_HANDLE_VALUE) { ! 294: lasterror = GetLastError (); ! 295: hFile = 0; ! 296: } ! 297: return (int) hFile; ! 298: } ! 299: ! 300: int stdioemu_fclose (FILE * fd) ! 301: { ! 302: if (fd) ! 303: CloseHandle ((HANDLE) fd); ! 304: return 0; ! 305: } ! 306: ! 307: void *mallocemu_malloc (int size) ! 308: { ! 309: return GlobalAlloc (GPTR, size); ! 310: } ! 311: ! 312: void mallocemu_free (void *ptr) ! 313: { ! 314: GlobalFree (ptr); ! 315: } ! 316: ! 317: static int hextol (char a) ! 318: { ! 319: if (a >= '0' && a <= '9') ! 320: return a - '0'; ! 321: if (a >= 'a' && a <= 'f') ! 322: return a - 'a' + 10; ! 323: if (a >= 'A' && a <= 'F') ! 324: return a - 'A' + 10; ! 325: return 2; ! 326: } ! 327: ! 328: /* Win32 file name restrictions suck... */ ! 329: void fname_wtoa (unsigned char *ptr) ! 330: { ! 331: unsigned char *lastslash = ptr; ! 332: ! 333: while (*ptr) { ! 334: if (*ptr == '~') { ! 335: *ptr = hextol (ptr[1]) * 16 + hextol (ptr[2]); ! 336: strcpy (ptr + 1, ptr + 3); ! 337: } else if (*ptr == '\\') { ! 338: if (checkspace (lastslash, ' ', 0xa0) && ptr - lastslash > 3 && lastslash[3] == 0xa0 && isillegal (lastslash)) { ! 339: ptr--; ! 340: strcpy (lastslash + 3, lastslash + 4); ! 341: } ! 342: *ptr = '/'; ! 343: lastslash = ptr + 1; ! 344: } ! 345: ptr++; ! 346: } ! 347: ! 348: if (checkspace (lastslash, ' ', 0xa0) && ptr - lastslash > 3 && lastslash[3] == 0xa0 && isillegal (lastslash)) ! 349: strcpy (lastslash + 3, lastslash + 4); ! 350: } ! 351: ! 352: DIR { ! 353: WIN32_FIND_DATA finddata; ! 354: HANDLE hDir; ! 355: int getnext; ! 356: }; ! 357: ! 358: DIR *opendir (char *path) ! 359: { ! 360: char buf[1024]; ! 361: DIR *dir; ! 362: ! 363: if (!(dir = (DIR *) GlobalAlloc (GPTR, sizeof (DIR)))) { ! 364: lasterror = GetLastError (); ! 365: return 0; ! 366: } ! 367: fname_atow (path, buf, sizeof buf - 4); ! 368: strcat (buf, "\\*"); ! 369: ! 370: if ((dir->hDir = FindFirstFile (buf, &dir->finddata)) == INVALID_HANDLE_VALUE) { ! 371: lasterror = GetLastError (); ! 372: GlobalFree (dir); ! 373: return 0; ! 374: } ! 375: return dir; ! 376: } ! 377: ! 378: struct dirent *readdir (DIR * dir) ! 379: { ! 380: if (dir->getnext) { ! 381: if (!FindNextFile (dir->hDir, &dir->finddata)) { ! 382: lasterror = GetLastError (); ! 383: return 0; ! 384: } ! 385: } ! 386: dir->getnext = TRUE; ! 387: ! 388: fname_wtoa (dir->finddata.cFileName); ! 389: return (struct dirent *) dir->finddata.cFileName; ! 390: } ! 391: ! 392: void closedir (DIR * dir) ! 393: { ! 394: FindClose (dir->hDir); ! 395: GlobalFree (dir); ! 396: } ! 397: ! 398: int setfiletime (char *name, unsigned int days, int minute, int tick) ! 399: { ! 400: FILETIME LocalFileTime, FileTime; ! 401: HANDLE hFile; ! 402: int success; ! 403: char buf[1024]; ! 404: ! 405: fname_atow (name, buf, sizeof buf); ! 406: ! 407: if ((hFile = CreateFile (buf, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { ! 408: lasterror = GetLastError (); ! 409: return 0; ! 410: } ! 411: *(__int64 *) & LocalFileTime = (((__int64) (377 * 365 + 91 + days) * (__int64) 1440 + (__int64) minute) * (__int64) (60 * 50) + (__int64) tick) * (__int64) 200000; ! 412: ! 413: if (!LocalFileTimeToFileTime (&LocalFileTime, &FileTime)) ! 414: FileTime = LocalFileTime; ! 415: ! 416: if (!(success = SetFileTime (hFile, &FileTime, &FileTime, &FileTime))) ! 417: lasterror = GetLastError (); ! 418: CloseHandle (hFile); ! 419: ! 420: return success; ! 421: } ! 422: ! 423: int posixemu_read (int hFile, char *ptr, int size) ! 424: { ! 425: DWORD actual; ! 426: ! 427: if (!ReadFile ((HANDLE) hFile, ptr, size, &actual, NULL)) ! 428: lasterror = GetLastError (); ! 429: ! 430: return actual; ! 431: } ! 432: ! 433: int posixemu_write (int hFile, char *ptr, int size) ! 434: { ! 435: DWORD actual; ! 436: ! 437: if (!WriteFile ((HANDLE) hFile, ptr, size, &actual, NULL)) ! 438: lasterror = GetLastError (); ! 439: ! 440: return actual; ! 441: } ! 442: ! 443: int posixemu_seek (int hFile, int pos, int type) ! 444: { ! 445: int result; ! 446: ! 447: switch (type) { ! 448: case SEEK_SET: ! 449: type = FILE_BEGIN; ! 450: break; ! 451: case SEEK_CUR: ! 452: type = FILE_CURRENT; ! 453: break; ! 454: case SEEK_END: ! 455: type = FILE_END; ! 456: } ! 457: ! 458: if ((result = SetFilePointer ((HANDLE) hFile, pos, NULL, type)) == ~0) ! 459: lasterror = GetLastError (); ! 460: ! 461: return result; ! 462: } ! 463: ! 464: int stdioemu_fread (char *buf, int l1, int l2, FILE * hFile) ! 465: { ! 466: return posixemu_read ((int) hFile, buf, l1 * l2); ! 467: } ! 468: ! 469: int stdioemu_fwrite (char *buf, int l1, int l2, FILE * hFile) ! 470: { ! 471: return posixemu_write ((int) hFile, buf, l1 * l2); ! 472: } ! 473: ! 474: int stdioemu_fseek (FILE * hFile, int pos, int type) ! 475: { ! 476: return posixemu_seek ((int) hFile, pos, type); ! 477: } ! 478: ! 479: int stdioemu_ftell (FILE * hFile) ! 480: { ! 481: return SetFilePointer ((HANDLE) hFile, 0, 0, FILE_CURRENT); ! 482: } ! 483: ! 484: int posixemu_stat (char *name, struct stat *statbuf) ! 485: { ! 486: char buf[1024]; ! 487: DWORD attr; ! 488: HANDLE hFile; ! 489: FILETIME ft, lft; ! 490: ! 491: fname_atow (name, buf, sizeof buf); ! 492: ! 493: if ((attr = getattr (buf, &ft, &statbuf->st_size)) == ~0) { ! 494: lasterror = GetLastError (); ! 495: return -1; ! 496: } else { ! 497: statbuf->st_mode = (attr & FILE_ATTRIBUTE_READONLY) ? 5 : 0; ! 498: if (attr & FILE_ATTRIBUTE_ARCHIVE) ! 499: statbuf->st_mode |= 0x10; ! 500: if (attr & FILE_ATTRIBUTE_DIRECTORY) ! 501: statbuf->st_mode |= 0x100; ! 502: ! 503: FileTimeToLocalFileTime (&ft, &lft); ! 504: statbuf->st_mtime = (*(__int64 *) & lft - ((__int64) (369 * 365 + 89) * (__int64) (24 * 60 * 60) * (__int64) 10000000)) / (__int64) 10000000; ! 505: } ! 506: ! 507: return 0; ! 508: } ! 509: ! 510: int posixemu_mkdir (char *name, int mode) ! 511: { ! 512: char buf[1024]; ! 513: ! 514: fname_atow (name, buf, sizeof buf); ! 515: ! 516: if (CreateDirectory (buf, NULL)) ! 517: return 0; ! 518: ! 519: lasterror = GetLastError (); ! 520: ! 521: return -1; ! 522: } ! 523: ! 524: int posixemu_unlink (char *name) ! 525: { ! 526: char buf[1024]; ! 527: ! 528: fname_atow (name, buf, sizeof buf); ! 529: if (DeleteFile (buf)) ! 530: return 0; ! 531: ! 532: lasterror = GetLastError (); ! 533: ! 534: return -1; ! 535: } ! 536: ! 537: int posixemu_rmdir (char *name) ! 538: { ! 539: char buf[1024]; ! 540: ! 541: fname_atow (name, buf, sizeof buf); ! 542: if (RemoveDirectory (buf)) ! 543: return 0; ! 544: ! 545: lasterror = GetLastError (); ! 546: ! 547: return -1; ! 548: } ! 549: ! 550: int posixemu_rename (char *name1, char *name2) ! 551: { ! 552: char buf1[1024]; ! 553: char buf2[1024]; ! 554: ! 555: fname_atow (name1, buf1, sizeof buf1); ! 556: fname_atow (name2, buf2, sizeof buf2); ! 557: if (MoveFile (name1, name2)) ! 558: return 0; ! 559: ! 560: lasterror = GetLastError (); ! 561: ! 562: return -1; ! 563: } ! 564: ! 565: int posixemu_chmod (char *name, int mode) ! 566: { ! 567: char buf[1024]; ! 568: DWORD attr = FILE_ATTRIBUTE_NORMAL; ! 569: ! 570: fname_atow (name, buf, sizeof buf); ! 571: ! 572: if (mode & 1) ! 573: attr |= FILE_ATTRIBUTE_READONLY; ! 574: if (mode & 0x10) ! 575: attr |= FILE_ATTRIBUTE_ARCHIVE; ! 576: ! 577: if (SetFileAttributes (buf, attr)) ! 578: return 1; ! 579: ! 580: lasterror = GetLastError (); ! 581: ! 582: return 0; ! 583: } ! 584: ! 585: void posixemu_tmpnam (char *name) ! 586: { ! 587: char buf[MAX_PATH]; ! 588: ! 589: GetTempPath (MAX_PATH, buf); ! 590: GetTempFileName (buf, "uae", 0, name); ! 591: } ! 592: ! 593: /* pthread Win32 emulation */ ! 594: void sem_init (HANDLE * event, int manual_reset, int initial_state) ! 595: { ! 596: *event = CreateEvent (NULL, manual_reset, initial_state, NULL); ! 597: } ! 598: ! 599: void sem_wait (HANDLE * event) ! 600: { ! 601: WaitForSingleObject (*event, INFINITE); ! 602: } ! 603: ! 604: void sem_post (HANDLE * event) ! 605: { ! 606: SetEvent (*event); ! 607: } ! 608: ! 609: int sem_trywait (HANDLE * event) ! 610: { ! 611: return WaitForSingleObject (*event, 0) == WAIT_OBJECT_0; ! 612: } ! 613: ! 614: /* Mega-klduge to prevent problems with Watcom's habit of passing ! 615: * arguments in registers... */ ! 616: static HANDLE thread_sem; ! 617: static void *(*thread_startfunc) (void *); ! 618: #ifndef __GNUC__ ! 619: static void * __stdcall thread_starter (void *arg) ! 620: #else ! 621: static void * thread_starter( void *arg ) ! 622: #endif ! 623: { ! 624: void *(*func) (void *) = thread_startfunc; ! 625: SetEvent (thread_sem); ! 626: return (*func) (arg); ! 627: } ! 628: ! 629: /* this creates high priority threads by default to speed up the file system (kludge, will be ! 630: * replaced by a set_penguin_priority() routine soon) */ ! 631: int start_penguin (void *(*f) (void *), void *arg, DWORD * foo) ! 632: { ! 633: static int have_event = 0; ! 634: HANDLE hThread; ! 635: ! 636: if (! have_event) { ! 637: thread_sem = CreateEvent (NULL, 0, 0, NULL); ! 638: have_event = 1; ! 639: } ! 640: thread_startfunc = f; ! 641: hThread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) thread_starter, arg, 0, foo); ! 642: SetThreadPriority (hThread, THREAD_PRIORITY_HIGHEST); ! 643: WaitForSingleObject (thread_sem, INFINITE); ! 644: } ! 645:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.