void SG_curl__get_response_headers(SG_context * pCtx, SG_curl * pCurl, SG_string ** ppHeaders) { _sg_curl* pMe = (_sg_curl*)pCurl; SG_ASSERT(pMe->pstrRawHeaders!=NULL); SG_ERR_CHECK_RETURN( SG_STRING__ALLOC__COPY(pCtx, ppHeaders, pMe->pstrRawHeaders) ); }
void sg_wc_liveview_item__alloc__clone_from_prescan(SG_context * pCtx, sg_wc_liveview_item ** ppLVI, SG_wc_tx * pWcTx, const sg_wc_prescan_row * pPrescanRow) { 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 = pPrescanRow->uiAliasGid; SG_ERR_CHECK( SG_STRING__ALLOC__COPY(pCtx, &pLVI->pStringEntryname, pPrescanRow->pStringEntryname) ); pLVI->pPrescanRow = pPrescanRow; // borrow a reference rather than cloning the scanrow. // because a liveview_item must start as an // exact clone of a scanrow, there cannot be // any in-tx changes yet for it. pLVI->pPcRow_PC = NULL; pLVI->scan_flags_Live = pPrescanRow->scan_flags_Ref; pLVI->tneType = pPrescanRow->tneType; 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); }
/** * 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); }
/** * 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); }
void sg_wc_tx__rp__undo_delete__lvi_lvi_sz(SG_context * pCtx, SG_wc_tx * pWcTx, sg_wc_liveview_item * pLVI_Src, sg_wc_liveview_item * pLVI_DestDir, const char * pszEntryname_Dest, const SG_wc_undo_delete_args * pArgs, SG_wc_status_flags xu_mask) { SG_string * pStringRepoPath_Src_Computed = NULL; SG_string * pStringRepoPath_DestDir_Computed = NULL; SG_string * pStringRepoPath_Dest_Computed = NULL; sg_wc_liveview_item * pLVI_Dest; // we do not own this sg_wc_liveview_dir * pLVD_DestDir; // we do not own this SG_bool bKnown_Dest; SG_NULLARGCHECK_RETURN( pWcTx ); SG_NULLARGCHECK_RETURN( pLVI_Src ); SG_NULLARGCHECK_RETURN( pLVI_DestDir ); SG_NONEMPTYCHECK_RETURN( pszEntryname_Dest ); // pArgs is optional SG_ERR_CHECK( sg_wc_tx__liveview__compute_live_repo_path(pCtx, pWcTx, pLVI_Src, &pStringRepoPath_Src_Computed) ); SG_ERR_CHECK( sg_wc_tx__liveview__compute_live_repo_path(pCtx, pWcTx, pLVI_DestDir, &pStringRepoPath_DestDir_Computed) ); if (SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_DELETED(pLVI_Src->scan_flags_Live) == SG_FALSE) SG_ERR_THROW2( SG_ERR_WC__ITEM_ALREADY_EXISTS, (pCtx, "Source '%s'", SG_string__sz(pStringRepoPath_Src_Computed)) ); if (SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_DELETED(pLVI_DestDir->scan_flags_Live)) SG_ERR_THROW2( SG_ERR_NOT_FOUND, (pCtx, "The destination directory '%s' has been REMOVED.", SG_string__sz(pStringRepoPath_DestDir_Computed)) ); if (SG_WC_PRESCAN_FLAGS__IS_CONTROLLED_LOST(pLVI_DestDir->scan_flags_Live)) SG_ERR_THROW2( SG_ERR_NOT_FOUND, (pCtx, "The destination directory '%s' is currently LOST.", SG_string__sz(pStringRepoPath_DestDir_Computed)) ); SG_ERR_CHECK( SG_STRING__ALLOC__COPY(pCtx, &pStringRepoPath_Dest_Computed, pStringRepoPath_DestDir_Computed) ); SG_ERR_CHECK( SG_repopath__append_entryname(pCtx, pStringRepoPath_Dest_Computed, pszEntryname_Dest, SG_FALSE) ); SG_ERR_CHECK( sg_wc_tx__liveview__fetch_item(pCtx, pWcTx, pStringRepoPath_Dest_Computed, &bKnown_Dest, &pLVI_Dest) ); if (bKnown_Dest) SG_ERR_THROW2( SG_ERR_WC__ITEM_ALREADY_EXISTS, (pCtx, "The destination '%s' already exists.", SG_string__sz(pStringRepoPath_Dest_Computed)) ); // I'm not going to worry about path-cycle problems // because we already know that the source does not // exist and the destination directory does, so it // isn't possible to have a cycle. SG_ERR_CHECK( sg_wc_tx__liveview__fetch_dir(pCtx, pWcTx, pLVI_DestDir, &pLVD_DestDir) ); SG_ERR_CHECK( sg_wc_liveview_dir__can_add_new_entryname(pCtx, pWcTx->pDb, pLVD_DestDir, NULL, NULL, pszEntryname_Dest, SG_TREENODEENTRY_TYPE__INVALID, SG_FALSE) ); SG_ERR_CHECK( sg_wc_tx__queue__undo_delete(pCtx, pWcTx, pStringRepoPath_Dest_Computed, pLVI_Src, pLVI_DestDir, pszEntryname_Dest, pArgs, xu_mask) ); fail: SG_STRING_NULLFREE(pCtx, pStringRepoPath_Src_Computed); SG_STRING_NULLFREE(pCtx, pStringRepoPath_DestDir_Computed); SG_STRING_NULLFREE(pCtx, pStringRepoPath_Dest_Computed); }
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); }