cparsingargumentsprogram-entry-pointpurify

C : Parsing options the right way


I'm trying to parse two options in a C program.

The program is called like this:

./a.out [OPTIONS] [DIRECTORY 1] [DIRECTORY 2]

The program syncs two directories and has two options. (-r) for recursive syncing (folder inside folder), and (-n) to copy file from local to remote in case it doesn't exist in remote.

Options are:
-r : recursive
-n : copy file if it doesn't exist in remote folder

So calling:

./a.out -r D1 D2

would recursively sync all files and directories from D1 to D2. Files presents in D1 and not present in D2 are ignored.

And calling:

./a.cout -rn D1 D2

would do the same thing but files present in D1 and not present in D2 are copied to D2.

The problem is that calling ./a.out -rn is not the same as calling ./a.out -nr and ./a.out -r -n is not working neither because (-n) is not D1.

Here is how I implement the main.

int main(int argc, char** argv) {
  int next_option = 0;
  const char* const short_options = "hrn:";

  const struct option long_options[] = {
    { "help",      0, NULL,  'h' },
    { "recursive", 1, NULL,  'r' },
    { "new", 1, NULL,  'n' },
    { NULL,        0, NULL,  0   }
  };

  int recursive = 0;
  int new = 0;

  do {
    next_option = getopt_long(argc, argv, short_options, long_options, NULL);

    switch(next_option) {
      case 'r':
        recursive = 1;
        break;

      case 'n':
        new = 1;
        break;

      case 'h':
        print_help();
        return 0;

      case -1:
        break;

      default:
        print_help();
        return -1;
    }

  } while(next_option != -1);

  sync(argv[2], argv[3], recursive, new);

  return EXIT_SUCCESS;
}

Solution

  • You have two (potential) problems here:

    1. You have a stray : in your short option string. This makes -n take an option that swallows any following r. You also have the long options set to take mandatory arguments.

    2. You've hard coded the argument numbers in an inflexible way, and you don't test that they exist.

    Try this:

    int main(int argc, char** argv) {
      int next_option = 0;
      const char* const short_options = "hrn";
      extern int optind;
    
      const struct option long_options[] = {
        { "help",      no_argument, NULL,  'h' },
        { "recursive", no_argument, NULL,  'r' },
        { "new",       no_argument, NULL,  'n' },
        { NULL,        no_argument, NULL,  0   }
      };
    
      int recursive = 0;
      int new = 0;
    
      do {
        next_option = getopt_long(argc, argv, short_options, long_options, NULL);
    
        switch(next_option) {
          case 'r':
            recursive = 1;
            break;
    
          case 'n':
            new = 1;
            break;
    
          case 'h':
            print_help();
            return 0;
    
          case -1:
            break;
    
          default:
            print_help();
            return -1;
        }
    
      } while(next_option != -1);
    
      if (optind + 1 >= argc)
        return -1;
    
      sync(argv[optind], argv[optind+1], recursive, new);
    
      return EXIT_SUCCESS;
    }