summaryrefslogtreecommitdiff
path: root/src/core/traffic.c
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
commit20cb29c2f8c5c87bc590896854a20b1473ceb358 (patch)
tree2857f41513a56ad41af97b57362639298aa7f033 /src/core/traffic.c
#2
Diffstat (limited to 'src/core/traffic.c')
-rw-r--r--src/core/traffic.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/core/traffic.c b/src/core/traffic.c
new file mode 100644
index 0000000..b74d48f
--- /dev/null
+++ b/src/core/traffic.c
@@ -0,0 +1,294 @@
+#include "hybbx/traffic.h"
+#if !defined(HYBBX_CLIENT_BUILD)
+#include "hybbx/config.h"
+#endif
+#include "hybbx/session.h"
+#include "hybbx/terminal.h"
+#include "hybbx/util.h"
+#include "hybbx/log.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#if defined(_WIN32)
+#include <windows.h>
+#else
+#include <sys/select.h>
+#include <sys/time.h>
+#endif
+
+static hybbx_traffic_config_t g_traffic_config;
+static int g_traffic_config_ready;
+
+void hybbx_traffic_config_defaults(hybbx_traffic_config_t *cfg)
+{
+ if (cfg == NULL) {
+ return;
+ }
+
+ cfg->baud = HYBBX_BAUD2400;
+ cfg->line_width = HYBBX_LINE_WIDTH;
+ cfg->pace_output = 1;
+ cfg->ansi = 0;
+ cfg->input_echo = 0;
+}
+
+#if !defined(HYBBX_CLIENT_BUILD)
+static unsigned parse_uint_default(const hybbx_config_t *config,
+ const char *section,
+ const char *key,
+ unsigned default_value,
+ unsigned min_value,
+ unsigned max_value)
+{
+ unsigned value;
+
+ value = hybbx_config_get_uint(config, section, key, default_value,
+ min_value, max_value);
+ return value;
+}
+#endif
+
+void hybbx_traffic_config_apply(const struct hybbx_config *config)
+{
+ hybbx_traffic_config_defaults(&g_traffic_config);
+
+#if !defined(HYBBX_CLIENT_BUILD)
+ if (config != NULL) {
+ g_traffic_config.baud = parse_uint_default(
+ config, "traffic", "baud", HYBBX_BAUD2400, 300u, 38400u);
+ g_traffic_config.line_width = parse_uint_default(
+ config, "traffic", "line_width", HYBBX_LINE_WIDTH, 20u,
+ HYBBX_LINE_WIDTH_MAX);
+ g_traffic_config.pace_output =
+ hybbx_config_get_bool(config, "traffic", "pace_output", 1);
+ g_traffic_config.ansi =
+ hybbx_config_get_bool(config, "traffic", "ansi", 0);
+ g_traffic_config.input_echo =
+ hybbx_config_get_bool(config, "traffic", "input_echo", 0);
+ }
+
+ hybbx_log_info("[traffic] baud=%u line_width=%u pace=%s ansi=%s echo=%s",
+ g_traffic_config.baud, g_traffic_config.line_width,
+ hybbx_bool_to_string(g_traffic_config.pace_output),
+ hybbx_bool_to_string(g_traffic_config.ansi),
+ hybbx_bool_to_string(g_traffic_config.input_echo));
+#else
+ (void)config;
+#endif
+
+ g_traffic_config_ready = 1;
+}
+
+const hybbx_traffic_config_t *hybbx_traffic_config_get(void)
+{
+ if (!g_traffic_config_ready) {
+ hybbx_traffic_config_defaults(&g_traffic_config);
+ g_traffic_config_ready = 1;
+ }
+
+ return &g_traffic_config;
+}
+
+unsigned hybbx_traffic_byte_delay_us(unsigned baud)
+{
+ if (baud == 0) {
+ return 0;
+ }
+
+ return 10000000u / baud;
+}
+
+static hybbx_result_t emit_raw(struct hybbx_session *session, char ch)
+{
+ const hybbx_traffic_config_t *cfg = hybbx_traffic_config_get();
+ char byte = ch;
+ hybbx_result_t rc;
+
+ if (session == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (session->transport != NULL && session->transport->write != NULL) {
+ rc = session->transport->write(session, &byte, 1);
+ } else {
+ if (fputc((unsigned char)byte, stdout) == EOF) {
+ return HYBBX_ERR_IO;
+ }
+ fflush(stdout);
+ rc = HYBBX_OK;
+ }
+
+ if (rc == HYBBX_OK && cfg->pace_output && cfg->baud > 0) {
+ unsigned delay = hybbx_traffic_byte_delay_us(cfg->baud);
+
+ if (delay > 0) {
+#if defined(_WIN32)
+ Sleep((DWORD)((delay + 999u) / 1000u));
+#else
+ struct timeval tv;
+
+ tv.tv_sec = (time_t)(delay / 1000000u);
+#if defined(__AMIGA__)
+ tv.tv_usec = (unsigned long)(delay % 1000000u);
+#else
+ tv.tv_usec = (suseconds_t)(delay % 1000000u);
+#endif
+ (void)select(0, NULL, NULL, NULL, &tv);
+#endif
+ }
+ }
+
+ return rc;
+}
+
+static hybbx_result_t emit_newline(struct hybbx_session *session,
+ unsigned *out_col)
+{
+ hybbx_result_t rc;
+
+ rc = emit_raw(session, '\r');
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ rc = emit_raw(session, '\n');
+ if (rc == HYBBX_OK && out_col != NULL) {
+ *out_col = 0;
+ }
+
+ return rc;
+}
+
+static int traffic_plain_char(const char *src, size_t len, size_t *consumed,
+ char *out)
+{
+ unsigned char ch;
+
+ if (src == NULL || len == 0 || consumed == NULL || out == NULL) {
+ return 0;
+ }
+
+ ch = (unsigned char)src[0];
+
+ if (ch == '\r') {
+ *consumed = 1;
+ *out = '\n';
+ return 1;
+ }
+
+ if (ch == '\n') {
+ *consumed = 1;
+ *out = '\n';
+ return 1;
+ }
+
+ if (ch == '\t') {
+ *consumed = 1;
+ *out = ' ';
+ return 1;
+ }
+
+ if (ch < 0x20 || ch == 0x7f) {
+ *consumed = 1;
+ return 0;
+ }
+
+ if (ch == 0x1b && len >= 2 && src[1] == '[') {
+ size_t i = 2;
+
+ while (i < len && (src[i] < 0x40 || src[i] > 0x7e)) {
+ i++;
+ }
+ if (i < len) {
+ *consumed = i + 1;
+ } else {
+ *consumed = len;
+ }
+ return 0;
+ }
+
+ if (ch == 0x1b) {
+ *consumed = 1;
+ return 0;
+ }
+
+ *consumed = 1;
+ *out = (char)ch;
+ return 1;
+}
+
+hybbx_result_t hybbx_traffic_emit(struct hybbx_session *session,
+ unsigned *out_col,
+ const char *data, size_t len)
+{
+ const hybbx_traffic_config_t *cfg = hybbx_traffic_config_get();
+ size_t pos = 0;
+ unsigned col = out_col != NULL ? *out_col : 0;
+
+ if (session == NULL || data == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (len == 0) {
+ return HYBBX_OK;
+ }
+
+ if (cfg->ansi && strcmp(data, HYBBX_TERM_SGR_LIGHTGRAY_ON_BLACK) == 0 &&
+ len == strlen(HYBBX_TERM_SGR_LIGHTGRAY_ON_BLACK)) {
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ hybbx_result_t rc = emit_raw(session, data[i]);
+
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+ }
+
+ return HYBBX_OK;
+ }
+
+ while (pos < len) {
+ char ch;
+ size_t step = 0;
+ hybbx_result_t rc;
+
+ if (!traffic_plain_char(data + pos, len - pos, &step, &ch)) {
+ pos += step > 0 ? step : 1;
+ continue;
+ }
+
+ pos += step;
+
+ if (ch == '\n') {
+ rc = emit_newline(session, out_col);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+ col = 0;
+ continue;
+ }
+
+ if (cfg->line_width > 0 && col >= cfg->line_width) {
+ rc = emit_newline(session, out_col);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+ col = 0;
+ }
+
+ rc = emit_raw(session, ch);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ col++;
+ }
+
+ if (out_col != NULL) {
+ *out_col = col;
+ }
+
+ return HYBBX_OK;
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com