|
|
#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(); |
|
|
} |