From d8a822a3fb39078f90deb7b14d51a00c8af7ac06 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 19 Feb 2022 02:12:24 +0100 Subject: [PATCH] Add support for configurable timestamp format Also changes default timestamp format from ISO8601 to classic 24-hour format as this is assumed to be the format that most users would prefer. And reintroduces strict but optional ISO8601 format. This feature allows to easily add more timestamp formats in the future. --- README.md | 4 +-- man/tio.1.in | 17 +++++++++-- src/bash-completion/tio.in | 2 +- src/misc.c | 58 ++++++++++++++++++++++++++++---------- src/options.c | 30 ++++++++++++++++---- src/options.h | 10 ++++++- 6 files changed, 94 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 772d25f..d80dd0a 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The command-line interface is straightforward as reflected in the output from -o, --output-delay Output delay (default: 0) -n, --no-autoconnect Disable automatic connect -e, --local-echo Enable local echo - -t, --timestamp Enable line timestamp + -t, --timestamp[=] Enable timestamp (default: 24hour) -L, --list-devices List available serial devices -l, --log Log to file -m, --map Map special characters @@ -42,7 +42,7 @@ The command-line interface is straightforward as reflected in the output from -v, --version Display version -h, --help Display help - See the man page for list of supported mapping flags. + See the man page for more details. In session, press ctrl-t q to quit. ``` diff --git a/man/tio.1.in b/man/tio.1.in index 7ff3825..c37f225 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -57,9 +57,21 @@ option is provided, tio will exit if the device is not present or an established Enable local echo. .TP -.BR \-t ", " \-\-timestamp +.BR \-t ", " \-\-timestamp[=] -Enable line timestamp. +Enable timestamp. Optionally you can specify any of the following timestamp formats: +.RS +.TP 16n +.IP "\fB24hour" +24-hour format ("hh:mm:ss.sss") +.IP "\fB24hour-start" +24-hour format relative to start time +.IP "\fBiso8601" +ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss") +.P +Default format is +.B 24hour +.RE .TP .BR \-L ", " \-\-list\-devices @@ -75,7 +87,6 @@ Log to file. .BR \-m ", " "\-\-map " \fI Map (replace, translate) special characters on input or output. The following mapping flags are supported: - .RS .TP 12n .IP "\fBICRNL" diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 2457f75..d64d7e3 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -69,7 +69,7 @@ _tio() return 0 ;; -t | --timestamp) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + COMPREPLY=( $(compgen -W "24hour 24hour-start iso8601" -- ${cur}) ) return 0 ;; -L | --list-devices) diff --git a/src/misc.c b/src/misc.c index 0f99024..1a40a0c 100644 --- a/src/misc.c +++ b/src/misc.c @@ -27,31 +27,59 @@ #include #include "error.h" #include "print.h" +#include "options.h" -// "YYYY-MM-DD hh:mm:ss.sss" (ISO-8601/RFC3339 format) -#define TIME_STRING_SIZE 24 +#define TIME_STRING_SIZE_MAX 24 -char * current_time(void) +char *current_time(void) { - static char time_string[TIME_STRING_SIZE]; - struct tm *tmp; - + static char time_string[TIME_STRING_SIZE_MAX]; + static struct timeval tv_start; + static bool first = true; + struct tm *tm; struct timeval tv; - gettimeofday(&tv,NULL); + size_t len; - tmp = localtime(&tv.tv_sec); - if (tmp == NULL) + gettimeofday(&tv, NULL); + + if (first) { - error_printf("Retrieving local time failed"); - exit(EXIT_FAILURE); + tv_start = tv; + first = false; } - size_t len = strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", tmp); - if (len) { - len = snprintf(time_string + len, TIME_STRING_SIZE - len, ".%03ld", (long)tv.tv_usec / 1000); + // Add formatted timestap + switch (option.timestamp) + { + case TIMESTAMP_NONE: + case TIMESTAMP_24HOUR: + // "hh:mm:ss.sss" (24 hour format) + tm = localtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); + break; + case TIMESTAMP_24HOUR_START: + // "hh:mm:ss.sss" (24 hour format relative to start time) + timersub(&tv, &tv_start, &tv); + tv.tv_sec -= 3600; // Why is this needed?? + tm = localtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); + break; + case TIMESTAMP_ISO8601: + // "YYYY-MM-DDThh:mm:ss.sss" (ISO-8601) + tm = localtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm); + break; + default: + return NULL; } - return (len < TIME_STRING_SIZE) ? time_string : NULL; + // Append milliseconds to all timestamps + if (len) + { + len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%03ld", (long)tv.tv_usec / 1000); + } + + return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL; } void delay(long ms) diff --git a/src/options.c b/src/options.c index 160faea..ed68437 100644 --- a/src/options.c +++ b/src/options.c @@ -46,7 +46,7 @@ struct option_t option = .no_autoconnect = false, .log = false, .local_echo = false, - .timestamp = false, + .timestamp = TIMESTAMP_NONE, .list_devices = false, .log_filename = "", .map = "", @@ -66,7 +66,7 @@ void print_help(char *argv[]) printf(" -o, --output-delay Output delay (default: 0)\n"); printf(" -n, --no-autoconnect Disable automatic connect\n"); printf(" -e, --local-echo Enable local echo\n"); - printf(" -t, --timestamp Enable line timestamp\n"); + printf(" -t, --timestamp[=] Enable timestamp (default: 24hour)\n"); printf(" -L, --list-devices List available serial devices\n"); printf(" -l, --log Log to file\n"); printf(" -m, --map Map special characters\n"); @@ -118,7 +118,7 @@ void parse_options(int argc, char *argv[]) {"output-delay", required_argument, 0, 'o'}, {"no-autoconnect", no_argument, 0, 'n'}, {"local-echo", no_argument, 0, 'e'}, - {"timestamp", no_argument, 0, 't'}, + {"timestamp", optional_argument, 0, 't'}, {"list-devices", no_argument, 0, 'L'}, {"log", required_argument, 0, 'l'}, {"map", required_argument, 0, 'm'}, @@ -132,7 +132,7 @@ void parse_options(int argc, char *argv[]) int option_index = 0; /* Parse argument using getopt_long */ - c = getopt_long(argc, argv, "b:d:f:s:p:o:netLl:m:c:vh", long_options, &option_index); + c = getopt_long(argc, argv, "b:d:f:s:p:o:net::Ll:m:c:vh", long_options, &option_index); /* Detect the end of the options */ if (c == -1) @@ -183,7 +183,27 @@ void parse_options(int argc, char *argv[]) break; case 't': - option.timestamp = true; + option.timestamp = TIMESTAMP_24HOUR; // Default + if (optarg != NULL) + { + if (strcmp(optarg, "24hour") == 0) + { + option.timestamp = TIMESTAMP_24HOUR; + } + else if (strcmp(optarg, "24hour-start") == 0) + { + option.timestamp = TIMESTAMP_24HOUR_START; + } + else if (strcmp(optarg, "iso8601") == 0) + { + option.timestamp = TIMESTAMP_ISO8601; + } + else + { + printf("Error: Unknown timestamp type\n"); + exit(EXIT_FAILURE); + } + } break; case 'L': diff --git a/src/options.h b/src/options.h index 8ff8eea..b03e9f0 100644 --- a/src/options.h +++ b/src/options.h @@ -26,6 +26,14 @@ #include #include +enum timestamp_t +{ + TIMESTAMP_NONE, + TIMESTAMP_24HOUR, + TIMESTAMP_24HOUR_START, + TIMESTAMP_ISO8601, +}; + /* Options */ struct option_t { @@ -39,7 +47,7 @@ struct option_t bool no_autoconnect; bool log; bool local_echo; - bool timestamp; + enum timestamp_t timestamp; bool list_devices; const char *log_filename; const char *map;