diff --git a/README.md b/README.md index 55d9410..2fa5b79 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ Text Editor ![Image of the program in use.](res/text.bmp) +Almost ready to merge upstream, still need cosmetic style changes, and to write the merge request. + Functional lightweight text editor written in C using ncurses. ## How to use: diff --git a/line.c b/line.c index a1acc7a..66e2f41 100644 --- a/line.c +++ b/line.c @@ -1,50 +1,55 @@ #include "line.h" + void init_line(LINE *s) { - s->size = LINE_SIZE; - s->line = (char *)malloc(LINE_SIZE * sizeof(char)); - s->line[0] = '\0'; -} // init_line - + s->line = malloc(LINE_SIZE * sizeof(char)); + if(s->line != NULL) + { + s->size = LINE_SIZE; + s->line[0] = '\0'; + } +} //init_line -// Insert char into string. -void insert_char(LINE *s, char c, int index) +void add_char(LINE *s, char c) { - int i; - - if(strlen(s->line) >= s->size - 2) expand(s); - - for(i = strlen(s->line); i >= index; i--) - s->line[i + 1] = s->line[i]; - - s->line[index] = c; -} // insert - + insert_char(s, c, strlen(s->line)); +} +// Insert char into string. +void insert_char(LINE *s, char c, int index){ + char temp[2]; + temp[0] = c; + temp[1] = '\0'; + insert_string(s, temp, index); +} // insert char using insert_string() -void remove_char(LINE *s, int index) +void insert_string(LINE *s, char *in, int index) { - int i; - int len = strlen(s->line); - for(i = index; i < len; i++) - s->line[i] = s->line[i + 1]; -} // remove_char + int line_ln = strlen(s->line); + int in_ln = strlen(in); + if(line_ln + in_ln >= s->size) expand(s, in_ln); + if(line_ln + in_ln >= s->size) return; + size_t append_ln = line_ln - index + 1; + memmove(s->line + index + in_ln, s->line + index, append_ln * sizeof(char)); + memcpy(s->line + index, in, (in_ln) * sizeof(char)); +} // insert string -// expands size of line -void expand(LINE *s) +void expand(LINE *s, int ln) { - int new_size = s->size * 2; - char *temp = (char *)malloc(new_size * sizeof(char)); - strcpy(temp, s->line); - free(s->line); - s->line = temp; - s->size = new_size; -} // expand - -void add_char(LINE *s, char c) -{ - insert_char(s, c, strlen(s->line)); + size_t new_size = s->size * 2 + ln; + char *p = realloc(s->line, new_size * sizeof(char)); + if(p != NULL) + { + s->size = new_size; + s->line = p; + } } + + +void remove_char(LINE *s, int index){ + size_t append_ln = strlen(s->line) - index; + memmove(s->line + index, s->line + index + 1, append_ln * sizeof(char)); +} // remove_char diff --git a/line.h b/line.h index 8188dce..84fd8b7 100644 --- a/line.h +++ b/line.h @@ -10,14 +10,16 @@ typedef struct { - char *line; - int size; // size of array, not string + char *line; + int size; // size of array, not string } LINE; void init_line(LINE *s); void insert_char(LINE *s, char c, int index); // inserts char to string +void insert_string(LINE *s, char *str, int index); void remove_char(LINE *s, int index); -void expand(LINE *s); // doubles the size of the line -void add_char(LINE *s, char c); // add to end of line - +void add_char(LINE *s, char c); //add to end of line +void expand(LINE *s, int ln); #endif + + diff --git a/page.c b/page.c index 52332fd..a25d5b9 100644 --- a/page.c +++ b/page.c @@ -1,97 +1,96 @@ -#include "page.h" + +#include "page.h" +void file_too_big(PAGE *p, int size); void init_page(PAGE *p, char *filename, int size) { - p->text = (LINE *)malloc(size * sizeof(LINE)); - - int i; - for(i = 0; i < size; i++) - { - init_line(p->text + i); - } + p->text = malloc(size * sizeof(LINE)); + if(p->text == NULL) file_too_big(p, 0); + + int i; + for(i = 0; i < size; i++) + { + init_line(p->text + i); + if(p->text + i == NULL) file_too_big(p, i); + } strcpy(p->filename, filename); - p->numlines = 0; - p->size = size; + p->numlines = size; + p->size = size; + + } // init_page void dest_page(PAGE *p) { - int i; - for(i = 0; i < p->numlines; i++) - { - free(p->text[i].line); // maybe replace with dest_line() - } - free(p->text); + int i; + for(i = 0; i < p->numlines; i++) + { + free((p->text + i)->line); // maybe replace with dest_line() + } + free(p->text); } // dest_page - - // WARNING: Expansion function implemented but not tested -void insert_line(PAGE *p, int index) -{ - if( p->numlines >= p->size ) expand_page(p); - - LINE newline; - init_line(&newline); - newline.line[0] = '\0'; - - int i; - - for(i = p->numlines - 1; i >= index; i--) - p->text[i + 1] = p->text[i]; - - p->text[index] = newline; - (p->numlines)++; +int insert_line(PAGE *p, int index) +{ + if(p->numlines + 1 >= p->size) expand_page(p); + if(p->numlines + 1 >= p->size) return 0; + + size_t lines = p->numlines - index; + memmove(&p->text[index + 1].line, &p->text[index].line, + lines * sizeof(LINE)); + + LINE newline; + init_line(&newline); + p->text[index] = newline; + p->numlines++; + return 1; } // insert_line - - void remove_line(PAGE *p, int index) { - if( p->numlines > 1 ) - { - free(p->text[index].line); - - int i; - for(i = index; i < p->numlines - 1; i++) - { - p->text[i] = p->text[i + 1]; - } - (p->numlines)--; - } + size_t lines; + + free(p->text[index].line); + lines = p->numlines - index - 1; + memmove(&p->text[index].line, &p->text[index + 1].line, + lines * sizeof(LINE)); + p->numlines--; } // remove_line + void expand_page(PAGE *p) { - int newsize = p->size * 2; - LINE *newline = malloc(newsize * sizeof(LINE)); - - int i; - for(i = 0; i < p->size; i++) // copy old lines - newline[i] = p->text[i]; - for(i = p->size; i < newsize; i++) // init new lines - init_line(newline + i); - - p->text = newline; - p->size = newsize; -} // expand_page - + int newsize = p->size * 2 + 1; + LINE *newline = realloc(p->text, newsize * sizeof(LINE)); + + if(newline != NULL) + { + p->size = newsize; + p->text = newline; + } +} + // NOTE: This moves the cursor to the end of the displayed text void print_page(const PAGE *p, int start, int end) { - int i, line; - for(i = start, line = 0; i < p->numlines && i < end; i++, line++) - { - move(line, 0); - clrtoeol(); - printw(" %s", p->text[i].line); - } - if(start < end) + int i, line; + for(i = start; i <= end; i++) { - move(line, 0); - clrtoeol(); // if we deleted a line this may be necessary - move(line-1, 1); + line = i - start; + move(line, 0); + clrtoeol(); + if(i < p->numlines) + printw(" %s", p->text[i].line); } - refresh(); + refresh(); } // print_page + +void file_too_big(PAGE *p, int last_line) +{ + p->size = last_line; + dest_page(p); + exit(EXIT_FAILURE); +} + diff --git a/page.h b/page.h index 62b5791..4fa653f 100644 --- a/page.h +++ b/page.h @@ -5,23 +5,24 @@ #include // might have to move this #include "line.h" -#define PAGE_SIZE 500 /* Default number of lines */ +#define PAGE_SIZE 1 /* Default number of lines */ #define WIN_SIZE (LINES - 2) /* Size of window, making room for bottom prompt */ #define NAME_LIMIT 256 /* Max size of a unix filename + 1 */ typedef struct { char filename[NAME_LIMIT]; - LINE *text; // lines of text - int numlines; - int size; // size of array + LINE *text; // lines of text + int numlines; + int size; // size of array } PAGE; void init_page(PAGE *p, char *filename, int size); void dest_page(PAGE *p); -void insert_line(PAGE *p, int index); +int insert_line(PAGE *p, int index); void remove_line(PAGE *p, int index); void expand_page(PAGE *p); void print_page(const PAGE *p, int start, int end); #endif + diff --git a/prompt.c b/prompt.c index cae663b..d5cf435 100644 --- a/prompt.c +++ b/prompt.c @@ -120,3 +120,4 @@ int prompt_yesno(const char *message) return !choice; // yes is 0 } + diff --git a/text.c b/text.c index 8d0edb7..ec05d00 100644 --- a/text.c +++ b/text.c @@ -3,242 +3,225 @@ /** * Word Processing Program * Features: - * -Real time editing - * -Saving - * -Loading + * -Real time editing + * -Saving + * -Loading **/ -int y_offset = 0; // TODO: move to local scope int tab_offset = 0; #define DEBUG -void print_loc(int x, int y) +void print_loc(int x, int y, int y_offset) { - #ifdef DEBUG - int oldx, oldy; - getyx(stdscr, oldy, oldx); - mvprintw(0, COLS - 20, "x: %d y: %d o: %d", x, y, y_offset); - move(oldy, oldx); - #endif + #ifdef DEBUG + int oldx, oldy; + getyx(stdscr, oldy, oldx); + mvprintw(WIN_SIZE + 1, COLS - 20, "x: %d y: %d o: %d", x, y, y_offset); + move(oldy, oldx); + #endif } int main(int argc, char *argv[]) -{ - PAGE page; +{ + PAGE page; - if(argc > 1) - { + if(argc > 1) + { if(file_exists(argv[1])) { - load_file(&page, argv[1]); + load_file(&page, argv[1]); } else { init_page(&page, argv[1], PAGE_SIZE); - page.numlines = 1; } - } - else // initialize - { - init_page(&page, "untitled.txt", PAGE_SIZE); - page.numlines = 1; - } - - /* curses interface */ - initscr(); - noecho(); - keypad(stdscr, true); - - int beg = 0; - int end = WIN_SIZE; - int y, x; // position on screen + } + else // initialize + { + init_page(&page, "untitled.txt", PAGE_SIZE); + } + + /* curses interface */ + initscr(); + noecho(); + keypad(stdscr, true); + + int beg = 0; + int end = WIN_SIZE; + int y, x = 0; // absolute position + int y_draw, x_draw = 0; //position on the screen + int y_offset = 0; int i; - + int old_x; + - update_status("Press F4 to quit"); - - print_page(&page, beg, end); - getyx(stdscr, y, x); + update_status("Press F4 to quit"); + + print_page(&page, beg, end); + getyx(stdscr, y, x); char status[NAME_LIMIT + 10]; - while(true) - { - print_loc(x, y); - beg = 0 + y_offset; - end = WIN_SIZE + y_offset; - int ch = getch(); - update_status("Press F4 to quit"); // default text - switch(ch) - { - case KEY_F(4): + while(true) + { + + + y = clamp(y, 0, page.numlines - 1); + x = clamp(x, 0, strlen(page.text[y].line)); + + if(y > y_offset + WIN_SIZE){ + y_offset = y - WIN_SIZE; + } else if(y < y_offset){ + y_offset = y; + } + beg = y_offset; + end = WIN_SIZE + y_offset; + print_page(&page, beg, end); + y_draw = y - y_offset; + x_draw = x + 1; + move(y_draw, x_draw); + print_loc(x, y, y_offset); + + int ch = getch(); + update_status("Press F4 to quit"); // default text + + switch(ch) + { + case KEY_F(4): if(prompt_yesno("Are you sure you want to quit?")) - goto endnc; - print_page(&page, beg, end); - break; - case KEY_F(5): - save_file(&page); - sprintf(status, "Saved as \'%s\'", page.filename); - update_status(status); - break; + goto endnc; + break; + case KEY_F(5): + save_file(&page); + sprintf(status, "Saved: \'%s\'", page.filename); + update_status(status); + break; case KEY_F(6): prompt_string("Save As:", page.filename, NAME_LIMIT); save_file(&page); - sprintf(status, "Saved as \'%s\'", page.filename); - print_page(&page, beg, end); - update_status(status); + sprintf(status, "Saved: \'%s\'", page.filename); + update_status(status); + break; + case KEY_UP: + y--; + break; + case KEY_DOWN: + y++; + break; + case KEY_LEFT: + x--; + break; + case KEY_RIGHT: + x++; + break; + case KEY_END: + x = strlen(page.text[y].line); + break; + case KEY_HOME: + x = 0; + break; + case KEY_NPAGE: + y += 10; + break; + case KEY_PPAGE: + y -= 10; + break; + case KEY_DC: + case 127: // backspace key... + case KEY_BACKSPACE: + if(x == 0 && y != 0) + { + old_x = strlen(page.text[y - 1].line); + insert_string(&page.text[y - 1], page.text[y].line, + strlen(page.text[y - 1].line)); + remove_line(&page, y); + y--; + x = old_x; + } + else if( x > 0 ) + { + remove_char(&page.text[y], x - 1); // delete + x--; // char behind cursor + } break; - case KEY_UP: - move_up(&page, &x, &y); - break; - case KEY_DOWN: - move_down(&page, &x, &y); - break; - case KEY_LEFT: - move_left(&x, &y); - break; - case KEY_RIGHT: - move_right(&page, &x, &y); - break; - case KEY_DC: - case 127: // backspace key... - case KEY_BACKSPACE: - if(strlen(page.text[y + y_offset].line) == 0) - { // can only delete blank lines for now - remove_line(&page, y + y_offset); - move_up(&page, &x, &y); - } - else if( x > 1 ) - { - remove_char(&page.text[y + y_offset], x - 2); // delete - move_left(&x, &y); // char behind cursor - } - print_page(&page, beg, end); - move(y, x); - break; case '\t': for(i = 0; i < TAB_WIDTH; i++) { - insert_char(&page.text[y + y_offset], ' ', x - 1); - print_page(&page, beg, end); - move_right(&page, &x, &y); + insert_char(&page.text[y], ' ', x); + } + x += TAB_WIDTH; + break; + case '\n': // newline + if(insert_line(&page, y + 1)) + { + insert_string(&page.text[y + 1], + page.text[y].line + x, 0); + page.text[y].line[x] = '\0'; + y++; + x = 0; } break; - case '\n': // newline - insert_line(&page, y + y_offset + 1); - print_page(&page, beg, end); - move_down(&page, &x, &y); - break; - default: // all other chars - if( isprint(ch) ) - { - insert_char(&page.text[y + y_offset], ch, x - 1); - print_page(&page, beg, end); - move_right(&page, &x, &y); - } - } - } -endnc: - /* end curses */ + default: // all other chars + if( isprint(ch) ) + { + insert_char(&page.text[y], ch, x); + x++; + } + } + } +endnc: + /* end curses */ endwin(); - dest_page(&page); - return EXIT_SUCCESS; + dest_page(&page); + return EXIT_SUCCESS; } // main +int clamp(int integer, int floor, int ceil){ + if(integer < floor) + return floor; + else if(integer > ceil) + return ceil; + return integer; +} // prints a message at the bottom of the window void update_status(char *info) { - int oldy, oldx; getyx(stdscr, oldy, oldx); - - attron(A_REVERSE); - move(LINES - 1, 0); - clrtoeol(); - printw(info); - attroff(A_REVERSE); - - move(oldy, oldx); + //int oldy, oldx; getyx(stdscr, oldy, oldx); + + attron(A_REVERSE); + move(LINES - 1, 0); + clrtoeol(); + printw(info); + attroff(A_REVERSE); + + //move(oldy, oldx); } // update_status -/* movement */ -void move_left(int *x, int *y) -{ - if(*x - 1 > 0) move(*y, --(*x)); -} - - -void move_right(PAGE *p, int *x, int *y) -{ - if(*x <= strlen(p->text[*y + y_offset].line)) - { - if(p->text[*y + y_offset].line[*x + tab_offset] == '\t') { - move(*y, ++(*x)); - } else { - move(*y, ++(*x)); - } - } -} - -void move_up(PAGE *p, int *x, int *y) -{ - if( *y > 0 ) - { - --(*y); - } - else if (y_offset > 0) - { - --(y_offset); - print_page(p, 0 + y_offset, WIN_SIZE + y_offset); - } - if( *x > strlen(p->text[*y + y_offset].line) + 1 ) // cursor adjusts - *x = strlen(p->text[*y + y_offset].line) + 1; // to smaller lines - move(*y, *x); -} -void move_down(PAGE *p, int *x, int *y) -{ - if( *y < WIN_SIZE - 1 && *y < p->numlines - 1 ) - { - ++(*y); - } - else if ( *y + y_offset < p->numlines - 1 ) - { - ++(y_offset); - print_page(p, 0 + y_offset, WIN_SIZE + y_offset); - } - - if( *x > strlen(p->text[*y + y_offset].line) + 1 ) - *x = strlen(p->text[*y + y_offset].line) + 1; - move(*y, *x); -} -/* movement */ int count_lines(FILE *fp) { - char ch = '\0'; - int count = 0; - while((ch = fgetc(fp)) != EOF) - if( ch == '\n' ) - count++; + int ch = '\0'; + int count = 0; + while((ch = fgetc(fp)) != EOF) + if( ch == '\n' ) + count++; fseek(fp, 0, SEEK_SET); // go to beginning of file - return count; + return count; } // count_lines /* saving and loading */ void load_file(PAGE *p, char *filename) { - FILE *fp = fopen(filename, "r"); - int size = count_lines(fp) * 2; - char ch = '\0'; - int line; - - if(size < PAGE_SIZE) - size = PAGE_SIZE; - - init_page(p, filename, size); + FILE *fp = fopen(filename, "r"); + int size = count_lines(fp); + int ch = '\0'; + int line; + if(size == 0) size = 1; + init_page(p, filename, size); if(fp == NULL) // file doesn't exist yet. don't bother reading { - p->numlines = 1; return; } @@ -263,30 +246,29 @@ void load_file(PAGE *p, char *filename) } ch = fgetc(fp); } - p->numlines++; } - fclose(fp); + fclose(fp); } // load_file void save_file(PAGE *p) { - FILE *fp = fopen(p->filename, "w"); - int line, col; + FILE *fp = fopen(p->filename, "w"); + int line, col; - for(line = 0; line < p->numlines; line++) - { - col = 0; - while(p->text[line].line[col] != '\0') - { - fputc(p->text[line].line[col], fp); - col++; - } - fputc('\n', fp); - } + for(line = 0; line < p->numlines; line++) + { + col = 0; + while(p->text[line].line[col] != '\0') + { + fputc(p->text[line].line[col], fp); + col++; + } + fputc('\n', fp); + } - fclose(fp); + fclose(fp); } // save_file diff --git a/text.h b/text.h index 536a751..2310712 100644 --- a/text.h +++ b/text.h @@ -11,17 +11,15 @@ #include "prompt.h" void update_status(char *info); - +int clamp(int, int, int); int count_lines(FILE *fp); -void load_file(PAGE *p, char* filename); +void load_file(PAGE *p, char *filename); void save_file(PAGE *p); int file_exists(char *filename); -void move_left(int *x, int *y); -void move_right(PAGE *p, int *x, int *y); -void move_up(PAGE *p, int *x, int *y); -void move_down(PAGE *p, int *x, int *y); #endif + +