ncbiapp.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:38k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: ncbiapp.cpp,v $
  4.  * PRODUCTION Revision 1000.5  2004/06/03 19:28:14  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.85
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: ncbiapp.cpp,v 1000.5 2004/06/03 19:28:14 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Authors:  Vsevolod Sandomirskiy, Denis Vakatov
  35.  *
  36.  * File Description:
  37.  *   CNcbiApplication -- a generic NCBI application class
  38.  *   CCgiApplication  -- a NCBI CGI-application class
  39.  *
  40.  */
  41. #include <ncbi_pch.hpp>
  42. #include <corelib/ncbienv.hpp>
  43. #include <corelib/metareg.hpp>
  44. #include <corelib/ncbiargs.hpp>
  45. #include <corelib/ncbifile.hpp>
  46. #include <corelib/ncbiapp.hpp>
  47. #if defined(NCBI_OS_MSWIN)
  48. #  include <corelib/ncbi_os_mswin.hpp>
  49. #  include <corelib/ncbidll.hpp>
  50. #endif
  51. #if defined(NCBI_OS_UNIX)
  52. #  include <unistd.h>
  53. #endif
  54. #if defined(NCBI_OS_DARWIN)
  55. #  define __NOEXTENSIONS__
  56. #  include <Carbon/Carbon.h>
  57. #endif
  58. BEGIN_NCBI_SCOPE
  59. /////////////////////////////////////////////////////////////////////////////
  60. //  Constants
  61. //
  62. static const char* s_ArgLogFile = "-logfile";
  63. static const char* s_ArgCfgFile = "-conffile";
  64. static const char* s_ArgVersion = "-version";
  65. static void s_DiagToStdlog_Cleanup(void* data)
  66. {
  67.     // SetupDiag(eDS_ToStdlog)
  68.     CNcbiOfstream* os_log = static_cast<CNcbiOfstream*> (data);
  69.     delete os_log;
  70. }
  71. ///////////////////////////////////////////////////////
  72. // CNcbiApplication
  73. //
  74. CNcbiApplication* CNcbiApplication::m_Instance;
  75. CNcbiApplication* CNcbiApplication::Instance(void)
  76. {
  77.     return m_Instance;
  78. }
  79. CNcbiApplication::CNcbiApplication(void)
  80. {
  81.     m_DisableArgDesc = false;
  82.     m_HideArgs = 0;
  83.     m_StdioFlags = 0;
  84.     m_CinBuffer = 0;
  85.     // Register the app. instance
  86.     if ( m_Instance ) {
  87.         NCBI_THROW(CAppException, eSecond,
  88.                    "Second instance of CNcbiApplication is prohibited");
  89.     }
  90.     m_Instance = this;
  91.     // Create empty version info
  92.     m_Version.reset(new CVersionInfo(0,0));
  93.     // Create empty application arguments & name
  94.     m_Arguments.reset(new CNcbiArguments(0,0));
  95.     // Create empty application environment
  96.     m_Environ.reset(new CNcbiEnvironment);
  97.     // Create an empty registry
  98.     m_Config = new CNcbiRegistry;
  99.     m_OwnsConfig = true;
  100. }
  101. CNcbiApplication::~CNcbiApplication(void)
  102. {
  103.     m_Instance = 0;
  104.     FlushDiag(0, true);
  105.     if (m_CinBuffer) {
  106.         delete [] m_CinBuffer;
  107.     }
  108.     if (m_OwnsConfig) {
  109.         delete m_Config;
  110.     }
  111. }
  112. void CNcbiApplication::Init(void)
  113. {
  114.     return;
  115. }
  116. void CNcbiApplication::Exit(void)
  117. {
  118.     return;
  119. }
  120. SIZE_TYPE CNcbiApplication::FlushDiag(CNcbiOstream* os, bool close_diag)
  121. {
  122.     // dyn.cast to CNcbiOstrstream
  123.     CNcbiOstrstream* ostr = dynamic_cast<CNcbiOstrstream*>(m_DiagStream.get());
  124.     if ( !ostr ) {
  125.         _ASSERT( !m_DiagStream.get() );
  126.         return 0;
  127.     }
  128.     // dump all content to "os"
  129.     SIZE_TYPE n_write = 0;
  130.     if ( os )
  131.         n_write = ostr->pcount();
  132.     if ( n_write ) {
  133.         os->write(ostr->str(), n_write);
  134.         ostr->rdbuf()->freeze(0);
  135.     }
  136.     // reset output buffer or destroy
  137.     if ( close_diag ) {
  138.         if ( IsDiagStream(m_DiagStream.get()) ) {
  139.             SetDiagStream(0);
  140.         }
  141.         m_DiagStream.reset(0);
  142.     } else {
  143.         ostr->rdbuf()->SEEKOFF(0, IOS_BASE::beg, IOS_BASE::out);
  144.     }
  145.     // return # of bytes dumped to "os"
  146.     return (os  &&  os->good()) ? n_write : 0;
  147. }
  148. #if defined(NCBI_OS_DARWIN)
  149. static void s_MacArgMunging(CNcbiApplication&   app,
  150.                             int*                argcPtr,  
  151.                             const char* const** argvPtr,
  152.                             const string&       exepath)
  153. {
  154.     // Sometimes on Mac there will be an argument -psn which 
  155.     // will be followed by the Process Serial Number, e.g. -psn_0_13107201
  156.     // this is in situations where the application could have no other
  157.     // arguments like when it is double clicked.
  158.     // This will mess up argument processing later, so get rid of it.
  159.     static const char* s_ArgMacPsn = "-psn_"; 
  160.     
  161.     if (*argcPtr == 2  && 
  162.         NStr::strncmp((*argvPtr)[1], s_ArgMacPsn, strlen(s_ArgMacPsn)) == 0) {
  163.         --*argcPtr;
  164.     }
  165.     
  166.     if (*argcPtr > 1)
  167.         return;
  168.     // Have no arguments from the operating system -- so use the '.args' file
  169.     // Open the args file.
  170.     string exedir;
  171.     CDir::SplitPath(exepath, &exedir);
  172.     string args_fname = exedir + app.GetProgramDisplayName() + ".args";
  173.     CNcbiIfstream in(args_fname.c_str());
  174.         
  175.     if ( !in.good() ) {
  176.         ERR_POST(Info << "Mac arguments file not found: " << args_fname);
  177.         return;
  178.     }
  179.     vector<string> v;
  180.             
  181.     // remember or fake the executable name.
  182.     if (*argcPtr > 0) {
  183.         v.push_back((*argvPtr)[0]); // preserve the original argv[0].
  184.     } else {
  185.         v.push_back(exepath);
  186.     }
  187.     
  188.     // grab the rest of the arguments from the file.
  189.     // arguments are separated by whitespace. Can be on 
  190.     // more than one line.
  191.     string arg;
  192.     while (in >> arg) {
  193.         v.push_back(arg);
  194.     }
  195.             
  196.     // stash them away in the standard argc and argv places.
  197.     *argcPtr = v.size();
  198.     char** argv =  new char*[v.size()]; 
  199.     int c = 0;
  200.     ITERATE(vector<string>, vp, v) { 
  201.         argv[c++] = strdup(vp->c_str());
  202.     }
  203.     *argvPtr = argv;
  204. }
  205. #endif  /* NCBI_OS_DARWIN */
  206. int CNcbiApplication::AppMain
  207. (int                argc,
  208.  const char* const* argv,
  209.  const char* const* envp,
  210.  EAppDiagStream     diag,
  211.  const char*        conf,
  212.  const string&      name)
  213. {
  214.     x_SetupStdio();
  215.     // Get program executable's name & path.
  216.     string exepath = FindProgramExecutablePath(argc, argv);
  217.     
  218.     // Get program display name
  219.     string appname = name;
  220.     if (appname.empty()) {
  221.         if (!exepath.empty()) {
  222.             CDirEntry::SplitPath(exepath, NULL, &appname);
  223.         } else if (argc > 0  &&  argv[0] != NULL  &&  *argv[0] != '') {
  224.             CDirEntry::SplitPath(argv[0], NULL, &appname);
  225.         } else {
  226.             appname = "ncbi";
  227.         }
  228.     }
  229.     SetProgramDisplayName(appname);
  230.     
  231.     // Make sure we have something as our 'real' executable's name.
  232.     // though if it does not contain a full path it won't be much use.
  233.     if ( exepath.empty() ) {
  234.         ERR_POST(Warning
  235.                  << "Warning:  Could not determine this application's "
  236.                  "file name and location.  Using ""
  237.                  << appname << "" instead.n"
  238.                  "Please fix FindProgramExecutablePath() on this platform.");
  239.         exepath = appname;
  240.     }
  241.     
  242. #if defined(NCBI_OS_DARWIN)
  243.     // We do not know standard way of passing arguments to C++ program on Mac,
  244.     // so we will read arguments from special file having extension ".args"
  245.     // and name equal to display name of program (name argument of AppMain).
  246.     s_MacArgMunging(*this, &argc, &argv, exepath);
  247. #endif
  248.     // Check command line for presence special arguments
  249.     // "-logfile", "-conffile", "-version"
  250.     bool is_diag_setup = false;
  251.     if (!m_DisableArgDesc && argc > 1  &&  argv) {
  252.         const char** v = new const char*[argc];
  253.         v[0] = argv[0];
  254.         int real_arg_index = 1;
  255.         for (int i = 1;  i < argc;  i++) {
  256.             if ( !argv[i] ) {
  257.                 continue;
  258.             }
  259.             // Log file
  260.             if ( NStr::strcmp(argv[i], s_ArgLogFile) == 0 ) {
  261.                 if ( !argv[i++] ) {
  262.                     continue;
  263.                 }
  264.                 const char* log = argv[i];
  265.                 auto_ptr<CNcbiOfstream> os(new CNcbiOfstream(log));
  266.                 if ( !os->good() ) {
  267.                     _TRACE("CNcbiApplication() -- cannot open log file: "
  268.                            << log);
  269.                     continue;
  270.                 }
  271.                 _TRACE("CNcbiApplication() -- opened log file: " << log);
  272.                 // (re)direct the global diagnostics to the log.file
  273.                 CNcbiOfstream* os_log = os.release();
  274.                 SetDiagStream(os_log, true, s_DiagToStdlog_Cleanup,
  275.                               (void*) os_log);
  276.                 diag = eDS_ToStdlog;
  277.                 is_diag_setup = true;
  278.                 // Configuration file
  279.             } else if ( NStr::strcmp(argv[i], s_ArgCfgFile) == 0 ) {
  280.                 if ( !argv[i++] ) {
  281.                     continue;
  282.                 }
  283.                 conf = argv[i];
  284.                 // Version
  285.             } else if ( NStr::strcmp(argv[i], s_ArgVersion) == 0 ) {
  286.                 if ( !argv[i++] ) {
  287.                     continue;
  288.                 }
  289.                 // Print USAGE
  290.                 LOG_POST(appname + ": " + GetVersion().Print());
  291.                 return 0;
  292.                 // Save real argument
  293.             } else {
  294.                 v[real_arg_index++] = argv[i];
  295.             }
  296.         }
  297.         if (real_arg_index == argc ) {
  298.             delete[] v;
  299.         } else {
  300.             argc = real_arg_index;
  301.             argv = v;
  302.         }
  303.     }
  304.     // Reset command-line args and application name
  305.     m_Arguments->Reset(argc, argv, exepath);
  306.     // Reset application environment
  307.     m_Environ->Reset(envp);
  308.     // Setup some debugging features from environment variables.
  309.     if ( !m_Environ->Get(DIAG_TRACE).empty() ) {
  310.         SetDiagTrace(eDT_Enable, eDT_Enable);
  311.     }
  312.     string post_level = m_Environ->Get(DIAG_POST_LEVEL);
  313.     if ( !post_level.empty() ) {
  314.         EDiagSev sev;
  315.         if (CNcbiDiag::StrToSeverityLevel(post_level.c_str(), sev)) {
  316.             SetDiagFixedPostLevel(sev);
  317.         }
  318.     }
  319.     if ( !m_Environ->Get(ABORT_ON_THROW).empty() ) {
  320.         SetThrowTraceAbort(true);
  321.     }
  322.     // Clear registry content
  323.     m_Config->Clear();
  324.     // Setup for diagnostics
  325.     try {
  326.         if ( !is_diag_setup  &&  !SetupDiag(diag) ) {
  327.             ERR_POST("Application diagnostic stream's setup failed");
  328.         }
  329.     } catch (CException& e) {
  330.         NCBI_RETHROW(e, CAppException, eSetupDiag,
  331.                      "Application diagnostic stream's setup failed");
  332.     } catch (exception& e) {
  333.         NCBI_THROW(CAppException, eSetupDiag,
  334.                    "Application diagnostic stream's setup failed: " +
  335.                    string(e.what()));
  336.     }
  337.     // Load registry from the config file
  338.     try {
  339.         if ( conf ) {
  340.             string x_conf(conf);
  341.             LoadConfig(*m_Config, &x_conf);
  342.         } else {
  343.             LoadConfig(*m_Config, NULL);
  344.         }
  345.     } catch (CException& e) {
  346.         NCBI_RETHROW(e, CAppException, eLoadConfig,
  347.                      "Registry data cannot be loaded");
  348.     } catch (exception& e) {
  349.         NCBI_THROW(CAppException, eLoadConfig,
  350.                    "Registry data cannot be loaded:  " + string(e.what()));
  351.     }
  352.     // Setup the debugging features from the config file.
  353.     // Don't call till after LoadConfig()
  354.     // NOTE: this will override environment variables, 
  355.     // except DIAG_POST_LEVEL which is Set*Fixed*.
  356.     
  357.     HonorDebugSettings();
  358.     
  359.     // Call:  Init() + Run() + Exit()
  360.     int exit_code = 1;
  361.     try {
  362.         // Init application
  363.         try {
  364.             Init();
  365.             // If the app still has no arg description - provide default one
  366.             if (!m_DisableArgDesc  &&  !m_ArgDesc.get()) {
  367.                 auto_ptr<CArgDescriptions> arg_desc(new CArgDescriptions);
  368.                 arg_desc->SetUsageContext
  369.                     (GetArguments().GetProgramBasename(),
  370.                      "This program has no mandatory arguments");
  371.                 SetupArgDescriptions(arg_desc.release());
  372.             }
  373.         }
  374.         catch (CArgHelpException& ) {
  375.             if ( !m_DisableArgDesc ) {
  376.                 if ((m_HideArgs & fHideHelp) != 0)
  377.                     {
  378.                         if (m_ArgDesc->Exist("h")) {
  379.                             m_ArgDesc->Delete("h");
  380.                         }
  381.                     }
  382.                 if ((m_HideArgs & fHideLogfile) == 0  &&
  383.                     !m_ArgDesc->Exist(s_ArgLogFile + 1)) {
  384.                     m_ArgDesc->AddOptionalKey
  385.                         (s_ArgLogFile+1, "File_Name",
  386.                          "File to which the program log should be redirected",
  387.                          CArgDescriptions::eOutputFile);
  388.                 }
  389.                 if ((m_HideArgs & fHideConffile) == 0  &&
  390.                     !m_ArgDesc->Exist(s_ArgCfgFile + 1)) {
  391.                     m_ArgDesc->AddOptionalKey
  392.                         (s_ArgCfgFile + 1, "File_Name",
  393.                          "Program's configuration (registry) data file",
  394.                          CArgDescriptions::eInputFile);
  395.                 }
  396.                 if ((m_HideArgs & fHideVersion) == 0  &&
  397.                     !m_ArgDesc->Exist(s_ArgVersion + 1)) {
  398.                     m_ArgDesc->AddFlag
  399.                         (s_ArgVersion + 1,
  400.                          "Print version number;  ignore other arguments");
  401.                 }
  402.             }
  403.             // Print USAGE
  404.             string str;
  405.             LOG_POST(m_ArgDesc->PrintUsage(str));
  406.             exit_code = 0;
  407.         }
  408.         catch (CArgException& e) {
  409.             NCBI_RETHROW_SAME(e, "Application's initialization failed");
  410.         }
  411.         catch (CException& e) {
  412.             NCBI_REPORT_EXCEPTION("Application's initialization failed", e);
  413.             exit_code = -2;
  414.         }
  415.         catch (exception& e) {
  416.             ERR_POST("Application's initialization failed: " << e.what());
  417.             exit_code = -2;
  418.         }
  419.         // Run application
  420.         if (exit_code == 1) {
  421.             try {
  422.                 exit_code = Run();
  423.             }
  424.             catch (CArgException& e) {
  425.                 NCBI_RETHROW_SAME(e, "Application's execution failed");
  426.             }
  427.             catch (CException& e) {
  428.                 NCBI_REPORT_EXCEPTION("Application's execution failed", e);
  429.                 exit_code = -3;
  430.             }
  431.             catch (exception& e) {
  432.                 ERR_POST("Application's execution failed: " << e.what());
  433.                 exit_code = -3;
  434.             }
  435.         }
  436.         // Close application
  437.         try {
  438.             Exit();
  439.         }
  440.         catch (CArgException& e) {
  441.             NCBI_RETHROW_SAME(e, "Application's cleanup failed");
  442.         }
  443.         catch (CException& e) {
  444.             NCBI_REPORT_EXCEPTION("Application's cleanup failed", e);
  445.         }
  446.         catch (exception& e) {
  447.             ERR_POST("Application's cleanup failed: "<< e.what());
  448.         }
  449.     }
  450.     catch (CArgException& e) {
  451.         // Print USAGE and the exception error message
  452.         string str;
  453.         LOG_POST(string(72, '='));
  454.         if ( m_ArgDesc.get() ) {
  455.             LOG_POST(m_ArgDesc->PrintUsage(str) << string(72, '='));
  456.         }
  457.         NCBI_REPORT_EXCEPTION("", e);
  458.         exit_code = -1;
  459.     }
  460.     catch (...) {
  461.         // MSVC++ 6.0 in Debug mode does not call destructors when
  462.         // unwinding the stack unless the exception is caught at least
  463.         // somewhere.
  464.         ERR_POST(Warning <<
  465.                  "Application has thrown an exception of unknown type");
  466.         throw;
  467.     }
  468.     // Exit
  469.     return exit_code;
  470. }
  471. void CNcbiApplication::SetEnvironment(const string& name, const string& value)
  472. {
  473.     SetEnvironment().Set(name, value);
  474. }
  475. void CNcbiApplication::SetVersion(const CVersionInfo& version)
  476. {
  477.     m_Version.reset(new CVersionInfo(version));
  478. }
  479. CVersionInfo CNcbiApplication::GetVersion(void)
  480. {
  481.     return *m_Version;
  482. }
  483. void CNcbiApplication::SetupArgDescriptions(CArgDescriptions* arg_desc)
  484. {
  485.     m_ArgDesc.reset(arg_desc);
  486.     if ( arg_desc ) {
  487.         m_Args.reset(arg_desc->CreateArgs(GetArguments()));
  488.     } else {
  489.         m_Args.reset();
  490.     }
  491. }
  492. bool CNcbiApplication::SetupDiag(EAppDiagStream diag)
  493. {
  494.     // Setup diagnostic stream
  495.     switch ( diag ) {
  496.     case eDS_ToStdout: {
  497.         SetDiagStream(&NcbiCout);
  498.         break;
  499.     }
  500.     case eDS_ToStderr: {
  501.         SetDiagStream(&NcbiCerr);
  502.         break;
  503.     }
  504.     case eDS_ToStdlog: {
  505.         // open log.file
  506.         string log = m_Arguments->GetProgramName() + ".log";
  507.         auto_ptr<CNcbiOfstream> os(new CNcbiOfstream(log.c_str()));
  508.         if ( !os->good() ) {
  509.             _TRACE("CNcbiApplication() -- cannot open log file: " << log);
  510.             return false;
  511.         }
  512.         _TRACE("CNcbiApplication() -- opened log file: " << log);
  513.         // (re)direct the global diagnostics to the log.file
  514.         CNcbiOfstream* os_log = os.release();
  515.         SetDiagStream(os_log, true, s_DiagToStdlog_Cleanup, (void*) os_log);
  516.         break;
  517.     }
  518.     case eDS_ToMemory: {
  519.         // direct global diagnostics to the memory-resident output stream
  520.         if ( !m_DiagStream.get() ) {
  521.             m_DiagStream.reset(new CNcbiOstrstream);
  522.         }
  523.         SetDiagStream(m_DiagStream.get());
  524.         break;
  525.     }
  526.     case eDS_Disable: {
  527.         SetDiagStream(0);
  528.         break;
  529.     }
  530.     case eDS_User: {
  531.         // dont change current diag.stream
  532.         break;
  533.     }
  534.     case eDS_AppSpecific: {
  535.         return SetupDiag_AppSpecific();
  536.     }
  537.     case eDS_Default: {
  538.         if ( !IsSetDiagHandler() ) {
  539.             return CNcbiApplication::SetupDiag(eDS_AppSpecific);
  540.         }
  541.         // else eDS_User -- dont change current diag.stream
  542.         break;
  543.     }
  544.     default: {
  545.         _ASSERT(0);
  546.         break;
  547.     }
  548.     } // switch ( diag )
  549.     return true;
  550. }
  551. bool CNcbiApplication::SetupDiag_AppSpecific(void)
  552. {
  553.     return SetupDiag(eDS_ToStderr);
  554. }
  555. bool CNcbiApplication::LoadConfig(CNcbiRegistry&        reg,
  556.                                   const string*         conf,
  557.                                   CNcbiRegistry::TFlags reg_flags)
  558. {
  559.     string basename (m_Arguments->GetProgramBasename(eIgnoreLinks));
  560.     string basename2(m_Arguments->GetProgramBasename(eFollowLinks));
  561.     CMetaRegistry::SEntry entry;
  562.     if ( !conf ) {
  563.         return false;
  564.     } else if (conf->empty()) {
  565.         entry = CMetaRegistry::Load(basename, CMetaRegistry::eName_Ini,
  566.                                     CMetaRegistry::fDontOwn, reg_flags, &reg);
  567.         if ( !entry.registry  &&  basename2 != basename ) {
  568.             entry = CMetaRegistry::Load(basename2, CMetaRegistry::eName_Ini,
  569.                                         CMetaRegistry::fDontOwn, reg_flags,
  570.                                         &reg);
  571.         }
  572.     } else {
  573.         entry = CMetaRegistry::Load(*conf, CMetaRegistry::eName_AsIs,
  574.                                     CMetaRegistry::fDontOwn, reg_flags, &reg);
  575.     }
  576.     if ( !entry.registry ) {
  577.         // failed; complain as appropriate
  578.         string dir;
  579.         CDirEntry::SplitPath(*conf, &dir, 0, 0);
  580.         if (dir.empty()) {
  581.             ERR_POST(Warning <<
  582.                      "Registry file of application "" << basename
  583.                      << "" is not found");
  584.         } else {
  585.             NCBI_THROW(CAppException, eNoRegistry,
  586.                        "Registry file "" + *conf + "" cannot be opened");
  587.         }
  588.         return false;
  589.     } else if (entry.registry != &reg) {
  590.         // should be impossible with new CMetaRegistry interface...
  591.         if (&reg == m_Config  &&  reg.Empty()) {
  592.             if (m_OwnsConfig) {
  593.                 delete m_Config;
  594.             }
  595.             m_Config     = entry.registry;
  596.             m_OwnsConfig = false;
  597.         } else {
  598.             // copy into reg
  599.             CNcbiStrstream str;
  600.             entry.registry->Write(str);
  601.             str.seekg(0);
  602.             reg.Read(str);
  603.         }
  604.     }
  605.     return true;
  606. }
  607. bool CNcbiApplication::LoadConfig(CNcbiRegistry& reg,
  608.                                   const string*  conf)
  609. {
  610.     return LoadConfig(reg, conf, 0);
  611. }
  612. void CNcbiApplication::DisableArgDescriptions(void)
  613. {
  614.     m_DisableArgDesc = true;
  615. }
  616. void CNcbiApplication::HideStdArgs(THideStdArgs hide_mask)
  617. {
  618.     m_HideArgs = hide_mask;
  619. }
  620. void CNcbiApplication::SetStdioFlags(TStdioSetupFlags stdio_flags)
  621. {
  622.     // do not call this function more than once
  623.     // and from places other than App constructor
  624.     _ASSERT(m_StdioFlags == 0);
  625.     m_StdioFlags = stdio_flags;
  626. }
  627. void CNcbiApplication::x_SetupStdio(void)
  628. {
  629.     if ((m_StdioFlags & fDefault_SyncWithStdio) == 0) {
  630.         // SUN WorkShop STL stream library has significant performance loss
  631.         // when sync_with_stdio is true (default),
  632.         // so we turn off sync_with_stdio here.
  633.         IOS_BASE::sync_with_stdio(false);
  634.     }
  635.     if ((m_StdioFlags & fDefault_CinBufferSize) == 0
  636. #ifdef NCBI_OS_UNIX
  637.         &&  !isatty(0)
  638. #endif
  639.         ) {
  640. #if defined(NCBI_COMPILER_GCC)
  641. #  if NCBI_COMPILER_VERSION >= 300
  642.         _ASSERT(!m_CinBuffer);
  643.         // Ugly work around for G++/Solaris C RTL interaction
  644.         const size_t kCinBufSize = 5120;
  645.         m_CinBuffer = new char[kCinBufSize];
  646.         cin.rdbuf()->pubsetbuf(m_CinBuffer, kCinBufSize);
  647. #  endif
  648. #endif
  649.     }
  650. }
  651. void CNcbiApplication::SetProgramDisplayName(const string& app_name)
  652. {
  653.     m_ProgramDisplayName = app_name;
  654. }
  655. string CNcbiApplication::FindProgramExecutablePath
  656. (int              /*argc*/, 
  657.  const char* const* argv)
  658. {
  659.     string ret_val;
  660. #if defined (NCBI_OS_DARWIN)  &&  defined (NCBI_COMPILER_METROWERKS)
  661.     // We don't want to impose a dependency on Carbon when building
  662.     // with GCC, since linking the framework *at all* makes remote
  663.     // execution impossible. :-/
  664.     OSErr               err;
  665.     ProcessSerialNumber psn;
  666.     FSRef               fsRef;
  667.     char                filePath[1024];
  668.     
  669.     err = GetCurrentProcess(&psn);
  670.     if (err == noErr) {
  671.         err = GetProcessBundleLocation(&psn, &fsRef);
  672.         if (err == noErr) {
  673.             err = FSRefMakePath(&fsRef, (UInt8*) filePath, sizeof(filePath));
  674.         }
  675.     }    
  676.     ret_val = filePath;
  677.     
  678. #elif defined(NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
  679.     string app_path = argv[0];
  680. #  if defined (NCBI_OS_MSWIN)
  681.     // MS Windows: Try more accurate method of detection
  682.     try {
  683.         // Load PSAPI dynamic library -- it should exists on MS-Win NT/2000/XP
  684.         CDll dll_psapi("psapi.dll", CDll::eLoadNow, CDll::eAutoUnload);
  685.         // Get function entry-point from DLL
  686.         BOOL  (STDMETHODCALLTYPE FAR * dllEnumProcessModules) 
  687.                 (HANDLE  hProcess,     // handle to process
  688.                  HMODULE *lphModule,   // array of module handles
  689.                  DWORD   cb,           // size of array
  690.                  LPDWORD lpcbNeeded    // number of bytes required
  691.                  ) = NULL;
  692.         dllEnumProcessModules =
  693.             dll_psapi.GetEntryPoint_Func("EnumProcessModules",
  694.                                          &dllEnumProcessModules);
  695.         if ( !dllEnumProcessModules ) {
  696.             NCBI_THROW(CException, eUnknown, kEmptyStr);
  697.         }
  698.         // Find executable file in the midst of all loaded modules
  699.         HANDLE  process = GetCurrentProcess();
  700.         HMODULE module  = 0;
  701.         DWORD   needed  = 0;
  702.         // Get first module of current process (it should be .exe file)
  703.         if ( dllEnumProcessModules(process,
  704.                                    &module, sizeof(HMODULE), &needed) ) {
  705.             if ( needed  &&  module ) {
  706.                 char buf[MAX_PATH + 1];
  707.                 DWORD ncount = GetModuleFileName(module, buf, MAX_PATH);
  708.                 if ( ncount ) {
  709.                     buf[ncount] = 0;
  710.                     return buf;
  711.                 }
  712.             }
  713.         }
  714.     }
  715.     catch (CException) {
  716.         ; // Just catch an all exceptions from CDll
  717.     }
  718.     // This method don't work -- use standard method
  719. #  endif
  720. #  if defined(NCBI_OS_LINUX) && 0
  721.     // Linux OS: Try more accurate method of detection
  722.     {{
  723.         char   buf[FILENAME_MAX + 1];
  724.         string procfile = "/proc/" + NStr::IntToString(getpid()) + "/exe";
  725.         int    ncount   = readlink((procfile).c_str(), buf, FILENAME_MAX);
  726.         if ( ncount != -1 ) {
  727.             buf[ncount] = 0;
  728.             return buf;
  729.         }
  730.     }}
  731.     // This method don't work -- use standard method
  732. #  endif
  733.     if ( !CDirEntry::IsAbsolutePath(app_path) ) {
  734. #  if defined(NCBI_OS_MSWIN)
  735.         // Add default ".exe" extention to the name of executable file
  736.         // if it running without extension
  737.         string dir, title, ext;
  738.         CDirEntry::SplitPath(app_path, &dir, &title, &ext);
  739.         if ( ext.empty() ) {
  740.             app_path = CDirEntry::MakePath(dir, title, "exe");
  741.         }
  742. #  endif
  743.         if ( CFile(app_path).Exists() ) {
  744.             // Relative path from the the current directory
  745.             app_path = CDir::GetCwd() + CDirEntry::GetPathSeparator()+app_path;
  746.             if ( !CFile(app_path).Exists() ) {
  747.                 app_path = kEmptyStr;
  748.             }
  749.         } else {
  750.             // Running from some path from PATH environment variable.
  751.             // Try to determine that path.
  752.             string env_path = GetEnvironment().Get("PATH");
  753.             list<string> split_path;
  754. #  if defined(NCBI_OS_MSWIN)
  755.             NStr::Split(env_path, ";", split_path);
  756. #  else
  757.             NStr::Split(env_path, ":", split_path);
  758. #  endif
  759.             string base_name = CDirEntry(app_path).GetBase();
  760.             ITERATE(list<string>, it, split_path) {
  761.                 app_path = CDirEntry::MakePath(*it, base_name);
  762.                 if ( CFile(app_path).Exists() ) {
  763.                     break;
  764.                 }
  765.                 app_path = kEmptyStr;
  766.             }
  767.         }
  768.     }
  769.     ret_val = CDirEntry::NormalizePath(app_path.empty() ? argv[0] : app_path);
  770. #else  // defined (NCBI_OS_MSWIN)  ||  defined(NCBI_OS_UNIX)
  771. #  error "Platform unrecognized"
  772. #endif
  773.     return ret_val;
  774. }
  775. void CNcbiApplication::HonorDebugSettings(CNcbiRegistry* reg)
  776. {
  777.     if (reg == 0) {
  778.         reg = m_Config;
  779.         if (reg == 0)
  780.             return;
  781.     }
  782.     
  783.     // Setup the debugging features
  784.     if ( !reg->Get("DEBUG", DIAG_TRACE).empty() ) {
  785.         SetDiagTrace(eDT_Enable, eDT_Enable);
  786.     }
  787.     if ( !reg->Get("DEBUG", ABORT_ON_THROW).empty() ) {
  788.         SetThrowTraceAbort(true);
  789.     }
  790.     string post_level = reg->Get("DEBUG", DIAG_POST_LEVEL);
  791.     if ( !post_level.empty() ) {
  792.         EDiagSev sev;
  793.         if (CNcbiDiag::StrToSeverityLevel(post_level.c_str(), sev)) {
  794.             SetDiagFixedPostLevel(sev);
  795.         }
  796.     }
  797.     string msg_file = reg->Get("DEBUG", DIAG_MESSAGE_FILE);
  798.     if ( !msg_file.empty() ) {
  799.         CDiagErrCodeInfo* info = new CDiagErrCodeInfo();
  800.         if ( !info  ||  !info->Read(msg_file) ) {
  801.             if ( info ) {
  802.                 delete info;
  803.             }
  804.             ERR_POST(Warning << "Applications message file ""
  805.                      << msg_file
  806.                      << "" is not found");
  807.         } else {
  808.             SetDiagErrCodeInfo(info);
  809.         }
  810.     }
  811. }
  812. END_NCBI_SCOPE
  813. /*
  814.  * ===========================================================================
  815.  * $Log: ncbiapp.cpp,v $
  816.  * Revision 1000.5  2004/06/03 19:28:14  gouriano
  817.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.85
  818.  *
  819.  * Revision 1.85  2004/06/02 20:45:27  vakatov
  820.  * CNcbiApplication::AppMain() not to printout line of '=' before USAGE
  821.  *
  822.  * Revision 1.84  2004/05/14 13:59:26  gorelenk
  823.  * Added include of ncbi_pch.hpp
  824.  *
  825.  * Revision 1.83  2004/03/02 15:57:34  ucko
  826.  * Downgrade severity of missing Mac arguments file to Info, since Darwin
  827.  * allows actual command lines.
  828.  *
  829.  * Revision 1.82  2004/01/06 18:17:49  dicuccio
  830.  * Added APIs for setting environment variables
  831.  *
  832.  * Revision 1.81  2003/12/17 20:25:46  ucko
  833.  * x_SetupStdio: for the sake of interactive applications, don't buffer
  834.  * cin if it's a terminal.
  835.  *
  836.  * Revision 1.80  2003/11/21 21:03:37  vakatov
  837.  * Cosmetics
  838.  *
  839.  * Revision 1.79  2003/11/21 20:12:20  kuznets
  840.  * Minor clean-up
  841.  *
  842.  * Revision 1.78  2003/11/21 19:53:58  kuznets
  843.  * Added catch(...) to intercept all exceptions and give compiler
  844.  * (MSVC) a chance to call destructors even if this exception
  845.  * will never be handled and causes crash.
  846.  *
  847.  * Revision 1.77  2003/11/19 13:54:19  ivanov
  848.  * FindProgramExecutablePath: Replaced GetEntryPoint with GetEntryPoint_Func
  849.  *
  850.  * Revision 1.76  2003/10/10 19:37:13  lavr
  851.  * Ugly workaround for G++/Solaris C RTL (FILE*) interactions that affect
  852.  * stdio constructed on pipes (sockets).  To be fully investigated later...
  853.  *
  854.  * Revision 1.75  2003/10/01 14:32:09  ucko
  855.  * +EFollowLinks
  856.  *
  857.  * Revision 1.74  2003/09/30 21:12:30  ucko
  858.  * CNcbiApplication::LoadConfig: if run through a symlink and there's no
  859.  * configuration file for the link's source, try its (ultimate) target.
  860.  *
  861.  * Revision 1.73  2003/09/29 20:28:00  vakatov
  862.  * + LoadConfig(...., reg_flags)
  863.  *
  864.  * Revision 1.72  2003/09/25 19:34:51  ucko
  865.  * FindProgramExecutablePath: disable Linux-specific logic, since it
  866.  * loses the ability to detect having been run through a symlink.
  867.  *
  868.  * Revision 1.71  2003/09/25 13:33:58  rsmith
  869.  * NCBI_OS_DARWIN and NCBI_OS_UNIX are not mutually exclusive.
  870.  *
  871.  * Revision 1.70  2003/09/22 13:43:41  ivanov
  872.  * Include <corelib/ncbidll.hpp> only on MSWin. + include <corelib/ncbi_os_mswin.hpp>
  873.  *
  874.  * Revision 1.69  2003/09/17 21:03:58  ivanov
  875.  * FindProgramExecutablePath:  try more accurate method of detection on MS Windows
  876.  *
  877.  * Revision 1.68  2003/09/16 20:55:33  ivanov
  878.  * FindProgramExecutablePath:  try more accurate method of detection on Linux.
  879.  *
  880.  * Revision 1.67  2003/09/16 16:35:38  ivanov
  881.  * FindProgramExecutablePath(): added implementation for UNIX and MS-Windows
  882.  *
  883.  * Revision 1.66  2003/08/06 20:27:17  ucko
  884.  * LoadConfig: take advantage of the latest changes to CMetaRegistry to
  885.  * reuse reg.
  886.  *
  887.  * Revision 1.65  2003/08/06 14:31:55  ucko
  888.  * LoadConfig: Only replace m_Config if it was empty, and remember to
  889.  * delete the old object if we owned it.
  890.  *
  891.  * Revision 1.64  2003/08/05 20:00:36  ucko
  892.  * Completely rewrite LoadConfig to use CMetaRegistry; properly handle
  893.  * only sometimes owning m_Config.
  894.  *
  895.  * Revision 1.63  2003/06/26 18:55:40  rsmith
  896.  * call LoadConfig even if conf is null, child classes might do something
  897.  * anyway. (gbench).
  898.  *
  899.  * Revision 1.62  2003/06/25 15:59:00  rsmith
  900.  * factor out config file DEBUG settings into HonorDebugSettings
  901.  *
  902.  * Revision 1.61  2003/06/23 18:02:31  vakatov
  903.  * CNcbiApplication::MacArgMunging() moved from header to the source file.
  904.  * Heed warnings, formally reformat the code and comments.
  905.  *
  906.  * Revision 1.60  2003/06/18 20:41:23  ucko
  907.  * FindProgramExecutablePath: conditionalize the Darwin-specific code on
  908.  * Metrowerks, since no executable linked against Carbon can run remotely. :-/
  909.  *
  910.  * Revision 1.59  2003/06/16 13:52:27  rsmith
  911.  * Add ProgramDisplayName member. Program name becomes real executable full
  912.  * path. Handle Mac special arg handling better.
  913.  *
  914.  * Revision 1.58  2003/04/07 21:22:54  gouriano
  915.  * correct App exit codes when CException was catched
  916.  *
  917.  * Revision 1.57  2003/03/19 19:37:10  gouriano
  918.  * added optional adjustment of stdio streams
  919.  *
  920.  * Revision 1.56  2003/03/14 21:01:06  kans
  921.  * commented out include Processes because Mac OS 10.2 libraries give an
  922.  * undefined symbol error
  923.  *
  924.  * Revision 1.55  2003/03/14 20:35:24  rsmith
  925.  * Comment out previous changes for Mac OSX since on 10.2 the header files
  926.  * do not compile properly.
  927.  *
  928.  * Revision 1.54  2003/03/13 22:16:11  rsmith
  929.  * on Metrowerks, Mac OSX, always use ncbi.args as the file name for arguments.
  930.  *
  931.  * Revision 1.53  2003/03/13 22:04:27  rsmith
  932.  * Changes to AppMain so MacOSX can find out the appname and arguments.
  933.  *
  934.  * Revision 1.52  2003/02/17 06:30:05  vakatov
  935.  * Get rid of an unused variable
  936.  *
  937.  * Revision 1.51  2002/12/26 17:13:03  ivanov
  938.  * Added version info and Set/GetVersion functions into CNcbiApplication class
  939.  *
  940.  * Revision 1.50  2002/12/18 22:54:48  dicuccio
  941.  * Shuffled some headers to avoid a potentially serious compiler warning
  942.  * (deletion of incomplete type in Windows).
  943.  *
  944.  * Revision 1.49  2002/08/08 18:36:50  gouriano
  945.  * added HideStdArgs function
  946.  *
  947.  * Revision 1.48  2002/08/08 13:39:06  gouriano
  948.  * logfile & conffile-related correction
  949.  *
  950.  * Revision 1.47  2002/08/02 20:13:06  gouriano
  951.  * added possibility to disable arg descriptions
  952.  *
  953.  * Revision 1.46  2002/08/01 19:02:17  ivanov
  954.  * Added autoload of the verbose message file specified in the
  955.  * applications configuration file.
  956.  *
  957.  * Revision 1.45  2002/07/31 18:33:44  gouriano
  958.  * added default argument description,
  959.  * added info about logfile and conffile optional arguments
  960.  *
  961.  * Revision 1.44  2002/07/22 19:33:28  ivanov
  962.  * Fixed bug with internal processing parameters -conffile and -logfile
  963.  *
  964.  * Revision 1.43  2002/07/15 18:17:23  gouriano
  965.  * renamed CNcbiException and its descendents
  966.  *
  967.  * Revision 1.42  2002/07/11 14:18:25  gouriano
  968.  * exceptions replaced by CNcbiException-type ones
  969.  *
  970.  * Revision 1.41  2002/07/10 16:20:13  ivanov
  971.  * Changes related with renaming
  972.  * SetDiagFixedStrPostLevel()->SetDiagFixedPostLevel()
  973.  *
  974.  * Revision 1.40  2002/07/09 16:36:52  ivanov
  975.  * s_SetFixedDiagPostLevel() moved to ncbidiag.cpp
  976.  * and renamed to SetDiagFixedStrPostLevel()
  977.  *
  978.  * Revision 1.39  2002/07/02 18:31:38  ivanov
  979.  * Added assignment the value of diagnostic post level from environment
  980.  * variable and config file if set. Disable to change it from application
  981.  * in this case.
  982.  *
  983.  * Revision 1.38  2002/06/19 16:57:56  ivanov
  984.  * Added new default command line parameters "-logfile <file>" and
  985.  * "-conffile <file>" into CNcbiApplication::AppMain()
  986.  *
  987.  * Revision 1.37  2002/04/11 21:08:00  ivanov
  988.  * CVS log moved to end of the file
  989.  *
  990.  * Revision 1.36  2002/01/22 19:28:37  ivanov
  991.  * Changed ConcatPath() -> ConcatPathEx() in LoadConfig()
  992.  *
  993.  * Revision 1.35  2002/01/20 05:53:59  vakatov
  994.  * Get rid of a GCC warning;  formally rearrange some code.
  995.  *
  996.  * Revision 1.34  2002/01/10 16:52:20  ivanov
  997.  * Changed LoadConfig() -- new method to search the config file
  998.  *
  999.  * Revision 1.33  2001/08/20 14:58:10  vakatov
  1000.  * Get rid of some compilation warnings
  1001.  *
  1002.  * Revision 1.32  2001/08/10 18:26:07  vakatov
  1003.  * Allow user to throw "CArgException" (and thus force USAGE printout
  1004.  * with user-provided explanation) not from any of Init(), Run() or Exit().
  1005.  *
  1006.  * Revision 1.31  2001/07/07 01:19:28  juran
  1007.  * Use "ncbi" for app name on Mac OS (if argv[0] is null).
  1008.  *
  1009.  * Revision 1.30  2001/03/26 20:07:40  vakatov
  1010.  * [NCBI_OS_MAC]  Use argv[0] (if available) as basename for ".args"
  1011.  *
  1012.  * Revision 1.29  2001/02/02 16:19:27  vasilche
  1013.  * Fixed reading program arguments on Mac
  1014.  *
  1015.  * Revision 1.28  2001/02/01 19:53:26  vasilche
  1016.  * Reading program arguments from file moved to CNcbiApplication::AppMain.
  1017.  *
  1018.  * Revision 1.27  2000/12/23 05:50:53  vakatov
  1019.  * AppMain() -- check m_ArgDesc for NULL
  1020.  *
  1021.  * Revision 1.26  2000/11/29 17:00:25  vakatov
  1022.  * Use LOG_POST instead of ERR_POST to print cmd.-line arg usage
  1023.  *
  1024.  * Revision 1.25  2000/11/24 23:33:12  vakatov
  1025.  * CNcbiApplication::  added SetupArgDescriptions() and GetArgs() to
  1026.  * setup cmd.-line argument description, and then to retrieve their
  1027.  * values, respectively. Also implements internal error handling and
  1028.  * printout of USAGE for the described arguments.
  1029.  *
  1030.  * Revision 1.24  2000/11/01 20:37:15  vasilche
  1031.  * Fixed detection of heap objects.
  1032.  * Removed ECanDelete enum and related constructors.
  1033.  * Disabled sync_with_stdio ad the beginning of AppMain.
  1034.  *
  1035.  * Revision 1.23  2000/06/09 18:41:04  vakatov
  1036.  * FlushDiag() -- check for empty diag.buffer
  1037.  *
  1038.  * Revision 1.22  2000/04/04 22:33:35  vakatov
  1039.  * Auto-set the tracing and the "abort-on-throw" debugging features
  1040.  * basing on the application environment and/or registry
  1041.  *
  1042.  * Revision 1.21  2000/01/20 17:51:18  vakatov
  1043.  * Major redesign and expansion of the "CNcbiApplication" class to 
  1044.  *  - embed application arguments   "CNcbiArguments"
  1045.  *  - embed application environment "CNcbiEnvironment"
  1046.  *  - allow auto-setup or "by choice" (see enum EAppDiagStream) of diagnostics
  1047.  *  - allow memory-resided "per application" temp. diagnostic buffer
  1048.  *  - allow one to specify exact name of the config.-file to load, or to
  1049.  *    ignore the config.file (via constructor's "conf" arg)
  1050.  *  - added detailed comments
  1051.  *
  1052.  * Revision 1.20  1999/12/29 21:20:18  vakatov
  1053.  * More intelligent lookup for the default config.file. -- try to strip off
  1054.  * file extensions if cannot find an exact match;  more comments and tracing
  1055.  *
  1056.  * Revision 1.19  1999/11/15 15:54:59  sandomir
  1057.  * Registry support moved from CCgiApplication to CNcbiApplication
  1058.  *
  1059.  * Revision 1.18  1999/06/11 20:30:37  vasilche
  1060.  * We should catch exception by reference, because catching by value
  1061.  * doesn't preserve comment string.
  1062.  *
  1063.  * Revision 1.17  1999/05/04 00:03:12  vakatov
  1064.  * Removed the redundant severity arg from macro ERR_POST()
  1065.  *
  1066.  * Revision 1.16  1999/04/30 19:21:03  vakatov
  1067.  * Added more details and more control on the diagnostics
  1068.  * See #ERR_POST, EDiagPostFlag, and ***DiagPostFlag()
  1069.  *
  1070.  * Revision 1.15  1999/04/27 14:50:06  vasilche
  1071.  * Added FastCGI interface.
  1072.  * CNcbiContext renamed to CCgiContext.
  1073.  *
  1074.  * Revision 1.14  1999/02/22 21:12:38  sandomir
  1075.  * MsgRequest -> NcbiContext
  1076.  *
  1077.  * Revision 1.13  1998/12/28 23:29:06  vakatov
  1078.  * New CVS and development tree structure for the NCBI C++ projects
  1079.  *
  1080.  * Revision 1.12  1998/12/28 15:43:12  sandomir
  1081.  * minor fixed in CgiApp and Resource
  1082.  *
  1083.  * Revision 1.11  1998/12/14 15:30:07  sandomir
  1084.  * minor fixes in CNcbiApplication; command handling fixed
  1085.  *
  1086.  * Revision 1.10  1998/12/09 22:59:35  lewisg
  1087.  * use new cgiapp class
  1088.  *
  1089.  * Revision 1.7  1998/12/09 16:49:56  sandomir
  1090.  * CCgiApplication added
  1091.  *
  1092.  * Revision 1.4  1998/12/03 21:24:23  sandomir
  1093.  * NcbiApplication and CgiApplication updated
  1094.  *
  1095.  * Revision 1.3  1998/12/01 19:12:08  lewisg
  1096.  * added CCgiApplication
  1097.  *
  1098.  * Revision 1.2  1998/11/05 21:45:14  sandomir
  1099.  * std:: deleted
  1100.  *
  1101.  * Revision 1.1  1998/11/02 22:10:13  sandomir
  1102.  * CNcbiApplication added; netest sample updated
  1103.  *
  1104.  * ===========================================================================
  1105.  */