C examples: Difference between revisions
(→trace) |
|||
(2 intermediate revisions by the same user not shown) | |||
Line 192: | Line 192: | ||
* The functions from ''ctype'' library: <code>isprint</code>, <code>isdigit</code>, <code>isxdigit</code>... |
* The functions from ''ctype'' library: <code>isprint</code>, <code>isdigit</code>, <code>isxdigit</code>... |
||
== |
== Debug == |
||
=== err, errx, warn, warnx: Report errors === |
|||
On Linux, use functions <code>err</code>, <code>errx</code> defined in header {{file|err.h}}. This replaces conveniently custom error-reporting functions: |
On Linux, use functions <code>err</code>, <code>errx</code> defined in header {{file|err.h}}. This replaces conveniently custom error-reporting functions: |
||
<source lang="c"> |
<source lang="c"> |
||
Line 205: | Line 206: | ||
</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 224: | ||
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[]) |
||
Line 233: | Line 236: | ||
} |
} |
||
#endif |
#endif |
||
</source> |
|||
=== dump === |
|||
<source lang="C"> |
|||
#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 |
|||
</source> |
|||
=== trace === |
|||
<source lang="C"> |
|||
#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 */ |
|||
</source> |
</source> |
Latest revision as of 18:22, 29 November 2022
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 */