C examples

From miki
Jump to navigation Jump to search

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

Debug

err, errx, warn, warnx: 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

dump

#ifndef __TRACE_H__
#define __TRACE_H__

#include <stdio.h>

/**
 * Convert an array of char into a list of hexadecimal values.
 * For instance, "\xca\xfe" is converted into "cafe".
 * ############################################################################################################
 */
static uint32_t _char2hex(char *s, const void *data, size_t data_n, size_t n)
{
    uint32_t       o     = 0;
    const uint8_t *data8 = data;

#define out(f, a ...)  {                                          \
        o += (uint32_t)snprintf(s + o, n - (uint32_t)o, f, ## a); \
        o  = o <= n ? o : n;                                      \
}
    if (n > 0) {
        s[0] = 0;
    }
    for (size_t i = 0; i < data_n; i++) {
        out("%.2x", data8[i]);
    }

    return o;
#undef out
}

#define dump(x, n)   xdump(__FILE__, __LINE__, __FUNCTION__, # x, &(x), n)
#define safedump(x)  xdump(__FILE__, __LINE__, __FUNCTION__, # x, &(x), sizeof(x))
static void xdump(const char *file, uint32_t line, const char *fct, const char *s, const void *data, uint32_t n)
{
    char      buf[2 * n + 1];
    uint32_t  o = _char2hex(buf, data, n, sizeof(buf));

    assert(o < sizeof(buf));                                         // Check if given buffer was big enough
    printf("%s:%s:%d: %s = %s\n", fct, file, line, s, buf);
    fflush(stdout);
}
#endif

trace

#define trace(format, a ...)  xtrace(__FILE__, __FUNCTION__, __LINE__, format, ## a)
// ############################################################################################################
int xtrace(const char *file, const char *function, int line, const char *format, ...)
{
  va_list  ap;
  char     buf[256];  // Should be extended if needed

  snprintf(buf, sizeof(buf), "%s:%d (%s)", file, line, function);

  va_start(ap, format);
  int      result = printf("%-30s: ", buf) + vprintf(format, ap);
  va_end(ap);
  return result;
} /* xtrace */