void SG_curl__perform(SG_context* pCtx, SG_curl* pCurl) { CURLcode rc = CURLE_OK; _sg_curl* pMe = (_sg_curl*)pCurl; SG_NULLARGCHECK_RETURN(pCurl); rc = curl_easy_perform(pMe->pCurl); // Check for errors in the request and response callbacks. SG_ERR_CHECK_RETURN_CURRENT; // No callback errors. Make sure curl result code is also kosher. if (rc) { switch (rc) { case CURLE_SEND_ERROR: case CURLE_RECV_ERROR: SG_ERR_THROW2_RETURN(SG_ERR_LIBCURL(rc), (pCtx, "%s", "please try again.")); break; #if defined(WINDOWS) case CURLE_SSL_CACERT: SG_ERR_THROW2_RETURN(SG_ERR_LIBCURL(rc), (pCtx, "Verify that the server/files setting is correct, or use the command 'vv config set network/verify_ssl_certs false' to disable this warning.")); break; #endif default: SG_ERR_THROW_RETURN(SG_ERR_LIBCURL(rc)); } } }
void sg_sync_client__bind_vtable(SG_context* pCtx, SG_sync_client * pClient) { SG_bool bRemote = SG_FALSE; SG_NULLARGCHECK_RETURN(pClient); if (pClient->p_vtable) // can only be bound once SG_ERR_THROW2_RETURN(SG_ERR_INVALIDARG, (pCtx, "vtable already bound")); // Select the vtable based on pClient's remote repo specification or the presence of a local repo handle. if (pClient->psz_remote_repo_spec) SG_ERR_CHECK_RETURN( SG_sync_client__spec_is_remote(pCtx, pClient->psz_remote_repo_spec, &bRemote) ); else if (!pClient->pRepoOther) SG_ERR_THROW2_RETURN(SG_ERR_INVALIDARG, (pCtx, "a repo spec or a local repo handle must be set")); if (bRemote) pClient->p_vtable = &s_client_vtable__http; else { pClient->p_vtable = &s_client_vtable__c; /* It would be better if this were done inside the C vtable, where pClient->pRepoOther is * actually used, but there's not a sane way to do that now; it would require a significant * re-org. */ if (!pClient->pRepoOther) { SG_ERR_CHECK_RETURN( SG_REPO__OPEN_REPO_INSTANCE(pCtx, pClient->psz_remote_repo_spec, &pClient->pRepoOther) ); pClient->bRepoOtherIsMine = SG_TRUE; } } }
void SG_jscore__new_context(SG_context * pCtx, JSContext ** pp_cx, JSObject ** pp_glob, const SG_vhash * pServerConfig) { JSContext * cx = NULL; JSObject * glob = NULL; SG_ASSERT(pCtx!=NULL); SG_NULLARGCHECK_RETURN(pp_cx); if(gpJSCoreGlobalState==NULL) SG_ERR_THROW2_RETURN(SG_ERR_UNINITIALIZED, (pCtx, "jscore has not been initialized")); if (gpJSCoreGlobalState->cb) JS_SetContextCallback(gpJSCoreGlobalState->rt, gpJSCoreGlobalState->cb); cx = JS_NewContext(gpJSCoreGlobalState->rt, 8192); if(cx==NULL) SG_ERR_THROW2_RETURN(SG_ERR_MALLOCFAILED, (pCtx, "Failed to allocate new JS context")); (void)JS_SetContextThread(cx); JS_BeginRequest(cx); JS_SetOptions(cx, JSOPTION_VAROBJFIX); JS_SetVersion(cx, JSVERSION_LATEST); JS_SetContextPrivate(cx, pCtx); glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL); if(glob==NULL) SG_ERR_THROW2(SG_ERR_JS, (pCtx, "Failed to create JavaScript global object for new JSContext.")); if(!JS_InitStandardClasses(cx, glob)) SG_ERR_THROW2(SG_ERR_JS, (pCtx, "JS_InitStandardClasses() failed.")); if (gpJSCoreGlobalState->shell_functions) if (!JS_DefineFunctions(cx, glob, gpJSCoreGlobalState->shell_functions)) SG_ERR_THROW2(SG_ERR_JS, (pCtx, "Failed to install shell functions")); SG_jsglue__set_sg_context(pCtx, cx); SG_ERR_CHECK( SG_jsglue__install_scripting_api(pCtx, cx, glob) ); SG_ERR_CHECK( SG_zing_jsglue__install_scripting_api(pCtx, cx, glob) ); if (! gpJSCoreGlobalState->bSkipModules) { _sg_jscore__install_modules(pCtx, cx, glob, pServerConfig); SG_ERR_CHECK_CURRENT_DISREGARD(SG_ERR_NOTAFILE); } *pp_cx = cx; *pp_glob = glob; return; fail: if (cx) { JS_EndRequest(cx); JS_DestroyContext(cx); } }
void SG_curl__set_headers_from_varray(SG_context * pCtx, SG_curl * pCurl, SG_varray * pvaHeaders, struct curl_slist ** ppHeaderList) { CURLcode rc = CURLE_OK; _sg_curl* p = (_sg_curl*)pCurl; struct curl_slist* pHeaderList = NULL; SG_uint32 count = 0; SG_uint32 i = 0; SG_NULLARGCHECK_RETURN(pCurl); SG_NULLARGCHECK_RETURN(pvaHeaders); SG_ERR_CHECK( SG_varray__count(pCtx, pvaHeaders, &count) ); for (i = 0; i < count; i++) { const char * psz = NULL; SG_ERR_CHECK_RETURN( SG_varray__get__sz(pCtx, pvaHeaders, i, &psz) ); pHeaderList = curl_slist_append(pHeaderList, psz); if (!pHeaderList) SG_ERR_THROW2_RETURN(SG_ERR_UNSPECIFIED, (pCtx, "Failed to add HTTP header.")); } rc = curl_easy_setopt(p->pCurl, CURLOPT_HTTPHEADER, pHeaderList); if (rc) SG_ERR_THROW2(SG_ERR_LIBCURL(rc), (pCtx, "Problem setting HTTP headers" )); SG_RETURN_AND_NULL(pHeaderList, ppHeaderList); fail: if (pHeaderList) SG_CURL_HEADERS_NULLFREE(pCtx, pHeaderList); }
/** * This is a special case of __get_original_content_hid() that * basically doest that and then fetches the blob and returns * the actual symlink target. * * This is a convenience for STATUS that can report 'target' * fields rather than (or in addition to) 'hid' for symlinks. * */ void sg_wc_liveview_item__get_original_symlink_target(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_string ** ppStringTarget) { const char * pszHidContent; SG_string * pStringTarget = NULL; SG_byte* pBuffer = NULL; SG_uint64 uSize = 0u; if (pLVI->tneType != SG_TREENODEENTRY_TYPE_SYMLINK) SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "GetOriginalSymlinkTarget: '%s' is not a symlink.", SG_string__sz(pLVI->pStringEntryname)) ); SG_ERR_CHECK( sg_wc_liveview_item__get_original_content_hid(pCtx, pLVI, pWcTx, SG_FALSE, &pszHidContent) ); SG_ERR_CHECK( SG_repo__fetch_blob_into_memory(pCtx, pWcTx->pDb->pRepo, pszHidContent, &pBuffer, &uSize) ); SG_ERR_CHECK( SG_STRING__ALLOC__BUF_LEN(pCtx, &pStringTarget, pBuffer, (SG_uint32)uSize) ); *ppStringTarget = pStringTarget; pStringTarget = NULL; fail: SG_STRING_NULLFREE(pCtx, pStringTarget); SG_NULLFREE(pCtx, pBuffer); }
void SG_dagquery__new_since(SG_context * pCtx, SG_repo* pRepo, SG_uint64 iDagNum, const char* pszOldNodeHid, const char* pszNewNodeHid, SG_rbtree** pprbNewNodeHids) { SG_dagquery_relationship rel; new_since_context cbCtx; cbCtx.dagnum = iDagNum; cbCtx.prbNewNodeHids = NULL; cbCtx.pszNewNodeHid = pszNewNodeHid; cbCtx.pszOldNodeHid = pszOldNodeHid; SG_ERR_CHECK( SG_dagquery__how_are_dagnodes_related(pCtx, pRepo, iDagNum, pszNewNodeHid, pszOldNodeHid, SG_FALSE, SG_FALSE, &rel) ); if (rel != SG_DAGQUERY_RELATIONSHIP__DESCENDANT) SG_ERR_THROW2_RETURN(SG_ERR_UNSPECIFIED, (pCtx, "pszNewNodeHid must be a descendant of pszOldNodeHid")); SG_ERR_CHECK( SG_RBTREE__ALLOC(pCtx, &cbCtx.prbNewNodeHids) ); SG_ERR_CHECK( SG_dagwalker__walk_dag_single(pCtx, pRepo, iDagNum, pszNewNodeHid, _dagquery__new_since__callback, (void*)&cbCtx) ); SG_RETURN_AND_NULL(cbCtx.prbNewNodeHids, pprbNewNodeHids); /* Fall through to common cleanup */ fail: SG_RBTREE_NULLFREE(pCtx, cbCtx.prbNewNodeHids); }
static void _complain_if_unnormalized(SG_context * pCtx, const char * pszInput) { const char * psz; SG_ASSERT( (pszInput[0] == '/') ); for (psz=pszInput; *psz; psz++) { if (psz[0]=='/') { if ((psz[1]=='.') && ((psz[2]=='/') || (psz[2]==0))) goto my_throw; // disallow "/./" or a trailing "/." if ((psz[1]=='.') && (psz[2]=='.') && ((psz[3]=='/') || (psz[3]==0))) goto my_throw; // disallow "/../" or a trailing "/.." if ((psz[1]=='/')) goto my_throw; // disallow "//" } if (psz[0]=='\\') goto my_throw; // disallow backslashes anywhere in repo-paths } return; my_throw: SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "The input '%s' is not a valid repo-path (unnormalized).", pszInput) ); }
static void _check_if_relative_paths_allowed(SG_context* pCtx, const sg_wc_db * pDb) { if (SG_FALSE == pDb->bWorkingDirPathFromCwd) { SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "Relative paths are allowed only in working copy transactions initiated in the current working directory.") ); } }
void SG_workingdir__construct_absolute_path_from_repo_path(SG_context * pCtx, const SG_pathname * pPathWorkingDirectoryTop, const char * pszRepoPath, SG_pathname ** ppPathAbsolute) { SG_pathname * pPath = NULL; #if defined(DEBUG) // TODO we should verify "<wd-top>/.sgdrawer" exists to make sure // TODO that we have the actual wd-root. especially when using vv // TODO with relative paths while cd'd somewhere deep inside the tree. #endif if (pszRepoPath[0] != '@') { SG_ERR_THROW2_RETURN( SG_ERR_INVALID_REPO_PATH, (pCtx, "Must begin with '@/': %s", pszRepoPath) ); } else { if (pszRepoPath[1] == 0) // quietly allow "@" as substitute for "@/" { SG_ERR_CHECK( SG_PATHNAME__ALLOC__COPY(pCtx, &pPath, pPathWorkingDirectoryTop) ); } else if (pszRepoPath[1] != '/') { SG_ERR_THROW2_RETURN( SG_ERR_INVALID_REPO_PATH, (pCtx, "Must begin with '@/': %s", pszRepoPath) ); } else { if (pszRepoPath[2]) SG_ERR_CHECK( SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPath, pPathWorkingDirectoryTop, &pszRepoPath[2]) ); else SG_ERR_CHECK( SG_PATHNAME__ALLOC__COPY(pCtx, &pPath, pPathWorkingDirectoryTop) ); } } *ppPathAbsolute = pPath; return; fail: SG_PATHNAME_NULLFREE(pCtx, pPath); }
static void _start_thread(SG_context * pCtx, LPTHREAD_START_ROUTINE pFunc, void* pParam, HANDLE* ppHandle) { HANDLE hThread; SG_UNUSED(pCtx); hThread = CreateThread(NULL, 0, pFunc, pParam, 0, NULL); if (hThread != NULL) *ppHandle = hThread; else { SG_ERR_THROW2_RETURN(SG_ERR_GETLASTERROR(GetLastError()), (pCtx, "%s", "CreateThread failed")); } }
static void _start_thread(SG_context * pCtx, void* pFunc, void* pParam, pthread_t* pThreadId) // pCtx: input parameter only (used for logging) { pthread_t thread_id; // pthread_attr_t attr; int retval; // (void) pthread_attr_init(&attr); // (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); retval = pthread_create(&thread_id, NULL, pFunc, pParam); if (retval) SG_ERR_THROW2_RETURN(SG_ERR_ERRNO(retval), (pCtx, "%s", "pthread_create failed")); *pThreadId = thread_id; }
void SG_password__set( SG_context* pCtx, const char *szRepoSpec, SG_string *pstrUserName, SG_string *pstrPassword) { SG_string* pstrTarget = NULL; LPWSTR pwszTarget = NULL; LPWSTR pwszPassword = NULL; SG_uint32 lenPassword = 0; LPWSTR pwszUserName = NULL; CREDENTIAL cred; SG_NULLARGCHECK_RETURN(szRepoSpec); SG_NULLARGCHECK_RETURN(pstrUserName); SG_NULLARGCHECK_RETURN(pstrPassword); if (!SG_string__length_in_bytes(pstrUserName)) SG_ERR_THROW2_RETURN(SG_ERR_INVALIDARG, (pCtx, "%s", "pstrUserName is empty")); SG_ERR_CHECK( _get_key(pCtx, szRepoSpec, SG_string__sz(pstrUserName), &pstrTarget) ); SG_ERR_CHECK( SG_utf8__extern_to_os_buffer__wchar(pCtx, SG_string__sz(pstrTarget), &pwszTarget, NULL) ); SG_ERR_CHECK( SG_utf8__extern_to_os_buffer__wchar(pCtx, SG_string__sz(pstrPassword), &pwszPassword, &lenPassword) ); SG_ERR_CHECK( SG_utf8__extern_to_os_buffer__wchar(pCtx, SG_string__sz(pstrUserName), &pwszUserName, NULL) ); SG_zero(cred); cred.Type = CRED_TYPE_GENERIC; cred.TargetName = pwszTarget; cred.CredentialBlob = (LPBYTE)pwszPassword; cred.CredentialBlobSize = lenPassword*sizeof(wchar_t); cred.Persist = CRED_PERSIST_LOCAL_MACHINE; // unsupported on Windows Vista Home Basic, Windows Vista Home Premium, Windows Vista Starter, and Windows XP Home Edition cred.UserName = pwszUserName; if ( !CredWriteW(&cred, 0) ) SG_ERR_THROW2( SG_ERR_GETLASTERROR(GetLastError()), (pCtx, "%s", "unable to save credentials") ); /* fall through */ fail: SG_STRING_NULLFREE(pCtx, pstrTarget); SG_NULLFREE(pCtx, pwszTarget); SG_NULLFREE(pCtx, pwszPassword); SG_NULLFREE(pCtx, pwszUserName); }
static void _read_file_chunk(SG_context* pCtx, SG_curl* pCurl, char* buffer, SG_uint32 bufLen, void* pVoidState, SG_uint32* pLenHandled) { struct _sg_curl__file_read_state* pReadState = (struct _sg_curl__file_read_state*)pVoidState; SG_UNUSED(pCurl); if (pReadState->finished) { SG_ERR_THROW2_RETURN(SG_ERR_UNSPECIFIED, (pCtx, "An unknown error occurred interfacing with libcurl. Please try again.")); } else if (pReadState->pos == pReadState->len) { pReadState->finished = SG_TRUE; *pLenHandled = 0; } else { SG_ERR_CHECK_RETURN( SG_file__read(pCtx, pReadState->pFile, bufLen, (SG_byte*)buffer, pLenHandled) ); pReadState->pos += *pLenHandled; } }
void sg_client__bind_vtable(SG_context* pCtx, SG_client * pClient) { char* psz_protocol = NULL; SG_NULLARGCHECK_RETURN(pClient); SG_NULLARGCHECK_RETURN(pClient->psz_remote_repo_spec); if (pClient->p_vtable) // can only be bound once SG_ERR_THROW2_RETURN(SG_ERR_INVALIDARG, (pCtx, "vtable already bound")); // Select the vtable based on pClient's destination repo specification. if (strlen(pClient->psz_remote_repo_spec) > 6) { SG_ERR_CHECK( SG_ascii__substring(pCtx, pClient->psz_remote_repo_spec, 0, 7, &psz_protocol) ); if (SG_stricmp("http://", (const char*)psz_protocol) == 0) pClient->p_vtable = &s_client_vtable__http; } if (!pClient->p_vtable) pClient->p_vtable = &s_client_vtable__c; // fall through fail: SG_NULLFREE(pCtx, psz_protocol); }
void SG_vv2__init_new_repo(SG_context * pCtx, const char * pszRepoName, const char * pszFolder, const char * pszStorage, const char * pszHashMethod, SG_bool bNoWD, const char * psz_shared_users, SG_bool bFromUserMaster, char ** ppszGidRepoId, char ** ppszHidCSetFirst) { char * pszGidRepoId = NULL; char * pszHidCSetFirst = NULL; if (bNoWD && pszFolder && *pszFolder) { SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "init_new_repo: option 'no-wc' given with a folder argument.") ); } if (bFromUserMaster && psz_shared_users) { SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "init_new_repo: options 'shared-users' and 'from-user-master' are incompatible.") ); } // create the new repo. SG_ERR_CHECK( _vv_verbs__init_new_repo__do_init(pCtx, pszRepoName, pszStorage, pszHashMethod, psz_shared_users, bFromUserMaster, &pszGidRepoId, &pszHidCSetFirst) ); if (!bNoWD) { // try to create a new WD for the new repo. the real goal here // is to create the directory (if necessary) and then the drawer // and control files. if the directory already exists, we do not // auto-add the contents. SG_ERR_CHECK( SG_wc__initialize(pCtx, pszRepoName, pszFolder, pszHidCSetFirst, SG_VC_BRANCHES__DEFAULT) ); } if (ppszGidRepoId) *ppszGidRepoId = pszGidRepoId; else SG_NULLFREE(pCtx, pszGidRepoId); if (ppszHidCSetFirst) *ppszHidCSetFirst = pszHidCSetFirst; else SG_NULLFREE(pCtx, pszHidCSetFirst); return; fail: if (pszGidRepoId) { // If we successfully created the repo, but had an error // trying to populate the WD, delete the repo. WE DO NOT // delete any mess we created in/around the WD because we // don't know how much of it the checkout created and how // much already existed. SG_ERR_IGNORE( _vv_verbs__init_new_repo__delete_new_repo(pCtx, pszRepoName) ); } SG_NULLFREE(pCtx, pszGidRepoId); SG_NULLFREE(pCtx, pszHidCSetFirst); }
/** * 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); }
/** * Get the current attrbits observed on the item *UNIONED* * with the baseline, if present. * * If the item is LOST/DELETED (not present in the working * directory), fall back to the baseline value. * */ void sg_wc_liveview_item__get_current_attrbits(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_uint64 * pAttrbits) { if (pLVI->queuedOverwrites.pvhAttrbits) { // We have a QUEUED operation on this item that set the attrbits. // get the value from the journal. SG_ERR_CHECK_RETURN( SG_vhash__get__int64(pCtx, pLVI->queuedOverwrites.pvhAttrbits, "attrbits", (SG_int64 *)pAttrbits) ); #if TRACE_WC_LIE { SG_int_to_string_buffer bufui64; SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetCurrentAttrbits: using journal: %s\n", SG_uint64_to_sz__hex((*pAttrbits), bufui64)) ); } #endif return; } #if 1 && TRACE_WC_ATTRBITS { sg_wc_db__pc_row__flags_net flags_net = SG_WC_DB__PC_ROW__FLAGS_NET__ZERO; SG_ERR_IGNORE( sg_wc_liveview_item__get_flags_net(pCtx, pLVI, &flags_net) ); SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetCurrentAttrbits: [flagsLive %d][flagsNet 0x%02x][pPcRow_PC %04x][pPcRow_Ref %04x][pRD %04x][pTneRow %04x] %s\n", ((SG_uint32)pLVI->scan_flags_Live), ((SG_uint32)flags_net), (SG_uint32)((pLVI->pPcRow_PC) ? (pLVI->pPcRow_PC->ref_attrbits) : 0xffff), (SG_uint32)((pLVI->pPrescanRow->pPcRow_Ref) ? (pLVI->pPrescanRow->pPcRow_Ref->ref_attrbits) : 0xffff), (SG_uint32)((pLVI->pPrescanRow->pRD && pLVI->pPrescanRow->pRD->pAttrbits) ? (*pLVI->pPrescanRow->pRD->pAttrbits) : 0xffff), (SG_uint32)((pLVI->pPrescanRow->pTneRow && pLVI->pPrescanRow->pTneRow->p_d) ? (pLVI->pPrescanRow->pTneRow->p_d->attrbits) : 0xffff), SG_string__sz(pLVI->pStringEntryname)) ); } #endif 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) ); *pAttrbits = pLVI->pPcRow_PC->p_d_sparse->attrbits; } else if (pLVI->pPrescanRow->pPcRow_Ref) { SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse) ); *pAttrbits = pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse->attrbits; } else { SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentAttrbits: unhandled case when sparse for '%s'.", SG_string__sz(pLVI->pStringEntryname)) ); } } else if (pLVI->pPrescanRow->pRD) { SG_ERR_CHECK_RETURN( sg_wc_readdir__row__get_attrbits(pCtx, pWcTx, pLVI->pPrescanRow->pRD) ); if (pLVI->pPcRow_PC) SG_ERR_CHECK_RETURN( sg_wc_attrbits__compute_effective_attrbits(pCtx, pWcTx->pDb->pAttrbitsData, pLVI->pPcRow_PC->ref_attrbits, *pLVI->pPrescanRow->pRD->pAttrbits, pAttrbits) ); else if (pLVI->pPrescanRow->pPcRow_Ref) SG_ERR_CHECK_RETURN( sg_wc_attrbits__compute_effective_attrbits(pCtx, pWcTx->pDb->pAttrbitsData, pLVI->pPrescanRow->pPcRow_Ref->ref_attrbits, *pLVI->pPrescanRow->pRD->pAttrbits, pAttrbits) ); else if (pLVI->pPrescanRow->pTneRow) SG_ERR_CHECK_RETURN( sg_wc_attrbits__compute_effective_attrbits(pCtx, pWcTx->pDb->pAttrbitsData, pLVI->pPrescanRow->pTneRow->p_d->attrbits, *pLVI->pPrescanRow->pRD->pAttrbits, pAttrbits) ); else *pAttrbits = *pLVI->pPrescanRow->pRD->pAttrbits; } else if (pLVI->pPrescanRow->pTneRow) { *pAttrbits = pLVI->pPrescanRow->pTneRow->p_d->attrbits; } else { *pAttrbits = SG_WC_ATTRBITS__ZERO; } #if 1 && TRACE_WC_ATTRBITS SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetCurrentAttrbits: yielded %x: %s\n", (SG_uint32)(*pAttrbits), SG_string__sz(pLVI->pStringEntryname)) ); #endif }
/** * Get the baseline value of the content HID. * * We DO NOT attempt to substitute the merge-created-hid. * But we do for created-by-update items. * * You DO NOT own the returned result. * */ void sg_wc_liveview_item__get_original_content_hid(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_bool bNoTSC, const char ** ppszHidContent) { sg_wc_db__pc_row__flags_net flags_net = SG_WC_DB__PC_ROW__FLAGS_NET__ZERO; if (SG_WC_PRESCAN_FLAGS__IS_RESERVED(pLVI->scan_flags_Live)) { // We should not ever be called on a reserved item // because our caller shouldn't be trying to compare // HIDs (or whatever). // // TODO 2012/10/05 This should probably be an assert. SG_ERR_THROW2_RETURN( SG_ERR_WC_RESERVED_ENTRYNAME, (pCtx, "%s", SG_string__sz(pLVI->pStringEntryname)) ); } SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow) ); // try to get baseline value if present. // fallback to current value if not. if (pLVI->pTneRow_AlternateBaseline) { *ppszHidContent = pLVI->pTneRow_AlternateBaseline->p_d->pszHid; return; } if (pLVI->pPrescanRow->pTneRow) { *ppszHidContent = pLVI->pPrescanRow->pTneRow->p_d->pszHid; return; } if (SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_SPARSE(pLVI->scan_flags_Live)) { // TODO 2012/12/06 I think this only kicks in for __SPARSE + __ADD_SPECIAL_U // TODO (otherwise, the above pTneRow would be present). if (pLVI->pPcRow_PC) { *ppszHidContent = pLVI->pPcRow_PC->p_d_sparse->pszHid; return; } else if (pLVI->pPrescanRow->pPcRow_Ref) { *ppszHidContent = pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse->pszHid; return; } else { SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetOriginalContentHid: %s", SG_string__sz(pLVI->pStringEntryname)) ); } } // TODO 2012/12/06 I think this can be simplified. // TODO We only set pszHidMerge for plain files. if (pLVI->tneType != SG_TREENODEENTRY_TYPE_DIRECTORY) { // If this item was an added-by-update, then we can give them the // HID of the file that UPDATE created. SG_ERR_CHECK_RETURN( sg_wc_liveview_item__get_flags_net(pCtx, pLVI, &flags_net) ); if (flags_net & SG_WC_DB__PC_ROW__FLAGS_NET__ADD_SPECIAL_U) { if (pLVI->pPcRow_PC) { if (pLVI->pPcRow_PC->pszHidMerge) { *ppszHidContent = pLVI->pPcRow_PC->pszHidMerge; return; } } else if (pLVI->pPrescanRow->pPcRow_Ref) { if (pLVI->pPrescanRow->pPcRow_Ref->pszHidMerge) { *ppszHidContent = pLVI->pPrescanRow->pPcRow_Ref->pszHidMerge; return; } } } } #if TRACE_WC_TSC if (!bNoTSC) SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetOriginalContentHid: looking up '%s'\n", SG_string__sz(pLVI->pStringEntryname)) ); #endif if (!pLVI->pPrescanRow->pRD) { // Not likely to happen, but we can give a better(?) error message // than sg_wc_readdir__row__get_content_hid. SG_ERR_THROW2_RETURN( SG_ERR_NOT_FOUND, (pCtx, "No pRD for item '%s' in GetOriginalContentHid().", SG_string__sz(pLVI->pStringEntryname)) ); } SG_ERR_CHECK_RETURN( sg_wc_readdir__row__get_content_hid(pCtx, pWcTx, pLVI->pPrescanRow->pRD, bNoTSC, ppszHidContent, NULL) ); }
/** * Get the current content HID and optionally the size. * (Note that the current HID is not usually defined for a directory. * And therefore the content size of a directory is not usually * defined either.) * */ void sg_wc_liveview_item__get_current_content_hid(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_bool bNoTSC, char ** ppszHidContent, SG_uint64 * pSize) { 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_RETURN( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "hid", &psz) ); if (psz) { // last overwrite-type operation used an HID. #if TRACE_WC_LIE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetCurrentContentHid: using journal %s for: %s\n", psz, SG_string__sz(pLVI->pStringEntryname)) ); #endif if (pSize) SG_ERR_CHECK_RETURN( _fetch_size_of_blob(pCtx, pWcTx, psz, pSize) ); SG_ERR_CHECK_RETURN( SG_strdup(pCtx, psz, ppszHidContent) ); return; } SG_ERR_CHECK_RETURN( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "file", &psz) ); if (psz) { // last overwrite-type operation used a TEMP file. SG_ERR_CHECK_RETURN( sg_wc_compute_file_hid__sz(pCtx, pWcTx, psz, ppszHidContent, pSize) ); return; } SG_ERR_CHECK_RETURN( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "target", &psz) ); if (psz) { // last overwrite-type operation gave us a SYMLINK-TARGET. // it is no problem to compute this, i'm just being // lazy since i'm not sure we need this. SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentContentHid: using journal: TODO compute HID of symlink target '%s' for: %s", psz, SG_string__sz(pLVI->pStringEntryname)) ); // TODO also return size } SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentContentHid: required field missing from vhash for: %s", SG_string__sz(pLVI->pStringEntryname)) ); } 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_RETURN( SG_STRDUP(pCtx, pLVI->pPcRow_PC->p_d_sparse->pszHid, ppszHidContent) ); if (pSize) SG_ERR_CHECK_RETURN( _fetch_size_of_blob(pCtx, pWcTx, pLVI->pPcRow_PC->p_d_sparse->pszHid, pSize) ); } else if (pLVI->pPrescanRow->pPcRow_Ref) { SG_ASSERT_RELEASE_RETURN( (pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse) ); SG_ERR_CHECK_RETURN( SG_STRDUP(pCtx, pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse->pszHid, ppszHidContent) ); if (pSize) SG_ERR_CHECK_RETURN( _fetch_size_of_blob(pCtx, pWcTx, pLVI->pPrescanRow->pPcRow_Ref->p_d_sparse->pszHid, pSize) ); } 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, "GetCurrentHid: unhandled case when sparse for '%s'.", SG_string__sz(pLVI->pStringEntryname)) ); } } else if (pLVI->pPrescanRow->pRD) { #if TRACE_WC_TSC if (!bNoTSC) SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetCurrentContentHid: looking up '%s'\n", SG_string__sz(pLVI->pStringEntryname)) ); #endif SG_ERR_CHECK_RETURN( sg_wc_readdir__row__get_content_hid__owned(pCtx, pWcTx, pLVI->pPrescanRow->pRD, bNoTSC, ppszHidContent, pSize) ); } else if (pLVI->pPrescanRow->pTneRow) { SG_ERR_CHECK_RETURN( SG_STRDUP(pCtx, pLVI->pPrescanRow->pTneRow->p_d->pszHid, ppszHidContent) ); if (pSize) SG_ERR_CHECK_RETURN( _fetch_size_of_blob(pCtx, pWcTx, pLVI->pPrescanRow->pTneRow->p_d->pszHid, pSize) ); } else { // perhaps an ADD-SPECIAL + DELETE // or an ADDED+LOST in an UPDATE ? SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetCurrentHid: unhandled case for '%s'.", SG_string__sz(pLVI->pStringEntryname)) ); } }
/** * 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); }
/** * Return a pathname (live or temp) to a file that contains * the CURRENTLY QUEUED content that this item **SHOULD** have * at this point in the TX. * * That is, the caller could be in the middle of a TX and have * overwritten the file once or twice and then may now be * requesting the path to show a diff. Or the file content may * be unchanged, but we have queued one or more moves/renames to * it or parent directories. * * As a good player INSIDE THE TX, we need to give them a path * to a CURRENT IN-TX COPY OF THE ***CONTENT*** (wherever it * may be). * * So the path we return may be to a temp file that was created * as a source for a QUEUED overwrite. Or it may be a path to * the unmodified content in the WD -- WHERE IT WAS BEFORE THE * TX -- because until APPLY is called, the WD hasn't been * changed yet. * * Regardless of whether the result is a temp file or not, the * caller should be careful to not let the user modify the file * without participating in the TX. That is, if we return the * actual non-temp working copy of a file and they use it in a * DIFF and the user's difftool is interactive and they alter * it and then we cancel the TX, what should the WD version of * the file contain? * * See also: * __overwrite_file_from_file() * __overwrite_file_from_repo() * __add_special() * __undo_delete() * * * We return an indication of whether the file is a TEMP file * and shouldn't be written to. It DOES NOT indicate that you * can delete it -- it indicates that you should not edit it because * *WE* will probably delete the file if the TX is rolled-back and so * the user would lose their edits. * */ void sg_wc_liveview_item__get_proxy_file_path(SG_context * pCtx, sg_wc_liveview_item * pLVI, SG_wc_tx * pWcTx, SG_pathname ** ppPath, SG_bool * pbIsTmp) { SG_string * pStringRepoPath = NULL; SG_pathname * pPathAbsolute = NULL; char * pszGid = NULL; const char * psz; SG_bool bIsTmp = SG_TRUE; if (pLVI->tneType != SG_TREENODEENTRY_TYPE_REGULAR_FILE) SG_ERR_THROW2_RETURN( SG_ERR_INVALIDARG, (pCtx, "GetProxyFilePath: '%s' is not a file.", SG_string__sz(pLVI->pStringEntryname)) ); if (pLVI->queuedOverwrites.pvhContent == NULL) { // No changes to the content yet in this TX. Return the PRE-TX // pathname of this file. (We may have QUEUED moves/renames on // the file or a parent directory, but they haven't been applied // yet.) SG_ASSERT( pLVI->pPrescanRow ); SG_ASSERT( pLVI->pPrescanRow->pStringEntryname ); SG_ASSERT( pLVI->pPrescanRow->pPrescanDir_Ref ); SG_ASSERT( pLVI->pPrescanRow->pPrescanDir_Ref->pStringRefRepoPath ); SG_ERR_CHECK( SG_STRING__ALLOC__COPY(pCtx, &pStringRepoPath, pLVI->pPrescanRow->pPrescanDir_Ref->pStringRefRepoPath) ); SG_ERR_CHECK( SG_repopath__append_entryname(pCtx, pStringRepoPath, SG_string__sz(pLVI->pPrescanRow->pStringEntryname), SG_FALSE) ); SG_ERR_CHECK( sg_wc_db__path__repopath_to_absolute(pCtx, pWcTx->pDb, pStringRepoPath, &pPathAbsolute) ); bIsTmp = SG_FALSE; // path is to actual WC file goto done; } SG_ERR_CHECK_RETURN( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "file", &psz) ); if (psz) { // return path to existing TEMP file. someone else owns the file. SG_ERR_CHECK( SG_PATHNAME__ALLOC__SZ(pCtx, &pPathAbsolute, psz) ); bIsTmp = SG_TRUE; // path is to a TEMP file (for which an overwrite-from-file has already been scheduled). goto done; } SG_ERR_CHECK_RETURN( SG_vhash__check__sz(pCtx, pLVI->queuedOverwrites.pvhContent, "hid", &psz) ); if (psz) { // synthesize a TEMP file for this. caller owns the new temp file. SG_ERR_CHECK( sg_wc_db__gid__get_gid_from_alias(pCtx, pWcTx->pDb, pLVI->uiAliasGid, &pszGid) ); SG_ERR_CHECK( sg_wc_diff_utils__export_to_temp_file(pCtx, pWcTx, "ref", pszGid, psz, SG_string__sz(pLVI->pStringEntryname), // for suffix only &pPathAbsolute) ); bIsTmp = SG_TRUE; // path is to a TEMP file that we just created. goto done; } SG_ERR_THROW2_RETURN( SG_ERR_NOTIMPLEMENTED, (pCtx, "GetProxyFilePath: required field missing from vhash for: %s", SG_string__sz(pLVI->pStringEntryname)) ); done: #if TRACE_WC_LIE SG_ERR_IGNORE( SG_console(pCtx, SG_CS_STDERR, "GetProxyFilePath: '%s' ==> '%s' [bIsTmp %d]\n", SG_string__sz(pLVI->pStringEntryname), SG_pathname__sz(pPathAbsolute), bIsTmp) ); #endif *ppPath = pPathAbsolute; pPathAbsolute = NULL; *pbIsTmp = bIsTmp; fail: SG_PATHNAME_NULLFREE(pCtx, pPathAbsolute); SG_STRING_NULLFREE(pCtx, pStringRepoPath); SG_NULLFREE(pCtx, pszGid); }
static void do_upload__string( SG_context* pCtx, const char* pszUrl, const char* psz_method, SG_pathname* pPath, const char* psz_username, const char* psz_password, SG_string** ppstr ) { SG_string* pstr = NULL; CFHTTPMessageRef myRequest = NULL; CFHTTPMessageRef myResponse = NULL; CFStringRef s_username = NULL; CFStringRef s_password = NULL; SG_ERR_CHECK( make_request(pCtx, pszUrl, psz_method, NULL, &myRequest) ); SG_ERR_CHECK( perform_upload_request__string(pCtx, myRequest, pPath, &myResponse, &pstr) ); #if 0 { CFDataRef d = CFHTTPMessageCopySerializedMessage(myResponse); fprintf(stderr, "%s\n", CFDataGetBytePtr(d)); CFRelease(d); } #endif UInt32 statusCode = CFHTTPMessageGetResponseStatusCode(myResponse); if ( psz_username && psz_password && (statusCode == 401 || statusCode == 407) ) { s_username = CFStringCreateWithCString(kCFAllocatorDefault, psz_username, kCFStringEncodingUTF8); s_password = CFStringCreateWithCString(kCFAllocatorDefault, psz_password, kCFStringEncodingUTF8); if (!CFHTTPMessageAddAuthentication(myRequest, myResponse, s_username, s_password, kCFHTTPAuthenticationSchemeDigest, FALSE)) { SG_ERR_THROW2(SG_ERR_UNSPECIFIED, (pCtx, "CFHTTPMessageAddAuthentication failed")); } #if 0 { CFDataRef d = CFHTTPMessageCopySerializedMessage(myRequest); fprintf(stderr, "%s\n", CFDataGetBytePtr(d)); CFRelease(d); } #endif CFRelease(s_username); s_username = NULL; CFRelease(s_password); s_password = NULL; CFRelease(myResponse); myResponse = NULL; SG_STRING_NULLFREE(pCtx, pstr); SG_ERR_CHECK( perform_upload_request__string(pCtx, myRequest, pPath, &myResponse, &pstr) ); #if 0 { CFDataRef d = CFHTTPMessageCopySerializedMessage(myResponse); fprintf(stderr, "%s\n", CFDataGetBytePtr(d)); CFRelease(d); } #endif statusCode = CFHTTPMessageGetResponseStatusCode(myResponse); } if (statusCode != 200) { SG_ERR_THROW2_RETURN(SG_ERR_SERVER_HTTP_ERROR, (pCtx, "%d", (int) statusCode)); } *ppstr = pstr; pstr = NULL; /* fall through */ fail: if (s_username) { CFRelease(s_username); s_username = NULL; } if (s_password) { CFRelease(s_password); s_password = NULL; } if (myRequest) { CFRelease(myRequest); myRequest = NULL; } if (myResponse) { CFRelease(myResponse); myResponse = NULL; } SG_STRING_NULLFREE(pCtx, pstr); }
void SG_getopt__long(SG_context* pCtx, SG_getopt* os, const SG_getopt_option* opts, SG_uint32* optch, const char** optarg, SG_bool* bNoMoreOptions) { const char *p; SG_int32 i; /* Let the calling program reset option processing. */ if (os->reset) { os->place = ""; os->ind = 1; os->reset = 0; } /* * We can be in one of two states: in the middle of processing a * run of short options, or about to process a new argument. * Since the second case can lead to the first one, handle that * one first. */ p = os->place; if (*p == '\0') { /* If we are interleaving, skip non-option arguments. */ if (os->interleave) { while (os->ind < os->count_args && *os->paszArgs[os->ind] != '-') os->ind++; os->skip_end = os->ind; } if (os->ind >= os->count_args || *os->paszArgs[os->ind] != '-') { os->ind = os->skip_start; //return APR_EOF; *bNoMoreOptions = SG_TRUE; SG_ERR_THROW_RETURN( SG_ERR_GETOPT_NO_MORE_OPTIONS ); } p = os->paszArgs[os->ind++] + 1; if (*p == '-' && p[1] != '\0') { /* Long option */ /* Search for the long option name in the caller's table. */ SG_uint32 len = 0; p++; for (i = 0; ; i++) { if (opts[i].optch == 0) /* No match */ SG_ERR_THROW2_RETURN( SG_ERR_GETOPT_BAD_ARG, (pCtx, "Invalid option: %s", p - 2) ); //return serr(os, "invalid option", p - 2, APR_BADCH); if (opts[i].pStringName) { len = strlen(opts[i].pStringName); if (strncmp(p, opts[i].pStringName, len) == 0 && (p[len] == '\0' || p[len] == '=')) break; } } *optch = opts[i].optch; if (opts[i].has_arg) { if (p[len] == '=') /* Argument inline */ *optarg = p + len + 1; else { if (os->ind >= os->count_args) /* Argument missing */ SG_ERR_THROW2_RETURN( SG_ERR_GETOPT_BAD_ARG, (pCtx, "Missing argument: %s", p - 2) ); //return serr(os, "missing argument", p - 2, APR_BADARG); else /* Argument in next arg */ *optarg = os->paszArgs[os->ind++]; } } else { *optarg = NULL; if (p[len] == '=') SG_ERR_THROW2_RETURN( SG_ERR_GETOPT_BAD_ARG, (pCtx, "Erroneous argument: %s", p - 2) ); //return serr(os, "erroneous argument", p - 2, APR_BADARG); } _sg_permute(os); //return APR_SUCCESS; *bNoMoreOptions = SG_FALSE; return; } else { if (*p == '-') { /* Bare "--"; we're done */ _sg_permute(os); os->ind = os->skip_start; //return APR_EOF; *bNoMoreOptions = SG_TRUE; SG_ERR_THROW_RETURN(SG_ERR_GETOPT_NO_MORE_OPTIONS); } else if (*p == '\0') /* Bare "-" is illegal */ SG_ERR_THROW2_RETURN( SG_ERR_GETOPT_BAD_ARG, (pCtx, "Invalid option: %s", p) ); //return serr(os, "invalid option", p, APR_BADCH); } } /* * Now we're in a run of short options, and *p is the next one. * Look for it in the caller's table. */ for (i = 0; ; i++) { if (opts[i].optch == 0) /* No match */ SG_ERR_THROW2_RETURN( SG_ERR_GETOPT_BAD_ARG, (pCtx, "Invalid option character: %c", *p) ); //return cerr(os, "invalid option character", *p, APR_BADCH); if (*p == opts[i].optch) break; } *optch = *p++; if (opts[i].has_arg) { if (*p != '\0') /* Argument inline */ *optarg = p; else { if (os->ind >= os->count_args) /* Argument missing */ SG_ERR_THROW2_RETURN( SG_ERR_GETOPT_BAD_ARG, (pCtx, "Missing argument: %c", *optch) ); //return cerr(os, "missing argument", *optch, APR_BADARG); else /* Argument in next arg */ *optarg = os->paszArgs[os->ind++]; } os->place = ""; } else { *optarg = NULL; os->place = p; } _sg_permute(os); *bNoMoreOptions = SG_FALSE; return; }