Commit 4d56b86a authored by Steffen Möller's avatar Steffen Möller

New upstream version 2.9.3+dfsg

parent aac8545b
# NCBI External Developer Release:
## SRA Toolkit 2.9.3
**October 17, 2018**
**kns**: added possibility to skip server's certificate validation
**kns**: expect to receive HTTP status 200 when sending range-request that includes the whole file
**vdb, vdb-copy**: fixed a bug in accessing pagemap process request for cursors which do not have pagemap thread running
## SRA Toolkit 2.9.2-2
**September 26, 2018**
**read-filter-redact**: Fixed to update HISTORY metadata
## SRA Toolkit 2.9.2
**July 23, 2018**
**kfg, vfs**: Introduced enhanced handling of download-only NGC files that lack read/decrypt permissions
......
......@@ -23,4 +23,4 @@
# ===========================================================================
# SRA-TOOLS and library version
VERSION = 2.9.2
VERSION = 2.9.3
#define TOOLKIT_VERS 0x02090002
#define TOOLKIT_VERS 0x02090003
......@@ -35,6 +35,7 @@ include $(TOP)/build/Makefile.config
# default
#
SUBDIRS = \
read-filter-redact \
prefetch \
dump-test \
vdb-config \
......
......@@ -36,8 +36,8 @@ KMER=$(SRA)/traces/nannot01/kmer/000/390/GCA_000390265.1_R
KMERF=$(SRAF):data/sracloud/traces/nannot01/kmer/000/390/GCA_000390265.1_R
REFSEQ=$(SRA)/traces/refseq/KC702174.1
REFSEQF=$(SRAF):data/sracloud/traces/refseq/KC702174.1
SRR=$(SRA)/traces/sra6/SRR/000052/SRR053325
SRRF=$(SRAF):data/sracloud/traces/sra6/SRR/000052/SRR053325
SRR=$(SRA)/traces/sra59/SRR/000052/SRR053325
SRRF=$(SRAF):data/sracloud/traces/sra59/SRR/000052/SRR053325
WGS=$(SRA)/traces/wgs03/WGS/AF/VF/AFVF01.1
WGSF=$(SRAF):data/sracloud/traces/wgs03/WGS/AF/VF/AFVF01.1
......@@ -205,10 +205,13 @@ urls_and_accs:
@rm `pwd`/tmp/sra/SRR053325.sra
@echo SRR FASP download when user repository is configured
@if ascp -h > /dev/null ; then \
if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
$(BINDIR)/prefetch $(SRRF) > /dev/null ; \
ls `pwd`/tmp/sra/SRR053325.sra > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of $(SRRF) when ascp is not found is disabled ; \
fi
# @echo
......@@ -216,9 +219,12 @@ urls_and_accs:
@rm `pwd`/tmp/refseq/KC702174.1
@echo REFSEQ download when user repository is configured
@if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
$(BINDIR)/prefetch $(REFSEQF) > /dev/null ; \
ls `pwd`/tmp/refseq/KC702174.1 > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of $(REFSEQF) when ascp is not found is disabled ; \
fi
# @echo
......@@ -226,9 +232,12 @@ urls_and_accs:
@rm `pwd`/tmp/nannot/GCA_000390265.1_R
@echo NANNOT FASP download when user repository is configured
@if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
$(BINDIR)/prefetch $(KMERF) > /dev/null ; \
ls `pwd`/tmp/nannot/GCA_000390265.1_R > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of $(KMERF) when ascp is not found is disabled ; \
fi
# @echo
......@@ -238,10 +247,13 @@ urls_and_accs:
@if ls `pwd`/tmp/wgs/AFVF01 2> /dev/null ; \
then echo AFVF01 found ; exit 1; fi
@if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
$(BINDIR)/prefetch $(WGSF) > /dev/null ; \
echo INCORRECT DOWNLOAD OF WGS SHOULD BE FIXED: VDB-3537 ; \
ls `pwd`/tmp/wgs/AFVF01 > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of $(WGSF) when ascp is not found is disabled ; \
fi
@echo
......@@ -295,9 +307,12 @@ urls_and_accs:
@if ls `pwd`/tmp2/100MB 2> /dev/null ; \
then echo 100MB found ; exit 1; fi
@if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; cd tmp2 ; \
$(BINDIR)/prefetch $(LARGE) > /dev/null ; \
ls `pwd`/100MB > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else \
if $(BINDIR)/prefetch $(LARGE) 2> /dev/null ; \
then echo "prefetch <FASP URL> when ascp is not found should fail" ; \
......@@ -310,9 +325,12 @@ urls_and_accs:
@if ls `pwd`/tmp2/1GB 2> /dev/null; \
then echo 1GB found ; exit 1; fi
@if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; cd tmp2 ; \
$(BINDIR)/prefetch fasp://anonftp@ftp.ncbi.nlm.nih.gov:1GB > /dev/null;\
ls `pwd`/1GB > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
fi
# @echo
......@@ -358,11 +376,14 @@ out_dir_and_file:
# @ echo
@ if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
echo PREFETCH SRR FASP URL TO OUT-FILE ; \
rm -f `pwd`/tmp3/dir/file ; \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
$(BINDIR)/prefetch $(SRRF) -O / -o tmp3/dir/file > /dev/null ; \
ls `pwd`/tmp3/dir/file > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of SRR FASP URL when ascp is not found is disabled ; \
fi
# @ echo
......@@ -384,11 +405,14 @@ out_dir_and_file:
# @ echo
@ if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
echo PREFETCH FASP URL TO OUT-FILE ; \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
rm -f `pwd`/tmp3/dir/file ; \
$(BINDIR)/prefetch $(LARGE) -O / -o tmp3/dir/file > /dev/null ; \
ls `pwd`/tmp3/dir/file > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of SRR FASP URL when ascp is not found is disabled ; \
fi
# @ echo
......@@ -424,11 +448,14 @@ out_dir_and_file:
# @ echo
@ if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
echo PREFETCH SRR FASP URL ; \
rm `pwd`/tmp/sra/SRR053325.sra ; \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
$(BINDIR)/prefetch $(SRRF) > /dev/null ; \
ls `pwd`/tmp/sra/SRR053325.sra > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of SRR FASP URL when ascp is not found is disabled ; \
fi
# @ echo
......@@ -449,11 +476,14 @@ out_dir_and_file:
# @ echo
@ if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
echo PREFETCH FASP URL ; \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; cd tmp2 ; \
if ls `pwd`/100MB 2> /dev/null ; then exit 1; fi ; \
$(BINDIR)/prefetch $(LARGE) > /dev/null ; \
ls `pwd`/100MB > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of SRR FASP URL when ascp is not found is disabled ; \
fi
# @ echo
......@@ -473,11 +503,14 @@ out_dir_and_file:
# @ echo
@ if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
echo PREFETCH SRR FASP URL TO OUT-DIR ; \
rm -f `pwd`/tmp3/dir/SRR053325.sra ; \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
$(BINDIR)/prefetch $(SRRF) -O tmp3/dir > /dev/null ; \
ls `pwd`/tmp3/dir/SRR053325.sra > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of SRR FASP URL when ascp is not found is disabled ; \
fi
# @ echo
......@@ -500,12 +533,15 @@ out_dir_and_file:
# @ echo
@ if ascp -h > /dev/null ; then \
if [ `hostname` != 'iebdev21' ] ; then \
echo PREFETCH FASP URL TO OUT-DIR ; \
export VDB_CONFIG=`pwd`/tmp; export NCBI_SETTINGS=/ ; \
if ls `pwd`/tmp3/dir/100MB 2> /dev/null ; then \
echo 100MB found ; exit 1 ; fi ; \
$(BINDIR)/prefetch $(LARGE) -O tmp3/dir > /dev/null ; \
ls `pwd`/tmp3/dir/100MB > /dev/null ; \
else echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD HOST; echo BAD;\
fi \
else echo download of SRR FASP URL when ascp is not found is disabled ; \
fi
# @ echo
......
# ===========================================================================
#
# PUBLIC DOMAIN NOTICE
# National Center for Biotechnology Information
#
# This software/database is a "United States Government Work" under the
# terms of the United States Copyright Act. It was written as part of
# the author's official duties as a United States Government employee and
# thus cannot be copyrighted. This software/database is freely available
# to the public for use. The National Library of Medicine and the U.S.
# Government have not placed any restriction on its use or reproduction.
#
# Although all reasonable efforts have been taken to ensure the accuracy
# and reliability of the software and data, the NLM and the U.S.
# Government do not and cannot warrant the performance or results that
# may be obtained by using this software or data. The NLM and the U.S.
# Government disclaim all warranties, express or implied, including
# warranties of performance, merchantability or fitness for any particular
# purpose.
#
# Please cite the author in any work or product based on this material.
#
# ==============================================================================
default: runtests
TOP ?= $(abspath ../..)
include $(TOP)/build/Makefile.env
runtests: check_version
RUN=tmp-read-filter-redact-test-run
FLT=tmp-read-filter-redact-test-in
check_version: #/align-cache/CSRA_file
# remove old test files
@ if $(BINDIR)/vdb-unlock $(RUN) 2>/dev/null ; then echo ; fi
@ rm -fr tmp-read-filter-redact-test-*
# prepare sources
@ echo 1 > $(FLT)
@ $(BINDIR)/kar --extract ../align-cache/CSRA_file \
--directory $(RUN)
# make sure HISTORY meta does not exist
@ if $(BINDIR)/kdbmeta tmp-read-filter-redact-test-run \
-TSEQUENCE HISTORY 2>/dev/null; \
then echo "error: HISTORY found in source metadata"; exit 1; fi
# read-filter-redact
@ $(BINDIR)/read-filter-redact -F$(FLT) $(RUN)
# make sure HISTORY meta was created
@ $(BINDIR)/kdbmeta tmp-read-filter-redact-test-run -TSEQUENCE HISTORY | \
grep '^ <EVENT_1 build="' | grep '" run="' | \
grep '" tool="read-filter-redact" vers="'
# remove old test files
@ $(BINDIR)/vdb-unlock $(RUN)
@ rm -fr tmp-read-filter-redact-test-*
<Run accession="SRR053325" read_length="fixed" spot_count="1" base_count="270" base_count_bio="237" spot_count_mates="0" base_count_bio_mates="0" spot_count_bad="0" base_count_bio_bad="0" spot_count_filtered="0" base_count_bio_filtered="0">
<Member member_name="AVP24_435_36" spot_count="1" base_count="270" base_count_bio="237" spot_count_mates="0" base_count_bio_mates="0" spot_count_bad="0" base_count_bio_bad="0" spot_count_filtered="0" base_count_bio_filtered="0"/>
<Size value="10800" units="bytes"/>
<Size value="31401" units="bytes"/>
<Bases cs_native="false" count="237">
<Base value="A" count="42"/>
<Base value="C" count="81"/>
......@@ -8,4 +8,15 @@
<Base value="T" count="56"/>
<Base value="N" count="0"/>
</Bases>
<QualityCount>
<Quality value="25" count="2"/>
<Quality value="29" count="4"/>
<Quality value="31" count="12"/>
<Quality value="32" count="7"/>
<Quality value="33" count="2"/>
<Quality value="36" count="5"/>
<Quality value="37" count="193"/>
<Quality value="38" count="13"/>
<Quality value="40" count="32"/>
</QualityCount>
</Run>
......@@ -35,15 +35,20 @@
#include <vdb/schema.h> /* VSchemaRelease */
#include <kdb/manager.h> /* KDBPathType */
#include <kdb/meta.h> /* KMetadataRelease */
#include <kdb/namelist.h> /* KMDataNodeListChild */
#include <kfs/directory.h> /* KDirectory */
#include <kfs/file.h> /* KFile */
#include <klib/debug.h> /* DBGMSG */
#include <klib/log.h> /* LOGERR */
#include <klib/out.h> /* OUTMSG */
#include <klib/status.h> /* STSMSG */
#include <klib/debug.h> /* DBGMSG */
#include <klib/printf.h> /* string_printf */
#include <klib/rc.h> /* RC */
#include <klib/status.h> /* STSMSG */
#include <klib/time.h> /* KTimeLocal */
#include <os-native.h>
#include <assert.h>
......@@ -57,8 +62,8 @@
static KDirectory* __SpotIteratorDirectory = NULL;
typedef struct CmdLine {
const char* table;
const char* file;
const char* table;
const char* file;
} CmdLine;
typedef struct SpotIterator {
spotid_t crnSpotId;
......@@ -91,6 +96,8 @@ typedef struct Db {
VCursor *wCursor;
uint32_t wIdx;
KMetadata *meta;
bool locked;
spotid_t nSpots;
......@@ -505,19 +512,27 @@ static rc_t DbInit(rc_t rc, const CmdLine* args, Db* db)
db->locked = true; /* has to be locked in production mode */
rc = VDBManagerOpenTableUpdate (db->mgr, &db->tbl, NULL, args->table);
if (rc != 0) {
VDatabase *vdb;
rc_t rc2 = VDBManagerOpenDBUpdate ( db->mgr, &vdb, NULL , args->table );
if( rc2 == 0) {
rc2 = VDatabaseOpenTableUpdate ( vdb, &db->tbl, "SEQUENCE" );
if (rc2 == 0 ) rc = 0;
VDatabaseRelease ( vdb );
}
}
if(rc != 0){
VDatabase *vdb;
rc_t rc2 = VDBManagerOpenDBUpdate ( db->mgr, &vdb, NULL,
args->table );
if( rc2 == 0) {
rc2 = VDatabaseOpenTableUpdate ( vdb, &db->tbl, "SEQUENCE" );
if (rc2 == 0 )
rc = 0;
VDatabaseRelease ( vdb );
}
}
if(rc != 0){
PLOGERR(klogErr, (klogErr, rc,
"while opening VTable '$(table)'", "table=%s", args->table));
}
}
}
if( rc == 0) {
rc = VTableOpenMetadataUpdate ( db->tbl, & db->meta );
DISP_RC(rc, "while Opening Metadata");
}
if( rc == 0) {
rc = VTableCreateCursorRead(db->tbl, &db->rCursor);
DISP_RC(rc, "while creating read cursor");
......@@ -558,6 +573,13 @@ static rc_t DbDestroy(Db* db)
assert(db);
{
rc_t rc2 = KMetadataRelease(db->meta);
db->meta = NULL;
if (rc == 0)
{ rc = rc2; }
}
{
rc_t rc2 = VCursorRelease(db->rCursor);
db->rCursor = NULL;
......@@ -623,7 +645,7 @@ static rc_t Work(Db* db, SpotIterator* it)
rc = VCursorReadDirect(db->rCursor, row_id, db->rFilterIdx,
elem_bits, bufferIn, sizeof bufferIn, &row_len);
DISP_RC(rc, "while reading READ_FILTER");
nreads = row_len;
nreads = row_len;
}
if (toRedact) {
buffer = filter;
......@@ -664,6 +686,168 @@ static rc_t Work(Db* db, SpotIterator* it)
return rc;
}
static uint32_t get_child_count( KMDataNode *node )
{
uint32_t res = 0;
KNamelist *names;
rc_t rc = KMDataNodeListChild ( node, &names );
DISP_RC( rc, "get_child_count:KMDataNodeListChild() failed" );
if ( rc == 0 )
{
rc = KNamelistCount ( names, &res );
DISP_RC( rc, "get_child_count:KNamelistCount() failed" );
KNamelistRelease ( names );
}
return res;
}
static rc_t fill_timestring( char * s, size_t size )
{
KTime tr;
rc_t rc;
KTimeLocal ( &tr, KTimeStamp() );
rc = string_printf ( s, size, NULL, "%lT", &tr );
DISP_RC( rc, "fill_timestring:string_printf( date/time ) failed" );
return rc;
}
static rc_t enter_time( KMDataNode *node, const char * key )
{
char timestring[ 160 ];
rc_t rc = fill_timestring( timestring, sizeof timestring );
if ( rc == 0 )
{
rc = KMDataNodeWriteAttr ( node, key, timestring );
DISP_RC( rc, "enter_time:KMDataNodeWriteAttr( timestring ) failed" );
}
return rc;
}
static rc_t enter_version( KMDataNode *node, const char * key )
{
char buff[ 32 ];
rc_t rc;
rc = string_printf ( buff, sizeof( buff ), NULL, "%.3V", KAppVersion() );
assert ( rc == 0 );
rc = KMDataNodeWriteAttr ( node, key, buff );
DISP_RC( rc, "enter_version:KMDataNodeWriteAttr() failed" );
return rc;
}
static rc_t enter_date_name_vers( KMDataNode *node )
{
rc_t rc = enter_time( node, "run" );
DISP_RC( rc, "enter_date_name_vers:enter_time() failed" );
if ( rc == 0 )
{
rc = KMDataNodeWriteAttr ( node, "tool", "read-filter-redact" );
DISP_RC( rc, "enter_date_name_vers:"
"KMDataNodeWriteAttr(tool=read-filter-redact) failed" );
if ( rc == 0 )
{
rc = enter_version ( node, "vers" );
DISP_RC( rc, "enter_date_name_vers:enter_version() failed" );
if ( rc == 0 )
{
rc = KMDataNodeWriteAttr ( node, "build", __DATE__ );
DISP_RC( rc, "enter_date_name_vers:KMDataNodeWriteAttr"
"(build=_DATE_) failed" );
}
}
}
return rc;
}
static rc_t update_history ( KMetadata *dst_meta ) {
rc_t rc = 0;
rc_t r2 = 0;
char event_name[ 32 ] = "";
KMDataNode *hist_node = NULL;
rc = KMetadataOpenNodeUpdate ( dst_meta, &hist_node, "HISTORY" );
DISP_RC(rc, "while Opening HISTORY Metadata");
if ( rc == 0 ) {
uint32_t index = get_child_count( hist_node );
if ( index > 0 ) { /* make sure EVENT_[i-1] exists */
const KMDataNode *node = NULL;
rc_t r = KMDataNodeOpenNodeRead ( hist_node, & node, "EVENT_%u",
index );
if ( r != 0 )
PLOGERR(klogErr, (klogErr, r,
"while opening metanode HISTORY/EVENT_'$(n)'",
"n=%u", index));
else
KMDataNodeRelease ( node );
}
++ index;
{ /* make sure EVENT_[i] does not exist */
const KMDataNode *node = NULL;
rc_t r = KMDataNodeOpenNodeRead ( hist_node, & node, "EVENT_%u",
index );
if ( r == 0 ) {
uint32_t mx = index * 2;
KMDataNodeRelease ( node );
r = RC ( rcExe, rcMetadata, rcReading, rcNode, rcExists );
PLOGERR(klogErr, (klogErr, r,
"while opening metanode HISTORY/EVENT_'$(n)'",
"n=%u", index));
r = 0;
for ( ; index < mx; ++ index ) {
r = KMDataNodeOpenNodeRead ( hist_node, & node, "EVENT_%u",
index);
if ( r == 0 ) {
KMDataNodeRelease ( node );
r = RC ( rcExe, rcMetadata, rcReading,
rcNode, rcExists );
PLOGERR(klogErr, (klogErr, r,
"while opening metanode HISTORY/EVENT_'$(n)'",
"n=%u", index));
r = 0;
}
else
break;
}
if ( r == 0 ) {
rc = RC ( rcExe, rcMetadata, rcReading, rcNode, rcExists );
LOGERR( klogErr, rc,
"cannot find next event metanode in HISTORY" );
}
}
else
KMDataNodeRelease ( node );
}
rc = string_printf ( event_name, sizeof( event_name ), NULL, "EVENT_%u",
index );
DISP_RC( rc, "update_history:string_printf(EVENT_NR) failed" );
if ( rc == 0 )
{
KMDataNode *event_node;
rc = KMDataNodeOpenNodeUpdate ( hist_node, &event_node,
event_name );
DISP_RC( rc,
"update_history:KMDataNodeOpenNodeUpdate('EVENT_NR') failed" );
if ( rc == 0 )
{
rc = enter_date_name_vers( event_node );
KMDataNodeRelease ( event_node );
}
}
}
r2 = KMDataNodeRelease ( hist_node );
if ( r2 != 0 && rc == 0 )
rc = r2;
return rc;
}
static rc_t Run(const CmdLine* args)
{
rc_t rc = 0;
......@@ -698,6 +882,9 @@ static rc_t Run(const CmdLine* args)
rc = Work(&db, &it);
}
if (rc == 0)
rc = update_history(db.meta);
if (rc == 0) {
PLOGMSG(klogInfo, (klogInfo,
"Success: redacted $(redacted) spots out of $(all)",
......@@ -745,7 +932,7 @@ rc_t CC Usage(const Args* args)
rc_t rc;
if (args == NULL)
rc = RC (rcApp, rcArgv, rcAccessing, rcSelf, rcNull);
rc = RC (rcExe, rcArgv, rcAccessing, rcSelf, rcNull);
else
rc = ArgsProgram (args, &fullpath, &progname);
if (rc)
......@@ -852,7 +1039,7 @@ rc_t CC KMain(int argc, char* argv[])
ArgsWhack(args);
if (rc == RC(rcVDB, rcTable, rcOpening, rcSchema, rcNotFound))
if (rc == SILENT_RC(rcVDB, rcTable, rcOpening, rcSchema, rcNotFound))
{ exit(10); }
return rc;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment