| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
|
|
| |
| |
|
|
| |
| |
| static bool nl_file (char const *file); |
|
|
| |
| extern char const *FORMAT_RIGHT_NOLZ; |
| static void reset_lineno(void); |
|
|
| |
| static char* make_print_no_line_fmt_buf(int width, const char* sep) |
| { |
| size_t seplen = strlen(sep); |
| char* s = (char*)malloc((size_t)width + seplen + 1); |
| if (!s) return NULL; |
| memset(s, ' ', (size_t)width); |
| memcpy(s + width, sep, seplen); |
| s[width + seplen] = '\0'; |
| return s; |
| } |
|
|
| |
| static int write_all(int fd, const char* data, size_t len) |
| { |
| size_t off = 0; |
| while (off < len) { |
| ssize_t w = write(fd, data + off, len - off); |
| if (w < 0) return -1; |
| off += (size_t)w; |
| } |
| return 0; |
| } |
|
|
| |
| |
| static char* create_temp_file_with(const char* content) |
| { |
| char tmpl[] = "/tmp/nl_test_XXXXXX"; |
| int fd = mkstemp(tmpl); |
| if (fd < 0) return NULL; |
| if (content && write_all(fd, content, strlen(content)) < 0) { |
| close(fd); |
| unlink(tmpl); |
| return NULL; |
| } |
| |
| lseek(fd, 0, SEEK_SET); |
| close(fd); |
| char* path = strdup(tmpl); |
| return path; |
| } |
|
|
| |
| static char* read_entire_file(const char* path) |
| { |
| FILE* f = fopen(path, "rb"); |
| if (!f) return NULL; |
| if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } |
| long sz = ftell(f); |
| if (sz < 0) { fclose(f); return NULL; } |
| if (fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } |
| char* buf = (char*)malloc((size_t)sz + 1); |
| if (!buf) { fclose(f); return NULL; } |
| size_t n = fread(buf, 1, (size_t)sz, f); |
| buf[n] = '\0'; |
| fclose(f); |
| return buf; |
| } |
|
|
| |
| static int redirect_stdout_to_temp(char** out_path) |
| { |
| fflush(stdout); |
| char tmpl[] = "/tmp/nl_out_XXXXXX"; |
| int outfd = mkstemp(tmpl); |
| if (outfd < 0) return -1; |
| int saved = dup(STDOUT_FILENO); |
| if (saved < 0) { close(outfd); unlink(tmpl); return -1; } |
| if (dup2(outfd, STDOUT_FILENO) < 0) { |
| close(outfd); |
| close(saved); |
| unlink(tmpl); |
| return -1; |
| } |
| close(outfd); |
| *out_path = strdup(tmpl); |
| return saved; |
| } |
|
|
| |
| static int redirect_stderr_to_temp(char** err_path) |
| { |
| fflush(stderr); |
| char tmpl[] = "/tmp/nl_err_XXXXXX"; |
| int errfd = mkstemp(tmpl); |
| if (errfd < 0) return -1; |
| int saved = dup(STDERR_FILENO); |
| if (saved < 0) { close(errfd); unlink(tmpl); return -1; } |
| if (dup2(errfd, STDERR_FILENO) < 0) { |
| close(errfd); |
| close(saved); |
| unlink(tmpl); |
| return -1; |
| } |
| close(errfd); |
| *err_path = strdup(tmpl); |
| return saved; |
| } |
|
|
| |
| static void restore_stdout(int saved_fd) |
| { |
| fflush(stdout); |
| dup2(saved_fd, STDOUT_FILENO); |
| close(saved_fd); |
| } |
|
|
| |
| static void restore_stderr(int saved_fd) |
| { |
| fflush(stderr); |
| dup2(saved_fd, STDERR_FILENO); |
| close(saved_fd); |
| } |
|
|
| |
| static int redirect_stdin_from_file(const char* path) |
| { |
| int fd = open(path, O_RDONLY); |
| if (fd < 0) return -1; |
| int saved = dup(STDIN_FILENO); |
| if (saved < 0) { close(fd); return -1; } |
| if (dup2(fd, STDIN_FILENO) < 0) { |
| close(fd); |
| close(saved); |
| return -1; |
| } |
| close(fd); |
| return saved; |
| } |
|
|
| extern char const *separator_str; |
| extern int lineno_width; |
| extern char const *lineno_format; |
| extern char *print_no_line_fmt; |
| extern intmax_t starting_line_number; |
| extern intmax_t page_incr; |
| extern bool reset_numbers; |
| extern char const *body_type; |
| extern char const *header_type; |
| extern char const *footer_type; |
| extern char const *current_type; |
| extern bool have_read_stdin; |
|
|
| |
| static char* allocated_print_no_line_fmt = NULL; |
|
|
| |
| void setUp(void) { |
| |
| separator_str = "\t"; |
| lineno_width = 2; |
| lineno_format = FORMAT_RIGHT_NOLZ; |
| starting_line_number = 1; |
| page_incr = 1; |
| reset_numbers = true; |
|
|
| |
| body_type = "t"; |
| header_type = "n"; |
| footer_type = "n"; |
| current_type = body_type; |
|
|
| |
| if (allocated_print_no_line_fmt) { |
| free(allocated_print_no_line_fmt); |
| allocated_print_no_line_fmt = NULL; |
| } |
| allocated_print_no_line_fmt = make_print_no_line_fmt_buf(lineno_width, separator_str); |
| print_no_line_fmt = allocated_print_no_line_fmt; |
|
|
| |
| reset_lineno(); |
|
|
| |
| have_read_stdin = false; |
| } |
|
|
| void tearDown(void) { |
| if (allocated_print_no_line_fmt) { |
| free(allocated_print_no_line_fmt); |
| allocated_print_no_line_fmt = NULL; |
| print_no_line_fmt = NULL; |
| } |
| } |
|
|
| |
| static void test_nl_file_numbers_simple_file(void) |
| { |
| const char* input = "line1\nline2\n"; |
| char* in_path = create_temp_file_with(input); |
| TEST_ASSERT_NOT_NULL(in_path); |
|
|
| char* out_path = NULL; |
| int saved_stdout = redirect_stdout_to_temp(&out_path); |
| TEST_ASSERT_TRUE(saved_stdout >= 0); |
| TEST_ASSERT_NOT_NULL(out_path); |
|
|
| |
| bool ok = nl_file(in_path); |
|
|
| |
| restore_stdout(saved_stdout); |
|
|
| |
| char* out = read_entire_file(out_path); |
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
|
|
| const char* expected = " 1\tline1\n 2\tline2\n"; |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
|
|
| free(out); |
| unlink(out_path); |
| free(out_path); |
| unlink(in_path); |
| free(in_path); |
| } |
|
|
| |
| static void test_nl_file_t_mode_blank_lines(void) |
| { |
| const char* input = "\nX\n\n"; |
| char* in_path = create_temp_file_with(input); |
| TEST_ASSERT_NOT_NULL(in_path); |
|
|
| |
| body_type = "t"; |
| current_type = body_type; |
| reset_lineno(); |
|
|
| char* out_path = NULL; |
| int saved_stdout = redirect_stdout_to_temp(&out_path); |
| TEST_ASSERT_TRUE(saved_stdout >= 0); |
| TEST_ASSERT_NOT_NULL(out_path); |
|
|
| bool ok = nl_file(in_path); |
|
|
| restore_stdout(saved_stdout); |
|
|
| char* out = read_entire_file(out_path); |
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
|
|
| const char* expected = " \t\n 1\tX\n \t\n"; |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
|
|
| free(out); |
| unlink(out_path); |
| free(out_path); |
| unlink(in_path); |
| free(in_path); |
| } |
|
|
| |
| static void test_nl_file_from_stdin_dash(void) |
| { |
| const char* input = "a\nb\n"; |
| char* in_path = create_temp_file_with(input); |
| TEST_ASSERT_NOT_NULL(in_path); |
|
|
| |
| int saved_stdin = redirect_stdin_from_file(in_path); |
| TEST_ASSERT_TRUE(saved_stdin >= 0); |
|
|
| char* out_path = NULL; |
| int saved_stdout = redirect_stdout_to_temp(&out_path); |
| TEST_ASSERT_TRUE(saved_stdout >= 0); |
| TEST_ASSERT_NOT_NULL(out_path); |
|
|
| |
| bool ok = nl_file("-"); |
|
|
| |
| restore_stdout(saved_stdout); |
| dup2(saved_stdin, STDIN_FILENO); |
| close(saved_stdin); |
|
|
| char* out = read_entire_file(out_path); |
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_TRUE(have_read_stdin); |
| TEST_ASSERT_NOT_NULL(out); |
|
|
| const char* expected = " 1\ta\n 2\tb\n"; |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
|
|
| free(out); |
| unlink(out_path); |
| free(out_path); |
| unlink(in_path); |
| free(in_path); |
| } |
|
|
| |
| static void test_nl_file_nonexistent_returns_false(void) |
| { |
| const char* bad_path = "/tmp/this_file_should_not_exist_nl_test_XXXX"; |
| |
| char* out_path = NULL; |
| int saved_stdout = redirect_stdout_to_temp(&out_path); |
| TEST_ASSERT_TRUE(saved_stdout >= 0); |
| TEST_ASSERT_NOT_NULL(out_path); |
|
|
| char* err_path = NULL; |
| int saved_stderr = redirect_stderr_to_temp(&err_path); |
| TEST_ASSERT_TRUE(saved_stderr >= 0); |
| TEST_ASSERT_NOT_NULL(err_path); |
|
|
| |
| bool ok = nl_file(bad_path); |
|
|
| |
| restore_stdout(saved_stdout); |
| restore_stderr(saved_stderr); |
|
|
| |
| char* out = read_entire_file(out_path); |
| TEST_ASSERT_FALSE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING("", out); |
|
|
| free(out); |
| unlink(out_path); |
| free(out_path); |
| unlink(err_path); |
| free(err_path); |
| } |
|
|
| |
| static void test_nl_file_starting_and_increment(void) |
| { |
| starting_line_number = 5; |
| page_incr = 3; |
| reset_numbers = true; |
| reset_lineno(); |
|
|
| |
| body_type = "t"; |
| current_type = body_type; |
|
|
| const char* input = "aa\nbb\n"; |
| char* in_path = create_temp_file_with(input); |
| TEST_ASSERT_NOT_NULL(in_path); |
|
|
| char* out_path = NULL; |
| int saved_stdout = redirect_stdout_to_temp(&out_path); |
| TEST_ASSERT_TRUE(saved_stdout >= 0); |
| TEST_ASSERT_NOT_NULL(out_path); |
|
|
| bool ok = nl_file(in_path); |
|
|
| restore_stdout(saved_stdout); |
|
|
| char* out = read_entire_file(out_path); |
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
|
|
| const char* expected = " 5\taa\n 8\tbb\n"; |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
|
|
| free(out); |
| unlink(out_path); |
| free(out_path); |
| unlink(in_path); |
| free(in_path); |
| } |
|
|
| |
| static void test_nl_file_a_mode_blank_join(void) |
| { |
| |
| body_type = "a"; |
| current_type = body_type; |
|
|
| extern intmax_t blank_join; |
| blank_join = 2; |
|
|
| reset_numbers = true; |
| starting_line_number = 1; |
| reset_lineno(); |
|
|
| const char* input = "\n\n\nx\n\n"; |
| char* in_path = create_temp_file_with(input); |
| TEST_ASSERT_NOT_NULL(in_path); |
|
|
| char* out_path = NULL; |
| int saved_stdout = redirect_stdout_to_temp(&out_path); |
| TEST_ASSERT_TRUE(saved_stdout >= 0); |
| TEST_ASSERT_NOT_NULL(out_path); |
|
|
| bool ok = nl_file(in_path); |
|
|
| restore_stdout(saved_stdout); |
|
|
| char* out = read_entire_file(out_path); |
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
|
|
| |
| |
| |
| |
| |
| |
| |
| const char* expected = |
| " \t\n" |
| " 1\t\n" |
| " \t\n" |
| " 2\tx\n" |
| " \t\n"; |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
|
|
| free(out); |
| unlink(out_path); |
| free(out_path); |
| unlink(in_path); |
| free(in_path); |
| } |
|
|
| int main(void) |
| { |
| UNITY_BEGIN(); |
| RUN_TEST(test_nl_file_numbers_simple_file); |
| RUN_TEST(test_nl_file_t_mode_blank_lines); |
| RUN_TEST(test_nl_file_from_stdin_dash); |
| RUN_TEST(test_nl_file_nonexistent_returns_false); |
| RUN_TEST(test_nl_file_starting_and_increment); |
| RUN_TEST(test_nl_file_a_mode_blank_join); |
| return UNITY_END(); |
| } |