cmdlineopt.cpp

// Copyright (C) 2001 Gianni Mariani
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception to the GNU General Public License, permission is
// granted for additional uses of the text contained in its release
// of Common C++.
//
// The exception is that, if you link the Common C++ library with other
// files to produce an executable, this does not by itself cause the
// resulting executable to be covered by the GNU General Public License.
// Your use of that executable is in no way restricted on account of
// linking the Common C++ library code into it.
//
// This exception does not however invalidate any other reasons why
// the executable file might be covered by the GNU General Public License.
//
// This exception applies only to the code released under the
// name Common C++.  If you copy code from other releases into a copy of
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//


//
// Example for Common C++ the command line parser interface.
//
//
// This exmaple code shows how to use the command line parser provided by
// CommonC++.  The command line parser provides an interface which is
// "object oriented" such that command line parameters are true "objects".
//
// Each command line option needs to be created.  By defining "CommandOption"s
// statically, the C++ constructor is called when the objects are loaded and
// before the "main" function is called.  The constructor links itself to
// a list of other CommandOptionXXX in the list provided.  If no
// list is specified in the constructor, a default one is used. Because of
// the undefined nature as to the order in which constructors are called,
// no assumption as to the order in which the CommandOptionXXX constructors
// are called should be made.
//
// CommandOptionXXX classes can be used to derive specialized parameter
// classes that are specific to applications.  The second example shows
// just how this can be done.
//

//
// Include the CommandOption definitions
//
#include <cc++/common.h>

#include <iostream>
#ifndef WIN32
#include <cstdlib>
#endif

#ifdef  CCXX_NAMESPACES
using namespace std;
using namespace ost;
#endif

//
// The following definition of options all use the list header
// defaultCommandOptionList (which is specified as the value of the
// default parameter in the constructor.  This convention would
// allow other object files to link into the same list and add parameters
// to the command line of this executable.

CommandOptionArg        test_option1(
        "test_option1", "p", "This option takes an argument", true
);

CommandOptionNoArg      test_noarg(
        "test_noarg", "b", "This option does not take an argument"
);

CommandOptionNoArg      helparg(
        "help", "?", "Print help usage"
);

CommandOptionCollect    restoargs(
        0, 0, "Collect all the parameters", true
);


//
// Normally this would me the regular main().  In this example
// this processes the first command option list.
//
int Example_main( int argc, char ** argv )
{

        // Create a CommandOptionParse object.  This takes the
        // defaultCommandOptionList and parses the command line arguments.
        //
        CommandOptionParse * args = makeCommandOptionParse(
                argc, argv,
                "CommonC++ command like option interface.  This is example\n"
                "       code only."
        );

        // If the user requested help then suppress all the usage error
        // messages.
        if ( helparg.numSet ) {
                cerr << args->printUsage();
                ::exit(0);
        }

        // Print usage your way.
        if ( args->argsHaveError() ) {
                cerr << args->printErrors();
                cerr << args->printUsage();
                ::exit(1);
        }

        // Go off and run any option specific task
        args->performTask();

        // print all the -p options
        for ( int i = 0; i < test_option1.numValue; i ++ ) {
                cerr << "test_option1 = " << test_option1.values[ i ] << endl;
        }

        // print all the other options.
        for ( int i = 0; i < restoargs.numValue; i ++ ) {
                cerr << "restoargs " << i << " : " << restoargs.values[ i ] << endl;
        }

        delete args;

        return 0;
}


//
// This shows how to build a second option list.  The example is similar to
// the first as well as it shows how to derive a new command object.
//

CommandOption * TestList = 0;

extern CommandOptionRest        test_restoargs;


#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strstream>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>


//
// This is a parameter class derived from CommandOptionArg that takes
// a file name parameter and detects wether the file is accessible
// flagging an error if the file is inaccessible to read.
//
class file_option : public CommandOptionArg {
public:

        // the constructor calls the regular CommandOptionArg constructor
        // and all should be well.
        file_option(
                const char      * in_option_name,
                const char      * in_option_letter,
                const char      * in_description,
                bool              in_required = false,
                CommandOption  ** pp_next = & defaultCommandOptionList
        )
                : CommandOptionArg(
                        in_option_name,
                        in_option_letter,
                        in_description,
                        in_required,
                        pp_next
                )
        {
        }

        //
        // When parsing is done check if the file is accessible and register
        // an error with the CommandOptionParse object to let it know so.
        virtual void parseDone( CommandOptionParse * cop ) {
                if ( numValue ) {
                        if ( ::access( values[ numValue - 1 ], R_OK ) ) {
                                int     errno_s = errno;
                                strstream msg;
                                msg << "Error: " << optionName << " '" << values[ numValue - 1 ];
                                msg << "' : " << ::strerror( errno_s );

                                cop->registerError( msg.str() );
                        }
                }
        }

        //
        // Open said file.  Do some operations on things - like open the file.
        int OpenFile() {
                // Should put in way more error handling here ...
                return ::open( values[ numValue - 1 ], O_RDONLY );
        }

        //
        // The most elaborate way to spit the contents of a file
        // to standard output.
        pid_t   pid;
        virtual void performTask( CommandOptionParse * cop ) {
                pid = ::fork();

                if ( pid ) {
                        return;
                }

                int fd = OpenFile();
                if ( fd < 0 ) {
                        int errno_s = errno;
                        cerr
                                << "Error:  '"
                                << values[ numValue - 1 ]
                                << "' : "
                                << ::strerror( errno_s )
                        ;

                        ::exit( 1 );
                }
                dup2(fd, 0);
                ::execvp( test_restoargs.values[0], (char**) test_restoargs.values );
                ::exit(1);
        }

        ~file_option() {
                if ( pid <= 0 ) return;
                int status;
		::wait(&status);
        }
};


//
// This is the linked list head for the options in the second example.
// Note that the first example used the default value defined in the
// method.  Here it is explicitly specified as TestList in all the following
// CommandOption constructors.

file_option     test_file(
        "test_file", "f", "Filename to read from", true, &TestList
);

CommandOptionNoArg      test_xnoarg(
        "test_xnoarg", "b", "This option does not take an argument", false, &TestList
);

CommandOptionNoArg      test_helparg(
        "help", "?", "Print help usage", false, &TestList
);

CommandOptionRest       test_restoargs(
        0, 0, "Command to be executed", true, &TestList
);

//
// in most apps this would be the regular "main" function.
int Test_main( int argc, char ** argv )
{
        CommandOptionParse * args = makeCommandOptionParse(
                argc, argv,
                "Command line parser X test.\n"
                "       This example is executed when the command ends in 'x'\n"
                "       It shows how the -f parameter can be specialized.\n",
                TestList
        );

        // If the user requested help then suppress all the usage error
        // messages.
        if ( test_helparg.numSet ) {
                cerr << args->printUsage();
                ::exit(0);
        }

        // Print usage your way.
        if ( args->argsHaveError() ) {
                cerr << args->printErrors();
                cerr << "Get help by --help\n";
                ::exit(1);
        }

        // Go off and run any option specific task
        args->performTask();

        for ( int i = 0; i < test_file.numValue; i ++ ) {
                cerr << "test_file = " << test_file.values[ i ] << endl;
        }

        for ( int i = 0; i < test_restoargs.numValue; i ++ ) {
                cerr << "test_restoargs " << i << " : " << test_restoargs.values[ i ] << endl;
        }

        delete args;

        return 0;
}


//
// This switches behaviour of this executable depending of wether it is
// invoked with a command ending in "x".  This is mimicking for example
// the behaviour of bunzip2 and bzip2.  These executables are THE SAME
// file i.e.
//   0 lrwxrwxrwx    1 root     root    5 Oct 11 14:04 /usr/bin/bunzip2 -> bzip2*
// and the behaviour is determined by the executable name.
//
// This example is way more complex than the way most people will end up
// using feature.

int main( int argc, char ** argv )
{

        int i = ::strlen( argv[ 0 ] );

        // determine which real "main" function do I call
        if ( argv[ 0 ][ i - 1 ] == 'x' ) {
                return Test_main( argc, argv );
        } else {
                return Example_main( argc, argv );
        }

}

Generated on Sat May 16 22:57:36 2009 for GNU CommonC++ by  doxygen 1.5.8