master
Alessandro Mauri 5 years ago
parent a2bfacedba
commit 51fe76b5e8
  1. 12
      Makefile
  2. 2
      hello
  3. BIN
      ste
  4. 940
      ste.c

@ -1,7 +1,7 @@
CC=gcc CC=gcc
CFLAGS=-Wall -Wextra -pedantic -Werror CFLAGS=-Wall -Wextra -pedantic -Werror
OFLAGS=-O3 OFLAGS=-O3
LFLAGS=-lncurses LFLAGS=-lncurses
ste: ste.c ste: ste.c
$(CC) $(CFLAGS) $(OFLAGS) $(LFLAGS) -o ste $^ $(CC) $(CFLAGS) $(OFLAGS) $(LFLAGS) -o ste $^

@ -1 +1 @@
hello world hello world

BIN
ste

Binary file not shown.

940
ste.c

@ -1,464 +1,476 @@
#include <ncurses.h> #include <ncurses.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
/* defines */ /* defines */
#define CTRL(k) ((k) & 0x1f) // Control mask modifier #define CTRL(k) ((k) & 0x1f) // Control mask modifier
#define TABSIZE 4 // Tab size as used in render #define TABSIZE 4 // Tab size as used in render
/* main data structure containing: /* main data structure containing:
* -cursor position * -cursor position
* -real (matching memory) * -real (matching memory)
* -offset (memory and screen offset) * -offset (memory and screen offset)
* -render (drawn on the screen) * -render (drawn on the screen)
* -window size * -window size
* -statusbar message */ * -statusbar message */
struct term { struct term {
struct { struct {
int x; int x;
int y; int y;
int off_x; int off_x;
int off_y; int off_y;
int r_x; int r_x;
int r_y; int r_y;
} cur; } cur;
struct { struct {
int x; int x;
int y; int y;
} dim; } dim;
char statusbar[30]; char statusbar[30];
int pad; int pad;
} t; } t;
/* Row structure, defines actual and /* Row structure, defines actual and
* render chars, actual and render size * render chars, actual and render size
* and difference between render and * and difference between render and
* real size of the row */ * real size of the row */
typedef struct row { typedef struct row {
int size; int size;
char *chars; char *chars;
int r_size; int r_size;
char *render; char *render;
int delta; int delta;
} row; } row;
/* Rows structure (or file buffer) /* Rows structure (or file buffer)
* defines rows and teh number of rows */ * defines rows and teh number of rows */
struct { struct {
row *rw; row *rw;
int rownum; int rownum;
} rows; } rows;
/* Prototypes */ /* Prototypes */
/* draw operations */ /* draw operations */
static void drawBar (char *s); static void drawBar (char *s);
static void drawScreen (); static void drawScreen ();
static void drawLines (void); static void drawLines (void);
static void updateRender (row *rw); static void updateRender (row *rw);
static void updateScroll (void); static void updateScroll (void);
static void cursorMove(int a); static void cursorMove(int a);
static int getLineNumberSize (void); static int getLineNumberSize (void);
static void lnMove (int y, int x); static void lnMove (int y, int x);
static int curRealToRender (row *rw, int c_x); static int curRealToRender (row *rw, int c_x);
/* Row operations */ /* Row operations */
static inline void rowInit (void); static inline void rowInit (void);
/* Terminal operations */ /* Terminal operations */
static void termInit (void); static void termInit (void);
static void termExit (void); static void termExit (void);
static void termDie (char *s); static void termDie (char *s);
/* file operations */ /* file operations */
static void fileOpen (char *filename); static void fileOpen (char *filename);
void fileSave (char *filename); void fileSave (char *filename);
/* buffer operations */ /* buffer operations */
static void rowAdd (char *s, int len); static void rowAdd (char *s, int len);
/* garbage */ /* garbage */
//inline void bufferFree (void); //inline void bufferFree (void);
static int whatsThat (void); static int whatsThat (void);
/* --------------------------------- main ------------------------------------ */ /* testing */
int main (int argc, char *argv[]) void updateInfo (void);
{
/* Initialize the first row */ /* --------------------------------- main ------------------------------------ */
rowInit(); int main (int argc, char *argv[])
{
/* Try to open the file */ /* Initialize the first row */
if (argc != 2) { rowInit();
perror("File not found");
exit(1); /* Try to open the file */
} else fileOpen(argv[1]); if (argc != 2) {
perror("File not found");
/* Initialize the terminal in raw mode, exit(1);
* start curses and initialize the term struct */ } else fileOpen(argv[1]);
termInit();
/* Initialize the terminal in raw mode,
/* Set the statusbar left (static) message */ * start curses and initialize the term struct */
sprintf(t.statusbar, "%s : %s %d lines %dx%d", argv[0], argv[1], rows.rownum, t.dim.y, t.dim.y); termInit();
/* Main event loop */ /* Set the statusbar left (static) message */
int c; sprintf(t.statusbar, "%s : %s %d lines %dx%d", argv[0], argv[1], rows.rownum, t.dim.y, t.dim.y);
while (1) {
/* Redraw the screen */ /* Main event loop */
drawScreen(); int c;
while (1) {
/* Wait for an event (keypress) */ /* Redraw the screen */
switch (c = getch()) { drawScreen();
case (CTRL('q')):
termExit(); /* Wait for an event (keypress) */
break; switch (c = getch()) {
case (KEY_LEFT): case (CTRL('q')):
case (KEY_RIGHT): termExit();
case (KEY_UP): break;
case (KEY_DOWN): case (KEY_LEFT):
cursorMove(c); case (KEY_RIGHT):
break; case (KEY_UP):
} case (KEY_DOWN):
} cursorMove(c);
break;
/* If by chance i find myself here be sure }
* end curses mode and clenaup */ }
termExit();
return 0; /* If by chance i find myself here be sure
} * end curses mode and clenaup */
termExit();
/* ----------------------------- end of main ---------------------------------- */ return 0;
}
void termInit (void)
{ /* ----------------------------- end of main ---------------------------------- */
/* Init the screen and refresh */
initscr(); void termInit (void)
refresh(); {
/* Init the screen and refresh */
/* Enable raw mode, this makes the termianl ignore initscr();
* interrupt signals like CTRL+C and CTRL+Z refresh();
* allowing us to make our own bindings */
raw(); /* Enable raw mode, this makes the termianl ignore
* interrupt signals like CTRL+C and CTRL+Z
/* Allow use of function keys */ * allowing us to make our own bindings */
keypad(stdscr, TRUE); raw();
/* Turn off echoing */ /* Allow use of function keys */
noecho(); keypad(stdscr, TRUE);
/* Set the tab size */ /* Turn off echoing */
set_tabsize(TABSIZE); noecho();
/* Start color mode */ /* Set the tab size */
start_color(); set_tabsize(TABSIZE);
init_pair(1, COLOR_BLUE, COLOR_BLACK);
/* Start color mode */
/* Populate the main data structure */ start_color();
getmaxyx(stdscr, t.dim.y, t.dim.x); init_pair(1, COLOR_BLUE, COLOR_BLACK);
t.dim.y -= 1;
t.pad = getLineNumberSize(); /* Populate the main data structure */
t.dim.x -= t.pad + 1; getmaxyx(stdscr, t.dim.y, t.dim.x);
t.dim.y -= 1;
/* Initialize the data staructure */ t.pad = getLineNumberSize();
t.cur.x = t.cur.off_x = 0; t.dim.x -= t.pad + 1;
t.cur.y = t.cur.off_y = 0;
} /* Initialize the data staructure */
t.cur.x = t.cur.off_x = 0;
/* Calculate the correct spacing for the line numbers t.cur.y = t.cur.off_y = 0;
* based on the size of the file */ }
int getLineNumberSize (void)
{ /* Calculate the correct spacing for the line numbers
int n = rows.rownum, l = 0; * based on the size of the file */
for (l = 0; n > 0; l++) n /= 10; int getLineNumberSize (void)
return l + 1; {
} int n = rows.rownum, l = 0;
for (l = 0; n > 0; l++) n /= 10;
void termExit (void) return l + 1;
{ }
// bufferFree();
erase(); void termExit (void)
refresh(); {
endwin(); // bufferFree();
exit(0); erase();
} refresh();
endwin();
void termDie (char *s) exit(0);
{ }
refresh();
endwin(); void termDie (char *s)
perror(s); {
exit(1); refresh();
} endwin();
perror(s);
/* ----------------------------- term operations -------------------------------- */ exit(1);
}
void drawScreen ()
{ /* ----------------------------- term operations -------------------------------- */
/* Clear the screen */
erase(); void drawScreen ()
/* Update Scroll */ {
updateScroll(); /* Clear the screen */
/* draw the lines */ erase();
drawLines(); /* Update Scroll */
/* draw the bar */ updateScroll();
drawBar(t.statusbar); /* draw the lines */
/* move back to the cursor position */ drawLines();
lnMove(t.cur.y, t.cur.r_x); /* draw the bar */
/* refresh the screen */ drawBar(t.statusbar);
refresh(); /* move back to the cursor position */
} lnMove(t.cur.y, t.cur.r_x);
/* refresh the screen */
/* Draw all the appropriate lines (following cursor) to the screen */ refresh();
void drawLines (void) }
{
int line = 0, ln; /* Draw all the appropriate lines (following cursor) to the screen */
/* move to the beginning of the screen */ void drawLines (void)
lnMove(0, 0); {
int line = 0, ln;
for (int i = 0; i < t.dim.y; i++) { /* move to the beginning of the screen */
if (i >= rows.rownum) break; lnMove(0, 0);
ln = i + t.cur.off_y;
for (int i = 0; i < t.dim.y; i++) {
/* Draw the line number */ if (i >= rows.rownum) break;
attron(COLOR_PAIR(1)); ln = i + t.cur.off_y;
mvprintw(i, 0, "%d", ln + 1);
attroff(COLOR_PAIR(1)); /* Draw the line number */
lnMove(i, 0); attron(COLOR_PAIR(1));
mvprintw(i, 0, "%d", ln + 1);
/* Draw the line matcing render memory */ attroff(COLOR_PAIR(1));
if (rows.rw[ln].r_size >= t.cur.off_x) { lnMove(i, 0);
addnstr(&rows.rw[ln].render[t.cur.off_x], t.dim.x + 1 - rows.rw[ln].delta);
} /* Draw the line matcing render memory */
lnMove(++line, 0); if (rows.rw[ln].r_size >= t.cur.off_x) {
} addnstr(&rows.rw[ln].render[t.cur.off_x], t.dim.x + 1 - rows.rw[ln].delta);
lnMove(t.cur.y, t.cur.x); }
} lnMove(++line, 0);
}
/* Move avoiding the space allocated for line numbers */ lnMove(t.cur.y, t.cur.x);
void lnMove (int y, int x) }
{
x += t.pad; /* Move avoiding the space allocated for line numbers */
move(y, x); void lnMove (int y, int x)
} {
x += t.pad;
/* Draw the status bar at the bottom of the screen */ move(y, x);
void drawBar (char *s) }
{
/* Set maximum contrast for bar */ /* Draw the status bar at the bottom of the screen */
attron(A_STANDOUT); void drawBar (char *s)
/* get the length of the statusbar text */ {
int len = strlen(s); /* Set maximum contrast for bar */
/* Print the message */ attron(A_STANDOUT);
mvprintw(t.dim.y, 0, s); /* get the length of the statusbar text */
/* Fill everything else with spaces */ int len = strlen(s);
for (int i = len; i <= t.dim.x + t.pad; i++) /* Print the message */
mvaddch(t.dim.y, i, ' '); mvprintw(t.dim.y, 0, s);
/* Fill everything else with spaces */
char m[10]; for (int i = len; i <= t.dim.x + t.pad; i++)
sprintf(m, "Zoom:\t%c", whatsThat()); mvaddch(t.dim.y, i, ' ');
mvaddstr(t.dim.y, t.dim.x + t.pad - strlen(m), m);
char m[10];
/* Return to normal contrast mode */ sprintf(m, "Zoom:\t%c", whatsThat());
attroff(A_STANDOUT); mvaddstr(t.dim.y, t.dim.x + t.pad - strlen(m), m);
}
/* Return to normal contrast mode */
/* convert the cursor matchoing the memory to the drawn one */ attroff(A_STANDOUT);
int curRealToRender (row *rw, int c_x) }
{
int r_x = 0; /* convert the cursor matchoing the memory to the drawn one */
for (int i = 0; i < c_x; i++) { int curRealToRender (row *rw, int c_x)
if (rw->chars[i] == '\t') r_x += (TABSIZE - 1) - (r_x % TABSIZE); {
r_x++; int r_x = 0;
} for (int i = 0; i < c_x; i++) {
return r_x; if (rw->chars[i] == '\t') r_x += (TABSIZE - 1) - (r_x % TABSIZE);
} r_x++;
}
/* -------------------------------- draw operations -------------------------------- */ return r_x;
}
/* Open a file and put it into a buffer line by line */
void fileOpen (char *filename) /* -------------------------------- draw operations -------------------------------- */
{
FILE *fp = fopen(filename, "r"); /* Open a file and put it into a buffer line by line */
if (!fp) termDie("Cannot open file"); void fileOpen (char *filename)
{
/* Init the linebuffer */ FILE *fp = fopen(filename, "r");
char *line = NULL; if (!fp) termDie("Cannot open file");
/* Set linecap to 0 so getline will atumatically allocate
* memory for the line buffer*/ /* Init the linebuffer */
size_t linecap = 0; char *line = NULL;
ssize_t linelen; /* Set linecap to 0 so getline will atumatically allocate
* memory for the line buffer*/
/* getline returns -1 if no new line is present */ size_t linecap = 0;
while ((linelen = getline(&line, &linecap, fp)) != -1) { ssize_t linelen;
while (linelen > 0 && (line[linelen - 1] == '\n' || line[linelen - 1] == '\r'))
linelen--; /* getline returns -1 if no new line is present */
rowAdd(line, linelen); while ((linelen = getline(&line, &linecap, fp)) != -1) {
} while (linelen > 0 && (line[linelen - 1] == '\n' || line[linelen - 1] == '\r'))
/* free the line buffer */ linelen--;
free(line); rowAdd(line, linelen);
/* close the file */ }
fclose(fp); /* free the line buffer */
} free(line);
/* close the file */
/* Add a row to the file buffer */ fclose(fp);
void rowAdd (char *s, int len) }
{
/* Extend the block of memory containing the lines */ /* Add a row to the file buffer */
rows.rw = realloc(rows.rw, sizeof(row) * (rows.rownum + 1)); void rowAdd (char *s, int len)
{
/* Allocate memory for the line and copy it /* Extend the block of memory containing the lines */
* at the current row number */ rows.rw = realloc(rows.rw, sizeof(row) * (rows.rownum + 1));
rows.rw[rows.rownum].chars = malloc(len + 1);
memcpy(rows.rw[rows.rownum].chars, s, len); /* Allocate memory for the line and copy it
rows.rw[rows.rownum].chars[len] = '\0'; * at the current row number */
rows.rw[rows.rownum].size = len; rows.rw[rows.rownum].chars = malloc(len + 1);
updateRender(&rows.rw[rows.rownum]); memcpy(rows.rw[rows.rownum].chars, s, len);
rows.rownum++; rows.rw[rows.rownum].chars[len] = '\0';
} rows.rw[rows.rownum].size = len;
updateRender(&rows.rw[rows.rownum]);
void updateRender (row *rw) rows.rownum++;
{ }
/* count the special characters (only tabs for now) */
int tabs = 0, i; void updateRender (row *rw)
for (i = 0; i < rw->size; i++) { {
if (rw->chars[i] == '\t') tabs++; /* count the special characters (only tabs for now) */
} int tabs = 0, i;
rw->render = NULL; for (i = 0; i < rw->size; i++) {
free(rw->render); if (rw->chars[i] == '\t') tabs++;
}
/* Render is long as size with the added tab spaces - 1 rw->render = NULL;
* (we already count for the \t as a char) */ free(rw->render);
rw->render = malloc(rw->size + tabs * (TABSIZE - 1) + 1);
/* Render is long as size with the added tab spaces - 1
if (rw->render == NULL) termDie ("malloc in updateRender"); * (we already count for the \t as a char) */
rw->render = malloc(rw->size + tabs * (TABSIZE - 1) + 1);
/* put all the characters (substituing all special chars)
* into the render buffer */ if (rw->render == NULL) termDie ("malloc in updateRender");
int off = 0;
for (i = 0; i < rw->size; i++) { /* put all the characters (substituing all special chars)
if (rw->chars[i] == '\t') { * into the render buffer */
for (int j = 0; j < TABSIZE; j++) int off = 0;
rw->render[off++] = ' '; for (i = 0; i < rw->size; i++) {
} else { if (rw->chars[i] == '\t') {
rw->render[off++] = rw->chars[i]; for (int j = 0; j < TABSIZE; j++)
} rw->render[off++] = ' ';
} } else {
rw->render[off] = '\0'; rw->render[off++] = rw->chars[i];
rw->r_size = off; }
} }
rw->render[off] = '\0';
void rowInit (void) rw->r_size = off;
{ }
rows.rw = NULL;
rows.rownum = 0; void rowInit (void)
} {
rows.rw = NULL;
/* ----------------------------- file operations --------------------------- */ rows.rownum = 0;
}
/* take care of the cursor movement */
void cursorMove (int a) /* ----------------------------- file operations --------------------------- */
{
switch (a) { /* take care of the cursor movement */
case (KEY_LEFT): void cursorMove (int a)
if (t.cur.x <= 0 && !t.cur.off_x) { {
if (t.cur.y) { switch (a) {
t.cur.y--; case (KEY_LEFT):
t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size; if (t.cur.x <= 0 && !t.cur.off_x) {
} if (t.cur.y) {
} else t.cur.x--; t.cur.y--;
break; t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size;
}
case (KEY_RIGHT): } else t.cur.x--;
if (t.cur.x + t.cur.off_x >= rows.rw[t.cur.y + t.cur.off_y].size) { break;
if (t.cur.y + t.cur.off_y < rows.rownum - 1) {
t.cur.y++; case (KEY_RIGHT):
if (t.cur.off_x) t.cur.off_x = 0; if (t.cur.x + t.cur.off_x >= rows.rw[t.cur.y + t.cur.off_y].size) {
t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size; if (t.cur.y + t.cur.off_y < rows.rownum - 1) {
} t.cur.y++;
} else t.cur.x++; if (t.cur.off_x) t.cur.off_x = 0;
break; t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size;
}
case (KEY_UP): } else t.cur.x++;
if (t.cur.y + t.cur.off_y > 0) { break;
if (t.cur.y) t.cur.y--;
if (t.cur.x + t.cur.off_x > rows.rw[t.cur.y + t.cur.off_y].size) { case (KEY_UP):
if (t.cur.off_x) t.cur.off_x = 0; if (t.cur.y + t.cur.off_y > 0) {
t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size; if (t.cur.y) t.cur.y--;
} if (t.cur.x + t.cur.off_x > rows.rw[t.cur.y + t.cur.off_y].size) {
} if (t.cur.off_x) t.cur.off_x = 0;
break; t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size;
}
case (KEY_DOWN): }
if (t.cur.y + t.cur.off_y < rows.rownum - 1) { break;
t.cur.y++;
if (t.cur.x + t.cur.off_x > rows.rw[t.cur.y + t.cur.off_y].size) { case (KEY_DOWN):
if (t.cur.off_x) t.cur.off_x = 0; if (t.cur.y + t.cur.off_y < rows.rownum - 1) {
t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size; t.cur.y++;
} if (t.cur.x + t.cur.off_x > rows.rw[t.cur.y + t.cur.off_y].size) {
} if (t.cur.off_x) t.cur.off_x = 0;
break; t.cur.x = rows.rw[t.cur.y + t.cur.off_y].size;
} }
} }
break;
void updateScroll (void) }
{ }
/* Set y offset */
if (t.cur.y >= t.dim.y) { void updateScroll (void)
if (t.cur.y == t.dim.y) t.cur.off_y++; {
else t.cur.off_y += t.cur.y - t.dim.y; /* Set y offset */
if (t.cur.y >= t.dim.y) {
t.cur.y = t.dim.y - 1; if (t.cur.y == t.dim.y) t.cur.off_y++;
else t.cur.off_y += t.cur.y - t.dim.y;
} else if (t.cur.y <= 0 && t.cur.off_y > 0) {
t.cur.off_y--; t.cur.y = t.dim.y - 1;
t.cur.y = 0;
} } else if (t.cur.y <= 0 && t.cur.off_y > 0) {
t.cur.off_y--;
/* Set x offeset */ t.cur.y = 0;
if (t.cur.x >= t.dim.x) { }
if (t.cur.x == t.dim.x) t.cur.off_x++;
else t.cur.off_x += t.cur.x - t.dim.x; /* Set x offeset */
if (t.cur.x >= t.dim.x) {
t.cur.x = t.dim.x; if (t.cur.x == t.dim.x) t.cur.off_x++;
else t.cur.off_x += t.cur.x - t.dim.x;
} else if (t.cur.x <= 0 && t.cur.off_x > 0) {
t.cur.off_x--; t.cur.x = t.dim.x;
t.cur.x = 0;
} } else if (t.cur.x <= 0 && t.cur.off_x > 0) {
/* convert the cursor from real to render */ t.cur.off_x--;
int ln = t.cur.y + t.cur.off_y; t.cur.x = 0;
t.cur.r_x = curRealToRender(&rows.rw[ln], t.cur.x); }
} /* convert the cursor from real to render */
int ln = t.cur.y + t.cur.off_y;
/*---------------------------------- scroll ------------------------------------*/ t.cur.r_x = curRealToRender(&rows.rw[ln], t.cur.x);
}
/*
void bufferFree (void) /*---------------------------------- scroll ------------------------------------*/
{
for (int i = 0; i < rows.rownum; i++) { /*
free(&rows.rw[i].chars); void bufferFree (void)
free(&rows.rw[i].render); {
} for (int i = 0; i < rows.rownum; i++) {
free(rows.rw); free(&rows.rw[i].chars);
} free(&rows.rw[i].render);
*/ }
free(rows.rw);
/* See whats under the cursor (memory) */ }
int whatsThat (void) { */
int ln = t.cur.y + t.cur.off_y;
int c = rows.rw[ln].chars[t.cur.x + t.cur.off_x]; /* See whats under the cursor (memory) */
switch (c) { int whatsThat (void) {
case ('\t'): int ln = t.cur.y + t.cur.off_y;
return '^'; int c = rows.rw[ln].chars[t.cur.x + t.cur.off_x];
break; switch (c) {
case (' '): case ('\t'):
return '~'; return '^';
break; break;
default: case (' '):
return c; return '~';
break; break;
} default:
return 0; return c;
} break;
}
/*--------------------------------- garbage ------------------------------------*/ return 0;
}
/*--------------------------------- garbage ------------------------------------*/
void updateInfo (void)
{
getmaxyx(stdscr, t.dim.y, t.dim.x);
t.dim.y -= 1;
t.dim.x -= t.pad + 1;
}
/*--------------------------------- testing ------------------------------------*/
Loading…
Cancel
Save