Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ rtgrep
tags
sample.c
test.txt
*.o
test.c
24 changes: 19 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@ CC = gcc
CFLAGS = -Wall -Wextra -std=c99
LIBS = -lncurses
TARGET = rtgrep
SOURCES = rtgrep.c
SOURCES = rtgrep.c line_list.c
OBJECTS = $(SOURCES:.c=.o)

$(TARGET): $(SOURCES)
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES) $(LIBS)
TEST_TARGET = test_runner
TEST_SOURCES = test/test_root.c test/line_list_tests.c line_list.c
TEST_OBJECTS = $(TEST_SOURCES:.c=.o)

$(TARGET): $(OBJECTS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS)

%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

test: $(TEST_TARGET)
./$(TEST_TARGET)

$(TEST_TARGET): $(TEST_OBJECTS)
$(CC) $(CFLAGS) -o $(TEST_TARGET) $(TEST_OBJECTS)

clean:
rm -f $(TARGET)
rm -f $(TARGET) $(OBJECTS) $(TEST_TARGET) $(TEST_OBJECTS)

.PHONY: clean
.PHONY: clean test
77 changes: 77 additions & 0 deletions line_list.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "line_list.h"

#define INIT_LINES_SIZE 500

void deallocate_lines_contents(int length, char** lines);

line_list_t* line_list_init() {
line_list_t *l;

l = malloc(sizeof(line_list_t));

if (l == NULL) {
// TOOD make this log to error
printf("ERROR: line_list_init: failed to allocate");
exit(1);
}

l->lines = malloc(sizeof(char*) * INIT_LINES_SIZE);
l->length = 0;
l->capacity = INIT_LINES_SIZE;

return l;
}

void line_list_add(line_list_t *l, int s, char line[]) {
char **new_lines;
char **lines_to_free;
char *line_copy;
int i;

// Expand lines if needed
if (l->length == l->capacity) {
new_lines = malloc(sizeof(char*) * l->capacity*2);
l->capacity *= 2;

for (i = 0; i < l->length; i++) {
new_lines[i] = l->lines[i];
}

lines_to_free = l->lines;
l->lines = new_lines;

free(lines_to_free);
}

line_copy = malloc(sizeof(char) * (s + 1));
strncpy(line_copy, line, s);
line_copy[s] = '\0';
l->lines[l->length] = line_copy;
l->length++;
}

void line_list_clear(line_list_t *l) {
deallocate_lines_contents(l->length, l->lines);
l->length = 0;
}

void line_list_deallocate(line_list_t **l) {
deallocate_lines_contents((*l)->length, (*l)->lines);
free((*l)->lines);
free((*l));
*l = NULL;
}

/*
* Deallocate the contents of char* list lines.
*/
void deallocate_lines_contents(int length, char** lines){
int i;

for (i = 0; i < length; i++) {
free(lines[i]);
}
}
15 changes: 15 additions & 0 deletions line_list.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef LINE_LIST_H
#define LINE_LIST_H

typedef struct {
int length;
int capacity;
char **lines;
} line_list_t;

line_list_t* line_list_init();
void line_list_add(line_list_t *l, int s, char line[]);
void line_list_clear(line_list_t *l);
void line_list_deallocate(line_list_t **l);

#endif
45 changes: 22 additions & 23 deletions rtgrep.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
#include <time.h>
#include <sys/time.h>
#include "ansi.h"
#include "line_list.h"

#define MAX_PATTERN_LEN 256
#define MAX_OUTPUT_LINES 1000
#define MAX_LINE_LEN 512
#define TYPING_DELAY_MS 100


typedef struct {
int input_height;
int width;
Expand All @@ -26,8 +26,7 @@ typedef struct {
} ui_context_t;

typedef struct {
char lines[MAX_OUTPUT_LINES][MAX_LINE_LEN];
int count;
line_list_t *line_list;
int display_start;
int last_displayed_count;
int needs_full_redraw;
Expand Down Expand Up @@ -66,6 +65,9 @@ int main(int argc, char *argv[]) {
output_buffer_t output = {0};
char pattern[MAX_PATTERN_LEN] = "";
grep_state_t grep_state = {0};

//init line list
output.line_list = line_list_init();

// Parse command line arguments
if (argc > 1) {
Expand Down Expand Up @@ -93,6 +95,8 @@ int main(int argc, char *argv[]) {

kill_current_grep(&grep_state);
cleanup_ui(&output);

line_list_deallocate(&(output.line_list));
return 0;
}

Expand Down Expand Up @@ -143,9 +147,9 @@ void cleanup_ui(output_buffer_t *output_buffer) {
close(original_stdout);

//print the output buffer to the original stdout
for (i = 0; i < output_buffer->count; i++)
for (i = 0; i < output_buffer->line_list->length; i++)
{
printf("%s\n", output_buffer->lines[i]);
printf("%s\n", output_buffer->line_list->lines[i]);
}

if (tty_file){
Expand All @@ -160,39 +164,39 @@ void cleanup_ui(output_buffer_t *output_buffer) {
*/
void draw_ui(ui_context_t *ui, const char *pattern, output_buffer_t *output) {
int display_lines = ui->height - ui->input_height - 1;
int start_line = (output->count > display_lines) ? output->count - display_lines : 0;
int start_line = (output->line_list->length > display_lines) ? output->line_list->length - display_lines : 0;

if (output->needs_full_redraw) {
printf(ANSI_CLEAR_SCREEN);

for (int i = 0; i < output->count && i < display_lines; i++) {
for (int i = 0; i < output->line_list->length && i < display_lines; i++) {
int line_idx = start_line + i;
printf(ANSI_GOTO_POS "%s\n", i + 1, 1, output->lines[line_idx]);
printf(ANSI_GOTO_POS "%s\n", i + 1, 1, output->line_list->lines[line_idx]);
}

output->needs_full_redraw = 0;
output->last_displayed_count = output->count;
output->last_displayed_count = output->line_list->length;
ui->separator_drawn = 0;
ui->input_needs_refresh = 1;
} else if (output->count > output->last_displayed_count) {
int lines_to_add = output->count - output->last_displayed_count;
} else if (output->line_list->length > output->last_displayed_count) {
int lines_to_add = output->line_list->length - output->last_displayed_count;

if (lines_to_add >= display_lines) {
printf(ANSI_GOTO_POS, 1, 1);
for (int i = 0; i < display_lines; i++) {
int line_idx = start_line + i;
printf(ANSI_CLEAR_LINE "%s\n", output->lines[line_idx]);
printf(ANSI_CLEAR_LINE "%s\n", output->line_list->lines[line_idx]);
}
} else {
for (int i = output->last_displayed_count; i < output->count; i++) {
for (int i = output->last_displayed_count; i < output->line_list->length; i++) {
if (i - start_line >= 0 && i - start_line < display_lines) {
int display_row = (i - start_line) + 1;
printf(ANSI_GOTO_POS "%s\n", display_row, 1, output->lines[i]);
printf(ANSI_GOTO_POS "%s\n", display_row, 1, output->line_list->lines[i]);
}
}
}

output->last_displayed_count = output->count;
output->last_displayed_count = output->line_list->length;
}

if (!ui->separator_drawn) {
Expand Down Expand Up @@ -236,8 +240,7 @@ void execute_grep(const char *pattern, output_buffer_t *output, grep_state_t *gr
}

kill_current_grep(grep_state);

output->count = 0;
line_list_clear(output->line_list);
output->needs_full_redraw = 1;

int pipefd[2];
Expand Down Expand Up @@ -272,12 +275,8 @@ void handle_grep_results_if_any(int *pipefd, output_buffer_t *output){

if ((bytes_read = read(*pipefd, &ch, 1)) > 0) {
if (ch == '\n') {
if (output->count < MAX_OUTPUT_LINES) {
line[line_pos] = '\0';
strncpy(output->lines[output->count], line, MAX_LINE_LEN - 1);
output->lines[output->count][MAX_LINE_LEN - 1] = '\0';
output->count++;
}
line[line_pos] = '\0';
line_list_add(output->line_list, MAX_LINE_LEN - 1, line);
line_pos = 0;
return;
} else if (line_pos < MAX_LINE_LEN - 1) {
Expand Down
109 changes: 109 additions & 0 deletions test/line_list_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "../line_list.h"

int test_count = 0;
int test_passed = 0;

void test_assert(int condition, const char* test_name) {
test_count++;
if (condition) {
printf("PASS: %s\n", test_name);
test_passed++;
} else {
printf("FAIL: %s\n", test_name);
}
}

void test_line_list_init() {
line_list_t* list = line_list_init();

test_assert(list != NULL, "line_list_init returns non-NULL");
test_assert(list->length == 0, "line_list_init sets length to 0");
test_assert(list->capacity == 500, "line_list_init sets capacity to 500");
test_assert(list->lines != NULL, "line_list_init allocates lines array");

line_list_deallocate(&list);
test_assert(list == NULL, "line_list_deallocate sets pointer to NULL");
}

void test_line_list_add() {
line_list_t* list = line_list_init();

line_list_add(list, 5, "hello");
test_assert(list->length == 1, "line_list_add increments length");
test_assert(strcmp(list->lines[0], "hello") == 0, "line_list_add stores correct string");

line_list_add(list, 5, "world");
test_assert(list->length == 2, "line_list_add handles multiple additions");
test_assert(strcmp(list->lines[1], "world") == 0, "line_list_add stores second string correctly");

line_list_deallocate(&list);
}

void test_line_list_add_truncation() {
line_list_t* list = line_list_init();

line_list_add(list, 3, "hello");
test_assert(strcmp(list->lines[0], "hel") == 0, "line_list_add truncates to specified length");

line_list_deallocate(&list);
}

void test_line_list_clear() {
line_list_t* list = line_list_init();

line_list_add(list, 5, "hello");
line_list_add(list, 5, "world");
test_assert(list->length == 2, "setup: list has 2 items before clear");

line_list_clear(list);
test_assert(list->length == 0, "line_list_clear sets length to 0");

line_list_deallocate(&list);
}

void test_line_list_capacity_expansion() {
line_list_t* list = line_list_init();
int initial_capacity = list->capacity;

// Add enough items to trigger capacity expansion
for (int i = 0; i < initial_capacity + 1; i++) {
char buffer[10];
sprintf(buffer, "item%d", i);
line_list_add(list, strlen(buffer), buffer);
}

test_assert(list->length == initial_capacity + 1, "capacity expansion allows adding beyond initial capacity");
test_assert(list->capacity > initial_capacity, "capacity expansion increases capacity");
test_assert(strcmp(list->lines[0], "item0") == 0, "capacity expansion preserves first item");
test_assert(strcmp(list->lines[initial_capacity], "item500") == 0, "capacity expansion preserves last item");

line_list_deallocate(&list);
}

void test_line_list_empty_string() {
line_list_t* list = line_list_init();

line_list_add(list, 0, "");
test_assert(list->length == 1, "line_list_add handles empty string");
test_assert(strcmp(list->lines[0], "") == 0, "line_list_add stores empty string correctly");

line_list_deallocate(&list);
}

int run_line_list_tests() {
printf("Running line_list tests...\n");

test_line_list_init();
test_line_list_add();
test_line_list_add_truncation();
test_line_list_clear();
test_line_list_capacity_expansion();
test_line_list_empty_string();

printf("\nLine list tests completed: %d/%d passed\n", test_passed, test_count);
return (test_passed == test_count) ? 0 : 1;
}
17 changes: 17 additions & 0 deletions test/test_root.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <stdio.h>

int run_line_list_tests();

int main(int argc, char** argv) {
printf("Running all tests...\n\n");

int result = run_line_list_tests();

if (result == 0) {
printf("\nAll tests passed!\n");
} else {
printf("\nSome tests failed!\n");
}

return result;
}