X-Git-Url: https://git.efficios.com/?a=blobdiff_plain;ds=sidebyside;f=common%2Fcommon.c;h=8d202ea5f743a50ad290018f87f0ea02e1f22e25;hb=db0f160afd671de44e52d2b364de957ddccdac02;hp=c5b733a249b38e09d4d5d86bf660c69d4b95c1b9;hpb=e027ff8c5de4bcf3584fc7de7615af295024e90c;p=babeltrace.git diff --git a/common/common.c b/common/common.c index c5b733a2..8d202ea5 100644 --- a/common/common.c +++ b/common/common.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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; +}