Skip to content

Instantly share code, notes, and snippets.

@worldOneo
Created July 3, 2021 14:35
Show Gist options
  • Save worldOneo/9220d5709f2b1f6a90af9562ac393974 to your computer and use it in GitHub Desktop.
Save worldOneo/9220d5709f2b1f6a90af9562ac393974 to your computer and use it in GitHub Desktop.
Simple terminal editor, cant read or write files currently, but can edit memory 😉
#include <termios.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <signal.h>
#include <math.h>
struct termios orig_termios;
short currentSequence;
#define _chr2shrt(x, y) (((short)x) << 8 | y)
const short UP = _chr2shrt('[', 'A');
const short DOWN = _chr2shrt('[', 'B');
const short RIGHT = _chr2shrt('[', 'C');
const short LEFT = _chr2shrt('[', 'D');
short chr2shrt(char a, char b)
{
return _chr2shrt(a, b);
}
#undef _chr2shrt
enum KEY_ACTION
{
KEY_NULL = 0,
CTRL_C = 3,
CTRL_D = 4,
CTRL_F = 6,
CTRL_H = 8,
TAB = 9,
CTRL_L = 12,
ENTER = 13,
CTRL_Q = 17,
CTRL_S = 19,
CTRL_U = 21,
ESC = 27,
BACKSPACE = 127,
ARROW_LEFT = 1000,
ARROW_RIGHT,
ARROW_UP,
ARROW_DOWN,
DEL_KEY,
HOME_KEY,
END_KEY,
PAGE_UP,
PAGE_DOWN
};
const char *moveCursorTo = "\033[%d;%dH";
const char *hideCursor = "\033[?25l";
const int hideCursorLen = 6;
const char *showCursor = "\033[?25h";
const int showCursorLen = 6;
const char *moveCursorHome = "\033[H";
const int moveCursorHomeLen = 3;
typedef struct
{
int len;
char *chars;
} charbuff;
void charbuffAppend(charbuff *cb, const char *s, int len)
{
char *new = realloc(cb->chars, cb->len + len);
if (new == NULL)
return;
memcpy(new + cb->len, s, len);
cb->chars = new;
cb->len += len;
}
void charbuffFree(charbuff *cb)
{
free(cb->chars);
}
typedef struct
{
char used;
int idx;
int size;
int displayed;
char *content;
} editorRow;
typedef struct
{
int cx, cy;
int rowcount;
int width;
int height;
int yoff;
int xoff;
editorRow **rows;
} editor;
editorRow *create_row()
{
editorRow *row = malloc(sizeof(editorRow));
row->used = 0;
row->idx = 0;
row->size = 120;
row->content = (char *)malloc(120);
return row;
}
void renderRow(editorRow *row, int width, int offset)
{
if (row->used == 0)
{
printf("\033[1;35m!\033[0m");
return;
}
for (int i = 0; i < width; i++)
{
int pos = i + offset;
if (pos >= row->displayed)
break;
printf("%c", row->content[pos]);
}
}
int getCursorPosition(int ifd, int ofd, int *x, int *y)
{
char buf[32];
unsigned int i = 0;
if (write(ifd, "\033[6n", 4) != 4)
return -1;
while (i < sizeof(buf) - 1)
{
if (read(ifd, buf + i, 1) != 1)
break;
if (buf[i] == 'R')
break;
i++;
}
if (sscanf(buf, "\033[%d;%dR", x, y) != 2)
return -1;
return 0;
}
int getWindowSize(int ifd, int ofd, int *rows, int *cols)
{
struct winsize ws;
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0)
{
int orig_row, orig_col, retval;
if (getCursorPosition(ifd, ofd, &orig_row, &orig_col) != 0)
return -1;
if (write(ofd, "\x1b[999C\x1b[999B", 12) != 12)
return -1;
if (getCursorPosition(ifd, ofd, rows, cols) != 0)
return -1;
char seq[32];
snprintf(seq, 32, "\x1b[%d;%dH", orig_row, orig_col);
write(ofd, seq, strlen(seq));
return 0;
}
else
{
*cols = ws.ws_col;
*rows = ws.ws_row;
return 0;
}
}
void clearScreen(int width, int height)
{
char cleanBuff[width];
memset(&cleanBuff, ' ', width);
cleanBuff[width - 1] = '\0';
for (int i = 0; i < height; i++)
{
printf("%s\r\n", cleanBuff);
}
}
editor *make_editor()
{
int w, h;
getWindowSize(STDIN_FILENO, STDOUT_FILENO, &h, &w);
h -= 1;
editorRow **rows = (editorRow **)malloc(sizeof(void *) * h);
clearScreen(w, h);
for (int i = 0; i < h; i++)
rows[i] = create_row();
editor *new = malloc(sizeof(editor));
new->rowcount = h;
new->width = w;
new->height = h;
new->yoff = 0;
new->xoff = 0;
new->rows = rows;
new->cx = 0;
new->cy = 0;
return new;
}
void printLineNumber(charbuff *out, int n)
{
char linenum[50];
int l = sprintf("%-5d |", linenum, ++n);
charbuffAppend(out, linenum, l);
}
void renderEditor(editor *e)
{
charbuff out = {0, NULL};
printf(hideCursor);
printf(moveCursorHome);
clearScreen(e->width, e->height);
printf(moveCursorHome);
for (int y = 0; y < e->height; y++)
{
int rownum = e->yoff + y;
editorRow *row = e->rows[rownum];
renderRow(row, e->width, e->xoff);
printf("\r\n");
}
printf("%d:%d - %s", e->cy + 1, e->cx + 1, "unknown");
printf(moveCursorTo, e->cy - e->yoff + 1, e->cx + 1);
printf(showCursor);
fflush(stdout);
charbuffFree(&out);
}
int insertToRow(editorRow *row, char c)
{
row->displayed++;
row->used = 1;
if (row->idx > row->size)
{
row->content = realloc(row->content, row->size + 100);
memset(row->content + row->size, ' ', 100);
row->content[row->size + 100] = '\0';
row->size += 100;
}
else
{
memmove(row->content + row->idx + 1, row->content + row->idx, row->size - row->idx + 1);
}
row->content[row->idx] = c;
row->idx++;
return row->idx - 1;
}
int editorRowMoveIdx(editorRow *row, int diff)
{
int new = row->idx + diff;
if (new < 0 || new > row->displayed)
return row->idx;
row->idx = new;
return new;
}
char editorRowDeleteChar(editorRow *row)
{
if (row->idx == 0)
return 1;
row->idx--;
memmove(row->content + row->idx, row->content + row->idx + 1, row->size - (row->size - row->idx));
row->displayed--;
row->content[row->idx + 1] = '\0';
return 0;
}
void editorRowConcat(editorRow *r1, editorRow *r2)
{
r1->content = realloc(r1->content, r1->displayed + r2->displayed);
memcpy(r1->content + r1->displayed, r2->content, r2->displayed);
r1->displayed += r2->displayed;
r1->size = r1->displayed + r2->displayed;
free(r2->content);
free(r2);
}
void editorEnter(editor *e)
{
e->rows[e->cy]->used = 1;
e->cy += 1;
e->rowcount += 1;
int size = sizeof(editorRow *) * e->rowcount;
e->rows = realloc(e->rows, size);
memmove(e->rows + e->cy + 1, e->rows + e->cy, sizeof(editorRow *) * (e->rowcount - e->cy - 1));
e->rows[e->cy] = create_row();
editorRow *prev = e->rows[e->cy - 1];
editorRow *curr = e->rows[e->cy];
int take = prev->displayed - prev->idx;
if (take > curr->size)
{
curr->content = realloc(curr->content, take);
curr->size = take;
}
memcpy(curr->content, prev->content + prev->idx, take);
curr->displayed = take;
e->rows[e->cy]->used = 1;
prev->displayed -= take;
prev->idx = prev->idx > prev->idx ? prev->displayed : prev->idx;
if (e->cy + 1 > e->height + e->yoff)
e->yoff++;
e->cx = e->rows[e->cy]->idx;
}
int editorReadKey(int fd)
{
int nread;
char c, seq[3];
while ((nread = read(fd, &c, 1)) == 0)
;
if (nread == -1)
exit(1);
while (1)
{
switch (c)
{
case ESC:
if (read(fd, seq, 1) == 0)
return ESC;
if (read(fd, seq + 1, 1) == 0)
return ESC;
if (seq[0] == '[')
{
if (seq[1] >= '0' && seq[1] <= '9')
{
if (read(fd, seq + 2, 1) == 0)
return ESC;
if (seq[2] == '~')
{
switch (seq[1])
{
case '3':
return DEL_KEY;
case '5':
return PAGE_UP;
case '6':
return PAGE_DOWN;
}
}
}
else
{
switch (seq[1])
{
case 'A':
return ARROW_UP;
case 'B':
return ARROW_DOWN;
case 'C':
return ARROW_RIGHT;
case 'D':
return ARROW_LEFT;
case 'H':
return HOME_KEY;
case 'F':
return END_KEY;
}
}
}
/* ESC O sequences. */
else if (seq[0] == 'O')
{
switch (seq[1])
{
case 'H':
return HOME_KEY;
case 'F':
return END_KEY;
}
}
break;
default:
return c;
}
}
}
void editorAddjustOffset(editor *e)
{
while (e->cy - e->yoff < 0)
--e->yoff;
}
void editorListenForKey(editor *e)
{
int nread;
int key = editorReadKey(STDIN_FILENO);
printf(" %d ", key);
if (nread == -1)
exit(1);
switch (key)
{
case CTRL_C:
exit(0);
case ENTER:
editorEnter(e);
return;
case BACKSPACE:
{
char endOfLine;
endOfLine = editorRowDeleteChar(e->rows[e->cy]);
if (endOfLine == 0)
e->cx = e->rows[e->cy]->idx;
else if (e->cy > 0)
{
--e->cy;
editorAddjustOffset(e);
editorRowConcat(e->rows[e->cy], e->rows[e->cy + 1]);
for (int i = e->cy + 1; i < e->rowcount; i++)
e->rows[i] = e->rows[i + 1];
e->rows[e->rowcount - 1] = create_row();
e->cx = e->rows[e->cy]->idx;
}
return;
}
case ARROW_LEFT:
e->cx = editorRowMoveIdx(e->rows[e->cy], -1);
return;
case ARROW_RIGHT:
e->cx = editorRowMoveIdx(e->rows[e->cy], 1);
return;
case ARROW_UP:
if (e->cy - 1 >= 0)
--e->cy;
e->cx = e->rows[e->cy]->idx;
return;
case ARROW_DOWN:
if (e->rowcount > e->cy)
++e->cy;
e->cx = e->rows[e->cy]->idx;
return;
}
e->cx = insertToRow(e->rows[e->cy], key) + 1;
}
void disableRawMode()
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}
void enableRawMode()
{
setvbuf(stdout, NULL, _IOFBF, 10000);
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disableRawMode);
struct termios raw = orig_termios;
raw.c_iflag &= ~(ICRNL | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
int main()
{
enableRawMode();
char c;
editor *e = make_editor();
renderEditor(e);
while (1)
{
editorListenForKey(e);
renderEditor(e);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment