void sg_wc_db__path__sz_repopath_to_absolute(SG_context * pCtx,
											 const sg_wc_db * pDb,
											 const char * pszRepoPath,
											 SG_pathname ** ppPathItem)
{
	SG_pathname * pPath = NULL;

	// TODO 2012/01/23 This code was written before the extended-prefix repo-path
	// TODO            stuff, but it is still valid to require a live/wd-relative
	// TODO            repo-path when building an absolute pathname (because it
	// TODO            probably doesn't make sense to convert an arbitrary-context
	// TODO            repo-path (without someone doing the implied context conversion)).
	// TODO
	// TODO            So I'm going to leave this as an assert for now.
	SG_ASSERT_RELEASE_FAIL(  ((pszRepoPath[0]=='@') && (pszRepoPath[1]=='/'))  );

	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx,
											&pPath,
											pDb->pPathWorkingDirectoryTop)  );
	SG_ERR_CHECK(  SG_pathname__add_final_slash(pCtx, pPath)  );

	if (pszRepoPath[2])
		SG_ERR_CHECK(  SG_pathname__append__from_sz(pCtx, pPath, &pszRepoPath[2])  );

	*ppPathItem = pPath;
	return;

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPath);
}
Exemple #2
0
void SG_workingdir__set_mapping(
	SG_context* pCtx,
	const SG_pathname* pPathLocalDirectory,
	const char* pszNameRepoInstanceDescriptor, /**< The name of the repo instance descriptor */
	const char* pszidGidAnchorDirectory /**< The GID of the directory within the repo to which this is anchored.  Usually it's user root. */
	)
{
	SG_vhash* pvhNew = NULL;
	SG_vhash* pvh = NULL;
	SG_pathname* pMyPath = NULL;
	SG_pathname* pDrawerPath = NULL;
	SG_pathname* pMappingFilePath = NULL;

	SG_NULLARGCHECK_RETURN(pPathLocalDirectory);
	SG_NULLARGCHECK_RETURN(pszNameRepoInstanceDescriptor);

	/* make a copy of the path so we can modify it (adding the final slash) */
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pMyPath, pPathLocalDirectory)  );

	/* null the original parameter pointer to make sure we don't use it anymore */
	pPathLocalDirectory = NULL;

	/* make sure the path we were given is a directory that exists */
	SG_ERR_CHECK(  SG_fsobj__verify_directory_exists_on_disk__pathname(pCtx, pMyPath)  );

	/* it's a directory, so it should have a final slash */
	SG_ERR_CHECK(  SG_pathname__add_final_slash(pCtx, pMyPath)  );

	/* make sure the name of the repo instance descriptor is valid */
	SG_ERR_CHECK(  SG_closet__descriptors__get(pCtx, pszNameRepoInstanceDescriptor, &pvh) );
	SG_VHASH_NULLFREE(pCtx, pvh);

	// TODO verify that the anchor GID is valid for that repo?

	SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhNew)  );

	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhNew, "descriptor", pszNameRepoInstanceDescriptor)  );
	SG_ERR_CHECK(  SG_vhash__add__string__sz(pCtx, pvhNew, "anchor", pszidGidAnchorDirectory)  );

	SG_ERR_CHECK(  SG_workingdir__verify_drawer_exists(pCtx, pMyPath, &pDrawerPath)  );
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pMappingFilePath, pDrawerPath, "repo.json")  );

	SG_ERR_CHECK(  SG_vfile__update__vhash(pCtx, pMappingFilePath, "mapping", pvhNew)  );

	SG_VHASH_NULLFREE(pCtx, pvhNew);
	SG_PATHNAME_NULLFREE(pCtx, pMyPath);
	SG_PATHNAME_NULLFREE(pCtx, pDrawerPath);
	SG_PATHNAME_NULLFREE(pCtx, pMappingFilePath);

	return;
fail:
	SG_PATHNAME_NULLFREE(pCtx, pDrawerPath);
	SG_PATHNAME_NULLFREE(pCtx, pMappingFilePath);
	SG_PATHNAME_NULLFREE(pCtx, pMyPath);
	SG_VHASH_NULLFREE(pCtx, pvhNew);
	SG_VHASH_NULLFREE(pCtx, pvh);
}
Exemple #3
0
void SG_workingdir__construct_absolute_path_from_repo_path(SG_context * pCtx,
														   const SG_pathname * pPathWorkingDirectoryTop,
														   const char * pszRepoPath,
														   SG_pathname ** ppPathAbsolute)
{
	SG_pathname * pPath = NULL;

#if defined(DEBUG)
	// TODO we should verify "<wd-top>/.sgdrawer" exists to make sure
	// TODO that we have the actual wd-root.  especially when using vv
	// TODO with relative paths while cd'd somewhere deep inside the tree.
#endif

	if (pszRepoPath[0] != '@')
	{
		SG_ERR_THROW2_RETURN(  SG_ERR_INVALID_REPO_PATH,
							   (pCtx, "Must begin with '@/': %s", pszRepoPath)  );
	}
	else
	{
		if (pszRepoPath[1] == 0)		// quietly allow "@" as substitute for "@/"
		{
			SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pPath, pPathWorkingDirectoryTop)  );
		}
		else if (pszRepoPath[1] != '/')
		{
			SG_ERR_THROW2_RETURN(  SG_ERR_INVALID_REPO_PATH,
								   (pCtx, "Must begin with '@/': %s", pszRepoPath)  );
		}
		else
		{
			if (pszRepoPath[2])
				SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPath, pPathWorkingDirectoryTop, &pszRepoPath[2])  );
			else
				SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pPath, pPathWorkingDirectoryTop)  );
		}
	}

	*ppPathAbsolute = pPath;
	return;

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPath);
}
Exemple #4
0
void _read_template_file(
	SG_context *pCtx,
	const char *templateFn,
	SG_string **pContent,	/**< we allocate, we free on error, else caller owns it */
	const _request_headers *pRequestHeaders,
	_replacer_cb replacer)
{
	SG_pathname *tpath = NULL;
	SG_file *pFile = NULL;
	SG_uint32 got = 0;
	char	tbuf[1024];

    //todo: make this thread-safe:
    if(_sg_uridispatch__templatePath==NULL)
        SG_ERR_CHECK(  _sgui_set_templatePath(pCtx)  );

	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &tpath, _sg_uridispatch__templatePath)  );
	SG_ERR_CHECK(  SG_pathname__append__from_sz(pCtx, tpath, templateFn)  );

	SG_ERR_CHECK(  SG_file__open__pathname(pCtx, tpath, SG_FILE_RDONLY|SG_FILE_OPEN_EXISTING, 0644, &pFile)  );
	SG_PATHNAME_NULLFREE(pCtx, tpath);

	SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, pContent)  );

	do
	{
		SG_file__read(pCtx, pFile, sizeof(tbuf), (SG_byte *)tbuf, &got);
        if (SG_context__err_equals(pCtx, SG_ERR_EOF))
        {
			SG_context__err_reset(pCtx);
            break;
        }
		SG_ERR_CHECK_CURRENT;

		SG_ERR_CHECK(  SG_string__append__buf_len(pCtx, *pContent, (const SG_byte *)tbuf, got) );
	} while (got > 0);

	SG_ERR_CHECK(  SG_file__close(pCtx, &pFile)  );

	SG_ERR_CHECK(  _templatize(pCtx, *pContent, pRequestHeaders, replacer)  );

	return;

fail:
	SG_STRING_NULLFREE(pCtx, *pContent);
	SG_FILE_NULLCLOSE(pCtx, pFile);
	SG_PATHNAME_NULLFREE(pCtx, tpath);
}
/**
 * Convert relative path to absolute, but make
 * sure that normalization doesn't take it outside
 * of the working directory.
 *
 * This is just pathname/string parsing; we DO NOT
 * confirm that the path exists or could exist.
 *
 */
void sg_wc_db__path__relative_to_absolute(SG_context * pCtx,
										  const sg_wc_db * pDb,
										  const SG_string * pStringRelativePath,
										  SG_pathname ** ppPathItem)
{
	SG_pathname * pPathWDT = NULL;
	SG_pathname * pPath = NULL;
	SG_uint32 lenWDT;

	// choke if they give us a repo-path.
	SG_ARGCHECK_RETURN(  (SG_string__sz(pStringRelativePath)[0] != '@'), pStringRelativePath  );

	// choke if this WC TX isn't cwd-based.
	SG_ERR_CHECK(  _check_if_relative_paths_allowed(pCtx, pDb)  );

	// clone WDT so that we can force a trailing slash.
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx,
											&pPathWDT,
											pDb->pPathWorkingDirectoryTop)  );
	SG_ERR_CHECK(  SG_pathname__add_final_slash(pCtx, pPathWDT)  );
	lenWDT = SG_pathname__length_in_bytes(pPathWDT);

	// allocate and normalize a new path with the net
	// result rather than writing on the clone (so that
	// we can do the following prefix test safely).
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPath,
												   pPathWDT,
												   SG_string__sz(pStringRelativePath))  );

	if (strncmp(SG_pathname__sz(pPath),
				SG_pathname__sz(pPathWDT),
				lenWDT) != 0)
	{
		SG_ERR_THROW2(  SG_ERR_PATH_NOT_IN_WORKING_COPY,
						(pCtx, "The path '%s' is not inside the working copy rooted at '%s'.",
						 SG_string__sz(pStringRelativePath),
						 SG_pathname__sz(pDb->pPathWorkingDirectoryTop))  );
	}

	SG_PATHNAME_NULLFREE(pCtx, pPathWDT);
	*ppPathItem = pPath;
	return;

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathWDT);
	SG_PATHNAME_NULLFREE(pCtx, pPath);
}
static void _loadModuleDir(SG_context *pCtx, const SG_pathname *path, const char *modname, JSContext *cx, JSObject *glob)
{
	SG_rbtree * pJavascriptFiles = NULL;
	SG_rbtree_iterator * pJavascriptFile = NULL;
	const char *szJavascriptFile = NULL; 
	SG_pathname *pJavascriptFilePath = NULL;
	SG_bool ok = SG_FALSE;
	SG_bool valid = SG_FALSE;
	char *psz_js = NULL; // free
	SG_uint32 len_js = 0;
	jsval rval = JSVAL_VOID;
	
	SG_ERR_CHECK(  SG_dir__list(pCtx, path, NULL, NULL, ".js", &pJavascriptFiles)  );
	SG_ERR_CHECK(  SG_rbtree__iterator__first(pCtx, &pJavascriptFile, pJavascriptFiles, &ok, &szJavascriptFile, NULL)  );
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pJavascriptFilePath, path)  );

	while(ok)
	{
		SG_ERR_CHECK(  _isValidJsFile(pCtx, szJavascriptFile, &valid)  );

		if (valid)
		{
			SG_ERR_CHECK(  SG_pathname__append__from_sz(pCtx, pJavascriptFilePath, szJavascriptFile)  );

			SG_ERR_CHECK(  sg_read_entire_file(pCtx, pJavascriptFilePath, &psz_js, &len_js)  );
			if(!JS_EvaluateScript(cx, glob, psz_js, len_js, szJavascriptFile, 1, &rval))
			{
				SG_ERR_CHECK_CURRENT;
				SG_ERR_THROW2(SG_ERR_JS, (pCtx, "An error occurred loading %s javascript file '%s'", modname, szJavascriptFile));
			}
			SG_NULLFREE(pCtx, psz_js);
			SG_ERR_CHECK(  SG_pathname__remove_last(pCtx, pJavascriptFilePath)  );
		}

		SG_ERR_CHECK(  SG_rbtree__iterator__next(pCtx, pJavascriptFile, &ok, &szJavascriptFile, NULL)  );
	}

fail:
	SG_PATHNAME_NULLFREE(pCtx, pJavascriptFilePath);
	SG_RBTREE_ITERATOR_NULLFREE(pCtx, pJavascriptFile);
	SG_RBTREE_NULLFREE(pCtx, pJavascriptFiles);
	SG_NULLFREE(pCtx, psz_js);
}
static void _sg_jscore__install_modules(SG_context * pCtx, JSContext *cx, JSObject *glob,
	const SG_vhash *pServerConfig)
{
	SG_pathname *pModuleDirPath = NULL;
	SG_rbtree_iterator *pModuleDir = NULL;
	SG_rbtree *pModules = NULL;
	const char *szModuleDir = NULL;
	SG_bool ok = SG_FALSE;
	SG_uint32 len_js = 0;
	jsval rval;
	char *psz_js = NULL;
	jsval fo = JSVAL_VOID;
	JSObject * jsoServerConfig = NULL;

	if (gpJSCoreGlobalState->bSkipModules)
		return;

	SG_ERR_CHECK(  _modulesInstalled(pCtx, cx, glob, &ok)  );

	if (ok)
		return;

	if (! gpJSCoreGlobalState->pPathToDispatchDotJS)
		return;
	
	SG_ERR_CHECK(  _setModulesInstalled(pCtx, cx, glob)  );

	SG_ERR_CHECK(  sg_read_entire_file(pCtx, gpJSCoreGlobalState->pPathToDispatchDotJS, &psz_js, &len_js)  );
	if(!JS_EvaluateScript(cx, glob, psz_js, len_js, "dispatch.js", 1, &rval))
	{
		SG_ERR_CHECK_CURRENT;
		SG_ERR_THROW2(SG_ERR_JS, (pCtx, "An error occurred evaluating dispatch.js!"));
	}
	SG_NULLFREE(pCtx, psz_js);

	// Call init function in dispatch.js
	if(pServerConfig)
	{
		jsval arg;
		JSBool js_ok;
		jsval rval2;

		SG_JS_NULL_CHECK(  jsoServerConfig = JS_NewObject(cx, NULL, NULL, NULL)  );
		SG_ERR_CHECK(  sg_jsglue__copy_vhash_into_jsobject(pCtx, cx, pServerConfig, jsoServerConfig)  );
		arg = OBJECT_TO_JSVAL(jsoServerConfig);
		js_ok = JS_CallFunctionName(cx, glob, "init", 1, &arg, &rval2);
		SG_ERR_CHECK_CURRENT;
		if(!js_ok)
			SG_ERR_THROW2(SG_ERR_JS, (pCtx, "An error occurred initializing JavaScript framework: call to JavaScript init() failed"));
		
		jsoServerConfig = NULL;
	}

	// Load core.
	SG_ERR_CHECK(  _loadModuleDir(pCtx, gpJSCoreGlobalState->pPathToCore, "core", cx, glob)  );

	// Load modules.

	SG_ERR_CHECK(  SG_dir__list(pCtx, gpJSCoreGlobalState->pPathToModules, NULL, NULL, NULL, &pModules)  );
	SG_ERR_CHECK(  SG_rbtree__iterator__first(pCtx, &pModuleDir, pModules, &ok, &szModuleDir, NULL)  );
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pModuleDirPath, gpJSCoreGlobalState->pPathToModules)  );
	while(ok)
	{
		if (szModuleDir[0] != '.')
		{
			SG_fsobj_stat fss;

			SG_ERR_CHECK(  SG_pathname__append__from_sz(pCtx, pModuleDirPath, szModuleDir)  );

			SG_ERR_CHECK(  SG_fsobj__stat__pathname(pCtx, pModuleDirPath, &fss)  );

			if (fss.type & SG_FSOBJ_TYPE__DIRECTORY)  // dot paths?
			{
				SG_ERR_CHECK(  _loadModuleDir(pCtx, pModuleDirPath, szModuleDir, cx, glob)  );
			}

			SG_ERR_CHECK(  SG_pathname__remove_last(pCtx, pModuleDirPath)  );
		}

		SG_ERR_CHECK(  SG_rbtree__iterator__next(pCtx, pModuleDir, &ok, &szModuleDir, NULL)  );
	}
												
	if (! JS_LookupProperty(cx, glob, "initModules", &fo))
	{
		SG_ERR_CHECK_CURRENT;
		SG_ERR_THROW2(SG_ERR_JS, (pCtx, "lookup of initModules() failed"));
	}

	if (!JSVAL_IS_VOID(fo))
		if (! JS_CallFunctionName(cx, glob, "initModules", 0, NULL, &rval))
			{
				SG_ERR_CHECK_CURRENT;
				SG_ERR_THROW2(SG_ERR_JS, (pCtx, "Call to initModules() failed"));
			}

fail:
	SG_NULLFREE(pCtx, psz_js);
	SG_PATHNAME_NULLFREE(pCtx, pModuleDirPath);
	SG_RBTREE_ITERATOR_NULLFREE(pCtx, pModuleDir);
	SG_RBTREE_NULLFREE(pCtx, pModules);
}
Exemple #8
0
/**
 * This function is used to get a new tree.  It must be called with the HID of
 * the top-level user root directory.  Not the super-root.  The directory which
 * corresponds to @.
 *
 * Record the file timestamp of each file we fetch in the pvhTimestamps; this
 * provides the basis for the timestamp check in scan-dir.
 */
static void sg_workingdir__do_get_dir__top(SG_context* pCtx,
										   SG_repo* pRepo,
										   const SG_pathname* pPathLocal,
										   const char* pszidHidTreeNode,
										   SG_vhash * pvhTimestamps)
{
	SG_pathname* pPathSub = NULL;
	SG_treenode* pTreenode = NULL;
	SG_string* pstrLink = NULL;
	SG_byte* pBytes = NULL;
    SG_vhash* pvhAttributes = NULL;
    SG_int64 iAttributeBits = 0;
    const SG_treenode_entry* pEntry = NULL;
    const char* pszidHidContent = NULL;
#ifdef SG_BUILD_FLAG_FEATURE_XATTR
    const char* pszidHidXattrs = NULL;
#endif
	SG_bool bExists = SG_FALSE;

    /* Load the treenode.  It should have exactly one entry, a subdirectory,
     * named @ */
	SG_ERR_CHECK(  SG_treenode__load_from_repo(pCtx, pRepo, pszidHidTreeNode, &pTreenode)  );
    SG_ERR_CHECK(  SG_treenode__get_nth_treenode_entry__ref(pCtx, pTreenode, 0, NULL, &pEntry)  );
    SG_ERR_CHECK(  SG_treenode_entry__get_hid_blob(pCtx, pEntry, &pszidHidContent)  );

#ifdef DEBUG
    {
        SG_uint32 count;
        SG_treenode_entry_type type;
        const char* pszName = NULL;

        SG_ERR_CHECK(  SG_treenode__count(pCtx, pTreenode, &count)  );
        SG_ASSERT(1 == count);

		SG_ERR_CHECK(  SG_treenode_entry__get_entry_type(pCtx, pEntry, &type)  );
		SG_ASSERT (SG_TREENODEENTRY_TYPE_DIRECTORY == type);

		SG_ERR_CHECK(  SG_treenode_entry__get_entry_name(pCtx, pEntry, &pszName)  );
        SG_ASSERT(0 == strcmp(pszName, "@"));
    }
#endif

    /* create the directory and then dive into it */
    SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pPathSub, pPathLocal)  );
	SG_ERR_CHECK(  SG_fsobj__exists__pathname(pCtx, pPathSub, &bExists, NULL, NULL)  );
	if (!bExists)
		SG_ERR_CHECK(  SG_fsobj__mkdir__pathname(pCtx, pPathSub)  );
    SG_ERR_CHECK(  _sg_workingdir__get_dir(pCtx, pRepo, pPathSub, pszidHidContent, pvhTimestamps)  );

#ifdef SG_BUILD_FLAG_FEATURE_XATTR
    /* fix up the xattrs on that directory */
    SG_ERR_CHECK(  SG_treenode_entry__get_hid_xattrs(pCtx, pEntry, &pszidHidXattrs)  );
    if (pszidHidXattrs)
    {
        SG_ERR_CHECK(  SG_repo__fetch_vhash(pCtx, pRepo, pszidHidXattrs, &pvhAttributes)  );
        SG_ERR_CHECK(  SG_attributes__xattrs__apply(pCtx, pPathSub, pvhAttributes, pRepo)  );
        SG_VHASH_NULLFREE(pCtx, pvhAttributes);
    }
#endif

    /* and the attrbits too */
    SG_ERR_CHECK(  SG_treenode_entry__get_attribute_bits(pCtx, pEntry, &iAttributeBits)  );
    SG_ERR_CHECK(  SG_attributes__bits__apply(pCtx, pPathSub, iAttributeBits)  );

    SG_PATHNAME_NULLFREE(pCtx, pPathSub);
	SG_TREENODE_NULLFREE(pCtx, pTreenode);

	return;
fail:
	/* TODO free stuff */
    SG_VHASH_NULLFREE(pCtx, pvhAttributes);
	SG_NULLFREE(pCtx, pBytes);
	SG_STRING_NULLFREE(pCtx, pstrLink);
}
Exemple #9
0
void SG_workingdir__find_mapping(
	SG_context* pCtx,
	const SG_pathname* pPathLocalDirectory,
	SG_pathname** ppPathMappedLocalDirectory, /**< Return the actual local directory that contains the mapping */
	SG_string** ppstrNameRepoInstanceDescriptor, /**< Return the name of the repo instance descriptor */
	char** ppszidGidAnchorDirectory /**< Return the GID of the repo directory */
	)
{
	SG_pathname* curpath = NULL;
	SG_string* result_pstrDescriptorName = NULL;
	char* result_pszidGid = NULL;
	SG_pathname* result_mappedLocalDirectory = NULL;
	SG_vhash* pvhMapping = NULL;
	SG_pathname* pDrawerPath = NULL;
	SG_pathname* pMappingFilePath = NULL;
	SG_vhash* pvh = NULL;

	SG_NULLARGCHECK_RETURN(pPathLocalDirectory);

	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &curpath, pPathLocalDirectory)  );

	/* it's a directory, so it should have a final slash */
	SG_ERR_CHECK(  SG_pathname__add_final_slash(pCtx, curpath)  );

	while (SG_TRUE)
	{
		SG_ERR_CHECK(  SG_workingdir__get_drawer_path(pCtx, curpath, &pDrawerPath)  );

		SG_fsobj__verify_directory_exists_on_disk__pathname(pCtx, pDrawerPath);
		if (!SG_context__has_err(pCtx))
		{
			const char* pszDescriptorName = NULL;
			const char* pszGid = NULL;

			SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pMappingFilePath, pDrawerPath, "repo.json")  );
			SG_PATHNAME_NULLFREE(pCtx, pDrawerPath);

			SG_ERR_CHECK(  SG_vfile__slurp(pCtx, pMappingFilePath, &pvh)  );

			SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvh, "mapping", &pvhMapping)  );

			SG_PATHNAME_NULLFREE(pCtx, pMappingFilePath);

			SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhMapping, "descriptor", &pszDescriptorName)  );
			SG_ERR_CHECK(  SG_vhash__get__sz(pCtx, pvhMapping, "anchor", &pszGid)  );

			SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &result_pstrDescriptorName)  );
			SG_ERR_CHECK(  SG_string__set__sz(pCtx, result_pstrDescriptorName, pszDescriptorName)  );

			if (pszGid)
			{
				SG_ERR_CHECK(  SG_gid__alloc_clone(pCtx, pszGid, &result_pszidGid)  );
			}
			else
			{
				result_pszidGid = NULL;
			}

			SG_VHASH_NULLFREE(pCtx, pvh);

			result_mappedLocalDirectory = curpath;
			curpath = NULL;

			break;
		}
		else
			SG_context__err_reset(pCtx);

		SG_PATHNAME_NULLFREE(pCtx, pDrawerPath);

		SG_pathname__remove_last(pCtx, curpath);

		if (SG_context__err_equals(pCtx, SG_ERR_CANNOTTRIMROOTDIRECTORY))
		{
			SG_context__err_reset(pCtx);
			break;
		}
		else
		{
			SG_ERR_CHECK_CURRENT;
		}
	}

	if (result_mappedLocalDirectory)
	{
        if (ppPathMappedLocalDirectory)
        {
		    *ppPathMappedLocalDirectory = result_mappedLocalDirectory;
        }
        else
        {
	        SG_PATHNAME_NULLFREE(pCtx, result_mappedLocalDirectory);
        }
        if (ppstrNameRepoInstanceDescriptor)
        {
            *ppstrNameRepoInstanceDescriptor = result_pstrDescriptorName;
        }
        else
        {
            SG_STRING_NULLFREE(pCtx, result_pstrDescriptorName);
        }
        if (ppszidGidAnchorDirectory)
        {
            *ppszidGidAnchorDirectory = result_pszidGid;
        }
        else
        {
            SG_NULLFREE(pCtx, result_pszidGid);
        }

		return;
	}
	else
	{
		SG_PATHNAME_NULLFREE(pCtx, curpath);

		SG_ERR_THROW_RETURN(SG_ERR_NOT_FOUND);
	}

fail:
	SG_VHASH_NULLFREE(pCtx, pvh);
	SG_PATHNAME_NULLFREE(pCtx, pDrawerPath);
	SG_PATHNAME_NULLFREE(pCtx, pMappingFilePath);
	SG_PATHNAME_NULLFREE(pCtx, result_mappedLocalDirectory);
	SG_PATHNAME_NULLFREE(pCtx, curpath);
}
/**
 * Import a raw string (probably as the user typed it) and
 * transform it into a repo-path.  And scrub it as appropriate.
 *
 * This routine is inside WC and takes a "pDb" because we need
 * to be able to interpret a null or relative path and convert
 * them into a wd-root-based current/live repo-path.
 *
 * But we also need to be able to call it for arbitrary-cset-based
 * repo-paths that when we don't have a WD.
 *
 * We return both the observed/computed repo-path and the
 * observed/computed repo-path-domain.  The returned repo-path
 * will contain the repo-path-domain so that they are always
 * in normal form.
 *
 * It is up to the caller to decide whether the domain is
 * valid for their purposes and/or throw.
 *
 */
void sg_wc_db__path__anything_to_repopath(SG_context * pCtx,
										  const sg_wc_db * pDb,
										  const char * pszInput,
										  sg_wc_db__path__import_flags flags,
										  SG_string ** ppStringRepoPath,
										  char * pcDomain)
{
	SG_pathname * pPathTemp = NULL;
	SG_string * pStringTemp = NULL;
	SG_bool bValid;

	if (!pszInput || !*pszInput)	// when null, assume either CWD or ROOT.
	{
		switch (flags)
		{
		default:
		case SG_WC_DB__PATH__IMPORT_FLAGS__TREAT_NULL_AS_ERROR:
			SG_ERR_THROW_RETURN(  SG_ERR_INVALIDARG  );

		case SG_WC_DB__PATH__IMPORT_FLAGS__TREAT_NULL_AS_ROOT:
			SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx, &pPathTemp, pDb->pPathWorkingDirectoryTop)  );
			SG_ERR_CHECK(  sg_wc_db__path__absolute_to_repopath(pCtx, pDb, pPathTemp, ppStringRepoPath)  );
			SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
			*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
			return;

		case SG_WC_DB__PATH__IMPORT_FLAGS__TREAT_NULL_AS_CWD:
			SG_ERR_CHECK(  SG_PATHNAME__ALLOC(pCtx, &pPathTemp)  );
			SG_ERR_CHECK(  SG_pathname__set__from_cwd(pCtx, pPathTemp)  );
			SG_ERR_CHECK(  sg_wc_db__path__absolute_to_repopath(pCtx, pDb, pPathTemp, ppStringRepoPath)  );
			SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
			*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
			return;
		}
	}

	if (pszInput[0] != '@')			// a non-repo pathname
	{
		SG_bool bPathIsRelative = SG_FALSE;
		SG_ERR_CHECK(  SG_pathname__is_relative(pCtx, pszInput, &bPathIsRelative)  );
		if (bPathIsRelative)
			SG_ERR_CHECK(  _check_if_relative_paths_allowed(pCtx, pDb)  );

		SG_ERR_CHECK(  SG_PATHNAME__ALLOC__SZ(pCtx, &pPathTemp, pszInput)  );
		SG_ERR_CHECK(  sg_wc_db__path__absolute_to_repopath(pCtx, pDb, pPathTemp, ppStringRepoPath)  );
		SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
		return;
	}

	// if they gave us something that started with a "@" we
	// either return it as is or throw if we can't parse it.
	// this ensures that any error messages don't leave the
	// domain out of the repo-path.

	switch (pszInput[1])
	{
	case 0:
		SG_ERR_THROW2_RETURN(  SG_ERR_INVALIDARG,
							   (pCtx, "The input '%s' is not a valid repo-path input.", pszInput)  );

	case SG_WC__REPO_PATH_DOMAIN__G:		// domain "@g..." is a GID and not a path.
		// A '@g' domain input should look like
		// "@gae9ce8e7eaf04b58aef9470b439329f32249aae2358511e1b77b002500da2b78"
		// That is, the 'g' domain specifier is shared with the 'g' in the
		// full GID.  For now a full GID is required; we don't try to do the
		// unique prefix trick like we do for CSET HIDs in a REV-SPEC.  We
		// may think about adding that later.

		SG_ERR_CHECK(  SG_gid__verify_format(pCtx, &pszInput[1], &bValid)  );
		if (!bValid)
			SG_ERR_THROW2(  SG_ERR_INVALIDARG,
							(pCtx, "The input '%s' is not a valid gid-domain input.", pszInput)  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__G;
		return;

	case SG_WC__REPO_PATH_DOMAIN__T:		// domain "@t..." is a temporary GID and not a path.
		// A '@t' domain input should look like
		// "@tae9ce8e7eaf04b58aef9470b439329f32249aae2358511e1b77b002500da2b78"
		// That is, the 't' domain specifier replaces the normal 'g' in the
		// full GID.  For now a full GID is required; we don't try to do the
		// unique prefix trick like we do for CSET HIDs in a REV-SPEC.  We
		// may think about adding that later.  We use a 't' when reporting
		// STATUS rather than a 'g' as a reminder that the ID is only valid
		// until the end of the transaction.
		//
		// make a g-version of the string so we can validate it.
		// but still return the t-version.

		SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pStringTemp)  );
		SG_ERR_CHECK(  SG_string__sprintf(pCtx, pStringTemp, "%c%s",
										  SG_WC__REPO_PATH_DOMAIN__G,
										  &pszInput[2])  );
		SG_ERR_CHECK(  SG_gid__verify_format(pCtx, SG_string__sz(pStringTemp), &bValid)  );
		SG_STRING_NULLFREE(pCtx, pStringTemp);
		if (!bValid)
			SG_ERR_THROW2(  SG_ERR_INVALIDARG,
							(pCtx, "The input '%s' is not a valid temp-gid-domain input.", pszInput)  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__T;
		return;

	case SG_WC__REPO_PATH_DOMAIN__LIVE:		// domain "@/foo..." is a normal current/live repo-path
		SG_ERR_CHECK(  _complain_if_unnormalized(pCtx, &pszInput[1])  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = SG_WC__REPO_PATH_DOMAIN__LIVE;
		return;

	default:		// we allow any other SINGLE character to be a domain.
		if (pszInput[2] != '/')
			SG_ERR_THROW2_RETURN(  SG_ERR_INVALIDARG,
								   (pCtx, "The input '%s' is not a valid repo-path input.", pszInput)  );

		// we have "@x/foo...." for some character x.
		SG_ERR_CHECK(  _complain_if_unnormalized(pCtx, &pszInput[2])  );
		SG_ERR_CHECK(  SG_STRING__ALLOC__SZ(pCtx, ppStringRepoPath, pszInput)  );
		*pcDomain = pszInput[1];
		return;
	}

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathTemp);
	SG_STRING_NULLFREE(pCtx, pStringTemp);
}