Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
// buffer is an area for the characters to be stored
// max is how big the field is
// init is optional initial value for field
// no 0 will placed after the returned characters
// if + return value is the number of chars returned
// if 0 is returned, user provides no input
// if - user aborted input by typing his intr or quit character
// if user types his kill character the field is reset to init value
int get_field(char *buffer, int max, char *init) {
struct termios orig, now;
int col, save_col, i, count, done, abort;
char *pinit;
char rdbuff[10], *prdbuff;
char *dbuff, *pdbuff;
char *bsbuff1, *pbsbuff1;
char *bsbuff2, *pbsbuff2;
char dummy[]="";
// If we get a null pointer for init, replace it with a valid pointer to an empty string.
if(!init)init=dummy;
// Let's say our field is 10 characters long. We then grab 30 consecutive bytes.
// Bytes 0 - 9 are bsbuff1 (backspace buffer) and are filled with backspace chars
// Bytes 10 - 19 are dbuff (display buffer) and reflect what is on the screen
// Bytes 20 - 29 are bsbuff2 (backspace buffer) and are filled with backspace chars
// So by carefully picking a starting byte and a length, we can backspace over what
// we have displayed, overwrite the display, and position the cursor in one write
// system call.
// These are not strings...no zero terminator is present.
bsbuff1 = (char *) malloc(max*3);
dbuff = bsbuff1+max;
bsbuff2 = dbuff+max;
col=0;
for(i=0, pdbuff=dbuff, pbsbuff1=bsbuff1, pbsbuff2=bsbuff2, pinit=init; i<max; i++,pdbuff++) {
*pbsbuff1++ = '\b';
*pbsbuff2++ = '\b';
if (*pinit) {
*pdbuff=*pinit++;
col++;
} else {
*pdbuff=' ';
}
}
fflush(stdout);
write(1,dbuff,2*max-col);
save_col=col;
// Set up tty
tcgetattr(0,&orig);
now=orig;
now.c_lflag &= ~(ISIG|ICANON|ECHO);
now.c_cc[VMIN]=10;
now.c_cc[VTIME]=2;
tcsetattr(0,TCSANOW, &now);
// Loop to read characters. We read 10 chars at a time because
// function keys return a sequence of characters and we want to ignore them all.
abort=0;
done=0;
while(!done) {
count = read(0,rdbuff,10);
for(i=0, prdbuff=rdbuff; i<count && !done; prdbuff++,i++) {
if(*prdbuff == now.c_cc[VERASE]) {
if(col) {
col--;
write(1,"\b \b",3);
} else {
write(1,"\a",1);
}
} else if(*prdbuff == now.c_cc[VKILL]) {
for(i=0, pdbuff=dbuff, pinit=init; i<max; i++,pdbuff++) {
if (*pinit) {
*pdbuff=*pinit++;
} else {
*pdbuff=' ';
}
}
write(1,bsbuff1+max-col,2*max+col-save_col);
col=save_col;
} else if (*prdbuff == '\033') {
write(1,"\a",1);
i=count;
} else if (*prdbuff == '\n') {
done=1;
} else if (*prdbuff == '\r') {
done=1;
} else if(*prdbuff == now.c_cc[VINTR]) {
done=1;
abort=1;
} else if(*prdbuff == now.c_cc[VQUIT]) {
done=1;
abort=1;
} else {
write(1,prdbuff,1);
dbuff[col++]=*prdbuff;
}
}
}
// Prepare for return
tcsetattr(0,TCSANOW, &orig);
free(bsbuff1);
// Return to caller
if(abort) {
return -1;
} else {
for(i=0; i<col; i++) {
buffer[i]=dbuff[i];
}
return col;
}
return 0;
}
int main(int argc, char *argv[]) {
char ibuf[80];
int iret;
fputs("generic prompt -", stdout);
if ((iret = get_field(ibuf, 10, "123" )) > 0) {
ibuf[iret]=0;
printf("\n ibuf is now \"%s\" %d\n", ibuf, iret);
} else if (iret == 0) {
printf("\n no input \n");
} else {
printf("\n input was aborted\n");
}
exit(0);
}