aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorOskari Timperi <oskari.timperi@iki.fi>2017-02-12 17:12:52 +0200
committerOskari Timperi <oskari.timperi@iki.fi>2017-02-12 17:12:52 +0200
commitcd2faf64b2e995dee42e7e3911325d372c6604a1 (patch)
treee0ab608cdd7005e759b570a5a4ec2031191bafb5 /tools
parent54f78d73ec378fe1d62a696edeb2ddc5a59e2669 (diff)
downloadmqtt-cd2faf64b2e995dee42e7e3911325d372c6604a1.tar.gz
mqtt-cd2faf64b2e995dee42e7e3911325d372c6604a1.zip
Add all the stuff
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt7
-rw-r--r--tools/amalgamate.py90
-rw-r--r--tools/getopt.c358
-rw-r--r--tools/getopt.h175
-rw-r--r--tools/pub.c96
-rw-r--r--tools/sub.c103
6 files changed, 829 insertions, 0 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..1a736ab
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,7 @@
+ADD_LIBRARY(getopt OBJECT getopt.c)
+
+ADD_EXECUTABLE(pub pub.c $<TARGET_OBJECTS:getopt>)
+TARGET_LINK_LIBRARIES(pub mqtt)
+
+ADD_EXECUTABLE(sub sub.c $<TARGET_OBJECTS:getopt>)
+TARGET_LINK_LIBRARIES(sub mqtt)
diff --git a/tools/amalgamate.py b/tools/amalgamate.py
new file mode 100644
index 0000000..a807603
--- /dev/null
+++ b/tools/amalgamate.py
@@ -0,0 +1,90 @@
+import io
+import os
+import sys
+
+this_dir = os.path.dirname(os.path.abspath(__file__))
+
+src_dir = os.path.join(this_dir, '..', 'src')
+
+sources = (
+ 'queue.h',
+ 'log.h',
+ 'misc.c',
+ 'stringbuf.c',
+ 'stream.c',
+ 'socketstream.c',
+ 'stringstream.c',
+ 'stream_mqtt.c',
+ 'socket.c',
+ 'packet.c',
+ 'serialize.c',
+ 'deserialize.c',
+ 'client.c'
+)
+
+
+def is_header(filename):
+ return os.path.splitext(filename)[1] == '.h'
+
+
+def get_header(src):
+ root, ext = os.path.splitext(src)
+ return root + '.h'
+
+
+def read_file(filename):
+ def tounicode(s):
+ if sys.version_info[0] == 2:
+ return s.decode('utf-8')
+ else:
+ return s
+ with open(filename, 'r') as fp:
+ buf = io.StringIO()
+ for line in fp:
+ if line.startswith('#include "'):
+ if line[10:].startswith('mqtt.h'):
+ pass
+ else:
+ continue
+ buf.write(tounicode(line))
+ return buf.getvalue()
+
+
+def file_header(filename):
+ filename = os.path.basename(filename)
+ # how long lines we create
+ linelen = 72
+ # how much space left after the necessary comment markup
+ chars = linelen - 4
+ # how much padding in total for filename
+ padding = chars - len(filename)
+ padding_l = padding // 2
+ padding_r = padding - padding_l
+ lines = (
+ '',
+ '/*' + '*'*chars + '*/',
+ '/*' + ' '*padding_l + filename + ' '*padding_r + '*/',
+ '/*' + '*'*chars + '*/',
+ '\n',
+ )
+ return '\n'.join(lines)
+
+
+def write_file(output, srcfilename):
+ output.write(file_header(srcfilename))
+ output.write(read_file(srcfilename))
+
+
+output_filename = sys.argv[1]
+
+with open(output_filename, 'w') as out:
+ for source in sources:
+ path = os.path.join(src_dir, source)
+
+ if is_header(path):
+ write_file(out, path)
+ else:
+ header = get_header(path)
+ if os.path.isfile(header):
+ write_file(out, header)
+ write_file(out, path)
diff --git a/tools/getopt.c b/tools/getopt.c
new file mode 100644
index 0000000..5277ed0
--- /dev/null
+++ b/tools/getopt.c
@@ -0,0 +1,358 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "getopt.h"
+
+/*
+ * Standard getopt global variables. optreset starts as non-zero in order to
+ * trigger initialization behaviour.
+ */
+const char * optarg = NULL;
+int optind = 1;
+int opterr = 1;
+int optreset = 1;
+
+/*
+ * Quasi-internal global variables -- these are used via GETOPT macros.
+ */
+const char * getopt_dummy = "(dummy)";
+int getopt_initialized = 0;
+
+/*
+ * Internal variables.
+ */
+static const char * cmdname = NULL;
+static struct opt {
+ const char * os;
+ size_t olen;
+ int hasarg;
+} * opts = NULL;
+static size_t nopts;
+static size_t opt_missing;
+static size_t opt_default;
+static size_t opt_found;
+static const char * packedopts;
+static char popt[3];
+static int atexit_registered = 0;
+
+/* Print a message. */
+#define PRINTMSG(...) do { \
+ if (cmdname != NULL) \
+ fprintf(stderr, "%s: ", cmdname); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+} while (0)
+
+/* Print an error message and die. */
+#define DIE(...) do { \
+ PRINTMSG(__VA_ARGS__); \
+ abort(); \
+} while (0)
+
+/* Print a warning, if warnings are enabled. */
+#define WARN(...) do { \
+ if (opterr == 0) \
+ break; \
+ if (opt_missing != opt_default) \
+ break; \
+ PRINTMSG(__VA_ARGS__); \
+} while (0)
+
+/* Free allocated options array. */
+static void
+atexit_handler(void)
+{
+
+ free(opts);
+ opts = NULL;
+}
+
+/* Reset internal state. */
+static void
+reset(int argc, char * const argv[])
+{
+ const char * p;
+
+ /* If we have arguments, stash argv[0] for error messages. */
+ if (argc > 0) {
+ /* Find the basename, without leading directories. */
+ for (p = cmdname = argv[0]; *p != '\0'; p++) {
+ if (*p == '/')
+ cmdname = p + 1;
+ }
+ }
+
+ /* Discard any registered command-line options. */
+ free(opts);
+ opts = NULL;
+
+ /* Register atexit handler if we haven't done so already. */
+ if (!atexit_registered) {
+ atexit(atexit_handler);
+ atexit_registered = 1;
+ }
+
+ /* We will start scanning from the first option. */
+ optind = 1;
+
+ /* We're not in the middle of any packed options. */
+ packedopts = NULL;
+
+ /* We haven't found any option yet. */
+ opt_found = (size_t)(-1);
+
+ /* We're not initialized yet. */
+ getopt_initialized = 0;
+
+ /* Finished resetting state. */
+ optreset = 0;
+}
+
+/* Search for an option string. */
+static size_t
+searchopt(const char * os)
+{
+ size_t i;
+
+ /* Scan the array of options. */
+ for (i = 0; i < nopts; i++) {
+ /* Is there an option in this slot? */
+ if (opts[i].os == NULL)
+ continue;
+
+ /* Does this match up to the length of the option string? */
+ if (strncmp(opts[i].os, os, opts[i].olen))
+ continue;
+
+ /* Do we have <option>\0 or <option>= ? */
+ if ((os[opts[i].olen] == '\0') || (os[opts[i].olen] == '='))
+ return (i);
+ }
+
+ /* Not found. */
+ return (opt_default);
+}
+
+const char *
+getopt(int argc, char * const argv[])
+{
+ const char * os = NULL;
+ const char * canonical_os = NULL;
+
+ /* No argument yet. */
+ optarg = NULL;
+
+ /* Reset the getopt state if needed. */
+ if (optreset)
+ reset(argc, argv);
+
+ /* If not initialized, return dummy option. */
+ if (!getopt_initialized)
+ return (GETOPT_DUMMY);
+
+ /* If we've run out of arguments, we're done. */
+ if (optind >= argc)
+ return (NULL);
+
+ /*
+ * If we're not already in the middle of a packed single-character
+ * options, see if we should start.
+ */
+ if ((packedopts == NULL) && (argv[optind][0] == '-') &&
+ (argv[optind][1] != '-') && (argv[optind][1] != '\0')) {
+ /* We have one or more single-character options. */
+ packedopts = &argv[optind][1];
+ }
+
+ /* If we're processing single-character options, fish one out. */
+ if (packedopts != NULL) {
+ /* Construct the option string. */
+ popt[0] = '-';
+ popt[1] = *packedopts;
+ popt[2] = '\0';
+ os = popt;
+
+ /* We've done this character. */
+ packedopts++;
+
+ /* Are we done with this string? */
+ if (*packedopts == '\0') {
+ packedopts = NULL;
+ optind++;
+ }
+ }
+
+ /* If we don't have an option yet, do we have dash-dash? */
+ if ((os == NULL) && (argv[optind][0] == '-') &&
+ (argv[optind][1] == '-')) {
+ /* If this is not "--\0", it's an option. */
+ if (argv[optind][2] != '\0')
+ os = argv[optind];
+
+ /* Either way, we want to eat the string. */
+ optind++;
+ }
+
+ /* If we have found nothing which looks like an option, we're done. */
+ if (os == NULL)
+ return (NULL);
+
+ /* Search for the potential option. */
+ opt_found = searchopt(os);
+
+ /* If the option is not registered, give up now. */
+ if (opt_found == opt_default) {
+ WARN("unknown option: %s", os);
+ return (os);
+ }
+
+ /* The canonical option string is the one registered. */
+ canonical_os = opts[opt_found].os;
+
+ /* Does the option take an argument? */
+ if (opts[opt_found].hasarg) {
+ /*
+ * If we're processing packed single-character options, the
+ * rest of the string is the argument to this option.
+ */
+ if (packedopts != NULL) {
+ optarg = packedopts;
+ packedopts = NULL;
+ optind++;
+ }
+
+ /*
+ * If the option string is <option>=<value>, extract that
+ * value as the option argument.
+ */
+ if (os[opts[opt_found].olen] == '=')
+ optarg = &os[opts[opt_found].olen + 1];
+
+ /*
+ * If we don't have an argument yet, take one from the
+ * remaining command line.
+ */
+ if ((optarg == NULL) && (optind < argc))
+ optarg = argv[optind++];
+
+ /* If we still have no option, declare it MIA. */
+ if (optarg == NULL) {
+ WARN("option requires an argument: %s",
+ opts[opt_found].os);
+ opt_found = opt_missing;
+ }
+ } else {
+ /* If we have --foo=bar, something went wrong. */
+ if (os[opts[opt_found].olen] == '=') {
+ WARN("option doesn't take an argument: %s",
+ opts[opt_found].os);
+ opt_found = opt_default;
+ }
+ }
+
+ /* Return the canonical option string. */
+ return (canonical_os);
+}
+
+size_t
+getopt_lookup(const char * os)
+{
+
+ /* Can't reset here. */
+ if (optreset)
+ DIE("Can't reset in the middle of getopt loop");
+
+ /* We should only be called after initialization is complete. */
+ assert(getopt_initialized);
+
+ /* GETOPT_DUMMY should never get passed back to us. */
+ assert(os != GETOPT_DUMMY);
+
+ /*
+ * Make sure the option passed back to us corresponds to the one we
+ * found earlier.
+ */
+ assert((opt_found == opt_missing) || (opt_found == opt_default) ||
+ ((opt_found < nopts) && (strcmp(os, opts[opt_found].os) == 0)));
+
+ /* Return the option number we identified earlier. */
+ return (opt_found);
+}
+
+void
+getopt_register_opt(const char * os, size_t ln, int hasarg)
+{
+
+ /* Can't reset here. */
+ if (optreset)
+ DIE("Can't reset in the middle of getopt loop");
+
+ /* We should only be called during initialization. */
+ assert(!getopt_initialized);
+
+ /* We should have space allocated for registering options. */
+ assert(opts != NULL);
+
+ /* We should not have registered an option here yet. */
+ assert(opts[ln].os == NULL);
+
+ /* Options should be "-X" or "--foo". */
+ if ((os[0] != '-') || (os[1] == '\0') ||
+ ((os[1] == '-') && (os[2] == '\0')) ||
+ ((os[1] != '-') && (os[2] != '\0')))
+ DIE("Not a valid command-line option: %s", os);
+
+ /* Make sure we haven't already registered this option. */
+ if (searchopt(os) != opt_default)
+ DIE("Command-line option registered twice: %s", os);
+
+ /* Record option. */
+ opts[ln].os = os;
+ opts[ln].olen = strlen(os);
+ opts[ln].hasarg = hasarg;
+}
+
+void
+getopt_register_missing(size_t ln)
+{
+
+ /* Can't reset here. */
+ if (optreset)
+ DIE("Can't reset in the middle of getopt loop");
+
+ /* We should only be called during initialization. */
+ assert(!getopt_initialized);
+
+ /* Record missing-argument value. */
+ opt_missing = ln;
+}
+
+void
+getopt_setrange(size_t ln)
+{
+ size_t i;
+
+ /* Can't reset here. */
+ if (optreset)
+ DIE("Can't reset in the middle of getopt loop");
+
+ /* We should only be called during initialization. */
+ assert(!getopt_initialized);
+
+ /* Allocate space for options. */
+ opts = malloc(ln * sizeof(struct opt));
+ if ((ln > 0) && (opts == NULL))
+ DIE("Failed to allocate memory in getopt");
+
+ /* Initialize options. */
+ for (i = 0; i < ln; i++)
+ opts[i].os = NULL;
+
+ /* Record the number of (potential) options. */
+ nopts = ln;
+
+ /* Record default missing-argument and no-such-option values. */
+ opt_missing = opt_default = ln + 1;
+}
diff --git a/tools/getopt.h b/tools/getopt.h
new file mode 100644
index 0000000..5ed3145
--- /dev/null
+++ b/tools/getopt.h
@@ -0,0 +1,175 @@
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+#include <setjmp.h>
+#include <stddef.h>
+
+/**
+ * This getopt implementation parses options of the following forms:
+ * -a -b -c foo (single-character options)
+ * -abc foo (packed single-character options)
+ * -abcfoo (packed single-character options and an argument)
+ * --foo bar (long option)
+ * --foo=bar (long option and argument separated by '=')
+ *
+ * It does not support abbreviated options (e.g., interpreting --foo as
+ * --foobar when there are no other --foo* options) since that misfeature
+ * results in breakage when new options are added. It also does not support
+ * options appearing after non-options (e.g., "cp foo bar -R") since that is
+ * a horrible GNU perversion.
+ */
+
+/* Work around LLVM bug. */
+#ifdef __clang__
+#warning Working around bug in LLVM optimizer
+#warning For more details see https://llvm.org/bugs/show_bug.cgi?id=27190
+#define DO_SETJMP _DO_SETJMP(__LINE__)
+#define _DO_SETJMP(x) __DO_SETJMP(x)
+#define __DO_SETJMP(x) \
+ void * getopt_initloop = && getopt_initloop_ ## x; \
+ getopt_initloop_ ## x:
+#define DO_LONGJMP \
+ goto *getopt_initloop
+#else
+#define DO_SETJMP \
+ sigjmp_buf getopt_initloop; \
+ if (!getopt_initialized) \
+ sigsetjmp(getopt_initloop, 0)
+#define DO_LONGJMP \
+ siglongjmp(getopt_initloop, 1)
+#endif
+
+/* Avoid namespace collisions with libc getopt. */
+#define getopt libcperciva_getopt
+#define optarg libcperciva_optarg
+#define optind libcperciva_optind
+#define opterr libcperciva_opterr
+#define optreset libcperciva_optreset
+
+/* Standard getopt global variables. */
+extern const char * optarg;
+extern int optind, opterr, optreset;
+
+/* Dummy option string, equal to "(dummy)". */
+#define GETOPT_DUMMY getopt_dummy
+
+/**
+ * GETOPT(argc, argv):
+ * When called for the first time (or the first time after optreset is set to
+ * a nonzero value), return GETOPT_DUMMY, aka. "(dummy)". Thereafter, return
+ * the next option string and set optarg / optind appropriately; abort if not
+ * properly initialized when not being called for the first time.
+ */
+#define GETOPT(argc, argv) getopt(argc, argv)
+
+/**
+ * GETOPT_SWITCH(ch):
+ * Jump to the appropriate GETOPT_OPT, GETOPT_OPTARG, GETOPT_MISSING_ARG, or
+ * GETOPT_DEFAULT based on the option string ${ch}. When called for the first
+ * time, perform magic to index the options.
+ *
+ * GETOPT_SWITCH(ch) is equivalent to "switch (ch)" in a standard getopt loop.
+ */
+#define GETOPT_SWITCH(ch) \
+ volatile size_t getopt_ln_min = __LINE__; \
+ volatile size_t getopt_ln = getopt_ln_min - 1; \
+ volatile int getopt_default_missing = 0; \
+ DO_SETJMP; \
+ switch (getopt_initialized ? getopt_lookup(ch) + getopt_ln_min : getopt_ln++)
+
+/**
+ * GETOPT_OPT(os):
+ * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH.
+ *
+ * GETOPT_OPT("-x") is equivalent to "case 'x'" in a standard getopt loop
+ * which has an optstring containing "x".
+ */
+#define GETOPT_OPT(os) _GETOPT_OPT(os, __LINE__)
+#define _GETOPT_OPT(os, ln) __GETOPT_OPT(os, ln)
+#define __GETOPT_OPT(os, ln) \
+ case ln: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ getopt_register_opt(os, ln - getopt_ln_min, 0); \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/**
+ * GETOPT_OPTARG(os):
+ * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH,
+ * unless no argument is available, in which case jump to GETOPT_MISSING_ARG
+ * (if present) or GETOPT_DEFAULT (if not).
+ *
+ * GETOPT_OPTARG("-x") is equivalent to "case 'x'" in a standard getopt loop
+ * which has an optstring containing "x:".
+ */
+#define GETOPT_OPTARG(os) _GETOPT_OPTARG(os, __LINE__)
+#define _GETOPT_OPTARG(os, ln) __GETOPT_OPTARG(os, ln)
+#define __GETOPT_OPTARG(os, ln) \
+ case ln: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ getopt_register_opt(os, ln - getopt_ln_min, 1); \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/**
+ * GETOPT_MISSING_ARG:
+ * Jump to this point if an option string specified in GETOPT_OPTARG is seen
+ * but no argument is available.
+ *
+ * GETOPT_MISSING_ARG is equivalent to "case ':'" in a standard getopt loop
+ * which has an optstring starting with ":". As such, it also has the effect
+ * of disabling warnings about invalid options, as if opterr had been zeroed.
+ */
+#define GETOPT_MISSING_ARG _GETOPT_MISSING_ARG(__LINE__)
+#define _GETOPT_MISSING_ARG(ln) __GETOPT_MISSING_ARG(ln)
+#define __GETOPT_MISSING_ARG(ln) \
+ case ln: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ getopt_register_missing(ln - getopt_ln_min); \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/**
+ * GETOPT_DEFAULT:
+ * Jump to this point if an unrecognized option is seen or if an option
+ * specified in GETOPT_OPTARG is seen, no argument is available, and there is
+ * no GETOPT_MISSING_ARG label.
+ *
+ * GETOPT_DEFAULT is equivalent to "case '?'" in a standard getopt loop.
+ *
+ * NOTE: This MUST be present in the GETOPT_SWITCH statement, and MUST occur
+ * after all other GETOPT_* labels.
+ */
+#define GETOPT_DEFAULT _GETOPT_DEFAULT(__LINE__)
+#define _GETOPT_DEFAULT(ln) __GETOPT_DEFAULT(ln)
+#define __GETOPT_DEFAULT(ln) \
+ goto getopt_skip_ ## ln; \
+ case ln: \
+ getopt_initialized = 1; \
+ break; \
+ default: \
+ if (getopt_initialized) \
+ goto getopt_skip_ ## ln; \
+ if (!getopt_default_missing) { \
+ getopt_setrange(ln - getopt_ln_min); \
+ getopt_default_missing = 1; \
+ } \
+ DO_LONGJMP; \
+ getopt_skip_ ## ln
+
+/*
+ * The back-end implementation. These should be considered internal
+ * interfaces and not used directly.
+ */
+const char * getopt(int, char * const []);
+size_t getopt_lookup(const char *);
+void getopt_register_opt(const char *, size_t, int);
+void getopt_register_missing(size_t);
+void getopt_setrange(size_t);
+extern const char * getopt_dummy;
+extern int getopt_initialized;
+
+#endif /* !_GETOPT_H_ */
diff --git a/tools/pub.c b/tools/pub.c
new file mode 100644
index 0000000..9b5af82
--- /dev/null
+++ b/tools/pub.c
@@ -0,0 +1,96 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mqtt.h"
+#include "getopt.h"
+
+struct options
+{
+ int qos;
+ int retain;
+ const char *topic;
+ const char *message;
+};
+
+void onConnect(MqttClient *client, MqttConnectionStatus status,
+ int sessionPresent)
+{
+ struct options *options = (struct options *) MqttClientGetUserData(client);
+ (void) client;
+ printf("onConnect rv=%d sessionPresent=%d\n", status, sessionPresent);
+ MqttClientPublishCString(client, options->qos, options->retain,
+ options->topic, options->message);
+ if (options->qos == 0)
+ MqttClientDisconnect(client);
+}
+
+void onPublish(MqttClient *client, int id)
+{
+ printf("onPublish id=%d\n", id);
+ MqttClientDisconnect(client);
+}
+
+void usage(const char *prog)
+{
+ fprintf(stderr, "%s [--qos QOS] [--retain] [--topic TOPIC] [--message MESSAGE]\n",
+ prog);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ MqttClient *client;
+ const char *opt;
+ struct options options;
+
+ options.qos = 0;
+ options.retain = 0;
+ options.topic = "my/topic";
+ options.message = "hello, world!";
+
+ while ((opt = GETOPT(argc, argv)) != NULL)
+ {
+ GETOPT_SWITCH(opt)
+ {
+ GETOPT_OPTARG("--qos"):
+ options.qos = strtol(optarg, NULL, 10);
+ if (options.qos < 0 || options.qos > 2)
+ {
+ fprintf(stderr, "invalid qos: %s\n", optarg);
+ return 1;
+ }
+ break;
+
+ GETOPT_OPT("--retain"):
+ options.retain = 1;
+ break;
+
+ GETOPT_OPTARG("--topic"):
+ options.topic = optarg;
+ break;
+
+ GETOPT_OPTARG("--message"):
+ options.message = optarg;
+ break;
+
+ GETOPT_MISSING_ARG:
+ fprintf(stderr, "missing argument to: %s\n", opt);
+
+ GETOPT_DEFAULT:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ client = MqttClientNew(NULL, 1);
+
+ MqttClientSetOnConnect(client, onConnect);
+ MqttClientSetOnPublish(client, onPublish);
+ MqttClientSetUserData(client, &options);
+
+ MqttClientConnect(client, "test.mosquitto.org", 1883, 60);
+
+ MqttClientRun(client);
+
+ return 0;
+}
diff --git a/tools/sub.c b/tools/sub.c
new file mode 100644
index 0000000..634c1e3
--- /dev/null
+++ b/tools/sub.c
@@ -0,0 +1,103 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mqtt.h"
+#include "getopt.h"
+
+struct options
+{
+ int qos;
+ const char *topic;
+ int clean;
+ const char *client_id;
+};
+
+void onConnect(MqttClient *client, MqttConnectionStatus status,
+ int sessionPresent)
+{
+ struct options *options = (struct options *) MqttClientGetUserData(client);
+ (void) client;
+ printf("onConnect rv=%d sessionPresent=%d\n", status, sessionPresent);
+ MqttClientSubscribe(client, options->topic, options->qos);
+}
+
+void onSubscribe(MqttClient *client, int id, MqttSubscriptionStatus status)
+{
+ (void) client;
+ printf("onSubscribe id=%d status=%d\n", id, status);
+}
+
+void onMessage(MqttClient *client, const char *topic, const void *data,
+ size_t size)
+{
+ (void) client;
+ printf("onMessage topic=<%s> message=<%.*s>\n", topic, (int) size,
+ (char *) data);
+ // MqttClientUnsubscribe(client, topic);
+}
+
+void usage(const char *prog)
+{
+ fprintf(stderr, "%s [--qos QOS] [--topic TOPIC] [--clean] [--id ID]\n",
+ prog);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ MqttClient *client;
+ const char *opt;
+ struct options options;
+
+ options.qos = 0;
+ options.topic = "$SYS/broker/load/messages/#";
+ options.clean = 1;
+ options.client_id = NULL;
+
+ while ((opt = GETOPT(argc, argv)) != NULL)
+ {
+ GETOPT_SWITCH(opt)
+ {
+ GETOPT_OPTARG("--qos"):
+ options.qos = strtol(optarg, NULL, 10);
+ if (options.qos < 0 || options.qos > 2)
+ {
+ fprintf(stderr, "invalid qos: %s\n", optarg);
+ return 1;
+ }
+ break;
+
+ GETOPT_OPTARG("--topic"):
+ options.topic = optarg;
+ break;
+
+ GETOPT_OPT("--no-clean"):
+ options.clean = 0;
+ break;
+
+ GETOPT_OPTARG("--id"):
+ options.client_id = optarg;
+ break;
+
+ GETOPT_MISSING_ARG:
+ fprintf(stderr, "missing argument to: %s\n", opt);
+
+ GETOPT_DEFAULT:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ client = MqttClientNew(options.client_id, options.clean);
+
+ MqttClientSetOnConnect(client, onConnect);
+ MqttClientSetOnSubscribe(client, onSubscribe);
+ MqttClientSetOnMessage(client, onMessage);
+ MqttClientSetUserData(client, &options);
+
+ MqttClientConnect(client, "test.mosquitto.org", 1883, 60);
+
+ MqttClientRun(client);
+
+ return 0;
+}