home *** CD-ROM | disk | FTP | other *** search
/ ftp.whtech.com / ftp.whtech.com.7z / ftp.whtech.com / emulators / v9t9 / linux / sources / V9t9 / source / OSLib / Win32.c < prev    next >
Encoding:
C/C++ Source or Header  |  2006-10-19  |  27.9 KB  |  1,423 lines

  1.  
  2. /*
  3.  *    Operating system library for WIN32.
  4.  *
  5.  *    This module handles interaction with the operating-system specific
  6.  *    intricacies with file manipulation, memory management, process
  7.  *    spawning, etc.
  8.  *
  9.  */
  10.  
  11. #if (defined(POSIX_FS) || defined(MAC_FS)) && !defined(WIN32_FS)
  12. #error Wrong module!
  13. #endif
  14.  
  15. #include "OSLib.h"
  16.  
  17. #include <string.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <malloc.h>
  21.  
  22. #include <x86_prefix.h>
  23. #include <winbase.h>
  24.  
  25. #include <stdlib.h>
  26. #include <assert.h>
  27.  
  28. #if DEBUG
  29. #define ASSERT(x)     assert(x)
  30. #else
  31. #define ASSERT(x)
  32. #endif
  33.  
  34. #define ERROR_RETURN 0xffffffff
  35.  
  36. #define ERROR_UNKNOWN 0xfafafafa
  37.  
  38. /*    get error text for an OSError */
  39. char
  40.            *
  41. OS_GetErrText(OSError err)
  42. {
  43.     static char errmsg[256];
  44.     char       *ptr;
  45.  
  46.     if (err != ERROR_UNKNOWN)
  47.         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
  48.                       0, errmsg, sizeof(errmsg), NULL);
  49.     else
  50.         strcpy(errmsg, "Unknown process spawning error");
  51.  
  52.     /*  Pointlessly, it has added CR/LF */
  53.     ptr = errmsg + strlen(errmsg) - 2;
  54.     if (ptr > errmsg && ptr[0] == 13 && ptr[1] == 10)
  55.         *ptr = 0;
  56.  
  57.     return errmsg;
  58. }
  59.  
  60. /*********************/
  61. #if 0
  62. #pragma mark -
  63. #endif
  64.  
  65.  
  66. /*    Initialize C program context  */
  67. OSError
  68. OS_InitProgram(int *argc, char ***argv)
  69. {
  70.     /*  This is needed to allow mangled old-style V9t9 names 
  71.        to be read properly without the OS re-mangling them. */
  72.     SetFileApisToOEM();
  73.     return OS_NOERR;
  74. }
  75.  
  76. /*    Terminate C program context  */
  77. OSError
  78. OS_TermProgram(void)
  79. {
  80.     return OS_NOERR;
  81. }
  82.  
  83. /*********************/
  84. #if 0
  85. #pragma mark -
  86. #endif
  87.  
  88.  
  89. /*  This buffer is used to hold one-time SpecToString() calls */
  90. static char intbuf[OS_PATHSIZE];
  91.  
  92. #define GETSPECSTR(sp,bf) if (OS_SpecToString(sp, bf, sizeof(bf))==NULL) return OS_FNTLERR
  93. #define GETPATHSPECSTR(sp,bf) if (OS_PathSpecToString(sp, bf, sizeof(bf))==NULL) return OS_FNTLERR
  94.  
  95. OSFileType  OS_TEXTTYPE = 0;
  96.  
  97. /*    create a new file, overwrite an old one if existing */
  98. OSError
  99. OS_Create(const OSSpec * spec, OSFileType * type)
  100. {
  101.     HANDLE      h;
  102.  
  103.     GETSPECSTR(spec, intbuf);
  104.     h = CreateFile(intbuf,
  105.                    GENERIC_WRITE | GENERIC_READ,
  106.                    FILE_SHARE_READ,
  107.                    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  108.     if (h == INVALID_HANDLE_VALUE)
  109.         return GetLastError();
  110.     else {
  111.         CloseHandle(h);
  112.         return OS_SetFileType(spec, type);
  113.     }
  114. }
  115.  
  116. /*    get status of a file */
  117. OSError
  118. OS_Status(const OSSpec * spec)
  119. {
  120.     DWORD       attr;
  121.  
  122.     GETSPECSTR(spec, intbuf);
  123.     attr = GetFileAttributes(intbuf);    /* works for directories */
  124.     if (attr == ERROR_RETURN)
  125.         return GetLastError();
  126.     else
  127.         return OS_NOERR;
  128. }
  129.  
  130.  
  131. /*  get type of a file;
  132.     return error if directory or not found */
  133. OSError
  134. OS_GetFileType(const OSSpec * spec, OSFileType * type)
  135. {
  136.     *type = 0;
  137.     return OS_NOERR;
  138. }
  139.  
  140. /*  set type for a file;
  141.     return error if directory or not found */
  142.  
  143. OSError
  144. OS_SetFileType(const OSSpec * spec, OSFileType * type)
  145. {
  146.     return OS_NOERR;
  147. }
  148.  
  149. /*  get timestamp of a file;
  150.     return error if directory or not found */
  151. OSError
  152. OS_GetFileTime(const OSSpec * spec, OSTime * crtm, OSTime * chtm)
  153. {
  154.     FILETIME    written, created;
  155.     OSRef       ref;
  156.     OSError     err;
  157.  
  158.     if ((err = OS_Open(spec, OSReadOnly, &ref)) != OS_NOERR)
  159.         return err;
  160.  
  161.     err = GetFileTime(ref, &created, NULL, &written) == 0 ?
  162.         GetLastError() : OS_NOERR;
  163.     OS_Close(ref);
  164.  
  165.     /*  Not simpler assignments because OSTime != FILETIME */
  166.     if (chtm) {
  167.         chtm->dwLowDateTime = written.dwLowDateTime;
  168.         chtm->dwHighDateTime = written.dwHighDateTime;
  169.     }
  170.  
  171.     if (crtm) {
  172.         crtm->dwLowDateTime = created.dwLowDateTime;
  173.         crtm->dwHighDateTime = created.dwHighDateTime;
  174.     }
  175.  
  176.     return err;
  177. }
  178.  
  179. /*  set timestamp of a file */
  180. OSError
  181. OS_SetFileTime(const OSSpec * spec, OSTime * crtm, OSTime * chtm)
  182. {
  183.     OSRef       ref;
  184.     OSError     err;
  185.     FILETIME    written, created;
  186.  
  187.     if (crtm) {
  188.         created.dwLowDateTime = crtm->dwLowDateTime;
  189.         created.dwHighDateTime = crtm->dwHighDateTime;
  190.     }
  191.  
  192.     if (chtm) {
  193.         written.dwLowDateTime = chtm->dwLowDateTime;
  194.         written.dwHighDateTime = chtm->dwHighDateTime;
  195.     }
  196.  
  197.     if ((err = OS_Open(spec, OSWrite, &ref)) != OS_NOERR)
  198.         return err;
  199.  
  200.     err =
  201.         SetFileTime(ref, crtm ? &created : NULL, NULL,
  202.                     chtm ? &written : NULL) == 0 ? GetLastError() : OS_NOERR;
  203.     OS_Close(ref);
  204.  
  205.     return err;
  206. }
  207.  
  208. /*    modify protection on a file */
  209. OSError
  210. OS_ModifyProtection(const OSSpec * spec, bool protect)
  211. {
  212.     DWORD       oldattr;
  213.  
  214.     GETSPECSTR(spec, intbuf);
  215.     if ((oldattr = GetFileAttributes(intbuf)) != ~0)
  216.         if (SetFileAttributes(intbuf,
  217.                               protect ? (oldattr | FILE_ATTRIBUTE_READONLY)
  218.                               : (oldattr & ~FILE_ATTRIBUTE_READONLY)))
  219.             return OS_NOERR;
  220.     return GetLastError();
  221. }
  222.  
  223. /*    check protection on a file */
  224. OSError
  225. OS_CheckProtection(const OSSpec * spec, bool *is_protected)
  226. {
  227.     DWORD       oldattr;
  228.  
  229.     GETSPECSTR(spec, intbuf);
  230.     if ((oldattr = GetFileAttributes(intbuf)) != ~0) {
  231.         *is_protected = !!(oldattr & FILE_ATTRIBUTE_READONLY);
  232.         return OS_NOERR;
  233.     }
  234.     return GetLastError();
  235. }
  236.  
  237. /*    get disk space info */
  238. OSError
  239. OS_GetDiskStats(const OSPathSpec * spec,
  240.                 OSSize * blocksize, OSSize * total, OSSize * free)
  241. {
  242.     char       *dptr;
  243.     DWORD       secsperclus, bytespersec, freeclus, totclus;
  244.  
  245.     GETPATHSPECSTR(spec, intbuf);
  246.     dptr = (char *) OS_GetDirPtr(intbuf);
  247.     if (*dptr == OS_PATHSEP)
  248.         dptr++;
  249.     *dptr = 0;
  250.     if (GetDiskFreeSpace
  251.         (intbuf, &secsperclus, &bytespersec, &freeclus, &totclus)) {
  252.         *blocksize = bytespersec * secsperclus;
  253.         *total = totclus;
  254.         *free = freeclus;
  255.         return OS_NOERR;
  256.     } else
  257.         return GetLastError();
  258. }
  259.  
  260. /*************************************/
  261. #if 0
  262. #pragma mark -
  263. #endif
  264.  
  265.  
  266. /*    open an existing file */
  267. OSError
  268. OS_Open(const OSSpec * spec, OSOpenMode mode, OSRef * ref)
  269. {
  270.     static int  modetrans[] = { GENERIC_READ, GENERIC_WRITE,
  271.         GENERIC_READ | GENERIC_WRITE, GENERIC_WRITE
  272.     };
  273.  
  274.     GETSPECSTR(spec, intbuf);
  275.     *ref = CreateFile(intbuf,
  276.                       modetrans[mode],
  277.                       FILE_SHARE_READ,
  278.                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  279.  
  280.     if (*ref == INVALID_HANDLE_VALUE)
  281.         return GetLastError();
  282.     else {
  283.         if (mode == OSAppend)
  284.             if (SetFilePointer(*ref, 0, NULL, FILE_END) == ERROR_RETURN)
  285.                 return GetLastError();
  286.  
  287.         return OS_NOERR;
  288.     }
  289. }
  290.  
  291. /*    write binary data, up to length bytes;
  292.     length==0 can extend file;
  293.     update length;
  294.     error indicates serious failure */
  295. OSError
  296. OS_Write(OSRef ref, void *buffer, OSSize * length)
  297. {
  298.     OSPos       pos;
  299.     OSSize      size;
  300.  
  301.     /*  This sucks, but Win32 apparently has a bug when
  302.        writing past position 0 on an empty file. */
  303.     if ((pos = SetFilePointer(ref, 0L, NULL, FILE_CURRENT)) == ERROR_RETURN ||
  304.         (size = GetFileSize(ref, NULL)) == ERROR_RETURN)
  305.         return GetLastError();
  306.  
  307.     if (pos > size) {
  308.         static char zeroes[32] = { 0 };
  309.         long        fill;
  310.  
  311.         if (SetFilePointer(ref, size, NULL, FILE_BEGIN) == ERROR_RETURN)
  312.             return GetLastError();
  313.  
  314.         while (pos > size) {
  315.             unsigned long wrote;
  316.  
  317.             fill =
  318.                 (pos - size > sizeof(zeroes)) ? sizeof(zeroes) : pos - size;
  319.             if (WriteFile(ref, zeroes, fill, &wrote, NULL) == 0)
  320.                 return GetLastError();
  321.             if (wrote < fill) {
  322.                 *length = 0;
  323.                 return OS_NOERR;
  324.             }
  325.             size += fill;
  326.         }
  327.     }
  328.  
  329.     if (WriteFile(ref, buffer, *length, length, NULL) == 0)
  330.         return GetLastError();
  331.     else
  332.         return OS_NOERR;
  333. }
  334.  
  335. /*    read binary data, up to length bytes;
  336.     update length;
  337.     error indicates serious failure.  */
  338. OSError
  339. OS_Read(OSRef ref, void *buffer, OSSize * length)
  340. {
  341.     if (ReadFile(ref, buffer, *length, length, NULL) == 0)
  342.         return GetLastError();
  343.     else
  344.         return OS_NOERR;
  345. }
  346.  
  347. /*    seek a file;
  348.     illegal seek is revealed by next write or read;
  349.     error indicates serious failure.  */
  350. OSError
  351. OS_Seek(OSRef ref, OSSeekMode how, OSPos offset)
  352. {
  353.     static int  howtrans[] = { FILE_CURRENT, FILE_BEGIN, FILE_END };
  354.  
  355.     if (SetFilePointer(ref, offset, NULL, howtrans[how]) == ERROR_RETURN)
  356.         return GetLastError();
  357.     else
  358.         return OS_NOERR;
  359. }
  360.  
  361. /*    return file pointer */
  362. OSError
  363. OS_Tell(OSRef ref, OSPos * offset)
  364. {
  365.     if ((*offset = SetFilePointer(ref, 0L, NULL, FILE_CURRENT)) ==
  366.         ERROR_RETURN) return GetLastError();
  367.     else
  368.         return OS_NOERR;
  369. }
  370.  
  371.  
  372. /*    close a file */
  373. OSError
  374. OS_Close(OSRef ref)
  375. {
  376.     if (CloseHandle(ref) == 0)
  377.         return GetLastError();
  378.     else
  379.         return OS_NOERR;
  380. }
  381.  
  382. /*  get length of a file;
  383.     return error if directory or not found */
  384. OSError
  385. OS_GetSize(OSRef ref, OSSize * length)
  386. {
  387.     *length = GetFileSize(ref, NULL);
  388.     if (*length == ERROR_RETURN)
  389.         return GetLastError();
  390.     else
  391.         return OS_NOERR;
  392. }
  393.  
  394. /*  set length of a file;
  395.     return error if directory or not found */
  396. OSError
  397. OS_SetSize(OSRef ref, OSSize length)
  398. {
  399.     DWORD       orig;
  400.  
  401.     if ((orig = SetFilePointer(ref, 0L, NULL, FILE_CURRENT)) == ERROR_RETURN
  402.         || SetFilePointer(ref, length, NULL, FILE_BEGIN) == ERROR_RETURN
  403.         || !SetEndOfFile(ref)
  404.         || SetFilePointer(ref, orig, NULL, FILE_BEGIN) == ERROR_RETURN)
  405.         return GetLastError();
  406.     else
  407.         return OS_NOERR;
  408. }
  409.  
  410.  
  411. /**************************************/
  412. #if 0
  413. #pragma mark -
  414. #endif
  415.  
  416.  
  417. /*    delete a file */
  418. OSError
  419. OS_Delete(const OSSpec * spec)
  420. {
  421.     GETSPECSTR(spec, intbuf);
  422.     if (DeleteFile(intbuf) == 0)
  423.         return GetLastError();
  424.     else
  425.         return OS_NOERR;
  426. }
  427.  
  428. /*    rename a file */
  429. OSError
  430. OS_Rename(const OSSpec * oldspec, const OSSpec * newspec)
  431. {
  432.     char        newfn[OS_PATHSIZE];
  433.  
  434.     GETSPECSTR(oldspec, intbuf);
  435.     GETSPECSTR(newspec, newfn);
  436.  
  437.     if (MoveFile(intbuf, newfn) == 0)
  438.         return GetLastError();
  439.     else
  440.         return OS_NOERR;
  441. }
  442.  
  443. /*    make directory */
  444. OSError
  445. OS_Mkdir(const OSSpec * spec)
  446. {
  447.     GETSPECSTR(spec, intbuf);
  448.     if (CreateDirectory(intbuf, NULL) == 0)
  449.         return GetLastError();
  450.     else
  451.         return OS_NOERR;
  452. }
  453.  
  454. /*    remove directory */
  455. OSError
  456. OS_Rmdir(const OSPathSpec * spec)
  457. {
  458.     GETPATHSPECSTR(spec, intbuf);
  459.     if (RemoveDirectory(intbuf) == 0)
  460.         return GetLastError();
  461.     else
  462.         return OS_NOERR;
  463. }
  464.  
  465. /*    change directory */
  466. OSError
  467. OS_Chdir(const OSPathSpec * spec)
  468. {
  469.     GETPATHSPECSTR(spec, intbuf);
  470.     if (SetCurrentDirectory(intbuf) == 0)
  471.         return GetLastError();
  472.     else
  473.         return OS_NOERR;
  474. }
  475.  
  476. static void
  477. CanonDir(char *s)
  478. {
  479.     s += strlen(s);
  480.     if (*(s - 1) != '\\') {
  481.         *s++ = '\\';
  482.         *s = 0;
  483.     }
  484. }
  485.  
  486. /*    get current working directory */
  487. OSError
  488. OS_GetCWD(OSPathSpec * spec)
  489. {
  490.     OSError     err;
  491.  
  492.     if ((err = GetCurrentDirectory(OS_PATHSIZE, spec->s)) == 0)
  493.         return GetLastError();
  494.     else {
  495.         CanonDir(spec->s);
  496.         return OS_NOERR;
  497.     }
  498. }
  499.  
  500. static      OSError
  501. RedirectStdHandle(HANDLE * savedStdHandle, long which,
  502.                   const char *outfilename)
  503. {
  504.     SECURITY_ATTRIBUTES saAttr;
  505.     HANDLE      hSaveStdHandle;
  506.     HANDLE      newfile;
  507.  
  508.     /* Set the bInheritHandle flag so pipe handles are inherited. */
  509.  
  510.     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  511.     saAttr.bInheritHandle = TRUE;
  512.     saAttr.lpSecurityDescriptor = NULL;
  513.  
  514.     /* Save the handle to the current STDOUT/STDERR. */
  515.  
  516.     hSaveStdHandle = GetStdHandle(which);
  517.  
  518.     /* Create a pipe for the child process's STDOUT/STDERR. */
  519.  
  520.     newfile = CreateFile(outfilename, GENERIC_WRITE, FILE_SHARE_READ,
  521.                          &saAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  522.     if (newfile == INVALID_HANDLE_VALUE)
  523.         return GetLastError();
  524.  
  525.     /* Set a write handle to the pipe to be STDOUT. */
  526.  
  527.     if (!SetStdHandle(which, newfile))
  528.         return GetLastError();
  529.  
  530.     *savedStdHandle = hSaveStdHandle;
  531.     return OS_NOERR;
  532. }
  533.  
  534. /*    spawn a subprocess */
  535. OSError
  536. OS_Execute(const OSSpec * spec, char **argv, char **envp,
  537.            const char *stdoutfile, const char *stderrfile, int *exitcode)
  538. {
  539.     char       *cmdline;
  540.     int         len = 0;
  541.     char      **ptr;
  542.     HANDLE      savedStdout, savedStderr;
  543.     BOOL        success;
  544.  
  545.     STARTUPINFO si;
  546.     PROCESS_INFORMATION pi;
  547.  
  548.     /*  Construct command line. */
  549.  
  550.     /*  Get maximum length... */
  551.     ptr = argv;
  552.     while (*ptr) {
  553.         len += strlen(*ptr) + 3;    /* space, possible quotes */
  554.         ptr++;
  555.     }
  556.     cmdline = (char *) malloc(len);
  557.     if (cmdline == NULL)
  558.         return OS_MEMERR;
  559.  
  560.     ptr = argv;
  561.     len = 0;
  562.     while (*ptr) {
  563.         if (strchr(*ptr, ' ') != NULL) {
  564.             cmdline[len++] = '\"';
  565.             strcpy(cmdline + len, *ptr);
  566.             len += strlen(*ptr);
  567.             cmdline[len++] = '\"';
  568.         } else if (strchr(*ptr, '\"') != NULL) {
  569.             char       *f = *ptr;
  570.  
  571.             while (*f) {
  572.                 if (*f == '\"') {
  573.                     *(cmdline + len) = '\\';
  574.                     *(cmdline + len + 1) = '\"';
  575.                     len++;
  576.                 } else
  577.                     *(cmdline + len) = *f;
  578.                 f++;
  579.                 len++;
  580.             }
  581.         } else {
  582.             strcpy(cmdline + len, *ptr);
  583.             len += strlen(*ptr);
  584.         }
  585.         cmdline[len++] = ' ';
  586.         ptr++;
  587.     }
  588.     cmdline[len] = 0;
  589.  
  590.     /*  Set up output handles */
  591.     if (stdoutfile) {
  592.         OSError     err =
  593.  
  594.             RedirectStdHandle(&savedStdout, STD_OUTPUT_HANDLE, stdoutfile);
  595.         if (err != OS_NOERR)
  596.             return err;
  597.     }
  598.  
  599.     if (stderrfile) {
  600.         OSError     err =
  601.  
  602.             RedirectStdHandle(&savedStderr, STD_ERROR_HANDLE, stderrfile);
  603.         if (err != OS_NOERR) {
  604.             if (stdoutfile)
  605.                 SetStdHandle(STD_OUTPUT_HANDLE, savedStdout);
  606.             return err;
  607.         }
  608.     }
  609.  
  610.     memset((void *) &si, 0, sizeof(si));
  611.     si.cb = sizeof(si);
  612.     si.lpTitle = "Linking";
  613.  
  614.     GETSPECSTR(spec, intbuf);
  615.  
  616.     // ignoring environment block; if used, use GetLogicalDrives and
  617.     // GetFullPathName 'X:' to add the mandatory CWDs of each directory
  618.     // to the new block
  619.  
  620.     success =
  621.         CreateProcess(intbuf, cmdline, NULL, NULL, true, 0, NULL, NULL, &si,
  622.                       &pi);
  623.  
  624.     if (stdoutfile)
  625.         SetStdHandle(STD_OUTPUT_HANDLE, savedStdout);
  626.     if (stderrfile)
  627.         SetStdHandle(STD_ERROR_HANDLE, savedStderr);
  628.  
  629.     if (!success)
  630.         return GetLastError();
  631.     else {
  632.  
  633.         free(cmdline);
  634.         WaitForSingleObject(pi.hProcess, INFINITE);
  635.         if (GetExitCodeProcess(pi.hProcess, (unsigned long *) exitcode)) {
  636.             if (*exitcode == STILL_ACTIVE) {
  637.                 fprintf(stderr, "??? OS_Exec: process still active ???\n");
  638.                 return ERROR_UNKNOWN;
  639.             } else
  640.                 return OS_NOERR;
  641.         } else {
  642.             return GetLastError();
  643.         }
  644.     }
  645. }
  646.  
  647. /*************************************/
  648. #if 0
  649. #pragma mark -
  650. #endif
  651.  
  652.  
  653. /*    skip a volume in a fullpath */
  654. const char *
  655. OS_GetDirPtr(const char *path)
  656. {
  657.     /*  These tests are redundant since we can have 
  658.        a full path and can distinguish the type
  659.        based on whether path[0] is '\\', but it's
  660.        easier to read this way.
  661.      */
  662.     if (path[0] == '\\' && path[1] == '\\') {    /* network */
  663.         const char *ptr = strchr(path + 2, '\\');
  664.  
  665.         if (ptr == NULL)
  666.             ptr = path + strlen(path);    /* only a node */
  667.         return ptr;
  668.     } else if (isalpha(path[0]) && path[1] == ':')    /* drive */
  669.         return path + 2;
  670.     else if (path[0] == '\\')
  671.         return path;
  672.     else {
  673.         ASSERT(!"Cannot discern type of fullpath");
  674.         return path;
  675.     }
  676. }
  677.  
  678.  
  679. /*    canonicalize a filepath for host; if dst is NULL, overwrite src in place */
  680. static      OSError
  681. OS_CanonPath(const char *src, char *dst)
  682. {
  683.     const char *ptr;
  684.     char       *dptr;
  685.  
  686.     if (strlen(src) > OS_MAXPATHLEN)
  687.         return OS_FNTLERR;
  688.  
  689.     /*  We can do this since dst is at most the length of src */
  690.     if (dst == NULL)
  691.         dst = (char *) src;
  692.  
  693.     ptr = src;
  694.     dptr = dst;
  695.  
  696.     /*  First, check for weird Cygnus usage  */
  697.     if (ptr[0] == '/' && ptr[1] == '/' &&
  698.         isalpha(ptr[2]) && (ptr[3] == '/' || ptr[3] == 0)) {
  699.         *dptr++ = ptr[2];
  700.         *dptr++ = ':';
  701.         ptr += 3;
  702.     } else
  703.         /*  Check for drive */
  704.     if (isalpha(ptr[0]) && ptr[1] == ':') {
  705.         *dptr++ = ptr[0] & ~0x20;    /* uppercase */
  706.         *dptr++ = ':';
  707.         ptr += 2;
  708.     }
  709.  
  710.     /*  Convert slashes to backslashes */
  711.  
  712.     while (*ptr) {
  713.         if (*ptr == '/')
  714.             *dptr++ = '\\';
  715.         else
  716.             *dptr++ = *ptr;
  717.         ptr++;
  718.     }
  719.  
  720.     *dptr = 0;
  721.  
  722.     return OS_NOERR;
  723. }
  724.  
  725.  
  726. /*    tell if a filepath is legal for filesystem;
  727.     call after OS_CanonPath if necessary */
  728. OSError
  729. OS_IsLegalPath(const char *path)
  730. {
  731.     const char *scan = path;
  732.     int         pthlen = 0, fnlen = 0;
  733.  
  734.     /*  Do NOT check for legal characters;
  735.        we're too dumb to know this, re:
  736.        \\?\C:\ ... and \\?\UNC\...  */
  737.  
  738.     while (*scan) {
  739.         if (*scan == '\\')
  740.             fnlen = 0;
  741.         else
  742.             fnlen++;
  743.  
  744.         pthlen++;
  745.  
  746.         if (fnlen > OS_MAXNAMELEN || pthlen > OS_MAXPATHLEN)
  747.             return OS_FNTLERR;
  748.  
  749.         scan++;
  750.     }
  751.     return OS_NOERR;
  752. }
  753.  
  754. /*    tell if a filepath represents a full path */
  755. int
  756. OS_IsFullPath(const char *path)
  757. {
  758.     /*  can be network, \\node\..., or X:\... */
  759.     return (path[0] == '\\' && path[1] == '\\') ||
  760.         (isalpha(path[0]) && path[1] == ':' && path[2] == '\\');
  761. }
  762.  
  763. /*    compact a canonical full path; if dst is NULL, overwrite src in place */
  764. static void
  765. OS_CompactPath(char *src)
  766. {
  767.     char       *bptr;
  768.     char       *to;
  769.     char       *from, *start;
  770.  
  771.     ASSERT(OS_IsFullPath(src));
  772.  
  773.     start = (char *) OS_GetDirPtr(src);
  774.  
  775.     bptr = start;
  776.     from = start;
  777.     to = bptr;
  778.  
  779.     while (*from) {
  780.         char       *brk;
  781.  
  782.         brk = from + 1;
  783.         while (*brk && *brk != '\\')
  784.             brk++;
  785.  
  786.         if (brk - from == 1)    /* eliminate "\\" */
  787.             from = brk;            /* skip path break */
  788.         else {
  789.             if (brk - from == 2 && from[1] == '.')
  790.                 from = brk;
  791.             else /* eliminate ".." and previous directory */ if (brk - from ==
  792.                                                                  3
  793.                                                                  && from[1] ==
  794.                                                                  '.'
  795.                                                                  && from[2] ==
  796.                                                                  '.') {
  797.                 if (to > bptr) {
  798.                     do
  799.                         to--;
  800.                     while (to >= bptr && *to != '\\');
  801.                 }
  802.                 from = brk;
  803.             } else                /* copy */
  804.                 while (from < brk)
  805.                     *to++ = *from++;
  806.  
  807.         }
  808.     }
  809.  
  810.     if (to == bptr || *(from - 1) == '\\')
  811.         *to++ = '\\';            /* ended at directory */
  812.  
  813.     *to = 0;                    /* end string */
  814. }
  815.  
  816.  
  817. /*    compare paths */
  818. int
  819. OS_EqualPath(const char *a, const char *b)
  820. {
  821.     int         offs = 0;
  822.  
  823.     while (a[offs] && b[offs] &&
  824.            (a[offs] >= 'A' && a[offs] <= 'Z' ? a[offs] + 32 : a[offs]) ==
  825.            (b[offs] >= 'A' && b[offs] <= 'Z' ? b[offs] + 32 : b[offs])) {
  826.         offs++;
  827.     }
  828.     return (a[offs] == 0 && b[offs] == 0);
  829. }
  830.  
  831. /*************************************/
  832. #if 0
  833. #pragma mark -
  834. #endif
  835.  
  836. /*    make OSSpec from a path and return
  837.     what kind it is */
  838. OSError
  839. OS_MakeSpec(const char *path, OSSpec * spec, bool * isfile)
  840. {
  841.     OSError     err;
  842.     DWORD       attr;
  843.     char        tmp[OS_PATHSIZE];
  844.     char        full[OS_PATHSIZE];
  845.     char       *ptr, *eptr;
  846.     const char *nptr;
  847.     int         len;
  848.  
  849.     if ((err = OS_CanonPath(path, tmp)) != OS_NOERR)
  850.         return err;
  851.  
  852.     if (GetFullPathName(tmp, OS_PATHSIZE, full, &ptr) == 0)
  853.         return GetLastError();
  854.  
  855.     /*  Restore stolen trailing '.' if needed */
  856.     eptr = full + strlen(full) - 1;
  857.     nptr = strrchr(path, OS_PATHSEP);
  858.     if (nptr)
  859.         nptr++;
  860.     else
  861.         nptr = path;
  862.     if (strcmp(nptr, "..") != 0 && strcmp(nptr, ".") != 0 &&
  863.         (nptr[strlen(nptr) - 1] == '.' && *eptr != '.')) {
  864.         *++eptr = '.';
  865.         *++eptr = 0;
  866.     }
  867.  
  868.     /*  Tell if it's a directory  */
  869.     if ((attr = GetFileAttributes(full)) != ERROR_RETURN) {
  870.         if (attr & FILE_ATTRIBUTE_DIRECTORY) {
  871.             if (isfile)
  872.                 *isfile = false;
  873.             ptr = NULL;
  874.         } else {
  875.             if (isfile)
  876.                 *isfile = true;
  877.         }
  878.     } else {
  879.         err = GetLastError();
  880.         if (err != OS_NOERR && err != OS_FNFERR)
  881.             return err;
  882.  
  883.         if (isfile)
  884.             *isfile = true;
  885.     }
  886.  
  887.     if (ptr == NULL) {
  888.         ptr = eptr + 1;
  889.         if (*(ptr - 1) != OS_PATHSEP) {
  890.             *ptr++ = OS_PATHSEP;
  891.             *ptr = 0;
  892.         }
  893.     }
  894.  
  895.     len = ptr - full;
  896.     if (len >= OS_PATHSIZE) {
  897.         *spec->path.s = 0;
  898.         return OS_FNTLERR;
  899.     }
  900.  
  901.     memcpy(spec->path.s, full, len);
  902.     spec->path.s[len] = 0;        // truncate
  903.  
  904.     len = strlen(ptr);
  905.     if (len >= OS_NAMESIZE) {
  906.         *spec->name.s = 0;
  907.         return OS_FNTLERR;
  908.     }
  909.  
  910.     memcpy(spec->name.s, ptr, len + 1);
  911.  
  912.     return OS_NOERR;
  913. }
  914.  
  915. /*    make OSSpec from a path;
  916.     must resolve to a file */
  917. OSError
  918. OS_MakeFileSpec(const char *path, OSSpec * spec)
  919. {
  920.     bool        isfile;
  921.     OSError     err;
  922.  
  923.     err = OS_MakeSpec(path, spec, &isfile);
  924.     if (err != OS_NOERR)
  925.         return err;
  926.  
  927.     if (!isfile)
  928.         return OS_FIDERR;
  929.  
  930.     return OS_NOERR;
  931. }
  932.  
  933. /*    make OSPathSpec from a volume and directory */
  934. OSError
  935. OS_MakePathSpec(const char *vol, const char *dir, OSPathSpec * spec)
  936. {
  937.     OSSpec      tmp;
  938.     OSError     err;
  939.     bool        isfile;
  940.     char        path[OS_PATHSIZE + OS_VOLSIZE];
  941.     int            len;
  942.     
  943.     if ((vol ? strlen(vol) : 0) + (dir ? strlen(dir) : 0) + 2 > sizeof(path))
  944.         return OS_FNTLERR;
  945.  
  946.     if (vol != NULL) {
  947.         if (*vol == 0)            /* non-drive root "\dl\" */
  948.             strcpy(path, dir ? dir : "\\");
  949.         else if (*(vol + 1) == 0)    /* assume this is a drive */
  950.             sprintf(path, "%s:%s", vol, dir ? dir : "\\");
  951.         else if (dir)
  952.             sprintf(path, "%s%s%s", vol, *dir == '\\' ? "" : "\\", dir);
  953.         else
  954.             sprintf(path, "%s", vol);
  955.     } else if (dir != NULL) {
  956.         strcpy(path, dir);
  957.     } else
  958.         strcpy(path, ".");
  959.  
  960.     err = OS_MakeSpec(path, &tmp, &isfile);
  961.     len = strlen(tmp.path.s);
  962.  
  963.     if (err != OS_NOERR) {
  964.             /* ensure that path is legal */
  965.         if (len+1 < OS_PATHSIZE)
  966.             memcpy(spec->s+len, "\\", 2);
  967.         else 
  968.             memcpy(spec->s+len-1, "\\", 2);
  969.         return err;
  970.     } else {
  971.         memcpy(spec->s, tmp.path.s, len + 1);
  972.         spec->s[OS_PATHSIZE-1] = 0;
  973.     }
  974.  
  975.     if (isfile)
  976.         return OS_FNIDERR;
  977.  
  978.     return OS_NOERR;
  979. }
  980.  
  981. /*    make OSNameSpec from a filename */
  982. OSError
  983. OS_MakeNameSpec(const char *name, OSNameSpec * spec)
  984. {
  985.     if (strchr(name, '\\') != NULL)
  986.         return OS_FIDERR;
  987.  
  988.     if (strlen(name) > OS_MAXNAMELEN)
  989.         return OS_FNTLERR;
  990.  
  991.     strcpy(spec->s, name);
  992.  
  993.     return OS_NOERR;
  994. }
  995.  
  996.  
  997. /*    return FS root spec */
  998. OSError
  999. OS_GetRootSpec(OSPathSpec * spec)
  1000. {
  1001.     strcpy(spec->s, "\\");
  1002.     return OS_NOERR;
  1003. }
  1004.  
  1005.  
  1006. /********************************************/
  1007. #if 0
  1008. #pragma mark -
  1009. #endif
  1010.  
  1011.  
  1012. /*    make a full pathname from OSSpec */
  1013. char       *
  1014. OS_SpecToString(const OSSpec * spec, char *path, int size)
  1015. {
  1016.     if (size == 0)
  1017.         size = OS_PATHSIZE;
  1018.  
  1019.     if (path == NULL && (path = (char *) malloc(size)) == NULL)
  1020.         return NULL;
  1021.     else {
  1022.         int         plen, nlen;
  1023.  
  1024.         plen = strlen(spec->path.s);
  1025.         nlen = strlen(spec->name.s);
  1026.         if (plen + nlen >= size) {
  1027.             if (plen >= size) {
  1028.                 nlen = 0;
  1029.                 plen = size - 1;
  1030.             } else
  1031.                 nlen = size - plen - 1;
  1032.         }
  1033.         memcpy(path, spec->path.s, plen);
  1034.         memcpy(path + plen, spec->name.s, nlen);
  1035.         path[plen + nlen] = 0;
  1036.         return path;
  1037.     }
  1038. }
  1039.  
  1040. /*    make a path from OSPathSpec */
  1041. char       *
  1042. OS_PathSpecToString(const OSPathSpec * pspec, char *path, int size)
  1043. {
  1044.     if (size == 0)
  1045.         size = OS_PATHSIZE;
  1046.  
  1047.     if (path == NULL && (path = (char *) malloc(size)) == NULL)
  1048.         return NULL;
  1049.     else {
  1050.         int         plen;
  1051.  
  1052.         plen = strlen(pspec->s);
  1053.         if (plen >= size)
  1054.             plen = size - 1;
  1055.         memcpy(path, pspec->s, plen);
  1056.         path[plen] = 0;
  1057.         return path;
  1058.     }
  1059. }
  1060.  
  1061. /*    make a name from OSNameSpec */
  1062. char       *
  1063. OS_NameSpecToString(const OSNameSpec * nspec, char *name, int size)
  1064. {
  1065.     if (size == 0)
  1066.         size = OS_NAMESIZE;
  1067.  
  1068.     if (name == NULL && (name = (char *) malloc(size)) == NULL)
  1069.         return NULL;
  1070.     else {
  1071.         int         nlen = strlen(nspec->s);
  1072.  
  1073.         if (nlen >= size)
  1074.             nlen = size - 1;
  1075.         memcpy(name, nspec->s, nlen);
  1076.         name[nlen] = 0;
  1077.         return name;
  1078.     }
  1079. }
  1080.  
  1081. /*    return the size of an OSPathSpec, for duplication purposes */
  1082. int
  1083. OS_SizeOfPathSpec(const OSPathSpec * spec)
  1084. {
  1085.     return (strlen(spec->s) + 1);
  1086. }
  1087.  
  1088. /*    return the size of an OSNameSpec, for duplication purposes */
  1089. int
  1090. OS_SizeOfNameSpec(const OSNameSpec * spec)
  1091. {
  1092.     return (strlen(spec->s) + 1);
  1093. }
  1094.  
  1095. /*    compare OSSpecs */
  1096. int
  1097. OS_EqualSpec(const OSSpec * a, const OSSpec * b)
  1098. {
  1099.     return OS_EqualPathSpec(&a->path, &b->path) &&
  1100.         OS_EqualNameSpec(&a->name, &b->name);
  1101. }
  1102.  
  1103. /*    compare OSPathSpecs */
  1104. int
  1105. OS_EqualPathSpec(const OSPathSpec * a, const OSPathSpec * b)
  1106. {
  1107.     return (OS_EqualPath(a->s, b->s));
  1108. }
  1109.  
  1110. /*    compare OSNameSpecs */
  1111. int
  1112. OS_EqualNameSpec(const OSNameSpec * a, const OSNameSpec * b)
  1113. {
  1114.     return (OS_EqualPath(a->s, b->s));
  1115. }
  1116.  
  1117. #if 0
  1118. #pragma mark -
  1119. #endif
  1120.  
  1121.  
  1122. /*    tell if OSSpec is a directory */
  1123. int
  1124. OS_IsDir(const OSSpec * spec)
  1125. {
  1126.     DWORD       attr;
  1127.     int         len;
  1128.  
  1129.     GETSPECSTR(spec, intbuf);
  1130.     len = strlen(intbuf);
  1131.     if (intbuf[len - 1] == OS_PATHSEP)
  1132.         intbuf[len - 1] = 0;
  1133.  
  1134.     attr = GetFileAttributes(intbuf);
  1135.     if (attr == ERROR_RETURN)
  1136.         return 0;
  1137.     else
  1138.         return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
  1139. }
  1140.  
  1141. /*    tell if OSSpec is a file */
  1142. int
  1143. OS_IsFile(const OSSpec * spec)
  1144. {
  1145.     DWORD       attr;
  1146.     int         len;
  1147.  
  1148.     GETSPECSTR(spec, intbuf);
  1149.     len = strlen(intbuf);
  1150.     if (intbuf[len - 1] == OS_PATHSEP)
  1151.         intbuf[len - 1] = 0;
  1152.  
  1153.     attr = GetFileAttributes(intbuf);
  1154.     if (attr == ERROR_RETURN)
  1155.         return 0;
  1156.     else
  1157.         return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0;
  1158. }
  1159.  
  1160. /*    tell if OSSpec is a softlink */
  1161. int
  1162. OS_IsLink(const OSSpec * spec)
  1163. {
  1164.     return 0;
  1165. }
  1166.  
  1167. /*    resolve a [soft] link / alias */
  1168. OSError
  1169. OS_ResolveLink(const OSSpec * link, OSSpec * target)
  1170. {
  1171.     *target = *link;
  1172.     return OS_NOERR;
  1173. }
  1174.  
  1175. /*************************************/
  1176. #if 0
  1177. #pragma mark -
  1178. #endif
  1179.  
  1180.  
  1181. /*    open a directory for reading */
  1182. OSError
  1183. OS_OpenDir(const OSPathSpec * spec, OSDirRef * ref)
  1184. {
  1185.     char        wildcard[OS_PATHSIZE];
  1186.  
  1187.     ref->dir.ffd = (WIN32_FIND_DATA *) malloc(sizeof(WIN32_FIND_DATA));
  1188.     if (ref->dir.ffd == NULL)
  1189.         return OS_MEMERR;
  1190.  
  1191.     ref->path = *spec;
  1192.     strcpy(wildcard, spec->s);
  1193.     strcat(wildcard, "*");        /* must specify wildcard */
  1194.  
  1195.     ref->dir.handle =
  1196.         FindFirstFile(wildcard, (WIN32_FIND_DATA *) ref->dir.ffd);
  1197.     if (ref->dir.handle == INVALID_HANDLE_VALUE)
  1198.         return GetLastError();
  1199.     else
  1200.         return OS_NOERR;
  1201. }
  1202.  
  1203. /*    read an entry from a directory;
  1204.     don't return "." or "..";
  1205.     return error when end-of-directory reached */
  1206. OSError
  1207. OS_ReadDir(OSDirRef * ref, OSSpec * entry, char *filename, bool * isfile)
  1208. {
  1209.     WIN32_FIND_DATA *ffd = (WIN32_FIND_DATA *) ref->dir.ffd;
  1210.     char        newname[OS_PATHSIZE];
  1211.     char        fn[OS_PATHSIZE];
  1212.     OSError     err;
  1213.     int         len;
  1214.  
  1215.     do {
  1216.         if (ref->dir.handle == INVALID_HANDLE_VALUE)
  1217.             return OS_FNFERR;
  1218.         else {
  1219.             /*  we already have one entry cached */
  1220.             if (strlen(ffd->cFileName) < OS_NAMESIZE)
  1221.                 strncpy(newname, ffd->cFileName, OS_NAMESIZE);
  1222.             else
  1223.                 strncpy(newname, ffd->cAlternateFileName, OS_NAMESIZE);
  1224.             newname[OS_PATHSIZE - 1] = 0;
  1225.  
  1226.             /*  cache next value */
  1227.             if (FindNextFile(ref->dir.handle, ffd) == 0)
  1228.                 OS_CloseDir(ref);
  1229.         }
  1230.     } while (strcmp(newname, ".") == 0 || strcmp(newname, "..") == 0 ||
  1231.              strlen(ref->path.s) + strlen(newname) >= OS_PATHSIZE);
  1232.  
  1233.     /* the following is so we properly create directory specs */
  1234.  
  1235.     len = strlen(ref->path.s);
  1236.     strncpy(fn, ref->path.s, OS_PATHSIZE - 1);
  1237.     if (len < OS_PATHSIZE) {
  1238.         strncpy(fn + len, newname, OS_PATHSIZE - 1 - len);
  1239.         fn[OS_PATHSIZE - 1] = 0;
  1240.     } else
  1241.         return OS_FNTLERR;
  1242.  
  1243.     strncpy(filename, newname, OS_NAMESIZE - 1);
  1244.     filename[OS_NAMESIZE - 1] = 0;
  1245.  
  1246.     err = OS_MakeSpec(fn, entry, isfile);
  1247.     return err;
  1248. }
  1249.  
  1250. /*    close directory */
  1251. OSError
  1252. OS_CloseDir(OSDirRef * ref)
  1253. {
  1254.     if (ref->dir.handle != INVALID_HANDLE_VALUE) {
  1255.         if (FindClose(ref->dir.handle) == 0)
  1256.             return GetLastError();
  1257.         if (ref->dir.ffd)
  1258.             free(ref->dir.ffd);
  1259.         ref->dir.ffd = NULL;
  1260.         ref->dir.handle = INVALID_HANDLE_VALUE;
  1261.     }
  1262.     return OS_NOERR;
  1263. }
  1264.  
  1265. #if 0
  1266. #pragma mark -
  1267. #endif
  1268.  
  1269. /*    return time in milliseconds */
  1270. unsigned long
  1271. OS_GetMilliseconds(void)
  1272. {
  1273.     return GetTickCount();
  1274. }
  1275.  
  1276. /*    return current time */
  1277. void
  1278. OS_GetTime(OSTime * tm)
  1279. {
  1280.     SYSTEMTIME  stm;
  1281.     FILETIME    ftm;
  1282.  
  1283.     GetSystemTime(&stm);
  1284.     SystemTimeToFileTime(&stm, &ftm);    /* may return error, ignored */
  1285.     *tm = *(OSTime *) & ftm;
  1286. }
  1287.  
  1288. #if 0
  1289. #pragma mark -
  1290. #endif
  1291.  
  1292. enum { OSMemDelta = 4096 };
  1293.  
  1294. /*    allocate a memory handle  */
  1295. OSError
  1296. OS_NewHandle(OSSize size, OSHandle * hand)
  1297. {
  1298.     hand->glob = GlobalAlloc(GMEM_FIXED, size);    /* size may be zero */
  1299.     hand->used = size;
  1300.     if (hand->glob == NULL)
  1301.         return GetLastError();
  1302.     else
  1303.         return OS_NOERR;
  1304. }
  1305.  
  1306. /*    resize handle  */
  1307. OSError
  1308. OS_ResizeHandle(OSHandle * hand, OSSize size)
  1309. {
  1310.     /*  reallocating a GMEM_FIXED object returns another GMEM_FIXED object
  1311.        in possibly a new location */
  1312.     HGLOBAL     nglob = GlobalReAlloc(hand->glob, size, GMEM_MOVEABLE);
  1313.  
  1314.     if (nglob == NULL) {
  1315.         hand->glob = NULL;        // assume it's hosed
  1316.         hand->used = 0;
  1317.         return GetLastError();
  1318.     } else {
  1319.         hand->glob = nglob;
  1320.         hand->used = size;
  1321.         return OS_NOERR;
  1322.     }
  1323. }
  1324.  
  1325. /*    lock handle  */
  1326. void       *
  1327. OS_LockHandle(OSHandle * hand)
  1328. {
  1329.     if (GlobalFlags(hand->glob) != GMEM_INVALID_HANDLE)
  1330.         return (void *) hand->glob;
  1331.     else
  1332.         return NULL;
  1333. }
  1334.  
  1335. /*    unlock handle  */
  1336. void
  1337. OS_UnlockHandle(OSHandle * hand)
  1338. {
  1339. }
  1340.  
  1341. /*    free handle  */
  1342. OSError
  1343. OS_FreeHandle(OSHandle * hand)
  1344. {
  1345.     if (GlobalFree(hand->glob) != NULL)
  1346.         return GetLastError();
  1347.     else {
  1348.         hand->glob = NULL;
  1349.         hand->used = 0;
  1350.         return OS_NOERR;
  1351.     }
  1352. }
  1353.  
  1354. /*    get handle size */
  1355. OSError
  1356. OS_GetHandleSize(OSHandle * hand, OSSize * size)
  1357. {
  1358.     if (GlobalFlags(hand->glob) != GMEM_INVALID_HANDLE) {
  1359.         *size = (OSSize) hand->used;    /* don't use GlobalSize since it rounds up */
  1360.         return OS_NOERR;
  1361.     } else {
  1362.         *size = 0;
  1363.         return OS_MEMERR;
  1364.     }
  1365. }
  1366.  
  1367. /*    invalidate handle */
  1368. void
  1369. OS_InvalidateHandle(OSHandle * hand)
  1370. {
  1371.     hand->glob = NULL;
  1372.     hand->used = 0;
  1373. }
  1374.  
  1375. /*    tell whether a handle is valid */
  1376. bool
  1377. OS_ValidHandle(OSHandle * hand)
  1378. {
  1379.     return hand != NULL && hand->glob != NULL;
  1380. }
  1381.  
  1382. /*********************************************/
  1383. #if 0
  1384. #pragma mark -
  1385. #endif
  1386.  
  1387.  
  1388. /*    Shared library routines.  */
  1389.  
  1390. /*    open a shared library  */
  1391. OSError
  1392. OS_OpenLibrary(const OSSpec * spec, OSLibrary * lib)
  1393. {
  1394.     GETSPECSTR(spec, intbuf);
  1395.  
  1396.     *lib = LoadLibrary(intbuf);
  1397.     if (*lib == NULL)
  1398.         return GetLastError();
  1399.     else
  1400.         return OS_NOERR;
  1401. }
  1402.  
  1403. /*    find a symbol in the library */
  1404. OSError
  1405. OS_GetLibrarySymbol(OSLibrary lib, char *name, void **sym)
  1406. {
  1407.     *(FARPROC *) (*sym) = GetProcAddress(lib, name);
  1408.     if (*sym == NULL)
  1409.         return GetLastError();
  1410.     else
  1411.         return OS_NOERR;
  1412. }
  1413.  
  1414. /*    close a shared library */
  1415. OSError
  1416. OS_CloseLibrary(OSLibrary lib)
  1417. {
  1418.     if (!FreeLibrary(lib))
  1419.         return GetLastError();
  1420.     else
  1421.         return OS_NOERR;
  1422. }
  1423.