//Deian Stefan
//noncononical io
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

#define BUF_SIZ  2048
#define MIN(a,b) ((a<=b)?a:b)

// buffer used by read_line
char __buf[BUF_SIZ];
int __buf_start=0;
int __buf_end=0;
// ------------------------

int erase(int *pos,char *buf, int buf_siz) {
	char *c_erase="\b \b";
	if((*pos)>0) {
		(*pos)--;
		write(0,c_erase,3);
		return 0;
	}
	return -1;
}

//read_line emulates the buffering of read, meaning
//if the user input > buf_siz read_line returns
//the first buf_siz bytes and then on the next call
//returns next remaining,etc.
int read_line(int fd, char *buf, int buf_siz) {
	int i;
	char c;
	struct termios t_orig,t;

	if(__buf_start||__buf_end) {
			goto fill_buf; //buffer is not empty
	}

	if(ioctl(fd,TCGETS,&t_orig)<0) {
		fprintf(stderr,"TCGETS failed: %s\n",strerror(errno));
		return -1;
	}
	if(ioctl(fd,TCGETS,&t)<0) {
		fprintf(stderr,"TCGETS failed: %s\n",strerror(errno));
		return -1;
	}
	t.c_lflag&=~ICANON; //noncononical
	t.c_lflag&=~ECHO;   //disable echo
	t.c_cc[VTIME]=0;
	t.c_cc[VMIN]=1;

	if(ioctl(fd,TCSETS,&t)<0) {
		fprintf(stderr,"TCSETS failed: %s\n",strerror(errno));
		return -1;
	}

	while(read(fd,&c,1)==1) {
		if(c==t.c_cc[VERASE]) {
			erase(&__buf_end,__buf,BUF_SIZ);
		} else if(c==t.c_cc[VWERASE]) {
			while(__buf_end>0&&__buf[__buf_end-1]==' ') {
				erase(&__buf_end,buf,buf_siz);
			}
			while(__buf_end>0&&__buf[__buf_end-1]!=' ') {
				erase(&__buf_end,buf,buf_siz);
			}
			while(__buf_end>0&&__buf[__buf_end-1]==' ') {
				erase(&__buf_end,buf,buf_siz);
			}
			if(__buf_end!=0) {
				c=' ';
				write(0,&c,1);
				__buf[__buf_end++]=c;
			}
		} else if(c==t.c_cc[VKILL]) {
			while(__buf_end>0) {erase(&__buf_end,__buf,BUF_SIZ);}
		} else if(c==t.c_cc[VEOF]) { 
			break;
		} else if(__buf_end<BUF_SIZ) {
			write(0,&c,1);
			__buf[__buf_end++]=c;
			if(c=='\n') { break; }
		}
	}

	if(ioctl(fd,TCSETS,&t_orig)<0) {
		fprintf(stderr,"TCSETS failed: %s\n",strerror(errno));
		return -1;
	}

fill_buf:

	for(i=0;i<MIN(buf_siz,__buf_end-__buf_start);i++) {
		buf[i]=__buf[i+__buf_start];
	}
	__buf_start+=i;
	if(__buf_start>=__buf_end) { __buf_end=__buf_start=0; }

	return i;
}

int main() {
	char buf[11];
	int rn=0;

	while((rn=read_line(0,buf,sizeof(buf)-1))>0) {
		buf[rn]='\0';
		fprintf(stderr,"Read %d: '%s'\n",rn,buf);
	}

	return 0;
}
/* SAMEPLE RUN:
 * *******************************
 * $./test 
 * 0123456789
 * Read 10: '0123456789'
 * Read 1: '
 * '
 * im in ur base killing ur d00dz
 * Read 10: 'im in ur b'
 * Read 10: 'ase killin'
 * Read 10: 'g ur d00dz'
 * Read 1: '
 * '
 * no!Read 3: 'no!'
 * yes?
 * Read 5: 'yes?
 * '
 * okRead 2: 'ok'
 * $
 * *******************************
 */

