1 /*  $Id: blast_formatter.cpp 616448 2020-09-16 13:25:04Z fongah2 $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Christiam Camacho
27  *
28  */
29 
30 /** @file blast_formatter.cpp
31  * Stand-alone command line formatter for BLAST.
32  */
33 
34 #include <ncbi_pch.hpp>
35 #include <corelib/ncbiapp.hpp>
36 #include <corelib/ncbistre.hpp>
37 #include <serial/iterator.hpp>
38 #include <algo/blast/api/version.hpp>
39 #include <algo/blast/api/remote_blast.hpp>
40 #include <algo/blast/blastinput/blast_input_aux.hpp>
41 #include <algo/blast/format/blast_format.hpp>
42 #include <algo/blast/api/objmgr_query_data.hpp>
43 #include <objtools/data_loaders/blastdb/bdbloader_rmt.hpp>
44 #include <objtools/data_loaders/genbank/gbloader.hpp>
45 #include <objtools/data_loaders/genbank/id2/reader_id2.hpp>
46 #include "blast_app_util.hpp"
47 
48 
49 #ifndef SKIP_DOXYGEN_PROCESSING
50 USING_NCBI_SCOPE;
51 USING_SCOPE(blast);
52 #endif
53 
54 /// The application class
55 class CBlastFormatterApp : public CNcbiApplication
56 {
57 public:
58     /** @inheritDoc */
CBlastFormatterApp()59     CBlastFormatterApp() {
60         CRef<CVersion> version(new CVersion());
61         version->SetVersionInfo(new CBlastVersion());
62         SetFullVersion(version);
63         m_LoadFromArchive = false;
64         m_StopWatch.Start();
65         if (m_UsageReport.IsEnabled()) {
66         	m_UsageReport.AddParam(CBlastUsageReport::eVersion, GetVersion().Print());
67         	m_UsageReport.AddParam(CBlastUsageReport::eProgram, (string) "blast_formatter");
68         }
69     }
70 
~CBlastFormatterApp()71     ~CBlastFormatterApp() {
72     	m_UsageReport.AddParam(CBlastUsageReport::eRunTime, m_StopWatch.Elapsed());
73     }
74 
75 private:
76     /** @inheritDoc */
77     virtual void Init();
78     /** @inheritDoc */
79     virtual int Run();
80 
81     /// Prints the BLAST formatted output
82     int PrintFormattedOutput(void);
83 
84     /// Extracts the queries to be formatted
85     /// @param query_is_protein Are the queries protein sequences? [in]
86     CRef<CBlastQueryVector> x_ExtractQueries(bool query_is_protein);
87 
88     /// Build the query from a PSSM
89     /// @param pssm PSSM to inspect [in]
90     CRef<CBlastSearchQuery>
91     x_BuildQueryFromPssm(const CPssmWithParameters& pssm);
92 
93     /// Package a scope and Seq-loc into a SSeqLoc from a Bioseq
94     /// @param bioseq Bioseq to inspect [in]
95     /// @param scope Scope object to add the sequence data to [in|out]
96     SSeqLoc x_QueryBioseqToSSeqLoc(const CBioseq& bioseq, CRef<CScope> scope);
97 
98     void x_AddCmdOptions();
99 
100     /// Our link to the NCBI BLAST service
101     CRef<CRemoteBlast> m_RmtBlast;
102 
103     /// The source of CScope objects for queries
104     CRef<CBlastScopeSource> m_QueryScopeSource;
105 
106     /// Tracks whether results come from an archive file.
107     bool m_LoadFromArchive;
108     CBlastUsageReport m_UsageReport;
109     CStopWatch m_StopWatch;
110 };
111 
Init()112 void CBlastFormatterApp::Init()
113 {
114     HideStdArgs(fHideLogfile | fHideConffile | fHideFullVersion | fHideXmlHelp | fHideDryRun);
115 
116     auto_ptr<CArgDescriptions> arg_desc(new CArgDescriptions);
117 
118     arg_desc->SetUsageContext(GetArguments().GetProgramBasename(),
119                   "Stand-alone BLAST formatter client, version "
120                   + CBlastVersion().Print());
121 
122     arg_desc->SetCurrentGroup("Input options");
123     arg_desc->AddOptionalKey(kArgRid, "BLAST_RID", "BLAST Request ID (RID)",
124                      CArgDescriptions::eString);
125 
126     // add input file for seq-align here?
127     arg_desc->AddOptionalKey(kArgArchive, "ArchiveFile", "File containing BLAST Archive format in ASN.1 (i.e.: output format 11)",
128                      CArgDescriptions::eInputFile);
129     arg_desc->SetDependency(kArgRid, CArgDescriptions::eExcludes, kArgArchive);
130 
131     CFormattingArgs fmt_args(false, CFormattingArgs::eIsSAM);
132     fmt_args.SetArgumentDescriptions(*arg_desc);
133 
134     arg_desc->SetCurrentGroup("Output configuration options");
135     arg_desc->AddDefaultKey(kArgOutput, "output_file", "Output file name",
136                             CArgDescriptions::eOutputFile, "-");
137 
138     arg_desc->SetCurrentGroup("Miscellaneous options");
139     arg_desc->AddFlag(kArgParseDeflines,
140                  "Should the query and subject defline(s) be parsed?", true);
141     arg_desc->SetCurrentGroup("");
142 
143     CDebugArgs debug_args;
144     debug_args.SetArgumentDescriptions(*arg_desc);
145 
146     SetupArgDescriptions(arg_desc.release());
147 }
148 
149 SSeqLoc
x_QueryBioseqToSSeqLoc(const CBioseq & bioseq,CRef<CScope> scope)150 CBlastFormatterApp::x_QueryBioseqToSSeqLoc(const CBioseq& bioseq,
151                                            CRef<CScope> scope)
152 {
153     static bool first_time = true;
154     _ASSERT(scope);
155 
156     if ( !HasRawSequenceData(bioseq) && first_time ) {
157         _ASSERT(m_QueryScopeSource);
158         m_QueryScopeSource->AddDataLoaders(scope);
159         first_time = false;
160     }
161     else {
162         scope->AddBioseq(bioseq);
163     }
164     CRef<CSeq_loc> seqloc(new CSeq_loc);
165     seqloc->SetWhole().Assign(*bioseq.GetFirstId());
166     return SSeqLoc(seqloc, scope);
167 }
168 
169 CRef<CBlastSearchQuery>
x_BuildQueryFromPssm(const CPssmWithParameters & pssm)170 CBlastFormatterApp::x_BuildQueryFromPssm(const CPssmWithParameters& pssm)
171 {
172     if ( !pssm.HasQuery() ) {
173         throw runtime_error("PSSM has no query");
174     }
175     CRef<CScope> scope(new CScope(*CObjectManager::GetInstance()));
176     const CSeq_entry& seq_entry = pssm.GetQuery();
177     if ( !seq_entry.IsSeq() ) {
178         throw runtime_error("Cannot have multiple queries in a PSSM");
179     }
180     SSeqLoc ssl = x_QueryBioseqToSSeqLoc(seq_entry.GetSeq(), scope);
181     CRef<CBlastSearchQuery> retval;
182     retval.Reset(new CBlastSearchQuery(*ssl.seqloc, *ssl.scope));
183     _ASSERT(ssl.scope.GetPointer() == scope.GetPointer());
184     return retval;
185 }
186 
187 CRef<CBlastQueryVector>
x_ExtractQueries(bool query_is_protein)188 CBlastFormatterApp::x_ExtractQueries(bool query_is_protein)
189 {
190     CRef<CBlast4_queries> b4_queries = m_RmtBlast->GetQueries();
191     _ASSERT(b4_queries);
192     const size_t kNumQueries = b4_queries->GetNumQueries();
193 
194     CRef<CBlastQueryVector> retval(new CBlastQueryVector);
195 
196     SDataLoaderConfig dlconfig(query_is_protein, SDataLoaderConfig::eUseNoDataLoaders);
197     dlconfig.OptimizeForWholeLargeSequenceRetrieval(false);
198     m_QueryScopeSource.Reset(new CBlastScopeSource(dlconfig));
199 
200     if (b4_queries->IsPssm()) {
201         retval->AddQuery(x_BuildQueryFromPssm(b4_queries->GetPssm()));
202     } else if (b4_queries->IsSeq_loc_list()) {
203         CRef<CScope> scope = m_QueryScopeSource->NewScope();
204         ITERATE(CBlast4_queries::TSeq_loc_list, seqloc,
205                 b4_queries->GetSeq_loc_list()) {
206             _ASSERT( !(*seqloc)->GetId()->IsLocal() );
207             CRef<CBlastSearchQuery> query(new CBlastSearchQuery(**seqloc,
208                                                                 *scope));
209             retval->AddQuery(query);
210         }
211     } else if (b4_queries->IsBioseq_set()) {
212         CTypeConstIterator<CBioseq> itr(ConstBegin(b4_queries->GetBioseq_set(),
213                                                    eDetectLoops));
214         CRef<CScope> scope(new CScope(*CObjectManager::GetInstance()));
215         for (; itr; ++itr) {
216             SSeqLoc ssl = x_QueryBioseqToSSeqLoc(*itr, scope);
217             CRef<CBlastSearchQuery> query(new CBlastSearchQuery(*ssl.seqloc,
218                                                                 *ssl.scope));
219             retval->AddQuery(query);
220         }
221     }
222 
223     (void)kNumQueries;  // eliminate compiler warning;
224     _ASSERT(kNumQueries == retval->size());
225     return retval;
226 }
227 
228 /// Extracts the subject sequence data from remote_blast into a TSeqLocVector.
229 /// All subjects are added to/use the same CScope object
230 /// @param remote_blast Source of subject sequences
231 static TSeqLocVector
s_ConvertSubjects2TSeqLocVector(CRef<CRemoteBlast> remote_blast)232 s_ConvertSubjects2TSeqLocVector(CRef<CRemoteBlast> remote_blast)
233 {
234     TSeqLocVector retval;
235     CRef<CScope> subj_scope(new CScope(*CObjectManager::GetInstance()));
236     if (remote_blast->GetSubjectSeqLocs().empty()) {
237         const list<CRef<CBioseq> > subjects =
238             remote_blast->GetSubjectSequences();
239         ITERATE(list<CRef<CBioseq> >, bioseq, subjects) {
240             subj_scope->AddBioseq(**bioseq);
241             CRef<CSeq_id> seqid = FindBestChoice((*bioseq)->GetId(),
242                                                  CSeq_id::BestRank);
243             const TSeqPos length = (*bioseq)->GetInst().GetLength();
244             CRef<CSeq_loc> sl(new CSeq_loc(*seqid, 0, length-1));
245             retval.push_back(SSeqLoc(sl, subj_scope));
246         }
247     } else {
248         const CBlast4_subject::TSeq_loc_list seqlocs =
249             remote_blast->GetSubjectSeqLocs();
250         ITERATE(CBlast4_subject::TSeq_loc_list, sl, seqlocs) {
251             retval.push_back(SSeqLoc(*sl, subj_scope));
252         }
253     }
254     return retval;
255 }
256 
257 bool
s_InitializeSubject(CRef<blast::CBlastDatabaseArgs> db_args,CRef<blast::CBlastOptionsHandle> opts_hndl,CRef<blast::CLocalDbAdapter> & db_adapter,CRef<objects::CScope> & scope)258 s_InitializeSubject(CRef<blast::CBlastDatabaseArgs> db_args,
259                   CRef<blast::CBlastOptionsHandle> opts_hndl,
260                   CRef<blast::CLocalDbAdapter>& db_adapter,
261                   CRef<objects::CScope>& scope)
262 {
263 	bool isRemote = false;
264     db_adapter.Reset();
265 
266     _ASSERT(db_args.NotEmpty());
267     CRef<CSearchDatabase> search_db = db_args->GetSearchDatabase();
268 
269     if (scope.Empty()) {
270 	   scope.Reset(new CScope(*CObjectManager::GetInstance()));
271     }
272 
273     CRef<IQueryFactory> subjects;
274     if ( (subjects = db_args->GetSubjects(scope)) ) {
275         _ASSERT(search_db.Empty());
276         char* bl2seq_legacy = getenv("BL2SEQ_LEGACY");
277         if (bl2seq_legacy) {
278         	db_adapter.Reset(new CLocalDbAdapter(subjects, opts_hndl, false));
279         }
280         else {
281             db_adapter.Reset(new CLocalDbAdapter(subjects, opts_hndl, true));
282         }
283     } else {
284         _ASSERT(search_db.NotEmpty());
285         try {
286             // Try to open the BLAST database even for remote searches, as if
287             // it is available locally, it will be better to fetch the
288             // sequence data for formatting from this (local) source
289             CRef<CSeqDB> seqdb = search_db->GetSeqDb();
290             db_adapter.Reset(new CLocalDbAdapter(*search_db));
291             scope->AddDataLoader(RegisterOMDataLoader(seqdb), CBlastDatabaseArgs::kSubjectsDataLoaderPriority);
292             LOG_POST(Info <<"Add local loader " << search_db->GetDatabaseName());
293         } catch (const CSeqDBException&) {
294            	SetDiagPostLevel(eDiag_Critical);
295             string remote_loader = kEmptyStr;
296             try {
297             db_adapter.Reset(new CLocalDbAdapter(*search_db));
298             remote_loader = CRemoteBlastDbDataLoader::RegisterInObjectManager
299                                     (*( CObjectManager::GetInstance()),
300                                     search_db->GetDatabaseName(),
301                                     search_db->IsProtein()  ? CBlastDbDataLoader::eProtein : CBlastDbDataLoader::eNucleotide,
302                                     true, CObjectManager::eDefault, CBlastDatabaseArgs::kSubjectsDataLoaderPriority)
303                                     .GetLoader()->GetName();
304             scope->AddDataLoader(remote_loader, CBlastDatabaseArgs::kSubjectsDataLoaderPriority);
305             SetDiagPostLevel(eDiag_Warning);
306             isRemote = true;
307             LOG_POST(Info <<"Remote " << search_db->GetDatabaseName());
308             }
309             catch (CException & e) {
310             	SetDiagPostLevel(eDiag_Warning);
311             	NCBI_THROW(CException, eUnknown, "Fail to initialize local or remote DB" );
312             }
313         }
314     }
315     try {
316     	const int kGenbankLoaderPriority = 99;
317         CRef<CReader> reader(new CId2Reader);
318         reader->SetPreopenConnection(false);
319         string genbank_loader = CGBDataLoader::RegisterInObjectManager
320             (*( CObjectManager::GetInstance()), reader,CObjectManager::eNonDefault).GetLoader()->GetName();
321         scope->AddDataLoader(genbank_loader, kGenbankLoaderPriority);
322     } catch (const CException& e) {
323     	LOG_POST(Info << "Failed to add genbank dataloader");
324     	// It's ok not to have genbank loader
325     }
326     return isRemote;
327 }
328 
PrintFormattedOutput(void)329 int CBlastFormatterApp::PrintFormattedOutput(void)
330 {
331     int retval = 0;
332     const CArgs& args = GetArgs();
333     const string& kRid = args[kArgRid].HasValue()
334         ? args[kArgRid].AsString() : kEmptyStr;
335     CNcbiOstream& out = args[kArgOutput].AsOutputFile();
336     CFormattingArgs fmt_args(false, CFormattingArgs::eIsSAM) ;
337 
338     CRef<CBlastOptionsHandle> opts_handle = m_RmtBlast->GetSearchOptions();
339     CBlastOptions& opts = opts_handle->SetOptions();
340     fmt_args.ExtractAlgorithmOptions(args, opts);
341     {{
342         CDebugArgs debug_args;
343         debug_args.ExtractAlgorithmOptions(args, opts);
344         if (debug_args.ProduceDebugOutput()) {
345             opts.DebugDumpText(NcbiCerr, "BLAST options", 1);
346         }
347     }}
348 
349 
350     const EBlastProgramType p = opts.GetProgramType();
351     if((fmt_args.GetFormattedOutputChoice() == CFormattingArgs::eSAM) &&
352        (p != eBlastTypeBlastn )) {
353 		NCBI_THROW(CInputException, eInvalidInput,
354 		                        "SAM format is only applicable to blastn results" );
355     }
356 
357     CRef<CBlastQueryVector> queries =
358 		x_ExtractQueries(Blast_QueryIsProtein(p)?true:false);
359     CRef<CScope> scope = queries->GetScope(0);
360     _ASSERT(queries);
361 
362     CRef<CBlastDatabaseArgs> db_args(new CBlastDatabaseArgs());  // FIXME, what about rpsblast?
363     int filtering_algorithm = -1;
364     if (m_RmtBlast->IsDbSearch())
365     {
366         CRef<CBlast4_database> db = m_RmtBlast->GetDatabases();
367         _ASSERT(db);
368         _TRACE("Fetching results for " + Blast_ProgramNameFromType(p) + " on "
369                + db->GetName());
370         filtering_algorithm = m_RmtBlast->GetDbFilteringAlgorithmId();
371         CRef<CSearchDatabase> search_db(new CSearchDatabase(db->GetName(), db->IsProtein()
372                               ? CSearchDatabase::eBlastDbIsProtein
373                               : CSearchDatabase::eBlastDbIsNucleotide));
374 
375         if(m_RmtBlast->GetTaxidList().size() > 0) {
376         	 CSeqDBGiList *gilist = new CSeqDBGiList();
377         	 gilist->AddTaxIds(m_RmtBlast->GetTaxidList());
378         	 search_db->SetGiList(gilist);
379         }
380 
381         if(m_RmtBlast->GetNegativeTaxidList().size() > 0) {
382         	 CSeqDBGiList *gilist = new CSeqDBGiList();
383         	 gilist->AddTaxIds(m_RmtBlast->GetTaxidList());
384         	 search_db->SetNegativeGiList(gilist);
385         }
386         db_args->SetSearchDatabase(search_db);
387     }
388     else
389     {
390         TSeqLocVector subjects = s_ConvertSubjects2TSeqLocVector(m_RmtBlast);
391         CRef<IQueryFactory> subject_factory(new CObjMgr_QueryFactory(subjects));
392         CRef<CScope> subj_scope = subjects.front().scope;
393         db_args->SetSubjects(subject_factory, subj_scope,
394 			Blast_SubjectIsProtein(p)?true:false);
395     }
396 
397     CRef<CLocalDbAdapter> db_adapter;
398     bool isRemoteLoader = s_InitializeSubject(db_args, opts_handle, db_adapter, scope);
399 
400     const string kTask = m_RmtBlast->GetTask();
401 
402     CBlastFormat formatter(opts, *db_adapter,
403                            fmt_args.GetFormattedOutputChoice(),
404                            static_cast<bool>(args[kArgParseDeflines]),
405                            out,
406                            fmt_args.GetNumDescriptions(),
407                            fmt_args.GetNumAlignments(),
408                            *scope,
409                            opts.GetMatrixName(),
410                            fmt_args.ShowGis(),
411                            fmt_args.DisplayHtmlOutput(),
412                            opts.GetQueryGeneticCode(),
413                            opts.GetDbGeneticCode(),
414                            opts.GetSumStatisticsMode(),
415                            (!kRid.empty() || isRemoteLoader),
416                            filtering_algorithm,
417                            fmt_args.GetCustomOutputFormatSpec(),
418                            kTask == "megablast",
419                            opts.GetMBIndexLoaded(),
420                            NULL, NULL,
421                            GetCmdlineArgs(GetArguments()));
422     formatter.SetLineLength(fmt_args.GetLineLength());
423     formatter.SetHitsSortOption(fmt_args.GetHitsSortOption());
424     formatter.SetHspsSortOption(fmt_args.GetHspsSortOption());
425     formatter.SetCustomDelimiter(fmt_args.GetCustomDelimiter());
426     if(UseXInclude(fmt_args, args[kArgOutput].AsString())) {
427        	formatter.SetBaseFile(args[kArgOutput].AsString());
428     }
429     CRef<CSearchResultSet> results = m_RmtBlast->GetResultSet();
430     formatter.PrintProlog();
431     bool isPsiBlast = ("psiblast" == kTask);
432     if (fmt_args.ArchiveFormatRequested(args))
433     {
434     	if(isPsiBlast)
435     	{
436     		CRef<objects::CPssmWithParameters> pssm = m_RmtBlast->GetPSSM();
437     		if(!pssm.Empty())
438     		{
439     			formatter.WriteArchive(*pssm, *opts_handle, *results, m_RmtBlast->GetPsiNumberOfIterations());
440     		}
441     		else
442     		{
443     			CRef<IQueryFactory> query_factory(new CObjMgr_QueryFactory(*queries));
444     			formatter.WriteArchive(*query_factory, *opts_handle, *results, m_RmtBlast->GetPsiNumberOfIterations());
445     		}
446     	}
447     	else
448     	{
449     		CRef<IQueryFactory> query_factory(new CObjMgr_QueryFactory(*queries));
450     		formatter.WriteArchive(*query_factory, *opts_handle, *results);
451     	}
452     } else {
453         while (1)
454 	{
455         	BlastFormatter_PreFetchSequenceData(*results, scope,
456                 		                        fmt_args.GetFormattedOutputChoice());
457     		ITERATE(CSearchResultSet, result, *results) {
458     			if(isPsiBlast)
459     			{
460     				formatter.PrintOneResultSet(**result, queries,
461 					m_RmtBlast->GetPsiNumberOfIterations());
462     			}
463     			else
464     			{
465     				formatter.PrintOneResultSet(**result, queries);
466     			}
467     		}
468 		// The entire archive file (multiple sets) is formatted in this loop for XML.
469 		// That does not work for other formats.  Ugly, but that's where it's at now.
470 		if (m_LoadFromArchive == false || (fmt_args.GetFormattedOutputChoice() != CFormattingArgs::eXml
471                         && fmt_args.GetFormattedOutputChoice() != CFormattingArgs::eXml2
472                         && fmt_args.GetFormattedOutputChoice() != CFormattingArgs::eJson
473                         && fmt_args.GetFormattedOutputChoice() != CFormattingArgs::eXml2_S
474                         && fmt_args.GetFormattedOutputChoice() != CFormattingArgs::eJson_S )
475 			|| !m_RmtBlast->LoadFromArchive()) {
476 			break;
477                 }
478 		// Reset these for next set from archive
479     		results.Reset(m_RmtBlast->GetResultSet());
480     		queries.Reset(x_ExtractQueries(Blast_QueryIsProtein(p)?true:false));
481     		_ASSERT(queries);
482                 if (fmt_args.GetFormattedOutputChoice() == CFormattingArgs::eXml) {
483     		    scope.Reset(queries->GetScope(0));
484                 }
485                 else {
486     		    scope->AddScope(*(queries->GetScope(0)));
487                 }
488     		    s_InitializeSubject(db_args, opts_handle, db_adapter, scope);
489 	}
490     }
491     formatter.PrintEpilog(opts);
492     return retval;
493 }
494 
495 #define EXIT_CODE__UNKNOWN_RID 1
496 #define EXIT_CODE__SEARCH_PENDING 2
497 #define EXIT_CODE__SEARCH_FAILED 3
498 
Run(void)499 int CBlastFormatterApp::Run(void)
500 {
501     int status = 0;
502     const CArgs& args = GetArgs();
503 
504     try {
505         SetDiagPostLevel(eDiag_Warning);
506         if (args[kArgArchive].HasValue()) {
507             CNcbiIstream& istr = args[kArgArchive].AsInputFile();
508             try { m_RmtBlast.Reset(new CRemoteBlast(istr)); }
509             catch (const CBlastException& e) {
510                 if (e.GetErrCode() == CBlastException::eInvalidArgument) {
511                     NCBI_RETHROW(e, CInputException, eInvalidInput,
512                                  "Invalid input format for BLAST Archive.");
513                 }
514             }
515 
516 	    m_LoadFromArchive = true;
517             try {
518                 while (m_RmtBlast->LoadFromArchive()) {
519                 	if(!m_RmtBlast->IsErrMsgArchive()) {
520                 		status = PrintFormattedOutput();
521                 	}
522                 }
523             } catch (const CSerialException& e) {
524                 NCBI_RETHROW(e, CInputException, eInvalidInput,
525                              "Invalid input format for BLAST Archive.");
526             }
527     	    return status;
528         }
529 
530         const string kRid = args[kArgRid].AsString();
531         m_RmtBlast.Reset(new CRemoteBlast(kRid));
532         {{
533             CDebugArgs debug_args;
534             CBlastOptions dummy_options;
535             debug_args.ExtractAlgorithmOptions(args, dummy_options);
536             if (debug_args.ProduceDebugRemoteOutput()) {
537                 m_RmtBlast->SetVerbose();
538             }
539         }}
540 
541         switch (m_RmtBlast->CheckStatus()) {
542         case CRemoteBlast::eStatus_Unknown:
543             cerr << "Unknown/invalid RID '" << kRid << "'." << endl;
544             status = EXIT_CODE__UNKNOWN_RID;
545             break;
546 
547         case CRemoteBlast::eStatus_Done:
548             status = PrintFormattedOutput();
549             break;
550 
551         case CRemoteBlast::eStatus_Pending:
552             cerr << "RID '" << kRid << "' is still pending." << endl;
553             status = EXIT_CODE__SEARCH_PENDING;
554             break;
555 
556         case CRemoteBlast::eStatus_Failed:
557             cerr << "RID '" << kRid << "' has failed" << endl;
558             cerr << m_RmtBlast->GetErrors() << endl;
559             status = EXIT_CODE__SEARCH_FAILED;
560             break;
561 
562         default:
563             abort();
564         }
565 
566     } CATCH_ALL(status)
567     x_AddCmdOptions();
568     m_UsageReport.AddParam(CBlastUsageReport::eExitStatus, status);
569     return status;
570 }
571 
x_AddCmdOptions()572 void CBlastFormatterApp::x_AddCmdOptions()
573 {
574 	const CArgs & args = GetArgs();
575     if (args[kArgRid].HasValue()) {
576     	 m_UsageReport.AddParam(CBlastUsageReport::eRIDInput, args[kArgRid].AsString());
577     }
578     else if (args[kArgArchive].HasValue()) {
579     	 m_UsageReport.AddParam(CBlastUsageReport::eArchiveInput, true);
580     }
581 
582     if(args["outfmt"].HasValue()) {
583     	m_UsageReport.AddParam(CBlastUsageReport::eOutputFmt, args["outfmt"].AsString());
584     }
585 }
586 
587 
588 #ifndef SKIP_DOXYGEN_PROCESSING
main(int argc,const char * argv[])589 int main(int argc, const char* argv[] /*, const char* envp[]*/)
590 {
591     return CBlastFormatterApp().AppMain(argc, argv);
592 }
593 #endif /* SKIP_DOXYGEN_PROCESSING */
594