/* v06.c
 * Visual Editor, Dateibetrachter
 * Autor: Andre Adrian
 * Version: 01Feb2011 fuer CP/M, MS-DOS, MS-Windows, Linux
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef CPM
#	include <conio.h>
#	define system(s)
#endif
#if defined(_MSC_VER) || defined(__TURBOC__)
#	include <conio.h>
#	define putch(c) putchar(c)
#	define cputs(s) fputs(s,stdout)
#	define system(s)
#endif
#ifdef linux
#	define putch(c) putchar(c)
#	define cputs(s) fputs(s,stdout)
#	define getch()  getchar()
#endif

#define SCRW	80		/* Bildschirm Breite */
#define SCRH	25		/* Bildschirm Hoehe */

/* Zeichen von Tastatur */
#define ETX	('C'-'@')	/* Strg-C */
#define CUUin	('W'-'@')
#define CUDin	('X'-'@')
#define CUFin	('D'-'@')
#define CUBin	('A'-'@')

/* ANSI Escape Sequenzen an Terminal(-Emulator) */
#define CUUout	"\033[A"
#define CUDout	"\033[B"
#define CUFout	"\033[C"
#define CUBout	"\033[D"
#define EL	"\033[K"	/* erase to end of line */
#define ED	"\033[J"	/* erase to end of screen */

char *text;		/* enthaelt Editortext */
unsigned int tsize;	/* Groesse von text */
char line[128];		/* enthaelt aktuelle Zeile */
int scrx, scry;		/* Screen Offset to text */
int curx, cury;		/* Cursorposition auf Bildschirm */

/* ANSI Escape Sequenz Cursor Position ausgeben */
void setCursor(int y, int x) {
	static char cup[] = "\033[01;01H";
	++y;			/* Terminal beginnt mit Zeile 1 */
	cup[2] = '0' + y / 10;	/* 10er Stelle Zeile eintragen */
	cup[3] = '0' + y % 10;	/* 1er Stelle Zeile eintragen */
	++x;
	cup[5] = '0' + x / 10;
	cup[6] = '0' + x % 10;
	cputs(cup);
}

/* Integer nach hexadezimale ASCII Zahl wandeln und ausgeben */
void hexputs(int i) {
	static char hex[] = "<00>";
	hex[1] = '0' + i / 16;
	if (hex[1] > '9') hex[1] += ('A'-'9'-1);
	hex[2] = '0' + i % 16;
	if (hex[2] > '9') hex[2] += ('A'-'9'-1);
	cputs(hex);
}

/* Zeige Dateiinhalt ab Zeile scry und Spalte scrx */
void show() {
	static char *ptr;
	static int x, y;
	/* Anfang der Zeile scry in text suchen */
	for (y = scry, ptr = text; y != 0; ++ptr) {
		if (0 == *ptr) goto fehler;
		if ('\n' == *ptr) --y;
	}
	/* Ausgabe Dateiausschnitt auf Bildschirm */
	setCursor(0, 0);
	for(y = SCRH ;;) {
		cputs(EL);
		for (x = 0; *ptr != '\n'; ++x, ++ptr) {
			if (0 == *ptr) goto fehler; 
			if (x >= scrx && x < scrx+SCRW) putch(*ptr);
		}
		++ptr;	/* zeige hinter \n */		
		if (0 == --y) break;	/* Kein \n in letzer Zeile */
		cputs("\n");		/* CONIO fuegt \r ein */
	}
	setCursor(cury, curx);
	return;
fehler:
	cputs("show() failed\n");
	setCursor(cury, curx);
}

int main(int argc, char *argv[]) {
	static int c;
	static FILE *fp;
	if (argc != 2) {
		cputs("usage: v filename\n");
		exit(1);
	}
	/* HI-TECH-C unter CP/M: Zuerst fopen(), dann malloc(),
	   sonst ist fopen() nicht erfolgreich */
	fp = fopen(argv[1], "r");
	
	for (tsize = 64512; tsize >= 2048; tsize -= 1024) {
		text = (char *)malloc(tsize);
		if (text != NULL) break;
	}
	if (NULL == text) exit(1);
	text[0] = 0;	/* Textspeicher initialisieren */

	if (fp != NULL) {
		while (fgets(line, sizeof(line)-1, fp) != NULL) {
			unsigned len;
			len = strlen(line);
			if (line[len-1] != '\n') {
				cputs("split long line\n");
				strcat(line, "\n");
			}
			strcat(text, line);
			if (strlen(text) + 1024 > tsize) {
				cputs("can't load complete file\n");
				break;
			}
		}
		fclose(fp);
	} else {
		cputs("can't open file\n");
	}

	system("stty -icanon -echo");
	curx = cury = 0;
	scrx = 0; scry = 0;
	setCursor(cury, curx);
	cputs(ED);
	show();

	do {
		c = getch();
		switch (c) {
		case CUUin:
			if (0 == cury) {
				if (scry > 0) {
					--scry;
					show();
				}
			} else {
				--cury;
				cputs(CUUout);
			}
			break;
		case CUDin:
			if (SCRH-1 == cury) {
				/* hier fehlt noch ein Vergleich */
				++scry;
				show();
			} else {
				++cury;
				cputs(CUDout);
			}
			break;
		case CUFin:
			if (SCRW-1 == curx) {
				if (scrx+SCRW < sizeof(line)) {
					++scrx;
					show();
				}
			} else {
				++curx;
				cputs(CUFout);
			}
			break;
		case CUBin:
			if (0 == curx) {
				if (scrx > 0) {
					--scrx;
					show();
				}
			} else {
				--curx;
				cputs(CUBout);
			}
			break;
		default:
			if (c >= 32 && c <= 126) {
				putch(c);
			} else {
				hexputs(c);
			}
			break;
		}
	} while (c != ETX);
	system("stty sane");
	return 0;
}
