summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixCompat.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixCompat.c')
-rw-r--r--unix/tclUnixCompat.c1022
1 files changed, 1022 insertions, 0 deletions
diff --git a/unix/tclUnixCompat.c b/unix/tclUnixCompat.c
new file mode 100644
index 0000000..ea6067e
--- /dev/null
+++ b/unix/tclUnixCompat.c
@@ -0,0 +1,1022 @@
+/*
+ * tclUnixCompat.c
+ *
+ * Written by: Zoran Vasiljevic (vasiljevic@users.sourceforge.net).
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tclInt.h"
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <string.h>
+
+/*
+ * See also: SC_BLOCKING_STYLE in unix/tcl.m4
+ */
+
+#ifdef USE_FIONBIO
+# ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h> /* For FIONBIO. */
+# endif
+# ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+# endif
+#endif /* USE_FIONBIO */
+
+/*
+ * Used to pad structures at size'd boundaries
+ *
+ * This macro assumes that the pointer 'buffer' was created from an aligned
+ * pointer by adding the 'length'. If this 'length' was not a multiple of the
+ * 'size' the result is unaligned and PadBuffer corrects both the pointer,
+ * _and_ the 'length'. The latter means that future increments of 'buffer' by
+ * 'length' stay aligned.
+ */
+
+#define PadBuffer(buffer, length, size) \
+ if (((length) % (size))) { \
+ (buffer) += ((size) - ((length) % (size))); \
+ (length) += ((size) - ((length) % (size))); \
+ }
+
+/*
+ * Per-thread private storage used to store values returned from MT-unsafe
+ * library calls.
+ */
+
+#ifdef TCL_THREADS
+
+typedef struct {
+ struct passwd pwd;
+#if defined(HAVE_GETPWNAM_R_5) || defined(HAVE_GETPWUID_R_5)
+#define NEED_PW_CLEANER 1
+ char *pbuf;
+ int pbuflen;
+#else
+ char pbuf[2048];
+#endif
+
+ struct group grp;
+#if defined(HAVE_GETGRNAM_R_5) || defined(HAVE_GETGRGID_R_5)
+#define NEED_GR_CLEANER 1
+ char *gbuf;
+ int gbuflen;
+#else
+ char gbuf[2048];
+#endif
+
+#if !defined(HAVE_MTSAFE_GETHOSTBYNAME) || !defined(HAVE_MTSAFE_GETHOSTBYADDR)
+ struct hostent hent;
+ char hbuf[2048];
+#endif
+} ThreadSpecificData;
+static Tcl_ThreadDataKey dataKey;
+
+#if ((!defined(HAVE_GETHOSTBYNAME_R) || !defined(HAVE_GETHOSTBYADDR_R)) && \
+ (!defined(HAVE_MTSAFE_GETHOSTBYNAME) || \
+ !defined(HAVE_MTSAFE_GETHOSTBYADDR))) || \
+ !defined(HAVE_GETPWNAM_R) || !defined(HAVE_GETPWUID_R) || \
+ !defined(HAVE_GETGRNAM_R) || !defined(HAVE_GETGRGID_R)
+/*
+ * Mutex to lock access to MT-unsafe calls. This is just to protect our own
+ * usage. It does not protect us from others calling the same functions
+ * without (or using some different) lock.
+ */
+
+static Tcl_Mutex compatLock;
+
+/*
+ * Helper function declarations. Note that these are only used if needed and
+ * only defined if used (via the NEED_* macros).
+ */
+
+#undef NEED_COPYARRAY
+#undef NEED_COPYGRP
+#undef NEED_COPYHOSTENT
+#undef NEED_COPYPWD
+#undef NEED_COPYSTRING
+
+#if !defined(HAVE_GETGRNAM_R_5) && !defined(HAVE_GETGRNAM_R_4)
+#define NEED_COPYGRP 1
+static int CopyGrp(struct group *tgtPtr, char *buf, int buflen);
+#endif
+
+#if !defined(HAVE_GETPWNAM_R_5) && !defined(HAVE_GETPWNAM_R_4)
+#define NEED_COPYPWD 1
+static int CopyPwd(struct passwd *tgtPtr, char *buf, int buflen);
+#endif
+
+static int CopyArray(char **src, int elsize, char *buf,
+ int buflen);
+static int CopyHostent(struct hostent *tgtPtr, char *buf,
+ int buflen);
+static int CopyString(const char *src, char *buf, int buflen);
+
+#endif
+
+#ifdef NEED_PW_CLEANER
+static void FreePwBuf(ClientData ignored);
+#endif
+#ifdef NEED_GR_CLEANER
+static void FreeGrBuf(ClientData ignored);
+#endif
+#endif /* TCL_THREADS */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclUnixSetBlockingMode --
+ *
+ * Set the blocking mode of a file descriptor.
+ *
+ * Results:
+ *
+ * 0 on success, -1 (with errno set) on error.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TclUnixSetBlockingMode(
+ int fd, /* File descriptor */
+ int mode) /* Either TCL_MODE_BLOCKING or
+ * TCL_MODE_NONBLOCKING. */
+{
+#ifndef USE_FIONBIO
+ int flags = fcntl(fd, F_GETFL);
+
+ if (mode == TCL_MODE_BLOCKING) {
+ flags &= ~O_NONBLOCK;
+ } else {
+ flags |= O_NONBLOCK;
+ }
+ return fcntl(fd, F_SETFL, flags);
+#else /* USE_FIONBIO */
+ int state = (mode == TCL_MODE_NONBLOCKING);
+
+ return ioctl(fd, FIONBIO, &state);
+#endif /* !USE_FIONBIO */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpGetPwNam --
+ *
+ * Thread-safe wrappers for getpwnam(). See "man getpwnam" for more
+ * details.
+ *
+ * Results:
+ * Pointer to struct passwd on success or NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+struct passwd *
+TclpGetPwNam(
+ const char *name)
+{
+#if !defined(TCL_THREADS)
+ return getpwnam(name);
+#else
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+#if defined(HAVE_GETPWNAM_R_5)
+ struct passwd *pwPtr = NULL;
+
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->pbuf == NULL) {
+ tsdPtr->pbuflen = (int) sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (tsdPtr->pbuflen < 1) {
+ tsdPtr->pbuflen = 1024;
+ }
+ tsdPtr->pbuf = ckalloc(tsdPtr->pbuflen);
+ Tcl_CreateThreadExitHandler(FreePwBuf, NULL);
+ }
+ while (1) {
+ int e = getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, tsdPtr->pbuflen,
+ &pwPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->pbuflen *= 2;
+ tsdPtr->pbuf = ckrealloc(tsdPtr->pbuf, tsdPtr->pbuflen);
+ }
+ return (pwPtr != NULL ? &tsdPtr->pwd : NULL);
+
+#elif defined(HAVE_GETPWNAM_R_4)
+ return getpwnam_r(name, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
+
+#else
+ struct passwd *pwPtr;
+
+ Tcl_MutexLock(&compatLock);
+ pwPtr = getpwnam(name);
+ if (pwPtr != NULL) {
+ tsdPtr->pwd = *pwPtr;
+ pwPtr = &tsdPtr->pwd;
+ if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
+ pwPtr = NULL;
+ }
+ }
+ Tcl_MutexUnlock(&compatLock);
+ return pwPtr;
+#endif
+
+ return NULL; /* Not reached. */
+#endif /* TCL_THREADS */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpGetPwUid --
+ *
+ * Thread-safe wrappers for getpwuid(). See "man getpwuid" for more
+ * details.
+ *
+ * Results:
+ * Pointer to struct passwd on success or NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+struct passwd *
+TclpGetPwUid(
+ uid_t uid)
+{
+#if !defined(TCL_THREADS)
+ return getpwuid(uid);
+#else
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+#if defined(HAVE_GETPWUID_R_5)
+ struct passwd *pwPtr = NULL;
+
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->pbuf == NULL) {
+ tsdPtr->pbuflen = (int) sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (tsdPtr->pbuflen < 1) {
+ tsdPtr->pbuflen = 1024;
+ }
+ tsdPtr->pbuf = ckalloc(tsdPtr->pbuflen);
+ Tcl_CreateThreadExitHandler(FreePwBuf, NULL);
+ }
+ while (1) {
+ int e = getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, tsdPtr->pbuflen,
+ &pwPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->pbuflen *= 2;
+ tsdPtr->pbuf = ckrealloc(tsdPtr->pbuf, tsdPtr->pbuflen);
+ }
+ return (pwPtr != NULL ? &tsdPtr->pwd : NULL);
+
+#elif defined(HAVE_GETPWUID_R_4)
+ return getpwuid_r(uid, &tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf));
+
+#else
+ struct passwd *pwPtr;
+
+ Tcl_MutexLock(&compatLock);
+ pwPtr = getpwuid(uid);
+ if (pwPtr != NULL) {
+ tsdPtr->pwd = *pwPtr;
+ pwPtr = &tsdPtr->pwd;
+ if (CopyPwd(&tsdPtr->pwd, tsdPtr->pbuf, sizeof(tsdPtr->pbuf)) == -1) {
+ pwPtr = NULL;
+ }
+ }
+ Tcl_MutexUnlock(&compatLock);
+ return pwPtr;
+#endif
+
+ return NULL; /* Not reached. */
+#endif /* TCL_THREADS */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreePwBuf --
+ *
+ * Helper that is used to dispose of space allocated and referenced from
+ * the ThreadSpecificData for user entries. (Darn that baroque POSIX
+ * reentrant interface.)
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_PW_CLEANER
+static void
+FreePwBuf(
+ ClientData ignored)
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ ckfree(tsdPtr->pbuf);
+}
+#endif /* NEED_PW_CLEANER */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpGetGrNam --
+ *
+ * Thread-safe wrappers for getgrnam(). See "man getgrnam" for more
+ * details.
+ *
+ * Results:
+ * Pointer to struct group on success or NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+struct group *
+TclpGetGrNam(
+ const char *name)
+{
+#if !defined(TCL_THREADS)
+ return getgrnam(name);
+#else
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+#if defined(HAVE_GETGRNAM_R_5)
+ struct group *grPtr = NULL;
+
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->gbuf == NULL) {
+ tsdPtr->gbuflen = (int) sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (tsdPtr->gbuflen < 1) {
+ tsdPtr->gbuflen = 1024;
+ }
+ tsdPtr->gbuf = ckalloc(tsdPtr->gbuflen);
+ Tcl_CreateThreadExitHandler(FreeGrBuf, NULL);
+ }
+ while (1) {
+ int e = getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, tsdPtr->gbuflen,
+ &grPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->gbuflen *= 2;
+ tsdPtr->gbuf = ckrealloc(tsdPtr->gbuf, tsdPtr->gbuflen);
+ }
+ return (grPtr != NULL ? &tsdPtr->grp : NULL);
+
+#elif defined(HAVE_GETGRNAM_R_4)
+ return getgrnam_r(name, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
+
+#else
+ struct group *grPtr;
+
+ Tcl_MutexLock(&compatLock);
+ grPtr = getgrnam(name);
+ if (grPtr != NULL) {
+ tsdPtr->grp = *grPtr;
+ grPtr = &tsdPtr->grp;
+ if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
+ grPtr = NULL;
+ }
+ }
+ Tcl_MutexUnlock(&compatLock);
+ return grPtr;
+#endif
+
+ return NULL; /* Not reached. */
+#endif /* TCL_THREADS */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpGetGrGid --
+ *
+ * Thread-safe wrappers for getgrgid(). See "man getgrgid" for more
+ * details.
+ *
+ * Results:
+ * Pointer to struct group on success or NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+struct group *
+TclpGetGrGid(
+ gid_t gid)
+{
+#if !defined(TCL_THREADS)
+ return getgrgid(gid);
+#else
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+#if defined(HAVE_GETGRGID_R_5)
+ struct group *grPtr = NULL;
+
+ /*
+ * How to allocate a buffer of the right initial size. If you want the
+ * gory detail, see http://www.opengroup.org/austin/docs/austin_328.txt
+ * and weep.
+ */
+
+ if (tsdPtr->gbuf == NULL) {
+ tsdPtr->gbuflen = (int) sysconf(_SC_GETGR_R_SIZE_MAX);
+ if (tsdPtr->gbuflen < 1) {
+ tsdPtr->gbuflen = 1024;
+ }
+ tsdPtr->gbuf = ckalloc(tsdPtr->gbuflen);
+ Tcl_CreateThreadExitHandler(FreeGrBuf, NULL);
+ }
+ while (1) {
+ int e = getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, tsdPtr->gbuflen,
+ &grPtr);
+
+ if (e == 0) {
+ break;
+ } else if (e != ERANGE) {
+ return NULL;
+ }
+ tsdPtr->gbuflen *= 2;
+ tsdPtr->gbuf = ckrealloc(tsdPtr->gbuf, tsdPtr->gbuflen);
+ }
+ return (grPtr != NULL ? &tsdPtr->grp : NULL);
+
+#elif defined(HAVE_GETGRGID_R_4)
+ return getgrgid_r(gid, &tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf));
+
+#else
+ struct group *grPtr;
+
+ Tcl_MutexLock(&compatLock);
+ grPtr = getgrgid(gid);
+ if (grPtr != NULL) {
+ tsdPtr->grp = *grPtr;
+ grPtr = &tsdPtr->grp;
+ if (CopyGrp(&tsdPtr->grp, tsdPtr->gbuf, sizeof(tsdPtr->gbuf)) == -1) {
+ grPtr = NULL;
+ }
+ }
+ Tcl_MutexUnlock(&compatLock);
+ return grPtr;
+#endif
+
+ return NULL; /* Not reached. */
+#endif /* TCL_THREADS */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * FreeGrBuf --
+ *
+ * Helper that is used to dispose of space allocated and referenced from
+ * the ThreadSpecificData for group entries. (Darn that baroque POSIX
+ * reentrant interface.)
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_GR_CLEANER
+static void
+FreeGrBuf(
+ ClientData ignored)
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ ckfree(tsdPtr->gbuf);
+}
+#endif /* NEED_GR_CLEANER */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpGetHostByName --
+ *
+ * Thread-safe wrappers for gethostbyname(). See "man gethostbyname" for
+ * more details.
+ *
+ * Results:
+ * Pointer to struct hostent on success or NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+struct hostent *
+TclpGetHostByName(
+ const char *name)
+{
+#if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYNAME)
+ return gethostbyname(name);
+#else
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+#if defined(HAVE_GETHOSTBYNAME_R_5)
+ int h_errno;
+
+ return gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
+ sizeof(tsdPtr->hbuf), &h_errno);
+
+#elif defined(HAVE_GETHOSTBYNAME_R_6)
+ struct hostent *hePtr = NULL;
+ int h_errno, result;
+
+ result = gethostbyname_r(name, &tsdPtr->hent, tsdPtr->hbuf,
+ sizeof(tsdPtr->hbuf), &hePtr, &h_errno);
+ return (result == 0) ? hePtr : NULL;
+
+#elif defined(HAVE_GETHOSTBYNAME_R_3)
+ struct hostent_data data;
+
+ return (gethostbyname_r(name, &tsdPtr->hent, &data) == 0)
+ ? &tsdPtr->hent : NULL;
+
+#else
+#define NEED_COPYHOSTENT 1
+ struct hostent *hePtr;
+
+ Tcl_MutexLock(&compatLock);
+ hePtr = gethostbyname(name);
+ if (hePtr != NULL) {
+ tsdPtr->hent = *hePtr;
+ hePtr = &tsdPtr->hent;
+ if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
+ sizeof(tsdPtr->hbuf)) == -1) {
+ hePtr = NULL;
+ }
+ }
+ Tcl_MutexUnlock(&compatLock);
+ return hePtr;
+#endif
+
+ return NULL; /* Not reached. */
+#endif /* TCL_THREADS */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclpGetHostByAddr --
+ *
+ * Thread-safe wrappers for gethostbyaddr(). See "man gethostbyaddr" for
+ * more details.
+ *
+ * Results:
+ * Pointer to struct hostent on success or NULL on error.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+struct hostent *
+TclpGetHostByAddr(
+ const char *addr,
+ int length,
+ int type)
+{
+#if !defined(TCL_THREADS) || defined(HAVE_MTSAFE_GETHOSTBYADDR)
+ return gethostbyaddr(addr, length, type);
+#else
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+#if defined(HAVE_GETHOSTBYADDR_R_7)
+ int h_errno;
+
+ return gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
+ sizeof(tsdPtr->hbuf), &h_errno);
+
+#elif defined(HAVE_GETHOSTBYADDR_R_8)
+ struct hostent *hePtr;
+ int h_errno;
+
+ return (gethostbyaddr_r(addr, length, type, &tsdPtr->hent, tsdPtr->hbuf,
+ sizeof(tsdPtr->hbuf), &hePtr, &h_errno) == 0)
+ ? &tsdPtr->hent : NULL;
+#else
+#define NEED_COPYHOSTENT 1
+ struct hostent *hePtr;
+
+ Tcl_MutexLock(&compatLock);
+ hePtr = gethostbyaddr(addr, length, type);
+ if (hePtr != NULL) {
+ tsdPtr->hent = *hePtr;
+ hePtr = &tsdPtr->hent;
+ if (CopyHostent(&tsdPtr->hent, tsdPtr->hbuf,
+ sizeof(tsdPtr->hbuf)) == -1) {
+ hePtr = NULL;
+ }
+ }
+ Tcl_MutexUnlock(&compatLock);
+ return hePtr;
+#endif
+
+ return NULL; /* Not reached. */
+#endif /* TCL_THREADS */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CopyGrp --
+ *
+ * Copies string fields of the group structure to the private buffer,
+ * honouring the size of the buffer.
+ *
+ * Results:
+ * 0 on success or -1 on error (errno = ERANGE).
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_COPYGRP
+#define NEED_COPYARRAY 1
+#define NEED_COPYSTRING 1
+
+static int
+CopyGrp(
+ struct group *tgtPtr,
+ char *buf,
+ int buflen)
+{
+ register char *p = buf;
+ register int copied, len = 0;
+
+ /*
+ * Copy username.
+ */
+
+ copied = CopyString(tgtPtr->gr_name, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->gr_name = (copied > 0) ? p : NULL;
+ len += copied;
+ p = buf + len;
+
+ /*
+ * Copy password.
+ */
+
+ copied = CopyString(tgtPtr->gr_passwd, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->gr_passwd = (copied > 0) ? p : NULL;
+ len += copied;
+ p = buf + len;
+
+ /*
+ * Copy group members.
+ */
+
+ PadBuffer(p, len, sizeof(char *));
+ copied = CopyArray((char **)tgtPtr->gr_mem, -1, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->gr_mem = (copied > 0) ? (char **)p : NULL;
+
+ return 0;
+
+ range:
+ errno = ERANGE;
+ return -1;
+}
+#endif /* NEED_COPYGRP */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CopyHostent --
+ *
+ * Copies string fields of the hostnent structure to the private buffer,
+ * honouring the size of the buffer.
+ *
+ * Results:
+ * Number of bytes copied on success or -1 on error (errno = ERANGE)
+ *
+ * Side effects:
+ * None
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_COPYHOSTENT
+#define NEED_COPYSTRING 1
+#define NEED_COPYARRAY 1
+
+static int
+CopyHostent(
+ struct hostent *tgtPtr,
+ char *buf,
+ int buflen)
+{
+ char *p = buf;
+ int copied, len = 0;
+
+ copied = CopyString(tgtPtr->h_name, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->h_name = (copied > 0) ? p : NULL;
+ len += copied;
+ p = buf + len;
+
+ PadBuffer(p, len, sizeof(char *));
+ copied = CopyArray(tgtPtr->h_aliases, -1, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->h_aliases = (copied > 0) ? (char **)p : NULL;
+ len += copied;
+ p += len;
+
+ PadBuffer(p, len, sizeof(char *));
+ copied = CopyArray(tgtPtr->h_addr_list, tgtPtr->h_length, p, buflen-len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->h_addr_list = (copied > 0) ? (char **)p : NULL;
+
+ return 0;
+
+ range:
+ errno = ERANGE;
+ return -1;
+}
+#endif /* NEED_COPYHOSTENT */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CopyPwd --
+ *
+ * Copies string fields of the passwd structure to the private buffer,
+ * honouring the size of the buffer.
+ *
+ * Results:
+ * 0 on success or -1 on error (errno = ERANGE).
+ *
+ * Side effects:
+ * We are not copying the gecos field as it may not be supported on all
+ * platforms.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_COPYPWD
+#define NEED_COPYSTRING 1
+
+static int
+CopyPwd(
+ struct passwd *tgtPtr,
+ char *buf,
+ int buflen)
+{
+ char *p = buf;
+ int copied, len = 0;
+
+ copied = CopyString(tgtPtr->pw_name, p, buflen - len);
+ if (copied == -1) {
+ range:
+ errno = ERANGE;
+ return -1;
+ }
+ tgtPtr->pw_name = (copied > 0) ? p : NULL;
+ len += copied;
+ p = buf + len;
+
+ copied = CopyString(tgtPtr->pw_passwd, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->pw_passwd = (copied > 0) ? p : NULL;
+ len += copied;
+ p = buf + len;
+
+ copied = CopyString(tgtPtr->pw_dir, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->pw_dir = (copied > 0) ? p : NULL;
+ len += copied;
+ p = buf + len;
+
+ copied = CopyString(tgtPtr->pw_shell, p, buflen - len);
+ if (copied == -1) {
+ goto range;
+ }
+ tgtPtr->pw_shell = (copied > 0) ? p : NULL;
+
+ return 0;
+}
+#endif /* NEED_COPYPWD */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CopyArray --
+ *
+ * Copies array of NULL-terminated or fixed-length strings to the private
+ * buffer, honouring the size of the buffer.
+ *
+ * Results:
+ * Number of bytes copied on success or -1 on error (errno = ERANGE)
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_COPYARRAY
+static int
+CopyArray(
+ char **src, /* Array of elements to copy. */
+ int elsize, /* Size of each element, or -1 to indicate
+ * that they are C strings of dynamic
+ * length. */
+ char *buf, /* Buffer to copy into. */
+ int buflen) /* Size of buffer. */
+{
+ int i, j, len = 0;
+ char *p, **new;
+
+ if (src == NULL) {
+ return 0;
+ }
+
+ for (i = 0; src[i] != NULL; i++) {
+ /*
+ * Empty loop to count how many.
+ */
+ }
+ len = sizeof(char *) * (i + 1); /* Leave place for the array. */
+ if (len > buflen) {
+ return -1;
+ }
+
+ new = (char **) buf;
+ p = buf + len;
+
+ for (j = 0; j < i; j++) {
+ int sz = (elsize<0 ? (int) strlen(src[j]) + 1 : elsize);
+
+ len += sz;
+ if (len > buflen) {
+ return -1;
+ }
+ memcpy(p, src[j], sz);
+ new[j] = p;
+ p = buf + len;
+ }
+ new[j] = NULL;
+
+ return len;
+}
+#endif /* NEED_COPYARRAY */
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * CopyString --
+ *
+ * Copies a NULL-terminated string to the private buffer, honouring the
+ * size of the buffer
+ *
+ * Results:
+ * 0 success or -1 on error (errno = ERANGE)
+ *
+ * Side effects:
+ * None
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifdef NEED_COPYSTRING
+static int
+CopyString(
+ const char *src, /* String to copy. */
+ char *buf, /* Buffer to copy into. */
+ int buflen) /* Size of buffer. */
+{
+ int len = 0;
+
+ if (src != NULL) {
+ len = strlen(src) + 1;
+ if (len > buflen) {
+ return -1;
+ }
+ memcpy(buf, src, len);
+ }
+
+ return len;
+}
+#endif /* NEED_COPYSTRING */
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * TclWinCPUID --
+ *
+ * Get CPU ID information on an Intel box under UNIX (either Linux or Cygwin)
+ *
+ * Results:
+ * Returns TCL_OK if successful, TCL_ERROR if CPUID is not supported.
+ *
+ * Side effects:
+ * If successful, stores EAX, EBX, ECX and EDX registers after the CPUID
+ * instruction in the four integers designated by 'regsPtr'
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclWinCPUID(
+ int index, /* Which CPUID value to retrieve. */
+ int *regsPtr) /* Registers after the CPUID. */
+{
+ int status = TCL_ERROR;
+
+ /* See: <http://en.wikipedia.org/wiki/CPUID> */
+#if defined(HAVE_CPUID)
+#if defined(__x86_64__) || defined(_M_AMD64) || defined (_M_X64)
+ __asm__ __volatile__("movq %%rbx, %%rsi \n\t" /* save %rbx */
+ "cpuid \n\t"
+ "xchgq %%rsi, %%rbx \n\t" /* restore the old %rbx */
+ : "=a"(regsPtr[0]), "=S"(regsPtr[1]), "=c"(regsPtr[2]), "=d"(regsPtr[3])
+ : "a"(index));
+#else
+ __asm__ __volatile__("mov %%ebx, %%esi \n\t" /* save %ebx */
+ "cpuid \n\t"
+ "xchg %%esi, %%ebx \n\t" /* restore the old %ebx */
+ : "=a"(regsPtr[0]), "=S"(regsPtr[1]), "=c"(regsPtr[2]), "=d"(regsPtr[3])
+ : "a"(index));
+#endif
+ status = TCL_OK;
+#endif
+ return status;
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */