Compare commits
4 Commits
main
...
without-li
| Author | SHA1 | Date | |
|---|---|---|---|
| b35706bab6 | |||
| 277759d11b | |||
| bc30a5a71c | |||
| 3ce65e6a33 |
2
Makefile
Normal file
2
Makefile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
texteditor: editor.c
|
||||||
|
$(CC) editor.c -o texteditor -Wall -Wextra -pedantic -std=c99
|
||||||
@@ -1,2 +1,4 @@
|
|||||||
# nice-nano
|
# texteditor
|
||||||
|
|
||||||
|
https://viewsourcecode.org/snaptoken/kilo/index.html
|
||||||
|
|
||||||
|
|||||||
570
editor.c
570
editor.c
@@ -1,110 +1,498 @@
|
|||||||
#ifdef _WIN32
|
// INCLUDE
|
||||||
#include <pdcurses.h> // PDCurses verwenden, wenn auf Windows kompiliert
|
|
||||||
#else
|
#define _DEFAULT_SOURCE
|
||||||
#include <ncurses.h> // Ncurses verwenden, wenn auf Unix/Linux kompiliert
|
#define _BSD_SOURCE
|
||||||
#endif
|
#define _GNU_SOURCE
|
||||||
#include <stdlib.h>
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// DEFINE
|
||||||
|
|
||||||
|
#define EDITOR_VERSION "0.0.1-alpha1"
|
||||||
|
|
||||||
#define CTRL_KEY(k) ((k) & 0x1f)
|
#define CTRL_KEY(k) ((k) & 0x1f)
|
||||||
|
|
||||||
void init_ncurses() {
|
enum editorKey
|
||||||
initscr(); // Startet den ncurses-Modus
|
{
|
||||||
raw(); // Direktes Erfassen der Eingaben (kein Zeilenpuffer)
|
ARROW_LEFT = 1000,
|
||||||
keypad(stdscr, TRUE); // Ermöglicht die Nutzung von Funktionstasten wie den Pfeiltasten
|
ARROW_RIGHT,
|
||||||
noecho(); // Verhindert, dass Eingaben direkt ausgegeben werden
|
ARROW_UP,
|
||||||
curs_set(1); // Cursor sichtbar machen
|
ARROW_DOWN,
|
||||||
|
|
||||||
|
DEL_KEY,
|
||||||
|
|
||||||
|
HOME_KEY,
|
||||||
|
END_KEY,
|
||||||
|
|
||||||
|
PAGE_UP,
|
||||||
|
PAGE_DOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
// DATA
|
||||||
|
|
||||||
|
typedef struct erow
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
char *chars;
|
||||||
|
} erow;
|
||||||
|
|
||||||
|
struct editorConfig
|
||||||
|
{
|
||||||
|
int cx, cy;
|
||||||
|
int rowoff;
|
||||||
|
int coloff;
|
||||||
|
int screenrows;
|
||||||
|
int screencols;
|
||||||
|
int numrows;
|
||||||
|
erow *row;
|
||||||
|
struct termios orig_termios;
|
||||||
|
};
|
||||||
|
struct editorConfig E;
|
||||||
|
|
||||||
|
// TERMINAL
|
||||||
|
|
||||||
|
void die(const char *s)
|
||||||
|
{
|
||||||
|
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||||
|
write(STDOUT_FILENO, "\x1b[H", 3);
|
||||||
|
perror(s);
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void shutdown_ncurses() {
|
void disableRawMode()
|
||||||
endwin(); // Beendet den ncurses-Modus
|
{
|
||||||
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &E.orig_termios) == -1)
|
||||||
|
die("tcsetattr");
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_file(const char *filename) {
|
void enableRawMode()
|
||||||
FILE *file = fopen(filename, "r");
|
{
|
||||||
if (file == NULL) {
|
if (tcgetattr(STDIN_FILENO, &E.orig_termios) == -1)
|
||||||
mvprintw(1, 0, "Fehler beim Öffnen der Datei %s.", filename);
|
die("tcgetattr");
|
||||||
return;
|
|
||||||
|
atexit(disableRawMode);
|
||||||
|
|
||||||
|
struct termios raw = E.orig_termios;
|
||||||
|
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||||
|
raw.c_oflag &= ~(OPOST);
|
||||||
|
raw.c_cflag |= (CS8);
|
||||||
|
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
|
||||||
|
|
||||||
|
raw.c_cc[VMIN] = 0;
|
||||||
|
raw.c_cc[VTIME] = 1;
|
||||||
|
|
||||||
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
|
||||||
|
die("tcsetattr");
|
||||||
|
}
|
||||||
|
|
||||||
|
int editorReadKey()
|
||||||
|
{
|
||||||
|
int nread;
|
||||||
|
char c;
|
||||||
|
while ((nread = read(STDIN_FILENO, &c, 1)) != 1)
|
||||||
|
{
|
||||||
|
if (nread == -1 && errno != EAGAIN)
|
||||||
|
die("read");
|
||||||
}
|
}
|
||||||
|
if (c == '\x1b')
|
||||||
int y = 1;
|
{
|
||||||
char line[256];
|
char seq[3];
|
||||||
while (fgets(line, sizeof(line), file)) {
|
if (read(STDIN_FILENO, &seq[0], 1) != 1)
|
||||||
mvprintw(y++, 0, "%s", line);
|
return '\x1b';
|
||||||
}
|
if (read(STDIN_FILENO, &seq[1], 1) != 1)
|
||||||
|
return '\x1b';
|
||||||
fclose(file);
|
if (seq[0] == '[')
|
||||||
}
|
{
|
||||||
|
if (seq[1] >= '0' && seq[1] <= '9')
|
||||||
void insert_char(int y, int x, char ch) {
|
{
|
||||||
int max_x = getmaxx(stdscr);
|
if (read(STDIN_FILENO, &seq[2], 1) != 1)
|
||||||
char line[max_x - x];
|
return '\x1b';
|
||||||
|
if (seq[2] == '~')
|
||||||
// Speichere den Rest der Zeile ab der aktuellen Position
|
{
|
||||||
for (int i = 0; i < max_x - x - 1; ++i) {
|
switch (seq[1])
|
||||||
line[i] = mvinch(y, x + i) & A_CHARTEXT;
|
{
|
||||||
}
|
case '1':
|
||||||
line[max_x - x - 1] = '\0';
|
return HOME_KEY;
|
||||||
|
case '4':
|
||||||
// Setze das neue Zeichen an der aktuellen Position
|
return END_KEY;
|
||||||
mvaddch(y, x, ch);
|
case '3':
|
||||||
|
return DEL_KEY;
|
||||||
// Schreibe den Rest der Zeile wieder zurück, um den Text nach rechts zu verschieben
|
case '5':
|
||||||
mvprintw(y, x + 1, "%s", line);
|
return PAGE_UP;
|
||||||
move(y, x + 1);
|
case '6':
|
||||||
}
|
return PAGE_DOWN;
|
||||||
|
case '7':
|
||||||
void editor_loop() {
|
return HOME_KEY;
|
||||||
int ch;
|
case '8':
|
||||||
while ((ch = getch()) != 27) { // Beenden mit ESC (ASCII 27)
|
return END_KEY;
|
||||||
int y = getcury(stdscr);
|
}
|
||||||
int x = getcurx(stdscr);
|
}
|
||||||
switch (ch) {
|
}
|
||||||
case KEY_UP:
|
else
|
||||||
// Cursor nach oben bewegen
|
{
|
||||||
move(y - 1, x);
|
switch (seq[1])
|
||||||
break;
|
{
|
||||||
case KEY_DOWN:
|
case 'A':
|
||||||
// Cursor nach unten bewegen
|
return ARROW_UP;
|
||||||
move(y + 1, x);
|
case 'B':
|
||||||
break;
|
return ARROW_DOWN;
|
||||||
case KEY_LEFT:
|
case 'C':
|
||||||
// Cursor nach links bewegen
|
return ARROW_RIGHT;
|
||||||
move(y, x - 1);
|
case 'D':
|
||||||
break;
|
return ARROW_LEFT;
|
||||||
case KEY_RIGHT:
|
case 'H':
|
||||||
// Cursor nach rechts bewegen
|
return HOME_KEY;
|
||||||
move(y, x + 1);
|
case 'F':
|
||||||
break;
|
return END_KEY;
|
||||||
case KEY_BACKSPACE:
|
}
|
||||||
case 127:
|
}
|
||||||
// Zeichen löschen (Rückschritt)
|
|
||||||
move(y, x - 1);
|
|
||||||
delch();
|
|
||||||
break;
|
|
||||||
case KEY_DC:
|
|
||||||
// Zeichen löschen (Entfernen)
|
|
||||||
delch();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Zeichen einfügen und Rest der Zeile nach rechts verschieben
|
|
||||||
insert_char(y, x, ch);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
refresh();
|
else if (seq[0] == 'O')
|
||||||
|
{
|
||||||
|
switch (seq[1])
|
||||||
|
{
|
||||||
|
case 'H':
|
||||||
|
return HOME_KEY;
|
||||||
|
case 'F':
|
||||||
|
return END_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '\x1b';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int getCursorPosition(int *rows, int *cols)
|
||||||
init_ncurses();
|
{
|
||||||
|
char buf[32];
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
mvprintw(0, 0, "Willkommen zu deinem Texteditor! Drücke ESC zum Beenden.");
|
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4)
|
||||||
load_file("test.txt");
|
return -1;
|
||||||
refresh();
|
|
||||||
|
|
||||||
editor_loop();
|
while (i < sizeof(buf) - 1)
|
||||||
|
{
|
||||||
|
if (read(STDIN_FILENO, &buf[i], 1) != 1)
|
||||||
|
break;
|
||||||
|
if (buf[i] == 'R')
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
buf[i] = '\0';
|
||||||
|
|
||||||
|
if (buf[0] != '\x1b' || buf[1] != '[')
|
||||||
|
return -1;
|
||||||
|
if (sscanf(&buf[2], "%d;%d", rows, cols) != 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
shutdown_ncurses();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getWindowSize(int *rows, int *cols)
|
||||||
|
{
|
||||||
|
struct winsize ws;
|
||||||
|
|
||||||
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0)
|
||||||
|
{
|
||||||
|
if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12)
|
||||||
|
return -1;
|
||||||
|
return getCursorPosition(rows, cols);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*cols = ws.ws_col;
|
||||||
|
*rows = ws.ws_row;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROW OPERATIONS
|
||||||
|
|
||||||
|
void editorAppendRow(char *s, size_t len)
|
||||||
|
{
|
||||||
|
E.row = realloc(E.row, sizeof(erow) * (E.numrows + 1));
|
||||||
|
int at = E.numrows;
|
||||||
|
E.row[at].size = len;
|
||||||
|
E.row[at].chars = malloc(len + 1);
|
||||||
|
memcpy(E.row[at].chars, s, len);
|
||||||
|
E.row[at].chars[len] = '\0';
|
||||||
|
E.numrows++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE I/O
|
||||||
|
|
||||||
|
void editorOpen(char *filename)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
if (!fp)
|
||||||
|
die("fopen");
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
size_t linecap = 0;
|
||||||
|
ssize_t linelen;
|
||||||
|
linelen = getline(&line, &linecap, fp);
|
||||||
|
while ((linelen = getline(&line, &linecap, fp)) != -1)
|
||||||
|
{
|
||||||
|
while (linelen > 0 && (line[linelen - 1] == '\n' ||
|
||||||
|
line[linelen - 1] == '\r'))
|
||||||
|
linelen--;
|
||||||
|
editorAppendRow(line, linelen);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// APPEND BUFFER
|
||||||
|
|
||||||
|
struct abuf
|
||||||
|
{
|
||||||
|
char *b;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ABUF_INIT {NULL, 0}
|
||||||
|
|
||||||
|
void abAppend(struct abuf *ab, const char *s, int len)
|
||||||
|
{
|
||||||
|
char *new = realloc(ab->b, ab->len + len);
|
||||||
|
if (new == NULL)
|
||||||
|
return;
|
||||||
|
memcpy(&new[ab->len], s, len);
|
||||||
|
ab->b = new;
|
||||||
|
ab->len += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void abFree(struct abuf *ab)
|
||||||
|
{
|
||||||
|
free(ab->b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// INPUT
|
||||||
|
|
||||||
|
void editorMoveCursor(int key)
|
||||||
|
{
|
||||||
|
erow *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case ARROW_LEFT:
|
||||||
|
if (E.cx != 0)
|
||||||
|
{
|
||||||
|
E.cx--;
|
||||||
|
}
|
||||||
|
else if (E.cy > 0)
|
||||||
|
{
|
||||||
|
E.cy--;
|
||||||
|
E.cx = E.row[E.cy].size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARROW_RIGHT:
|
||||||
|
if (row && E.cx < row->size)
|
||||||
|
{
|
||||||
|
E.cx++;
|
||||||
|
}
|
||||||
|
else if (row && E.cx == row->size)
|
||||||
|
{
|
||||||
|
E.cy++;
|
||||||
|
E.cx = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARROW_UP:
|
||||||
|
if (E.cy != 0)
|
||||||
|
{
|
||||||
|
E.cy--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARROW_DOWN:
|
||||||
|
if (E.cy < E.numrows)
|
||||||
|
{
|
||||||
|
E.cy++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
|
||||||
|
int rowlen = row ? row->size : 0;
|
||||||
|
if (E.cx > rowlen)
|
||||||
|
{
|
||||||
|
E.cx = rowlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorProcessKeypress()
|
||||||
|
{
|
||||||
|
int c = editorReadKey();
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case CTRL_KEY('q'):
|
||||||
|
write(STDOUT_FILENO, "\x1b[2J", 4);
|
||||||
|
write(STDOUT_FILENO, "\x1b[H", 3);
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HOME_KEY:
|
||||||
|
E.cx = 0;
|
||||||
|
break;
|
||||||
|
case END_KEY:
|
||||||
|
E.cx = E.screencols - 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PAGE_UP:
|
||||||
|
case PAGE_DOWN:
|
||||||
|
{
|
||||||
|
int times = E.screenrows;
|
||||||
|
while (times--)
|
||||||
|
editorMoveCursor(c == PAGE_UP ? ARROW_UP : ARROW_DOWN);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARROW_UP:
|
||||||
|
case ARROW_DOWN:
|
||||||
|
case ARROW_LEFT:
|
||||||
|
case ARROW_RIGHT:
|
||||||
|
editorMoveCursor(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OUTPUT
|
||||||
|
|
||||||
|
void editorScroll()
|
||||||
|
{
|
||||||
|
if (E.cy < E.rowoff)
|
||||||
|
{
|
||||||
|
E.rowoff = E.cy;
|
||||||
|
}
|
||||||
|
if (E.cy >= E.rowoff + E.screenrows)
|
||||||
|
{
|
||||||
|
E.rowoff = E.cy - E.screenrows + 1;
|
||||||
|
}
|
||||||
|
if (E.cx < E.coloff)
|
||||||
|
{
|
||||||
|
E.coloff = E.cx;
|
||||||
|
}
|
||||||
|
if (E.cx >= E.coloff + E.screencols)
|
||||||
|
{
|
||||||
|
E.coloff = E.cx - E.screencols + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorDrawRows(struct abuf *ab)
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
for (y = 0; y < E.screenrows; y++)
|
||||||
|
{
|
||||||
|
int filerow = y + E.rowoff;
|
||||||
|
if (filerow >= E.numrows)
|
||||||
|
{
|
||||||
|
if (E.numrows == 0 && y == E.screenrows / 3)
|
||||||
|
{
|
||||||
|
char welcome[80];
|
||||||
|
int welcomelen = snprintf(welcome, sizeof(welcome),
|
||||||
|
"Thank you for using text editor -- version %s", EDITOR_VERSION);
|
||||||
|
if (welcomelen > E.screencols)
|
||||||
|
welcomelen = E.screencols;
|
||||||
|
|
||||||
|
int padding = (E.screencols - welcomelen) / 2;
|
||||||
|
if (padding)
|
||||||
|
{
|
||||||
|
char buffer[32];
|
||||||
|
int len = snprintf(buffer, sizeof(buffer), "%d", y + 1);
|
||||||
|
|
||||||
|
abAppend(ab, buffer, len);
|
||||||
|
padding--;
|
||||||
|
}
|
||||||
|
while (padding--)
|
||||||
|
abAppend(ab, " ", 1);
|
||||||
|
abAppend(ab, welcome, welcomelen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char buffer[32];
|
||||||
|
int len = snprintf(buffer, sizeof(buffer), "%d", y + 1);
|
||||||
|
|
||||||
|
abAppend(ab, buffer, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = E.row[filerow].size - E.coloff;
|
||||||
|
if (len < 0)
|
||||||
|
len = 0;
|
||||||
|
if (len > E.screencols)
|
||||||
|
len = E.screencols;
|
||||||
|
abAppend(ab, &E.row[filerow].chars[E.coloff], len);
|
||||||
|
}
|
||||||
|
|
||||||
|
abAppend(ab, "\x1b[K", 3);
|
||||||
|
if (y < E.screenrows - 1)
|
||||||
|
{
|
||||||
|
abAppend(ab, "\r\n", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void editorRefreshScreen()
|
||||||
|
{
|
||||||
|
editorScroll();
|
||||||
|
|
||||||
|
struct abuf ab = ABUF_INIT;
|
||||||
|
|
||||||
|
abAppend(&ab, "\x1b[?25l", 6);
|
||||||
|
abAppend(&ab, "\x1b[H", 3);
|
||||||
|
|
||||||
|
editorDrawRows(&ab);
|
||||||
|
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", (E.cy - E.rowoff) + 1, (E.cx - E.coloff) + 1);
|
||||||
|
abAppend(&ab, buf, strlen(buf));
|
||||||
|
|
||||||
|
abAppend(&ab, "\x1b[?25h", 6);
|
||||||
|
|
||||||
|
write(STDOUT_FILENO, ab.b, ab.len);
|
||||||
|
abFree(&ab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// INIT
|
||||||
|
|
||||||
|
void initEditor()
|
||||||
|
{
|
||||||
|
E.cx = 0;
|
||||||
|
E.cy = 0;
|
||||||
|
E.rowoff = 0;
|
||||||
|
E.coloff = 0;
|
||||||
|
E.numrows = 0;
|
||||||
|
E.row = NULL;
|
||||||
|
|
||||||
|
if (getWindowSize(&E.screenrows, &E.screencols) == -1)
|
||||||
|
die("getWindowSize");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
enableRawMode();
|
||||||
|
initEditor();
|
||||||
|
if (argc >= 2)
|
||||||
|
{
|
||||||
|
editorOpen(argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
editorRefreshScreen();
|
||||||
|
editorProcessKeypress();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
13
test.txt
13
test.txt
@@ -2,4 +2,15 @@ Hallo dies ist ein Test.
|
|||||||
|
|
||||||
123
|
123
|
||||||
|
|
||||||
Ich war hier.
|
Ich war hier.
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if(i == 0){
|
||||||
|
printf("hallo");
|
||||||
|
}
|
||||||
|
|
||||||
|
Passt es?
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||||
|
Hallo
|
||||||
Reference in New Issue
Block a user