LCOV - code coverage report
Current view: top level - source/app - clOptions.cpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 58.7 % 121 71
Test Date: 2026-08-30 22:23:37 Functions: 81.8 % 11 9

            Line data    Source code
       1              : /** \file clOptions.cpp
       2              :  * \author Jared R. Males
       3              :  * \brief Implementatino of a command line parser
       4              :  *
       5              :  * \ingroup mxApp_files
       6              :  */
       7              : 
       8              : //***********************************************************************//
       9              : // Copyright 2021 Jared R. Males (jaredmales@gmail.com)
      10              : //
      11              : // This file is part of mxlib.
      12              : //
      13              : // mxlib is free software: you can redistribute it and/or modify
      14              : // it under the terms of the GNU General Public License as published by
      15              : // the Free Software Foundation, either version 3 of the License, or
      16              : // (at your option) any later version.
      17              : //
      18              : // mxlib is distributed in the hope that it will be useful,
      19              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      20              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21              : // GNU General Public License for more details.
      22              : //
      23              : // You should have received a copy of the GNU General Public License
      24              : // along with mxlib.  If not, see <http://www.gnu.org/licenses/>.
      25              : //***********************************************************************//
      26              : 
      27              : #include "app/clOptions.hpp"
      28              : 
      29              : namespace mx
      30              : {
      31              : namespace app
      32              : {
      33              : 
      34              : static const char *falseStr = "false";
      35              : static const char *trueStr = "true";
      36              : static const char *blankStr = "";
      37              : 
      38           54 : static option::ArgStatus Arg_Required( const option::Option &option, bool msg )
      39              : {
      40              :     static_cast<void>( msg );
      41              : 
      42           54 :     if( option.arg != 0 )
      43           54 :         return option::ARG_OK;
      44              : 
      45            0 :     return option::ARG_ILLEGAL;
      46              : }
      47              : 
      48           24 : clOptions::~clOptions()
      49              : {
      50           24 :     if( options )
      51           24 :         delete[] options;
      52           24 :     if( buffer )
      53           24 :         delete[] buffer;
      54           24 : }
      55              : 
      56            0 : void clOptions::clear()
      57              : {
      58            0 :     if( options )
      59            0 :         delete[] options;
      60            0 :     options = 0;
      61              : 
      62            0 :     if( buffer )
      63            0 :         delete[] buffer;
      64            0 :     buffer = 0;
      65              : 
      66            0 :     map.clear();
      67            0 :     typeMap.clear();
      68            0 :     descriptions.clear();
      69            0 : }
      70              : 
      71           98 : void clOptions::add( const std::string &optName, const char *const shortOpt, const char *const longOpt, int argT )
      72              : {
      73           98 :     mapIterator it;
      74           98 :     it = map.find( optName );
      75              : 
      76           98 :     if( it == map.end() )
      77              :     {
      78           98 :         if( argT == argType::Optional )
      79              :         {
      80            0 :             descriptions.push_back( { nOpts, argT, shortOpt, longOpt, option::Arg::Optional, "" } );
      81              :         }
      82           98 :         else if( argT == argType::Required )
      83              :         {
      84           78 :             descriptions.push_back( { nOpts, argT, shortOpt, longOpt, Arg_Required, "" } );
      85              :         }
      86              :         else
      87              :         {
      88           20 :             descriptions.push_back( { nOpts, argT, shortOpt, longOpt, option::Arg::None, "" } );
      89              :         }
      90              : 
      91           98 :         map.insert( { optName, nOpts } );
      92           98 :         typeMap.insert( { optName, argT } );
      93              : 
      94           98 :         ++nOpts;
      95           98 :         return;
      96              :     }
      97              : }
      98              : 
      99           24 : void clOptions::parse( int argc, char **argv, std::vector<std::string> *nonOptions )
     100              : {
     101           24 :     argc -= ( argc > 0 );
     102           24 :     argv += ( argc > 0 ); // skip program name argv[0] if present
     103              : 
     104              :     // If not already done, we push the unknown catcher and the termination descriptor.
     105           24 :     if( descriptions.size() == 0 || descriptions.back().shortopt != 0 )
     106              :     {
     107              :         // This is inserted to catch unknown options
     108           24 :         descriptions.push_back( { nOpts, 0, "", "", option::Arg::None, "" } );
     109           24 :         descriptions.push_back( { 0, 0, 0, 0, 0, 0 } );
     110              :     }
     111              : 
     112              :     // Now allocate.
     113           24 :     option::Stats stats( descriptions.data(), argc, argv );
     114          170 :     options = new option::Option[stats.options_max];
     115           80 :     buffer = new option::Option[stats.buffer_max];
     116              : 
     117           24 :     option::Parser parse( false, descriptions.data(), argc, argv, options, buffer );
     118              : 
     119           24 :     if( nonOptions )
     120              :     {
     121           24 :         nonOptions->resize( parse.nonOptionsCount() );
     122              : 
     123           24 :         for( int i = 0; i < parse.nonOptionsCount(); ++i )
     124              :         {
     125            0 :             ( *nonOptions )[i] = parse.nonOption( i );
     126              :         }
     127              :     }
     128           24 : }
     129              : 
     130            0 : const char *clOptions::operator[]( const std::string &key )
     131              : {
     132            0 :     mapIterator it = map.find( key );
     133              : 
     134            0 :     if( it == map.end() )
     135              :     {
     136            0 :         std::cerr << "oh no\n";
     137            0 :         return 0;
     138              :     }
     139              : 
     140            0 :     int typeDesc = typeMap[key];
     141              : 
     142              :     // If this option is not pressent, either return the opposite T/F condition or blank
     143            0 :     if( options[it->second].type() == argType::None )
     144              :     {
     145            0 :         if( typeDesc == argType::False )
     146            0 :             return trueStr;
     147            0 :         if( typeDesc == argType::True )
     148            0 :             return falseStr;
     149            0 :         return blankStr;
     150              :     }
     151              : 
     152            0 :     if( typeDesc == argType::False || typeDesc == argType::True )
     153              :     {
     154            0 :         if( options[it->second].last()->type() == argType::False )
     155            0 :             return falseStr;
     156              :         else
     157            0 :             return trueStr;
     158              :     }
     159              : 
     160            0 :     if( options[it->second].arg == 0 )
     161            0 :         return blankStr;
     162              : 
     163            0 :     return options[it->second].last()->arg;
     164              : }
     165              : 
     166           19 : int clOptions::count( const std::string &key )
     167              : {
     168           19 :     mapIterator it = map.find( key );
     169              : 
     170           19 :     if( it == map.end() )
     171            0 :         return -1;
     172              : 
     173           19 :     return options[it->second].count();
     174              : }
     175              : 
     176           19 : void clOptions::getAll( std::vector<std::string> &args, const std::string &key )
     177              : {
     178           19 :     mapIterator it = map.find( key );
     179              : 
     180           19 :     if( it == map.end() )
     181              :     {
     182            0 :         std::cerr << "oh no\n";
     183            3 :         return;
     184              :     }
     185              : 
     186           19 :     int typeDesc = typeMap[key];
     187              : 
     188              :     // If this option is not present, either return the opposite T/F condition or blank
     189           19 :     if( options[it->second].type() == argType::None )
     190              :     {
     191              : 
     192            0 :         if( typeDesc == argType::False )
     193              :         {
     194            0 :             args.resize( 1 );
     195            0 :             args[0] = trueStr;
     196            0 :             return;
     197              :         }
     198              : 
     199            0 :         if( typeDesc == argType::True )
     200              :         {
     201            0 :             args.resize( 1 );
     202            0 :             args[0] = falseStr;
     203            0 :             return;
     204              :         }
     205              : 
     206            0 :         args.clear();
     207              : 
     208            0 :         return;
     209              :     }
     210              : 
     211           19 :     if( typeDesc == argType::False || typeDesc == argType::True )
     212              :     {
     213            3 :         args.resize( 1 );
     214              : 
     215            3 :         if( options[it->second].last()->type() == argType::False )
     216            0 :             args[0] = falseStr;
     217              :         else
     218            3 :             args[0] = trueStr;
     219            3 :         return;
     220              :     }
     221              : 
     222           16 :     if( options[it->second].arg == 0 )
     223              :     {
     224            0 :         args.clear();
     225            0 :         return;
     226              :     }
     227              : 
     228           16 :     int N = options[it->second].count();
     229              : 
     230           16 :     args.resize( N );
     231              : 
     232           16 :     int i = 0;
     233              : 
     234           32 :     for( option::Option *opt = options[it->second]; opt != NULL && i < N; opt = opt->next() )
     235              :     {
     236           16 :         args[i] = opt->arg;
     237           16 :         ++i;
     238              :     }
     239              : }
     240              : 
     241           71 : bool clOptions::optSet( const std::string &key )
     242              : {
     243           71 :     mapIterator it = map.find( key );
     244              : 
     245           71 :     if( it == map.end() )
     246            0 :         return false; // Not found --> e.g. if neither command line short nor long option set.
     247              : 
     248           71 :     if( options[it->second].type() != argType::None )
     249           19 :         return true;
     250           52 :     return false;
     251              : };
     252              : 
     253           26 : int clOptions::numUnknown()
     254              : {
     255              :     // The dummy description to catch unknown options is inserted at position nOpts.
     256           26 :     return options[nOpts].count();
     257              : }
     258              : 
     259            2 : int clOptions::unknown( std::vector<std::string> &unk )
     260              : {
     261            2 :     unk.clear();
     262              : 
     263            2 :     if( numUnknown() == 0 )
     264            0 :         return 0;
     265              : 
     266            4 :     for( option::Option *opt = options[nOpts]; opt != NULL; opt = opt->next() )
     267              :     {
     268            4 :         unk.push_back( opt->name );
     269              :     }
     270              : 
     271            2 :     return 0;
     272              : }
     273              : 
     274              : } // namespace app
     275              : } // namespace mx
        

Generated by: LCOV version 2.0-1