summaryrefslogtreecommitdiffstats
path: root/generic/tclPathObj.c
diff options
context:
space:
mode:
authorapnadkarni <apnmbx-wits@yahoo.com>2022-08-06 03:12:49 (GMT)
committerapnadkarni <apnmbx-wits@yahoo.com>2022-08-06 03:12:49 (GMT)
commit90b60f208406b3811bad8f7327b663239fd1eea7 (patch)
tree8ec303940d6d62b3b8cb7bd7b183d65a3a33cf1e /generic/tclPathObj.c
parent6fa6c0b20139268bc5455dcdd3172b187b375476 (diff)
downloadtcl-90b60f208406b3811bad8f7327b663239fd1eea7.zip
tcl-90b60f208406b3811bad8f7327b663239fd1eea7.tar.gz
tcl-90b60f208406b3811bad8f7327b663239fd1eea7.tar.bz2
Added file home command for (possible) Tcl 9 TIP 602 migration
Diffstat (limited to 'generic/tclPathObj.c')
-rw-r--r--generic/tclPathObj.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/generic/tclPathObj.c b/generic/tclPathObj.c
index 9524f26..bb269de 100644
--- a/generic/tclPathObj.c
+++ b/generic/tclPathObj.c
@@ -2559,6 +2559,245 @@ TclNativePathInFilesystem(
}
/*
+ *----------------------------------------------------------------------
+ *
+ * TclGetHomeDir --
+ *
+ * Returns the home directory of a user. Note there is a difference
+ * between not specifying a user and explicitly specifying the current
+ * user. This mimics Tcl8's tilde expansion.
+ *
+ * Results:
+ * Returns TCL_OK on success with home directory path in *dsPtr
+ * and TCL_ERROR on failure with error message in interp if non-NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclGetHomeDir(
+ Tcl_Interp *interp, /* May be NULL. Only used for error messages */
+ const char *user, /* User name. NULL -> current user */
+ Tcl_DString *dsPtr) /* Output. Is initialized by the function. Must be
+ freed on success */
+{
+ const char *dir;
+ Tcl_DString nativeString;
+
+ Tcl_DStringInit(dsPtr);
+ Tcl_DStringInit(&nativeString);
+
+ if (user == NULL || user[0] == 0) {
+ /* No user name specified -> current user */
+
+ dir = TclGetEnv("HOME", &nativeString);
+ if (dir == NULL) {
+ if (interp) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "couldn't find HOME environment variable to"
+ " expand path", -1));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH",
+ "HOMELESS", NULL);
+ }
+ return TCL_ERROR;
+ }
+ } else {
+ /* User name specified - ~user */
+ dir = TclpGetUserHome(user, &nativeString);
+ if (dir == NULL) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "user \"%s\" doesn't exist", user));
+ Tcl_SetErrorCode(interp, "TCL", "VALUE", "PATH", "NOUSER",
+ NULL);
+ }
+ return TCL_ERROR;
+ }
+ }
+ Tcl_JoinPath(1, &dir, dsPtr);
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclGetHomeDirObj --
+ *
+ * Wrapper around TclGetHomeDir. See that function.
+ *
+ * Results:
+ * Returns a Tcl_Obj containing the home directory of a user
+ * or NULL on failure with error message in interp if non-NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Obj *
+TclGetHomeDirObj(
+ Tcl_Interp *interp, /* May be NULL. Only used for error messages */
+ const char *user) /* User name. NULL -> current user */
+{
+ Tcl_DString dirString;
+
+ if (TclGetHomeDir(interp, user, &dirString) != TCL_OK) {
+ return NULL;
+ }
+ return TclDStringToObj(&dirString);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclResolveTildePath --
+ *
+ * If the passed path is begins with a tilde, does tilde resolution
+ * and returns a Tcl_Obj containing the resolved path. If the tilde
+ * component cannot be resolved, returns NULL. If the path does not
+ * begin with a tilde, returns as is.
+ *
+ * The trailing components of the path are returned verbatim. No
+ * processing is done on them. Moreover, no assumptions should be
+ * made about the separators in the returned path. They may be /
+ * or native. Appropriate path manipulations functions should be
+ * used by caller if desired.
+ *
+ * Results:
+ * Returns a Tcl_Obj with resolved path. This may be a new Tcl_Obj
+ * with ref count 0 or that pathObj that was passed in without its
+ * ref count modified.
+ * Returns NULL if the path begins with a ~ that cannot be resolved
+ * and stores an error message in interp if non-NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Obj *
+TclResolveTildePath(
+ Tcl_Interp *interp, /* May be NULL. Only used for error messages */
+ Tcl_Obj *pathObj)
+{
+ const char *path;
+ int len;
+ Tcl_Obj *resolvedObj;
+ Tcl_DString dirString;
+ int split;
+
+ path = Tcl_GetStringFromObj(pathObj, &len);
+ if (path[0] != '~') {
+ return pathObj;
+ }
+
+ /*
+ * We have multiple cases '~/foo/bar...', '~user/foo/bar...', etc.
+ * split becomes value 1 for '~/...' as well as for '~'. Note on
+ * Windows FindSplitPos will implicitly check for '\' as separator
+ * in addition to what is passed.
+ */
+ split = FindSplitPos(path, '/');
+
+ if (split == 1) {
+ /* No user name specified -> current user */
+ if (TclGetHomeDir(interp, NULL, &dirString) != TCL_OK) {
+ return NULL;
+ }
+ } else {
+ /* User name specified - ~user */
+
+ const char *expandedUser;
+ Tcl_DString userName;
+
+ Tcl_DStringInit(&userName);
+ Tcl_DStringAppend(&userName, path+1, split-1);
+ expandedUser = Tcl_DStringValue(&userName);
+
+ if (TclGetHomeDir(interp, expandedUser, &dirString) != TCL_OK) {
+ Tcl_DStringFree(&userName);
+ return NULL;
+ }
+ Tcl_DStringFree(&userName);
+ }
+ resolvedObj = TclDStringToObj(&dirString);
+
+ if (split < len) {
+ /* If any trailer, append it verbatim */
+ Tcl_AppendToObj(resolvedObj, split + path, len-split);
+ }
+
+ return resolvedObj;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclResolveTildePathList --
+ *
+ * Given a Tcl_Obj that is a list of paths, returns a Tcl_Obj containing
+ * the paths with any ~-prefixed paths resolved.
+ *
+ * Empty strings and ~-prefixed paths that cannot be resolved are
+ * removed from the returned list.
+ *
+ * The trailing components of the path are returned verbatim. No
+ * processing is done on them. Moreover, no assumptions should be
+ * made about the separators in the returned path. They may be /
+ * or native. Appropriate path manipulations functions should be
+ * used by caller if desired.
+ *
+ * Results:
+ * Returns a Tcl_Obj with resolved paths. This may be a new Tcl_Obj with
+ * reference count 0 or the original passed-in Tcl_Obj if no paths needed
+ * resolution. A NULL is returned if the passed in value is not a list
+ * or was NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Tcl_Obj *
+TclResolveTildePathList(
+ Tcl_Obj *pathsObj)
+{
+ Tcl_Obj **objv;
+ int objc;
+ int i;
+ Tcl_Obj *resolvedPaths;
+ const char *path;
+
+ if (pathsObj == NULL) {
+ return NULL;
+ }
+ if (Tcl_ListObjGetElements(NULL, pathsObj, &objc, &objv) != TCL_OK) {
+ return NULL; /* Not a list */
+ }
+
+ /*
+ * Figure out if any paths need resolving to avoid unnecessary allocations.
+ */
+ for (i = 0; i < objc; ++i) {
+ path = Tcl_GetString(objv[i]);
+ if (path[0] == '~') {
+ break; /* At least one path needs resolution */
+ }
+ }
+ if (i == objc) {
+ return pathsObj; /* No paths needed to be resolved */
+ }
+
+ resolvedPaths = Tcl_NewListObj(objc, NULL);
+ for (i = 0; i < objc; ++i) {
+ Tcl_Obj *resolvedPath;
+ path = Tcl_GetString(objv[i]);
+ if (path[0] == 0) {
+ continue; /* Skip empty strings */
+ }
+ resolvedPath = TclResolveTildePath(NULL, objv[i]);
+ if (resolvedPath) {
+ /* Paths that cannot be resolved are skipped */
+ Tcl_ListObjAppendElement(NULL, resolvedPaths, resolvedPath);
+ }
+ }
+
+ return resolvedPaths;
+}
+
+
+/*
* Local Variables:
* mode: c
* c-basic-offset: 4