NCBI C++ ToolKit
eutils_client.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: eutils_client.cpp 102513 2024-05-20 11:33:22Z grichenk $
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  * Authors: Mike DiCuccio
27  *
28  * File Description:
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 
34 #include <corelib/ncbi_system.hpp>
35 #include <corelib/ncbistr.hpp>
36 #include <corelib/ncbi_config.hpp>
37 #include <corelib/ncbiapp.hpp>
42 #include <connect/ncbi_socket.hpp>
43 #include <misc/error_codes.hpp>
44 
45 #include <cmath>
46 #include <sstream>
47 #include <iterator>
48 #include <algorithm>
49 #include <type_traits>
50 
51 #define NCBI_USE_ERRCODE_X Misc_EutilsClient
52 
54 
55 using namespace objects;
56 
57 
58 //////////////////////////////////////////////////////////////////////////////
59 
60 namespace edirect {
61 
62  // Authors: Jonathan Kans, Aaron Ucko
63 
64  string Execute (
65  const string& cmmd,
66  const vector<string>& args,
67  const string& data
68  )
69 
70  {
71  static const STimeout five_seconds = { 5, 0 };
72  CConn_PipeStream ps(cmmd.c_str(), args, CPipe::fStdErr_Share, 0, &five_seconds);
73 
74  if ( ! data.empty() ) {
75  ps << data;
76  if (! NStr::EndsWith(data, "\n")) {
77  ps << "\n";
78  }
79  }
80 
81  ps.flush();
83 
84  string str;
85  NcbiStreamToString(&str, ps);
86 
87  return str;
88  }
89 }
90 
91 
92 static const char*
94 {
95  switch ( err_code ) {
97  return "Phrase not found";
99  return "Field not found";
101  return "Phrase ignored";
103  return "Quoted phrase not found";
105  return "Output message";
106  default:
107  return "Unknown error";
108  }
109 }
110 
111 template<class T> static void s_FormatIds(ostream& osm, const vector<T>& uids) {
112  osm << "&id=";
113  if (!uids.empty()) {
114  osm << uids.front();
115  for (auto it = uids.begin()+1; it != uids.end(); ++it) {
116  osm << ',' << *it;
117  }
118  }
119 }
120 
121 template<> void s_FormatIds<CSeq_id_Handle>(ostream& osm, const vector<CSeq_id_Handle>& uids) {
122  osm << "&id=";
124  for (auto it = uids.begin(); it != uids.end(); ++it) {
125  if (it != uids.begin()) {
126  osm << ',';
127  }
128  auto& seh = *it;
129  if (seh.Which() == CSeq_id::e_Gi) {
130  if (type != CSeq_id::e_Gi && type != CSeq_id::e_not_set) {
132  "Argument list contains seq-ids of mixed types");
133  }
135  osm << seh.GetGi();
136  } else {
137  if (type != CSeq_id::e_not_set && type != seh.Which()) {
139  "Argument list contains seq-ids of mixed types");
140  }
141  type = seh.Which();
142  osm << seh.GetSeqId()->GetSeqIdString(true);
143  }
144  }
145  osm << "&idtype=" << (type == CSeq_id::e_Gi ? "gi" : "acc");
146 }
147 
148 template<> void s_FormatIds<string>(ostream& osm, const vector<string>& uids)
149 {
150  osm << "&id=";
151  if (!uids.empty()) {
152  osm << uids.front();
153  for (auto it = uids.begin()+1; it != uids.end(); ++it) {
154  osm << ',' << *it;
155  }
156  }
157  osm << "&idtype=acc";
158 }
159 
160 template<class T> static inline T s_ParseId(const string& str)
161 {
162  return NStr::StringToNumeric<T>(str);
163 }
164 
165 template<> inline CSeq_id_Handle s_ParseId<CSeq_id_Handle>(const string& str)
166 {
168 }
169 
170 template<> inline string s_ParseId<string>(const string& str)
171 {
172  return str;
173 }
174 
175 
176 const char* CEUtilsException::GetErrCodeString(void) const
177 {
178  return s_GetErrCodeString(GetErrCode());
179 }
180 
181 
183 {
184 public:
185  virtual void HandleMessage(EDiagSev severity,
187  const string & message) const
188  {
190  << " - " << s_GetErrCodeString(err_code)
191  << ": " << message);
192  }
193 };
194 
195 
197 {
198 public:
199  virtual void HandleMessage(EDiagSev severity,
201  const string& message) const
202  {
203  // ERR_POST, but with severity as a variable.
204  CNcbiDiag(DIAG_COMPILE_INFO, severity).GetRef()
205  << s_GetErrCodeString(err_code)
206  << ": "
207  << message << Endm;
208  }
209 };
210 
211 
213 {
214 public:
215  virtual void HandleMessage(EDiagSev severity,
217  const string& message) const
218  {
219  switch(severity) {
220  case eDiag_Error:
221  case eDiag_Critical:
222  case eDiag_Fatal:
223  if (err_code != CEUtilsException::ePhraseNotFound ||
224  !IsTaxidQuery(message))
225  {
226  // NCBI_THROW, but wth err_code as a variable.
227  throw CEUtilsException(DIAG_COMPILE_INFO, 0, err_code, message);
228  }
229 
230  // This is a taxid query that happens to be be found in database;
231  // treat as warning
232 
234 
235  default:
236  // ERR_POST, but with severity as a variable.
237  CNcbiDiag(DIAG_COMPILE_INFO, severity).GetRef()
238  << s_GetErrCodeString(err_code)
239  << ": "
240  << message << Endm;
241  }
242  }
243 
244 private:
245 
246  bool IsTaxidQuery(const string &message) const {
247  /// Taxid queries are of form "txid<number>[orgn]" or "txid<number>[progn"}
248  /// The best way to check this would be using class CRegexp, but that
249  /// could create a circular library dependency, so using more primitive
250  /// string checking methods
251  if (!NStr::StartsWith(message, "txid") ||
252  (!NStr::EndsWith(message, "[orgn]") &&
253  !NStr::EndsWith(message, "[porgn]")))
254  {
255  return false;
256  }
257  try {
258  NStr::StringToUInt(message.substr(4, message.find('[')-4));
259  } catch (CStringException &) {
260  /// "taxid" string is not a number
261  return false;
262  }
263  return true;
264  }
265 };
266 
267 
269 {
270 public:
272  : m_HasError(false)
273  {
274  }
275 
276  bool HasError(void) const { return m_HasError; }
277  void GetErrors(list<string>& errors) { errors = m_Errors; }
278 
279 protected:
280  bool error(const string& message)
281  {
282  ERR_POST(Error << "parse error: " << message);
283  return false;
284  }
285 
286  bool start_element(const string& name,
287  const attrs_type& attrs)
288  {
289  m_Text_chunks.clear();
290 
291  if ( !m_Path.empty() ) {
292  m_Path += "/";
293  }
294  m_Path += name;
295  return true;
296  }
297 
298  bool end_element(const string& name)
299  {
300  bool result = OnEndElement();
301 
302  string::size_type pos = m_Path.find_last_of("/");
303  if (pos != string::npos) {
304  m_Path.erase(pos);
305  }
306  return result;
307  }
308 
309  bool text(const string& contents)
310  {
311  m_Text_chunks.push_back(contents);
312  return true;
313  }
314 
315  virtual bool OnEndElement(void) { return true; }
316 
317  string GetText(void) const { return NStr::Join(m_Text_chunks, ""); }
318 
319 protected:
320  string m_Path;
321  /// List of parsing errors.
322  /// @note Parsing errors deal with the syntax of a
323  /// reply from E-Utils. Parsing errors should be
324  /// distinguished from errors encountered in
325  /// processing a data request.
326  list<string> m_Errors;
328  // Accumulator of text chunks.
329  list<string> m_Text_chunks;
330 };
331 
332 
333 template<class T>
335 {
336 public:
337  CESearchParser(vector<T>& uids,
338  CEutilsClient::CMessageHandler& message_handler)
339  : m_MessageHandler(message_handler)
340  , m_Count(0)
341  , m_Uids(uids)
342  { }
343 
345  {
346  ProcessMessages();
347  }
348 
349  Uint8 GetCount(void) const { return m_Count; }
350 
351  /// Processes the warning and error messages from
352  /// E-Utils, delivering them to the message handler
353  /// and clearing them from the queue.
354  ///
355  /// The XML parser does not like exceptions thrown
356  /// during the parse, so the messages are queued.
357  void ProcessMessages(void)
358  {
359  // Process warnings first; it's better to emit
360  // as many messages as possible, and errors are
361  // more likely to lead to premature exit.
362  ITERATE(list<TMessage>, i, m_ResultWarnings) {
363  m_MessageHandler.HandleMessage(eDiag_Warning,
364  i->first, i->second);
365  }
366  m_ResultWarnings.clear();
367  ITERATE(list<TMessage>, i, m_ResultErrors) {
368  m_MessageHandler.HandleMessage(eDiag_Error,
369  i->first, i->second);
370  }
371  m_ResultErrors.clear();
372  }
373 
374 protected:
375  typedef pair<CEUtilsException::EErrCode, string> TMessage;
376 
377  bool x_IsSuffix(const string& s,
378  const char* suffix)
379  {
380  string::size_type pos = s.rfind(suffix);
381  return (pos != string::npos && pos == s.size() - strlen(suffix));
382  }
383 
384 
385  bool OnEndElement(void)
386  {
387  string contents = GetText();
388 
389  if (m_Path == "eSearchResult/Count") {
390  m_Count = NStr::StringToUInt8(contents);
391  }
392  else if (x_IsSuffix(m_Path, "/IdList/Id")) {
393  m_Uids.push_back(s_ParseId<T>(contents));
394  }
395  else if (x_IsSuffix(m_Path, "/ErrorList/PhraseNotFound")) {
396  TMessage message(CEUtilsException::ePhraseNotFound, contents);
397  m_ResultErrors.push_back(message);
398  }
399  else if (x_IsSuffix(m_Path, "/ErrorList/FieldNotFound")) {
400  TMessage message(CEUtilsException::eFieldNotFound, contents);
401  m_ResultErrors.push_back(message);
402  }
403  else if (x_IsSuffix(m_Path, "/WarningList/PhraseIgnored")) {
404  TMessage message(CEUtilsException::ePhraseIgnored, contents);
405  m_ResultWarnings.push_back(message);
406  }
407  else if (x_IsSuffix(m_Path, "/WarningList/QuotedPhraseNotFound")) {
409  m_ResultWarnings.push_back(message);
410  }
411  else if (x_IsSuffix(m_Path, "/WarningList/OutputMessage")) {
412  TMessage message(CEUtilsException::eOutputMessage, contents);
413  m_ResultWarnings.push_back(message);
414  }
415  else if (m_Path == "ERROR" || m_Path == "eSearchResult/ERROR") {
416  m_HasError = true;
417  m_Errors.push_back(contents);
418  return false;
419  }
420  return true;
421  }
422 
423 private:
426  vector<T>& m_Uids;
427 
428  /// List of error messages from the E-Utils request.
429  /// These are distinct from errors in parsing the
430  /// E-Utils reply.
431  list<TMessage> m_ResultErrors;
432 
433  /// List of warning messages from the E-Utils request.
434  /// These are distinct from errors in parsing the
435  /// E-Utils reply.
436  list<TMessage> m_ResultWarnings;
437 };
438 
439 
440 template<class T>
442 {
443 public:
444  CELinkParser(const string& dbfrom, const string& dbto,
445  vector<T>& uids)
446  : m_LinkName(dbfrom + "_" + dbto)
447  , m_Uids(uids)
448  , m_InLinkSet(false)
449  {
450  NStr::ToLower(m_LinkName);
451  }
452 
453  void SetLinkName(const string& link_name)
454  {
455  m_LinkName = link_name;
456  NStr::ToLower(m_LinkName);
457  }
458 
459 protected:
460 
461  bool end_element(const string& name)
462  {
464  if (name == "LinkSetDb") {
465  m_InLinkSet = false;
466  }
467  return true;
468  }
469 
470  bool OnEndElement(void)
471  {
472  if ( !m_InLinkSet && NStr::EndsWith(m_Path, "/LinkName") ) {
473  if ( GetText() == m_LinkName) {
474  m_InLinkSet = true;
475  }
476  }
477  else if (m_InLinkSet && NStr::EndsWith(m_Path, "/Link/Id") ) {
478  m_Uids.push_back(s_ParseId<T>( GetText() ));
479  }
480  return true;
481  }
482 
483 private:
484  string m_LinkName;
485  vector<T>& m_Uids;
487 };
488 
489 
490 
491 //////////////////////////////////////////////////////////////////////////////
492 
493 
494 #define EUTILS_CLIENT_SECTION "eutils_client"
495 // The default values approximate the old "sqrt(retry)" wait times.
496 #define DEFAULT_MAX_RETRIES 9
497 #define DEFAULT_WAIT_TIME 0
498 #define DEFAULT_WAIT_TIME_MULTIPLIER 1
499 #define DEFAULT_WAIT_TIME_INCREMENT 0.5
500 #define DEFAULT_WAIT_TIME_MAX 3
501 
502 
504  {
505  "wait_time",
506  0,
508  },
509  {
510  "wait_time_max",
511  0,
513  },
514  {
515  "wait_time_multiplier",
516  0,
518  },
519  {
520  "wait_time_increment",
521  0,
523  }
524 };
525 
526 
528  : m_CachedHostNameCount(0), m_RetMax(kMax_Int), m_MaxRetries(DEFAULT_MAX_RETRIES), m_WaitTime(s_WaitTimeParams)
529 {
530  class CInPlaceConnIniter : protected CConnIniter
531  {
532  } conn_initer; /*NCBI_FAKE_WARNING*/
534  x_InitParams();
535 }
536 
537 CEutilsClient::CEutilsClient(const string& host)
538  : m_CachedHostNameCount(0),
539  m_HostName(host),
540  m_RetMax(kMax_Int),
541  m_MaxRetries(DEFAULT_MAX_RETRIES),
542  m_WaitTime(s_WaitTimeParams)
543 {
544  class CInPlaceConnIniter : protected CConnIniter
545  {
546  } conn_initer; /*NCBI_FAKE_WARNING*/
548  x_InitParams();
549 }
550 
552 {
554 }
555 
557 {
559 }
560 
562 {
564 }
565 
567 {
568  m_MessageHandler.Reset(&message_handler);
569 }
570 
571 void CEutilsClient::SetUserTag(const string& tag)
572 {
573  m_UrlTag = tag;
574 }
575 
577 {
579 }
580 
581 void CEutilsClient::AddParameter(const string &name, const string &value)
582 {
584 }
585 
586 void CEutilsClient::SetLinkName(const string& link_name)
587 {
588  m_LinkName = link_name;
589 }
590 
592 {
593  m_RetMax = ret_max;
594 }
595 
596 
597 //////////////////////////////////////////////////////////////////////////////
598 
600 {
603  if (app) {
604  const CNcbiRegistry& reg = app->GetConfig();
605  string max_retries = reg.GetString(EUTILS_CLIENT_SECTION, "max_retries", "");
606  if (!max_retries.empty()) {
607  try {
608  m_MaxRetries = NStr::StringToNumeric<unsigned int>(max_retries);
609  }
610  catch (...) {
611  ERR_POST("Invalid max_retries value: " << max_retries);
613  }
614  }
615  string timeout = reg.GetString(EUTILS_CLIENT_SECTION, "conn_timeout", "");
616  if (!timeout.empty()) {
617  try {
619  }
620  catch (...) {
621  ERR_POST("Invalid conn_timeout value: " << timeout);
623  }
624  }
626  }
627 }
628 
629 
630 template<class Call>
631 typename std::invoke_result<Call>::type CEutilsClient::CallWithRetry(Call&& call, const char* name)
632 {
633  for ( m_Attempt = 1; m_Attempt < m_MaxRetries; ++m_Attempt ) {
634  try {
635  return call();
636  }
637  catch ( CException& exc ) {
638  LOG_POST(Warning<<"CEutilsClient::" << name << "() try " << m_Attempt << " exception: " << exc);
639  }
640  catch ( exception& exc ) {
641  LOG_POST(Warning<<"CEutilsClient::" << name << "() try " << m_Attempt << " exception: " << exc.what());
642  }
643  catch ( ... ) {
644  LOG_POST(Warning<<"CEutilsClient::" << name << "() try " << m_Attempt << " exception");
645  }
646  double wait_sec = m_WaitTime.GetTime(m_Attempt - 1);
647  LOG_POST(Warning<<"CEutilsClient: waiting " << wait_sec << "s before retry");
648  SleepMilliSec(Uint4(wait_sec*1000));
649  }
650  return call();
651 }
652 
653 
654 Uint8 CEutilsClient::Count(const string& db,
655  const string& term)
656 {
657  string params;
658  params += "db=" + NStr::URLEncode(db);
659  params += "&term=" + NStr::URLEncode(term);
660  params += "&retmode=xml&retmax=1";
661  if ( !m_UrlTag.empty() ) {
662  params += "&user=" + NStr::URLEncode(m_UrlTag);
663  }
665 
666  LOG_POST(Trace << "Executing: db=" << db << " query=" << term);
667  m_Url.clear();
668  m_Time.clear();
669  try {
670  return CallWithRetry(bind(&CEutilsClient::x_CountOnce, this, cref(params)), "Count");
671  }
672  catch (...) {
674  "failed to execute query: " + term);
675  }
676 
677  return 0;
678 }
679 
680 
681 Uint8 CEutilsClient::x_CountOnce(const string& params)
682 {
683  string path = "/entrez/eutils/esearch.fcgi";
684  string hostname = x_GetHostName();
685  STimeout timeout_value;
686  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
687  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
688  m_Url.push_back(x_BuildUrl(hostname, path, params));
689  istr << params;
690  m_Time.push_back(CTime(CTime::eCurrent));
691  vector<Int8> uids;
692  CESearchParser<Int8> parser(uids, *m_MessageHandler);
693 
694  xml::error_messages msgs;
695  parser.parse_stream(istr, &msgs);
696 
697  if (msgs.has_errors() || msgs.has_fatal_errors()) {
699  "error parsing xml: " + msgs.print());
700  }
701 
702  parser.ProcessMessages();
703  return parser.GetCount();
704 }
705 
706 
707 
708 #ifdef NCBI_INT8_GI
710  vector<int>& uids)
711 {
712  return x_ParseSearchResults(istr, uids);
713 }
714 #endif
715 
717  vector<CSeq_id_Handle>& uids)
718 {
719  return x_ParseSearchResults(istr, uids);
720 }
721 
723  vector<string>& uids)
724 {
725  return x_ParseSearchResults(istr, uids);
726 }
727 
729  vector<TEntrezId>& uids)
730 {
731  return x_ParseSearchResults(istr, uids);
732 }
733 
734 template<class T>
736  vector<T>& uids)
737 {
738  CESearchParser<T> parser(uids, *m_MessageHandler);
739  xml::error_messages msgs;
740  parser.parse_stream(istr, &msgs);
741 
742  if (msgs.has_errors() || msgs.has_fatal_errors()) {
744  "error parsing xml: " + msgs.print());
745  }
746 
747  if (parser.HasError()) {
748  list<string> errs;
749  parser.GetErrors(errs);
750 
751  string msg = NStr::Join(errs, "; ");
753  "error returned from query: " + msg);
754  }
755 
756  parser.ProcessMessages();
757  return parser.GetCount();
758 }
759 
760 #ifdef NCBI_INT8_GI
761 Uint8 CEutilsClient::ParseSearchResults(const string& xml_file,
762  vector<int>& uids)
763 {
764  return x_ParseSearchResults(xml_file, uids);
765 }
766 #endif
767 
768 Uint8 CEutilsClient::ParseSearchResults(const string& xml_file,
769  vector<CSeq_id_Handle>& uids)
770 {
771  return x_ParseSearchResults(xml_file, uids);
772 }
773 
775  vector<string>& uids)
776 {
777  return x_ParseSearchResults(xml_file, uids);
778 }
779 
781  vector<TEntrezId>& uids)
782 {
783  return x_ParseSearchResults(xml_file, uids);
784 }
785 
786 template<class T>
788  vector<T>& uids)
789 {
790  CNcbiIfstream istr(xml_file.c_str());
791  if ( !istr ) {
793  "failed to open file: " + xml_file);
794  }
795  return ParseSearchResults(istr, uids);
796 }
797 
798 #ifdef NCBI_INT8_GI
799 Uint8 CEutilsClient::Search(const string& db,
800  const string& term,
801  vector<int>& uids,
802  const string& xml_path)
803 {
804  return x_Search(db, term, uids, xml_path);
805 }
806 #endif
807 
808 Uint8 CEutilsClient::Search(const string& db,
809  const string& term,
810  vector<CSeq_id_Handle>& uids,
811  const string& xml_path)
812 {
813  return x_Search(db, term, uids, xml_path);
814 }
815 
816 Uint8 CEutilsClient::Search(const string& db,
817  const string& term,
818  vector<string>& uids,
819  const string& xml_path)
820 {
821  return x_Search(db, term, uids, xml_path);
822 }
823 
824 
825 Uint8 CEutilsClient::Search(const string& db,
826  const string& term,
827  vector<TEntrezId>& uids,
828  const string& xml_path)
829 {
830  return x_Search(db, term, uids, xml_path);
831 }
832 
833 template<class T>
835  const string& term,
836  vector<T>& uids,
837  const string& xml_path)
838 {
839  string params;
840  params += "db=" + NStr::URLEncode(db);
841  params += "&term=" + NStr::URLEncode(term);
842  params += "&retmode=xml";
843  if (m_RetMax) {
844  params += "&retmax=" + NStr::NumericToString(m_RetMax);
845  }
846  if ( !m_UrlTag.empty() ) {
847  params += "&user=" + NStr::URLEncode(m_UrlTag);
848  }
850  params += "&idtype=gi";
851  } else {
852  params += "&idtype=acc";
853  }
855 
856  LOG_POST(Trace << "Executing: db=" << db << " query=" << term);
857  m_Url.clear();
858  m_Time.clear();
859 
860  try {
861  return CallWithRetry(bind(&CEutilsClient::x_SearchOnce<T>, this, cref(params), ref(uids), cref(xml_path)), "Search");
862  }
863  catch (...) {
865  "failed to execute query: " + term);
866  }
867 
868  return 0;
869 }
870 
871 
872 template<class T>
873 Uint8 CEutilsClient::x_SearchOnce(const string& params,
874  vector<T>& uids,
875  const string& xml_path)
876 {
877  Uint8 count = 0;
878 
879  string path = "/entrez/eutils/esearch.fcgi";
880  string hostname = x_GetHostName();
881  STimeout timeout_value;
882  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
883  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
884  m_Url.push_back(x_BuildUrl(hostname, path, params));
885  istr << params;
886  m_Time.push_back(CTime(CTime::eCurrent));
887 
888  if(!xml_path.empty()) {
889  string xml_file = xml_path + '.' + NStr::NumericToString(m_Attempt);
890  CNcbiOfstream ostr(xml_file.c_str());
891  if (ostr.good()) {
892  NcbiStreamCopy(ostr, istr);
893  ostr.close();
894 
895  if(!ostr || 200 != istr.GetStatusCode()) {
897  "Failure while writing entrez xml response"
898  " to file: " + xml_file);
899  }
900 
901  count = ParseSearchResults(xml_file, uids);
902  }
903  else {
904  ERR_POST(Error << "Unable to open file for writing: "
905  + xml_file);
906  count = ParseSearchResults(istr, uids);
907  }
908  }
909  else {
910  count = ParseSearchResults(istr, uids);
911  }
912 
913  return count;
914 }
915 
916 
917 void CEutilsClient::Search(const string& db,
918  const string& term,
919  CNcbiOstream& ostr,
920  EUseHistory use_history)
921 {
922  ostringstream oss;
923  oss << "db=" << NStr::URLEncode(db)
924  << "&term=" << NStr::URLEncode(term)
925  << "&retmode=xml";
926  if (m_RetMax) {
927  oss << "&retmax="
928  << m_RetMax;
929  }
930 
931  if ( use_history == eUseHistoryEnabled ) {
932  oss << "&usehistory=y";
933  }
934 
935  x_Get("/entrez/eutils/esearch.fcgi", oss.str(), ostr);
936 }
937 
938 static void s_SearchHistoryQuery(ostringstream& oss,
939  const string& db,
940  const string& term,
941  const string& web_env,
942  int retstart,
943  int retmax)
944 {
945  oss << "db=" << NStr::URLEncode(db)
946  << "&term=" << NStr::URLEncode(term)
947  << "&retmode=xml";
948  if ( retstart ) {
949  oss << "&retstart="
950  << retstart;
951  }
952  if (retmax) {
953  oss << "&retmax="
954  << retmax;
955  }
956 
957  oss << "&usehistory=y"
958  << "&WebEnv="
959  << web_env;
960 }
961 
962 void CEutilsClient::SearchHistory(const string& db,
963  const string& term,
964  const string& web_env,
965  Int8 query_key,
966  int retstart,
967  CNcbiOstream& ostr)
968 {
969  ostringstream oss;
970  s_SearchHistoryQuery(oss, db, term, web_env, retstart, m_RetMax);
971  if ( query_key > 0 ) {
972  oss << "&query_key="
973  << query_key;
974  }
975 
976  x_Get("/entrez/eutils/esearch.fcgi", oss.str(), ostr);
977 }
978 
979 void CEutilsClient::SearchHistory(const string& db,
980  const string& term,
981  const string& web_env,
982  CSeq_id_Handle query_key,
983  int retstart,
984  CNcbiOstream& ostr)
985 {
986  ostringstream oss;
987  s_SearchHistoryQuery(oss, db, term, web_env, retstart, m_RetMax);
988  oss << "&query_key=" << query_key << "&idtype=acc";
989 
990  x_Get("/entrez/eutils/esearch.fcgi", oss.str(), ostr);
991 }
992 
993 void CEutilsClient::SearchHistory(const string& db,
994  const string& term,
995  const string& web_env,
996  const string& query_key,
997  int retstart,
998  CNcbiOstream& ostr)
999 {
1000  ostringstream oss;
1001  s_SearchHistoryQuery(oss, db, term, web_env, retstart, m_RetMax);
1002  oss << "&query_key=" << query_key << "&idtype=acc";
1003 
1004  x_Get("/entrez/eutils/esearch.fcgi", oss.str(), ostr);
1005 }
1006 
1007 
1008 #define HOST_NAME_REFRESH_FREQ 100
1009 
1010 const string& CEutilsClient::x_GetHostName(void) const
1011 {
1012  if (!m_HostName.empty()) {
1013  return m_HostName;
1014  }
1015 
1017  m_CachedHostName.clear();
1019  }
1020 
1021  if (!m_CachedHostName.empty()) {
1022  return m_CachedHostName;
1023  }
1024 
1025  // See also: objtools/eutils/api/eutils.cpp
1026  static const char kEutils[] = "eutils.ncbi.nlm.nih.gov";
1027  static const char kEutilsLB[] = "eutils_lb";
1028 
1029  string host;
1030  SConnNetInfo* net_info = ConnNetInfo_Create(kEutilsLB);
1031  SSERV_Info* info = SERV_GetInfo(kEutilsLB, fSERV_Dns,
1032  SERV_ANYHOST, net_info);
1033  ConnNetInfo_Destroy(net_info);
1034  if (info) {
1035  if (info->host) {
1036  host = CSocketAPI::ntoa(info->host);
1037  }
1038  free(info);
1039  }
1040 
1041  string scheme("http");
1042  if (host.empty()) {
1043  char buf[80];
1044  const char* web = ConnNetInfo_GetValue(kEutilsLB, REG_CONN_HOST,
1045  buf, sizeof(buf), kEutils);
1046  host = string(web && *web ? web : kEutils);
1047  scheme += 's';
1048  }
1049  _ASSERT(!host.empty());
1050  m_CachedHostName = scheme + "://" + host;
1051  return m_CachedHostName;
1052 }
1053 
1054 
1055 void CEutilsClient::x_Get(string const& path,
1056  string const& params,
1057  CNcbiOstream& ostr)
1058 {
1059  m_Url.clear();
1060  m_Time.clear();
1061  string extra_params{ params };
1062  x_AddAdditionalParameters(extra_params);
1063  try {
1064  CallWithRetry(bind(&CEutilsClient::x_GetOnce, this, cref(path), cref(extra_params), ref(ostr)), "Get");
1065  }
1066  catch (...) {
1067  ostringstream msg;
1068  msg << "Failed to execute request: "
1069  << path
1070  << "?"
1071  << params;
1072  NCBI_THROW(CException, eUnknown, msg.str());
1073  }
1074 }
1075 
1076 
1077 void CEutilsClient::x_GetOnce(string const& path, string const& extra_params, CNcbiOstream& ostr)
1078 {
1079  string hostname = x_GetHostName();
1080  STimeout timeout_value;
1081  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
1082  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
1083  m_Url.push_back(x_BuildUrl(hostname, path, extra_params));
1084  istr << extra_params;
1085  m_Time.push_back(CTime(CTime::eCurrent));
1086  ostringstream reply;
1087  if (!NcbiStreamCopy(reply, istr) || 200 != istr.GetStatusCode()) {
1088  NCBI_THROW(CException, eUnknown, "Failure while reading response");
1089  }
1090  string reply_str = reply.str();
1091  istringstream reply_istr(reply.str());
1092  vector<TEntrezId> uids;
1093  /// We don't actually need the uids, but parse the reply anyway
1094  /// in order to catch errors
1095  ParseSearchResults(reply_istr, uids);
1096 
1097  /// No errors found in reply; copy to output stream
1098  ostr << reply_str;
1099 }
1100 
1101 
1102 //////////////////////////////////////////////////////////////////////////////
1103 
1104 #ifdef NCBI_INT8_GI
1105 void CEutilsClient::Link(const string& db_from,
1106  const string& db_to,
1107  const vector<int>& uids_from,
1108  vector<int>& uids_to,
1109  const string& xml_path,
1110  const string& command)
1111 
1112 {
1113  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1114 }
1115 #endif
1116 
1117 void CEutilsClient::Link(const string& db_from,
1118  const string& db_to,
1119  const vector<CSeq_id_Handle>& uids_from,
1120  vector<CSeq_id_Handle>& uids_to,
1121  const string& xml_path,
1122  const string& command)
1123 {
1124  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1125 }
1126 
1127 void CEutilsClient::Link(const string& db_from,
1128  const string& db_to,
1129  const vector<string>& uids_from,
1130  vector<string>& uids_to,
1131  const string& xml_path,
1132  const string& command)
1133 {
1134  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1135 }
1136 
1137 void CEutilsClient::Link(const string& db_from,
1138  const string& db_to,
1139  const vector<TEntrezId>& uids_from,
1140  vector<TEntrezId>& uids_to,
1141  const string& xml_path,
1142  const string& command)
1143 {
1144  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1145 }
1146 
1147 #ifdef NCBI_INT8_GI
1148 void CEutilsClient::Link(const string& db_from,
1149  const string& db_to,
1150  const vector<int>& uids_from,
1151  vector<TEntrezId>& uids_to,
1152  const string& xml_path,
1153  const string& command)
1154 {
1155  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1156 }
1157 
1158 void CEutilsClient::Link(const string& db_from,
1159  const string& db_to,
1160  const vector<TEntrezId>& uids_from,
1161  vector<int>& uids_to,
1162  const string& xml_path,
1163  const string& command)
1164 {
1165  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1166 }
1167 #endif
1168 
1169 void CEutilsClient::Link(const string& db_from,
1170  const string& db_to,
1171  const vector<TEntrezId>& uids_from,
1172  vector<CSeq_id_Handle>& uids_to,
1173  const string& xml_path,
1174  const string& command)
1175 {
1176  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1177 }
1178 
1179 void CEutilsClient::Link(const string& db_from,
1180  const string& db_to,
1181  const vector<CSeq_id_Handle>& uids_from,
1182  vector<TEntrezId>& uids_to,
1183  const string& xml_path,
1184  const string& command)
1185 {
1186  x_Link(db_from, db_to, uids_from, uids_to, xml_path, command);
1187 }
1188 
1189 template<class T1, class T2>
1190 void CEutilsClient::x_Link(const string& db_from,
1191  const string& db_to,
1192  const vector<T1>& uids_from,
1193  vector<T2>& uids_to,
1194  const string& xml_path,
1195  const string& command)
1196 {
1197  std::ostringstream oss;
1198 
1199  oss << "db=" << NStr::URLEncode(db_to)
1200  << "&dbfrom=" << NStr::URLEncode(db_from)
1201  << "&retmode=xml"
1202  << "&cmd=" << NStr::URLEncode(command);
1203  s_FormatIds(oss, uids_from);
1204  string params = oss.str();
1205  x_AddAdditionalParameters(params);
1206 
1207  m_Url.clear();
1208  m_Time.clear();
1209 
1210  try {
1211  CallWithRetry(bind(&CEutilsClient::x_LinkOnceT<T2>, this,
1212  cref(db_from), cref(db_to), ref(uids_to), cref(xml_path), cref(params)), "Link");
1213  }
1214  catch (...) {
1216  "failed to execute elink request: " + params);
1217  }
1218 }
1219 
1220 
1221 template<class T>
1222 void CEutilsClient::x_LinkOnceT(const string& db_from,
1223  const string& db_to,
1224  vector<T>& uids_to,
1225  const string& xml_path,
1226  const string& params)
1227 {
1228  string path = "/entrez/eutils/elink.fcgi";
1229  string hostname = x_GetHostName();
1230  STimeout timeout_value;
1231  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
1232  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
1233  m_Url.push_back(x_BuildUrl(hostname, path, params));
1234  istr << params;
1235  m_Time.push_back(CTime(CTime::eCurrent));
1236  CELinkParser<T> parser(db_from, db_to, uids_to);
1237  if ( !m_LinkName.empty() ) {
1238  parser.SetLinkName(m_LinkName);
1239  }
1240  xml::error_messages msgs;
1241  if(!xml_path.empty()) {
1242  string xml_file
1243  = xml_path + '.' + NStr::NumericToString(m_Attempt);
1244 
1245  CNcbiOfstream ostr(xml_file.c_str());
1246  if(ostr.good()) {
1247  NcbiStreamCopy(ostr, istr);
1248  ostr.close();
1249  parser.parse_file(xml_file.c_str(), &msgs);
1250  if(!ostr || 200 != istr.GetStatusCode()) {
1252  "Failure while writing entrez xml response"
1253  " to file: " + xml_file);
1254  }
1255  }
1256  else {
1257  ERR_POST(Error << "Unable to open file for writing: "
1258  + xml_file);
1259  parser.parse_stream(istr, &msgs);
1260  }
1261  }
1262  else {
1263  parser.parse_stream(istr, &msgs);
1264  }
1265 
1266  if (msgs.has_errors() || msgs.has_fatal_errors()) {
1268  "error parsing xml: " + msgs.print());
1269  }
1270 }
1271 
1272 
1273 #ifdef NCBI_INT8_GI
1274 void CEutilsClient::Link(const string& db_from,
1275  const string& db_to,
1276  const vector<int>& uids_from,
1277  CNcbiOstream& ostr,
1278  const string& command)
1279 {
1280  x_Link(db_from, db_to, uids_from, ostr, command);
1281 }
1282 #endif
1283 
1284 void CEutilsClient::Link(const string& db_from,
1285  const string& db_to,
1286  const vector<CSeq_id_Handle>& uids_from,
1287  CNcbiOstream& ostr,
1288  const string& command)
1289 {
1290  x_Link(db_from, db_to, uids_from, ostr, command);
1291 }
1292 
1293 void CEutilsClient::Link(const string& db_from,
1294  const string& db_to,
1295  const vector<string>& uids_from,
1296  CNcbiOstream& ostr,
1297  const string& command)
1298 {
1299  x_Link(db_from, db_to, uids_from, ostr, command);
1300 }
1301 
1302 void CEutilsClient::Link(const string& db_from,
1303  const string& db_to,
1304  const vector<TEntrezId>& uids_from,
1305  CNcbiOstream& ostr,
1306  const string& command)
1307 {
1308  x_Link(db_from, db_to, uids_from, ostr, command);
1309 }
1310 
1311 template<class T>
1312 void CEutilsClient::x_Link(const string& db_from,
1313  const string& db_to,
1314  const vector<T>& uids_from,
1315  CNcbiOstream& ostr,
1316  const string& command)
1317 {
1318  std::ostringstream oss;
1319 
1320  oss << "db=" << NStr::URLEncode(db_to)
1321  << "&dbfrom=" << NStr::URLEncode(db_from)
1322  << "&retmode=xml"
1323  << "&cmd=" + NStr::URLEncode(command);
1324  s_FormatIds(oss, uids_from);
1325  string params = oss.str();
1326  x_AddAdditionalParameters(params);
1327 
1328  m_Url.clear();
1329  m_Time.clear();
1330  try {
1331  CallWithRetry(bind(&CEutilsClient::x_LinkOnce, this, ref(ostr), cref(params)), "Link");
1332  }
1333  catch (...) {
1335  "failed to execute elink request: " + params);
1336  }
1337 }
1338 
1339 
1340 void CEutilsClient::x_LinkOnce(CNcbiOstream& ostr, const string& params)
1341 {
1342  string path = "/entrez/eutils/elink.fcgi";
1343  string hostname = x_GetHostName();
1344  STimeout timeout_value;
1345  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
1346  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
1347  m_Url.push_back(x_BuildUrl(hostname, path, params));
1348  istr << params;
1349  m_Time.push_back(CTime(CTime::eCurrent));
1350  if (!NcbiStreamCopy(ostr, istr) || 200 != istr.GetStatusCode()) {
1351  NCBI_THROW(CException, eUnknown, "Failure while reading response");
1352  }
1353 }
1354 
1355 
1356 void CEutilsClient::LinkHistory(const string& db_from,
1357  const string& db_to,
1358  const string& web_env,
1359  Int8 query_key,
1360  CNcbiOstream& ostr)
1361 {
1362  std::ostringstream oss;
1363 
1364  oss << "db=" << NStr::URLEncode(db_to)
1365  << "&dbfrom=" << NStr::URLEncode(db_from)
1366  << "&retmode=xml"
1367  << "&WebEnv=" << web_env
1368  << "&query_key=" << query_key;
1369 
1370  x_Get("/entrez/eutils/elink.fcgi", oss.str(), ostr);
1371 }
1372 
1373 void CEutilsClient::LinkHistory(const string& db_from,
1374  const string& db_to,
1375  const string& web_env,
1376  CSeq_id_Handle query_key,
1377  CNcbiOstream& ostr)
1378 {
1379  std::ostringstream oss;
1380 
1381  oss << "db=" << NStr::URLEncode(db_to)
1382  << "&dbfrom=" << NStr::URLEncode(db_from)
1383  << "&retmode=xml"
1384  << "&WebEnv=" << web_env
1385  << "&query_key=" << query_key
1386  << "&idtype=acc";
1387 
1388  x_Get("/entrez/eutils/elink.fcgi", oss.str(), ostr);
1389 }
1390 
1391 void CEutilsClient::LinkHistory(const string& db_from,
1392  const string& db_to,
1393  const string& web_env,
1394  const string& query_key,
1395  CNcbiOstream& ostr)
1396 {
1397  std::ostringstream oss;
1398 
1399  oss << "db=" << NStr::URLEncode(db_to)
1400  << "&dbfrom=" << NStr::URLEncode(db_from)
1401  << "&retmode=xml"
1402  << "&WebEnv=" << web_env
1403  << "&query_key=" << query_key
1404  << "&idtype=acc";
1405 
1406  x_Get("/entrez/eutils/elink.fcgi", oss.str(), ostr);
1407 }
1408 
1409 #ifdef NCBI_INT8_GI
1410 void CEutilsClient::LinkOut(const string& db,
1411  const vector<int>& uids,
1412  xml::document& doc,
1413  const string& cmd)
1414 {
1415  x_LinkOut(db, uids, doc, cmd);
1416 }
1417 
1418 #endif
1419 
1420 void CEutilsClient::LinkOut(const string& db,
1421  const vector<objects::CSeq_id_Handle>& uids,
1422  xml::document& doc,
1423  const string& cmd)
1424 {
1425  x_LinkOut(db, uids, doc, cmd);
1426 }
1427 
1428 void CEutilsClient::LinkOut(const string& db,
1429  const vector<string>& uids,
1430  xml::document& doc,
1431  const string& cmd)
1432 {
1433  x_LinkOut(db, uids, doc, cmd);
1434 }
1435 
1436 void CEutilsClient::LinkOut(const string& db,
1437  const vector<TEntrezId>& uids,
1438  xml::document& doc,
1439  const string& cmd)
1440 {
1441  x_LinkOut(db, uids, doc, cmd);
1442 }
1443 
1444 template<class T> void CEutilsClient::x_LinkOut(const string& db,
1445  const vector<T>& uids,
1446  xml::document& doc,
1447  const string& cmd)
1448 {
1449  ostringstream oss;
1450  oss << "dbfrom=" << NStr::URLEncode(db)
1451  << "&cmd=" << NStr::URLEncode(cmd)
1452  << "&retmode=xml";
1453  s_FormatIds(oss, uids);
1454  string params = oss.str();
1455  x_AddAdditionalParameters(params);
1456 
1457  m_Url.clear();
1458  m_Time.clear();
1459  try {
1460  CallWithRetry(bind(&CEutilsClient::x_LinkOutOnce, this, ref(doc), cref(params)), "LinkOut");
1461  }
1462  catch (...) {
1464  "failed to execute esummary request: " + params);
1465  }
1466 }
1467 
1468 
1469 void CEutilsClient::x_LinkOutOnce(xml::document& doc, const string& params)
1470 {
1471  string path = "/entrez/eutils/elink.fcgi?";
1472  string hostname = x_GetHostName();
1473  string url = x_BuildUrl(hostname, path, params);
1474  LOG_POST(Trace << "query: " << url);
1475  STimeout timeout_value;
1476  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
1477  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
1478  m_Url.push_back(url);
1479  istr << params;
1480  m_Time.push_back(CTime(CTime::eCurrent));
1481  // slurp up all the output.
1482  stringbuf sb;
1483  istr >> &sb;
1484  if (200 != istr.GetStatusCode()) {
1485  NCBI_THROW(CException, eUnknown, "Failure while reading response");
1486  }
1487  string docstr(sb.str());
1488  // turn it into an xml::document
1489  xml::error_messages msgs;
1490  xml::document xmldoc(docstr.data(), docstr.size(), &msgs );
1491  doc.swap(xmldoc);
1492 }
1493 
1494 
1495 #ifdef NCBI_INT8_GI
1496 void CEutilsClient::Summary(const string& db,
1497  const vector<int>& uids,
1498  xml::document& docsums,
1499  const string& version)
1500 {
1501  x_Summary(db, uids, docsums, version);
1502 }
1503 #endif
1504 
1505 void CEutilsClient::Summary(const string& db,
1506  const vector<CSeq_id_Handle>& uids,
1507  xml::document& docsums,
1508  const string& version)
1509 {
1510  x_Summary(db, uids, docsums, version);
1511 }
1512 
1513 void CEutilsClient::Summary(const string& db,
1514  const vector<string>& uids,
1515  xml::document& docsums,
1516  const string& version)
1517 {
1518  x_Summary(db, uids, docsums, version);
1519 }
1520 
1521 void CEutilsClient::Summary(const string& db,
1522  const vector<TEntrezId>& uids,
1523  xml::document& docsums,
1524  const string& version)
1525 {
1526  x_Summary(db, uids, docsums, version);
1527 }
1528 
1529 template<class T>
1530 void CEutilsClient::x_Summary(const string& db,
1531  const vector<T>& uids,
1532  xml::document& docsums,
1533  const string& version)
1534 {
1535  ostringstream oss;
1536  oss << "db=" << NStr::URLEncode(db)
1537  << "&retmode=xml";
1538  if ( !version.empty() ) {
1539  oss << "&version="
1540  << version;
1541  }
1542  s_FormatIds(oss, uids);
1543  string params = oss.str();
1544  x_AddAdditionalParameters(params);
1545 
1546  m_Url.clear();
1547  m_Time.clear();
1548  try {
1549  CallWithRetry(bind(&CEutilsClient::x_SummaryOnce, this, ref(docsums), cref(params)), "Summary");
1550  }
1551  catch (...) {
1553  "failed to execute esummary request: " + params);
1554  }
1555 }
1556 
1557 
1558 void CEutilsClient::x_SummaryOnce(xml::document& docsums, const string& params)
1559 {
1560  string path = "/entrez/eutils/esummary.fcgi?";
1561  string hostname = x_GetHostName();
1562  string url = x_BuildUrl(hostname, path, params);
1563  LOG_POST(Trace << "query: " << url);
1564  STimeout timeout_value;
1565  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
1566  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
1567  m_Url.push_back(url);
1568  istr << params;
1569  m_Time.push_back(CTime(CTime::eCurrent));
1570  // slurp up all the output.
1571  stringbuf sb;
1572  istr >> &sb;
1573  if (200 != istr.GetStatusCode()) {
1574  NCBI_THROW(CException, eUnknown, "Failure while reading response");
1575  }
1576  string docstr(sb.str());
1577  // turn it into an xml::document
1578  xml::error_messages msgs;
1579  xml::document xmldoc(docstr.data(), docstr.size(), &msgs );
1580  docsums.swap(xmldoc);
1581 }
1582 
1583 
1584 static inline void s_SummaryHistoryQuery(ostream& oss,
1585  const string& db,
1586  const string& web_env,
1587  int retstart,
1588  const string version,
1589  int retmax)
1590 {
1591  oss << "db=" << NStr::URLEncode(db)
1592  << "&retmode=xml"
1593  << "&WebEnv=" << web_env;
1594 
1595  if ( retstart > 0 ) {
1596  oss << "&retstart=" << retstart;
1597  }
1598 
1599  if ( retmax ) {
1600  oss << "&retmax=" << retmax;
1601  }
1602 
1603  if ( !version.empty() ) {
1604  oss << "&version="
1605  << version;
1606  }
1607 }
1608 
1609 void CEutilsClient::SummaryHistory(const string& db,
1610  const string& web_env,
1611  Int8 query_key,
1612  int retstart,
1613  const string& version,
1614  CNcbiOstream& ostr)
1615 {
1616  ostringstream oss;
1617  s_SummaryHistoryQuery(oss, db, web_env, retstart, version, m_RetMax);
1618  oss << "&query_key=" << query_key;
1619  x_Get("/entrez/eutils/esummary.fcgi?", oss.str(), ostr);
1620 }
1621 
1622 void CEutilsClient::SummaryHistory(const string& db,
1623  const string& web_env,
1624  CSeq_id_Handle query_key,
1625  int retstart,
1626  const string& version,
1627  CNcbiOstream& ostr)
1628 {
1629  ostringstream oss;
1630  s_SummaryHistoryQuery(oss, db, web_env, retstart, version, m_RetMax);
1631  oss << "&query_key=" << query_key
1632  << "&idtype=acc";
1633  x_Get("/entrez/eutils/esummary.fcgi?", oss.str(), ostr);
1634 }
1635 
1636 void CEutilsClient::SummaryHistory(const string& db,
1637  const string& web_env,
1638  const string& query_key,
1639  int retstart,
1640  const string& version,
1641  CNcbiOstream& ostr)
1642 {
1643  ostringstream oss;
1644  s_SummaryHistoryQuery(oss, db, web_env, retstart, version, m_RetMax);
1645  oss << "&query_key=" << query_key
1646  << "&idtype=acc";
1647  x_Get("/entrez/eutils/esummary.fcgi?", oss.str(), ostr);
1648 }
1649 
1650 #ifdef NCBI_INT8_GI
1651 void CEutilsClient::Fetch(const string& db,
1652  const vector<int>& uids,
1653  CNcbiOstream& ostr,
1654  const string& retmode)
1655 {
1656  x_Fetch(db, uids, ostr, retmode);
1657 }
1658 #endif
1659 
1660 void CEutilsClient::Fetch(const string& db,
1661  const vector<CSeq_id_Handle>& uids,
1662  CNcbiOstream& ostr,
1663  const string& retmode)
1664 {
1665  x_Fetch(db, uids, ostr, retmode);
1666 }
1667 
1668 void CEutilsClient::Fetch(const string& db,
1669  const vector<string>& uids,
1670  CNcbiOstream& ostr,
1671  const string& retmode)
1672 {
1673  x_Fetch(db, uids, ostr, retmode);
1674 }
1675 
1676 void CEutilsClient::Fetch(const string& db,
1677  const vector<TEntrezId>& uids,
1678  CNcbiOstream& ostr,
1679  const string& retmode)
1680 {
1681  x_Fetch(db, uids, ostr, retmode);
1682 }
1683 
1684 template<class T>
1685 void CEutilsClient::x_Fetch(const string& db,
1686  const vector<T>& uids,
1687  CNcbiOstream& ostr,
1688  const string& retmode)
1689 {
1690  ostringstream oss;
1691  oss << "db=" << NStr::URLEncode(db)
1692  << "&retmode=" << NStr::URLEncode(retmode);
1693 
1694  s_FormatIds(oss, uids);
1695  string params = oss.str();
1696  x_AddAdditionalParameters(params);
1697 
1698  m_Url.clear();
1699  m_Time.clear();
1700  try {
1701  CallWithRetry(bind(&CEutilsClient::x_FetchOnce, this, ref(ostr), cref(params)), "Fetch");
1702  }
1703  catch (...) {
1705  "failed to execute efetch request: " + params);
1706  }
1707 }
1708 
1709 
1710 void CEutilsClient::x_FetchOnce(CNcbiOstream& ostr, const string& params)
1711 {
1712  string path = "/entrez/eutils/efetch.fcgi";
1713  string hostname = x_GetHostName();
1714  STimeout timeout_value;
1715  const STimeout* timeout = g_CTimeoutToSTimeout(m_Timeout, timeout_value);
1716  CConn_HttpStream istr(x_BuildUrl(hostname, path, kEmptyStr), fHTTP_AutoReconnect, timeout);
1717  m_Url.push_back(x_BuildUrl(hostname, path, params));
1718  istr << params;
1719  m_Time.push_back(CTime(CTime::eCurrent));
1720  if (!NcbiStreamCopy(ostr, istr) || 200 != istr.GetStatusCode()) {
1721  NCBI_THROW(CException, eUnknown, "Failure while reading response");
1722  }
1723 }
1724 
1725 
1726 static inline string s_GetContentType(CEutilsClient::EContentType content_type)
1727 {
1728  switch (content_type) {
1730  return "xml";
1732  return "text";
1734  return "html";
1736  return "asn.1";
1737  default:
1738  break;
1739  }
1740  // Default content type
1741  return "xml";
1742 }
1743 
1744 static inline void s_FetchHistoryQuery(ostream& oss,
1745  const string& db,
1746  const string& web_env,
1747  int retstart,
1748  int retmax,
1749  CEutilsClient::EContentType content_type)
1750 {
1751  oss << "db=" << NStr::URLEncode(db)
1752  << "&retmode=" << s_GetContentType(content_type)
1753  << "&WebEnv=" << web_env;
1754  if ( retstart > 0 ) {
1755  oss << "&retstart=" << retstart;
1756  }
1757 
1758  if ( retmax ) {
1759  oss << "&retmax=" << retmax;
1760  }
1761 }
1762 
1763 void CEutilsClient::FetchHistory(const string& db,
1764  const string& web_env,
1765  Int8 query_key,
1766  int retstart,
1767  EContentType content_type,
1768  CNcbiOstream& ostr)
1769 {
1770  ostringstream oss;
1771  s_FetchHistoryQuery(oss, db, web_env, retstart, m_RetMax, content_type);
1772  oss << "&query_key=" << query_key;
1773 
1774  x_Get("/entrez/eutils/efetch.fcgi", oss.str(), ostr);
1775 }
1776 
1777 void CEutilsClient::FetchHistory(const string& db,
1778  const string& web_env,
1779  CSeq_id_Handle query_key,
1780  int retstart,
1781  EContentType content_type,
1782  CNcbiOstream& ostr)
1783 {
1784  ostringstream oss;
1785  s_FetchHistoryQuery(oss, db, web_env, retstart, m_RetMax, content_type);
1786  oss << "&query_key=" << query_key
1787  << "&idtype=acc";
1788 
1789  x_Get("/entrez/eutils/efetch.fcgi", oss.str(), ostr);
1790 }
1791 
1792 void CEutilsClient::FetchHistory(const string& db,
1793  const string& web_env,
1794  const string& query_key,
1795  int retstart,
1796  EContentType content_type,
1797  CNcbiOstream& ostr)
1798 {
1799  ostringstream oss;
1800  s_FetchHistoryQuery(oss, db, web_env, retstart, m_RetMax, content_type);
1801  oss << "&query_key=" << query_key
1802  << "&idtype=acc";
1803 
1804  x_Get("/entrez/eutils/efetch.fcgi", oss.str(), ostr);
1805 }
1806 
1807 const list<string> CEutilsClient::GetUrl() const
1808 {
1809  return m_Url;
1810 }
1811 
1812 const list<CTime> CEutilsClient::GetTime() const
1813 {
1814  return m_Time;
1815 }
1816 
1817 string CEutilsClient::x_BuildUrl(const string& host, const string &path,
1818  const string &params)
1819 {
1820  string url = host + path;
1821  if(!params.empty()) {
1822  url += '?' + params;
1823  }
1824  return url;
1825 }
1826 
1827 inline void CEutilsClient::x_AddAdditionalParameters(string &params)
1828 {
1829  if (m_AdditionalParams.empty())
1830  return;
1831  ostringstream oss;
1832  for (const TParamList::value_type &param : m_AdditionalParams) {
1833  oss << '&' << param.first << '=' << param.second;
1834  }
1835  params += oss.str();
1836 }
1837 
Helper hook-up class that installs default logging/registry/locking (but only if they have not yet be...
This stream exchanges data with an HTTP server located at the URL: http[s]://host[:port]/path[?...
CConn_PipeStream for command piping.
void SetLinkName(const string &link_name)
bool end_element(const string &name)
CELinkParser(const string &dbfrom, const string &dbto, vector< T > &uids)
vector< T > & m_Uids
bool OnEndElement(void)
pair< CEUtilsException::EErrCode, string > TMessage
bool OnEndElement(void)
CEutilsClient::CMessageHandler & m_MessageHandler
list< TMessage > m_ResultErrors
List of error messages from the E-Utils request.
Uint8 GetCount(void) const
bool x_IsSuffix(const string &s, const char *suffix)
vector< T > & m_Uids
CESearchParser(vector< T > &uids, CEutilsClient::CMessageHandler &message_handler)
void ProcessMessages(void)
Processes the warning and error messages from E-Utils, delivering them to the message handler and cle...
list< TMessage > m_ResultWarnings
List of warning messages from the E-Utils request.
virtual const char * GetErrCodeString(void) const override
Get error code interpreted as text.
bool end_element(const string &name)
void GetErrors(list< string > &errors)
virtual bool OnEndElement(void)
bool text(const string &contents)
string GetText(void) const
bool start_element(const string &name, const attrs_type &attrs)
list< string > m_Errors
List of parsing errors.
list< string > m_Text_chunks
bool error(const string &message)
bool HasError(void) const
Subclass this to override how messages (warnings and errors) are handled.
const list< CTime > GetTime(void) const
list< CTime > m_Time
void LinkOut(const string &db, const vector< objects::CSeq_id_Handle > &uids, xml::document &docsums, const string &cmd="llinks")
Uint8 x_CountOnce(const string &params)
void Fetch(const string &db, const vector< objects::CSeq_id_Handle > &uids, CNcbiOstream &ostr, const string &retmode="xml")
void SetMaxReturn(int ret_max)
CRef< CMessageHandler > m_MessageHandler
void x_SummaryOnce(xml::document &docsums, const string &params)
void SearchHistory(const string &db, const string &term, const string &web_env, Int8 query_key, int retstart, CNcbiOstream &ostr)
string m_CachedHostName
Uint8 Search(const string &db, const string &term, vector< objects::CSeq_id_Handle > &uids, const string &xml_path=kEmptyStr)
TParamList m_AdditionalParams
const list< string > GetUrl(void) const
static string x_BuildUrl(const string &host, const string &path, const string &params)
void x_AddAdditionalParameters(string &params)
void x_Fetch(const string &db, const vector< T > &uids, CNcbiOstream &ostr, const string &retmode="xml")
void x_Summary(const string &db, const vector< T > &uids, xml::document &docsums, const string &version="")
void AddParameter(const string &name, const string &value)
CIncreasingTime m_WaitTime
void Summary(const string &db, const vector< objects::CSeq_id_Handle > &uids, xml::document &docsums, const string &version="")
unsigned int m_MaxRetries
void SetLinkName(const string &link_name)
void ClearAddedParameters()
void x_Link(const string &db_from, const string &db_to, const vector< T1 > &uids_from, vector< T2 > &uids_to, const string &xml_path, const string &command)
unsigned int m_Attempt
Uint8 x_Search(const string &db, const string &term, vector< T > &uids, const string &xml_path=kEmptyStr)
void x_FetchOnce(CNcbiOstream &ostr, const string &params)
std::invoke_result< Call >::type CallWithRetry(Call &&call, const char *name)
void SetMessageHandlerDiagPost(void)
Equivalent to: ERR_POST(Warning|Error << ...).
void x_Get(string const &path, string const &params, CNcbiOstream &ostr)
CTimeout m_Timeout
Uint8 x_ParseSearchResults(const string &xml_file, vector< T > &uids)
void x_LinkOnce(CNcbiOstream &ostr, const string &params)
void x_InitParams(void)
void SetMessageHandlerThrowOnError(void)
Equivalent to: NCBI_THROW, ERR_POST, LOG_POST as appropriate.
void Link(const string &db_from, const string &db_to, const vector< objects::CSeq_id_Handle > &uids_from, vector< objects::CSeq_id_Handle > &uids_to, const string &xml_path=kEmptyStr, const string &command="neighbor")
const string & x_GetHostName(void) const
void x_LinkOnceT(const string &db_from, const string &db_to, vector< T > &uids_to, const string &xml_path, const string &params)
void SetMessageHandler(CMessageHandler &message_handler)
Set custom message handler.
void x_GetOnce(string const &path, string const &extra_params, CNcbiOstream &ostr)
void SetMessageHandlerDefault(void)
Default is to log all messages at informational level.
void SetUserTag(const string &tag)
Uint8 x_SearchOnce(const string &params, vector< T > &uids, const string &xml_path)
Uint8 ParseSearchResults(CNcbiIstream &istr, vector< objects::CSeq_id_Handle > &uids)
void FetchHistory(const string &db, const string &web_env, Int8 query_key, int retstart, EContentType content_type, CNcbiOstream &ostr)
void LinkHistory(const string &db_from, const string &db_to, const string &web_env, Int8 query_key, CNcbiOstream &ostr)
Uint8 Count(const string &db, const string &term)
list< string > m_Url
void x_LinkOutOnce(xml::document &doc, const string &params)
void SummaryHistory(const string &db, const string &web_env, Int8 query_key, int retstart, const string &version, CNcbiOstream &ostr)
void x_LinkOut(const string &db, const vector< T > &uids, xml::document &doc, const string &cmd)
double GetTime(int step) const
Definition: incr_time.cpp:136
void Init(CConfig &conf, const string &driver_name, const SAllParams &params)
Definition: incr_time.cpp:61
virtual void HandleMessage(EDiagSev severity, CEUtilsException::EErrCode err_code, const string &message) const
Pure virtual function, to be implemented by subclass.
virtual void HandleMessage(EDiagSev severity, CEUtilsException::EErrCode err_code, const string &message) const
Pure virtual function, to be implemented by subclass.
bool IsTaxidQuery(const string &message) const
virtual void HandleMessage(EDiagSev severity, CEUtilsException::EErrCode err_code, const string &message) const
Pure virtual function, to be implemented by subclass.
CNcbiDiag –.
Definition: ncbidiag.hpp:924
CNcbiRegistry –.
Definition: ncbireg.hpp:913
CStringException –.
Definition: ncbistr.hpp:4506
CTime –.
Definition: ncbitime.hpp:296
bool empty() const
Definition: map.hpp:149
void clear()
Definition: map.hpp:169
The xml::document class is used to hold the XML tree and various bits of information about it.
Definition: document.hpp:80
void swap(document &other)
Swap one xml::document object for another.
Definition: document.cpp:530
The xml::error_messages class is used to store all the error message which are collected while parsin...
Definition: errors.hpp:137
bool has_fatal_errors(void) const
Check if there are fatal errors in the error messages.
Definition: errors.cpp:126
bool has_errors(void) const
Check if there are errors in the error messages.
Definition: errors.cpp:122
std::string print(void) const
Convert error messages into a single printable string.
Definition: errors.cpp:130
The xml::event_parser is used to parse an XML document by calling member functions when certain thing...
std::map< std::string, std::string > attrs_type
a type for holding XML node attributes
bool parse_stream(std::istream &stream, error_messages *messages, warnings_as_errors_type how=type_warnings_not_errors)
Parse what ever data that can be read from the given stream.
bool parse_file(const char *filename, error_messages *messages, warnings_as_errors_type how=type_warnings_not_errors)
Call this member function to parse the given file.
The NCBI C++ standard methods for dealing with std::string.
#define T(s)
Definition: common.h:230
void s_FormatIds< string >(ostream &osm, const vector< string > &uids)
static const char * s_GetErrCodeString(CEUtilsException::TErrCode err_code)
#define DEFAULT_WAIT_TIME_MAX
static CIncreasingTime::SAllParams s_WaitTimeParams
string s_ParseId< string >(const string &str)
void s_FormatIds< CSeq_id_Handle >(ostream &osm, const vector< CSeq_id_Handle > &uids)
static void s_SearchHistoryQuery(ostringstream &oss, const string &db, const string &term, const string &web_env, int retstart, int retmax)
#define DEFAULT_WAIT_TIME_MULTIPLIER
CSeq_id_Handle s_ParseId< CSeq_id_Handle >(const string &str)
static string s_GetContentType(CEutilsClient::EContentType content_type)
#define DEFAULT_WAIT_TIME_INCREMENT
static void s_FetchHistoryQuery(ostream &oss, const string &db, const string &web_env, int retstart, int retmax, CEutilsClient::EContentType content_type)
#define HOST_NAME_REFRESH_FREQ
#define EUTILS_CLIENT_SECTION
static void s_SummaryHistoryQuery(ostream &oss, const string &db, const string &web_env, int retstart, const string version, int retmax)
static void s_FormatIds(ostream &osm, const vector< T > &uids)
#define DEFAULT_MAX_RETRIES
#define DEFAULT_WAIT_TIME
static T s_ParseId(const string &str)
This file contains the definition of the xml::event_parser class.
static CS_COMMAND * cmd
Definition: ct_dynamic.c:26
#define false
Definition: bool.h:36
static int type
Definition: getdata.c:31
static const char * str(char *buf, int n)
Definition: stats.c:84
char data[12]
Definition: iconv.c:80
static CNcbiApplicationGuard InstanceGuard(void)
Singleton method.
Definition: ncbiapp.cpp:133
const CNcbiRegistry & GetConfig(void) const
Get the application's cached configuration parameters (read-only).
#define ITERATE(Type, Var, Cont)
ITERATE macro to sequence through container elements.
Definition: ncbimisc.hpp:815
string
Definition: cgiapp.hpp:687
int GetStatusCode(void) const
Get the last seen HTTP status code, if available.
CPipe & GetPipe(void)
Return an underlying CPipe; it's valid for as long as the stream exists.
@ fHTTP_AutoReconnect
See HTTP_CreateConnectorEx()
#define DIAG_COMPILE_INFO
Make compile time diagnostic information object to use in CNcbiDiag and CException.
Definition: ncbidiag.hpp:170
const CNcbiDiag & GetRef(void) const
Some compilers (e.g.
Definition: ncbidiag.hpp:946
static const char * SeverityName(EDiagSev sev)
Get a common symbolic name for the severity levels.
#define ERR_POST(message)
Error posting with file, line number information but without error codes.
Definition: ncbidiag.hpp:186
EDiagSev
Severity level for the posted diagnostics.
Definition: ncbidiag.hpp:650
#define LOG_POST(message)
This macro is deprecated and it's strongly recomended to move in all projects (except tests) to macro...
Definition: ncbidiag.hpp:226
@ eDiag_Error
Error message.
Definition: ncbidiag.hpp:653
@ eDiag_Warning
Warning message.
Definition: ncbidiag.hpp:652
@ eDiag_Fatal
Fatal error – guarantees exit(or abort)
Definition: ncbidiag.hpp:655
@ eDiag_Critical
Critical error message.
Definition: ncbidiag.hpp:654
void Error(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1197
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
void Trace(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1179
void Warning(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1191
int TErrCode
Definition: ncbiexpt.hpp:889
virtual const char * what(void) const noexcept
Standard report (includes full backlog).
Definition: ncbiexpt.cpp:342
void Info(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1185
@ eUnknown
Definition: app_popup.hpp:72
static CSeq_id_Handle GetHandle(const CSeq_id &id)
Normal way of getting a handle, works for any seq-id.
EIO_Status CloseHandle(EChildIOHandle handle)
Close the specified child's pipe handle (even for CPipe opened with OpenSelf()).
Definition: ncbi_pipe.cpp:1974
@ fStdIn
Definition: ncbi_pipe.hpp:115
@ fStdErr_Share
Keep stderr (share it with child)
Definition: ncbi_pipe.hpp:91
#define NCBI_FALLTHROUGH
uint32_t Uint4
4-byte (32-bit) unsigned integer
Definition: ncbitype.h:103
#define kMax_Int
Definition: ncbi_limits.h:184
int64_t Int8
8-byte (64-bit) signed integer
Definition: ncbitype.h:104
uint64_t Uint8
8-byte (64-bit) unsigned integer
Definition: ncbitype.h:105
virtual string GetString(const string &section, const string &name, const string &default_value, TFlags flags=0) const
Get the parameter string value.
Definition: ncbireg.cpp:321
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
#define SERV_ANYHOST
Definition: ncbi_service.h:55
SSERV_Info * SERV_GetInfo(const char *service, TSERV_Type types, unsigned int preferred_host, const SConnNetInfo *net_info)
Same as "SERV_GetInfoEx(., ., ., ., 0, 0, 0)" – i.e.
Definition: ncbi_service.c:872
@ fSERV_Dns
static string ntoa(unsigned int host)
BSD-like API. NB: when int, "host" must be in network byte order.
IO_PREFIX::ofstream CNcbiOfstream
Portable alias for ofstream.
Definition: ncbistre.hpp:500
IO_PREFIX::ostream CNcbiOstream
Portable alias for ostream.
Definition: ncbistre.hpp:149
IO_PREFIX::istream CNcbiIstream
Portable alias for istream.
Definition: ncbistre.hpp:146
IO_PREFIX::ifstream CNcbiIfstream
Portable alias for ifstream.
Definition: ncbistre.hpp:439
bool NcbiStreamCopy(CNcbiOstream &os, CNcbiIstream &is)
Copy the entire contents of stream "is" to stream "os".
Definition: ncbistre.cpp:211
size_t NcbiStreamToString(string *s, CNcbiIstream &is, size_t pos=0)
Input the entire contents of an istream into a string (NULL causes drain).
Definition: ncbistre.cpp:296
#define kEmptyStr
Definition: ncbistr.hpp:123
static bool EndsWith(const CTempString str, const CTempString end, ECase use_case=eCase)
Check if a string ends with a specified suffix value.
Definition: ncbistr.hpp:5430
static double StringToDouble(const CTempStringEx str, TStringToNumFlags flags=0)
Convert string to double.
Definition: ncbistr.cpp:1387
static string Join(const TContainer &arr, const CTempString &delim)
Join strings using the specified delimiter.
Definition: ncbistr.hpp:2697
static bool StartsWith(const CTempString str, const CTempString start, ECase use_case=eCase)
Check if a string starts with a specified prefix value.
Definition: ncbistr.hpp:5412
static Uint8 StringToUInt8(const CTempString str, TStringToNumFlags flags=0, int base=10)
Convert string to Uint8.
Definition: ncbistr.cpp:873
static unsigned int StringToUInt(const CTempString str, TStringToNumFlags flags=0, int base=10)
Convert string to unsigned int.
Definition: ncbistr.cpp:642
static enable_if< is_arithmetic< TNumeric >::value||is_convertible< TNumeric, Int8 >::value, string >::type NumericToString(TNumeric value, TNumToStringFlags flags=0, int base=10)
Convert numeric value to string.
Definition: ncbistr.hpp:673
static string URLEncode(const CTempString str, EUrlEncode flag=eUrlEnc_SkipMarkChars)
URL-encode string.
Definition: ncbistr.cpp:6062
static string & ToLower(string &str)
Convert string to lower case – string& version.
Definition: ncbistr.cpp:405
@ fDecimalPosixOrLocal
StringToDouble*(): For decimal point, try both C and current locale.
Definition: ncbistr.hpp:301
void Set(EType type)
Set special value.
Definition: ncbitime.cpp:3577
@ eCurrent
Use current time. See also CCurrentTime.
Definition: ncbitime.hpp:300
@ eDefault
Default timeout (to be interpreted by the client code)
Definition: ncbitime.hpp:1698
#define REG_CONN_HOST
const STimeout * g_CTimeoutToSTimeout(const CTimeout &cto, STimeout &sto)
CTimeout/STimeout adapters.
const char * ConnNetInfo_GetValue(const char *service, const char *param, char *value, size_t value_size, const char *def_value)
SConnNetInfo * ConnNetInfo_Create(const char *service)
void ConnNetInfo_Destroy(SConnNetInfo *net_info)
E_Choice
Choice variants.
Definition: Seq_id_.hpp:93
@ e_Gi
GenInfo Integrated Database.
Definition: Seq_id_.hpp:106
@ e_not_set
No variant selected.
Definition: Seq_id_.hpp:94
char * buf
int i
static MDB_envinfo info
Definition: mdb_load.c:37
static int version
Definition: mdb_load.c:29
string Execute(const string &cmmd, const vector< string > &args, const string &data=kEmptyStr)
const GenericPointer< typename T::ValueType > T2 value
Definition: pointer.h:1227
Parameters initialization model.
const char * tag
void SleepMilliSec(unsigned long ml_sec, EInterruptOnSignal onsignal=eRestartOnSignal)
Defines the CNcbiApplication and CAppException classes for creating NCBI applications.
const char * command
static const char * suffix[]
Definition: pcregrep.c:408
Timeout structure.
Definition: ncbi_types.h:76
Definition: type.c:6
#define _ASSERT
else result
Definition: token2.c:20
void free(voidpf ptr)
Modified on Mon Jun 17 05:05:02 2024 by modify_doxy.py rev. 669887