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_treediff_cache::NotifyExplorerToRefreshPath(SG_context * pCtx, wxFileName path, bool bSpecific) { SG_string * pstrFullPath = NULL; wxString fullName = path.GetFullName(); SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pstrFullPath, path.GetFullPath().ToUTF8()) ); //Use %s to handle getting a path that has a %s in it. //SG_ERR_CHECK( SG_UTF8__INTERN_FROM_OS_BUFFER(pCtx, pstrFullPath, path.GetFullPath().ToStdWstring()) ); //SG_log__report_verbose(pCtx, "FS_CHANGE(%s): %s", (const char *)ExplainAction(dwAction), SG_string__sz(pstrFullPath)); { clearCache(pCtx); if (!m_bIsWin7) //if we're running on XP or Vista { wxCriticalSectionLocker lock(m_notify_critical_section); m_bHaveGottenNewFileChanges = true; //For some reason, on XP and Vista, we need to notify for every folder up to the root. if (path.FileExists() || path.DirExists()) { if (m_aPathsToNotify.Index(path.GetFullPath()) == wxNOT_FOUND) m_aPathsToNotify.Add(path.GetFullPath()); } wxFileName currentDir = wxFileName::DirName(path.GetPath(wxPATH_GET_VOLUME)); wxFileName topDir = GetTopDir(); //currentDir = topDir; while (topDir.GetDirCount() < currentDir.GetDirCount()) { if (m_aPathsToNotify.Index(currentDir.GetFullPath()) == wxNOT_FOUND) m_aPathsToNotify.Add(currentDir.GetFullPath()); currentDir.RemoveLastDir(); } } else { wxCriticalSectionLocker lock(m_notify_critical_section); m_bHaveGottenNewFileChanges = true; //On Win 7, just notifying for the top of the working folder //will cause a recursive refresh down the tree. if (m_aPathsToNotify.Index(GetTopDir().GetFullPath()) == wxNOT_FOUND) m_aPathsToNotify.Add(GetTopDir().GetFullPath()); if (bSpecific) m_aPathsToNotify.Add(path.GetFullPath()); } } fail: return; }
static void _getEncoded( SG_context *pCtx, const char *raw, SG_string *encoded) { SG_string *rawstr = NULL; SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &rawstr, raw) ); SG_ERR_CHECK( SG_htmlencode(pCtx, rawstr, encoded) ); fail: SG_STRING_NULLFREE(pCtx, rawstr); }
/** * "Throws" SG_ERR_NOT_A_WORKING_COPY if no --repo was specified and the cwd's not inside a working copy. */ void SG_cmd_util__get_descriptor_name_from_options_or_cwd(SG_context* pCtx, SG_option_state* pOptSt, SG_string** ppstrDescriptorName, SG_pathname** ppPathCwd) { SG_string* pstr = NULL; SG_pathname* pPathCwd = NULL; SG_NULLARGCHECK_RETURN(pOptSt); if (pOptSt->psz_repo) SG_STRING__ALLOC__SZ(pCtx, &pstr, pOptSt->psz_repo); else SG_ERR_CHECK( SG_cmd_util__get_descriptor_name_from_cwd(pCtx, &pstr, &pPathCwd) ); SG_RETURN_AND_NULL(pstr, ppstrDescriptorName); SG_RETURN_AND_NULL(pPathCwd, ppPathCwd); fail: SG_STRING_NULLFREE(pCtx, pstr); SG_PATHNAME_NULLFREE(pCtx, pPathCwd); }
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); }
void SG_workingdir__wdpath_to_repopath( SG_context* pCtx, const SG_pathname* pPathWorkingDirectoryTop, const SG_pathname* pPath, SG_bool bFinalSlash, SG_string** ppStrRepoPath) { SG_string* pstrRoot = NULL; SG_string* pstrRelative = NULL; SG_string* pstrRepoPath = NULL; /* TODO fix the following line to get the real anchor path for this working * dir */ SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pstrRoot, "@/") ); SG_ERR_CHECK( SG_pathname__make_relative(pCtx, pPathWorkingDirectoryTop, pPath, &pstrRelative) ); if (pstrRelative) { SG_ERR_CHECK( SG_repopath__combine(pCtx, pstrRoot, pstrRelative, bFinalSlash, &pstrRepoPath) ); SG_STRING_NULLFREE(pCtx, pstrRelative); SG_STRING_NULLFREE(pCtx, pstrRoot); *ppStrRepoPath = pstrRepoPath; } else { *ppStrRepoPath = pstrRoot; } return; fail: SG_STRING_NULLFREE(pCtx, pstrRoot); SG_STRING_NULLFREE(pCtx, pstrRelative); SG_STRING_NULLFREE(pCtx, pstrRepoPath); }
/** * 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); }
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; }
static void _format_comment(SG_context* pCtx, SG_bool onlyIncludeFirstLine, const char* szLinePrefix, const char* szComment, char** ppszReturn) { SG_bool bFoundLineBreak = SG_FALSE; SG_string* pstr = NULL; SG_uint32 lenPrefix = SG_STRLEN(szLinePrefix); SG_uint32 offset; { const char* pos; for (pos = szComment; *pos; pos++) { if (*pos == SG_CR || *pos == SG_LF) { bFoundLineBreak = SG_TRUE; break; } } if (!bFoundLineBreak) return; if(onlyIncludeFirstLine) { SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &pstr, (const SG_byte*)szComment, (SG_uint32)(pos-szComment)) ); SG_ERR_CHECK( SG_string__sizzle(pCtx, &pstr, (SG_byte**)ppszReturn, NULL) ); return; } offset = (SG_uint32)(pos-szComment); } SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pstr, szComment) ); while (offset < SG_string__length_in_bytes(pstr)) { SG_byte current; SG_ERR_CHECK( SG_string__get_byte_l(pCtx, pstr, offset, ¤t) ); if (current == SG_CR) { SG_byte next; bFoundLineBreak = SG_TRUE; SG_ERR_CHECK( SG_string__get_byte_l(pCtx, pstr, offset+1, &next) ); if (next != SG_LF) { // Mac format, lines end with \r only. Consoles will not advance a line. SG_ERR_CHECK( SG_string__insert__sz(pCtx, pstr, offset+1, "\n") ); } offset++; } else if (current == SG_LF) { bFoundLineBreak = SG_TRUE; } offset++; if (bFoundLineBreak) { SG_ERR_CHECK( SG_string__insert__sz(pCtx, pstr, offset, szLinePrefix) ); offset += lenPrefix; bFoundLineBreak = SG_FALSE; } } SG_ERR_CHECK( SG_string__sizzle(pCtx, &pstr, (SG_byte**)ppszReturn, NULL) ); return; fail: SG_STRING_NULLFREE(pCtx, pstr); }
void SG_cmd_util__get_username_and_password( SG_context *pCtx, const char *szWhoami, SG_bool force_whoami, SG_bool bHadSavedCredentials, SG_uint32 kAttempt, SG_string **ppUsername, SG_string **ppPassword ) { SG_string * pUsername = NULL; SG_string * pPassword = NULL; SG_NULLARGCHECK_RETURN(ppPassword); SG_NULLARGCHECK_RETURN(ppUsername); if (kAttempt == 0) { if (bHadSavedCredentials) { SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "\nAuthorization required. Saved username/password not valid.\n") ); } else { SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "\nAuthorization required.") ); if (SG_password__supported()) SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, " Use --remember to save this password.") ); SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "\n") ); } } else if (kAttempt >= 3) { SG_ERR_THROW( SG_ERR_AUTHORIZATION_TOO_MANY_ATTEMPTS ); } else { SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "\nInvalid username or password. Please try again.\n") ); } if(szWhoami!=NULL && force_whoami) { SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pUsername, szWhoami) ); SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "Enter password for %s: ", szWhoami) ); SG_ERR_CHECK( SG_console__get_password(pCtx, &pPassword) ); } else { if(szWhoami) { SG_bool bAllWhitespace = SG_FALSE; SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "Enter username [%s]: ", szWhoami) ); SG_ERR_CHECK( SG_console__readline_stdin(pCtx, &pUsername) ); SG_ERR_CHECK( SG_sz__is_all_whitespace(pCtx, SG_string__sz(pUsername), &bAllWhitespace) ); if(bAllWhitespace) SG_ERR_CHECK( SG_string__set__sz(pCtx, pUsername, szWhoami) ); } else { SG_ERR_CHECK( SG_console(pCtx, SG_CS_STDERR, "Enter username: "******"Enter password: ") ); SG_ERR_CHECK( SG_console__get_password(pCtx, &pPassword) ); } *ppUsername = pUsername; *ppPassword = pPassword; return; fail: SG_STRING_NULLFREE(pCtx, pUsername); SG_STRING_NULLFREE(pCtx, pPassword); }
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); }
void SG_time__parse(SG_context* pCtx,const char* pszInputString, SG_int64 * pTime, SG_bool bParseToMaximumValidValue) { SG_string * pWorkingStr = NULL; char** splitOnSpaces = NULL; char** splitOnColons = NULL; char** splitOnDashes = NULL; SG_uint32 nCountAfterSplitOnSpaces = 0; SG_uint32 nCountAfterSplitOnDashes = 0; SG_uint32 nCountAfterSplitOnColons = 0; SG_string * pWorkingStringDate = NULL; SG_string * pWorkingStringTime = NULL; time_t resultTicks = 0; struct tm result; SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pWorkingStr, pszInputString) ); //This understands exactly two formats //YYYY-MM-DD // //and //YYYY-MM-DD hh:mm:ss //The local time zone is always assumed. SG_ERR_CHECK( SG_string__split__asciichar(pCtx, pWorkingStr, ' ', 2, &splitOnSpaces, &nCountAfterSplitOnSpaces) ); if (nCountAfterSplitOnSpaces == 1) { //YYYY-M-DD SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pWorkingStringDate, splitOnSpaces[0]) ); SG_ERR_CHECK( SG_string__split__asciichar(pCtx, pWorkingStringDate, '-', 3, &splitOnDashes, &nCountAfterSplitOnDashes) ); if(nCountAfterSplitOnDashes == 3) { SG_uint32 year = 0; SG_uint32 month = 0; SG_uint32 dayOfMonth = 0; SG_ERR_CHECK( SG_uint32__parse(pCtx, &year, splitOnDashes[0]) ); SG_ERR_CHECK( SG_uint32__parse(pCtx, &month, splitOnDashes[1]) ); SG_ERR_CHECK( SG_uint32__parse(pCtx, &dayOfMonth, splitOnDashes[2]) ); if (month<= 0 || month > 12) SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); if (dayOfMonth<= 0 || dayOfMonth > 31) SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); result.tm_year = year - 1900; result.tm_mon = month - 1; result.tm_mday = dayOfMonth; if (bParseToMaximumValidValue == SG_TRUE) { result.tm_hour = 23; result.tm_min = 59; result.tm_sec = 59; } else { result.tm_hour = 0; result.tm_min = 0; result.tm_sec = 0; } } else SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); } else if (nCountAfterSplitOnSpaces == 2) { //YYYY-MM-DD hh:mm:ss //YYYY-M-DD SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pWorkingStringDate, splitOnSpaces[0]) ); SG_ERR_CHECK( SG_string__split__asciichar(pCtx, pWorkingStringDate, '-', 3, &splitOnDashes, &nCountAfterSplitOnDashes) ); if(nCountAfterSplitOnDashes == 3) { SG_uint32 year = 0; SG_uint32 month = 0; SG_uint32 dayOfMonth = 0; SG_ERR_CHECK( SG_uint32__parse(pCtx, &year, splitOnDashes[0]) ); SG_ERR_CHECK( SG_uint32__parse(pCtx, &month, splitOnDashes[1]) ); SG_ERR_CHECK( SG_uint32__parse(pCtx, &dayOfMonth, splitOnDashes[2]) ); if (month<= 0 || month > 12) SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); if (dayOfMonth<= 0 || dayOfMonth > 31) SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); result.tm_year = year - 1900; result.tm_mon = month - 1; result.tm_mday = dayOfMonth; } else SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pWorkingStringTime, splitOnSpaces[1]) ); SG_ERR_CHECK( SG_string__split__asciichar(pCtx, pWorkingStringTime, ':', 3, &splitOnColons, &nCountAfterSplitOnColons) ); if(nCountAfterSplitOnColons == 3) { SG_uint32 hour = 0; SG_uint32 minute = 0; SG_uint32 second = 0; SG_ERR_CHECK( SG_uint32__parse(pCtx, &hour, splitOnColons[0]) ); SG_ERR_CHECK( SG_uint32__parse(pCtx, &minute, splitOnColons[1]) ); SG_ERR_CHECK( SG_uint32__parse(pCtx, &second, splitOnColons[2]) ); if (hour > 23) SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); if (minute > 59) SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); if (second > 59) SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); result.tm_hour = hour; result.tm_min = minute; result.tm_sec = second; } else SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); } else { //Invalid date. SG_ERR_THROW(SG_ERR_DATE_PARSING_ERROR); } result.tm_isdst = -1; resultTicks = mktime(&result); if (resultTicks >= 0) { *pTime = resultTicks; *pTime = *pTime * MILLISECONDS_PER_SECOND; if (bParseToMaximumValidValue == SG_TRUE) { *pTime += 999; } } SG_STRING_NULLFREE(pCtx, pWorkingStringTime); SG_STRING_NULLFREE(pCtx, pWorkingStringDate); if (splitOnColons != NULL) SG_ERR_CHECK( SG_freeStringList(pCtx, (const char ***)&splitOnColons, nCountAfterSplitOnColons) ); if (splitOnSpaces != NULL) SG_ERR_CHECK( SG_freeStringList(pCtx, (const char ***)&splitOnSpaces, nCountAfterSplitOnSpaces) ); if (splitOnDashes != NULL) SG_ERR_CHECK( SG_freeStringList(pCtx, (const char ***)&splitOnDashes, nCountAfterSplitOnDashes) ); SG_STRING_NULLFREE(pCtx, pWorkingStr); return; fail: SG_STRING_NULLFREE(pCtx, pWorkingStringTime); SG_STRING_NULLFREE(pCtx, pWorkingStringDate); if (splitOnColons != NULL && nCountAfterSplitOnColons > 0) SG_ERR_IGNORE( SG_freeStringList(pCtx, (const char ***)&splitOnColons, nCountAfterSplitOnColons) ); if (splitOnSpaces != NULL && nCountAfterSplitOnSpaces > 0) SG_ERR_IGNORE( SG_freeStringList(pCtx, (const char ***)&splitOnSpaces, nCountAfterSplitOnSpaces) ); if (splitOnDashes != NULL) SG_ERR_IGNORE( SG_freeStringList(pCtx, (const char ***)&splitOnDashes, nCountAfterSplitOnDashes) ); SG_STRING_NULLFREE(pCtx, pWorkingStr); return; }
void sg_wc_liveview_item__alloc__add_special(SG_context * pCtx, sg_wc_liveview_item ** ppLVI, SG_wc_tx * pWcTx, SG_uint64 uiAliasGid, SG_uint64 uiAliasGidParent, const char * pszEntryname, SG_treenode_entry_type tneType, const char * pszHidMerge, SG_int64 attrbits, SG_wc_status_flags statusFlagsAddSpecialReason) { sg_wc_liveview_item * pLVI = NULL; SG_bool bFoundIssue = SG_FALSE; SG_ERR_CHECK( SG_alloc1(pCtx, pLVI) ); // caller needs to set the backptr if appropriate // if/when it adds this LVI to the LVD's vector. pLVI->pLiveViewDir = NULL; pLVI->uiAliasGid = uiAliasGid; SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &pLVI->pStringEntryname, pszEntryname) ); // During the QUEUE phase where we are doing this // special-add, we have not yet actually created // the item on disk. So we should indicate that // scandir/readdir didn't know anything about it. // // TODO 2012/01/31 During the APPLY phase, we need to // TODO fix-up this field. Because all of // TODO the __get_original_ and __get_current_ // TODO routines below assume that this field // TODO is set. That is, if you want to do // TODO additional operations (like status) // TODO after a merge, for example. // TODO // TODO 2012/04/12 Think about using pLVI->queuedOverwrite // TODO fields for this. // NOTE // NOTE 2012/05/16 Setting this field to NULL caused a problem // NOTE in _deal_with_moved_out_list() and // NOTE sg_wc_liveview_item__alter_structure__move_rename() // NOTE during UPDATE when an ADD-SPECIAL item was initially // NOTE PARKED because of a transient collision (because the // NOTE final UNPARK step uses move/rename to do the work). pLVI->pPrescanRow = NULL; // because a liveview_item must start as an // exact clone of a scanrow, there cannot be // any in-tx changes yet for it. SG_ERR_CHECK( SG_WC_DB__PC_ROW__ALLOC(pCtx, &pLVI->pPcRow_PC) ); if (statusFlagsAddSpecialReason & SG_WC_STATUS_FLAGS__S__MERGE_CREATED) pLVI->pPcRow_PC->flags_net = SG_WC_DB__PC_ROW__FLAGS_NET__ADD_SPECIAL_M; else if (statusFlagsAddSpecialReason & SG_WC_STATUS_FLAGS__S__UPDATE_CREATED) pLVI->pPcRow_PC->flags_net = SG_WC_DB__PC_ROW__FLAGS_NET__ADD_SPECIAL_U; else SG_ERR_THROW2( SG_ERR_INVALIDARG, (pCtx, "Invalid statusFlagsAddSpecialReason for '%s'", pszEntryname) ); pLVI->pPcRow_PC->p_s->uiAliasGid = uiAliasGid; pLVI->pPcRow_PC->p_s->uiAliasGidParent = uiAliasGidParent; SG_ERR_CHECK( SG_STRDUP(pCtx, pszEntryname, &pLVI->pPcRow_PC->p_s->pszEntryname) ); pLVI->pPcRow_PC->p_s->tneType = tneType; if (pszHidMerge) SG_ERR_CHECK( SG_STRDUP(pCtx, pszHidMerge, &pLVI->pPcRow_PC->pszHidMerge) ); pLVI->tneType = tneType; pLVI->scan_flags_Live = SG_WC_PRESCAN_FLAGS__CONTROLLED_ACTIVE_POSTSCAN; if (statusFlagsAddSpecialReason & SG_WC_STATUS_FLAGS__A__SPARSE) { pLVI->pPcRow_PC->flags_net |= SG_WC_DB__PC_ROW__FLAGS_NET__SPARSE; SG_ERR_CHECK( sg_wc_db__state_dynamic__alloc(pCtx, &pLVI->pPcRow_PC->p_d_sparse) ); pLVI->pPcRow_PC->p_d_sparse->attrbits = attrbits; if (tneType != SG_TREENODEENTRY_TYPE_DIRECTORY) { SG_ASSERT( pszHidMerge && *pszHidMerge ); SG_ERR_CHECK( SG_STRDUP(pCtx, pszHidMerge, &pLVI->pPcRow_PC->p_d_sparse->pszHid) ); } pLVI->scan_flags_Live = SG_WC_PRESCAN_FLAGS__CONTROLLED_ACTIVE_SPARSE; // not |= } pLVI->pPcRow_PC->ref_attrbits = attrbits; SG_ERR_CHECK( sg_wc_db__issue__get_issue(pCtx, pWcTx->pDb, pLVI->uiAliasGid, &bFoundIssue, &pLVI->statusFlags_x_xr_xu, &pLVI->pvhIssue, &pLVI->pvhSavedResolutions) ); *ppLVI = pLVI; return; fail: SG_WC_LIVEVIEW_ITEM__NULLFREE(pCtx, pLVI); }
/** * This is a special case of __get_current_content_hid() that * basically does that and then fetches the blob and returns * the actual target string contained within. * * Normally, we wouldn't need this specialization, but if we * have a QUEUED overwrite-symlink-target, THEN WE NEED TO LIE * and return the new/queued value rather than the actual * target value that the existing symlink has in the WD. * */ void sg_wc_liveview_item__get_current_symlink_target(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_string ** ppStringTarget) { SG_byte* pBuffer = NULL; SG_uint64 uSize = 0u; if (pLVI->tneType != SG_TREENODEENTRY_TYPE_SYMLINK) SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "GetCurrentSymlinkTarget: '%s' is not a symlink.", SG_string__sz(pLVI->pStringEntryname)) ); if (pLVI->queuedOverwrites.pvhContent) { // We have a QUEUED operation on this item that changed the // contents. Get the 'current' value from the journal. const char * psz = NULL; SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "target", &psz) ); if (psz) { // last overwrite-type operation gave us a SYMLINK-TARGET. #if TRACE_WC_LIE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetCurrentSymlinkTarget: using journal '%s' for: %s\n", psz, SG_string__sz(pLVI->pStringEntryname)) ); #endif SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, ppStringTarget, psz) ); return; } SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "file", &psz) ); if (psz) { // last overwrite-type operation used a TEMP file. // this cannot/should not happen. SG_ERR_THROW2( SG_ERR_INVALIDARG, (pCtx, "GetCurrentSymlinkTarget: journal contains temp file '%s' for: %s", psz, SG_string__sz(pLVI->pStringEntryname)) ); } SG_ERR_CHECK( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "hid", &psz) ); if (psz) { // last overwrite-type operation used an HID. SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, psz, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } SG_ERR_THROW2( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentSymlinkTarget: required field missing from vhash for: %s", SG_string__sz(pLVI->pStringEntryname)) ); } // Otherwise, return the current pre-tx value. SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow) ); if (SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_SPARSE(pLVI->scan_flags_Live)) { if (pLVI->pPcRow_PC) { SG_ASSERT_RELEASE_RETURN( (pLVI->pPcRow_PC->p_d_sparse) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pLVI->pPcRow_PC->p_d_sparse->pszHid, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } else if (pLVI->pPrescanRow->pPcRow_Ref) { SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse->pszHid, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } else { // With the addition of {sparse_hid,sparse_attrbits} to tbl_PC, // we should not get here. SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentSymlinkTarget: unhandled case when sparse for '%s'.", SG_string__sz(pLVI->pStringEntryname)) ); } } else if (pLVI->pPrescanRow->pRD) { const SG_string * pStringTargetRef; SG_ERR_CHECK( sg_wc_readdir__row__get_content_symlink_target(pCtx, pWcTx, pLVI->pPrescanRow->pRD, &pStringTargetRef) ); SG_ERR_CHECK( SG_STRING__ALLOC__COPY(pCtx, ppStringTarget, pStringTargetRef) ); return; } else if (pLVI->pPrescanRow->pTneRow) { SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pLVI->pPrescanRow->pTneRow->p_d->pszHid, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, ppStringTarget, pBuffer, (SG_uint32)uSize) ); SG_NULLFREE(pCtx, pBuffer); return; } else { // perhaps an ADD-SPECIAL + DELETE // or an ADDED+LOST in an UPDATE ? SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentSymlinkTarget: unhandled case for '%s'.", SG_string__sz(pLVI->pStringEntryname)) ); } fail: SG_NULLFREE(pCtx, pBuffer); }
/** * 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); }
static void _sg_password__parse_url( SG_context *pCtx, const char *url, SG_bool *pIsValid, SG_string **ppProto, SG_string **ppServer, SG_string **ppPath, SG_uint32 *pPort ) { const char *slash, *colon, *ss; SG_string *server = NULL; SG_string *path = NULL; SG_string *proto = NULL; SG_NULLARGCHECK(url); *pIsValid = SG_FALSE; if (strncmp("http://", url, 7) == 0) { SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &proto, "http") ); *pPort = 80; url += 7; } else if (strncmp("https://", url, 8) == 0) { SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &proto, "https") ); *pPort = 443; url += 8; } else goto fail; slash = strchr(url, '/'); if (! slash) goto fail; SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &server, (const SG_byte *)url, (SG_uint32)(slash - url)) ); ss = SG_string__sz(server); colon = strchr(ss, ':'); if (colon) { SG_uint32 port = atoi(colon + 1); SG_uint32 plen = colon - ss; if (port == 0) goto fail; *pPort = port; SG_ERR_CHECK( SG_string__remove(pCtx, server, plen, SG_STRLEN(ss) - plen) ); } SG_ERR_CHECK( SG_STRING__ALLOC__SZ(pCtx, &path, "/") ); *ppPath = path; path = NULL; *ppServer = server; server = NULL; *ppProto = proto; proto = NULL; *pIsValid = SG_TRUE; fail: SG_STRING_NULLFREE(pCtx, server); SG_STRING_NULLFREE(pCtx, path); SG_STRING_NULLFREE(pCtx, proto); }