kruschk
kruschk

Reputation: 123

Customized command-line arguments in C

I'm currently learning C by working through K&R's The C Programming Language, and have reached the point in the book where command-line arguments are discussed. In the book, the main routine would be written something like this:

int main(int argc, char *argv[])
{
    do something
}

From my understanding, at some point the number of arguments passed to the program must be counted and stored in argc. Also, the arguments themselves must be stored, and pointers to the first characters of each are stored in the array argv, where argv[0] is a pointer to the name of the command and argv[argc] is a null pointer. These steps can't just magically occur, this behaviour must be defined somewhere!

As an example, imagine that I want to store the first character of each argument passed to the program, firstc, and discard the remainder of that argument (let's pretend that I had a really, really good reason for doing this). I could write main() like so:

int main(char firstc[])
{
    do something
}

Clearly, this can already be done quite easily with the default argc and argv, and I wouldn't actually do it in practice. I can't even imagine a scenario in which this would actually be necessary, but I'm curious to know if it's possible.

So my (entirely theoretical, completely impractical) question is this: is it possible to define my own behaviour for the command line arguments? If it is, how would one go about doing so? If it's relevant, I'm using Ubuntu 16.04 and the GNOME Terminal.

P.S.

I just realized while writing this question that it is entirely possible (perhaps probable) that the C script is completely blind to what's going on outside, and that the terminal emulator is what prepares the command-line arguments for the C program.

Upvotes: 2

Views: 926

Answers (4)

VolAnd
VolAnd

Reputation: 6407

There are standards (like ANSI C, C89, etc.), that provide main rules and set set of restrictions, and there are agreements, that do not violate the standards and provide you some possibility.

Fist, I have one more example for you:

#include <stdio.h>

int main(int argc, char * argv[], char * envs[])
{
    int i = 1;
    while (envs[i] != NULL)
    { 
        printf("%d : %s\n", i, envs[i]);
        i++;
    }
    return 0;
}

Just try and see how this third argument of main can be useful.

Also, I want to explain my approach to command line arguments processing. I make ParseCommandLine (or EvaluateParameters) and call it at the beginning of main. That function analyze strings from command line and store all settings for further convenient usage. E.g. if I expect my program to be run as

  prog.exe -i input_file_name -o output_file_name -e

I will do something like:

#include <string.h>
#include <stdio.h>

#define FNAME_LEN 20

struct settings
{
    char inpFName[FNAME_LEN];
    char outFName[FNAME_LEN];
    bool isEncoded;
} globalSettings;

bool ParseCommandLine(int argc, char * argv[])
{
    int c;
    for (int c = 1; c < argc; c += 2)
    {
        if (!strcmp(argv[c], "-i") && c < argc - 1)
        {
            strncpy(globalSettings.inpFName, argv[c + 1], FNAME_LEN - 1);
            continue;
        }
        if (!strcmp(argv[c], "-o") && c < argc - 1)
        {
            strncpy(globalSettings.outFName, argv[c + 1], FNAME_LEN - 1);
            continue;
        }
        if (!strcmp(argv[c], "-e"))
        {
            globalSettings.isEncoded = true;
            c--;
            continue;
        }
    }
    // rules to check mandatory values
    if (strlen(globalSettings.inpFName) == 0 || strlen(globalSettings.outFName) == 0)
    {
        return false;
    }
    return true;
}

int main(int argc, char * argv[])
{
    if (ParseCommandLine(argc, argv))
    {
        // do something
    }
    else
    {
        // explain how to run program
    }
    return 0;
}

Upvotes: 0

paxdiablo
paxdiablo

Reputation: 882426

The setup of arguments is not actually within the purview of the C standard, it simply dictates the allowable forms of main that you can use. There are two canonical forms of this (assuming a hosted implementation), one being the argc/argv option, the other being the void option (although note that an implementation is free to provide others).

Typically, there is code that runs before main is called, such as from startup code in an object file like crt0.o.

However, as stated, the standard doesn't dictate anything that happens at that stage, it's the responsibility of the "environment" to set up things correctly so that main can be called.

In terms of doing what you request, I suspect the easiest solution would be to provide a main taking the canonical form and simply call a myMain with the first character of each argument, though you would probably need to intelligently handle any number of arguments that may be given to main.

An example follows which can handle between one and three arguments:

#include <stdio.h>

int myMain0(void) {
    printf ("myMain0\n");
    return 0;
}

int myMain1(char p1) {
    printf ("myMain1 [%c]\n", p1);
    return 0;
}

int myMain2(char p1, char p2) {
    printf ("myMain2 [%c] [%c]\n", p1, p2);
    return 0;
}

int main(int argc, char *argv[]) {
    switch (argc) {
        case 1: return myMain0();
        case 2: return myMain1(argv[1][0]);
        case 3: return myMain2(argv[1][0], argv[2][0]);
    }
    printf ("Invalid argument count of %d\n", argc - 1);
    return 1;
}

Upvotes: 5

CiaPan
CiaPan

Reputation: 9570

Your main() declaration does not actually define what parameters you get. That is the responsibility of the program's environment: the operating system and the calling program, usually the command line processor.

The caller (typically a shell program) prepares parameters and passes them to appropriate operating system's routine for the program to be called. The OS routine prepares those data for a callee, typically on the stack, and makes a jump into your program's entry point, which then follows to the main function.

Your main() declaration just declares what your main expects on the stack, which indirectly defines how you can use those data, but not what they are. That's also why you can declare main without parameters as main(void) – that simply means 'whatever is passed to me, I'll ignore it anyway'.

Upvotes: 0

Israel Unterman
Israel Unterman

Reputation: 13520

The operating system is the one that passes the number of arguments and the arguments themselves from the command line to the C program.

The function main is not picky about its arguments. You can get no argument at all, you can get only argc, and you can get both argc and argv.

You can even get 3 or 4 arguments with whatever types you want, but they will contain garbage. The operating system will always pass the number of arguments and their names as an int and an array of pointers to strings.

Upvotes: 0

Related Questions