void SG_localsettings__update__varray(SG_context * pCtx, const char * psz_path, const SG_varray * pValue) { SG_jsondb* p = NULL; SG_string* pstr_path = NULL; SG_ASSERT(pCtx); SG_NONEMPTYCHECK_RETURN(psz_path); SG_NULLARGCHECK_RETURN(pValue); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr_path) ); if ('/' == psz_path[0]) { SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s", psz_path) ); } else { SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s", SG_LOCALSETTING__SCOPE__MACHINE, psz_path) ); } SG_ERR_CHECK( SG_closet__get_localsettings(pCtx, &p) ); SG_ERR_CHECK( SG_jsondb__update__varray(pCtx, p, SG_string__sz(pstr_path), SG_TRUE, pValue) ); fail: SG_JSONDB_NULLFREE(pCtx, p); SG_STRING_NULLFREE(pCtx, pstr_path); }
void SG_getopt__print_option(SG_context* pCtx, SG_console_stream cs, SG_getopt_option* opt, const char * overrideDesc) { SG_string *opts; if (opt == NULL) { SG_ERR_IGNORE( SG_console(pCtx, cs, "?") ); return; } SG_ERR_CHECK( SG_string__alloc(pCtx, &opts) ); /* We have a valid option which may or may not have a "short name" (a single-character alias for the long option). */ if (opt->optch <= 255) SG_ERR_CHECK( SG_string__sprintf(pCtx, opts, "-%c [--%s]", opt->optch, opt->pStringName) ); else SG_ERR_CHECK( SG_string__sprintf(pCtx, opts, "--%s", opt->pStringName) ); if (opt->has_arg) SG_ERR_CHECK( SG_string__append__sz(pCtx, opts, " ARG") ); if (overrideDesc) SG_ERR_IGNORE( SG_console(pCtx, cs, "%-20s : %s\n", SG_string__sz(opts), overrideDesc) ); else SG_ERR_IGNORE( SG_console(pCtx, cs, "%-20s : %s\n", SG_string__sz(opts), opt->pStringDescription) ); fail: SG_STRING_NULLFREE(pCtx, opts); }
void SG_localsettings__varray__append(SG_context * pCtx, const char* psz_path, const char* pValue) { SG_jsondb* p = NULL; SG_string* pstr = NULL; SG_varray* pva = NULL; SG_string* pstr_path_found = NULL; SG_ASSERT(pCtx); SG_NONEMPTYCHECK_RETURN(psz_path); SG_ERR_CHECK( SG_closet__get_localsettings(pCtx, &p) ); SG_ERR_CHECK( SG_localsettings__get__varray(pCtx, psz_path, NULL, &pva, &pstr_path_found) ); if (!pstr_path_found) { // this came from factory defaults. SG_ERR_CHECK( SG_string__alloc(pCtx, &pstr_path_found) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path_found, "%s/%s", SG_LOCALSETTING__SCOPE__MACHINE, psz_path) ); SG_ERR_CHECK( SG_localsettings__update__varray(pCtx, SG_string__sz(pstr_path_found), pva) ); } SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr, "%s/#", SG_string__sz(pstr_path_found)) ); SG_ERR_CHECK( SG_jsondb__update__string__sz(pCtx, p, SG_string__sz(pstr), SG_TRUE, pValue) ); fail: SG_STRING_NULLFREE(pCtx, pstr); SG_JSONDB_NULLFREE(pCtx, p); SG_VARRAY_NULLFREE(pCtx, pva); SG_STRING_NULLFREE(pCtx, pstr_path_found); }
void SG_localsettings__reset(SG_context * pCtx, const char* psz_path) { SG_jsondb* p = NULL; SG_string* pstr_path = NULL; SG_ASSERT(pCtx); SG_NONEMPTYCHECK_RETURN(psz_path); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr_path) ); if ('/' == psz_path[0]) { SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s", psz_path) ); } else { SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s", SG_LOCALSETTING__SCOPE__MACHINE, psz_path) ); } SG_ERR_CHECK( SG_closet__get_localsettings(pCtx, &p) ); SG_jsondb__remove(pCtx, p, SG_string__sz(pstr_path)); SG_ERR_CHECK_CURRENT_DISREGARD(SG_ERR_NOT_FOUND); fail: SG_JSONDB_NULLFREE(pCtx, p); SG_STRING_NULLFREE(pCtx, pstr_path); }
void SG_localsettings__varray__remove_first_match(SG_context * pCtx, const char* psz_path, const char* psz_val) { SG_jsondb* p = NULL; SG_string* pstr_path_element = NULL; SG_varray* pva = NULL; SG_uint32 ndx = 0; SG_uint32 count = 0; SG_uint32 i = 0; SG_bool b_found = SG_FALSE; SG_string* pstr_path_found = NULL; SG_ASSERT(pCtx); SG_NONEMPTYCHECK_RETURN(psz_path); SG_ERR_CHECK( SG_closet__get_localsettings(pCtx, &p) ); SG_ERR_CHECK( SG_localsettings__get__varray(pCtx, psz_path, NULL, &pva, &pstr_path_found) ); if (pva) { if (!pstr_path_found) { // this came from factory defaults. SG_ERR_CHECK( SG_string__alloc(pCtx, &pstr_path_found) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path_found, "%s/%s", SG_LOCALSETTING__SCOPE__MACHINE, psz_path) ); SG_ERR_CHECK( SG_localsettings__update__varray(pCtx, SG_string__sz(pstr_path_found), pva) ); } SG_ERR_CHECK( SG_varray__count(pCtx, pva, &count) ); for (i=0; i<count; i++) { const char* psz = NULL; SG_ERR_CHECK( SG_varray__get__sz(pCtx, pva, i, &psz) ); if (0 == strcmp(psz, psz_val)) { b_found = SG_TRUE; ndx = i; break; } } if (b_found) { SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr_path_element) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path_element, "%s/%d", SG_string__sz(pstr_path_found), ndx) ); SG_ERR_CHECK( SG_jsondb__remove(pCtx, p, SG_string__sz(pstr_path_element)) ); } } fail: SG_VARRAY_NULLFREE(pCtx, pva); SG_STRING_NULLFREE(pCtx, pstr_path_found); SG_STRING_NULLFREE(pCtx, pstr_path_element); SG_JSONDB_NULLFREE(pCtx, p); }
/** * Create a label for one side of a UNIFIED DIFF. This will appear on one of * the header lines after the "+++ " or "--- ". (We DO NOT create the "+++ " * portion.) * * Gnu-diff (when --label is not used) creates something like: * +++ %s\t%s with the file's relative pathname and date-time-modified. * * --- d1/date2 1970-01-01 00:00:00.000000000 +0000 * +++ d2/date2 2009-04-30 13:10:57.000000000 +0000 * * BZR's diff command creates something like: * * --- d2/date2 2009-04-30 12:38:41 +0000 * +++ d2/date2 2009-04-30 13:10:57 +0000 * * GIT's diff command creates something like: * * --- a/eeeeee.txt * +++ b/eeeeee.txt * * So, we can pretty much do whatever we want here. * * I'm going to try the following and see if we like it: * [] for a historical version, print: * +++ %s\t%s with the repo-path and the HID. * * we don't have a valid date-stamp to print. (our caller might have * the date-stamp on the changeset, but that doesn't have anything to * to with the date on an indivdual file (the file might not have even * been modified in that changeset). * * the HID may be useful later. * * [] for a working-directory version, print: * +++ %s\t%s with the repo-path and the live date-time-stamp. * * since the file is in the working-directory, the date stamp has * some validity (it doesn't mean that they changed the file or reflect * the last change (it could be date of the last get-latest for all we * know)). * * [] just for sanity, we allow a repo-path only version: * +++ %s * * In all cases we print the complete repo-path "@/a/b/c/foo.c". * The repo-path is as computed in SG_treediff2 and reflects all pathname * renames/moves. * * TODO do we care about feeding this output to PATCH and how it digests * pathnames and with the various -p0 -p1 ... arguments? * * We return a string that you must free. */ void SG_diff_utils__make_label(SG_context * pCtx, const char * szRepoPath, const char * szHid, const char * szDate, SG_string ** ppStringLabel) { SG_string * pString = NULL; SG_NONEMPTYCHECK_RETURN(szRepoPath); if (szHid && szDate) // allow zero or one, but not both SG_ERR_THROW_RETURN( SG_ERR_INVALIDARG ); SG_NULLARGCHECK(ppStringLabel); SG_ERR_CHECK_RETURN( SG_STRING__ALLOC(pCtx,&pString) ); ////////////////////////////////////////////////////////////////// // WARNING: All of the input arguments are in our internal NFC UTF-8 format // WARNING: (as is everything). SG_exec__() will convert whatever we construct // WARNING: here to an os-buffer (utf-8, locale, or wchar_t) before giving it // WARNING: to execvp() or CreateProcessW(). // WARNING: // WARNING: I *think* this is what we want. // WARNING: // WARNING: We can't be sure how the child process will handle the label text // WARNING: when it prints it to its STDOUT. This may cause some confusion. // WARNING: For example, NFC/NFD issues on MAC or some '?' characters getting // WARNING: printed by cygwin's version of gnu-diff and etc. // WARNING: // WARNING: I don't know if there are actually any issues here or not or if // WARNING: so whether they will cause a problem. ////////////////////////////////////////////////////////////////// if (szHid) SG_ERR_CHECK( SG_string__sprintf(pCtx, pString,"%s\t%s", szRepoPath,szHid) ); else if (szDate) SG_ERR_CHECK( SG_string__sprintf(pCtx, pString,"%s\t%s", szRepoPath,szDate) ); else SG_ERR_CHECK( SG_string__set__sz(pCtx,pString,szRepoPath) ); *ppStringLabel = pString; return; fail: SG_STRING_NULLFREE(pCtx, pString); }
/** * Create a pathname in the per-file temp-dir for one * version of the file. * * We use the ancestor version of the entryname * to avoid issues with pending renames. * */ void _sg_mrg__create_pathname_for_conflict_file(SG_context * pCtx, SG_mrg * pMrg, SG_mrg_cset_entry_conflict * pMrgCSetEntryConflict, const char * pszPrefix, SG_pathname ** ppPathReturned) { SG_pathname * pPath = NULL; SG_string * pString = NULL; SG_ERR_CHECK( _sg_mrg__ensure_temp_dir_for_file_conflict(pCtx, pMrg, pMrgCSetEntryConflict) ); // create something like: "<sgtemp>/<gid7>_YYYYMMDD_<k>/<prefix>~<entryname>" SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pString) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pString, "%s~%s", pszPrefix, SG_string__sz(pMrgCSetEntryConflict->pMrgCSetEntry_Ancestor->pStringEntryname)) ); SG_ERR_CHECK( SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPath, pMrgCSetEntryConflict->pPathTempDirForFile, SG_string__sz(pString)) ); SG_STRING_NULLFREE(pCtx, pString); *ppPathReturned = pPath; return; fail: SG_STRING_NULLFREE(pCtx, pString); SG_PATHNAME_NULLFREE(pCtx, pPath); }
void SG_sync__remember_sync_target(SG_context* pCtx, const char * pszLocalRepoDescriptor, const char * pszSyncTarget) { SG_string * pString = NULL; SG_varray * pva_targets = NULL; SG_bool bFound = SG_FALSE; SG_uint32 nEntry = 0; //Save this destination to the local setting of previously used destinations. SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pString) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pString, "%s/%s/%s", SG_LOCALSETTING__SCOPE__INSTANCE, pszLocalRepoDescriptor, SG_LOCALSETTING__SYNC_TARGETS) ); SG_ERR_CHECK( SG_localsettings__get__varray(pCtx, SG_string__sz(pString), NULL, &pva_targets, NULL) ); if (pva_targets) SG_ERR_CHECK( SG_varray__find__sz(pCtx, pva_targets, pszSyncTarget, &bFound, &nEntry) ); else SG_VARRAY__ALLOC(pCtx, &pva_targets); if (!bFound) { SG_ERR_CHECK( SG_varray__append__string__sz(pCtx, pva_targets, pszSyncTarget) ); SG_ERR_CHECK( SG_localsettings__update__varray(pCtx, SG_string__sz(pString), pva_targets) ); } fail: SG_STRING_NULLFREE(pCtx, pString); SG_VARRAY_NULLFREE(pCtx, pva_targets); }
void SG_localsettings__descriptor__update__sz( SG_context * pCtx, const char * psz_descriptor_name, const char * psz_path, const char * pValue) { SG_jsondb* p = NULL; SG_string* pstr_path = NULL; SG_ASSERT(pCtx); SG_NONEMPTYCHECK_RETURN(psz_descriptor_name); SG_NONEMPTYCHECK_RETURN(psz_path); SG_ARGCHECK_RETURN('/' != psz_path[0], psz_path); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr_path) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s/%s", SG_LOCALSETTING__SCOPE__INSTANCE, psz_descriptor_name, psz_path) ); SG_ERR_CHECK( SG_closet__get_localsettings(pCtx, &p) ); SG_ERR_CHECK( SG_jsondb__update__string__sz(pCtx, p, SG_string__sz(pstr_path), SG_TRUE, pValue) ); fail: SG_STRING_NULLFREE(pCtx, pstr_path); SG_JSONDB_NULLFREE(pCtx, p); }
void SG_workingdir__generate_and_create_temp_dir_for_purpose(SG_context * pCtx, const SG_pathname * pPathWorkingDirectoryTop, const char * pszPurpose, SG_pathname ** ppPathTempDir) { SG_pathname * pPathTempRoot = NULL; SG_pathname * pPath = NULL; SG_string * pString = NULL; SG_int64 iTimeUTC; SG_time tmLocal; SG_uint32 kAttempt = 0; SG_NONEMPTYCHECK_RETURN(pszPurpose); SG_NULLARGCHECK_RETURN(ppPathTempDir); // get path to "<wd-top>/.sgtemp". SG_ERR_CHECK( SG_workingdir__get_temp_path(pCtx,pPathWorkingDirectoryTop,&pPathTempRoot) ); SG_ERR_CHECK( SG_time__get_milliseconds_since_1970_utc(pCtx,&iTimeUTC) ); SG_ERR_CHECK( SG_time__decode__local(pCtx,iTimeUTC,&tmLocal) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx,&pString) ); while (1) { // build path "<wd-top>/.sgtemp/<purpose>_20091201_0". // where <purpose> is something like "revert" or "merge". SG_ERR_CHECK( SG_string__sprintf(pCtx,pString,"%s_%04d%02d%02d_%d", pszPurpose, tmLocal.year,tmLocal.month,tmLocal.mday, kAttempt++) ); SG_ERR_CHECK( SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx,&pPath,pPathTempRoot,SG_string__sz(pString)) ); // try to create a NEW temp directory. if this path already exists on disk, // loop and try again. if we have a hard errors, just give up. SG_fsobj__mkdir_recursive__pathname(pCtx,pPath); if (SG_context__has_err(pCtx) == SG_FALSE) goto success; if (SG_context__err_equals(pCtx,SG_ERR_DIR_ALREADY_EXISTS) == SG_FALSE) SG_ERR_RETHROW; SG_context__err_reset(pCtx); SG_PATHNAME_NULLFREE(pCtx,pPath); } success: *ppPathTempDir = pPath; SG_STRING_NULLFREE(pCtx, pString); SG_PATHNAME_NULLFREE(pCtx, pPathTempRoot); return; fail: SG_STRING_NULLFREE(pCtx, pString); SG_PATHNAME_NULLFREE(pCtx, pPathTempRoot); SG_PATHNAME_NULLFREE(pCtx, pPath); }
void SG_user__set_user__repo( SG_context* pCtx, SG_repo* pRepo, const char* psz_email ) { SG_vhash* pvh_user = NULL; char* psz_admin_id = NULL; const char* psz_userid = NULL; SG_string* pstr_path = NULL; SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr_path) ); if (pRepo) { SG_ERR_CHECK( SG_user__lookup_by_email(pCtx, pRepo, psz_email, &pvh_user) ); if (!pvh_user) { SG_ERR_THROW( SG_ERR_USER_NOT_FOUND ); } SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvh_user, SG_ZING_FIELD__RECID, &psz_userid) ); SG_ERR_CHECK( SG_repo__get_admin_id(pCtx, pRepo, &psz_admin_id) ); // we store this userid under the admin scope of the repo we were given SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s/%s", SG_LOCALSETTING__SCOPE__ADMIN, psz_admin_id, SG_LOCALSETTING__USERID ) ); SG_ERR_CHECK( SG_localsettings__update__sz(pCtx, SG_string__sz(pstr_path), psz_userid) ); } // AND we store this email address in machine scope for fallback lookups SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s", SG_LOCALSETTING__SCOPE__MACHINE, SG_LOCALSETTING__USEREMAIL ) ); SG_ERR_CHECK( SG_localsettings__update__sz(pCtx, SG_string__sz(pstr_path), psz_email) ); fail: SG_STRING_NULLFREE(pCtx, pstr_path); SG_NULLFREE(pCtx, psz_admin_id); SG_VHASH_NULLFREE(pCtx, pvh_user); }
/** * Convert an alias into a domain-specific repo-path * relative to this cset. This is based *STRICTLY* * upon the content of this tne_* table. We DO NOT * know anything about pending changes or the tbl_pc * table. * * We also DO NOT know anything about the association * of domain and which tne_* table to use; that is the * responsibility of the caller. * * For example, it is usually the case that we have the * domain mapping: * 'b' ==> "L0" * 'c' ==> "L1" * but we don't assume that. * */ void sg_wc_db__tne__get_extended_repo_path_from_alias(SG_context * pCtx, sg_wc_db * pDb, const sg_wc_db__cset_row * pCSetRow, SG_uint64 uiAliasGid, char chDomain, SG_string ** ppStringRepoPath) { SG_string * pStringRepoPath = NULL; sg_wc_db__tne_row * pTneRow = NULL; SG_NULLARGCHECK_RETURN( pDb ); SG_NULLARGCHECK_RETURN( pCSetRow ); SG_ARGCHECK_RETURN( (strchr(("abcdef" // 'g' is reserved "hijklmnopqrs" // 't' is reserved "uvwxyz" // '/' is reserved "0123456789"), chDomain)), chDomain ); SG_NULLARGCHECK_RETURN( ppStringRepoPath ); SG_ERR_CHECK( sg_wc_db__tne__get_row_by_alias(pCtx, pDb, pCSetRow, uiAliasGid, NULL, &pTneRow) ); SG_ASSERT( (pTneRow) ); if (pTneRow->p_s->uiAliasGidParent == SG_WC_DB__ALIAS_GID__NULL_ROOT) { SG_ASSERT_RELEASE_FAIL( (strcmp(pTneRow->p_s->pszEntryname, "@")==0) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pStringRepoPath) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pStringRepoPath, "@%c/", chDomain) ); } else { SG_ERR_CHECK( sg_wc_db__tne__get_extended_repo_path_from_alias(pCtx, pDb, pCSetRow, pTneRow->p_s->uiAliasGidParent, chDomain, &pStringRepoPath) ); SG_ASSERT( pStringRepoPath ); SG_ERR_CHECK( SG_repopath__append_entryname(pCtx, pStringRepoPath, pTneRow->p_s->pszEntryname, (pTneRow->p_s->tneType == SG_TREENODEENTRY_TYPE_DIRECTORY)) ); } *ppStringRepoPath = pStringRepoPath; pStringRepoPath = NULL; fail: SG_WC_DB__TNE_ROW__NULLFREE(pCtx, pTneRow); SG_STRING_NULLFREE(pCtx, pStringRepoPath); }
void MyFn(test__formatRfc850)(SG_context *pCtx) { const char *expected = "Tue, 16 Mar 2010 14:11:13 GMT"; SG_int32 epoch = 1268748673; SG_int64 sgtime = (SG_int64)epoch * 1000; SG_string *msg = NULL; char formatted[1024]; SG_ERR_CHECK( SG_time__formatRFC850(pCtx, sgtime, formatted, sizeof(formatted)) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &msg) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, msg, "formatting '%lli': expected '%s', got '%s'", sgtime, expected, formatted) ); VERIFY_COND(SG_string__sz(msg), strcmp(expected, formatted) == 0); fail: SG_STRING_NULLFREE(pCtx, msg); }
void MyFn(test__parseRfc850)(SG_context *pCtx) { const char *ims = "Tue, 16 Mar 2010 14:11:13 GMT"; SG_int32 epoch = 1268748673; SG_int64 sgtime = (SG_int64)epoch * 1000; SG_string *msg = NULL; SG_int64 parsed = 0, localparsed; SG_ERR_CHECK( SG_time__parseRFC850(pCtx, ims, &parsed, &localparsed) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &msg) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, msg, "parsing '%s': expected '%lli', got '%lli'", ims, (long long)sgtime, (long long)parsed) ); VERIFY_COND(SG_string__sz(msg), parsed == sgtime); fail: SG_STRING_NULLFREE(pCtx, msg); }
/** * Convert simple GID into a gid-domain repo-path. * */ void sg_wc_db__path__gid_to_gid_repopath(SG_context * pCtx, const char * pszGid, SG_string ** ppStringGidRepoPath) { SG_string * pString = NULL; SG_NONEMPTYCHECK_RETURN( pszGid ); SG_NULLARGCHECK_RETURN( ppStringGidRepoPath ); SG_ARGCHECK_RETURN( ((pszGid[0] == 'g') || (pszGid[0] == 't')), pszGid ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pString) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pString, "@%s", pszGid) ); *ppStringGidRepoPath = pString; return; fail: SG_STRING_NULLFREE(pCtx, pString); }
void sg_wc_db__path__generate_backup_path(SG_context * pCtx, const sg_wc_db * pDb, const char * pszGid, const char * pszEntryname, SG_pathname ** ppPathBackup) { SG_pathname * pPath = NULL; SG_string * pString = NULL; SG_uint32 k = 0; SG_bool bExists; // TODO 2011/10/25 assert no slashes in entryname. SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pString) ); for (k=0; k<100; k++) { SG_ERR_CHECK( SG_string__clear(pCtx, pString) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pString, SG_BACKUP_FORMAT, pszGid, pszEntryname, k) ); SG_ERR_CHECK( SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPath, pDb->pPathWorkingDirectoryTop, SG_string__sz(pString)) ); SG_ERR_CHECK( SG_fsobj__exists__pathname(pCtx, pPath, &bExists, NULL, NULL) ); if (!bExists) break; SG_PATHNAME_NULLFREE(pCtx, pPath); } if (k == 100) { SG_ERR_THROW2( SG_ERR_TOO_MANY_BACKUP_FILES, (pCtx, "%s", pszEntryname) ); } *ppPathBackup = pPath; pPath = NULL; fail: SG_PATHNAME_NULLFREE(pCtx, pPath); SG_STRING_NULLFREE(pCtx, pString); }
/** * 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); }
/** * Our caller is trying to create new repo and create a WD * mapped to it. The destination directory may or may not * have already existed on disk before we started. If we are * building upon an existing directory, verify that it doesn't * contain any submodules because we don't yet support them. * */ static void _check_for_nested_drawer(SG_context * pCtx, SG_wc_tx * pWcTx) { SG_varray * pvaStatus = NULL; SG_string * pString_MyDrawerRepoPath = NULL; SG_string * pString_MatchedRepoPath = NULL; const char * psz_MyDrawerName = NULL; const char * psz_MyDrawerRepoPath = NULL; SG_uint32 k, nrItems; if (pWcTx->bWeCreated_WD || pWcTx->bWeCreated_WD_Contents) return; SG_ERR_CHECK( SG_wc_tx__status(pCtx, pWcTx, NULL, SG_UINT32_MAX, SG_FALSE, // bListUnchanged SG_TRUE, // bNoIgnores SG_TRUE, // bNoTSC, SG_FALSE, // bListSparse SG_TRUE, // bListReserved SG_TRUE, // bNoSort, &pvaStatus, NULL) ); if (!pvaStatus) return; // TODO 2012/11/13 For now I'm just going to see if there is a // TODO .sgdrawer somewhere within the directory tree. // TODO In theory, we could have ADD/ADDREMOVE just // TODO look for them and refuse to add its parent // TODO directory, but I don't to even support that // TODO until we've properly dealt with submodules. // TODO // TODO So for now, if there is a WD deeply nested within // TODO this directory, we just complain. This is mainly // TODO to prevent accidents. (Because they can still // TODO manually move a sub-WD somehere deep into this // TODO directory at some point in the future.) SG_ERR_CHECK( SG_workingdir__get_drawer_directory_name(pCtx, &psz_MyDrawerName) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pString_MyDrawerRepoPath) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pString_MyDrawerRepoPath, "@/%s", psz_MyDrawerName) ); SG_ERR_CHECK( SG_repopath__ensure_final_slash(pCtx, pString_MyDrawerRepoPath) ); psz_MyDrawerRepoPath = SG_string__sz(pString_MyDrawerRepoPath); SG_ERR_CHECK( SG_varray__count(pCtx, pvaStatus, &nrItems) ); for (k=0; k<nrItems; k++) { SG_vhash * pvhItem; SG_vhash * pvhItemStatus; SG_bool bIsReserved; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaStatus, k, &pvhItem) ); SG_ERR_CHECK( SG_vhash__get__vhash(pCtx, pvhItem, "status", &pvhItemStatus) ); SG_ERR_CHECK( SG_vhash__has(pCtx, pvhItemStatus, "isReserved", &bIsReserved) ); if (bIsReserved) { // Don't freak out over the .sgdrawer that we just created in the root. const char * pszRepoPath; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhItem, "path", &pszRepoPath) ); if (strcmp(pszRepoPath, psz_MyDrawerRepoPath) != 0) { SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pString_MatchedRepoPath, pszRepoPath) ); SG_ERR_CHECK( SG_repopath__remove_last(pCtx, pString_MatchedRepoPath) ); SG_ERR_THROW2( SG_ERR_ENTRY_ALREADY_UNDER_VERSION_CONTROL, (pCtx, "The directory '%s' contains a working copy and submodules are not yet supported.", SG_string__sz(pString_MatchedRepoPath)) ); } } } fail: SG_STRING_NULLFREE(pCtx, pString_MatchedRepoPath); SG_STRING_NULLFREE(pCtx, pString_MyDrawerRepoPath); SG_VARRAY_NULLFREE(pCtx, pvaStatus); }
static void _s2__do_cset_vs_cset(SG_context * pCtx, const SG_option_state * pOptSt, const SG_stringarray * psaArgs, SG_uint32 * pNrErrors) { SG_varray * pvaStatus = NULL; SG_varray * pvaStatusDirtyFiles = NULL; SG_stringarray * psa1 = NULL; SG_string * pStringGidRepoPath = NULL; SG_string * pStringErr = NULL; SG_uint32 nrErrors = 0; SG_ERR_CHECK( SG_vv2__status(pCtx, pOptSt->psz_repo, pOptSt->pRevSpec, psaArgs, WC__GET_DEPTH(pOptSt), SG_FALSE, // bNoSort &pvaStatus, NULL) ); if (pvaStatus) { if (pOptSt->bInteractive) { // Filter list down to just modified files and show them one-by-one. SG_ERR_CHECK( _get_dirty_files(pCtx, pvaStatus, &pvaStatusDirtyFiles) ); if (pvaStatusDirtyFiles) SG_ERR_CHECK( _do_gui_diffs(pCtx, SG_FALSE, pOptSt, pvaStatusDirtyFiles, &nrErrors) ); } else { SG_uint32 k, nrItems; // Print the changes with PATCH-like headers. // Accumulate any tool errors. SG_ERR_CHECK( SG_varray__count(pCtx, pvaStatus, &nrItems) ); for (k=0; k<nrItems; k++) { SG_vhash * pvhItem; const char * pszGid = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pvaStatus, k, &pvhItem) ); // TODO 2013/02/22 Our pvhItem has all of the details for the diff, // TODO but we don't yet have a public API to let it be // TODO used as is. So we build a @gid repo-path and // TODO run the old historical diff code on a 1-item array // TODO containing this @gid. // TODO // TODO We should fix this to just pass down the pvhItem // TOOD so that it doesn't have to repeat the status lookup. SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhItem, "gid", &pszGid) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pStringGidRepoPath) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pStringGidRepoPath, "@%s", pszGid) ); SG_ERR_CHECK( SG_STRINGARRAY__ALLOC(pCtx, &psa1, 1) ); SG_ERR_CHECK( SG_stringarray__add(pCtx, psa1, SG_string__sz(pStringGidRepoPath)) ); SG_vv2__diff_to_stream__throw(pCtx, pOptSt->psz_repo, pOptSt->pRevSpec, psa1, 0, SG_TRUE, // bNoSort -- doesn't matter only 1 item in list SG_FALSE, // bInteractive, pOptSt->psz_tool); // Don't throw the error from the tool. Just print it on STDERR // and remember that we had an error so that don't stop showing // the diffs just because we stumble over a changed binary file // or mis-configured tool, for example. if (SG_context__has_err(pCtx)) { SG_context__err_to_string(pCtx, SG_FALSE, &pStringErr); SG_context__err_reset(pCtx); SG_ERR_CHECK( SG_console__raw(pCtx, SG_CS_STDERR, SG_string__sz(pStringErr)) ); SG_STRING_NULLFREE(pCtx, pStringErr); nrErrors++; } SG_STRING_NULLFREE(pCtx, pStringGidRepoPath); SG_STRINGARRAY_NULLFREE(pCtx, psa1); } } } *pNrErrors = nrErrors; fail: SG_VARRAY_NULLFREE(pCtx, pvaStatus); SG_VARRAY_NULLFREE(pCtx, pvaStatusDirtyFiles); SG_STRINGARRAY_NULLFREE(pCtx, psa1); SG_STRING_NULLFREE(pCtx, pStringGidRepoPath); SG_STRING_NULLFREE(pCtx, pStringErr); }
/** * Do diff of an individual item. * When WC-based, we have a "DiffStep" vhash. * When historical, we have an item from a pvaStatus. * */ static void _do_diff1(SG_context * pCtx, SG_bool bWC, const SG_option_state * pOptSt, const SG_vhash * pvhItem, SG_uint32 * piResult) { SG_string * pStringGidRepoPath = NULL; SG_vhash * pvhResultCodes = NULL; SG_stringarray * psa1 = NULL; const char * pszGid; SG_int64 i64Result = 0; SG_string * pStringErr = NULL; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhItem, "gid", &pszGid) ); if (bWC) { SG_pathname * pPathWc = NULL; SG_bool bHasTool = SG_FALSE; // With the __diff__setup() and __diff__run() changes, we have already // examined the items during the __setup() step and recorded a tool for // the *FILE* that have changed content. So if "tool" isn't set in the // DiffStep/Item, we don't need to diff it -- it could be a structural // change, a non-file, a found item, etc. // // we do not use SG_wc__diff__throw() because we already have the diff info // and we want to control the result-code processing below. SG_ERR_CHECK( SG_vhash__has(pCtx, pvhItem, "tool", &bHasTool) ); if (bHasTool) SG_ERR_CHECK( SG_wc__diff__run(pCtx, pPathWc, pvhItem, &pvhResultCodes) ); } else { SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pStringGidRepoPath) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pStringGidRepoPath, "@%s", pszGid) ); SG_ERR_CHECK( SG_STRINGARRAY__ALLOC(pCtx, &psa1, 1) ); SG_ERR_CHECK( SG_stringarray__add(pCtx, psa1, SG_string__sz(pStringGidRepoPath)) ); // we do not use the __throw() version of this routine so we can control // result-code processing below. SG_ERR_CHECK( SG_vv2__diff_to_stream(pCtx, pOptSt->psz_repo, pOptSt->pRevSpec, psa1, 0, SG_FALSE, // bNoSort SG_TRUE, // bInteractive, pOptSt->psz_tool, &pvhResultCodes) ); } if (pvhResultCodes) { SG_vhash * pvhResult; // we do not own this SG_ERR_CHECK( SG_vhash__check__vhash(pCtx, pvhResultCodes, pszGid, &pvhResult) ); if (pvhResult) { const char * pszTool; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pvhResult, "tool", &pszTool) ); SG_ERR_CHECK( SG_vhash__get__int64(pCtx, pvhResult, "result", &i64Result) ); SG_difftool__check_result_code__throw(pCtx, i64Result, pszTool); if (SG_context__has_err(pCtx)) { SG_context__err_to_string(pCtx, SG_FALSE, &pStringErr); SG_context__err_reset(pCtx); SG_ERR_CHECK( SG_console__raw(pCtx, SG_CS_STDERR, SG_string__sz(pStringErr)) ); // eat the tool error. the result code is set. } } } if (piResult) *piResult = (SG_uint32)i64Result; fail: SG_STRING_NULLFREE(pCtx, pStringGidRepoPath); SG_VHASH_NULLFREE(pCtx, pvhResultCodes); SG_STRINGARRAY_NULLFREE(pCtx, psa1); SG_STRING_NULLFREE(pCtx, pStringErr); }
static void _fillInUserSelection( SG_context *pCtx, SG_string *pstrRepoDescriptorName, SG_string *replacement) { SG_repo *repo = NULL; SG_varray *users = NULL; SG_vhash *user = NULL; SG_uint32 i = 0; SG_uint32 count; SG_string *semail = NULL; SG_string *suid = NULL; SG_string *entry = NULL; SG_string *curuid = NULL; SG_ERR_CHECK( SG_string__clear(pCtx, replacement) ); SG_ERR_CHECK( SG_repo__open_repo_instance(pCtx, SG_string__sz(pstrRepoDescriptorName), &repo) ); SG_ERR_CHECK( SG_user__list_all(pCtx, repo, &users) ); SG_ERR_CHECK( SG_varray__count(pCtx, users, &count) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &semail) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &suid) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &entry) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &curuid) ); SG_ERR_CHECK( _getUserId(pCtx, repo, curuid) ); for ( i = 0; i < count; ++i ) { const char *uid = NULL; const char *email = NULL; const char *selected = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, users, i, &user) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, user, "recid", &uid) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, user, "email", &email) ); SG_ERR_CHECK( _getEncoded(pCtx, uid, suid) ); SG_ERR_CHECK( _getEncoded(pCtx, email, semail) ); if (eq(SG_string__sz(curuid), uid)) { selected = " selected='selected' "; } else { selected = ""; } SG_ERR_CHECK( SG_string__sprintf(pCtx, entry, "<option value=\"%s\" %s>%s</option>", SG_string__sz(suid), selected, SG_string__sz(semail)) ); SG_ERR_CHECK( SG_string__append__string(pCtx, replacement, entry) ); } fail: SG_VARRAY_NULLFREE(pCtx, users); SG_REPO_NULLFREE(pCtx, repo); SG_STRING_NULLFREE(pCtx, semail); SG_STRING_NULLFREE(pCtx, suid); SG_STRING_NULLFREE(pCtx, entry); SG_STRING_NULLFREE(pCtx, curuid); }
void SG_localsettings__get__variant(SG_context * pCtx, const char * psz_path, SG_repo* pRepo, SG_variant** ppv, SG_string** ppstr_where_found) { SG_jsondb* p = NULL; SG_string* pstr_path = NULL; SG_variant* pv = NULL; char* psz_repo_id = NULL; char* psz_admin_id = NULL; const char* psz_ref_descriptor_name = NULL; SG_ASSERT(pCtx); SG_NONEMPTYCHECK_RETURN(psz_path); SG_ERR_CHECK( SG_closet__get_localsettings(pCtx, &p) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pstr_path) ); if ('/' == psz_path[0]) { SG_bool b = SG_FALSE; SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s", psz_path) ); SG_ERR_CHECK( SG_jsondb__has(pCtx, p, SG_string__sz(pstr_path), &b) ); if (b) { SG_ERR_CHECK( SG_jsondb__get__variant(pCtx, p, SG_string__sz(pstr_path), &pv) ); } } else { SG_bool b_has_val = SG_FALSE; // try the instance of the repo if (!b_has_val && pRepo) { SG_ERR_CHECK( SG_repo__get_descriptor_name(pCtx, pRepo, &psz_ref_descriptor_name) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s/%s", SG_LOCALSETTING__SCOPE__INSTANCE, psz_ref_descriptor_name, psz_path ) ); SG_ERR_CHECK( SG_jsondb__has(pCtx, p, SG_string__sz(pstr_path), &b_has_val) ); if (b_has_val) { SG_ERR_CHECK( SG_jsondb__get__variant(pCtx, p, SG_string__sz(pstr_path), &pv) ); } } // then the repo if (!b_has_val && pRepo) { SG_ERR_CHECK( SG_repo__get_repo_id(pCtx, pRepo, &psz_repo_id) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s/%s", SG_LOCALSETTING__SCOPE__REPO, psz_repo_id, psz_path ) ); SG_ERR_CHECK( SG_jsondb__has(pCtx, p, SG_string__sz(pstr_path), &b_has_val) ); if (b_has_val) { SG_ERR_CHECK( SG_jsondb__get__variant(pCtx, p, SG_string__sz(pstr_path), &pv) ); } } // then the admin group of repos if (!b_has_val && pRepo) { SG_ERR_CHECK( SG_repo__get_admin_id(pCtx, pRepo, &psz_admin_id) ); SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s/%s", SG_LOCALSETTING__SCOPE__ADMIN, psz_admin_id, psz_path ) ); SG_ERR_CHECK( SG_jsondb__has(pCtx, p, SG_string__sz(pstr_path), &b_has_val) ); if (b_has_val) { SG_ERR_CHECK( SG_jsondb__get__variant(pCtx, p, SG_string__sz(pstr_path), &pv) ); } } // then the machine if (!b_has_val) { SG_ERR_CHECK( SG_string__sprintf(pCtx, pstr_path, "%s/%s", SG_LOCALSETTING__SCOPE__MACHINE, psz_path) ); SG_ERR_CHECK( SG_jsondb__has(pCtx, p, SG_string__sz(pstr_path), &b_has_val) ); if (b_has_val) { SG_ERR_CHECK( SG_jsondb__get__variant(pCtx, p, SG_string__sz(pstr_path), &pv) ); } } // then the factory default if (!b_has_val) { SG_STRING_NULLFREE(pCtx, pstr_path); SG_ERR_CHECK( SG_localsettings__factory__get__variant(pCtx, psz_path, &pv) ); } } *ppv = pv; pv = NULL; if (ppstr_where_found) { *ppstr_where_found = pstr_path; pstr_path = NULL; } fail: SG_NULLFREE(pCtx, psz_repo_id); SG_NULLFREE(pCtx, psz_admin_id); SG_VARIANT_NULLFREE(pCtx, pv); SG_STRING_NULLFREE(pCtx, pstr_path); SG_JSONDB_NULLFREE(pCtx, p); }