#include #include #include #include #include #include #include typedef struct Buffer_struct Buffer; struct Buffer_struct { char* text; char* start; char* end; int size; int gap_size; }; Buffer* new_buffer(int size) { Buffer* buffer = malloc(sizeof(Buffer)); buffer->size = size; buffer->text = malloc(buffer->size); buffer->gap_size = size; buffer->start = buffer->text; buffer->end = buffer->start + buffer->gap_size; return buffer; } void buffer_grow(Buffer* buffer) { int old_size = buffer->size; /* I am making use of the fact that, whenever this function is called, the strlen of the string must equal the size of the buffer (since the gap size is zero) */ int start_offset = buffer->start - buffer->text; buffer->size += 10; buffer->text = realloc(buffer->text,buffer->size); buffer->start = buffer->text + start_offset; buffer->gap_size = 10; for (int i=0; i < (old_size - start_offset); i++) { *(buffer->start + i + buffer->gap_size) = *(buffer->start + i); *(buffer->start + i) = 0; } buffer->end = buffer->start + buffer->gap_size; } void buffer_insert(char ch, Buffer* buffer) { *(buffer->start) = ch; buffer->start++; buffer->gap_size--; if (buffer->gap_size == 0) { buffer_grow(buffer); } } void buffer_delete(Buffer* buffer) { if (buffer->start != buffer->text) { buffer->start--; buffer->gap_size++; } } void buffer_right(Buffer* buffer) { if (buffer->end != buffer->text + buffer->size) { char c = *(buffer->end); buffer->start++; buffer->end++; *(buffer->start -1) = c; } } void buffer_left(Buffer* buffer) { if (buffer->start != buffer->text) { char c = *(buffer->start - 1); buffer->start--; buffer->end--; *(buffer->end) = c; *(buffer->start) = 0; } } void init_curses() { initscr(); noecho(); keypad(stdscr,TRUE); cbreak(); } void sigint_handler(int dummy) { endwin(); exit(130); } int main(int argc, char** argv) { signal(SIGINT,sigint_handler); Buffer* buffer = new_buffer(10); if (argc == 2) { if (access(argv[1],F_OK) == 0) { /* If the file exists */ FILE* file = fopen(argv[1],"r"); char c; while (c = fgetc(file) != EOF) { buffer_insert(c,buffer); } } else { printf("File does not exist.\n"); return -10; } } init_curses(); int ch; int y, x; while (true) { clear(); int i=0; if (buffer->start != buffer->text) { /* We don't want to print the string, if the gap starts at the first index of the string */ while (i < buffer->size) { addch(*(buffer->text + i)); i++; if ((buffer->start - buffer->text) == i) { /* If we have encountered the start of the gap */ getyx(stdscr,y,x); i += buffer->gap_size; } } move(y,x); } ch = getch(); switch(ch) { case KEY_BACKSPACE: buffer_delete(buffer); break; case KEY_LEFT: buffer_left(buffer); break; case KEY_RIGHT: buffer_right(buffer); break; case 10: /* Enter key */ buffer_insert('\n',buffer); /* Why handle this separately? Because, by default, curses seems to send '\r\n', which is technically two characters. I should probably add some code to deal with this scenario in the 'insert' method (instead of creating an exception here), but that's a problem for another day. */ break; default: buffer_insert(ch,buffer); } continue_while_loop: } endwin(); }