Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align examples and remove reading from stdin #255

Merged
merged 22 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5464c80
Remove reading from stdin, align example implementations
oteffahi Feb 22, 2024
482ec03
Add argument parsing implementation for examples
oteffahi Mar 21, 2024
2ab6dc0
Add argument parsing to examples, format files
oteffahi Mar 21, 2024
f8ecc57
Replace getchar with sleep in z_pong example
oteffahi Mar 21, 2024
c7eb3a0
Fix typo in include
oteffahi Mar 22, 2024
d16cba6
Use null-pointers instead of empty strings, remove unnecessary mallocs
oteffahi Mar 22, 2024
dc3ec9c
Free returned pointer after parse_pos_args usage
oteffahi Mar 22, 2024
d022944
Add common and positional args parsing to z_ping example
oteffahi Mar 22, 2024
e4fcb3c
Add formatting for parsed config options
oteffahi Mar 22, 2024
1396838
Add const to function parameters
oteffahi Mar 22, 2024
bd8c0ec
Update mode option help
oteffahi Mar 22, 2024
e8fddc9
Fix pos_args memory leak
oteffahi Mar 28, 2024
6fd4091
Refactor parse_args, remove possible strcpy buffer overflow
oteffahi Mar 28, 2024
184ea9f
Change parse_args function returns to const where applicable
oteffahi Apr 3, 2024
5846a79
Fix const initialization warning
oteffahi Apr 5, 2024
00a54cf
Remove redundant const for value parameters
oteffahi Apr 5, 2024
333839c
Fix buf variable memory leak
oteffahi Apr 12, 2024
bc16f57
Update insert json-list config error message
oteffahi Apr 12, 2024
bdfb131
Add usage example for -e and -l arguments in help
oteffahi Apr 12, 2024
c386aa8
Update example notation in help message
oteffahi Apr 12, 2024
46b82d8
Update example notation in help message (2/2)
oteffahi Apr 12, 2024
70ac694
Fix parameter in error message
oteffahi Apr 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions examples/parse_args.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
//
// Copyright (c) 2024 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
// ZettaScale Zenoh Team, <[email protected]>
//

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

#include "zenoh.h"

#define COMMON_HELP \
"\
-c <CONFIG> (optional, string): The path to a configuration file for the session. If this option isn't passed, the default configuration will be used.\n\
-m <MODE> (optional, string, default='peer'): JSON-serialized string of the zenoh session mode. [possible values: 'peer', 'client', 'router']\n\
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
-e <CONNECT> (optional, string): JSON-serialized list of locators to connect to. If none are given, endpoints will be discovered through multicast-scouting if it is enabled.\n\
-l <LISTEN> (optional, string): JSON-serialized list of locators to listen on. If none are given, the default configuration will be used.\n\
--no-multicast-scouting (optional): By default zenohd replies to multicast scouting messages for being discovered by peers and clients. This option disables this feature.\n\
"

/**
* Parse an option of format `-f`, `--flag`, `-f <value>` or `--flag <value>` from `argv`. If found, the option and its
* eventual value are each replaced by an empty string in `argv`
* @param argc: argc passed from `main` function
* @param argv: argv passed from `main` function
* @param opt: option to parse (without `-` or `--` prefix)
* @param opt_has_value: if true, the option is of format `-f <value>` or `--flag <value>` and `value` will be returned
* if found, else an error message is printed and program will exit. If false, option has no value and a non-null
* pointer will be returned if option is found.
* @returns NULL if option was not found, else a non-null value depending on if `opt_has_value`.
*/
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
char* parse_opt(int argc, char** argv, char* opt, bool opt_has_value) {
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
size_t optlen = strlen(opt);
for (int i = 1; i < argc; i++) {
size_t len = strlen(argv[i]);
if (len >= 2) {
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
if (optlen == 1) {
if (argv[i][0] == '-' && argv[i][1] == opt[0]) {
argv[i][0] = '\0';
if (!opt_has_value) {
return opt;
} else if (i + 1 < argc && strlen(argv[i + 1]) > 0) {
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
char* buf = (char*)malloc(strlen(argv[i + 1]));
strcpy(buf, argv[i + 1]);
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
argv[i + 1][0] = '\0';
return buf;
} else {
printf("Option -%s given without a value\n", opt);
exit(-1);
}
}
} else if (optlen > 1 && len > 3 && argv[i][0] == '-' && argv[i][1] == '-') {
// Note: support for '--arg=<value>' syntax can be added here
if (strcmp(argv[i] + 2, opt) == 0) {
argv[i][0] = '\0';
if (!opt_has_value) {
return opt;
} else if (i + 1 < argc && strlen(argv[i + 1]) > 0) {
char* buf = (char*)malloc(strlen(argv[i + 1]));
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
strcpy(buf, argv[i + 1]);
argv[i + 1][0] = '\0';
return buf;
} else {
printf("Option --%s given without a value\n", opt);
exit(-1);
}
}
}
}
}
return NULL;
}

/**
* Check if any options remains in `argv`. Must be called after all expected options are parsed
* @param argc
* @param argv
* @returns NULL if no option was found, else the first option string that was found
*/
char* check_unknown_opts(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
return argv[i];
}
}
return NULL;
}

/**
* Parse positional arguments from `argv`. Must be called after all expected options are parsed, and after checking that
* no unknown options remain in `argv`
* @param argc
* @param argv
* @param nb_args: number of expected positional arguments
* @returns NULL if found more positional arguments than `nb_args`. Else an array of found arguments in order, followed
* by NULL values if found less positional arguments than `nb_args`
*/
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
char** parse_pos_args(int argc, char** argv, size_t nb_args) {
char** pos_argv = (char**)malloc(nb_args * sizeof(char*));
// Initialize pointers to NULL to detect when example is called with number of args < nb_args
for (int i = 0; i < nb_args; i++) {
pos_argv[i] = NULL;
}
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
size_t pos_argc = 0;
for (int i = 1; i < argc; i++) {
if (strlen(argv[i]) > 0) {
pos_argc++;
if (pos_argc > nb_args) {
free(pos_argv);
return NULL;
}
pos_argv[pos_argc - 1] = argv[i];
}
}
return pos_argv;
}

/**
* Parse zenoh options that are common to all examples (-c, -m, -e, -l, --no-multicast-scouting) and add them to
* `config`
* @param argc
* @param argv
* @param config: address of an owned zenoh configuration
*/
void parse_zenoh_common_args(int argc, char** argv, z_owned_config_t* config) {
// -c: A configuration file.
char* config_file = parse_opt(argc, argv, "c", true);
if (config_file) {
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
*config = zc_config_from_file(config_file);
}
// -m: The Zenoh session mode [default: peer].
char* mode = parse_opt(argc, argv, "m", true);
if (mode && zc_config_insert_json(z_loan(*config), Z_CONFIG_MODE_KEY, mode) < 0) {
printf(
"Couldn't insert value `%s` in configuration at `%s`. This is likely because `%s` expects a "
"JSON-serialized string. Value must be one of: 'client', 'peer' or 'router'\n",
mode, Z_CONFIG_MODE_KEY, Z_CONFIG_MODE_KEY);
exit(-1);
}
// -e: Endpoints to connect to.
char* connect = parse_opt(argc, argv, "e", true);
if (connect && zc_config_insert_json(z_loan(*config), Z_CONFIG_CONNECT_KEY, connect) < 0) {
printf(
"Couldn't insert value `%s` in configuration at `%s`. This is likely because `%s` expects a "
"JSON-serialized list of strings\n",
connect, Z_CONFIG_CONNECT_KEY, Z_CONFIG_CONNECT_KEY);
exit(-1);
}
// -l: Endpoints to listen on.
char* listen = parse_opt(argc, argv, "l", true);
if (listen && zc_config_insert_json(z_loan(*config), Z_CONFIG_LISTEN_KEY, listen) < 0) {
printf(
"Couldn't insert value `%s` in configuration at `%s`. This is likely because `%s` expects a "
"JSON-serialized list of strings\n",
listen, Z_CONFIG_LISTEN_KEY, Z_CONFIG_LISTEN_KEY);
exit(-1);
}
// --no-multicast-scrouting: Disable the multicast-based scouting mechanism.
char* no_multicast_scouting = parse_opt(argc, argv, "no-multicast-scouting", false);
if (no_multicast_scouting && zc_config_insert_json(z_loan(*config), Z_CONFIG_MULTICAST_SCOUTING_KEY, "false") < 0) {
printf("Couldn't disable multicast-scouting.\n");
exit(-1);
}
}
60 changes: 46 additions & 14 deletions examples/z_delete.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,19 @@
#include <stdio.h>
#include <string.h>

#include "parse_args.h"
#include "zenoh.h"

int main(int argc, char **argv) {
char *keyexpr = "demo/example/zenoh-c-put";
#define DEFAULT_KEYEXPR "demo/example/zenoh-c-put"

if (argc > 1) keyexpr = argv[1];
struct args_t {
char* keyexpr; // -k
};
struct args_t parse_args(int argc, char** argv, z_owned_config_t* config);

int main(int argc, char** argv) {
z_owned_config_t config = z_config_default();
if (argc > 3) {
if (zc_config_insert_json(z_loan(config), Z_CONFIG_CONNECT_KEY, argv[3]) < 0) {
printf(
"Couldn't insert value `%s` in configuration at `%s`. This is likely because `%s` expects a "
"JSON-serialized list of strings\n",
argv[3], Z_CONFIG_CONNECT_KEY, Z_CONFIG_CONNECT_KEY);
exit(-1);
}
}
struct args_t args = parse_args(argc, argv, &config);

printf("Opening session...\n");
z_owned_session_t s = z_open(z_move(config));
Expand All @@ -39,13 +35,49 @@ int main(int argc, char **argv) {
exit(-1);
}

printf("Deleting resources matching '%s'...\n", keyexpr);
printf("Deleting resources matching '%s'...\n", args.keyexpr);
z_delete_options_t options = z_delete_options_default();
int res = z_delete(z_loan(s), z_keyexpr(keyexpr), &options);
int res = z_delete(z_loan(s), z_keyexpr(args.keyexpr), &options);
if (res < 0) {
printf("Delete failed...\n");
}

z_close(z_move(s));
return 0;
}

void print_help() {
printf(
"\
Usage: z_delete [OPTIONS]\n\n\
Options:\n\
-k <KEY> (optional, string, default='%s'): The key expression to write to\n",
DEFAULT_KEYEXPR);
printf(COMMON_HELP);
printf(
"\
-h: print help\n");
}

struct args_t parse_args(int argc, char** argv, z_owned_config_t* config) {
if (parse_opt(argc, argv, "h", false)) {
print_help();
exit(1);
}
char* keyexpr = parse_opt(argc, argv, "k", true);
if (!keyexpr) {
keyexpr = DEFAULT_KEYEXPR;
}
parse_zenoh_common_args(argc, argv, config);
char* arg = check_unknown_opts(argc, argv);
if (arg) {
printf("Unknown option %s\n", arg);
exit(-1);
}
char** pos_args = parse_pos_args(argc, argv, 1);
if (!pos_args || pos_args[0]) {
printf("Unexpected positional arguments\n");
exit(-1);
}
oteffahi marked this conversation as resolved.
Show resolved Hide resolved
return (struct args_t){.keyexpr = keyexpr};
}
91 changes: 61 additions & 30 deletions examples/z_get.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,27 @@
#include <stdio.h>
#include <string.h>

#include "parse_args.h"
#include "zenoh.h"

int main(int argc, char **argv) {
char *expr = "demo/example/**";
char *value = NULL;
switch (argc) {
default:
case 3:
value = argv[2];
case 2:
expr = argv[1];
break;
case 1:
// Do nothing
break;
}
z_keyexpr_t keyexpr = z_keyexpr(expr);
#define DEFAULT_SELECTOR "demo/example/**"
#define DEFAULT_VALUE NULL

struct args_t {
char* selector; // -s
char* value; // -v
};
struct args_t parse_args(int argc, char** argv, z_owned_config_t* config);
oteffahi marked this conversation as resolved.
Show resolved Hide resolved

int main(int argc, char** argv) {
z_owned_config_t config = z_config_default();
struct args_t args = parse_args(argc, argv, &config);

z_keyexpr_t keyexpr = z_keyexpr(args.selector);
if (!z_check(keyexpr)) {
printf("%s is not a valid key expression", expr);
printf("%s is not a valid key expression", args.selector);
exit(-1);
}
z_owned_config_t config = z_config_default();
if (argc > 3) {
if (zc_config_insert_json(z_loan(config), Z_CONFIG_CONNECT_KEY, argv[3]) < 0) {
printf(
"Couldn't insert value `%s` in configuration at `%s`. This is likely because `%s` expects a "
"JSON-serialized list of strings\n",
argv[3], Z_CONFIG_CONNECT_KEY, Z_CONFIG_CONNECT_KEY);
exit(-1);
}
}

printf("Opening session...\n");
z_owned_session_t s = z_open(z_move(config));
Expand All @@ -53,11 +43,11 @@ int main(int argc, char **argv) {
exit(-1);
}

printf("Sending Query '%s'...\n", expr);
printf("Sending Query '%s'...\n", args.selector);
z_owned_reply_channel_t channel = zc_reply_fifo_new(16);
z_get_options_t opts = z_get_options_default();
if (value != NULL) {
opts.value.payload = z_bytes_from_str(value);
if (args.value != NULL) {
opts.value.payload = z_bytes_from_str(args.value);
}
z_get(z_loan(s), keyexpr, "", z_move(channel.send),
&opts); // here, the send is moved and will be dropped by zenoh when adequate
Expand All @@ -76,4 +66,45 @@ int main(int argc, char **argv) {
z_drop(z_move(channel));
z_close(z_move(s));
return 0;
}
}

void print_help() {
printf(
"\
Usage: z_get [OPTIONS]\n\n\
Options:\n\
-s <SELECTOR> (optional, string, default='%s'): The selection of resources to query\n\
-v <VALUE> (optional, string): An optional value to put in the query\n",
DEFAULT_SELECTOR);
printf(COMMON_HELP);
printf(
"\
-h: print help\n");
}

struct args_t parse_args(int argc, char** argv, z_owned_config_t* config) {
if (parse_opt(argc, argv, "h", false)) {
print_help();
exit(1);
}
char* selector = parse_opt(argc, argv, "s", true);
if (!selector) {
selector = DEFAULT_SELECTOR;
}
char* value = parse_opt(argc, argv, "v", true);
if (!value) {
value = DEFAULT_VALUE;
}
parse_zenoh_common_args(argc, argv, config);
char* arg = check_unknown_opts(argc, argv);
if (arg) {
printf("Unknown option %s\n", arg);
exit(-1);
}
char** pos_args = parse_pos_args(argc, argv, 1);
if (!pos_args || pos_args[0]) {
printf("Unexpected positional arguments\n");
exit(-1);
}
return (struct args_t){.selector = selector, .value = value};
}
Loading
Loading