#08
// ebuf.h --- editor buffer API.
#include <stdbool.h>
#define MAXSTR 100
struct ebuf;
typedef struct ebuf *ebufp;
ebufp ebuf_new(); // create ebuf
bool ebuf_iseof(ebufp e); // see if current line is EOF
bool ebuf_forward(ebufp e); // forward 1 line
bool ebuf_backward(ebufp e); // backward 1 line
void ebuf_top(ebufp e); // go to top
char *ebuf_str(ebufp e); // obtain current line string
void ebuf_insert(ebufp e, char *s); // insert a line
// ebufdemo.c --- demonstration of ebuf.
#include <stdio.h>
#include "ebuf.h"
int main(void) {
ebufp e = ebuf_new();
ebuf_insert(e, "abc");
ebuf_insert(e, "def");
ebuf_insert(e, "ghi");
ebuf_top(e);
while(!ebuf_iseof(e)) {
printf("%s\n", ebuf_str(e)); ebuf_forward(e);
}
while(ebuf_backward(e)) { printf("%s\n", ebuf_str(e)); }
return 0;
}
// ebuf.c --- editor buffer implementation
#include <stdlib.h>
#include <string.h>
#include "ebuf.h"
struct line {
struct line *prev, *next; char str[MAXSTR];
};
struct ebuf { struct line *head, *cur; };
ebufp ebuf_new() {
ebufp r = (ebufp)malloc(sizeof(struct ebuf));
r->head = (struct line*)malloc(sizeof(struct line));
r->cur = r->head->next = r->head->prev = r->head;
strcpy(r->head->str, "EOF"); return r;
}
bool ebuf_iseof(ebufp e) { return e->cur == e->head; }
bool ebuf_forward(ebufp e) {
if(e->cur == e->head) { return false; }
e->cur = e->cur->next; return true;
}
bool ebuf_backward(ebufp e) {
if(e->cur->prev == e->head) { return false; }
e->cur = e->cur->prev; return true;
}
void ebuf_top(ebufp e) { e->cur = e->head->next; }
char *ebuf_str(ebufp e) { return e->cur->str; }
void ebuf_insert(ebufp e, char *s) {
struct line *p = (struct line*)malloc(sizeof(struct line));
strncpy(p->str, s, MAXSTR); p->str[MAXSTR-1] = '\0';
p->prev = e->cur->prev; p->next = e->cur;
e->cur->prev->next = p; e->cur->prev = p;
}
// test_ebuf.c --- unit test for ebuf.
#include <stdio.h>
#include <string.h>
#include "ebuf.h"
void expect_str(char *s1, char *s2, char *msg) {
printf("%s '%s':'%s' %s\n", strcmp(s1, s2)?"NG":"OK", s1, s2, msg);
}
int main(void) {
ebufp e = ebuf_new();
ebuf_insert(e, "abc"); ebuf_insert(e, "def");
ebuf_top(e); expect_str(ebuf_str(e), "abc", "line 1: abc");
ebuf_forward(e); expect_str(ebuf_str(e), "def", "line 2: def");
ebuf_insert(e, "ghi"); ebuf_top(e);
ebuf_forward(e); expect_str(ebuf_str(e), "ghi", "new line 2: ghi");
ebuf_forward(e); expect_str(ebuf_str(e), "def", "new line 3: def");
return 0;
}
// editor.c --- a simple line editor.
#include <stdio.h>
#include "ebuf.h"
bool getl(char s[], int lim) {
int c, i = 0;
for(c = getchar(); c != EOF && c != '\n'; c = getchar()) {
s[i++] = c; if(i+1 >= lim) { break; }
}
s[i] = '\0'; return c != EOF;
}
int main(void) {
char buf[200];
ebufp e = ebuf_new();
printf("> ");
while(getl(buf, 200)) {
if(buf[0] == 'q') { // quit
break;
} else if(buf[0] == 'p') { // print
printf(" %s\n", ebuf_str(e));
} else if(buf[0] == 't') { // top
ebuf_top(e);
} else if(buf[0] == 'f') { // fwd
ebuf_forward(e);
} else if(buf[0] == 'b') { // back
ebuf_backward(e);
} else if(buf[0] == 'i') { // insert
ebuf_insert(e, buf+1);
} else { // other --- fwd and print
ebuf_forward(e); printf(" %s\n", ebuf_str(e));
}
printf("> ");
}
return 0;
}
bool readfile(ebufp e, char *fname) {
char str[200];
FILE *f = fopen(fname, "r");
if(f == NULL) { return false; }
while(fgets(str, 200, f) != NULL) {
int len = strlen(str);
if(len > 0) { str[len-1] = '\0'; }
ebuf_insert(e, str);
}
fclose(f); return true;
}
bool writefile(ebufp e, char *fname) {
FILE *f = fopen(fname, "w");
if(f == NULL) { return false; }
ebuf_top(e);
while(!ebuf_iseof(e)) {
fprintf(f, "%s\n", ebuf_str(e)); ebuf_forward(e);
}
fclose(f); return true;
}
// ncursesdemo.c --- show usage of ncurses.
#include <ncurses.h>
#include <stdlib.h>
int main(void) {
initscr(); noecho(); cbreak(); system("stty raw"); clear();
move(10, 10); addstr("press any key"); refresh();
int ch = getch(); addch('a'); addch('b'); refresh();
ch = getch(); move(10, 15); insch('a'); insch('b'); refresh();
ch = getch(); delch(); move(10, 20); clrtoeol(); refresh();
ch = getch(); endwin(); return 0;
}
// sedit.c --- very primitive screen editor w/ ncurses.
#include <stdio.h>
#include <stdbool.h>
#include <ncurses.h>
#include <stdlib.h>
#include <string.h>
#include "ebuf.h"
bool readfile(ebufp e, char *fname) {
char str[200];
FILE *f = fopen(fname, "r");
if(f == NULL) { return false; }
while(fgets(str, 200, f) != NULL) {
int len = strlen(str);
if(len > 0) { str[len-1] = '\0'; }
ebuf_insert(e, str);
}
fclose(f); return true;
}
bool writefile(ebufp e, char *fname) {
FILE *f = fopen(fname, "w");
if(f == NULL) { return false; }
ebuf_top(e);
while(!ebuf_iseof(e)) {
fprintf(f, "%s\n", ebuf_str(e)); ebuf_forward(e);
}
fclose(f); return true;
}
void inschar(char *s, char ch, int p, int len) {
int i;
for(i = len + 1; i > p; --i) { s[i] = s[i-1]; }
s[p] = ch;
}
void delchar(char *s, int p, int len) {
int i;
for(i = p; i < len; ++i) { s[i] = s[i+1]; }
}
bool handleline(int c, char *s, int *pos, int *len) {
if(c >= ' ' && c <= '~') {
if(*len >= MAXSTR-1) { return false; }
insch(c); inschar(s, c, *pos, (*len)++); move(10, ++(*pos));
} else if(c == 'B'-'@') {
if(*pos > 0) { move(10, --(*pos)); }
} else if(c == 'F'-'@') {
if(*pos < *len) { move(10, ++(*pos)); }
} else if(c == 'H'-'@') {
if(*pos <= 0) { return false; }
move(10, *pos - 1); delch(); delchar(s, --(*pos), (*len)--);
} else {
return false;
}
return true;
}
int main(int argc, char *argv[]) {
ebufp e = ebuf_new();
if(!readfile(e, argv[1])) { printf("?\n"); return 1; }
ebuf_top(e);
int len, pos, ch; char *str;
bool show = true;
initscr(); noecho(); cbreak(); system("stty raw"); clear();
while(true) {
if(show) {
str = ebuf_str(e); len = strlen(str); pos = 0;
move(10, 0); addstr(str); clrtoeol(); move(10, 0); show = false;
}
refresh(); ch = getch();
if(handleline(ch, str, &pos, &len)) {
// do nothing
} else if(ch == 'J'-'@') {
ebuf_forward(e); ebuf_insert(e, "");
ebuf_backward(e); show = true;
} else if(ch == 'N'-'@') {
ebuf_forward(e); show = true;
} else if(ch == 'P'-'@') {
ebuf_backward(e); show = true;
} else if(ch == 'Z'-'@') {
break;
}
}
endwin(); writefile(e, argv[1]); return 0;
}