| | #include "../../unity/unity.h" |
| | #include <stdio.h> |
| | #include <stdlib.h> |
| | #include <string.h> |
| | #include <unistd.h> |
| | #include <errno.h> |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | static void build_line(struct line *ln, const char *const *vals, const idx_t *lens, idx_t n) |
| | { |
| | memset(ln, 0, sizeof(*ln)); |
| | ln->nfields = n; |
| | ln->nfields_allocated = n; |
| | if (n == 0) { |
| | ln->fields = NULL; |
| | return; |
| | } |
| | ln->fields = (struct field *)malloc(sizeof(struct field) * n); |
| | for (idx_t i = 0; i < n; ++i) { |
| | idx_t L = lens ? lens[i] : (idx_t)strlen(vals[i]); |
| | char *buf = (char *)malloc((size_t)L); |
| | if (L > 0) { |
| | memcpy(buf, vals[i], (size_t)L); |
| | } |
| | ln->fields[i].beg = buf; |
| | ln->fields[i].len = L; |
| | } |
| | } |
| |
|
| | |
| | static void free_line(struct line *ln) |
| | { |
| | if (!ln) return; |
| | for (idx_t i = 0; i < ln->nfields; ++i) { |
| | free(ln->fields[i].beg); |
| | ln->fields[i].beg = NULL; |
| | ln->fields[i].len = 0; |
| | } |
| | free(ln->fields); |
| | ln->fields = NULL; |
| | ln->nfields = 0; |
| | ln->nfields_allocated = 0; |
| | } |
| |
|
| | |
| | |
| | static char *capture_prfields_output(struct line const *line, idx_t join_field, idx_t autocount) |
| | { |
| | char *result = NULL; |
| | FILE *tmp = tmpfile(); |
| | if (!tmp) { |
| | return NULL; |
| | } |
| |
|
| | int out_fd = fileno(stdout); |
| | int saved_fd = dup(out_fd); |
| | if (saved_fd < 0) { |
| | fclose(tmp); |
| | return NULL; |
| | } |
| |
|
| | int tmp_fd = fileno(tmp); |
| |
|
| | |
| | fflush(stdout); |
| | if (dup2(tmp_fd, out_fd) < 0) { |
| | close(saved_fd); |
| | fclose(tmp); |
| | return NULL; |
| | } |
| |
|
| | |
| | prfields(line, join_field, autocount); |
| |
|
| | |
| | fflush(stdout); |
| | long size = 0; |
| | if (fseek(tmp, 0, SEEK_END) == 0) { |
| | long endpos = ftell(tmp); |
| | if (endpos >= 0) { |
| | size = endpos; |
| | (void)fseek(tmp, 0, SEEK_SET); |
| | } |
| | } |
| |
|
| | if (size < 0) size = 0; |
| | result = (char *)malloc((size_t)size + 1); |
| | if (!result) { |
| | |
| | dup2(saved_fd, out_fd); |
| | close(saved_fd); |
| | fclose(tmp); |
| | return NULL; |
| | } |
| |
|
| | if (size > 0) { |
| | size_t rd = fread(result, 1, (size_t)size, tmp); |
| | result[rd] = '\0'; |
| | if (rd != (size_t)size) { |
| | |
| | dup2(saved_fd, out_fd); |
| | close(saved_fd); |
| | fclose(tmp); |
| | free(result); |
| | return NULL; |
| | } |
| | } else { |
| | result[0] = '\0'; |
| | } |
| |
|
| | |
| | dup2(saved_fd, out_fd); |
| | close(saved_fd); |
| | fclose(tmp); |
| | return result; |
| | } |
| |
|
| | void setUp(void) { |
| | |
| | autoformat = false; |
| | output_separator = " "; |
| | output_seplen = 1; |
| | empty_filler = NULL; |
| | } |
| |
|
| | void tearDown(void) { |
| | |
| | } |
| |
|
| | |
| | static void test_prfields_basic_skip_join_middle(void) |
| | { |
| | const char *vals[] = { "A", "B", "C" }; |
| | struct line ln; |
| | build_line(&ln, vals, NULL, 3); |
| |
|
| | output_separator = ","; |
| | output_seplen = (idx_t)strlen(output_separator); |
| | autoformat = false; |
| | empty_filler = NULL; |
| |
|
| | char *out = capture_prfields_output(&ln, (idx_t)1, (idx_t)0); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING(",A,C", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | |
| | static void test_prfields_join_field_out_of_range_high(void) |
| | { |
| | const char *vals[] = { "A", "B", "C" }; |
| | struct line ln; |
| | build_line(&ln, vals, NULL, 3); |
| |
|
| | output_separator = ","; |
| | output_seplen = 1; |
| | autoformat = false; |
| |
|
| | char *out = capture_prfields_output(&ln, (idx_t)5, (idx_t)0); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING(",A,B,C", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | |
| | static void test_prfields_no_fields_no_output(void) |
| | { |
| | struct line ln; |
| | build_line(&ln, NULL, NULL, 0); |
| |
|
| | output_separator = ","; |
| | output_seplen = 1; |
| | autoformat = false; |
| |
|
| | char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)0); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING("", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | |
| | static void test_prfields_empty_field_with_empty_filler(void) |
| | { |
| | const char *vals[] = { "A", "", "C" }; |
| | idx_t lens[] = { 1, 0, 1 }; |
| | struct line ln; |
| | build_line(&ln, vals, lens, 3); |
| |
|
| | output_separator = ","; |
| | output_seplen = 1; |
| | autoformat = false; |
| | empty_filler = "<E>"; |
| |
|
| | |
| | char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)0); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING(",<E>,C", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | |
| | static void test_prfields_autoformat_extends_and_fills(void) |
| | { |
| | const char *vals[] = { "A", "B" }; |
| | struct line ln; |
| | build_line(&ln, vals, NULL, 2); |
| |
|
| | output_separator = ","; |
| | output_seplen = 1; |
| | autoformat = true; |
| | empty_filler = "<E>"; |
| |
|
| | |
| | char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)4); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING(",B,<E>,<E>", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | |
| | static void test_prfields_autoformat_limits(void) |
| | { |
| | const char *vals[] = { "A", "B", "C", "D" }; |
| | struct line ln; |
| | build_line(&ln, vals, NULL, 4); |
| |
|
| | output_separator = ","; |
| | output_seplen = 1; |
| | autoformat = true; |
| | empty_filler = NULL; |
| |
|
| | |
| | char *out = capture_prfields_output(&ln, (idx_t)2, (idx_t)3); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING(",A,B", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | |
| | static void test_prfields_custom_separator_multichar(void) |
| | { |
| | const char *vals[] = { "X", "Y" }; |
| | struct line ln; |
| | build_line(&ln, vals, NULL, 2); |
| |
|
| | output_separator = "::"; |
| | output_seplen = (idx_t)strlen(output_separator); |
| | autoformat = false; |
| |
|
| | |
| | char *out = capture_prfields_output(&ln, (idx_t)1, (idx_t)0); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING("::X", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | |
| | static void test_prfields_autoformat_no_empty_filler_only_separators(void) |
| | { |
| | const char *vals[] = { "A" }; |
| | struct line ln; |
| | build_line(&ln, vals, NULL, 1); |
| |
|
| | output_separator = ","; |
| | output_seplen = 1; |
| | autoformat = true; |
| | empty_filler = NULL; |
| |
|
| | |
| | |
| | char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)3); |
| | TEST_ASSERT_NOT_NULL(out); |
| | TEST_ASSERT_EQUAL_STRING(",,", out); |
| |
|
| | free(out); |
| | free_line(&ln); |
| | } |
| |
|
| | int main(void) |
| | { |
| | UNITY_BEGIN(); |
| | RUN_TEST(test_prfields_basic_skip_join_middle); |
| | RUN_TEST(test_prfields_join_field_out_of_range_high); |
| | RUN_TEST(test_prfields_no_fields_no_output); |
| | RUN_TEST(test_prfields_empty_field_with_empty_filler); |
| | RUN_TEST(test_prfields_autoformat_extends_and_fills); |
| | RUN_TEST(test_prfields_autoformat_limits); |
| | RUN_TEST(test_prfields_custom_separator_multichar); |
| | RUN_TEST(test_prfields_autoformat_no_empty_filler_only_separators); |
| | return UNITY_END(); |
| | } |