gpiomon: simplify custom formats
authorBartosz Golaszewski <bartekgola@gmail.com>
Wed, 21 Jun 2017 11:47:30 +0000 (13:47 +0200)
committerBartosz Golaszewski <bartekgola@gmail.com>
Wed, 21 Jun 2017 11:53:18 +0000 (13:53 +0200)
Instead of replacing the format specifiers in a separate buffer, just
make use of the output stream buffering.

Also: update the gpiomon tests with some corner cases.

Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
src/tools/gpiomon.c
tests/tests-gpiomon.c

index 10b07d9a3440413c8332c72e356db812b9f17f05..53402ac52a04bf2c9118a2e5b8751a7dd19c5e6b 100644 (file)
@@ -70,94 +70,56 @@ struct callback_data {
        char *fmt;
 };
 
-static void replace_fmt(char **base, size_t off, const char *new)
-{
-       size_t newlen, baselen;
-       char *tmp;
-
-       newlen = strlen(new);
-       baselen = strlen(*base);
-
-       if (newlen > 2) {
-               /*
-                * FIXME This should be done with realloc() but valgrind
-                * is reporting problems with using uninitialized memory.
-                *
-                * Also: it could be made more efficient by allocating more
-                * memory at the beginning and then doubling the size of the
-                * buffer once the previous one is exhausted.
-                */
-               tmp = malloc(baselen + newlen + 1);
-               if (!tmp)
-                       die("out of memory");
-
-               memset(tmp, 0, baselen + newlen + 1);
-               strcpy(tmp, *base);
-               free(*base);
-               *base = tmp;
-       }
-
-       memmove(*base + off + newlen, *base + off + 2, baselen - off - 2);
-       strncpy(*base + off, new, newlen);
-
-       if (newlen < 2)
-               *(*base + baselen - 1) = '\0';
-}
-
 static void event_print_custom(int type, const struct timespec *ts,
                               struct callback_data *data)
 {
-       char repbuf[64], *str, *pos, fmt;
-       size_t off;
+       char *prev, *curr, fmt;
 
-       str = strdup(data->fmt);
-       if (!str)
-               die("out of memory");
-
-       for (off = 0;;) {
-               pos = strchr(str + off, '%');
-               if (!pos)
+       for (prev = curr = data->fmt;;) {
+               curr = strchr(curr, '%');
+               if (!curr) {
+                       fputs(prev, stdout);
                        break;
+               }
 
-               fmt = *(pos + 1);
-               off = pos - str;
+               if (prev != curr)
+                       fwrite(prev, curr - prev, 1, stdout);
 
-               if (fmt == '%') {
-                       memmove(str + off, str + off + 1, strlen(str) - off);
-                       off += 1;
-                       continue;
-               }
+               fmt = *(curr + 1);
 
                switch (fmt) {
                case 'o':
-                       snprintf(repbuf, sizeof(repbuf), "%u", data->offset);
-                       replace_fmt(&str, off, repbuf);
+                       printf("%u", data->offset);
                        break;
                case 'e':
                        if (type == GPIOD_EVENT_CB_RISING_EDGE)
-                               snprintf(repbuf, sizeof(repbuf), "1");
+                               fputc('1', stdout);
                        else
-                               snprintf(repbuf, sizeof(repbuf), "0");
-                       replace_fmt(&str, off, repbuf);
+                               fputc('0', stdout);
                        break;
                case 's':
-                       snprintf(repbuf, sizeof(repbuf), "%ld", ts->tv_sec);
-                       replace_fmt(&str, off, repbuf);
+                       printf("%ld", ts->tv_sec);
                        break;
                case 'n':
-                       snprintf(repbuf, sizeof(repbuf), "%ld", ts->tv_nsec);
-                       replace_fmt(&str, off, repbuf);
+                       printf("%ld", ts->tv_nsec);
                        break;
+               case '%':
+                       fputc('%', stdout);
+                       break;
+               case '\0':
+                       fputc('%', stdout);
+                       goto end;
                default:
-                       off += 2;
-                       continue;
+                       fwrite(curr, 2, 1, stdout);
+                       break;
                }
 
-               off += strlen(repbuf);
+               curr += 2;
+               prev = curr;
        }
 
-       printf("%s\n", str);
-       free(str);
+end:
+       fputc('\n', stdout);
 }
 
 static void event_print_human_readable(int type, const struct timespec *ts,
index 4080af9bf7745e27e5e76b33f02b97028240b8f0..19b9a16620d1097ef408e84f9e8fbdb7423a38de 100644 (file)
@@ -242,7 +242,24 @@ TEST_DEFINE(gpiomon_custom_format_timestamp,
            "tools: gpiomon - custom output format: timestamp",
            0, { 8, 8 });
 
-static void gpiomon_custom_format_double_percent(void)
+static void gpiomon_custom_format_double_percent_sign(void)
+{
+       test_tool_run("gpiomon", "--num-events=1", "--format=%%",
+                     test_chip_name(0), "3", (char *)NULL);
+       test_set_event(0, 3, TEST_EVENT_RISING, 100);
+       test_tool_wait();
+
+       TEST_ASSERT(test_tool_exited());
+       TEST_ASSERT_RET_OK(test_tool_exit_status());
+       TEST_ASSERT_NOT_NULL(test_tool_stdout());
+       TEST_ASSERT_NULL(test_tool_stderr());
+       TEST_ASSERT_STR_EQ(test_tool_stdout(), "%\n");
+}
+TEST_DEFINE(gpiomon_custom_format_double_percent_sign,
+           "tools: gpiomon - custom output format: double percent sign",
+           0, { 8, 8 });
+
+static void gpiomon_custom_format_double_percent_sign_and_spec(void)
 {
        test_tool_run("gpiomon", "--num-events=1", "--format=%%e",
                      test_chip_name(0), "3", (char *)NULL);
@@ -255,8 +272,42 @@ static void gpiomon_custom_format_double_percent(void)
        TEST_ASSERT_NULL(test_tool_stderr());
        TEST_ASSERT_STR_EQ(test_tool_stdout(), "%e\n");
 }
-TEST_DEFINE(gpiomon_custom_format_double_percent,
-           "tools: gpiomon - custom output format: double percent",
+TEST_DEFINE(gpiomon_custom_format_double_percent_sign_and_spec,
+           "tools: gpiomon - custom output format: double percent sign with specifier",
+           0, { 8, 8 });
+
+static void gpiomon_custom_format_single_percent_sign(void)
+{
+       test_tool_run("gpiomon", "--num-events=1", "--format=%",
+                     test_chip_name(0), "3", (char *)NULL);
+       test_set_event(0, 3, TEST_EVENT_RISING, 100);
+       test_tool_wait();
+
+       TEST_ASSERT(test_tool_exited());
+       TEST_ASSERT_RET_OK(test_tool_exit_status());
+       TEST_ASSERT_NOT_NULL(test_tool_stdout());
+       TEST_ASSERT_NULL(test_tool_stderr());
+       TEST_ASSERT_STR_EQ(test_tool_stdout(), "%\n");
+}
+TEST_DEFINE(gpiomon_custom_format_single_percent_sign,
+           "tools: gpiomon - custom output format: single percent sign",
+           0, { 8, 8 });
+
+static void gpiomon_custom_format_single_percent_sign_between_chars(void)
+{
+       test_tool_run("gpiomon", "--num-events=1", "--format=foo % bar",
+                     test_chip_name(0), "3", (char *)NULL);
+       test_set_event(0, 3, TEST_EVENT_RISING, 100);
+       test_tool_wait();
+
+       TEST_ASSERT(test_tool_exited());
+       TEST_ASSERT_RET_OK(test_tool_exit_status());
+       TEST_ASSERT_NOT_NULL(test_tool_stdout());
+       TEST_ASSERT_NULL(test_tool_stderr());
+       TEST_ASSERT_STR_EQ(test_tool_stdout(), "foo % bar\n");
+}
+TEST_DEFINE(gpiomon_custom_format_single_percent_sign_between_chars,
+           "tools: gpiomon - custom output format: single percent sign between other characters",
            0, { 8, 8 });
 
 static void gpiomon_custom_format_unknown_specifier(void)