2011-11-18 18:56:55 -05:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <string.h>
|
2011-11-22 11:26:47 -05:00
|
|
|
#include <errno.h>
|
2011-11-26 05:00:45 -05:00
|
|
|
#include <error.h>
|
2011-11-18 18:56:55 -05:00
|
|
|
#include <libmaxsi/platform.h>
|
|
|
|
#include <libmaxsi/sortix-keyboard.h>
|
|
|
|
|
|
|
|
const int MODE_QUIT = 0;
|
|
|
|
const int MODE_TEXT = 1;
|
|
|
|
const int MODE_CONFIRM_QUIT = 2;
|
|
|
|
const int MODE_SAVE = 3;
|
|
|
|
const int MODE_LOAD = 4;
|
|
|
|
|
|
|
|
const unsigned WIDTH = 80;
|
|
|
|
const unsigned HEIGHT = 24;
|
|
|
|
char buffers[HEIGHT+1][WIDTH+1];
|
|
|
|
|
|
|
|
unsigned cursorx = 0;
|
|
|
|
unsigned cursory = 0;
|
|
|
|
unsigned numlines = 1;
|
|
|
|
|
|
|
|
char filename[256];
|
|
|
|
|
|
|
|
bool bufferchanged;
|
|
|
|
|
|
|
|
void clearbuffers()
|
|
|
|
{
|
|
|
|
for ( unsigned y = 0; y < HEIGHT + 1; y++ )
|
|
|
|
{
|
|
|
|
for ( unsigned x = 0; x < WIDTH + 1; x++ ) { buffers[y][x] = 0; }
|
|
|
|
}
|
|
|
|
|
|
|
|
bufferchanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
using namespace Maxsi;
|
|
|
|
|
|
|
|
void cursorto(unsigned x, unsigned y)
|
|
|
|
{
|
|
|
|
printf("\e[%u;%uH", y+1+1, x+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void drawtextmode()
|
|
|
|
{
|
|
|
|
const char* printfilename = ( strlen(filename) > 0 ) ? filename : "New Buffer";
|
|
|
|
printf("\e[30m\e[47m\e[2J\e[H");
|
|
|
|
printf("Text Editor\t\tfile: %s\n", printfilename);
|
|
|
|
printf("\e[37m\e[40m\e[0J");
|
|
|
|
for ( unsigned y = 0; y < HEIGHT; y++ )
|
|
|
|
{
|
|
|
|
printf("%s", buffers[y]);
|
|
|
|
if ( y < HEIGHT-1 ) { printf("\n"); }
|
|
|
|
}
|
|
|
|
|
|
|
|
cursorto(cursorx, cursory);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned textmode()
|
|
|
|
{
|
|
|
|
drawtextmode();
|
|
|
|
|
|
|
|
bool ctrl = false;
|
|
|
|
|
2011-11-24 13:27:24 -05:00
|
|
|
int oldcursorx = -1;
|
|
|
|
int oldcursory = -1;
|
2011-11-18 18:56:55 -05:00
|
|
|
while ( true )
|
|
|
|
{
|
2011-11-24 13:27:24 -05:00
|
|
|
if ( oldcursorx != cursorx || oldcursory != cursory )
|
|
|
|
{
|
|
|
|
cursorto(cursorx, cursory);
|
|
|
|
oldcursorx = cursorx;
|
|
|
|
oldcursory = cursory;
|
|
|
|
}
|
2011-11-18 18:56:55 -05:00
|
|
|
|
|
|
|
unsigned method = System::Keyboard::POLL;
|
|
|
|
uint32_t codepoint = System::Keyboard::ReceiveKeystroke(method);
|
|
|
|
|
|
|
|
if ( codepoint == 0 ) { continue; }
|
|
|
|
if ( codepoint & Keyboard::DEPRESSED )
|
|
|
|
{
|
|
|
|
if ( codepoint == Keyboard::CTRL | Keyboard::DEPRESSED ) { ctrl = false; }
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ctrl )
|
|
|
|
{
|
|
|
|
if ( codepoint == 'o' || codepoint == 'O' ) { return MODE_SAVE; }
|
|
|
|
if ( codepoint == 'r' || codepoint == 'R' ) { return MODE_LOAD; }
|
|
|
|
if ( codepoint == 'x' || codepoint == 'X' ) { return MODE_CONFIRM_QUIT; }
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( codepoint )
|
|
|
|
{
|
|
|
|
case Keyboard::ESC:
|
|
|
|
return MODE_CONFIRM_QUIT;
|
|
|
|
break;
|
|
|
|
case Keyboard::CTRL:
|
|
|
|
ctrl = true;
|
|
|
|
break;
|
|
|
|
case Keyboard::UP:
|
|
|
|
if ( cursory ) { cursory--; }
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
cursorx = 0;
|
|
|
|
if ( cursory < HEIGHT-1 ) { numlines++; }
|
|
|
|
case Keyboard::DOWN:
|
|
|
|
if ( cursory < numlines-1 ) { cursory++; }
|
|
|
|
break;
|
|
|
|
case Keyboard::LEFT:
|
|
|
|
if ( cursorx ) { cursorx--; }
|
|
|
|
break;
|
|
|
|
case Keyboard::RIGHT:
|
|
|
|
if ( cursorx < WIDTH-1 ) { cursorx++; }
|
|
|
|
break;
|
|
|
|
case '\b':
|
|
|
|
if ( cursorx )
|
|
|
|
{
|
|
|
|
cursorx--;
|
|
|
|
|
|
|
|
for ( unsigned x = cursorx; x < WIDTH; x++ )
|
|
|
|
{
|
|
|
|
buffers[cursory][x] = buffers[cursory][x+1];
|
|
|
|
bufferchanged = true;
|
|
|
|
}
|
|
|
|
printf("\e[2K\r%s", buffers[cursory]);
|
|
|
|
}
|
|
|
|
else if ( 0 < cursory && strlen(buffers[cursory]) == 0 )
|
|
|
|
{
|
|
|
|
for ( unsigned y = cursory; y < numlines; y++ )
|
|
|
|
{
|
|
|
|
for ( unsigned x = 0; x < WIDTH; x++ )
|
|
|
|
{
|
|
|
|
buffers[y][x] = buffers[y+1][x];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
numlines--;
|
|
|
|
cursory--;
|
|
|
|
cursorx = strlen(buffers[cursory]);
|
|
|
|
|
|
|
|
drawtextmode();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t linelen = strlen(buffers[cursory]);
|
|
|
|
if ( linelen < cursorx ) { cursorx = linelen; }
|
|
|
|
|
|
|
|
if ( WIDTH <= cursorx ) { continue; }
|
|
|
|
if ( codepoint >= 0x80 ) { continue; }
|
|
|
|
if ( codepoint == '\t' ) { continue; }
|
|
|
|
if ( codepoint == '\b' ) { continue; }
|
|
|
|
if ( codepoint == '\n' ) { continue; }
|
|
|
|
if ( codepoint == '\r' ) { continue; }
|
|
|
|
|
|
|
|
char msg[2];
|
|
|
|
msg[0] = codepoint;
|
|
|
|
msg[1] = 0;
|
|
|
|
printf("%s", msg);
|
|
|
|
buffers[cursory][cursorx++] = codepoint;
|
|
|
|
bufferchanged = true;
|
|
|
|
if ( WIDTH <= cursorx ) { cursorx = WIDTH-1; }
|
|
|
|
}
|
|
|
|
|
|
|
|
return MODE_QUIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned confirmquit()
|
|
|
|
{
|
|
|
|
if ( !bufferchanged ) { return MODE_QUIT; }
|
|
|
|
|
|
|
|
printf("\e37m\e40m\e[2J\e[H");
|
|
|
|
printf("There are unsaved changes: Are you sure you want to quit? (Y/N)\n");
|
|
|
|
|
|
|
|
while ( true )
|
|
|
|
{
|
|
|
|
unsigned method = System::Keyboard::POLL;
|
|
|
|
uint32_t codepoint = System::Keyboard::ReceiveKeystroke(method);
|
|
|
|
|
|
|
|
if ( codepoint == 0 ) { continue; }
|
|
|
|
if ( codepoint & Keyboard::DEPRESSED ) { continue; }
|
|
|
|
|
|
|
|
switch ( codepoint )
|
|
|
|
{
|
|
|
|
case Keyboard::ESC:
|
|
|
|
return MODE_QUIT;
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
case 'N':
|
|
|
|
return MODE_TEXT;
|
|
|
|
case 'y':
|
|
|
|
case 'Y':
|
|
|
|
return MODE_QUIT;
|
|
|
|
default:
|
|
|
|
printf("Would you like to quit? N for No, Y for Yes\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool savetofile(const char* path)
|
|
|
|
{
|
|
|
|
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0777);
|
2011-11-26 05:00:45 -05:00
|
|
|
if ( fd < 0 ) { error(0, errno, "%s", path); return false; }
|
2011-11-18 18:56:55 -05:00
|
|
|
|
|
|
|
for ( unsigned y = 0; y < numlines; y++ )
|
|
|
|
{
|
|
|
|
size_t len = strlen(buffers[y]);
|
|
|
|
buffers[y][len] = '\n';
|
2011-11-24 04:26:36 -05:00
|
|
|
bool result = !writeall(fd, buffers[y], len+1);
|
2011-11-18 18:56:55 -05:00
|
|
|
buffers[y][len] = 0;
|
2011-11-26 05:00:45 -05:00
|
|
|
if ( !result ) { error(0, errno, "write: %s", path); close(fd); return false; }
|
2011-11-18 18:56:55 -05:00
|
|
|
}
|
|
|
|
|
2011-11-26 05:00:45 -05:00
|
|
|
if ( close(fd) ) { error(0, errno, "close: %s", path); return false; }
|
2011-11-18 18:56:55 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int savemode()
|
|
|
|
{
|
|
|
|
printf("\e37m\e40m\e[2J\e[H");
|
|
|
|
printf("Please enter the filename you wish to save the text to and press "
|
|
|
|
"enter. Type an empty filename or press ESC to abort.\n\n");
|
|
|
|
|
|
|
|
char writefilename[256];
|
|
|
|
strcpy(writefilename, filename);
|
|
|
|
|
|
|
|
retry:
|
|
|
|
size_t len = strlen(writefilename);
|
|
|
|
printf("File Name to Write: %s", writefilename);
|
|
|
|
|
|
|
|
bool readytosave = false;
|
|
|
|
|
|
|
|
while ( !readytosave )
|
|
|
|
{
|
|
|
|
unsigned method = System::Keyboard::POLL;
|
|
|
|
uint32_t codepoint = System::Keyboard::ReceiveKeystroke(method);
|
|
|
|
|
|
|
|
if ( codepoint == 0 ) { continue; }
|
|
|
|
if ( codepoint & Keyboard::DEPRESSED ) { continue; }
|
|
|
|
|
|
|
|
switch ( codepoint )
|
|
|
|
{
|
|
|
|
case Keyboard::ESC:
|
|
|
|
return MODE_TEXT;
|
|
|
|
break;
|
|
|
|
case '\b':
|
|
|
|
if ( 0 < len ) { printf("\b"); writefilename[--len] = 0; }
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
if ( len == 0 ) { return MODE_TEXT; }
|
|
|
|
readytosave = true;
|
|
|
|
printf("\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if ( 0x80 <= codepoint ) { continue; }
|
|
|
|
writefilename[len++] = codepoint;
|
|
|
|
writefilename[len+1] = 0;
|
|
|
|
char msg[2];
|
|
|
|
msg[0] = codepoint;
|
|
|
|
msg[1] = 0;
|
|
|
|
printf("%s", msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !savetofile(writefilename) ) { goto retry; }
|
|
|
|
|
|
|
|
strcpy(filename, writefilename);
|
|
|
|
bufferchanged = false;
|
|
|
|
|
|
|
|
printf("Succesfully saved\n");
|
|
|
|
sleep(1);
|
|
|
|
return MODE_TEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool loadfromfile(const char* path)
|
|
|
|
{
|
|
|
|
int fd = open(path, O_RDONLY, 0777);
|
2011-11-26 05:00:45 -05:00
|
|
|
if ( fd < 0 ) { error(0, errno, "%s", path); return false; }
|
2011-11-18 18:56:55 -05:00
|
|
|
|
|
|
|
clearbuffers();
|
|
|
|
|
|
|
|
const size_t BUFFER_SIZE = 256;
|
|
|
|
char buffer[BUFFER_SIZE];
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
while ( !done )
|
|
|
|
{
|
|
|
|
ssize_t bytesread = read(fd, buffer, BUFFER_SIZE);
|
2011-11-26 05:00:45 -05:00
|
|
|
if ( bytesread < 0 ) { error(0, errno, "read: %s", path); close(fd); return false; }
|
2011-11-18 18:56:55 -05:00
|
|
|
if ( bytesread == 0 ) { break; }
|
|
|
|
for ( ssize_t i = 0; i < bytesread; i++ )
|
|
|
|
{
|
|
|
|
if ( buffer[i] == '\n' )
|
|
|
|
{
|
|
|
|
if ( HEIGHT-1 <= ++cursory ) { done = true; break; }
|
|
|
|
cursorx = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( WIDTH <= cursorx ) { continue; }
|
|
|
|
buffers[cursory][cursorx++] = buffer[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
numlines = cursory + 1;
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
cursorx = 0;
|
|
|
|
cursory = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int loadmode()
|
|
|
|
{
|
|
|
|
printf("\e37m\e40m\e[2J\e[H");
|
|
|
|
printf("Please enter the filename you wish to load text from and press "
|
|
|
|
"enter. Type an empty filename or press ESC to abort.\n\n");
|
|
|
|
|
|
|
|
char loadfilename[256];
|
|
|
|
loadfilename[0] = 0;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
size_t len = strlen(loadfilename);
|
|
|
|
printf("File Name to Load: %s", loadfilename);
|
|
|
|
|
|
|
|
bool readytoload = false;
|
|
|
|
|
|
|
|
while ( !readytoload )
|
|
|
|
{
|
|
|
|
unsigned method = System::Keyboard::POLL;
|
|
|
|
uint32_t codepoint = System::Keyboard::ReceiveKeystroke(method);
|
|
|
|
|
|
|
|
if ( codepoint == 0 ) { continue; }
|
|
|
|
if ( codepoint & Keyboard::DEPRESSED ) { continue; }
|
|
|
|
|
|
|
|
switch ( codepoint )
|
|
|
|
{
|
|
|
|
case Keyboard::ESC:
|
|
|
|
return MODE_TEXT;
|
|
|
|
break;
|
|
|
|
case '\b':
|
|
|
|
if ( 0 < len ) { printf("\b"); loadfilename[--len] = 0; }
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
if ( len == 0 ) { return MODE_TEXT; }
|
|
|
|
readytoload = true;
|
|
|
|
printf("\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if ( 0x80 <= codepoint ) { continue; }
|
|
|
|
loadfilename[len++] = codepoint;
|
|
|
|
loadfilename[len+1] = 0;
|
|
|
|
char msg[2];
|
|
|
|
msg[0] = codepoint;
|
|
|
|
msg[1] = 0;
|
|
|
|
printf("%s", msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !loadfromfile(loadfilename) )
|
|
|
|
{
|
|
|
|
clearbuffers();
|
|
|
|
cursorx = 0;
|
|
|
|
cursory = 0;
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(filename, loadfilename);
|
|
|
|
bufferchanged = false;
|
|
|
|
|
|
|
|
return MODE_TEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
void run()
|
|
|
|
{
|
|
|
|
bufferchanged = false;
|
|
|
|
|
|
|
|
unsigned mode = MODE_TEXT;
|
|
|
|
while ( mode != MODE_QUIT )
|
|
|
|
{
|
|
|
|
switch ( mode )
|
|
|
|
{
|
|
|
|
case MODE_TEXT:
|
|
|
|
mode = textmode();
|
|
|
|
break;
|
|
|
|
case MODE_CONFIRM_QUIT:
|
|
|
|
mode = confirmquit();
|
|
|
|
break;
|
|
|
|
case MODE_SAVE:
|
|
|
|
mode = savemode();
|
|
|
|
break;
|
|
|
|
case MODE_LOAD:
|
|
|
|
mode = loadmode();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("Application Bug: Unknown Mode\n");
|
|
|
|
sleep(1);
|
|
|
|
mode = MODE_TEXT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\e[37m\e[40m\e[2J\e[H");
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2011-11-26 14:56:45 -05:00
|
|
|
if ( !isatty(1) ) { error(1, errno, "stdout must be a tty"); return 1; }
|
|
|
|
|
2011-11-18 18:56:55 -05:00
|
|
|
if ( argc < 2 )
|
|
|
|
{
|
|
|
|
clearbuffers();
|
|
|
|
strcpy(filename, "");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
strcpy(filename, argv[1]);
|
|
|
|
if ( !loadfromfile(filename) ) { clearbuffers(); }
|
|
|
|
cursorx = 0;
|
|
|
|
cursory = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
run();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|