162

I'm trying to write a program that can compare two files line by line, word by word, or character by character in C. It has to be able to read in command line options -l, -w, -i or --...

  • if the option is -l, it compares the files line by line.
  • if the option is -w, it compares the files word by word.
  • if the option is --, it automatically assumes that the next argument is the first filename.
  • if the option is -i, it compares them in a case insensitive manner.
  • defaults to comparing the files character by character.

It's not supposed to matter how many times the options are input as long as -w and -l aren't inputted at the same time and there are no more or less than two files.

I don't even know where to begin with parsing the command line arguments.

So this is the code that I came up with for everything. I haven't error checked it quite yet, but am I writing things in an overcomplicated manner?

/* * Functions to compare files. */ int compare_line(); int compare_word(); int compare_char(); int case_insens(); /* * Program to compare the information in two files and print message saying * whether or not this was successful. */ int main(int argc, char* argv[]) { /* Loop counter */ size_t i = 0; /* Variables for functions */ int caseIns = 0; int line = 0; int word = 0; /* File pointers */ FILE *fp1, *fp2; /* * Read through command-line arguments for options. */ for (i = 1; i < argc; i++) { printf("argv[%u] = %s\n", i, argv[i]); if (argv[i][0] == '-') { if (argv[i][1] == 'i') { caseIns = 1; } if (argv[i][1] == 'l') { line = 1; } if (argv[i][1] == 'w') { word = 1; } if (argv[i][1] == '-') { fp1 = argv[i][2]; fp2 = argv[i][3]; } else { printf("Invalid option."); return 2; } } else { fp1(argv[i]); fp2(argv[i][1]); } } /* * Check that files can be opened. */ if(((fp1 = fopen(fp1, "rb")) == NULL) || ((fp2 = fopen(fp2, "rb")) == NULL)) { perror("fopen()"); return 3; } else { if (caseIns == 1) { if(line == 1 && word == 1) { printf("That is invalid."); return 2; } if(line == 1 && word == 0) { if(compare_line(case_insens(fp1, fp2)) == 0) return 0; } if(line == 0 && word == 1) { if(compare_word(case_insens(fp1, fp2)) == 0) return 0; } else { if(compare_char(case_insens(fp1,fp2)) == 0) return 0; } } else { if(line == 1 && word == 1) { printf("That is invalid."); return 2; } if(line == 1 && word == 0) { if(compare_line(fp1, fp2) == 0) return 0; } if(line == 0 && word == 1) { if(compare_word(fp1, fp2) == 0) return 0; } else { if(compare_char(fp1, fp2) == 0) return 0; } } } return 1; if(((fp1 = fclose(fp1)) == NULL) || (((fp2 = fclose(fp2)) == NULL))) { perror("fclose()"); return 3; } else { fp1 = fclose(fp1); fp2 = fclose(fp2); } } /* * Function to compare two files line-by-line. */ int compare_line(FILE *fp1, FILE *fp2) { /* Buffer variables to store the lines in the file */ char buff1 [LINESIZE]; char buff2 [LINESIZE]; /* Check that neither is the end of file */ while((!feof(fp1)) && (!feof(fp2))) { /* Go through files line by line */ fgets(buff1, LINESIZE, fp1); fgets(buff2, LINESIZE, fp2); } /* Compare files line by line */ if(strcmp(buff1, buff2) == 0) { printf("Files are equal.\n"); return 0; } printf("Files are not equal.\n"); return 1; } /* * Function to compare two files word-by-word. */ int compare_word(FILE *fp1, FILE *fp2) { /* File pointers */ FILE *fp1, *fp2; /* Arrays to store words */ char fp1words[LINESIZE]; char fp2words[LINESIZE]; if(strtok(fp1, " ") == NULL || strtok(fp2, " ") == NULL) { printf("File is empty. Cannot compare.\n"); return 0; } else { fp1words = strtok(fp1, " "); fp2words = strtok(fp2, " "); if(fp1words == fp2words) { fputs(fp1words); fputs(fp2words); printf("Files are equal.\n"); return 0; } } return 1; } /* * Function to compare two files character by character. */ int compare_char(FILE *fp1,FILE *fp2) { /* Variables to store the characters from both files */ int c; int d; /* Buffer variables to store chars */ char buff1 [LINESIZE]; char buff2 [LINESIZE]; while(((c = fgetc(fp1))!= EOF) && (((d = fgetc(fp2))!=EOF))) { if(c == d) { if((fscanf(fp1, "%c", buff1)) == (fscanf(fp2, "%c", buff2))) { printf("Files have equivalent characters.\n"); return 1; break; } } } return 0; } /* * Function to compare two files in a case-insensitive manner. */ int case_insens(FILE *fp1, FILE *fp2, size_t n) { /* Pointers for files. */ FILE *fp1, *fp2; /* Variable to go through files. */ size_t i = 0; /* Arrays to store file information. */ char fp1store[LINESIZE]; char fp2store[LINESIZE]; while(!feof(fp1) && !feof(fp2)) { for(i = 0; i < n; i++) { fscanf(fp1, "%s", fp1store); fscanf(fp2, "%s", fp2store); fp1store = tolower(fp1store); fp2store = tolower(fp2store); return 1; } } return 0; } 
8
  • 5
    So, go and read the manual page for it; it is not very complex, and the manual page probably includes an example for you to experiment with (and if your local man page doesn't, you can certainly find examples on the web). Commented Mar 10, 2012 at 0:54
  • 2
    This is a high level library: argparse in c, very easy to use. Commented Jan 15, 2015 at 2:49
  • You can use James Theiler's "opt" package. And a flattering post with some examples of how it is so much simpler than other approaches is here: Opt 3.19 review and upgrades Commented Nov 16, 2015 at 0:41
  • stackoverflow.com/questions/189972/… Commented Oct 4, 2016 at 3:43
  • 1
    Whoa, that's a lot of strcmps :q Commented Jul 4, 2018 at 19:27

15 Answers 15

334

To my knowledge, the three most popular ways how to parse command line arguments in C are:

  • Getopt (#include <unistd.h> from the POSIX C Library), which can solve simple argument parsing tasks. If you're a bit familiar with bash, the getopt built-in of bash is based on Getopt from the GNU libc.
  • Argp (#include <argp.h> from the GNU C Library), which can solve more complex tasks and takes care of stuff like, for example:
    • -?, --help for help message, including email address
    • -V, --version for version information
    • --usage for usage message
  • Doing it yourself, which I don't recommend for programs that would be given to somebody else, as there is too much that could go wrong or lower quality. The popular mistake of forgetting about -- to stop option parsing is just one example.

The GNU C Library documentation has some nice examples for Getopt and Argp.

Example for using Getopt

#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { bool isCaseInsensitive = false; int opt; enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE; while ((opt = getopt(argc, argv, "ilw")) != -1) { switch (opt) { case 'i': isCaseInsensitive = true; break; case 'l': mode = LINE_MODE; break; case 'w': mode = WORD_MODE; break; default: fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]); exit(EXIT_FAILURE); } } // Now optind (declared extern int by <unistd.h>) is the index of the first non-option argument. // If it is >= argc, there were no non-option arguments. // ... } 

Example for using Argp

#include <argp.h> #include <stdbool.h> const char *argp_program_version = "programname programversion"; const char *argp_program_bug_address = "<[email protected]>"; static char doc[] = "Your program description."; static char args_doc[] = "[FILENAME]..."; static struct argp_option options[] = { { "line", 'l', 0, 0, "Compare lines instead of characters."}, { "word", 'w', 0, 0, "Compare words instead of characters."}, { "nocase", 'i', 0, 0, "Compare case insensitive instead of case sensitive."}, { 0 } }; struct arguments { enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode; bool isCaseInsensitive; }; static error_t parse_opt(int key, char *arg, struct argp_state *state) { struct arguments *arguments = state->input; switch (key) { case 'l': arguments->mode = LINE_MODE; break; case 'w': arguments->mode = WORD_MODE; break; case 'i': arguments->isCaseInsensitive = true; break; case ARGP_KEY_ARG: return 0; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 }; int main(int argc, char *argv[]) { struct arguments arguments; arguments.mode = CHARACTER_MODE; arguments.isCaseInsensitive = false; argp_parse(&argp, argc, argv, 0, 0, &arguments); // ... } 

Example for Doing it Yourself

#include <stdbool.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { bool isCaseInsensitive = false; enum { CHARACTER_MODE, WORD_MODE, LINE_MODE } mode = CHARACTER_MODE; size_t optind; for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) { switch (argv[optind][1]) { case 'i': isCaseInsensitive = true; break; case 'l': mode = LINE_MODE; break; case 'w': mode = WORD_MODE; break; default: fprintf(stderr, "Usage: %s [-ilw] [file...]\n", argv[0]); exit(EXIT_FAILURE); } } argv += optind; // *argv points to the remaining non-option arguments. // If *argv is NULL, there were no non-option arguments. // ... } 

Disclaimer: I am new to Argp, the example might contain errors.

Sign up to request clarification or add additional context in comments.

7 Comments

Really thorough answer, thanks Christian (upvoted). However, mac users should be aware that the argp approach isn't cross-platform compatible. As I found here, Argp is a non-standardized glibc API extension. It is available in gnulib so can be added to a project explicitly. However, it's probably simpler for mac-only or cross-platform developers to use the getopt approach.
For the do it yourself version, I don't like that the options allow extra text afterwards, like -wzzz parses the same as -w, and also that the options have to come before the file arguments.
@Jake you are right. Respect for spotting that. I don't remember whether I spotted that when I wrote it. It's again a perfect example that DIY is so easy to get wrong and thus shouldn't be done. Thanks for telling, I might fix the example.
This is just a nitpicking--*argv does not point to the remaining non-option arguments! argv += optind or similer should be added. As @ChristianHujer mentioned though, this is another example that DIY is easy to get wrong.
I know the thread is a bit old, but I'm surprised nobody has yet mentioned glib's GOption functionality. glib is cross platform and the options interface is quite powerful and easy to use. It can take arguments from argv or from any gchar**, so it's good for parsing a custom command interface within a program as well as parsing arguments to the program from the shell command line.
|
25

Use getopt(), or perhaps getopt_long().

int iflag = 0; enum { WORD_MODE, LINE_MODE } op_mode = WORD_MODE; // Default set int opt; while ((opt = getopt(argc, argv, "ilw") != -1) { switch (opt) { case 'i': iflag = 1; break; case 'l': op_mode = LINE_MODE; break; case 'w': op_mode = WORD_MODE; break; default: fprintf(stderr, "Usage: %s [-ilw] [file ...]\n", argv[0]); exit(EXIT_FAILURE); } } /* Process file names or stdin */ if (optind >= argc) process(stdin, "(standard input)", op_mode); else { int i; for (i = optind; i < argc; i++) { FILE *fp = fopen(argv[i], "r"); if (fp == 0) fprintf(stderr, "%s: failed to open %s (%d %s)\n", argv[0], argv[i], errno, strerror(errno)); else { process(fp, argv[i], op_mode); fclose(fp); } } } 

Note that you need to determine which headers to include (I make it 4 that are required), and the way I wrote the op_mode type means you have a problem in the function process() - you can't access the enumeration down there. It's best to move the enumeration outside the function; you might even make op_mode a file-scope variable without external linkage (a fancy way of saying static) to avoid passing it to the function. This code does not handle - as a synonym for standard input, another exercise for the reader. Note that getopt() automatically takes care of -- to mark the end of options for you.

I've not run any version of the typing above past a compiler; there could be mistakes in it.


For extra credit, write a (library) function:

int filter(int argc, char **argv, int idx, int (*function)(FILE *fp, const char *fn)); 

which encapsulates the logic for processing file name options after the getopt() loop. It should handle - as standard input. Note that using this would indicate that op_mode should be a static file scope variable. The filter() function takes argc, argv, optind and a pointer to the processing function. It should return 0 (EXIT_SUCCESS) if it was able to open all the files and all invocations of the function reported 0, otherwise 1 (or EXIT_FAILURE). Having such a function simplifies writing Unix-style 'filter' programs that read files specified on the command line or standard input.

3 Comments

I don't like that getopt() doesn't allow options after the first file.
POSIX getopt() doesn’t; GNU getopt() does by default. Take your pick. I’m not keen on the options after file names behaviour, mainly because it isn’t reliable across platforms.
In MacOS, getopt_long() is in getopt.h and getopt() is in unistd.h. The former #includes the latter.
23

I've found Gengetopt to be quite useful - you specify the options you want with a simple configuration file, and it generates a .c/.h pair that you simply include and link with your application. The generated code makes use of getopt_long, appears to handle most common sorts of command line parameters, and it can save a lot of time.

A gengetopt input file might look something like this:

version "0.1" package "myApp" purpose "Does something useful." # Options option "filename" f "Input filename" string required option "verbose" v "Increase program verbosity" flag off option "id" i "Data ID" int required option "value" r "Data value" multiple(1-) int optional 

Generating the code is easy and spits out cmdline.h and cmdline.c:

$ gengetopt --input=myApp.cmdline --include-getopt 

The generated code is easily integrated:

#include <stdio.h> #include "cmdline.h" int main(int argc, char ** argv) { struct gengetopt_args_info ai; if (cmdline_parser(argc, argv, &ai) != 0) { exit(1); } printf("ai.filename_arg: %s\n", ai.filename_arg); printf("ai.verbose_flag: %d\n", ai.verbose_flag); printf("ai.id_arg: %d\n", ai.id_arg); int i; for (i = 0; i < ai.value_given; ++i) { printf("ai.value_arg[%d]: %d\n", i, ai.value_arg[i]); } } 

If you need to do any extra checking (such as ensuring flags are mutually exclusive), you can do this fairly easily with the data stored in the gengetopt_args_info struct.

7 Comments

1++ except that it generates code that generates warnings :(
Yes unfortunately. I put exceptions in my cmake file.
I'll probably just use GCC pragmas to ignore warnings for that file (awful I know)
Note that you'll obviously lose them if you regenerate the source, so you might want to apply them as a patch in your build process. Frankly I did find it easier to just turn off warnings on those specific files.
well no, I mean putting the pragmas around the #include, not in the generated file itself. to me turning off warnings is verboten :-)
|
7

Docopt has a C implementation that I thought was quite nice:

From a man-page standardized format describing command line options, docopt infers and creates an argument parser. This got started in Python; the Python version literally just parses the docstring and returns a dict. To do this in C takes a little more work, but it's clean to use and has no external dependencies.

Comments

3

There is a great general-purpose C library, libUCW, which includes neat command-line option parsing and configuration file loading.

The library also comes with good documentation and includes some other useful stuff (fast I/O, data structures, allocators, ...) but this can be used separately.

Example libUCW option parser (from the library docs)

#include <ucw/lib.h> #include <ucw/opt.h> int english; int sugar; int verbose; char *tea_name; static struct opt_section options = { OPT_ITEMS { OPT_HELP("A simple tea boiling console."), OPT_HELP("Usage: teapot [options] name-of-the-tea"), OPT_HELP(""), OPT_HELP("Options:"), OPT_HELP_OPTION, OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"), OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"), OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"), OPT_STRING(OPT_POSITIONAL(1), NULL, tea_name, OPT_REQUIRED, ""), OPT_END } }; int main(int argc, char **argv) { opt_parse(&options, argv+1); return 0; } 

1 Comment

positional option has bug. if there are two OPT_STRING, and one is positional, one not, it can not parse.
2

Tooting my own horn if I may, I'd also like to suggest taking a look at an option parsing library that I've written: dropt.

  • It's a C library (with a C++ wrapper if desired).
  • It's lightweight.
  • It's extensible (custom argument types can be easily added and have equal footing with built-in argument types).
  • It should be very portable (it's written in standard C) with no dependencies (other than the C standard library).
  • It has a very unrestrictive license (zlib/libpng).

One feature that it offers that many others don't is the ability to override earlier options. For example, if you have a shell alias:

alias bar="foo --flag1 --flag2 --flag3" 

and you want to use bar but with--flag1 disabled, it allows you to do:

bar --flag1=0 

Comments

2

I wrote a command-line parser library called cmdparser

https://github.com/XUJINKAI/cmdparser/

It's fully tested and support nested sub-commands.

An example for question:

static cmdp_action_t callback(cmdp_process_param_st *params); static bool g_line_by_line = false; static bool g_word_by_word = false; static bool g_case_insensitive = false; static cmdp_command_st cmdp = { .options = { {'l', NULL, "line by line", CMDP_TYPE_BOOL, &g_line_by_line }, {'w', NULL, "word by word", CMDP_TYPE_BOOL, &g_word_by_word }, {'i', NULL, "case insensitive", CMDP_TYPE_BOOL, &g_case_insensitive }, {0}, }, .fn_process = callback, }; int main(int argc, char **argv) { return cmdp_run(argc - 1, argv + 1, &cmdp); } static cmdp_action_t callback(cmdp_process_param_st *params) { if (g_line_by_line && g_word_by_word) { return CMDP_ACT_FAIL | CMDP_ACT_SHOW_HELP; } // your code here... return CMDP_ACT_OVER; } 

Comments

0
#include <stdio.h> int main(int argc, char **argv) { size_t i; size_t filename_i = -1; for (i = 0; i < argc; i++) { char const *option = argv[i]; if (option[0] == '-') { printf("I am a flagged option"); switch (option[1]) { case 'a': /*someting*/ break; case 'b': break; case '-': /* "--" -- the next argument will be a file.*/ filename_i = i; i = i + 1; break; default: printf("flag not recognised %s", option); break; } } else { printf("I am a positional argument"); } /* At this point, if -- was specified, then filename_i contains the index into argv that contains the filename. If -- was not specified, then filename_i will be -1*/ } return 0; } 

3 Comments

No; absolutely not a good way of doing it...Use one of the argument parsing functions - getopt() or getopt_long().
Sounds like a cheat, given that this is blatently a homework question. Additionally, the OP is having a hard time understanding the concept of what a string is and how to read parts of it. Foisting getopts on him is a mistake.
It is a homework question. I know what a string is. I just don't understand how to break down the command line arguments because it seems confusing to me when you can input the options any number of times, so you can't really figure out where the filenames are. Maybe I'm overthinking it?
0

Instructional template for parsing command line arguments in C.

C:>programName -w -- fileOne.txt fileTwo.txt

BOOL argLine = FALSE; BOOL argWord = FALSE; BOOL argChar = FALSE; char * fileName1 = NULL; char * fileName2 = NULL; int main(int argc, char * argv[]) { int i; printf("Argument count=%d\n",argc); for (i = 0; i < argc; i++) { printf("Argument %s\n",argv[i]); if (strcmp(argv[i],"-l")==0) { argLine = TRUE; printf(" argLine=TRUE\n"); } else if (strcmp(argv[i],"-w")==0) { argWord = TRUE; printf(" argWord=TRUE\n"); } else if (strcmp(argv[i],"-c")==0) { argChar = TRUE; printf(" argChar=TRUE\n"); } else if (strcmp(argv[i],"--")==0) { if (i+1 <= argc) { fileName1 = argv[++i]; printf(" fileName1=%s\n",fileName1); } if (i+1 <= argc) { fileName2 = argv[++i]; printf(" fileName2=%s\n",fileName2); } } } return 0; } 

9 Comments

... I don't think there is a boolean variable in C...?
My eclipse/windows environment has type BOOL. Simply change it to type int or char and adjust code accordingly.
C99 has a type _Bool at all times, and a header <stdbool.h> which defines bool as _Bool and true and false and __bool_true_false_are_defined, all macros (which, exceptionally, can be undefined and redefined without invoking undefined behaviour; that licence is, however, tagged 'obsolescent'). So, if you have a C99 compiler, you can use <stdbool.h> and bool. If not, you either write one for yourself (it isn't hard) or you use a native equivalent.
@Wolfer My C environment has type BOOL (as typedef int BOOL) and type boolean (as typedef unsigned char boolean) and no definition for type bool. In the example, simply change to type int or char and adjust code accordingly.
I disagree with this approach. Use a library function to parse options.
|
0
 /* Here's a rough one not relying on any libraries. Example: -wi | -iw //word case insensitive -li | -il //line case insensitive -- file //specify the first filename (you could just get the files as positional arguments in the else statement instead) PS: don't mind the #define's, they're just pasting code :D */ #ifndef OPT_H #define OPT_H //specify option requires argument #define require \ optarg = opt_pointer + 1; \ if (*optarg == '\0') \ { \ if (++optind == argc) \ goto opt_err_arg; \ else \ optarg = argv[optind]; \ } \ opt_pointer = opt_null_terminator; //start processing argv #define opt \ int optind = 1; \ char *opt_pointer = argv[1]; \ char *optarg = NULL; \ char opt_null_terminator[2] = {'\0','\0'}; \ if (0) \ { \ opt_err_arg: \ fprintf(stderr,"option %c requires argument.\n",*opt_pointer); \ return 1; \ opt_err_opt: \ fprintf(stderr,"option %c is invalid.\n",*opt_pointer); \ return 1; \ } \ for (; optind < argc; opt_pointer = argv[++optind]) \ if (*opt_pointer++ == '-') \ { \ for (;;++opt_pointer) \ switch (*opt_pointer) \ { //stop processing argv #define done \ default: \ if (*opt_pointer != '\0') \ goto opt_err_opt; \ else \ goto opt_next; \ break; \ } \ opt_next:; \ } #endif //opt.h #include <stdio.h> #include "opt.h" int main (int argc, char **argv) { #define by_character 0 #define by_word 1 #define by_line 2 int cmp = by_character; int case_insensitive = 0; opt case 'h': puts ("HELP!"); break; case 'v': puts ("fileCMP Version 1.0"); break; case 'i': case_insensitive = 1; break; case 'w': cmp = by_word; break; case 'l': cmp = by_line; break; case '-':required printf("first filename: %s\n", optarg); break; done else printf ("Positional Argument %s\n", argv[optind]); return 0; } 

1 Comment

You need to explain your code rather than just throwing it up and expecting everyone to understand it. This is a site for learning not just copying and pasting.
0

Okay, that's the start of long story - made short abort parsing a command line in C ...

/** * Helper function to parse the command line * @param argc Argument Counter * @param argv Argument Vector * @param prog Program Instance Reference to fill with options */ bool parseCommandLine(int argc, char* argv[], DuplicateFileHardLinker* prog) { bool pathAdded = false; // Iterate over all arguments... for (int i = 1; i<argc; i++) { // Is argv a command line option? if (argv[i][0] == '-' || argv[i][0] == '/') { // ~~~~~~ Optionally Cut that part vvvvvvvvvvvvv for sake of simplicity ~~~~~~~ // Check for longer options if (stricmp( &argv[i][1], "NoFileName") == 0 || strcmp( &argv[i][1], "q1" ) == 0 ) { boNoFileNameLog = true; } else if (strcmp( &argv[i][1], "HowAreYou?") == 0 ) { logInfo( "SECRET FOUND: Well - wow I'm glad ya ask me."); } else { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Now here comes the main thing: // // Check for one-character options while (char option = *++argv[i]) { switch (option) { case '?': // Show program usage logInfo(L"Options:"); logInfo(L" /q\t>Quite mode"); logInfo(L" /v\t>Verbose mode"); logInfo(L" /d\t>Debug mode"); return false; // Log options case 'q': setLogLevel(LOG_ERROR); break; case 'v': setLogLevel(LOG_VERBOSE); break; case 'd': setLogLevel(LOG_DEBUG); break; default: logError(L"'%s' is an illegal command line option!" " Use /? to see valid options!", option); return false; } // switch one-char-option } // while one-char-options } // else one vs longer options } // if isArgAnOption // // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ So that's it! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // What follows now is are some useful extras... // else { // The command line options seems to be a path... WCHAR tmpPath[MAX_PATH_LENGTH]; mbstowcs(tmpPath, argv[i], sizeof(tmpPath)); // Check if the path is existing! //... prog->addPath(tmpPath); // Comment or remove to get a working example pathAdded = true; } } // Check for parameters if (!pathAdded) { logError("You need to specify at least one folder to process!\n" "Use /? to see valid options!"); return false; } return true; } int main(int argc, char* argv[]) { try { // Parse the command line if ( !parseCommandLine(argc, argv, prog) ) { return 1; } // I know that sample is just to show how the nicely parse command-line arguments // So Please excuse more nice useful C-glatter that follows now... } catch ( LPCWSTR err ) { DWORD dwError = GetLastError(); if ( wcslen(err) > 0 ) { if ( dwError != 0 ) { logError(dwError, err); } else { logError(err); } } return 2; } } #define LOG_ERROR 1 #define LOG_INFO 0 #define LOG_VERBOSE -1 #define LOG_DEBUG -2 /** Logging level for the console output */ int logLevel = LOG_INFO; void logError(LPCWSTR message, ...) { va_list argp; fwprintf(stderr, L"ERROR: "); va_start(argp, message); vfwprintf(stderr, message, argp); va_end(argp); fwprintf(stderr, L"\n"); } void logInfo(LPCWSTR message, ...) { if ( logLevel <= LOG_INFO ) { va_list argp; va_start(argp, message); vwprintf(message, argp); va_end(argp); wprintf(L"\n"); } } 

Note that this version will also support combining arguments: So instead of writing /h /s -> /hs will also work.

Sorry for being the n-th person posting here - however I wasn't really satisfied with all the stand-alone-versions I saw here. Well, the library ones are quiet nice. So I would prefer the libUCW option parser, Arg or Getopt over a home-made ones.

Note you may change:

*++argv[i] -> (++argv*)[0]

It is longer and less cryptic, but still cryptic.

Okay, let's break it down:

  1. argv[i]-> access i-th element in the argv-char pointer field

  2. ++*... -> will forward the argv-pointer by one char

  3. ... [0]-> will follow the pointer read the char

  4. ++(...) -> bracket are there so we'll increase the pointer and not the char value itself.

It is so nice that in C#, the pointers 'died' - long live the pointers!!

Comments

0
#include <stdio.h> #include <string.h> #include <stdlib.h> int is_arg(int ac, char **argv, char *arg) { if (ac < 2) { return 0; } for(int x=1; x < ac; x++) { if (0 == strcmp(argv[x], arg)) { return x; // return position of arg } } return 0; // arg not present } int main (int argc, char *argv[]) { int z = 0; if (argc < 2) { printf("no args present, aborting.\n"); exit(1); } (z=is_arg(argc, argv, "bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n"); (z=is_arg(argc, argv, "one bar")) ? printf("TRUE %d\n", z) : printf("FALSE\n"); (z=is_arg(argc, argv, "foo")) ? printf("TRUE %d\n", z) : printf("FALSE\n"); /* testing: run: ./getopt two bar "one bar" foo TRUE 2 TRUE 3 TRUE 4 run: ./getopt two bar one bar foo TRUE 2 FALSE TRUE 5 */ return 0; } 

Comments

0

You can try out my cargs library, which is the easiest way to manage command line arguments. Its documentation is very complete and you can learn to use it in like 30 mins.

The syntax is as sweet as this:

#include <stdio.h> #include <cargs.h> // You just declare your arguments const char* bool_arg_options = "lwi" enum bool_args_positions { ARG_L, ARG_W, ARG_I } // This is for efficient access int main(int argc, char** argv) { // Set the boolean arguments and data arguments cargs_set_args(bool_arg_options, NULL); // And then you just load the arguments cargs_load_args(argc, argv); // And last you check your arguments and do what your program needs if(cargs_check_bool(ARG_L)) { /*Do stuff*/ } if(cargs_check_bool(ARG_W)) { /*Do other stuff*/ } // And so on... } 

There are a lot more configuration options as well.

3 Comments

Please disclose your affiliation with the linked github repo. This is mandatory on the site.
@cigien How do I do it? I'm actually the owner of the repository
It's fairly simple, you just need to make it clear in the answer that the repository is yours. I've edited the answer in a way that makes it clear, but feel free to rephrase it if you want, so long as the disclosure is still clear.
0

You can parse flags, integers, strings and ranges using this code C99 :

#include "stdio.h" #include "string.h" typedef struct { struct { long long int my_flag; long long int my_int; char *my_string; struct { long long int min; long long int max; } my_range; } params; } state; int read_arg_no_value(char **argv, state *state) { char *key = *argv; if (strcmp(key, "--my_flag") == 0 || strcmp(key, "-f") == 0) state->params.my_flag = 1; else return 0; *argv = 0; return 1; } long long int to_int(char *s) { long long int res = 0; long long int sign = *s == '-' ? (++s, -1) : (s += *s == '+', 1); if (*s >= '1' && *s <= '9') for (--s; *++s && *s >= '0' && *s <= '9';) res = res * 10 + (*s - '0'); return *s || res >> 32 ? 0 : res * sign; } int read_arg_one_value(char **argv, state *state) { char *key = *argv, *value = *(argv + 1); if (strcmp(key, "--my-int") == 0 || strcmp(key, "-t") == 0) state->params.my_int = to_int(value); else if (strcmp(key, "--my-string") == 0 || strcmp(key, "-s") == 0) state->params.my_string = value; else return 0; *argv = *(argv + 1) = 0; return 1; } int read_arg_two_values(char **argv, state *state) { char *key = *argv, *min = *(argv + 1), *max = *(argv + 2); if (strcmp(key, "--my-range") == 0 || strcmp(key, "-r") == 0) { state->params.my_range.min = to_int(min); state->params.my_range.max = to_int(max); } else return 0; *argv = *(argv + 1) = *(argv + 2) = 0; return 1; } int main(int argc, char *argv[]) { state state = {0}; for (int i = 1; i < argc; ++i) if (!read_arg_no_value(argv + i, &state)) if (i + 1 < argc && !(read_arg_one_value(argv + i, &state) && ++i)) if (i + 2 < argc && !(read_arg_two_values(argv + i, &state) && (i += 2))) continue; for (int i = 1; i < argc; ++i) if (argv[i]) fprintf(stderr, "Unknown argument '%s'.\n", argv[i]); printf("flag ? %lld\n", state.params.my_flag); printf("single value number [%lld]\n", state.params.my_int); printf("single value string [%s]\n", state.params.my_string); printf("range [%lld, %lld]\n", state.params.my_range.min, state.params.my_range.max); return 0; } 

Try it online! with the parameters --my_flag --my-int -17 --my-range 5 10 --my-string "the solution" 1234.

Comments

0

great stuff here. My take: I was looking getopt_long(3) documentation (linux and openbsd pages) and got headache reading the diffs ;/ -- and then after looking all that string parsing that the getopt(3) implementation has to do... and argp is less portable interface to getopt...

Over the decades I've had quite a few arg scanning things in progress, most converging to use char *** argvp at some point. This 100-line sample does it too (while doing most, missing some (abbrev) and adding something (-NUM), of getopt[_long]). I named the file as opts--long.c, "self-contained, tunable getopts-long style arg scanning":

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <err.h> static const char * optarg_(const char * optp, const char *** argvp) { if ((++optp)[0] != 0) return optp; const char * ov = (++(*argvp))[0]; if (ov == NULL) errx(1, "option requires an argument -- '%c'", optp[-1]); return ov; } static const char * longoptarg(const char * optend, const char *** argvp) { if (*optend == '=') return optend + 1; if (*optend != 0) return 0; const char * ov = (++(*argvp))[0]; if (ov == NULL) errx(1, "option '%s' requires an argument", argvp[0][-1]); return ov; } #define IF_LONGOPT(o) if (memcmp(arg + 2, "" o, sizeof(o)) == 0) #define LONGOPTARG(v, o) \ if (memcmp(arg + 2, "" o, sizeof(o) - 1) == 0) { \ const char * ov = longoptarg(arg + 1 + sizeof(o), &argv); \ if (ov) { v = ov; continue; }} ((void)0) int main(int argc, const char * argv[]) { const char ** prognamep = argv++; if (argc < 2) { fprintf( stderr, "\n" "Usage: %s [opts] [(-opts|--opts)...] [--] rest-args\n\n" "opts--long sample (POSIXLY_CORRECTish way), " "mimicking some GNU tar(1) options:\n" " -c, -C DIR, -f FILE, -T FILES-FROM\n" " --create, --directory=DIR, --file=FILE, --files-from=FILE\n" " --totals[=SIGNAL] (i.e. optional argument, need = for arg)\n" "and one \"special case\" (not in tar), -NUMBER\n" "First (opt)arg may lack leading '-' " "(like GNU tar) (removable hack)\n" "Single '-' considered non-option; no options after nonoption\n" "License: Unlicense\n\n", *prognamep); exit(0); } char opt_create = 0; int opt_number = -1; const char * opt_file = NULL; const char * opt_ffrm = NULL; const char * opt_chdir = NULL; const char * opt_totals = NULL; for (const char * arg = argv[0]; arg; arg = (++argv)[0]) { if (arg[0] != '-') { #if 0 break; #else if (argv - 1 != prognamep) break; // hack to support trad. tar(1) feature... arg--; #endif } if (arg[1] == 0) break; if (arg[1] == '-') { if (arg[2] == '\0') { argv++; break; } IF_LONGOPT("create") { opt_create = 1; continue; } IF_LONGOPT("totals") { opt_totals = "yes"; continue; } LONGOPTARG(opt_file, "file"); LONGOPTARG(opt_ffrm, "files-from"); LONGOPTARG(opt_chdir, "directory"); LONGOPTARG(opt_totals, "totals"); errx(1, "unrecognized option '%s'", arg); } if (arg[1] >= '0' && arg[1] <= '9') { opt_number = atoi(arg + 1); continue; } for (int c, i = 1; (c = arg[i]) != 0; i++) { switch (c) { case 'c': opt_create = 1; continue; case 'f': opt_file = optarg_(arg + i, &argv); break; case 'T': opt_ffrm = optarg_(arg + i, &argv); break; case 'C': opt_chdir = optarg_(arg + i, &argv); break; default: errx(1, "invalid option -- '%c'", c); } break; } } printf("create: %d\nfile: %s\nfiles from: %s\n" "directory: %s\ntotals: %s\nnumber: %d\n", opt_create, opt_file, opt_ffrm, opt_chdir, opt_totals, opt_number); printf("args left: %d\n", argc - (int)(argv - prognamep)); while (argv[0]) printf("arg: %s\n", argv++[0]); return 0; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.