summaryrefslogtreecommitdiffstats
path: root/macosx/tclMacOSXFCmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tclMacOSXFCmd.c')
-rw-r--r--macosx/tclMacOSXFCmd.c694
1 files changed, 694 insertions, 0 deletions
diff --git a/macosx/tclMacOSXFCmd.c b/macosx/tclMacOSXFCmd.c
new file mode 100644
index 0000000..8eb3e57
--- /dev/null
+++ b/macosx/tclMacOSXFCmd.c
@@ -0,0 +1,694 @@
+/*
+ * tclMacOSXFCmd.c
+ *
+ * This file implements the MacOSX specific portion of file manipulation
+ * subcommands of the "file" command.
+ *
+ * Copyright (c) 2003-2007 Daniel A. Steffen <das@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"
+
+#ifdef HAVE_GETATTRLIST
+#include <sys/attr.h>
+#include <sys/paths.h>
+#include <libkern/OSByteOrder.h>
+#endif
+
+/* Darwin 8 copyfile API. */
+#ifdef HAVE_COPYFILE
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
+/* Support for weakly importing copyfile. */
+#define WEAK_IMPORT_COPYFILE
+extern int copyfile(const char *from, const char *to, copyfile_state_t state,
+ copyfile_flags_t flags) WEAK_IMPORT_ATTRIBUTE;
+#endif /* HAVE_WEAK_IMPORT */
+#else /* HAVE_COPYFILE_H */
+int copyfile(const char *from, const char *to, void *state, uint32_t flags);
+#define COPYFILE_ACL (1<<0)
+#define COPYFILE_XATTR (1<<2)
+#define COPYFILE_NOFOLLOW_SRC (1<<18)
+#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
+/* Support for weakly importing copyfile. */
+#define WEAK_IMPORT_COPYFILE
+extern int copyfile(const char *from, const char *to, void *state,
+ uint32_t flags) WEAK_IMPORT_ATTRIBUTE;
+#endif /* HAVE_WEAK_IMPORT */
+#endif /* HAVE_COPYFILE_H */
+#endif /* HAVE_COPYFILE */
+
+#include <libkern/OSByteOrder.h>
+
+/*
+ * Constants for file attributes subcommand. Need to be kept in sync with
+ * tclUnixFCmd.c !
+ */
+
+enum {
+ UNIX_GROUP_ATTRIBUTE,
+ UNIX_OWNER_ATTRIBUTE,
+ UNIX_PERMISSIONS_ATTRIBUTE,
+#ifdef HAVE_CHFLAGS
+ UNIX_READONLY_ATTRIBUTE,
+#endif
+#ifdef MAC_OSX_TCL
+ MACOSX_CREATOR_ATTRIBUTE,
+ MACOSX_TYPE_ATTRIBUTE,
+ MACOSX_HIDDEN_ATTRIBUTE,
+ MACOSX_RSRCLENGTH_ATTRIBUTE,
+#endif
+};
+
+typedef u_int32_t OSType;
+
+static int GetOSTypeFromObj(Tcl_Interp *interp,
+ Tcl_Obj *objPtr, OSType *osTypePtr);
+static Tcl_Obj * NewOSTypeObj(const OSType newOSType);
+static int SetOSTypeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
+static void UpdateStringOfOSType(Tcl_Obj *objPtr);
+
+static Tcl_ObjType tclOSTypeType = {
+ "osType", /* name */
+ NULL, /* freeIntRepProc */
+ NULL, /* dupIntRepProc */
+ UpdateStringOfOSType, /* updateStringProc */
+ SetOSTypeFromAny /* setFromAnyProc */
+};
+
+enum {
+ kIsInvisible = 0x4000,
+};
+
+#define kFinfoIsInvisible (OSSwapHostToBigConstInt16(kIsInvisible))
+
+typedef struct finderinfo {
+ u_int32_t type;
+ u_int32_t creator;
+ u_int16_t fdFlags;
+ u_int32_t location;
+ u_int16_t reserved;
+ u_int32_t extendedFileInfo[4];
+} __attribute__ ((__packed__)) finderinfo;
+
+typedef struct fileinfobuf {
+ u_int32_t info_length;
+ u_int32_t data[8];
+} fileinfobuf;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclMacOSXGetFileAttribute
+ *
+ * Gets a MacOSX attribute of a file. Which attribute is controlled by
+ * objIndex. The object will have ref count 0.
+ *
+ * Results:
+ * Standard TCL result. Returns a new Tcl_Obj in attributePtrPtr if there
+ * is no error.
+ *
+ * Side effects:
+ * A new object is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclMacOSXGetFileAttribute(
+ Tcl_Interp *interp, /* The interp we are using for errors. */
+ int objIndex, /* The index of the attribute. */
+ Tcl_Obj *fileName, /* The name of the file (UTF-8). */
+ Tcl_Obj **attributePtrPtr) /* A pointer to return the object with. */
+{
+#ifdef HAVE_GETATTRLIST
+ int result;
+ Tcl_StatBuf statBuf;
+ struct attrlist alist;
+ fileinfobuf finfo;
+ finderinfo *finder = (finderinfo*)(&finfo.data);
+ off_t *rsrcForkSize = (off_t*)(&finfo.data);
+ const char *native;
+
+ result = TclpObjStat(fileName, &statBuf);
+
+ if (result != 0) {
+ Tcl_AppendResult(interp, "could not read \"",
+ TclGetString(fileName), "\": ", Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+
+ if (S_ISDIR(statBuf.st_mode) && objIndex != MACOSX_HIDDEN_ATTRIBUTE) {
+ /*
+ * Directories only support attribute "-hidden".
+ */
+
+ errno = EISDIR;
+ Tcl_AppendResult(interp, "invalid attribute: ",
+ Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+
+ bzero(&alist, sizeof(struct attrlist));
+ alist.bitmapcount = ATTR_BIT_MAP_COUNT;
+ if (objIndex == MACOSX_RSRCLENGTH_ATTRIBUTE) {
+ alist.fileattr = ATTR_FILE_RSRCLENGTH;
+ } else {
+ alist.commonattr = ATTR_CMN_FNDRINFO;
+ }
+ native = Tcl_FSGetNativePath(fileName);
+ result = getattrlist(native, &alist, &finfo, sizeof(fileinfobuf), 0);
+
+ if (result != 0) {
+ Tcl_AppendResult(interp, "could not read attributes of \"",
+ TclGetString(fileName), "\": ", Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+
+ switch (objIndex) {
+ case MACOSX_CREATOR_ATTRIBUTE:
+ *attributePtrPtr = NewOSTypeObj(
+ OSSwapBigToHostInt32(finder->creator));
+ break;
+ case MACOSX_TYPE_ATTRIBUTE:
+ *attributePtrPtr = NewOSTypeObj(
+ OSSwapBigToHostInt32(finder->type));
+ break;
+ case MACOSX_HIDDEN_ATTRIBUTE:
+ *attributePtrPtr = Tcl_NewBooleanObj(
+ (finder->fdFlags & kFinfoIsInvisible) != 0);
+ break;
+ case MACOSX_RSRCLENGTH_ATTRIBUTE:
+ *attributePtrPtr = Tcl_NewWideIntObj(*rsrcForkSize);
+ break;
+ }
+ return TCL_OK;
+#else
+ Tcl_AppendResult(interp, "Mac OS X file attributes not supported", NULL);
+ return TCL_ERROR;
+#endif
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclMacOSXSetFileAttribute --
+ *
+ * Sets a MacOSX attribute of a file. Which attribute is controlled by
+ * objIndex.
+ *
+ * Results:
+ * Standard TCL result.
+ *
+ * Side effects:
+ * As above.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TclMacOSXSetFileAttribute(
+ Tcl_Interp *interp, /* The interp for error reporting. */
+ int objIndex, /* The index of the attribute. */
+ Tcl_Obj *fileName, /* The name of the file (UTF-8). */
+ Tcl_Obj *attributePtr) /* New owner for file. */
+{
+#ifdef HAVE_GETATTRLIST
+ int result;
+ Tcl_StatBuf statBuf;
+ struct attrlist alist;
+ fileinfobuf finfo;
+ finderinfo *finder = (finderinfo*)(&finfo.data);
+ off_t *rsrcForkSize = (off_t*)(&finfo.data);
+ const char *native;
+
+ result = TclpObjStat(fileName, &statBuf);
+
+ if (result != 0) {
+ Tcl_AppendResult(interp, "could not read \"",
+ TclGetString(fileName), "\": ", Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+
+ if (S_ISDIR(statBuf.st_mode) && objIndex != MACOSX_HIDDEN_ATTRIBUTE) {
+ /*
+ * Directories only support attribute "-hidden".
+ */
+
+ errno = EISDIR;
+ Tcl_AppendResult(interp, "invalid attribute: ",
+ Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+
+ bzero(&alist, sizeof(struct attrlist));
+ alist.bitmapcount = ATTR_BIT_MAP_COUNT;
+ if (objIndex == MACOSX_RSRCLENGTH_ATTRIBUTE) {
+ alist.fileattr = ATTR_FILE_RSRCLENGTH;
+ } else {
+ alist.commonattr = ATTR_CMN_FNDRINFO;
+ }
+ native = Tcl_FSGetNativePath(fileName);
+ result = getattrlist(native, &alist, &finfo, sizeof(fileinfobuf), 0);
+
+ if (result != 0) {
+ Tcl_AppendResult(interp, "could not read attributes of \"",
+ TclGetString(fileName), "\": ", Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+
+ if (objIndex != MACOSX_RSRCLENGTH_ATTRIBUTE) {
+ OSType t;
+ int h;
+
+ switch (objIndex) {
+ case MACOSX_CREATOR_ATTRIBUTE:
+ if (GetOSTypeFromObj(interp, attributePtr, &t) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ finder->creator = OSSwapHostToBigInt32(t);
+ break;
+ case MACOSX_TYPE_ATTRIBUTE:
+ if (GetOSTypeFromObj(interp, attributePtr, &t) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ finder->type = OSSwapHostToBigInt32(t);
+ break;
+ case MACOSX_HIDDEN_ATTRIBUTE:
+ if (Tcl_GetBooleanFromObj(interp, attributePtr, &h) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (h) {
+ finder->fdFlags |= kFinfoIsInvisible;
+ } else {
+ finder->fdFlags &= ~kFinfoIsInvisible;
+ }
+ break;
+ }
+
+ result = setattrlist(native, &alist,
+ &finfo.data, sizeof(finfo.data), 0);
+
+ if (result != 0) {
+ Tcl_AppendResult(interp, "could not set attributes of \"",
+ TclGetString(fileName), "\": ",
+ Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_WideInt newRsrcForkSize;
+
+ if (Tcl_GetWideIntFromObj(interp, attributePtr,
+ &newRsrcForkSize) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (newRsrcForkSize != *rsrcForkSize) {
+ Tcl_DString ds;
+
+ /*
+ * Only setting rsrclength to 0 to strip a file's resource fork is
+ * supported.
+ */
+
+ if(newRsrcForkSize != 0) {
+ Tcl_AppendResult(interp,
+ "setting nonzero rsrclength not supported", NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Construct path to resource fork.
+ */
+
+ Tcl_DStringInit(&ds);
+ Tcl_DStringAppend(&ds, native, -1);
+ Tcl_DStringAppend(&ds, _PATH_RSRCFORKSPEC, -1);
+
+ result = truncate(Tcl_DStringValue(&ds), (off_t)0);
+ if (result != 0) {
+ /*
+ * truncate() on a valid resource fork path may fail with
+ * a permission error in some OS releases, try truncating
+ * with open() instead:
+ */
+ int fd = open(Tcl_DStringValue(&ds), O_WRONLY | O_TRUNC);
+ if (fd > 0) {
+ result = close(fd);
+ }
+ }
+
+ Tcl_DStringFree(&ds);
+
+ if (result != 0) {
+ Tcl_AppendResult(interp,
+ "could not truncate resource fork of \"",
+ TclGetString(fileName), "\": ",
+ Tcl_PosixError(interp), NULL);
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+#else
+ Tcl_AppendResult(interp, "Mac OS X file attributes not supported", NULL);
+ return TCL_ERROR;
+#endif
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclMacOSXCopyFileAttributes --
+ *
+ * Copy the MacOSX attributes and resource fork (if present) from one
+ * file to another.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * MacOSX attributes and resource fork are updated in the new file to
+ * reflect the old file.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TclMacOSXCopyFileAttributes(
+ CONST char *src, /* Path name of source file (native). */
+ CONST char *dst, /* Path name of target file (native). */
+ CONST Tcl_StatBuf *statBufPtr)
+ /* Stat info for source file */
+{
+#ifdef WEAK_IMPORT_COPYFILE
+ if (copyfile != NULL) {
+#endif
+#ifdef HAVE_COPYFILE
+ if (copyfile(src, dst, NULL, COPYFILE_XATTR |
+ (S_ISLNK(statBufPtr->st_mode) ? COPYFILE_NOFOLLOW_SRC :
+ COPYFILE_ACL)) < 0) {
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+#endif /* HAVE_COPYFILE */
+#ifdef WEAK_IMPORT_COPYFILE
+ } else {
+#endif
+#if !defined(HAVE_COPYFILE) || defined(WEAK_IMPORT_COPYFILE)
+#ifdef HAVE_GETATTRLIST
+ struct attrlist alist;
+ fileinfobuf finfo;
+ off_t *rsrcForkSize = (off_t*)(&finfo.data);
+
+ bzero(&alist, sizeof(struct attrlist));
+ alist.bitmapcount = ATTR_BIT_MAP_COUNT;
+ alist.commonattr = ATTR_CMN_FNDRINFO;
+
+ if (getattrlist(src, &alist, &finfo, sizeof(fileinfobuf), 0)) {
+ return TCL_ERROR;
+ }
+
+ if (setattrlist(dst, &alist, &finfo.data, sizeof(finfo.data), 0)) {
+ return TCL_ERROR;
+ }
+
+ if (!S_ISDIR(statBufPtr->st_mode)) {
+ /*
+ * Only copy non-empty resource fork.
+ */
+
+ alist.commonattr = 0;
+ alist.fileattr = ATTR_FILE_RSRCLENGTH;
+
+ if (getattrlist(src, &alist, &finfo, sizeof(fileinfobuf), 0)) {
+ return TCL_ERROR;
+ }
+
+ if(*rsrcForkSize > 0) {
+ int result;
+ Tcl_DString ds_src, ds_dst;
+
+ /*
+ * Construct paths to resource forks.
+ */
+
+ Tcl_DStringInit(&ds_src);
+ Tcl_DStringAppend(&ds_src, src, -1);
+ Tcl_DStringAppend(&ds_src, _PATH_RSRCFORKSPEC, -1);
+ Tcl_DStringInit(&ds_dst);
+ Tcl_DStringAppend(&ds_dst, dst, -1);
+ Tcl_DStringAppend(&ds_dst, _PATH_RSRCFORKSPEC, -1);
+
+ result = TclUnixCopyFile(Tcl_DStringValue(&ds_src),
+ Tcl_DStringValue(&ds_dst), statBufPtr, 1);
+
+ Tcl_DStringFree(&ds_src);
+ Tcl_DStringFree(&ds_dst);
+
+ if (result != 0) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ return TCL_OK;
+#else
+ return TCL_ERROR;
+#endif /* HAVE_GETATTRLIST */
+#endif /* !defined(HAVE_COPYFILE) || defined(WEAK_IMPORT_COPYFILE) */
+#ifdef WEAK_IMPORT_COPYFILE
+ }
+#endif
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclMacOSXMatchType --
+ *
+ * This routine is used by the globbing code to check if a file
+ * matches a given mac type and/or creator code.
+ *
+ * Results:
+ * The return value is 1, 0 or -1 indicating whether the file
+ * matches the given criteria, does not match them, or an error
+ * occurred (in wich case an error is left in interp).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclMacOSXMatchType(
+ Tcl_Interp *interp, /* Interpreter to receive errors. */
+ CONST char *pathName, /* Native path to check. */
+ CONST char *fileName, /* Native filename to check. */
+ Tcl_StatBuf *statBufPtr, /* Stat info for file to check */
+ Tcl_GlobTypeData *types) /* Type description to match against. */
+{
+#ifdef HAVE_GETATTRLIST
+ struct attrlist alist;
+ fileinfobuf finfo;
+ finderinfo *finder = (finderinfo*)(&finfo.data);
+ OSType osType;
+
+ bzero(&alist, sizeof(struct attrlist));
+ alist.bitmapcount = ATTR_BIT_MAP_COUNT;
+ alist.commonattr = ATTR_CMN_FNDRINFO;
+ if (getattrlist(pathName, &alist, &finfo, sizeof(fileinfobuf), 0) != 0) {
+ return 0;
+ }
+ if ((types->perm & TCL_GLOB_PERM_HIDDEN) &&
+ !((finder->fdFlags & kFinfoIsInvisible) || (*fileName == '.'))) {
+ return 0;
+ }
+ if (S_ISDIR(statBufPtr->st_mode) && (types->macType || types->macCreator)) {
+ /* Directories don't support types or creators */
+ return 0;
+ }
+ if (types->macType) {
+ if (GetOSTypeFromObj(interp, types->macType, &osType) != TCL_OK) {
+ return -1;
+ }
+ if (osType != OSSwapBigToHostInt32(finder->type)) {
+ return 0;
+ }
+ }
+ if (types->macCreator) {
+ if (GetOSTypeFromObj(interp, types->macCreator, &osType) != TCL_OK) {
+ return -1;
+ }
+ if (osType != OSSwapBigToHostInt32(finder->creator)) {
+ return 0;
+ }
+ }
+#endif
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetOSTypeFromObj --
+ *
+ * Attempt to return an OSType from the Tcl object "objPtr".
+ *
+ * Results:
+ * Standard TCL result. If an error occurs during conversion, an error
+ * message is left in interp->objResult.
+ *
+ * Side effects:
+ * The string representation of objPtr will be updated if necessary.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+GetOSTypeFromObj(
+ Tcl_Interp *interp, /* Used for error reporting if not NULL. */
+ Tcl_Obj *objPtr, /* The object from which to get an OSType. */
+ OSType *osTypePtr) /* Place to store resulting OSType. */
+{
+ int result = TCL_OK;
+
+ if (objPtr->typePtr != &tclOSTypeType) {
+ result = tclOSTypeType.setFromAnyProc(interp, objPtr);
+ };
+ *osTypePtr = (OSType) objPtr->internalRep.longValue;
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NewOSTypeObj --
+ *
+ * Create a new OSType object.
+ *
+ * Results:
+ * The newly created OSType object is returned, it has ref count 0.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tcl_Obj *
+NewOSTypeObj(
+ const OSType osType) /* OSType used to initialize the new object. */
+{
+ Tcl_Obj *objPtr;
+
+ TclNewObj(objPtr);
+ Tcl_InvalidateStringRep(objPtr);
+ objPtr->internalRep.longValue = (long) osType;
+ objPtr->typePtr = &tclOSTypeType;
+ return objPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetOSTypeFromAny --
+ *
+ * Attempts to force the internal representation for a Tcl object to
+ * tclOSTypeType, specifically.
+ *
+ * Results:
+ * The return value is a standard object Tcl result. If an error occurs
+ * during conversion, an error message is left in the interpreter's
+ * result unless "interp" is NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SetOSTypeFromAny(
+ Tcl_Interp *interp, /* Tcl interpreter */
+ Tcl_Obj *objPtr) /* Pointer to the object to convert */
+{
+ char *string;
+ int length, result = TCL_OK;
+ Tcl_DString ds;
+ Tcl_Encoding encoding = Tcl_GetEncoding(NULL, "macRoman");
+
+ string = Tcl_GetStringFromObj(objPtr, &length);
+ Tcl_UtfToExternalDString(encoding, string, length, &ds);
+
+ if (Tcl_DStringLength(&ds) > 4) {
+ Tcl_AppendResult(interp, "expected Macintosh OS type but got \"",
+ string, "\": ", NULL);
+ result = TCL_ERROR;
+ } else {
+ OSType osType;
+ char string[4] = {'\0','\0','\0','\0'};
+ memcpy(string, Tcl_DStringValue(&ds),
+ (size_t) Tcl_DStringLength(&ds));
+ osType = (OSType) string[0] << 24 |
+ (OSType) string[1] << 16 |
+ (OSType) string[2] << 8 |
+ (OSType) string[3];
+ TclFreeIntRep(objPtr);
+ objPtr->internalRep.longValue = (long) osType;
+ objPtr->typePtr = &tclOSTypeType;
+ }
+ Tcl_DStringFree(&ds);
+ Tcl_FreeEncoding(encoding);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateStringOfOSType --
+ *
+ * Update the string representation for an OSType object. Note: This
+ * function does not free an existing old string rep so storage will be
+ * lost if this has not already been done.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The object's string is set to a valid string that results from the
+ * OSType-to-string conversion.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateStringOfOSType(
+ register Tcl_Obj *objPtr) /* OSType object whose string rep to update. */
+{
+ char string[5];
+ OSType osType = (OSType) objPtr->internalRep.longValue;
+ Tcl_DString ds;
+ Tcl_Encoding encoding = Tcl_GetEncoding(NULL, "macRoman");
+
+ string[0] = (char) (osType >> 24);
+ string[1] = (char) (osType >> 16);
+ string[2] = (char) (osType >> 8);
+ string[3] = (char) (osType);
+ string[4] = '\0';
+ Tcl_ExternalToUtfDString(encoding, string, -1, &ds);
+ objPtr->bytes = ckalloc((unsigned) Tcl_DStringLength(&ds) + 1);
+ strcpy(objPtr->bytes, Tcl_DStringValue(&ds));
+ objPtr->length = Tcl_DStringLength(&ds);
+ Tcl_DStringFree(&ds);
+ Tcl_FreeEncoding(encoding);
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */