C examples: Difference between revisions

From miki
Jump to navigation Jump to search
Line 205: Line 205:
</source>
</source>


On Windows, <code>errx</code> and <code>warnx</code> can be implemented as:
On Windows, <code>errx</code> and <code>warnx</code> can be implemented as (io flush seemed necessary when running through SSH to a remote MSYS2):

<source lang="c">
<source lang="c">
#if defined(WIN32)
#if defined(WIN32)
Line 222: Line 223:
va_end(ap);
va_end(ap);
fprintf(stderr, "\n");
fprintf(stderr, "\n");
fflush(stdout); // necessary in weird cases


exit(errcode);
exit(errcode);
}
}


#define warnx(fmt, ...) fprintf(stderr, "%s: " fmt "\n", APPNAME, ## __VA_ARGS__)
#define warnx(fmt, ...) do { fprintf(stderr, "%s: " fmt "\n", APPNAME, ## __VA_ARGS__); fflush(stderr); } while (0)


int main(int argc, char *argv[])
int main(int argc, char *argv[])

Revision as of 00:17, 7 March 2021

Parse command-line parameters

The standard solution is to use either getopt or argp SO, GNU C library.

Using getopt

This is suitable to parse short arguments, although getopt can also parse long ones. The short-option only version is actually a part of the POSIX standard.

From SO:

#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.

    // ...
}

Below another example using short options from the GNU C lib manual. See that manual for another example using getopt_long.

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main (int argc, char **argv)
{
  int aflag = 0;
  int bflag = 0;
  char *cvalue = NULL;
  int index;
  int c;

  opterr = 0;


  while ((c = getopt (argc, argv, "abc:")) != -1)
    switch (c)
      {
      case 'a':
        aflag = 1;
        break;
      case 'b':
        bflag = 1;
        break;
      case 'c':
        cvalue = optarg;
        break;
      case '?':
        if (optopt == 'c')
          fprintf (stderr, "Option -%c requires an argument.\n", optopt);
        else if (isprint (optopt))
          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
        else
          fprintf (stderr,
                   "Unknown option character `\\x%x'.\n",
                   optopt);
        return 1;
      default:
        abort ();
      }


  printf ("aflag = %d, bflag = %d, cvalue = %s\n",
          aflag, bflag, cvalue);

  for (index = optind; index < argc; index++)
    printf ("Non-option argument %s\n", argv[index]);
  return 0;
}

Using argp

argp (Linux only) is a complete solution to parse long parameters, including support for -?, --help, -V, --version, and --usage.

From SO:

#include <argp.h>                  // Not available on Windows/MSYS2
#include <stdbool.h>

const char *argp_program_version = "programname programversion";
const char *argp_program_bug_address = "<your@email.address>";
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);

    // ...
}

Below the smallest program using argp, from GNU C Lib

/* This is (probably) the smallest possible program that
   uses argp.  It won’t do much except give an error
   messages and exit when there are any arguments, and print
   a (rather pointless) messages for –help. */

#include <stdlib.h>
#include <argp.h>

int
main (int argc, char **argv)
{
  argp_parse (0, argc, argv, 0, 0, 0);
  exit (0);
}

Another example using only default options. See the GNU C Lib manual for more examples including user-defined options, and sub-options.

#include <stdlib.h>
#include <argp.h>

const char *argp_program_version =
  "argp-ex2 1.0";
const char *argp_program_bug_address =
  "<bug-gnu-utils@gnu.org>";

/* Program documentation. */
static char doc[] =
  "Argp example #2 -- a pretty minimal program using argp";

/* Our argument parser.  The options, parser, and
   args_doc fields are zero because we have neither options or
   arguments; doc and argp_program_bug_address will be
   used in the output for ‘--help’, and the ‘--version’
   option will print out argp_program_version. */
static struct argp argp = { 0, 0, 0, doc };

int
main (int argc, char **argv)
{
  argp_parse (&argp, argc, argv, 0, 0, 0);
  exit (0);
}

Useful function for arg parsing

  • The functions from ctype library: isprint, isdigit, isxdigit...

Report errors

On Linux, use functions err, errx defined in header err.h. This replaces conveniently custom error-reporting functions:

#include <err.h>

// ...

if ( bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0 ) {
    err(1, "bind failed");
    /* errx(1, "bind failed: ", strerror(errno)); // Equivalent */
}

On Windows, errx and warnx can be implemented as (io flush seemed necessary when running through SSH to a remote MSYS2):

#if defined(WIN32)
#include <libgen.h>

const char * APPNAME; 

__attribute__((noreturn))
void errx(int errcode, const char *format, ...)
{
    va_list  ap;

    fprintf(stderr, "%s: ", APPNAME);
    va_start(ap, format);
    vfprintf(stderr, format, ap);
    va_end(ap);
    fprintf(stderr, "\n");
    fflush(stdout); // necessary in weird cases

    exit(errcode);
}

#define warnx(fmt, ...)  do { fprintf(stderr, "%s: " fmt "\n", APPNAME, ## __VA_ARGS__); fflush(stderr); } while (0)

int main(int argc, char *argv[])
{
    APPNAME = basename(argv[0]);
}
#endif