// Line Input with editing

// It would be possible to check the variable insert, and if it's on, make
// the cursor large, and if it's off, make the cursor small, but my primary
// goal is portability, not fancy add-ons

#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define ESC     0x1B
#define LEFT    0x4B
#define RIGHT   0x4D
#define HOME    0x47
#define END     0x4F
#define INSERT  0x52
#define DELETE  0x53
#define BACKSPC 0x08
#define ENTER   0x0D
#define CTLEND  0x75
#define CTLHOME 0x77
#define CTLRT   0x74
#define CTLLFT  0x73

//
//  Password mode - '*' is echoed for all characters, only ESC, ENTER,
//  BACKSPC, and CTLHOME are active.
//

int password_mode = 0;

int jgets(char *s, int maxlen)
{
      char temp[500];
      int insert = 1, done = 0, pos = 0, len = 0, i, j, c, zeroflag;

      for (i = 0; i < maxlen; ++i)
            putch('²');
      for (i = 0; i < maxlen; ++i)
            putch(BACKSPC);
      while (!done)
      {
            zeroflag = 0;
            if ((c = getch()) == 0)
            {
                  zeroflag = 1;
                  c = getch();
            }
            switch (c)
            {
            case ESC :
                  if (len == 0)
                        break;
                  if (pos != len)
                  {
                        for (i = pos; i < len; i++)
                              putch('²');
                        for (i = len; i >= 0; i--)
                        {
                              putch(BACKSPC);
                              putch('²');
                              putch(BACKSPC);
                        }
                        pos = len = 0;
                        break;
                  }

            case LEFT :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        if (pos == 0)
                              break;
                        pos--;
                        putch(BACKSPC);
                        break;
                  }

            case RIGHT :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        if (pos == len)
                              break;
                        if (pos != maxlen)
                        {
                              putch(temp[pos]);
                              pos++;
                        }
                        break;
                  }

            case HOME :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        while (pos-- > 0)
                              putch(BACKSPC);
                        pos = 0;
                        break;
                  }

            case END :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        while (pos < len)
                              putch(temp[pos++]);
                        break;
                  }

            case INSERT :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        insert = (!(insert));
                        break;
                  }

            case DELETE :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        if (pos == len)
                              break;
                        for (i = pos; i < len; i++)
                              temp[i] = temp[i + 1];
                        len--;
                        for (i = pos; i < len; i++)
                              putch(temp[i]);
                        putch('²');
                        for (i = len + 1; i > pos; i--)
                              putch(BACKSPC);
                        break;
                  }

            case BACKSPC :
                  if (c == BACKSPC)
                  {
                        if (pos == 0)
                              break;
                        if (pos != len)
                        {
                              for (i = pos - 1; i < len; i++)
                                    temp[i] = temp[i + 1];
                              pos--;
                              len--;
                              putch(BACKSPC);
                              for (i = pos; i < len; i++)
                                    putch(temp[i]);
                              putch('²');
                              for (i = len; i >= pos; i--)
                                    putch(BACKSPC);
                        }
                        else
                        {
                              putch(BACKSPC);
                              putch('²');
                              putch(BACKSPC);
                              pos = --len;
                        }
                        break;
                  }

            case ENTER :
                  if (c == ENTER)
                  {
                        done = 1;
                        break;
                  }

            case CTLEND :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        for (i = pos; i < len; ++i)
                              putch('²');
                        for (i = pos; i < len; ++i)
                              putch(BACKSPC);
                        len = pos;
                        break;
                  }

            case CTLHOME :
                  if (zeroflag)
                  {
                        if (pos == 0)
                              break;
                        if (pos != len)
                        {
                              while (0 != pos)
                              {
                                    for (i = pos - 1; i < len; i++)
                                          temp[i] = temp[i + 1];
                                    pos--;
                                    len--;
                                    putch(BACKSPC);
                                    for (i = pos; i < len; i++)
                                          putch(temp[i]);
                                    putch('²');
                                    for (i = len; i >= pos; i--)
                                          putch(BACKSPC);
                              }
                        }
                        else
                        {
                              while (0 != pos)
                              {
                                    putch(BACKSPC);
                                    putch('²');
                                    putch(BACKSPC);
                                    pos = --len;
                              }
                        }
                        break;
                  }

            case CTLRT :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        do
                        {
                              if (pos == len)
                                    break;
                              if (pos != maxlen)
                              {
                                    putch(temp[pos]);
                                    pos++;
                              }
                        } while (isspace(temp[pos]));
                        do
                        {
                              if (pos == len)
                                    break;
                              if (pos != maxlen)
                              {
                                    putch(temp[pos]);
                                    pos++;
                              }
                        } while (!isspace(temp[pos]));
                        break;
                  }

            case CTLLFT :
                  if (zeroflag)
                  {
                        if (password_mode)
                              break;
                        do
                        {
                              if (pos == 0)
                                    break;
                              pos--;
                              putch(BACKSPC);
                        } while (isspace(temp[pos]));
                        do
                        {
                              if (pos == 0)
                                    break;
                              pos--;
                              putch(BACKSPC);
                        } while (!isspace(temp[pos]));
                        break;
                  }

            default :
                  if (zeroflag)
                        break;
                  if (c == 0 || pos == maxlen)
                        break;
                  if ((!(insert)) || pos == len)
                  {
                        temp[pos++] = (char)c;
                        if (pos > len) len++;
                        if (password_mode)
                              putch('*');
                        else  putch(c);
                  }
                  else
                  {
                        for (i = len++; i >= pos; i--)
                              temp[i + 1] = temp[i];
                        temp[pos++] = (char)c;
                        if (password_mode)
                              putch('*');
                        else  putch(c);
                        for (i = pos; i < len; i++)
                              putch(temp[i]);
                        for (i = len; i > pos; i--)
                              putch(BACKSPC);
                  }
            }
      }
      temp[len] = '\0';
      strcpy(s, temp);
      return len;
}
