#include #include #include #include #include #include #include int index_to_start = 0; 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= (old_size - start_offset) - 1; i >= 0; 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 scroll_page_down_handler(Buffer* buffer) { while (*(buffer->text + index_to_start) != '\n') { index_to_start++; } index_to_start++; } void page_down_handler(Buffer* buffer) { buffer_right(buffer); /* I must advance the cursor at least once, so this hardcoded statement is fine. */ while (*(buffer->start - 1) != '\n') { buffer_right(buffer); } if ((getcury(stdscr) + 1) == getmaxy(stdscr)) { scroll_page_down_handler(buffer); } /* You would think that I need to call 'buffer_right' once more, to advance the cursor onto the next line. In fact, if you think about it, the place where the cursor (the rectangle) is, is actually the character _after_ the gap. Therefore, by advancing the start to the newline character, the character after the gap (i.e. the cursor) will automatically be moved to the next line. */ } void scroll_page_up_handler(Buffer* buffer) { for (int i=0;i<2;i++) { /* we need to encounter two 'newlines' before we stop */ while((*(buffer->text + index_to_start) != '\n') && (index_to_start >= 0)) { index_to_start--; } } index_to_start++; } void page_up_handler(Buffer* buffer) { while((*(buffer->start - 1) != '\n') && (buffer->text != buffer->start)) { buffer_left(buffer); } buffer_left(buffer); while((*(buffer->start - 1) != '\n') && (buffer->text != buffer->start)) { buffer_left(buffer); } if ((getcury(stdscr) == 0) && (index_to_start > 0)) { scroll_page_up_handler(buffer); } } void init_curses() { initscr(); noecho(); keypad(stdscr,TRUE); cbreak(); // scrollok(stdscr,TRUE); } void sigint_handler(int dummy) { endwin(); exit(130); } int main(int argc, char** argv) { signal(SIGINT,sigint_handler); Buffer* buffer = new_buffer(10); FILE* logfile = fopen("logfile.txt","w"); if (argc == 2) { int num_of_chars = 0; 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); num_of_chars++; } } else { printf("File does not exist.\n"); return -10; } while (num_of_chars > 0) { buffer_left(buffer); num_of_chars--; } } init_curses(); int ch; int y, x; while (true) { clear(); int i=index_to_start; if ((buffer->start != buffer->text) || (buffer->gap_size != buffer->size)) { /* We don't want to print the string, if the gap starts at the first index of the string, and continues till the end */ while (i < buffer->size) { if ((buffer->start - buffer->text) == i) { /* If we have encountered the start of the gap */ getyx(stdscr,y,x); i += buffer->gap_size; } if (i >= buffer->size) { break; } addch(*(buffer->text + i)); i++; fprintf(logfile,"At line %d of %d\n",getcury(stdscr),getmaxy(stdscr)); if (*(buffer->text + i) == '\n' && ((getcury(stdscr) + 1) == getmaxy(stdscr))) { fprintf(logfile,"Read newline\n"); break; } } 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; case KEY_NPAGE: page_down_handler(buffer); break; case KEY_PPAGE: page_up_handler(buffer); break; default: buffer_insert(ch,buffer); } continue_while_loop: } endwin(); }