diff --git a/Usermode/Applications/edit_src/Makefile b/Usermode/Applications/edit_src/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..05748b1cb9135be918d36d48007a8ef8599d6a51
--- /dev/null
+++ b/Usermode/Applications/edit_src/Makefile
@@ -0,0 +1,12 @@
+# Project: text editor
+
+-include ../Makefile.cfg
+
+OBJ = main.o
+BIN = edit
+
+-include ../Makefile.tpl
+
+edit.lin:
+	gcc main.c -o edit.lin -DUSE_LOCAL=1 -Wall
+
diff --git a/Usermode/Applications/edit_src/main.c b/Usermode/Applications/edit_src/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..eba9be606dcdda2a05d9de64e5d1e020e3322e5e
--- /dev/null
+++ b/Usermode/Applications/edit_src/main.c
@@ -0,0 +1,374 @@
+/*
+ * Acess2 Text Editor
+ */
+#if !USE_LOCAL
+# include <acess/sys.h>	// Needed for IOCtl
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#if USE_LOCAL
+# include <sys/ioctl.h>
+# include <termios.h>
+#else
+# include <acess/devices/terminal.h>
+#endif
+
+#define MAX_FILES_OPEN	1
+
+// === TYPES ===
+typedef struct
+{
+	const char	*Filename;
+	char	**LineBuffer;
+	 int	FileSize;
+	 int	LineCount;
+	 
+	 int	FirstLine;
+	 int 	CurrentLine;	//!< Currently selected line
+	 int 	CurrentPos;	//!< Position in that line
+}	tFile;
+
+// === PROTOTYPES ===
+void	SigINT_Handler(int Signum);
+void	ExitHandler(void);
+ int	main(int argc, char *argv[]);
+ int	OpenFile(tFile *Dest, const char *Path);
+void	UpdateDisplayFull(void);
+void	UpdateDisplayLine(int LineNum);
+void	UpdateDisplayStatus(void);
+
+void	CursorUp(tFile *File);
+void	CursorDown(tFile *File);
+void	CursorRight(tFile *File);
+void	CursorLeft(tFile *File);
+
+void	Term_SetPos(int Row, int Col);
+
+// === GLOBALS ===
+ int	giProgramState = 0;
+tFile	gaFiles[MAX_FILES_OPEN];
+tFile	*gpCurrentFile;
+#if USE_LOCAL
+struct termios	gOldTermios;
+#endif
+ int	giTerminal_Width = 80;
+ int	giTerminal_Height = 25;
+
+// === CODE ===
+void SigINT_Handler(int Signum)
+{
+	exit(55);
+}
+
+void ExitHandler(void)
+{
+	#if USE_LOCAL
+	tcsetattr(0, TCSANOW, &gOldTermios);
+	#endif
+	printf("\x1B[?1047l");
+}
+
+/**
+ * \fn int main(int argc, char *argv[])
+ * \brief Entrypoint
+ */
+int main(int argc, char *argv[])
+{
+	
+	if(argc < 2) {
+		printf("Usage: edit <file>\n");
+		return -1;
+	}
+
+	if( OpenFile(&gaFiles[0], argv[1]) ) {
+		return -1;
+	}
+	
+	signal(SIGINT, SigINT_Handler);
+	atexit(ExitHandler);
+
+	// Disable input buffering
+	#if USE_LOCAL
+	{
+		struct termios	term;
+		tcgetattr(0, &gOldTermios);
+		term = gOldTermios;
+		term.c_lflag &= ~(ICANON|ECHO);
+		tcsetattr(0, TCSANOW, &term);
+		//setbuf(stdin, NULL);
+	}
+	#endif
+	
+	// Go to alternte screen buffer
+	printf("\x1B[?1047h");
+	
+	gpCurrentFile = &gaFiles[0];
+	
+	UpdateDisplayFull();
+	
+	giProgramState = 1;
+	
+	while(giProgramState)
+	{
+		char	ch = 0;
+		
+		read(0, &ch, 1);
+		
+		if(ch == '\x1B')
+		{
+			read(0, &ch, 1);
+			switch(ch)
+			{
+			case 'A':	CursorUp(gpCurrentFile);	break;
+			case 'B':	CursorDown(gpCurrentFile);	break;
+			case 'C':	CursorRight(gpCurrentFile);	break;
+			case 'D':	CursorLeft(gpCurrentFile);	break;
+			
+			case '[':
+				read(0, &ch, 1);
+				switch(ch)
+				{
+				case 'A':	CursorUp(gpCurrentFile);	break;
+				case 'B':	CursorDown(gpCurrentFile);	break;
+				case 'C':	CursorRight(gpCurrentFile);	break;
+				case 'D':	CursorLeft(gpCurrentFile);	break;
+				default:
+					printf("ch (\\x1B[) = %x\r", ch);
+					fflush(stdout);
+					break;
+				}
+				break;
+			
+			default:
+				printf("ch (\\x1B) = %x\r", ch);
+				fflush(stdout);
+				break;
+			}
+			// TODO: Move
+		}
+		
+		switch(ch)
+		{
+		case 'q':
+			giProgramState = 0;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+int OpenFile(tFile *Dest, const char *Path)
+{
+	FILE	*fp;
+	char	*buffer;
+	 int	i, j;
+	 int	start;
+	
+	fp = fopen(Path, "r");
+	if(!fp) {
+		fprintf(stderr, "Unable to open %s\n", Path);
+		return -1;
+	}
+	
+	Dest->Filename = Path;
+	
+	fseek(fp, 0, SEEK_END);
+	Dest->FileSize = ftell(fp);
+	fseek(fp, 0, SEEK_SET);
+	
+	buffer = malloc(Dest->FileSize+1);
+	fread(buffer, Dest->FileSize, 1, fp);
+	fclose(fp);
+	buffer[Dest->FileSize] = '\0';
+	
+	Dest->LineCount = 1;
+	for( i = 0; i < Dest->FileSize; i ++ )
+	{
+		if( buffer[i] == '\n' )
+			Dest->LineCount ++;
+	}
+	
+	Dest->LineBuffer = malloc( Dest->LineCount * sizeof(char*) );
+	start = 0;
+	j = 0;
+	for( i = 0; i < Dest->FileSize; i ++ )
+	{
+		if( buffer[i] == '\n' )
+		{
+			buffer[i] = '\0';
+			Dest->LineBuffer[j] = strdup( &buffer[start] );
+			start = i+1;
+			j ++;
+		}
+	}
+	Dest->LineBuffer[j] = strdup( &buffer[start] );
+	
+	free(buffer);
+	
+	Dest->FirstLine = 0;
+	Dest->CurrentLine = 0;
+	Dest->CurrentPos = 0;
+	
+	return 0;
+}
+
+void ShowLine(tFile *File, int LineNum, int X, int Y, int W)
+{
+	 int	j, k;
+	char	*line;
+	
+	line = File->LineBuffer[LineNum];
+	printf("%6i  ", LineNum+1);
+	j = 8;
+	for( k = 0; j < W-1 && line[k]; j++, k++ )
+	{
+		switch( line[k] )
+		{
+		case '\t':
+			printf("\t");
+			j += 8;
+			j &= ~7;
+			break;
+		default:
+			if( line[k] < ' ' || line[k] >= 0x7F )	continue;
+			printf("%c", line[k]);
+			break;
+		}
+	}
+	
+	for( ; j < W-1; j++ )
+		printf(" ");
+	printf("\n");
+}
+
+void ShowFile(tFile *File, int X, int Y, int W, int H)
+{
+	 int	i;
+	Term_SetPos(Y, X);
+	
+	for( i = 0; i < H; i ++ )
+	{
+		if( File->FirstLine + i >= File->LineCount )	break;
+		ShowLine( File, File->FirstLine + i, X, Y + i, W );
+	}
+	for( ; i < H; i++ )
+		printf("\n");
+}
+
+void UpdateDisplayFull(void)
+{
+	#if USE_LOCAL
+	{
+		struct winsize ws;
+		ioctl(0, TIOCGWINSZ, &ws);
+		giTerminal_Width = ws.ws_col;
+		giTerminal_Height = ws.ws_row;
+	}
+	#else
+	giTerminal_Width = ioctl(1, TERM_IOCTL_WIDTH, NULL);
+	giTerminal_Height = ioctl(1, TERM_IOCTL_HEIGHT, NULL);
+	#endif
+	
+	printf("\x1B[2J");
+	
+	ShowFile(gpCurrentFile, 0, 0, giTerminal_Width, giTerminal_Height - 1);
+	
+	
+	// Status Line
+	UpdateDisplayStatus();
+	
+	Term_SetPos(
+		gpCurrentFile->CurrentLine-gpCurrentFile->FirstLine,
+		8 + gpCurrentFile->CurrentPos
+		);
+	fflush(stdout);
+}
+
+void UpdateDisplayLine(int Line)
+{
+	ShowLine(
+		gpCurrentFile, Line,
+		0, Line - gpCurrentFile->FirstLine,
+		giTerminal_Width );
+}
+
+void UpdateDisplayStatus(void)
+{
+	int lastLine = gpCurrentFile->FirstLine + giTerminal_Height - 1;
+	
+	if( lastLine > gpCurrentFile->LineCount )
+		lastLine = gpCurrentFile->LineCount;
+	
+	printf("--- Line %i/%i (showing %i to %i)",
+		gpCurrentFile->CurrentLine + 1, gpCurrentFile->LineCount,
+		gpCurrentFile->FirstLine, lastLine);
+}
+
+void CursorUp(tFile *File)
+{
+	if( File->CurrentLine > 0 )
+	{
+		File->CurrentLine --;
+		if( File->FirstLine > File->CurrentLine )
+		{
+			File->FirstLine = File->CurrentLine;
+			UpdateDisplayFull();
+		}
+		else
+		{
+			UpdateDisplayLine(File->CurrentLine + 1);
+			UpdateDisplayLine(File->CurrentLine);
+			UpdateDisplayStatus();
+		}
+	}
+}
+
+void CursorDown(tFile *File)
+{
+	if( File->CurrentLine+1 < File->LineCount )
+	{
+		File->CurrentLine ++;
+		if( File->FirstLine < File->CurrentLine - (giTerminal_Height-2) )
+		{
+			File->FirstLine = File->CurrentLine - (giTerminal_Height-2);
+			UpdateDisplayFull();
+		}
+		else
+		{
+			UpdateDisplayLine(File->CurrentLine - 1);
+			UpdateDisplayLine(File->CurrentLine);
+			UpdateDisplayStatus();
+		}
+	}
+}
+
+void CursorRight(tFile *File)
+{
+	if( File->CurrentPos > 0 )
+	{
+		File->CurrentPos --;
+		UpdateDisplayLine(File->CurrentLine);
+		UpdateDisplayStatus();
+	}
+}
+
+void CursorLeft(tFile *File)
+{
+	if( File->LineBuffer[File->CurrentLine][File->CurrentPos+1] )
+	{
+		File->CurrentPos --;
+		UpdateDisplayLine(File->CurrentLine);
+		UpdateDisplayStatus();
+	}
+}
+
+void Term_SetPos(int Row, int Col)
+{
+	printf("\x1B[%i;%iH", Row+1, Col+1);	// Set cursor
+	fflush(stdout);
+}