/* custom keyboard input routines without ncurses */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <keycodes.h>


static struct termios old_tty, tty;

static int chk_tty(void)
{
   tcgetattr(STDIN_FILENO, &tty);

   if ((tty.c_lflag & ECHO) || (tty.c_lflag & ICANON)) {
       old_tty = tty;
       tty.c_lflag &= ~(ECHO | ICANON);
       tcsetattr(STDIN_FILENO, TCSANOW, &tty);
       return 1;
   }
   return 0;
}

static void restore_tty(void)
{
   tcsetattr(STDIN_FILENO, TCSANOW, &old_tty);
}

static void do_char(int c, char *temp, int mk_upper, int *pos, int *len,
       int maxlen, int insert)
{
   int i;

   if (mk_upper)
       c = toupper(c);

   if ((c == 0) || ((*pos) == maxlen))
       return;
   if ((!(insert)) || ((*pos) == (*len))) {
       temp[(*pos)++] = (char)c;
       if ((*pos) > (*len)) (*len)++;
           putchar(c);
   } else {
       if ((*len) == maxlen)
           return;

       for (i = (*len)++; i >= (*pos); i--)
           temp[i + 1] = temp[i];
       temp[(*pos)++] = (char)c;
       putchar(c);
       for (i = (*pos); i < (*len); i++)
           putchar(temp[i]);
       for (i = (*len); i > (*pos); i--)
           putchar(K_BACKSPACE);
   }
}

int edgets(char *s, int maxlen, int mk_upper)
{
   char *temp;
   int insert = 1, done = 0, pos, len, i, c, tty_changed;

   tty_changed = chk_tty();

   printf("%s", s);

   temp = malloc(maxlen + 1);
   strcpy(temp, s);

   pos = strlen(s);
   len = strlen(s);

   while (! done) {
       c = getchar();

       switch (c) {
           case K_CTL_U:       /* Kill backward from point to */
               if (len == 0)   /* beginning of line */
                   break;

               for (i = pos; i < len; i++)
                   putchar(' ');
               for (i = len-1; i >= 0; i--) {
                   putchar(K_BACKSPACE);
                   putchar(' ');
                   putchar(K_BACKSPACE);
               }
               pos = len = 0;
               break;
           case K_CTL_B:       /* Move back a character */
               if (pos == 0)
                   break;
               pos--;
               putchar(K_BACKSPACE);
               break;
           case K_CTL_F:       /* Move forward a character */
               if (pos == len)
                   break;
               if (pos != maxlen) {
                   putchar(temp[pos]);
                   pos++;
               }
               break;
           case K_CTL_A:       /* Move to the start of line */
               while (pos-- > 0)
                   putchar(K_BACKSPACE);
               pos = 0;
               break;
           case K_CTL_E:       /* Move to the end of line */
               while (pos < len)
                   putchar(temp[pos++]);
               break;
           case K_CTL_D:       /* Delete the character under */
               if (pos == len) /* the cursor */
                   break;
               for (i = pos; i < len; i++)
                   temp[i] = temp[i + 1];
               len--;
               for (i = pos; i < len; i++)
                   putchar(temp[i]);
               putchar(' ');
               for (i = len + 1; i > pos; i--)
                   putchar(K_BACKSPACE);
               break;
           case K_BACKSPACE:   /* Backward delete character */
           case 127:
               if (pos == 0)
                   break;
               if (pos != len) {
                   for (i = pos - 1; i < len; i++)
                       temp[i] = temp[i + 1];
                   pos--;
                   len--;
                   putchar(K_BACKSPACE);
                   for (i = pos; i < len; i++)
                       putchar(temp[i]);
                   putchar(' ');
                   for (i = len; i >= pos; i--)
                       putchar(K_BACKSPACE);
               } else {
                   putchar(K_BACKSPACE);
                   putchar(' ');
                   putchar(K_BACKSPACE);
                   pos = --len;
               }
               break;
           case K_LINEFEED:    /* We're done! */
               putchar(K_LINEFEED);
               done = 1;
               break;
       case K_CTL_K:           /* Kill from the cursor to end of line */
               for (i = pos; i < len; ++i)
                   putchar(' ');
               for (i = pos; i < len; ++i)
                   putchar(K_BACKSPACE);
               len = pos;
               break;
#if 0
           case K_CTL_F:       /* Move forward one word */
               do {
                   if (pos == len)
                       break;
                   if (pos != maxlen) {
                       putchar(temp[pos]);
                       pos++;
                   }
               } while (isspace(temp[pos]));
               do {
                   if (pos == len)
                       break;
                   if (pos != maxlen) {
                       putchar(temp[pos]);
                       pos++;
                   }
               } while (!isspace(temp[pos]));
               break;
           case K_CTL_B:       /* Move backward one word */
               do {
                   if (pos == 0)
                       break;
                   pos--;
                   putchar(K_BACKSPACE);
               } while (isspace(temp[pos]));
               do {
                   if (pos == 0)
                       break;
                   pos--;
                   putchar(K_BACKSPACE);
               } while (!isspace(temp[pos]));
               break;
#endif
           default :
               if(c >= 0x20)
                   do_char(c, temp, mk_upper, &pos, &len, maxlen, insert);
       }
   }

   temp[len] = '\0';
   strcpy(s, temp);
   free(temp);
   if(tty_changed)
       restore_tty();
   return len;
}

int yesno(void)
{
   int c, tty_changed;

   tty_changed = chk_tty();

   while (1) {
       c = toupper(getchar());
       if ((c == 'Y') || (c == 'N')) break;
   }

   if (tty_changed)
       restore_tty();

   if (c == 'Y')
       return 1;
   else
       return 0;
}