Question

When attempting to use getopt multiple times, the error I get in valgrind is Invalid read of size 1. The error only occurs when doing something like this:

ls -a -b
ls -a -b

Therefore I'm assuming the problem is with reusing the getopt functions.

Command.h

class Command {
    protected:
            // for use in getopt
        int c = 0;
            // name of command
        char* name;
    public:
        Command(const char* nname) : name((char*)nname) { }
        virtual ~Command() { };
        virtual void process_args(int argc, char* argv[]) = 0;
        virtual char* get_name() const {
            return name;
        }
};

ls.h is simply this wrapped in a class:

class ls : public Command {
    public:
        ls() : Command("ls") { }
        ~ls() { }
        void process_args(int input_argc, char* input_argv[]) {
            int verbose_flag = 0;

            struct option long_options[] =
            {
                    /* These options set a flag. */
                    {"verbose", no_argument,       &verbose_flag, 1},
                    {"brief",   no_argument,       &verbose_flag, 0},
                    /* These options don't set a flag.
                        We distinguish them by their indices. */
                    {"add",     no_argument,       0, 'a'},
                    {"append",  no_argument,       0, 'b'},
                    {"delete",  required_argument, 0, 'd'},
                    {"create",  required_argument, 0, 'c'},
                    {"file",    required_argument, 0, 'f'},
                    {0, 0, 0, 0}
            };

            while (1) {
                      // removed static and moved struct outside
                      // everything else is the same
                    }
        }
};

main.cpp

std::vector<std::unique_ptr<Command>> commands;
commands.push_back(std::unique_ptr<Command>(new ls()));
commands.push_back(std::unique_ptr<Command>(new shibe()));

while (true) {
    std::string input;
    std::getline(std::cin, input);
    if (input == "quit")
        break;
    std::istringstream iss(input);
    std::vector<std::string> args;
    std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter(args));
    int input_argc = args.size();
    char* input_argv[input_argc];
    for (int i = 0; i < args.size(); i++) {
        input_argv[i] = (char*)args[i].c_str();
    }

    for (int i = 0; i < commands.size(); i++) {
        if (strcmp(input_argv[0], commands[i]->get_name()) == 0) {
            commands[i]->process_args(input_argc, input_argv);
            break;
        }
    }
}

Valgrind output is:

ls -a -b
--30624-- REDIR: 0x375e8812d0 (strlen) redirected to 0x480155c (_vgnU_ifunc_wrap                                                                                                 per)
--30624-- REDIR: 0x375e87f810 (__GI_strchr) redirected to 0x4a07b30 (__GI_strchr                                                                                                 )
option -a
option -b
ls -a -b
==30624== Invalid read of size 1
==30624==    at 0x375E8CDFDC: _getopt_internal_r (in /lib64/libc-2.12.so)
==30624==    by 0x375E8CF1EA: _getopt_internal (in /lib64/libc-2.12.so)
==30624==    by 0x375E8CF2D2: getopt_long (in /lib64/libc-2.12.so)
==30624==    by 0x401E1C: ls::process_args(int, char**) (ls.h:31)
==30624==    by 0x4019CB: main (main.cpp:36)
==30624==  Address 0x513e5da is 26 bytes inside a block of size 27 free'd
==30624==    at 0x4A05FD6: operator delete(void*) (vg_replace_malloc.c:480)
==30624==    by 0x4CCADFE: std::basic_string<char, std::char_traits<char>, std::                                                                                                 allocator<char> >::~basic_string() (basic_string.h:538)
==30624==    by 0x403AA5: void std::_Destroy<std::string>(std::string*) (stl_con                                                                                                 struct.h:93)
==30624==    by 0x403855: void std::_Destroy_aux<false>::__destroy<std::string*>                                                                                                 (std::string*, std::string*) (stl_construct.h:103)
==30624==    by 0x403466: void std::_Destroy<std::string*>(std::string*, std::st                                                                                                 ring*) (stl_construct.h:126)
==30624==    by 0x402DE6: void std::_Destroy<std::string*, std::string>(std::str                                                                                                 ing*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==30624==    by 0x402878: std::vector<std::string, std::allocator<std::string> >                                                                                                 ::~vector() (stl_vector.h:415)
==30624==    by 0x401A03: main (main.cpp:26)
==30624==
--30624-- REDIR: 0x375e8846b0 (mempcpy) redirected to 0x4a09f80 (mempcpy)
non-option input_argv-elements: s b
quit
--30624-- REDIR: 0x375e87b6d0 (free) redirected to 0x4a06369 (free)
==30624==
==30624== HEAP SUMMARY:
==30624==     in use at exit: 0 bytes in 0 blocks
==30624==   total heap usage: 36 allocs, 36 frees, 916 bytes allocated
==30624==
==30624== All heap blocks were freed -- no leaks are possible
==30624==
==30624== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
==30624==
Was it helpful?

Solution

Linux's manpage on getopt() makes it clear how to reset getopt() (which unfortuantely uses a number of global variables to communicate with the caller and maintain state):

The variable optind is the index of the next element to be processed in argv. The system initializes this value to 1. The caller can reset it to 1 to restart scanning of the same argv, or when scanning a new argument vector.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top