diff --git a/utils/Makefile b/utils/Makefile index 0033c559..daad2298 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -13,6 +13,7 @@ ls \ help \ uname \ idle \ +editor \ BINARIES:=$(addprefix $(INITRDDIR)/,$(LOCALBINARIES)) diff --git a/utils/editor.cpp b/utils/editor.cpp new file mode 100644 index 00000000..4470ab0d --- /dev/null +++ b/utils/editor.cpp @@ -0,0 +1,434 @@ +#include +#include +#include +#include +#include +#include + +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; + + while ( true ) + { + cursorto(cursorx, cursory); + + 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 writeall(int fd, const void* buffer, size_t len) +{ + const char* buf = (const char*) buffer; + while ( len ) + { + ssize_t byteswritten = write(fd, buf, len); + if ( byteswritten < 0 ) { return false; } + buf += byteswritten; + len -= byteswritten; + } + + return true; +} + +bool savetofile(const char* path) +{ + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0777); + if ( fd < 0 ) { printf("unable to open for writing: %s\n", path); return false; } + + for ( unsigned y = 0; y < numlines; y++ ) + { + size_t len = strlen(buffers[y]); + buffers[y][len] = '\n'; + bool result = writeall(fd, buffers[y], len+1); + buffers[y][len] = 0; + if ( !result ) { printf("unable to write: %s\n", path); close(fd); return false; } + } + + if ( close(fd) ) { printf("unable to write: %s\n", path); return false; } + 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); + if ( fd < 0 ) { printf("unable to open for reading: %s\n", path); return false; } + + clearbuffers(); + + const size_t BUFFER_SIZE = 256; + char buffer[BUFFER_SIZE]; + + bool done = false; + while ( !done ) + { + ssize_t bytesread = read(fd, buffer, BUFFER_SIZE); + if ( bytesread < 0 ) { close(fd); return false; } + 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[]) +{ + if ( argc < 2 ) + { + clearbuffers(); + strcpy(filename, ""); + } + else + { + strcpy(filename, argv[1]); + if ( !loadfromfile(filename) ) { clearbuffers(); } + cursorx = 0; + cursory = 0; + } + + run(); + + return 0; +}