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); }
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); }
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); }
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); }
/** * 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); }
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); }