tclUnixCompat.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:16k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclUnixCompat.c
  3.  *
  4.  * Written by: Zoran Vasiljevic (vasiljevic@users.sourceforge.net).
  5.  *
  6.  * See the file "license.terms" for information on usage and redistribution
  7.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  8.  *
  9.  * RCS: @(#) $Id: tclUnixCompat.c,v 1.1.2.12 2007/12/14 12:45:48 vasiljevic Exp $
  10.  *
  11.  */
  12. #include "tclInt.h"
  13. #include "tclPort.h"
  14. #include <pwd.h>
  15. #include <grp.h>
  16. #include <errno.h>
  17. #include <string.h>
  18. /*
  19.  * Used to pad structures at size'd boundaries
  20.  *
  21.  * This macro assumes that the pointer 'buffer' was created from an
  22.  * aligned pointer by adding the 'length'. If this 'length' was not a
  23.  * multiple of the 'size' the result is unaligned and PadBuffer
  24.  * corrects both the pointer, _and_ the 'length'. The latter means
  25.  * that future increments of 'buffer' by 'length' stay aligned.
  26.  */
  27. #define PadBuffer(buffer, length, size)             
  28.     if (((length) % (size))) {                      
  29. (buffer) += ((size) - ((length) % (size))); 
  30. (length) += ((size) - ((length) % (size))); 
  31.     }
  32. /*
  33.  * Per-thread private storage used to store values
  34.  * returned from MT-unsafe library calls.
  35.  */
  36. #ifdef TCL_THREADS
  37. typedef struct ThreadSpecificData {
  38.     struct passwd pwd;
  39.     char pbuf[2048];
  40.     struct group grp;
  41.     char gbuf[2048];
  42. #if !defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)
  43.     struct hostent hent;
  44.     char hbuf[2048];
  45. #endif
  46. }  ThreadSpecificData;
  47. static Tcl_ThreadDataKey dataKey;
  48. #if ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && 
  49.      (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || 
  50.       !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || 
  51.       !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R)
  52. /*
  53.  * Mutex to lock access to MT-unsafe calls. This is just to protect
  54.  * our own usage. It does not protect us from others calling the
  55.  * same functions without (or using some different) lock.
  56.  */
  57. static Tcl_Mutex compatLock;
  58. /*
  59.  *---------------------------------------------------------------------------
  60.  *
  61.  * CopyArray --
  62.  *
  63.  *      Copies array of NULL-terminated or fixed-length strings
  64.  *      to the private buffer, honouring the size of the buffer.
  65.  *
  66.  * Results:
  67.  *      Number of bytes copied on success or -1 on error (errno = ERANGE)
  68.  *
  69.  * Side effects:
  70.  *      None.
  71.  *
  72.  *---------------------------------------------------------------------------
  73.  */
  74. static int
  75. CopyArray(char **src, int elsize, char *buf, int buflen)
  76. {
  77.     int i, j, len = 0;
  78.     char *p, **new;
  79.     if (src == NULL) {
  80. return 0;
  81.     }
  82.     for (i = 0; src[i] != NULL; i++) {
  83. /* Empty loop to count howmany */
  84.     }
  85.     if ((sizeof(char *)*(i + 1)) >  buflen) {
  86. return -1;
  87.     }
  88.     len = (sizeof(char *)*(i + 1)); /* Leave place for the array */
  89.     new = (char **)buf;
  90.     p = buf + (sizeof(char *)*(i + 1));
  91.     for (j = 0; j < i; j++) {
  92. if (elsize < 0) {
  93.     len += strlen(src[j]) + 1;
  94. } else {
  95.     len += elsize;
  96. }
  97. if (len > buflen) {
  98.     return -1;
  99. }
  100. if (elsize < 0) {
  101.     strcpy(p, src[j]);
  102. } else {
  103.     memcpy(p, src[j], elsize);
  104. }
  105. new[j] = p;
  106. p = buf + len;
  107.     }
  108.     new[j] = NULL;
  109.     return len;
  110. }
  111. /*
  112.  *---------------------------------------------------------------------------
  113.  *
  114.  * CopyString --
  115.  *
  116.  *      Copies a NULL-terminated string to the private buffer,
  117.  *      honouring the size of the buffer
  118.  *
  119.  * Results:
  120.  *      0 success or -1 on error (errno = ERANGE)
  121.  *
  122.  * Side effects:
  123.  *      None
  124.  *
  125.  *---------------------------------------------------------------------------
  126.  */
  127. static int
  128. CopyString(char *src, char *buf, int buflen)
  129. {
  130.     int len = 0;
  131.     if (src != NULL) {
  132. len += strlen(src) + 1;
  133. if (len > buflen) {
  134.     return -1;
  135. }
  136. strcpy(buf, src);
  137.     }
  138.     return len;
  139. }
  140. #endif /* ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && 
  141.    (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || 
  142.     !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || 
  143.     !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) */
  144. #if (!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && 
  145.     (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR))
  146. /*
  147.  *---------------------------------------------------------------------------
  148.  *
  149.  * CopyHostnent --
  150.  *
  151.  *      Copies string fields of the hostnent structure to the
  152.  *      private buffer, honouring the size of the buffer.
  153.  *
  154.  * Results:
  155.  *      Number of bytes copied on success or -1 on error (errno = ERANGE)
  156.  *
  157.  * Side effects:
  158.  *      None
  159.  *
  160.  *---------------------------------------------------------------------------
  161.  */
  162. static int
  163. CopyHostent(struct hostent *tgtPtr, char *buf, int buflen)
  164. {
  165.     char *p = buf;
  166.     int copied, len = 0;
  167.     copied = CopyString(tgtPtr->h_name, p, buflen - len);
  168.     if (copied == -1) {
  169.     range:
  170. errno = ERANGE;
  171. return -1;
  172.     }
  173.     tgtPtr->h_name = (copied > 0) ? p : NULL;
  174.     len += copied;
  175.     p = buf + len;
  176.     PadBuffer(p, len, sizeof(char *));
  177.     copied = CopyArray(tgtPtr->h_aliases, -1, p, buflen - len);
  178.     if (copied == -1) {
  179. goto range;
  180.     }
  181.     tgtPtr->h_aliases = (copied > 0) ? (char **)p : NULL;
  182.     len += copied;
  183.     p += len;
  184.     PadBuffer(p, len, sizeof(char *));
  185.     copied = CopyArray(tgtPtr->h_addr_list, tgtPtr->h_length, p, buflen - len);
  186.     if (copied == -1) {
  187. goto range;
  188.     }
  189.     tgtPtr->h_addr_list = (copied > 0) ? (char **)p : NULL;
  190.     return 0;
  191. }
  192. #endif /* (!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && 
  193.   (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)) */
  194. #if !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R)
  195. /*
  196.  *---------------------------------------------------------------------------
  197.  *
  198.  * CopyPwd --
  199.  *
  200.  *      Copies string fields of the passwd structure to the
  201.  *      private buffer, honouring the size of the buffer.
  202.  *
  203.  * Results:
  204.  *      0 on success or -1 on error (errno = ERANGE)
  205.  *
  206.  * Side effects:
  207.  *      We are not copying the gecos field as it may not be supported
  208.  *      on all platforms.
  209.  *
  210.  *---------------------------------------------------------------------------
  211.  */
  212. static int
  213. CopyPwd(struct passwd *tgtPtr, char *buf, int buflen)
  214. {
  215.     char *p = buf;
  216.     int copied, len = 0;
  217.     copied = CopyString(tgtPtr->pw_name, p, buflen - len);
  218.     if (copied == -1) {
  219.     range:
  220. errno = ERANGE;
  221. return -1;
  222.     }
  223.     tgtPtr->pw_name = (copied > 0) ? p : NULL;
  224.     len += copied;
  225.     p = buf + len;
  226.     copied = CopyString(tgtPtr->pw_passwd, p, buflen - len);
  227.     if (copied == -1) {
  228. goto range;
  229.     }
  230.     tgtPtr->pw_passwd = (copied > 0) ? p : NULL;
  231.     len += copied;
  232.     p = buf + len;
  233.     copied = CopyString(tgtPtr->pw_dir, p, buflen - len);
  234.     if (copied == -1) {
  235. goto range;
  236.     }
  237.     tgtPtr->pw_dir = (copied > 0) ? p : NULL;
  238.     len += copied;
  239.     p = buf + len;
  240.     copied = CopyString(tgtPtr->pw_shell, p, buflen - len);
  241.     if (copied == -1) {
  242. goto range;
  243.     }
  244.     tgtPtr->pw_shell = (copied > 0) ? p : NULL;
  245.     return 0;
  246. }
  247. #endif /* !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) */
  248. #if !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R)
  249. /*
  250.  *---------------------------------------------------------------------------
  251.  *
  252.  * CopyGrp --
  253.  *
  254.  *      Copies string fields of the group structure to the
  255.  *      private buffer, honouring the size of the buffer.
  256.  *
  257.  * Results:
  258.  *      0 on success or -1 on error (errno = ERANGE)
  259.  *
  260.  * Side effects:
  261.  *      None.
  262.  *
  263.  *---------------------------------------------------------------------------
  264.  */
  265. static int
  266. CopyGrp(struct group *tgtPtr, char *buf, int buflen)
  267. {
  268.     register char *p = buf;
  269.     register int copied, len = 0;
  270.     /* Copy username */
  271.     copied = CopyString(tgtPtr->gr_name, p, buflen - len);
  272.     if (copied == -1) {
  273.     range:
  274. errno = ERANGE;
  275. return -1;
  276.     }
  277.     tgtPtr->gr_name = (copied > 0) ? p : NULL;
  278.     len += copied;
  279.     p = buf + len;
  280.     /* Copy password */
  281.     copied = CopyString(tgtPtr->gr_passwd, p, buflen - len);
  282.     if (copied == -1) {
  283. goto range;
  284.     }
  285.     tgtPtr->gr_passwd = (copied > 0) ? p : NULL;
  286.     len += copied;
  287.     p = buf + len;
  288.     /* Copy group members */
  289.     PadBuffer(p, len, sizeof(char *));
  290.     copied = CopyArray((char **)tgtPtr->gr_mem, -1, p, buflen - len);
  291.     if (copied == -1) {
  292. goto range;
  293.     }
  294.     tgtPtr->gr_mem = (copied > 0) ? (char **)p : NULL;
  295.     return 0;
  296. }
  297. #endif /* !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R) */
  298. #endif /* TCL_THREADS */
  299. /*
  300.  *---------------------------------------------------------------------------
  301.  *
  302.  * TclpGetPwNam --
  303.  *
  304.  *      Thread-safe wrappers for getpwnam().
  305.  *      See "man getpwnam" for more details.
  306.  *
  307.  * Results:
  308.  *      Pointer to struct passwd on success or NULL on error.
  309.  *
  310.  * Side effects:
  311.  *      None.
  312.  *
  313.  *---------------------------------------------------------------------------
  314.  */
  315. struct passwd *
  316. TclpGetPwNam(const char *name)
  317. {
  318. #if !defined(TCL_THREADS)
  319.     return getpwnam(name);
  320. #else
  321.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  322. #if defined(HAVE_GETPWNAM_R_5)
  323.     struct passwd *pwPtr = NULL;
  324.     return (getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
  325.        &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
  326. #elif defined(HAVE_GETPWNAM_R_4)
  327.     return getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
  328. #else
  329.     struct passwd *pwPtr;
  330.     Tcl_MutexLock(&compatLock);
  331.     pwPtr = getpwnam(name);
  332.     if (pwPtr != NULL) {
  333. tsdPtr->pwd = *pwPtr;
  334. pwPtr = &tsdPtr->pwd;
  335. if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
  336.     pwPtr = NULL;
  337. }
  338.     }
  339.     Tcl_MutexUnlock(&compatLock);
  340.     return pwPtr;
  341. #endif
  342.     return NULL; /* Not reached */
  343. #endif /* TCL_THREADS */
  344. }
  345. /*
  346.  *---------------------------------------------------------------------------
  347.  *
  348.  * TclpGetPwUid --
  349.  *
  350.  *      Thread-safe wrappers for getpwuid().
  351.  *      See "man getpwuid" for more details.
  352.  *
  353.  * Results:
  354.  *      Pointer to struct passwd on success or NULL on error.
  355.  *
  356.  * Side effects:
  357.  *      None.
  358.  *
  359.  *---------------------------------------------------------------------------
  360.  */
  361. struct passwd *
  362. TclpGetPwUid(uid_t uid)
  363. {
  364. #if !defined(TCL_THREADS)
  365.     return getpwuid(uid);
  366. #else
  367.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  368. #if defined(HAVE_GETPWUID_R_5)
  369.     struct passwd *pwPtr = NULL;
  370.     return (getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf),
  371.        &pwPtr) == 0 && pwPtr != NULL) ? &tsdPtr->pwd : NULL;
  372. #elif defined(HAVE_GETPWUID_R_4)
  373.     return getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
  374. #else
  375.     struct passwd *pwPtr;
  376.     Tcl_MutexLock(&compatLock);
  377.     pwPtr = getpwuid(uid);
  378.     if (pwPtr != NULL) {
  379. tsdPtr->pwd = *pwPtr;
  380. pwPtr = &tsdPtr->pwd;
  381. if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
  382.     pwPtr = NULL;
  383. }
  384.     }
  385.     Tcl_MutexUnlock(&compatLock);
  386.     return pwPtr;
  387. #endif
  388.     return NULL; /* Not reached */
  389. #endif /* TCL_THREADS */
  390. }
  391. /*
  392.  *---------------------------------------------------------------------------
  393.  *
  394.  * TclpGetGrNam --
  395.  *
  396.  *      Thread-safe wrappers for getgrnam().
  397.  *      See "man getgrnam" for more details.
  398.  *
  399.  * Results:
  400.  *      Pointer to struct group on success or NULL on error.
  401.  *
  402.  * Side effects:
  403.  *      None.
  404.  *
  405.  *---------------------------------------------------------------------------
  406.  */
  407. struct group *
  408. TclpGetGrNam(const char *name)
  409. {
  410. #if !defined(TCL_THREADS)
  411.     return getgrnam(name);
  412. #else
  413.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  414. #if defined(HAVE_GETGRNAM_R_5)
  415.     struct group *grPtr = NULL;
  416.     return (getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
  417.        &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
  418. #elif defined(HAVE_GETGRNAM_R_4)
  419.     return getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
  420. #else
  421.     struct group *grPtr;
  422.     Tcl_MutexLock(&compatLock);
  423.     grPtr = getgrnam(name);
  424.     if (grPtr != NULL) {
  425. tsdPtr->grp = *grPtr;
  426. grPtr = &tsdPtr->grp;
  427. if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
  428.     grPtr = NULL;
  429. }
  430.     }
  431.     Tcl_MutexUnlock(&compatLock);
  432.     return grPtr;
  433. #endif
  434.     return NULL; /* Not reached */
  435. #endif /* TCL_THREADS */
  436. }
  437. /*
  438.  *---------------------------------------------------------------------------
  439.  *
  440.  * TclpGetGrGid --
  441.  *
  442.  *      Thread-safe wrappers for getgrgid().
  443.  *      See "man getgrgid" for more details.
  444.  *
  445.  * Results:
  446.  *      Pointer to struct group on success or NULL on error.
  447.  *
  448.  * Side effects:
  449.  *      None.
  450.  *
  451.  *---------------------------------------------------------------------------
  452.  */
  453. struct group *
  454. TclpGetGrGid(gid_t gid)
  455. {
  456. #if !defined(TCL_THREADS)
  457.     return getgrgid(gid);
  458. #else
  459.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  460. #if defined(HAVE_GETGRGID_R_5)
  461.     struct group *grPtr = NULL;
  462.     return (getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf),
  463.        &grPtr) == 0 && grPtr != NULL) ? &tsdPtr->grp : NULL;
  464. #elif defined(HAVE_GETGRGID_R_4)
  465.     return getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
  466. #else
  467.     struct group *grPtr;
  468.     Tcl_MutexLock(&compatLock);
  469.     grPtr = getgrgid(gid);
  470.     if (grPtr != NULL) {
  471. tsdPtr->grp = *grPtr;
  472. grPtr = &tsdPtr->grp;
  473. if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
  474.     grPtr = NULL;
  475. }
  476.     }
  477.     Tcl_MutexUnlock(&compatLock);
  478.     return grPtr;
  479. #endif
  480.     return NULL; /* Not reached */
  481. #endif /* TCL_THREADS */
  482. }
  483. /*
  484.  *---------------------------------------------------------------------------
  485.  *
  486.  * TclpGetHostByName --
  487.  *
  488.  *      Thread-safe wrappers for gethostbyname().
  489.  *      See "man gethostbyname" for more details.
  490.  *
  491.  * Results:
  492.  *      Pointer to struct hostent on success or NULL on error.
  493.  *
  494.  * Side effects:
  495.  *      None.
  496.  *
  497.  *---------------------------------------------------------------------------
  498.  */
  499. struct hostent *
  500. TclpGetHostByName(const char *name)
  501. {
  502. #if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYNAME)
  503.     return gethostbyname(name);
  504. #else
  505.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  506. #if defined(HAVE_GETHOSTBYNAME_R_5)
  507.     int h_errno;
  508.     return gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
  509.    sizeof(tsdPtr->hbuf), &h_errno);
  510. #elif defined(HAVE_GETHOSTBYNAME_R_6)
  511.     struct hostent *hePtr;
  512.     int result, h_errno;
  513.     result = gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
  514.             sizeof(tsdPtr->hbuf), &hePtr, &h_errno);
  515.     return (result == 0) ? hePtr : NULL;
  516. #elif defined(HAVE_GETHOSTBYNAME_R_3)
  517.     struct hostent_data data;
  518.     return (gethostbyname_r(name, &tsdPtr->hent, &data) == 0) ?
  519. &tsdPtr->hent : NULL;
  520. #else
  521.     struct hostent *hePtr;
  522.     Tcl_MutexLock(&compatLock);
  523.     hePtr = gethostbyname(name);
  524.     if (hePtr != NULL) {
  525. tsdPtr->hent = *hePtr;
  526. hePtr = &tsdPtr->hent;
  527. if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
  528. sizeof(tsdPtr->hbuf)) == -1) {
  529.     hePtr = NULL;
  530. }
  531.     }
  532.     Tcl_MutexUnlock(&compatLock);
  533.     return hePtr;
  534. #endif
  535.     return NULL; /* Not reached */
  536. #endif /* TCL_THREADS */
  537. }
  538. /*
  539.  *---------------------------------------------------------------------------
  540.  *
  541.  * TclpGetHostByAddr --
  542.  *
  543.  *      Thread-safe wrappers for gethostbyaddr().
  544.  *      See "man gethostbyaddr" for more details.
  545.  *
  546.  * Results:
  547.  *      Pointer to struct hostent on success or NULL on error.
  548.  *
  549.  * Side effects:
  550.  *      None.
  551.  *
  552.  *---------------------------------------------------------------------------
  553.  */
  554. struct hostent *
  555. TclpGetHostByAddr(const char *addr, int length, int type)
  556. {
  557. #if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYADDR)
  558.     return gethostbyaddr(addr, length, type);
  559. #else
  560.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  561. #if defined(HAVE_GETHOSTBYADDR_R_7)
  562.     int h_errno;
  563.     return gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
  564.    sizeof(tsdPtr->hbuf), &h_errno);
  565. #elif defined(HAVE_GETHOSTBYADDR_R_8)
  566.     struct hostent *hePtr;
  567.     int h_errno;
  568.     return (gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
  569.     sizeof(tsdPtr->hbuf), &hePtr, &h_errno) == 0) ?
  570. &tsdPtr->hent : NULL;
  571. #else
  572.     struct hostent *hePtr;
  573.     Tcl_MutexLock(&compatLock);
  574.     hePtr = gethostbyaddr(addr, length, type);
  575.     if (hePtr != NULL) {
  576. tsdPtr->hent = *hePtr;
  577. hePtr = &tsdPtr->hent;
  578. if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
  579. sizeof(tsdPtr->hbuf)) == -1) {
  580.     hePtr = NULL;
  581. }
  582.     }
  583.     Tcl_MutexUnlock(&compatLock);
  584.     return hePtr;
  585. #endif
  586.     return NULL; /* Not reached */
  587. #endif /* TCL_THREADS */
  588. }