void SG_tid__generate2(SG_context * pCtx,
					   char * bufTid, SG_uint32 lenBuf,
					   SG_uint32 nrRandomDigits)
{
	char bufGid[SG_GID_BUFFER_LENGTH];
	SG_uint32 lenRequired;

	SG_NULLARGCHECK_RETURN( bufTid );

	// Note: Arbitrarily enforce a 4 digit lower bound.
	SG_ARGCHECK_RETURN(  ((nrRandomDigits >= 4) && (nrRandomDigits <= SG_TID_LENGTH_RANDOM_MAX)), nrRandomDigits  );

	lenRequired = SG_TID_LENGTH_PREFIX + nrRandomDigits + 1;	// +1 for trailing null

	SG_ARGCHECK_RETURN( (lenBuf >= lenRequired), lenBuf );

	// We create a real GID.  This gives us a lot of random hex digits.
	// We then use the first n hex digits to create a TID.  We assume
	// that a TID is narrower than a GID (or else we'd have to create
	// more than one GID).
	SG_ASSERT(  (SG_TID_LENGTH_RANDOM_MAX <= SG_GID_LENGTH_RANDOM)  );
	SG_ERR_CHECK_RETURN(  SG_gid__generate(pCtx, bufGid, sizeof(bufGid))  );

	SG_ASSERT(  (SG_TID_LENGTH_PREFIX == 1)  );

	bufTid[0] = 't';
	memcpy(&bufTid[SG_TID_LENGTH_PREFIX], &bufGid[SG_GID_LENGTH_PREFIX], nrRandomDigits);
	bufTid[SG_TID_LENGTH_PREFIX + nrRandomDigits] = 0;
}
Esempio n. 2
0
void SG_time__format_local__i64(SG_context* pCtx, SG_int64 iTime,
								  char * pBuf, SG_uint32 lenBuf)
{
	// do simple formatting of time value and generate a localtime-based, human-readable time.
	// this is like ctime(3), asctime(3), gmtime(3), localtime(3) only i don't currently
	// care about all the various formatting options.
	//
	// that is, we don't care about:
	// [] which fields we show,
	// [] localization (which order we show the fields and month names),
	// [] 12 vs 24 hour time.
	// [] UTF8.
	//
	// we are given a buffer that must be SG_TIME_FORMAT_LENGTH+1 characters or larger.

	SG_time tm;

	SG_NULLARGCHECK_RETURN( pBuf );
	SG_ARGCHECK_RETURN( (lenBuf >= SG_TIME_FORMAT_LENGTH+1), lenBuf );

	// convert milliseconds-since-epoch into individual fields.

	SG_ERR_CHECK_RETURN(  SG_time__decode__local(pCtx, iTime,&tm)  );

	SG_ERR_CHECK_RETURN( SG_sprintf(pCtx, pBuf,lenBuf,"%04d/%02d/%02d %02d:%02d:%02d.%03d %+03d%02d",
					 tm.year,tm.month,tm.mday,
					 tm.hour,tm.min,tm.sec,tm.msec,
					 tm.offsetUTC/3600,abs((tm.offsetUTC/60)%60)) );

}
Esempio n. 3
0
void SG_dagfrag__alloc(SG_context * pCtx,
					   SG_dagfrag ** ppNew,
					   const char* psz_repo_id,
					   const char* psz_admin_id,
					   SG_uint32 iDagNum)
{
	SG_dagfrag * pFrag = NULL;

	SG_NULLARGCHECK_RETURN(ppNew);
	SG_ARGCHECK_RETURN( (iDagNum != SG_DAGNUM__NONE), iDagNum );
	SG_ERR_CHECK_RETURN(  SG_gid__argcheck(pCtx, psz_repo_id)  );
	SG_ERR_CHECK_RETURN(  SG_gid__argcheck(pCtx, psz_admin_id)  );

    SG_ERR_CHECK(  SG_alloc(pCtx, 1, sizeof(SG_dagfrag), &pFrag)  );

    SG_ERR_CHECK(  SG_strcpy(pCtx, pFrag->m_sz_repo_id, sizeof(pFrag->m_sz_repo_id), psz_repo_id)  );
    SG_ERR_CHECK(  SG_strcpy(pCtx, pFrag->m_sz_admin_id, sizeof(pFrag->m_sz_admin_id), psz_admin_id)  );

    pFrag->m_iDagNum = iDagNum;

	SG_ERR_CHECK(  SG_RBTREE__ALLOC(pCtx,&pFrag->m_pRB_Cache)  );

	pFrag->m_pRB_GenerationSortedMemberCache = NULL;		// only create this if needed

	*ppNew = pFrag;
	return;

fail:
	SG_DAGFRAG_NULLFREE(pCtx, pFrag);
}
void SG_unzip__currentfile__close(SG_context* pCtx, SG_unzip* s)
{

    file_in_zip_read_info_s* pfile_in_zip_read_info;

    SG_NULLARGCHECK_RETURN( s );

    pfile_in_zip_read_info = s->pfile_in_zip_read;

	SG_ARGCHECK_RETURN((pfile_in_zip_read_info!=NULL), s);

    if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
    {
        if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
        {
            SG_ERR_THROW(  SG_ERR_ZIP_CRC  );
        }
    }

    SG_NULLFREE(pCtx, pfile_in_zip_read_info->read_buffer);
    pfile_in_zip_read_info->read_buffer = NULL;
    if (pfile_in_zip_read_info->stream_initialised)
    {
        inflateEnd(&pfile_in_zip_read_info->stream);
    }

    pfile_in_zip_read_info->stream_initialised = 0;
    SG_NULLFREE(pCtx, pfile_in_zip_read_info);

    s->pfile_in_zip_read=NULL;

fail:
    return;
}
Esempio n. 5
0
void SG_localsettings__descriptor__update__sz(
	SG_context * pCtx,
	const char * psz_descriptor_name,
	const char * psz_path,
	const char * pValue)
{
	SG_jsondb* p = NULL;
	SG_string* pstr_path = NULL;

	SG_ASSERT(pCtx);
	SG_NONEMPTYCHECK_RETURN(psz_descriptor_name);
	SG_NONEMPTYCHECK_RETURN(psz_path);
	SG_ARGCHECK_RETURN('/' != psz_path[0], psz_path);

	SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pstr_path)  );
	SG_ERR_CHECK(  SG_string__sprintf(pCtx, pstr_path, "%s/%s/%s",
		SG_LOCALSETTING__SCOPE__INSTANCE, psz_descriptor_name, psz_path)  );

	SG_ERR_CHECK(  SG_closet__get_localsettings(pCtx, &p)  );
	SG_ERR_CHECK(  SG_jsondb__update__string__sz(pCtx, p, SG_string__sz(pstr_path), SG_TRUE, pValue)  );

fail:
	SG_STRING_NULLFREE(pCtx, pstr_path);
	SG_JSONDB_NULLFREE(pCtx, p);
}
Esempio n. 6
0
void SG_vector_i64__set(SG_context* pCtx, SG_vector_i64 * pVector, SG_uint32 k, SG_int64 value)
{
	SG_NULLARGCHECK_RETURN(pVector);

	SG_ARGCHECK_RETURN(k < pVector->m_uiSizeInUse, pVector->m_uiSizeInUse);

	pVector->m_array[k] = value;
}
Esempio n. 7
0
void SG_vector_i64__get(SG_context* pCtx, const SG_vector_i64 * pVector, SG_uint32 k, SG_int64 * pValue)
{
	SG_NULLARGCHECK_RETURN(pVector);
	SG_NULLARGCHECK_RETURN(pValue);

	SG_ARGCHECK_RETURN(k < pVector->m_uiSizeInUse, pVector->m_uiSizeInUse);

	*pValue = pVector->m_array[k];
}
Esempio n. 8
0
void SG_vfile__end(
	SG_context* pCtx,
	SG_vfile** ppvf,
	const SG_vhash* pvh
	)
{
	SG_string* pstr = NULL;
	SG_vfile* pvf = NULL;

	SG_NULLARGCHECK_RETURN(ppvf);

	pvf = *ppvf;

	if (pvh)
	{
		SG_ARGCHECK_RETURN( !(pvf->mode & SG_FILE_RDONLY) , pvh );

		SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pstr)  );

		SG_ERR_CHECK(  SG_vhash__to_json(pCtx, pvh,pstr)  );

		SG_ERR_CHECK(  SG_file__seek(pCtx, pvf->pFile, 0)  );

		SG_ERR_CHECK(  SG_file__truncate(pCtx, pvf->pFile)  );

#if TRACE_VFILE
		SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR, "VFileEnd: Writing %d bytes to file.\n",
								   SG_string__length_in_bytes(pstr))  );
#endif

		SG_ERR_CHECK(  SG_file__write(pCtx, pvf->pFile, SG_string__length_in_bytes(pstr), (const SG_byte *)SG_string__sz(pstr), NULL)  );
		SG_STRING_NULLFREE(pCtx, pstr);
	}
	else
	{
		if (!(pvf->mode & SG_FILE_RDONLY))
		{
			SG_ERR_CHECK(  SG_file__seek(pCtx, pvf->pFile, 0)  );

			SG_ERR_CHECK(  SG_file__truncate(pCtx, pvf->pFile)  );

#if TRACE_VFILE
			SG_ERR_IGNORE(  SG_console(pCtx, SG_CS_STDERR, "VFileEnd: Truncating file.\n")  );
#endif
		}
	}

	SG_FILE_NULLCLOSE(pCtx, pvf->pFile);

	SG_NULLFREE(pCtx, pvf);
	*ppvf = NULL;

	return;
fail:
	SG_STRING_NULLFREE(pCtx, pstr);
}
/**
 * Iterate over all items that list the given gid as
 * their parent directory.
 *
 * Warning: if you give a bogus directory gid, we can't
 * tell -- our SELECT just won't find any.
 *
 */
void sg_wc_db__tne__foreach_in_dir_by_parent_alias(SG_context * pCtx,
												   sg_wc_db * pDb,
												   const sg_wc_db__cset_row * pCSetRow,
												   SG_uint64 uiAliasGidParent,
												   sg_wc_db__tne__foreach_cb * pfn_cb,
												   void * pVoidData)
{
	sqlite3_stmt * pStmt = NULL;
	sg_wc_db__tne_row * pTneRow = NULL;
	int rc;

	SG_ARGCHECK_RETURN(  (uiAliasGidParent != SG_WC_DB__ALIAS_GID__UNDEFINED), uiAliasGidParent  );

	SG_ERR_CHECK(  sg_sqlite__prepare(pCtx, pDb->psql, &pStmt,
									  ("SELECT"
									   "    alias_gid,"			// 0
									   "    hid,"				// 1
									   "    type,"				// 2
									   "    attrbits,"			// 3
									   "    entryname"			// 4
									   "  FROM %s"
									   "  WHERE (alias_gid_parent = ?)"),
									  pCSetRow->psz_tne_table_name)  );
	SG_ERR_CHECK(  sg_sqlite__bind_int64(pCtx, pStmt, 1, uiAliasGidParent)  );

	while ((rc=sqlite3_step(pStmt)) == SQLITE_ROW)
	{
		SG_ERR_CHECK(  sg_wc_db__tne_row__alloc(pCtx, &pTneRow)  );

		pTneRow->p_s->uiAliasGid       = (SG_uint64)sqlite3_column_int64(pStmt, 0);
		pTneRow->p_s->uiAliasGidParent = uiAliasGidParent;
		SG_ERR_CHECK(  SG_STRDUP(pCtx, (const char *)sqlite3_column_text(pStmt, 1), &pTneRow->p_d->pszHid)  );
		pTneRow->p_s->tneType          = (SG_uint32)sqlite3_column_int(pStmt, 2);
		pTneRow->p_d->attrbits         = (SG_uint64)sqlite3_column_int64(pStmt, 3);
		SG_ERR_CHECK(  SG_STRDUP(pCtx, (const char *)sqlite3_column_text(pStmt, 4), &pTneRow->p_s->pszEntryname)  );

		// pass the tne_row by address so that the caller can steal it if they want to.
		SG_ERR_CHECK(  (*pfn_cb)(pCtx, pVoidData, &pTneRow)  );

		SG_WC_DB__TNE_ROW__NULLFREE(pCtx, pTneRow);
	}
	if (rc != SQLITE_DONE)
	{
		SG_ERR_THROW(  SG_ERR_SQLITE(rc)  );
	}

	SG_ERR_CHECK(  sg_sqlite__nullfinalize(pCtx, &pStmt)  );
	return;

fail:
	SG_ERR_IGNORE(  sg_sqlite__nullfinalize(pCtx, &pStmt)  );
	SG_WC_DB__TNE_ROW__NULLFREE(pCtx, pTneRow);
}
/**
 * Convert an alias into a domain-specific repo-path
 * relative to this cset.  This is based *STRICTLY*
 * upon the content of this tne_* table.  We DO NOT
 * know anything about pending changes or the tbl_pc
 * table.
 *
 * We also DO NOT know anything about the association
 * of domain and which tne_* table to use; that is the
 * responsibility of the caller.
 *
 * For example, it is usually the case that we have the
 * domain mapping:
 *     'b' ==> "L0"
 *     'c' ==> "L1"
 * but we don't assume that.
 *
 */
void sg_wc_db__tne__get_extended_repo_path_from_alias(SG_context * pCtx,
													  sg_wc_db * pDb,
													  const sg_wc_db__cset_row * pCSetRow,
													  SG_uint64 uiAliasGid,
													  char chDomain,
													  SG_string ** ppStringRepoPath)
{
	SG_string * pStringRepoPath = NULL;
	sg_wc_db__tne_row * pTneRow = NULL;

	SG_NULLARGCHECK_RETURN( pDb );
	SG_NULLARGCHECK_RETURN( pCSetRow );
	SG_ARGCHECK_RETURN( (strchr(("abcdef"        // 'g' is reserved
								 "hijklmnopqrs"  // 't' is reserved
								 "uvwxyz"        // '/' is reserved
								 "0123456789"),
								chDomain)), chDomain );
	SG_NULLARGCHECK_RETURN( ppStringRepoPath );

	SG_ERR_CHECK(  sg_wc_db__tne__get_row_by_alias(pCtx, pDb, pCSetRow,
												   uiAliasGid,
												   NULL, &pTneRow)  );
	SG_ASSERT(  (pTneRow)  );

	if (pTneRow->p_s->uiAliasGidParent == SG_WC_DB__ALIAS_GID__NULL_ROOT)
	{
		SG_ASSERT_RELEASE_FAIL( (strcmp(pTneRow->p_s->pszEntryname, "@")==0) );

		SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pStringRepoPath)  );
		SG_ERR_CHECK(  SG_string__sprintf(pCtx, pStringRepoPath, "@%c/", chDomain)  );
	}
	else
	{
		SG_ERR_CHECK(  sg_wc_db__tne__get_extended_repo_path_from_alias(pCtx, pDb, pCSetRow,
																		pTneRow->p_s->uiAliasGidParent,
																		chDomain,
																		&pStringRepoPath)  );
		SG_ASSERT( pStringRepoPath );
		SG_ERR_CHECK(  SG_repopath__append_entryname(pCtx, pStringRepoPath,
													 pTneRow->p_s->pszEntryname,
													 (pTneRow->p_s->tneType == SG_TREENODEENTRY_TYPE_DIRECTORY))  );
	}
	
	*ppStringRepoPath = pStringRepoPath;
	pStringRepoPath = NULL;

fail:
	SG_WC_DB__TNE_ROW__NULLFREE(pCtx, pTneRow);
	SG_STRING_NULLFREE(pCtx, pStringRepoPath);
}
/**
 * Convert relative path to absolute, but make
 * sure that normalization doesn't take it outside
 * of the working directory.
 *
 * This is just pathname/string parsing; we DO NOT
 * confirm that the path exists or could exist.
 *
 */
void sg_wc_db__path__relative_to_absolute(SG_context * pCtx,
										  const sg_wc_db * pDb,
										  const SG_string * pStringRelativePath,
										  SG_pathname ** ppPathItem)
{
	SG_pathname * pPathWDT = NULL;
	SG_pathname * pPath = NULL;
	SG_uint32 lenWDT;

	// choke if they give us a repo-path.
	SG_ARGCHECK_RETURN(  (SG_string__sz(pStringRelativePath)[0] != '@'), pStringRelativePath  );

	// choke if this WC TX isn't cwd-based.
	SG_ERR_CHECK(  _check_if_relative_paths_allowed(pCtx, pDb)  );

	// clone WDT so that we can force a trailing slash.
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__COPY(pCtx,
											&pPathWDT,
											pDb->pPathWorkingDirectoryTop)  );
	SG_ERR_CHECK(  SG_pathname__add_final_slash(pCtx, pPathWDT)  );
	lenWDT = SG_pathname__length_in_bytes(pPathWDT);

	// allocate and normalize a new path with the net
	// result rather than writing on the clone (so that
	// we can do the following prefix test safely).
	SG_ERR_CHECK(  SG_PATHNAME__ALLOC__PATHNAME_SZ(pCtx, &pPath,
												   pPathWDT,
												   SG_string__sz(pStringRelativePath))  );

	if (strncmp(SG_pathname__sz(pPath),
				SG_pathname__sz(pPathWDT),
				lenWDT) != 0)
	{
		SG_ERR_THROW2(  SG_ERR_PATH_NOT_IN_WORKING_COPY,
						(pCtx, "The path '%s' is not inside the working copy rooted at '%s'.",
						 SG_string__sz(pStringRelativePath),
						 SG_pathname__sz(pDb->pPathWorkingDirectoryTop))  );
	}

	SG_PATHNAME_NULLFREE(pCtx, pPathWDT);
	*ppPathItem = pPath;
	return;

fail:
	SG_PATHNAME_NULLFREE(pCtx, pPathWDT);
	SG_PATHNAME_NULLFREE(pCtx, pPath);
}
Esempio n. 12
0
void SG_tid__generate2__suffix(SG_context * pCtx,
							   char * bufTid, SG_uint32 lenBuf,
							   SG_uint32 nrRandomDigits,
							   const char * pszSuffix)
{
	SG_uint32 lenSuffix;

	SG_NULLARGCHECK_RETURN( bufTid );
	SG_NONEMPTYCHECK_RETURN( pszSuffix );

	lenSuffix = SG_STRLEN(pszSuffix);
	SG_ARGCHECK_RETURN(  ((nrRandomDigits + 1 + lenSuffix + 1) <= lenBuf), lenBuf  );

	SG_ERR_CHECK_RETURN(  SG_tid__generate2(pCtx, bufTid, lenBuf, nrRandomDigits)  );
	SG_ERR_CHECK_RETURN(  SG_strcat(pCtx, bufTid, lenBuf, ".")  );
	SG_ERR_CHECK_RETURN(  SG_strcat(pCtx, bufTid, lenBuf, pszSuffix)  );
}
Esempio n. 13
0
void SG_time__format_local__i64_ns(SG_context* pCtx, SG_int64 iTime_ns,
								   char * pBuf, SG_uint32 lenBuf)
{
	SG_int64 iTime_ms  = iTime_ns / 1000000;
	SG_int32 remainder = (SG_int32)(iTime_ns % 1000000);
	SG_time tm;

	SG_NULLARGCHECK_RETURN( pBuf );
	SG_ARGCHECK_RETURN( (lenBuf >= SG_TIME_FORMAT_LENGTH+1), lenBuf );

	// convert milliseconds-since-epoch into individual fields.

	SG_ERR_CHECK_RETURN(  SG_time__decode__local(pCtx, iTime_ms, &tm)  );

	SG_ERR_CHECK_RETURN( SG_sprintf(pCtx, pBuf,lenBuf,"%04d/%02d/%02d %02d:%02d:%02d.%03d%06d %+03d%02d",
									tm.year,tm.month,tm.mday,
									tm.hour,tm.min,tm.sec,tm.msec,remainder,
									tm.offsetUTC/3600,abs((tm.offsetUTC/60)%60)) );
}
/**
 * Convert simple GID into a gid-domain repo-path.
 *
 */
void sg_wc_db__path__gid_to_gid_repopath(SG_context * pCtx,
										 const char * pszGid,
										 SG_string ** ppStringGidRepoPath)
{
	SG_string * pString = NULL;

	SG_NONEMPTYCHECK_RETURN( pszGid );
	SG_NULLARGCHECK_RETURN( ppStringGidRepoPath );

	SG_ARGCHECK_RETURN( ((pszGid[0] == 'g') || (pszGid[0] == 't')), pszGid );

	SG_ERR_CHECK(  SG_STRING__ALLOC(pCtx, &pString)  );
	SG_ERR_CHECK(  SG_string__sprintf(pCtx, pString, "@%s", pszGid)  );

	*ppStringGidRepoPath = pString;
	return;

fail:
	SG_STRING_NULLFREE(pCtx, pString);
}
Esempio n. 15
0
void sg_client__c__open(SG_context* pCtx,
						SG_client * pClient,
						const SG_vhash* pvh_credentials)
{
	sg_client_c_instance_data* pMe = NULL;

	SG_NULLARGCHECK_RETURN(pClient);
	SG_ARGCHECK_RETURN(pvh_credentials || pvh_credentials == NULL_CREDENTIAL, pvh_credentials);

	SG_ERR_CHECK(  SG_alloc1(pCtx, pMe)  );
	SG_ERR_CHECK(  SG_server__alloc(pCtx, &pMe->pServer)  );

	pClient->p_vtable_instance_data = (sg_client__vtable__instance_data *)pMe;

	return;

fail:
	if (pMe)
	{
		SG_ERR_IGNORE(  SG_server__free(pCtx, pMe->pServer)  );
		SG_NULLFREE(pCtx, pMe);
	}
}
Esempio n. 16
0
/**
 * Add to the fragball request vhash (see SG_server_prototypes.h for format).
 */
void SG_pull__add(
	SG_context* pCtx,
	SG_pull* pPull,
	SG_uint32 iDagnum,
	SG_rbtree* prbDagnodes,
	SG_rbtree* prbTags,
	SG_rbtree* prbDagnodePrefixes)
{
	_sg_pull* pMyPull = NULL;
	char bufDagnum[SG_DAGNUM__BUF_MAX__DEC];
	SG_bool found = SG_FALSE;
	SG_vhash* pvhDags = NULL; // Needs to be freed
	SG_vhash* pvhDagsRef = NULL; // Does not need to be freed, owned by parent vhash
	SG_vhash* pvhDagnum = NULL; // Needs to be freed
	SG_vhash* pvhDagnumRef = NULL; // Does not need to be freed, owned by parent vhash
	SG_rbtree_iterator* pit = NULL;

	SG_NULLARGCHECK_RETURN(pPull);
	SG_ARGCHECK_RETURN(iDagnum, iDagnum);
	pMyPull = (_sg_pull*)pPull;

	if (!pMyPull->pvhFragballRequest)
		SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pMyPull->pvhFragballRequest)  );

	SG_ERR_CHECK(  SG_dagnum__to_sz__decimal(pCtx, iDagnum, bufDagnum, sizeof(bufDagnum))  );

	/* Get dagnum vhash, adding it if necessary. */
	SG_ERR_CHECK(  SG_vhash__has(pCtx, pMyPull->pvhFragballRequest, SG_SYNC_STATUS_KEY__DAGS, &found)  );
	if (found)
		SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pMyPull->pvhFragballRequest, SG_SYNC_STATUS_KEY__DAGS, &pvhDagsRef)  );
	else
	{
		SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhDags)  );
		pvhDagsRef = pvhDags;
		SG_ERR_CHECK(  SG_vhash__add__vhash(pCtx, pMyPull->pvhFragballRequest, SG_SYNC_STATUS_KEY__DAGS, &pvhDags)  );
	}
	
	SG_ERR_CHECK(  SG_vhash__has(pCtx, pvhDagsRef, bufDagnum, &found)  );
	if (found)
		SG_ERR_CHECK(  SG_vhash__get__vhash(pCtx, pvhDagsRef, bufDagnum, &pvhDagnumRef)  );
	
	if (!pvhDagnumRef)
	{
		SG_ERR_CHECK(  SG_VHASH__ALLOC(pCtx, &pvhDagnum)  );
		pvhDagnumRef = pvhDagnum;
		SG_ERR_CHECK(  SG_vhash__add__vhash(pCtx, pvhDagsRef, bufDagnum, &pvhDagnum)  );
	}

	/* If dagnodes were provided, add them to the dagnum vhash */
	if (prbDagnodes)
	{
		const char* pszHid;
		SG_ERR_CHECK(  SG_rbtree__iterator__first(pCtx, &pit, prbDagnodes, &found, &pszHid, NULL)  );
		while (found)
		{
			SG_ERR_CHECK(  SG_vhash__update__null(pCtx, pvhDagnumRef, pszHid)  );
			SG_ERR_CHECK(  SG_rbtree__iterator__next(pCtx, pit, &found, &pszHid, NULL)  );
		}
		SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit);
	}	
	/* If tags were provided, add them to the dagnum vhash */
	if (prbTags)
	{
		const char* pszTag;
		SG_ERR_CHECK(  SG_rbtree__iterator__first(pCtx, &pit, prbTags, &found, &pszTag, NULL)  );
		while (found)
		{
			SG_ERR_CHECK(  SG_vhash__update__string__sz(pCtx, pvhDagnumRef, pszTag, SG_SYNC_REQUEST_VALUE_TAG)  );
			SG_ERR_CHECK(  SG_rbtree__iterator__next(pCtx, pit, &found, &pszTag, NULL)  );
		}
		SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit);
	}	
	/* If dagnode hid prefixes were provided, add them to the dagnum vhash */
	if (prbDagnodePrefixes)
	{
		const char* pszHidPrefix;
		SG_ERR_CHECK(  SG_rbtree__iterator__first(pCtx, &pit, prbDagnodePrefixes, &found, &pszHidPrefix, NULL)  );
		while (found)
		{
			SG_ERR_CHECK(  SG_vhash__update__string__sz(pCtx, pvhDagnumRef, pszHidPrefix, SG_SYNC_REQUEST_VALUE_HID_PREFIX)  );
			SG_ERR_CHECK(  SG_rbtree__iterator__next(pCtx, pit, &found, &pszHidPrefix, NULL)  );
		}
		SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit);
	}	

	return;

fail:
	SG_VHASH_NULLFREE(pCtx, pvhDagnum);
	SG_VHASH_NULLFREE(pCtx, pvhDags);
	SG_RBTREE_ITERATOR_NULLFREE(pCtx, pit);
}
void SG_unzip__locate_file(SG_context* pCtx, SG_unzip* s, const char* psz_filename, SG_bool* pb, SG_uint64* piLength)
{
    SG_bool b = SG_FALSE;

    /* We remember the 'current' position in the file so that we can jump
     * back there if we fail.
     */
    unz_file_info cur_file_infoSaved;
    unz_file_info_internal cur_file_info_internalSaved;
    SG_uint32 num_fileSaved;
    SG_uint32 pos_in_central_dirSaved;

    SG_NULLARGCHECK_RETURN( s );

	SG_ARGCHECK_RETURN((strlen(psz_filename) < UNZ_MAXFILENAMEINZIP), psz_filename);

    /* TODO hmmm.  why do we require the current file state to be "ok" here ? */
    if (!s->current_file_ok)
    {
        *pb = SG_FALSE;
        return;
    }

    /* Save the current state */
    num_fileSaved = s->num_file;
    pos_in_central_dirSaved = s->pos_in_central_dir;
    cur_file_infoSaved = s->cur_file_info;
    cur_file_info_internalSaved = s->cur_file_info_internal;

    SG_ERR_CHECK(  SG_unzip__goto_first_file(pCtx, s, &b, NULL, NULL)  );

    while (b)
    {
        if (strcmp(s->cur_file_name, psz_filename) == 0)
        {
            break;
        }

        SG_ERR_CHECK(  SG_unzip__goto_next_file(pCtx, s, &b, NULL, NULL)  );
    }

    if (b)
    {
        if (pb)
        {
            *pb = SG_TRUE;
        }
        if (piLength)
        {
            *piLength = s->cur_file_info.uncompressed_size;
        }
    }
    else
    {
        if (pb)
        {
            *pb = SG_FALSE;
            goto fail;
        }
        else
        {
            SG_ERR_THROW(  SG_ERR_NOT_FOUND  );
        }
    }

    return;

fail:
    /* We failed, so restore the state of the 'current file' to where we
     * were.
     */
    s->num_file = num_fileSaved ;
    s->pos_in_central_dir = pos_in_central_dirSaved ;
    s->cur_file_info = cur_file_infoSaved;
    s->cur_file_info_internal = cur_file_info_internalSaved;

}