void SG_password__forget_all(SG_context *pCtx) { SG_string* pstrFilter = NULL; WCHAR* pwszFilter = NULL; DWORD numCreds = 0; PCREDENTIAL* paCreds = NULL; SG_uint32 i; SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pstrFilter, KEY_PREFIX) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrFilter, KEY_SEPARATOR) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrFilter, "*") ); SG_ERR_CHECK( SG_utf8__extern_to_os_buffer__wchar(pCtx, SG_string__sz(pstrFilter), &pwszFilter, NULL) ); if ( !CredEnumerateW( pwszFilter, 0, &numCreds, &paCreds) ) { DWORD err = GetLastError(); if ( ERROR_NOT_FOUND != err) SG_ERR_THROW2( SG_ERR_GETLASTERROR(err), (pCtx, "%s", "unable to enumerate saved credentials") ); } for (i = 0; i < numCreds; i++) { if ( !CredDeleteW( paCreds[i]->TargetName, CRED_TYPE_GENERIC, 0) ) SG_ERR_THROW2( SG_ERR_GETLASTERROR(GetLastError()), (pCtx, "%s", "unable to delete credential") ); } /* fall through */ fail: SG_NULLFREE(pCtx, pwszFilter); SG_STRING_NULLFREE(pCtx, pstrFilter); }
void SG_dagfrag_debug__dump(SG_context * pCtx, SG_dagfrag * pFrag, const char * szLabel, SG_uint32 indent, SG_string * pStringOutput) { char buf[4000]; struct _dump_data dump_data; dump_data.indent = indent+4; dump_data.nrDigits = 6; dump_data.pStringOutput = pStringOutput; SG_ERR_CHECK_RETURN( SG_sprintf(pCtx, buf,SG_NrElements(buf),"%*cDagFrag[%p] [%s]\n",indent,' ',pFrag,szLabel) ); SG_ERR_CHECK_RETURN( SG_string__append__sz(pCtx, pStringOutput,buf) ); dump_data.stateWanted = SG_DFS_START_MEMBER; SG_ERR_CHECK_RETURN( SG_sprintf(pCtx, buf,SG_NrElements(buf),"%*cStartMembers:\n",indent+2,' ') ); SG_ERR_CHECK_RETURN( SG_string__append__sz(pCtx, pStringOutput,buf) ); SG_ERR_CHECK_RETURN( SG_rbtree__foreach(pCtx, pFrag->m_pRB_Cache,_dump_cb,&dump_data) ); dump_data.stateWanted = SG_DFS_INTERIOR_MEMBER; SG_ERR_CHECK_RETURN( SG_sprintf(pCtx,buf,SG_NrElements(buf),"%*cInteriorMembers:\n",indent+2,' ') ); SG_ERR_CHECK_RETURN( SG_string__append__sz(pCtx,pStringOutput,buf) ); SG_ERR_CHECK_RETURN( SG_rbtree__foreach(pCtx,pFrag->m_pRB_Cache,_dump_cb,&dump_data) ); dump_data.stateWanted = SG_DFS_END_FRINGE; SG_ERR_CHECK_RETURN( SG_sprintf(pCtx,buf,SG_NrElements(buf),"%*cEndFringe:\n",indent+2,' ') ); SG_ERR_CHECK_RETURN( SG_string__append__sz(pCtx,pStringOutput,buf) ); SG_ERR_CHECK_RETURN( SG_rbtree__foreach(pCtx,pFrag->m_pRB_Cache,_dump_cb,&dump_data) ); }
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); }
static void _get_key(SG_context* pCtx, const char* szRepoSpec, const char* szUserName, SG_string** ppstrKey) { SG_string* pstrKey = NULL; SG_string* pstrHostname = NULL; SG_ERR_CHECK( _get_host(pCtx, szRepoSpec, &pstrHostname) ); SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pstrKey, KEY_PREFIX KEY_SEPARATOR) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrKey, SG_string__sz(pstrHostname)) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrKey, KEY_SEPARATOR) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrKey, szUserName) ); *ppstrKey = pstrKey; pstrKey = NULL; /* fall through */ fail: SG_STRING_NULLFREE(pCtx, pstrKey); SG_STRING_NULLFREE(pCtx, pstrHostname); }
static void my_dump_id(SG_context * pCtx, const char* psz_hid, SG_uint32 nrDigits, SG_uint32 indent, SG_string * pStringOutput) { char buf[4000]; // we can abbreviate the full IDs. nrDigits = SG_MIN(nrDigits, SG_HID_MAX_BUFFER_LENGTH); nrDigits = SG_MAX(nrDigits, 4); // create: // Dagnode[addr]: <child_id> <gen> [<parent_id>...] SG_ERR_CHECK_RETURN( SG_sprintf(pCtx,buf,SG_NrElements(buf),"%*c ",indent,' ') ); SG_ERR_CHECK_RETURN( SG_string__append__sz(pCtx,pStringOutput,buf) ); SG_ERR_CHECK_RETURN( SG_string__append__buf_len(pCtx,pStringOutput,(SG_byte *)(psz_hid),nrDigits) ); SG_ERR_CHECK_RETURN( SG_string__append__sz(pCtx,pStringOutput,"\n") ); }
static void _get_sync_url(SG_context* pCtx, const char* pszBaseUrl, const char* pszUrlSuffix, const char* pszPushId, const char* pszQueryString, char** ppszUrl) { SG_string* pstrUrl = NULL; SG_ERR_CHECK( SG_STRING__ALLOC__RESERVE(pCtx, &pstrUrl, 1024) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrUrl, pszBaseUrl) ); if (pszUrlSuffix) SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrUrl, pszUrlSuffix) ); if (pszPushId) { SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrUrl, "/") ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrUrl, pszPushId) ); } if (pszQueryString) { SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrUrl, "?") ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pstrUrl, pszQueryString) ); } SG_ERR_CHECK( SG_string__sizzle(pCtx, &pstrUrl, (SG_byte**)ppszUrl, NULL) ); return; fail: SG_STRING_NULLFREE(pCtx, pstrUrl); }
void SG_validate__sanitize( SG_context* pCtx, const char* szValue, SG_uint32 uMin, SG_uint32 uMax, const char* szInvalids, SG_uint32 uFixFlags, const char* szReplace, const char* szAdd, SG_string** ppSanitized ) { SG_string* sSanitized = NULL; SG_NULLARGCHECK(ppSanitized); // treat NULL replacement string as empty if (szReplace == NULL) { szReplace = ""; } // allocate our result string SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &sSanitized, szValue) ); // if we need to sanitize bad characters, do that // Note: We do this first because sanitizing characters might change the // length of the string and affect the min/max length check. if (uFixFlags & SG_VALIDATE__RESULT__INVALID_CHARACTER) { SG_ERR_CHECK( _replace_chars_with_string(pCtx, sSanitized, szInvalids, szReplace) ); } if (uFixFlags & SG_VALIDATE__RESULT__CONTROL_CHARACTER) { SG_ERR_CHECK( _replace_chars_with_string(pCtx, sSanitized, SG_VALIDATE__CHARS__CONTROL, szReplace) ); } // if we need to lengthen the string, do that // Note: We do this prior to checking the max length because we have more fine // grained control over reducing length than we do over expanding it. We // can remove individual characters, but only add characters in blocks of // strlen(szAdd). If uMin and uMax are close to each other, then adding // a single szAdd might take us over uMax. If that happens, we want to // be able to trim that back down to uMax afterward. if (uFixFlags & SG_VALIDATE__RESULT__TOO_SHORT) { SG_uint32 uSanitized = 0u; SG_uint32 uAdd = 0u; SG_ARGCHECK(szAdd != NULL && SG_STRLEN(szAdd) > 0u, szAdd); // get the length of both strings SG_ERR_CHECK( SG_utf8__length_in_characters__sz(pCtx, SG_string__sz(sSanitized), &uSanitized) ); SG_ERR_CHECK( SG_utf8__length_in_characters__sz(pCtx, szAdd, &uAdd) ); // keep adding until the sanitized string is long enough while (uSanitized < uMin) { SG_ERR_CHECK( SG_string__append__sz(pCtx, sSanitized, szAdd) ); uSanitized += uAdd; } } // if we need to shorten the string, do that if (uFixFlags & SG_VALIDATE__RESULT__TOO_LONG) { SG_ERR_CHECK( _truncate_string(pCtx, sSanitized, uMax) ); } // return the sanitized result *ppSanitized = sSanitized; sSanitized = NULL; fail: SG_STRING_NULLFREE(pCtx, sSanitized); return; }
void SG_cmd_util__get_username_for_repo( SG_context *pCtx, const char *szRepoName, char **ppUsername ) { SG_string * pUsername = NULL; SG_repo * pRepo = NULL; char * psz_username = NULL; SG_curl * pCurl = NULL; SG_string * pUri = NULL; SG_string * pResponse = NULL; SG_int32 responseStatusCode = 0; SG_vhash * pRepoInfo = NULL; char * psz_userid = NULL; SG_varray * pUsers = NULL; SG_NULLARGCHECK_RETURN(ppUsername); if(!szRepoName) { // Look up username based on 'whoami' of repo associated with cwd. SG_ERR_IGNORE( SG_cmd_util__get_repo_from_cwd(pCtx, &pRepo, NULL) ); if(pRepo) SG_ERR_IGNORE( SG_user__get_username_for_repo(pCtx, pRepo, &psz_username) ); SG_REPO_NULLFREE(pCtx, pRepo); } else if(SG_sz__starts_with(szRepoName, "http://") || SG_sz__starts_with(szRepoName, "https://")) { // Look up username based on 'whoami' of admin id of remote repo. SG_ERR_CHECK( SG_curl__alloc(pCtx, &pCurl) ); SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pUri, szRepoName) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pUri, ".json") ); SG_ERR_CHECK( SG_curl__reset(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__setopt__sz(pCtx, pCurl, CURLOPT_URL, SG_string__sz(pUri)) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pResponse) ); SG_ERR_CHECK( SG_curl__set__write_string(pCtx, pCurl, pResponse) ); SG_ERR_CHECK( SG_curl__perform(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__getinfo__int32(pCtx, pCurl, CURLINFO_RESPONSE_CODE, &responseStatusCode) ); if(responseStatusCode==200) { const char * szAdminId = NULL; SG_ERR_CHECK( SG_VHASH__ALLOC__FROM_JSON__STRING(pCtx, &pRepoInfo, pResponse) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pRepoInfo, SG_SYNC_REPO_INFO_KEY__ADMIN_ID, &szAdminId) ); SG_ERR_CHECK( SG_string__clear(pCtx, pUri) ); SG_ERR_CHECK( SG_string__append__format(pCtx, pUri, "/admin/%s/whoami/userid", szAdminId) ); SG_ERR_IGNORE( SG_localsettings__get__sz(pCtx, SG_string__sz(pUri), NULL, &psz_userid, NULL) ); if(psz_userid) { // We now have the userid. Look up the username. SG_ERR_CHECK( SG_string__clear(pCtx, pUri) ); SG_ERR_CHECK( SG_string__append__format(pCtx, pUri, "%s/users.json", szRepoName) ); SG_ERR_CHECK( SG_curl__reset(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__setopt__sz(pCtx, pCurl, CURLOPT_URL, SG_string__sz(pUri)) ); SG_ERR_CHECK( SG_string__clear(pCtx, pResponse) ); SG_ERR_CHECK( SG_curl__set__write_string(pCtx, pCurl, pResponse) ); SG_ERR_CHECK( SG_curl__perform(pCtx, pCurl) ); SG_ERR_CHECK( SG_curl__getinfo__int32(pCtx, pCurl, CURLINFO_RESPONSE_CODE, &responseStatusCode) ); if(responseStatusCode==200) { SG_uint32 i, nUsers; SG_ERR_CHECK( SG_VARRAY__ALLOC__FROM_JSON__STRING(pCtx, &pUsers, pResponse) ); SG_ERR_CHECK( SG_varray__count(pCtx, pUsers, &nUsers) ); for(i=0; i<nUsers; ++i) { SG_vhash * pUser = NULL; const char * psz_recid = NULL; SG_ERR_CHECK( SG_varray__get__vhash(pCtx, pUsers, i, &pUser) ); SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pUser, "recid", &psz_recid) ); if(!strcmp(psz_recid, psz_userid)) { const char * psz_name = NULL; SG_ERR_CHECK( SG_vhash__get__sz(pCtx, pUser, "name", &psz_name) ); SG_ERR_CHECK( SG_STRDUP(pCtx, psz_name, &psz_username) ); break; } } SG_VARRAY_NULLFREE(pCtx, pUsers); } SG_NULLFREE(pCtx, psz_userid); } SG_VHASH_NULLFREE(pCtx, pRepoInfo); } SG_STRING_NULLFREE(pCtx, pResponse); SG_STRING_NULLFREE(pCtx, pUri); SG_CURL_NULLFREE(pCtx, pCurl); } else { // Look up username based on 'whoami' of repo provided. SG_ERR_CHECK( SG_REPO__OPEN_REPO_INSTANCE(pCtx, szRepoName, &pRepo) ); SG_ERR_IGNORE( SG_user__get_username_for_repo(pCtx, pRepo, &psz_username) ); SG_REPO_NULLFREE(pCtx, pRepo); } *ppUsername = psz_username; return; fail: SG_STRING_NULLFREE(pCtx, pUsername); SG_REPO_NULLFREE(pCtx, pRepo); SG_NULLFREE(pCtx, psz_username); SG_CURL_NULLFREE(pCtx, pCurl); SG_STRING_NULLFREE(pCtx, pUri); SG_STRING_NULLFREE(pCtx, pResponse); SG_VHASH_NULLFREE(pCtx, pRepoInfo); SG_NULLFREE(pCtx, psz_userid); SG_VARRAY_NULLFREE(pCtx, pUsers); }
SG_error SG_context__err_to_string(SG_context* pCtx, SG_string** ppErrString) { SG_error err = SG_ERR_OK; char szErr[SG_ERROR_BUFFER_SIZE]; SG_string* pTempStr = NULL; SG_uint32 lenRequired; SG_ASSERT( pCtx ); // SG_ASSERT( pCtx->level < SG_CONTEXT_MAX_ERROR_LEVELS ); if (!ppErrString) return SG_ERR_INVALIDARG; if (pCtx->level > 0) { // when in an error-on-error (level > 0), the error string DOES NOT belong to the // error context/level that we are in. The full message is only defined for the // original error that triggered things. *ppErrString = NULL; return SG_ERR_OK; } SG_error__get_message(pCtx->errValues[pCtx->level], szErr, sizeof(szErr)); lenRequired = ( strlen(szErr) + strlen(pCtx->szDescription) + pCtx->lenStackTrace + 100); // do all of the formatting in an error-on-error context/level // so that none of the string allocation/manipulation trashes // the current error context. // // ***DO NOT JUMP OUT OF THIS PUSH..POP BLOCK.*** SG_context__push_level(pCtx); { SG_STRING__ALLOC__RESERVE(pCtx,&pTempStr,lenRequired); if (SG_IS_OK(pCtx->errValues[pCtx->level])) { // since we pre-reserved all of the space we require, we // don't expect any of the appends to fail. So we can // ignore intermediate errors after each step. if one of // them does have a problem, the subsequent statements will // fail because we already have an error at this level. // either way, we don't care because we're going to pop the // level in a minute anyway. if (szErr[0] != 0) { SG_string__append__sz(pCtx, pTempStr, szErr); if (pCtx->szDescription[0] != 0) SG_string__append__sz(pCtx, pTempStr, ": "); else SG_string__append__sz(pCtx, pTempStr, "."); } if (pCtx->szDescription[0] != 0) SG_string__append__sz(pCtx, pTempStr, pCtx->szDescription); if (szErr[0] != 0 || pCtx->szDescription[0] != 0) SG_string__append__sz(pCtx, pTempStr, "\n"); SG_string__append__sz(pCtx, pTempStr, pCtx->szStackTrace); // if all of the formating works, we return the allocated string. // if not, we delete it and return the formatting error. if (SG_IS_OK(pCtx->errValues[pCtx->level])) *ppErrString = pTempStr; else SG_STRING_NULLFREE(pCtx, pTempStr); } err = pCtx->errValues[pCtx->level]; // we return the error value of the allocation/formatting } SG_context__pop_level(pCtx); return err; }
static void _templatize( SG_context *pCtx, SG_string *content, const _request_headers *pRequestHeaders, _replacer_cb replacer) { SG_string *instr = NULL; const char *next = NULL; SG_string *piece = NULL; SG_string *replacement = NULL; SG_string *inclusion = NULL; SG_string *replaceRaw = NULL; SG_ERR_CHECK( SG_STRING__ALLOC__COPY(pCtx, &instr, content) ); SG_ERR_CHECK( SG_string__clear(pCtx, content) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &piece) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &replacement) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &replaceRaw) ); next = SG_string__sz(instr); while (*next) { const char *delim = strstr(next, "{{{"); if (delim == NULL) { SG_ERR_CHECK( SG_string__append__sz(pCtx, content, next) ); break; } else { const char *rest = delim + 3; const char *end = strstr(rest, "}}}"); if (end == NULL) { SG_ERR_CHECK( SG_string__append__sz(pCtx, content, next) ); break; } SG_ERR_CHECK( SG_string__append__buf_len(pCtx, content, (const SG_byte *)next, (delim - next) * sizeof(char)) ); SG_ERR_CHECK( SG_string__clear(pCtx, piece) ); SG_ERR_CHECK( SG_string__clear(pCtx, replacement) ); SG_ERR_CHECK( SG_string__append__buf_len(pCtx, piece, (const SG_byte *)rest, (end - rest) * sizeof(char)) ); SG_ERR_CHECK( SG_string__append__string(pCtx, replacement, piece) ); if (SG_string__sz(piece)[0] == '<') { SG_string__remove(pCtx, piece, 0, 1); SG_ASSERT(inclusion == NULL); SG_ERR_CHECK( _read_template_file(pCtx, SG_string__sz(piece), &inclusion, pRequestHeaders, replacer) ); SG_STRING_NULLFREE(pCtx, replacement); replacement = inclusion; inclusion = NULL; } else { SG_bool needEncoding = SG_TRUE; SG_ERR_CHECK( replacer(pCtx, pRequestHeaders, piece, replaceRaw) ); // no verb-specific replacement? try generics if (sgeq(piece, replacement)) { SG_ERR_CHECK( _default_replacer(pCtx, pRequestHeaders, piece, replaceRaw, &needEncoding) ); } if (needEncoding) SG_ERR_CHECK( SG_htmlencode(pCtx, replaceRaw, replacement) ); else SG_ERR_CHECK( SG_string__set__string(pCtx, replacement, replaceRaw) ); } SG_ERR_CHECK( SG_string__append__string(pCtx, content, replacement) ); next = end + 3; } } fail: SG_STRING_NULLFREE(pCtx, instr); SG_STRING_NULLFREE(pCtx, piece); SG_STRING_NULLFREE(pCtx, replacement); SG_STRING_NULLFREE(pCtx, replaceRaw); SG_STRING_NULLFREE(pCtx, inclusion); }
/** * Provides each value from a vhash whose name matches a pattern to another callback. * Values that are themselves vhashes are recursed into. * Each value's name is prefixed such that it's fully-qualified when passed to the callback. * Intended for use as a SG_vhash_foreach_callback. */ static void provide_matching_values( SG_context* pCtx, //< Error and context information. void* pCallerData, //< An allocated instance of provide_matching_values__data. const SG_vhash* pHash, //< The hash that the current value is from. const char* szName, //< The name of the current value. const SG_variant* pValue //< The current value. ) { SG_string* pFullName = NULL; SG_string* pScopeName = NULL; SG_string* pSettingName = NULL; SG_uint32 uValueSize = 0u; provide_matching_values__data* pData = NULL; SG_UNUSED(pHash); SG_NULLARGCHECK_RETURN(pCallerData); pData = (provide_matching_values__data*) pCallerData; // build the full name of this value from the incoming prefix and name SG_ERR_CHECK( SG_STRING__ALLOC__COPY(pCtx, &pFullName, pData->pPrefix) ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pFullName, "/") ); SG_ERR_CHECK( SG_string__append__sz(pCtx, pFullName, szName) ); // if this is a vhash, get its size if (pValue->type == SG_VARIANT_TYPE_VHASH) { SG_ERR_CHECK( SG_vhash__count(pCtx, pValue->v.val_vhash, &uValueSize) ); } // if this is a vhash with children, then recurse into it // otherwise provide it to the callback if (uValueSize > 0u) { // use our full name as the prefix during recursion // to accomplish that, we'll swap pData->pPrefix and pFullName // that way if an error occurs during recursion, everything will still be freed SG_string* pTemp = NULL; pTemp = pData->pPrefix; pData->pPrefix = pFullName; pFullName = pTemp; SG_ERR_CHECK( SG_vhash__foreach(pCtx, pValue->v.val_vhash, provide_matching_values, pData) ); pTemp = pData->pPrefix; pData->pPrefix = pFullName; pFullName = pTemp; } else { // if we have a pattern that starts with a slash // then match it against the start of the full name // if the name doesn't match, skip this one if (pData->szPattern != NULL && pData->szPattern[0] == '/') { if (strncmp(SG_string__sz(pFullName), pData->szPattern, strlen(pData->szPattern)) != 0) { goto fail; } } // split our full name into a scope name and a local name SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pScopeName) ); SG_ERR_CHECK( SG_STRING__ALLOC(pCtx, &pSettingName) ); SG_ERR_CHECK( SG_localsettings__split_full_name(pCtx, SG_string__sz(pFullName), NULL, pScopeName, pSettingName) ); // if we have a pattern that doesn't start with a slash // then match it against just the local name of the setting // if the name doesn't match, skip this one if (pData->szPattern != NULL && pData->szPattern[0] != '/') { if (strstr(SG_string__sz(pSettingName), pData->szPattern) == NULL) { goto fail; } } // send the data to the callback pData->pCallback(pCtx, SG_string__sz(pFullName), SG_string__sz(pScopeName), SG_string__sz(pSettingName), pValue, pData->pCallerData); } fail: SG_STRING_NULLFREE(pCtx, pFullName); SG_STRING_NULLFREE(pCtx, pScopeName); SG_STRING_NULLFREE(pCtx, pSettingName); }