| #include "../../unity/unity.h" |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdbool.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| static const char line_buf_init_template[] = { |
| ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', |
| ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0', |
| '\t', '\0' |
| }; |
|
|
| |
| static void reset_line_numbering_state(void) { |
| |
| memcpy(line_buf, line_buf_init_template, sizeof(line_buf_init_template)); |
| line_num_print = line_buf + LINE_COUNTER_BUF_LEN - 8; |
| line_num_start = line_buf + LINE_COUNTER_BUF_LEN - 3; |
| line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3; |
| } |
|
|
| |
| |
| |
| |
| static char* run_cat_and_capture(const unsigned char *input, size_t input_len, |
| idx_t insize, idx_t outsize, |
| bool show_nonprinting, bool show_tabs, |
| bool number, bool number_nonblank, |
| bool show_ends, bool squeeze_blank, |
| size_t *out_len) |
| { |
| *out_len = 0; |
| int inpipe[2]; |
| if (pipe(inpipe) != 0) { |
| return NULL; |
| } |
|
|
| |
| ssize_t wr = write(inpipe[1], input, input_len); |
| if (wr < 0 || (size_t)wr != input_len) { |
| close(inpipe[0]); |
| close(inpipe[1]); |
| return NULL; |
| } |
| close(inpipe[1]); |
|
|
| |
| int outpipe[2]; |
| if (pipe(outpipe) != 0) { |
| close(inpipe[0]); |
| return NULL; |
| } |
|
|
| int saved_stdout = dup(STDOUT_FILENO); |
| if (saved_stdout < 0) { |
| close(inpipe[0]); |
| close(outpipe[0]); |
| close(outpipe[1]); |
| return NULL; |
| } |
| if (dup2(outpipe[1], STDOUT_FILENO) < 0) { |
| close(inpipe[0]); |
| close(outpipe[0]); |
| close(outpipe[1]); |
| close(saved_stdout); |
| return NULL; |
| } |
| close(outpipe[1]); |
|
|
| |
| infile = "test-input"; |
| input_desc = inpipe[0]; |
|
|
| |
| idx_t inbuf_len = insize + 1; |
| if (inbuf_len < 2) inbuf_len = 2; |
| char *inbuf = (char *)malloc((size_t)inbuf_len); |
| if (!inbuf) { |
| |
| dup2(saved_stdout, STDOUT_FILENO); |
| close(saved_stdout); |
| close(inpipe[0]); |
| close(outpipe[0]); |
| return NULL; |
| } |
|
|
| idx_t outbuf_len = outsize * 4 + 1024; |
| if (outbuf_len < outsize + 16) outbuf_len = outsize + 16; |
| char *outbuf = (char *)malloc((size_t)outbuf_len); |
| if (!outbuf) { |
| dup2(saved_stdout, STDOUT_FILENO); |
| close(saved_stdout); |
| close(inpipe[0]); |
| close(outpipe[0]); |
| free(inbuf); |
| return NULL; |
| } |
|
|
| |
| bool ok = cat(inbuf, insize, outbuf, outsize, |
| show_nonprinting, show_tabs, number, number_nonblank, |
| show_ends, squeeze_blank); |
|
|
| |
| close(inpipe[0]); |
|
|
| |
| dup2(saved_stdout, STDOUT_FILENO); |
| close(saved_stdout); |
|
|
| |
| char *result = NULL; |
| size_t cap = 0; |
| for (;;) { |
| char buf[4096]; |
| ssize_t r = read(outpipe[0], buf, sizeof buf); |
| if (r < 0) { |
| free(inbuf); |
| free(outbuf); |
| close(outpipe[0]); |
| free(result); |
| return NULL; |
| } |
| if (r == 0) break; |
| if (*out_len + (size_t)r + 1 > cap) { |
| size_t new_cap = (*out_len + (size_t)r + 1) * 2; |
| char *tmp = (char *)realloc(result, new_cap); |
| if (!tmp) { |
| free(inbuf); |
| free(outbuf); |
| close(outpipe[0]); |
| free(result); |
| return NULL; |
| } |
| result = tmp; |
| cap = new_cap; |
| } |
| memcpy(result + *out_len, buf, (size_t)r); |
| *out_len += (size_t)r; |
| } |
| close(outpipe[0]); |
|
|
| |
| if (result) result[*out_len] = '\0'; |
|
|
| free(inbuf); |
| free(outbuf); |
|
|
| |
| if (!ok) { |
| free(result); |
| *out_len = 0; |
| return NULL; |
| } |
|
|
| return result; |
| } |
|
|
| void setUp(void) { |
| |
| newlines2 = 0; |
| pending_cr = false; |
| reset_line_numbering_state(); |
| |
| infile = "test-input"; |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
| static void fmt_num_prefix(int n, char *dst, size_t dstlen) { |
| |
| snprintf(dst, dstlen, "%6d\t", n); |
| } |
|
|
| |
| void test_cat_basic_passthrough(void) { |
| const char *in = "Hello\tWorld\nSecond line\n"; |
| size_t out_len = 0; |
| char *out = run_cat_and_capture((const unsigned char *)in, strlen(in), |
| (idx_t)64, (idx_t)4096, |
| false, false, false, false, false, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(in, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_show_tabs(void) { |
| const char *in = "a\tb\n\t\n"; |
| const char *expected = "a^Ib\n^I\n"; |
| size_t out_len = 0; |
| char *out = run_cat_and_capture((const unsigned char *)in, strlen(in), |
| (idx_t)64, (idx_t)4096, |
| false, true, false, false, false, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_show_ends_basic(void) { |
| const char *in = "line1\n\nline2\n"; |
| const char *expected = "line1$\n$\nline2$\n"; |
| size_t out_len = 0; |
| char *out = run_cat_and_capture((const unsigned char *)in, strlen(in), |
| (idx_t)64, (idx_t)4096, |
| false, false, false, false, true, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_show_ends_crlf_in_buffer(void) { |
| const unsigned char in[] = { 'A', '\r', '\n', 'B', '\n' }; |
| const char *expected = "A^M$\nB$\n"; |
| size_t out_len = 0; |
| |
| char *out = run_cat_and_capture(in, sizeof(in), |
| (idx_t)64, (idx_t)4096, |
| false, false, false, false, true, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_show_ends_crlf_across_boundary(void) { |
| const unsigned char in[] = { 'A', '\r', '\n' }; |
| const char *expected = "A^M$\n"; |
| size_t out_len = 0; |
| |
| char *out = run_cat_and_capture(in, sizeof(in), |
| (idx_t)2, (idx_t)4096, |
| false, false, false, false, true, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_number_all_lines(void) { |
| const char *in = "a\n\nb\n"; |
| char expected[256]; |
| char p1[16], p2[16], p3[16]; |
| fmt_num_prefix(1, p1, sizeof p1); |
| fmt_num_prefix(2, p2, sizeof p2); |
| fmt_num_prefix(3, p3, sizeof p3); |
| snprintf(expected, sizeof expected, "%s%s\n%s\n%s%s\n", p1, "a", p2, p3, "b"); |
| size_t out_len = 0; |
| char *out = run_cat_and_capture((const unsigned char *)in, strlen(in), |
| (idx_t)64, (idx_t)4096, |
| false, false, true, false, false, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_number_nonblank(void) { |
| const char *in = "a\n\n\nb\n\n"; |
| char expected[256]; |
| char p1[16], p2[16]; |
| fmt_num_prefix(1, p1, sizeof p1); |
| fmt_num_prefix(2, p2, sizeof p2); |
| |
| |
| |
| |
| |
| |
| |
| snprintf(expected, sizeof expected, "%s%s\n\n\n%s%s\n\n", p1, "a", p2, "b"); |
| size_t out_len = 0; |
| char *out = run_cat_and_capture((const unsigned char *)in, strlen(in), |
| (idx_t)64, (idx_t)4096, |
| false, false, true, true, false, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_squeeze_blank(void) { |
| const char *in = "a\n\n\n\nb\n\n"; |
| const char *expected = "a\n\nb\n\n"; |
| size_t out_len = 0; |
| char *out = run_cat_and_capture((const unsigned char *)in, strlen(in), |
| (idx_t)64, (idx_t)4096, |
| false, false, false, false, false, true, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_show_nonprinting_basic(void) { |
| unsigned char in[] = { 0x01, 0x09, 0x7F, 0xC8, '\n' }; |
| const char *expected = "^A\t^?M-H\n"; |
| size_t out_len = 0; |
| char *out = run_cat_and_capture(in, sizeof(in), |
| (idx_t)64, (idx_t)4096, |
| true, false, false, false, false, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| |
| void test_cat_show_nonprinting_with_tabs(void) { |
| const char *in = "\t\n"; |
| const char *expected = "^I\n"; |
| size_t out_len = 0; |
| char *out = run_cat_and_capture((const unsigned char *)in, strlen(in), |
| (idx_t)64, (idx_t)4096, |
| true, true, false, false, false, false, &out_len); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
| free(out); |
| } |
|
|
| int main(void) { |
| UNITY_BEGIN(); |
| RUN_TEST(test_cat_basic_passthrough); |
| RUN_TEST(test_cat_show_tabs); |
| RUN_TEST(test_cat_show_ends_basic); |
| RUN_TEST(test_cat_show_ends_crlf_in_buffer); |
| RUN_TEST(test_cat_show_ends_crlf_across_boundary); |
| RUN_TEST(test_cat_number_all_lines); |
| RUN_TEST(test_cat_number_nonblank); |
| RUN_TEST(test_cat_squeeze_blank); |
| RUN_TEST(test_cat_show_nonprinting_basic); |
| RUN_TEST(test_cat_show_nonprinting_with_tabs); |
| return UNITY_END(); |
| } |