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; }
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)) ); }
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; }
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); }
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; }
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]; }
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); }
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) ); }
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); }
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); } }
/** * 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; }