34

OK, I have searched and found the following two StackOverflow topics that started me in the right direction:

Argument-parsing helpers for C/UNIX

Pass arguments into C program from command line

NOTE: ALL CODE IS PSEUDO-CODE. WILL POST COMPILABLE CODE WHEN IT WORKS.

However, I'm still completely confused on how to use getopt_long() in C. The program I'm writing is defined as having the following possible tags (but can include as many as you absolutely need, filling the rest in with empty values):

id3tagEd filename -title "title" -artist "artist" -year 1991 -comment "comment" -album "album" -track 1

Now, from what I read, I need to utilize a struct for the long options, correct? If so, I wrote something along the lines of this:

struct fields field =
{
    char *[] title;
    char *[] artist;
    char *[] album;
    int year;
    char *[] comment;
    int track;
}


static struct options long_options[] =
{
    {"title", 0, &field.title, 't'},
    {"artist", 0, &field.artist, 'a'},
    {"album", 0, &field.album, 'b'},
    {"year", 0, &field.year, 'y'},
    {"comment", 0, &field.comment, 'c'},
    {"track", 0, &field.track, 'u'},
    {0, 0, 0, 0}
}

Now, from what I gathered, I would be calling it via this:

int option_index = 0;

int values = getopt_long(argc, argv, "tabycu", long_options, &option_index);

From here, could I strictly use the field struct and do what I need to within my program? However, if this is the case, can someone explain the whole long_options struct? I read the man pages and such, and I'm just utterly confused. By rereading the man pages, I can see I can set variables to null, and should be setting all my option requirements to "required_argument"? And then setting the structs via a while() loop? However, I see optarg being used. Is this set by getopt_long()? Or is it missing from the example?

And one last issue, I will always have an unnamed required option: filename, would I just use argv[0] to gain access to that? (Since I can assume it'll be first).

On a side note, this is related to a homework problem, but it has nothing to do with fixing it, its more of a fundamental, have to understand argument passing and parsing in C via command line first.

Community
  • 1
  • 1
Jeremy Dentel
  • 837
  • 2
  • 9
  • 19
  • The struct definition shown won't compile. Please provide compilable code. – Jonathan Leffler Sep 20 '11 at 17:24
  • `char *[] title;` is not a valid declaration. try `char *title[];` Note that `&field.title` will return a `char***` which is probably not what you want. – Oscar Korz Sep 20 '11 at 17:25
  • 1
    See the man pages for getopt(3) and getopt_long(3), both have examples. – ott-- Sep 20 '11 at 17:26
  • I retract that bit about `&field.title`. Apparently it's ok (it's a `char**`). – Oscar Korz Sep 20 '11 at 17:27
  • @JonathanLeffler, will it not compile based on the fact that its a struct or am I doing something wrong? I'm still relatively new to the whole C-thing. – Jeremy Dentel Sep 20 '11 at 17:32
  • @okorz001, I meant to put it that way, but ok. – Jeremy Dentel Sep 20 '11 at 17:33
  • @ott, I have read those and I'm still confused on how they work. I'll amend my question with some more specifics. – Jeremy Dentel Sep 20 '11 at 17:33
  • It won't compile on multiple grounds. You need something like: `struct fields { char title[128]; ... } field;` or some variant of that. You need to specify the body of the structure before defining a variable of the type; the `=` would be used to initialize a structure. You could use pointers (`char *title;`) but then you have more complicated memory management (which you are, to be frank, not ready to deal with). What did your compiler say when you submitted the code you showed? – Jonathan Leffler Sep 20 '11 at 18:11
  • @JonathanLeffler the above code was a quick rewrite, however, I hadn't attempted to compile because I wasn't sure on proper use. Now that Carl has described proper use (and pointed out my errors), I plan on attempting to compile it. I had noticed and added the length after the post, and modified it some more. So most of your comments were already changed (just not on here). The only thing I had to modify was I still had char *title[30]. – Jeremy Dentel Sep 20 '11 at 18:49
  • As a courtesy to people reading and answering your questions, try to make the code you show compilable. If it is only pseudo-code, state as much. – Jonathan Leffler Sep 20 '11 at 18:53
  • as I stated in the answer, it is "struct option" not "struct options" – Nikko Sep 24 '11 at 09:24

2 Answers2

43

First off, you probably don't want 0 for the has_arg field - it must be one of no_argument, required_arguemnt, or optional_argument. In your case, all of them are going to be required_argument. Besides that, you're not using the flag field correctly - it has to be an integer pointer. If the corresponding flag is set, getopt_long() will fill it in with the integer you passed in via the val field. I don't think you need this feature at all. Here's a better (shortened) example for your case:

static struct option long_options[] =
{
    {"title", required_argument, NULL, 't'},
    {"artist", required_argument, NULL, 'a'},
    {NULL, 0, NULL, 0}
};

Then later, you can use it appropriately (straight from the manpage, I added some comments):

// loop over all of the options
while ((ch = getopt_long(argc, argv, "t:a:", long_options, NULL)) != -1)
{
    // check to see if a single character or long option came through
    switch (ch)
    {
         // short option 't'
         case 't':
             field.title = optarg; // or copy it if you want to
             break;
         // short option 'a'
         case 'a':
             field.artist = optarg; // or copy it if you want to
             break;
    }
}

You can extend for your other fields as necessary (and add some error handling, please!). Note - if you want to use -title and -artist like you have in your example, you'll need to use getopt_long_only(), which doesn't have short options.

As to your filename option, you'll get that out as a '?' from the getopt_long() call, so you could handle it at that time. Your other options are to require that it is either the first or the last option and handle it by itself separately.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • The example given to use uses -title, so I guess getopt_long_only() will be my best bet. Just to be sure: I will do a while loop and switch them, and do all my setting there. This makes a lot of sense. How would I handle the unnamed variable in this example? Set it using argv[0] before I start processing these? – Jeremy Dentel Sep 20 '11 at 17:40
  • @Jeremy, yeah if you know it will always be the first one that'll be fine. Make sure to pass `argv+1` and `argc-1` to the `getopt()` call then, though. You could also handle it in a `case '?'` inside the switch statement. – Carl Norum Sep 20 '11 at 17:51
  • It should be "struct option" and not "struct options" – Nikko Sep 23 '11 at 15:50
  • @CarlNorum I'm having issues with this. I'm getting a bunch of warnings because of long_options (id3tagEd.c:30: warning: excess elements in struct initializer id3tagEd.c:30: warning: (near initialization for `long_options[0-6]'), and errors: id3tagEd.c:28: error: storage size of 'long_options' isn't known; id3tagEd.c:30: error: `required_argument' undeclared (first use in this function); id3tagEd.c:20: error: two or more data types in declaration of `title set'; id3tagEd.c:28: error: elements of array `long_options' have incomplete type Any ideas? I'll post full code in a few and link you. – Jeremy Dentel Sep 23 '11 at 18:48
  • 3
    You need to include `getopt.h`. – Carl Norum Sep 23 '11 at 20:13
  • *whistles* I even read the the man page to get the include... Hm, still get thing the "initializer element is not constant" error. But that's it, so I should be able to figure that out... – Jeremy Dentel Sep 24 '11 at 06:18
  • You're missing a semicolon after your structure definition. Besides that, your switch statement isn't going to do what you want. I'm sure you'll be able to debug it. – Carl Norum Sep 24 '11 at 16:27
  • Debugging shall be a very good practice for me... I guess this is what happens when you are an extreme n00b at C... :( – Jeremy Dentel Sep 24 '11 at 17:32
7

If you use the popt library, you will be able to create something smart as you did in your pseudo-code:

#include <stdio.h>
#include "popt.h"

struct _field {
    char *title;
    char *artist;
    /* etc */
} field;

field.title = NULL;
field.artist = NULL;

/* HERE IS WHAT YOU WANTED IN YOUR PSEUDO-CODE */
struct poptOption optionsTable[] = {

   {"title", 't', POPT_ARG_STRING, &field.title, 't'
    "set the 'title' of the album" },
   {"artist", 'a', POPT_ARG_STRING, &field.artist, 'a'
    "set the 'artist' of the album" },
   POPT_AUTOHELP
   POPT_TABLEEND
};

poptContext optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
poptSetOtherOptionHelp(optCon, "[OPTIONS]");

char c;
while ((c = poptGetNextOpt(optCon)) >= 0) {
    switch (c) {
        case 't':
            /* do extra stuff only if you need */
            break;
        case 'a':
            /* do extra stuff only if you need */
            break;
        default:
            poptPrintUsage(optCon, stderr, 0);
            exit(1);
    }
}

if (field.title) printf("\nTitle is [%s]", field.title);
if (field.artist) printf("\nArtist is [%s]", field.artist)

Be smart than getopt ;)

user1823890
  • 704
  • 8
  • 7