CLI: add `run` command and make `convert` command use it
[babeltrace.git] / common / common.c
index c5b733a249b38e09d4d5d86bf660c69d4b95c1b9..8d202ea5f743a50ad290018f87f0ea02e1f22e25 100644 (file)
@@ -28,6 +28,7 @@
 #include <pwd.h>
 #include <unistd.h>
 #include <assert.h>
+#include <ctype.h>
 #include <glib.h>
 #include <babeltrace/babeltrace-internal.h>
 #include <babeltrace/common-internal.h>
@@ -313,3 +314,355 @@ const char *bt_common_color_bg_light_gray(void)
        return bt_common_colors_supported() ?
                BT_COMMON_COLOR_BG_LIGHT_GRAY : "";
 }
+
+BT_HIDDEN
+GString *bt_common_string_until(const char *input, const char *escapable_chars,
+               const char *end_chars, size_t *end_pos)
+{
+       GString *output = g_string_new(NULL);
+       const char *ch;
+       const char *es_char;
+       const char *end_char;
+
+       if (!output) {
+               goto error;
+       }
+
+       for (ch = input; *ch != '\0'; ch++) {
+               if (*ch == '\\') {
+                       bool continue_loop = false;
+
+                       if (ch[1] == '\0') {
+                               /* `\` at the end of the string: append `\` */
+                               g_string_append_c(output, *ch);
+                               ch++;
+                               goto set_end_pos;
+                       }
+
+                       for (es_char = escapable_chars; *es_char != '\0'; es_char++) {
+                               if (ch[1] == *es_char) {
+                                       /*
+                                        * `\` followed by an escapable
+                                        * character: append the escaped
+                                        * character only.
+                                        */
+                                       g_string_append_c(output, ch[1]);
+                                       ch++;
+                                       continue_loop = true;
+                                       break;
+                               }
+                       }
+
+                       if (continue_loop) {
+                               continue;
+                       }
+
+                       /*
+                        * `\` followed by a non-escapable character:
+                        * append `\` and the character.
+                        */
+                       g_string_append_c(output, *ch);
+                       g_string_append_c(output, ch[1]);
+                       ch++;
+                       continue;
+               } else {
+                       for (end_char = end_chars; *end_char != '\0'; end_char++) {
+                               if (*ch == *end_char) {
+                                       /*
+                                        * End character found:
+                                        * terminate this loop.
+                                        */
+                                       goto set_end_pos;
+                               }
+                       }
+
+                       /* Normal character: append */
+                       g_string_append_c(output, *ch);
+               }
+       }
+
+set_end_pos:
+       if (end_pos) {
+               *end_pos = ch - input;
+       }
+
+       goto end;
+
+error:
+       if (output) {
+               g_string_free(output, TRUE);
+       }
+
+end:
+       return output;
+}
+
+BT_HIDDEN
+GString *bt_common_shell_quote(const char *input)
+{
+       GString *output = g_string_new(NULL);
+       const char *ch;
+       bool no_quote = true;
+
+       if (!output) {
+               goto end;
+       }
+
+       if (strlen(input) == 0) {
+               g_string_assign(output, "''");
+               goto end;
+       }
+
+       for (ch = input; *ch != '\0'; ch++) {
+               const char c = *ch;
+
+               if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' &&
+                               c != '@' && c != '%' && c != '+' && c != '=' &&
+                               c != ':' && c != ',' && c != '.' && c != '/' &&
+                               c != '-') {
+                       no_quote = false;
+                       break;
+               }
+       }
+
+       if (no_quote) {
+               g_string_assign(output, input);
+               goto end;
+       }
+
+       g_string_assign(output, "'");
+
+       for (ch = input; *ch != '\0'; ch++) {
+               if (*ch == '\'') {
+                       g_string_append(output, "'\"'\"'");
+               } else {
+                       g_string_append_c(output, *ch);
+               }
+       }
+
+       g_string_append_c(output, '\'');
+
+end:
+       return output;
+}
+
+BT_HIDDEN
+bool bt_common_string_is_printable(const char *input)
+{
+       const char *ch;
+       bool printable = true;
+       assert(input);
+
+       for (ch = input; *ch != '\0'; ch++) {
+               if (!isprint(*ch) && *ch != '\n' && *ch != '\r' &&
+                               *ch != '\t' && *ch != '\v') {
+                       printable = false;
+                       goto end;
+               }
+       }
+
+end:
+       return printable;
+}
+
+BT_HIDDEN
+void bt_common_destroy_lttng_live_url_parts(
+               struct bt_common_lttng_live_url_parts *parts)
+{
+       if (!parts) {
+               goto end;
+       }
+
+       if (parts->proto) {
+               g_string_free(parts->proto, TRUE);
+               parts->proto = NULL;
+       }
+
+       if (parts->hostname) {
+               g_string_free(parts->hostname, TRUE);
+               parts->hostname = NULL;
+       }
+
+       if (parts->target_hostname) {
+               g_string_free(parts->target_hostname, TRUE);
+               parts->target_hostname = NULL;
+       }
+
+       if (parts->session_name) {
+               g_string_free(parts->session_name, TRUE);
+               parts->session_name = NULL;
+       }
+
+end:
+       return;
+}
+
+BT_HIDDEN
+struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
+               const char *url, char *error_buf, size_t error_buf_size)
+{
+       struct bt_common_lttng_live_url_parts parts;
+       const char *at = url;
+       size_t end_pos;
+
+       assert(url);
+       memset(&parts, 0, sizeof(parts));
+       parts.port = -1;
+
+       /* Protocol */
+       parts.proto = bt_common_string_until(at, "", ":", &end_pos);
+       if (!parts.proto || parts.proto->len == 0) {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size, "Missing protocol");
+               }
+
+               goto error;
+       }
+
+       if (strcmp(parts.proto->str, "net") == 0) {
+               g_string_assign(parts.proto, "net4");
+       }
+
+       if (strcmp(parts.proto->str, "net4") != 0 &&
+                       strcmp(parts.proto->str, "net6") != 0) {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size,
+                               "Unknown protocol: `%s`", parts.proto->str);
+               }
+
+               goto error;
+       }
+
+       if (at[end_pos] != ':') {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size,
+                               "Expecting `:` after `%s`", parts.proto->str);
+               }
+
+               goto error;
+       }
+
+       at += end_pos;
+
+       /* :// */
+       if (strncmp(at, "://", 3) != 0) {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size,
+                               "Expecting `://` after protocol");
+               }
+
+               goto error;
+       }
+
+       at += 3;
+
+       /* Hostname */
+       parts.hostname = bt_common_string_until(at, "", ":/", &end_pos);
+       if (!parts.hostname || parts.hostname->len == 0) {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size, "Missing hostname");
+               }
+
+               goto error;
+       }
+
+       if (at[end_pos] == ':') {
+               /* Port */
+               GString *port;
+
+               at += end_pos + 1;
+               port = bt_common_string_until(at, "", "/", &end_pos);
+               if (!port || port->len == 0) {
+                       if (error_buf) {
+                               snprintf(error_buf, error_buf_size, "Missing port");
+                       }
+
+                       goto error;
+               }
+
+               if (sscanf(port->str, "%d", &parts.port) != 1) {
+                       if (error_buf) {
+                               snprintf(error_buf, error_buf_size,
+                                       "Invalid port: `%s`", port->str);
+                       }
+
+                       g_string_free(port, TRUE);
+                       goto error;
+               }
+
+               g_string_free(port, TRUE);
+
+               if (parts.port < 0 || parts.port >= 65536) {
+                       if (error_buf) {
+                               snprintf(error_buf, error_buf_size,
+                                       "Invalid port: %d", parts.port);
+                       }
+
+                       goto error;
+               }
+       }
+
+       if (at[end_pos] == '\0') {
+               goto end;
+       }
+
+       at += end_pos;
+
+       /* /host/ */
+       if (strncmp(at, "/host/", 6) != 0) {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size,
+                               "Expecting `/host/` after hostname or port");
+               }
+
+               goto error;
+       }
+
+       at += 6;
+
+       /* Target hostname */
+       parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos);
+       if (!parts.target_hostname || parts.target_hostname->len == 0) {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size,
+                               "Missing target hostname");
+               }
+
+               goto error;
+       }
+
+       if (at[end_pos] == '\0') {
+               goto end;
+       }
+
+       at += end_pos + 1;
+
+       /* Session name */
+       parts.session_name = bt_common_string_until(at, "", "/", &end_pos);
+       if (!parts.session_name || parts.session_name->len == 0) {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size,
+                               "Missing session name");
+               }
+
+               goto error;
+       }
+
+       if (at[end_pos] == '/') {
+               if (error_buf) {
+                       snprintf(error_buf, error_buf_size,
+                               "Unexpected `/` after session name (`%s`)",
+                               parts.session_name->str);
+               }
+
+               goto error;
+       }
+
+       goto end;
+
+error:
+       bt_common_destroy_lttng_live_url_parts(&parts);
+
+end:
+       return parts;
+}
This page took 0.026779 seconds and 4 git commands to generate.