about summary refs log blame commit diff stats
path: root/src/LYStrings.c
blob: f5c88ffdded8109e910861f37f119561fc6942b4 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                         

                      




                    




                                           





































































                                                                               


















                                                            





































                                                                      






                                 










                                                              


                          



                                                                         
                













                                                                       
 










































                                                                         































                                                                              











                                                                    

         


























                                                                              
                
                                       
                     

































                                                                          

























                                                                             




























                                                           





                                        



































                                                                     





                                                                            







                                                                         
     

               

































































                                                                              
 


                            
               








                                                    



















                                                                              


                                  

         
                                 
 






                                                                          













































































































































































































































































































































                                                                               



































































































                                                                             
                                              








































































































                                                                           


















































































































































                                                                           








































                                                                   
#include "HTUtils.h"
#include "tcp.h"
#include "HTCJK.h"
#include "LYCurses.h"
#include "LYUtils.h"
#include "LYStrings.h"
#include "LYGlobalDefs.h"
#include "GridText.h"
#include "LYKeymap.h"
#include "LYSignal.h"
#include "LYClean.h"
#include "LYMail.h"
#include "LYOptions.h"

#include <ctype.h>

#include "LYLeaks.h"

#define FREE(x) if (x) {free(x); x = NULL;}

extern BOOL HTPassHighCtrlRaw;
extern HTCJKlang HTCJK;

/* If you want to add mouse support for some new platform, it's fairly
** simple to do.  Once you've determined the X and Y coordinates of
** the mouse event, loop through the elements in the links[] array and
** see if the coordinates fall within a highlighted link area.  If so,
** the code must set mouse_link to the index of the chosen link,
** and return a key value that corresponds to LYK_ACTIVATE.  The
** LYK_ACTIVATE code in LYMainLoop.c will then check mouse_link
** and activate that link.  If the mouse event didn't fall within a
** link, the code should just set mouse_link to -1 and return -1. --AMK
**/

/* The number of the link selected w/ the mouse (-1 if none) */
static int mouse_link = -1;   

/* Return the value of mouse_link, erasing it */
PUBLIC int get_mouse_link NOARGS
{
  int t; 
  t=mouse_link; 
  mouse_link = -1;
  return t;
}

/* Given X and Y coordinates of a mouse event, set mouse_link to the
** index of the corresponding hyperlink, or set mouse_link to -1 if no
** link matches the event.  Returns -1 if no link matched the click,
** or a keycode that must be returned from LYgetch() to activate the
** link. 
**/

PRIVATE int set_clicked_link ARGS2(int,x,int,y)
{
  int i;

  /* Loop over the links and see if we can get a match */
  for(i=0; i < nlinks && mouse_link == -1; i++) {
    /* Check the first line of the link */
    if ( links[i].hightext != NULL && 
	 links[i].ly == y && 
	 (x - links[i].lx) < strlen(links[i].hightext ) ) {
      mouse_link=i;
    }
    /* Check the second line */
    if (links[i].hightext2 != NULL &&
	1+links[i].ly == y &&
	(x - links[i].hightext2_offset) < strlen(links[i].hightext2) ) {
      mouse_link=i;
    }
  }
  /* If no link was found, just return a do-nothing code */
  if (mouse_link == -1) return -1;

  /* If a link was hit, we must look for a key which will activate LYK_ACTIVATE
  ** XXX The 127 in the following line will depend on the size of the keymap[]
  ** array.  However, usually we'll find LYK_ACTIVATE somewhere in the first
  ** 127 keys (it's usually mapped to the Enter key) 
  **/
  for (i=0; i<127; i++) {	 
    if (LYisNonAlnumKeyname(i, LYK_ACTIVATE)) {
      return i;
    }
  }
    /* Whoops!  Nothing's defined as LYK_ACTIVATE!
       Well, who are we to argue with the user? 
       Forget about the mouse click */
  mouse_link = -1;
  return -1;
}


/*
 * LYstrncpy() terminates strings with a null byte.
 * Writes a null byte into the n+1 byte of dst.
 */
PUBLIC char * LYstrncpy ARGS3(char *,dst, char *,src, int,n)
{
    char *val;
    int len=strlen(src);

    if (n < 0)
        n = 0;

    val = strncpy(dst, src, n);
    if (len < n)
        *(dst+len) = '\0';
    else
        *(dst+n) = '\0';
    return val;
}
#ifdef EXP_CHARTRANS
/*
 * LYmbcsstrncpy() terminates strings with a null byte.
 * Takes acount of multibyte characters.
 * src string is copied until either end of string or max number of
 * either bytes or glyphs (mbcs sequences) (currently only UTF8).
 */
PUBLIC char * LYmbcsstrncpy ARGS5(char *,dst, char *,src, int,n_bytes,
				  int,n_glyphs,	int,enc)
{
    char *val = dst;
    int i_bytes = 0, i_glyphs = 0;

    if (n_bytes < 0)
        n_bytes = 0;
    if (n_glyphs < 0)
        n_glyphs = 0;

#define IS_NEW_GLYPH(ch) (enc && ((unsigned char)(ch)&0xc0) != 0x80)

    for (; *src != '\0' && i_bytes < n_bytes;
	 i_bytes++) {
	if (IS_NEW_GLYPH(*src))
	    if (i_glyphs++ >= n_glyphs) {
		*dst = '\0';
		return val;
	    }
	*(dst++) = *(src++);
    }
    *dst = '\0';

    return val;
}
#endif /* EXP_CHARTRANS */

#undef GetChar

#undef GetChar

#ifdef USE_SLANG
#ifdef VMS
#define GetChar() ttgetc()
#else
#define GetChar (int)SLang_getkey
#endif /* VMS */
#endif /* USE_SLANG */

#if !defined(GetChar) && defined(NCURSES)
#define GetChar() wgetch(my_subwindow ? my_subwindow : stdscr)
#endif

#if !defined(GetChar) && defined(SNAKE)
#define GetChar() wgetch(stdscr)
#endif

#if !defined(GetChar)
#ifdef VMS
#define GetChar() ttgetc()
#else
#ifndef USE_GETCHAR
#define USE_GETCHAR
#endif /* !USE_GETCHAR */
#define GetChar() getchar()  /* used to be "getc(stdin)" and "getch()" */
#endif /* VMS */
#endif /* !defined(GetChar) */

#if defined(NCURSES)
/*
 * Workaround a bug in ncurses order-of-refresh by setting a pointer to
 * the topmost window that should be displayed.
 */
PRIVATE WINDOW *my_subwindow;

PUBLIC void LYsubwindow ARGS1(WINDOW *, param)
{
	my_subwindow = param;
}
#endif

#ifdef USE_SLANG_MOUSE
PRIVATE int sl_parse_mouse_event ARGS3(int *, x, int *, y, int *, button)
{
   /* "ESC [ M" has already been processed.  There more characters are 
    * expected:  BUTTON X Y
    */
   *button = SLang_getkey ();
   switch (*button)
     {
      case 040:			       /* left button */
      case 041:			       /* middle button */
      case 042:			       /* right button */
	*button -= 040;
	break;

      default:			       /* Hmmm.... */
	SLang_flush_input ();
	return -1;
     }

   *x = SLang_getkey () - 33;
   *y = SLang_getkey () - 33;
   return 0;
}
#endif

#ifdef USE_SLANG_MOUSE
PRIVATE int map_function_to_key ARGS1(char, keysym)
{
   int i;
   
   /* I would prefer to use sizeof keymap but its size is not available.
    * A better method would be to declare it as some fixed size.
    */
   for (i = 1; i < 256; i++)
     {
	if (keymap[i] == keysym)
	  return i - 1;
     }
   return -1;
}
#endif

/*
 * LYgetch() translates some escape sequences and may fake noecho
 */
PUBLIC int LYgetch ()
{
    int a, b, c, d = -1;

#if defined(IGNORE_CTRL_C) || defined(USE_GETCHAR)
re_read:
#endif /* IGNORE_CTRL_C || USE_GETCHAR */
#ifndef USE_SLANG
    clearerr(stdin); /* needed here for ultrix and SOCKETSHR, but why? - FM */
#endif /* !USE_SLANG */
#if !defined(USE_SLANG) || defined(VMS)
    c = GetChar();
#else
    if (LYCursesON) {
	c = GetChar();
    } else {
        c = getchar();
	if (c == EOF && errno == EINTR)	/* Ctrl-Z causes EINTR in getchar() */
	    clearerr(stdin);
	if (feof(stdin) || ferror(stdin) || c == EOF) {
#ifdef IGNORE_CTRL_C
	    if (sigint)
	        sigint = FALSE;
#endif /* IGNORE_CTRL_C */
	    return(7); /* use ^G to cancel whatever called us. */
	}
   }
#endif /* !USE_SLANG || VMS */

#ifdef DOSPATH
	if (c == 0) c = '/';
	if (c > 255) {      /* handle raw dos keys */
		switch (c)
		{
			case 464: c = '-'; break;  /* keypad minus*/
			case 465: c = '+'; break;  /* keypad plus*/
			case 459: c = 13; break;  /* keypad enter*/
			case 463: c = '*'; break;  /* keypad * */
			case 440: c = 'Q'; break;  /* alt x */
			default: break;
		}
	}
#endif
#ifdef USE_GETCHAR
    if (c == EOF && errno == EINTR)	/* Ctrl-Z causes EINTR in getchar() */
        goto re_read;
#endif /* USE_GETCHAR */

#ifdef USE_SLANG
    if (c == 0xFFFF && LYCursesON) {
#ifdef IGNORE_CTRL_C
	if (sigint) {
	    sigint = FALSE;
	    goto re_read;
	}
#endif /* IGNORE_CTRL_C */
        return(7); /* use ^G to cancel whatever called us. */
    }
#else
    if (feof(stdin) || ferror(stdin) || c == EOF) {
	if (recent_sizechange)
	    return(7); /* use ^G to cancel whatever called us. */
#ifdef IGNORE_CTRL_C
	if (sigint) {
	    sigint = FALSE;
	    /* clearerr(stdin);  don't need here if stays above - FM */
	    goto re_read;
	}
#endif /* IGNORE_CTRL_C */
	cleanup();
#ifndef NOSIGHUP
        (void) signal(SIGHUP, SIG_DFL);
#endif /* NOSIGHUP */
        (void) signal(SIGTERM, SIG_DFL);
#ifndef VMS
        (void) signal(SIGINT, SIG_DFL);
#endif /* !VMS */
#ifdef SIGTSTP
	if (no_suspend)
	  (void) signal(SIGTSTP,SIG_DFL);
#endif /* SIGTSTP */
	exit(0);
    }
#endif /* USE_SLANG */

    if (c == 27 || c == 155) {      /* handle escape sequence */
        b = GetChar();

        if (b == '[' || b == 'O') {
            a = GetChar();
        } else {
            a = b;
	}

        switch (a) {
        case 'A': c = UPARROW; break;
        case 'x': c = UPARROW; break;  /* keypad up on pc ncsa telnet */
        case 'B': c = DNARROW; break;
        case 'r': c = DNARROW; break; /* keypad down on pc ncsa telnet */
        case 'C': c = RTARROW; break;
        case 'v': c = RTARROW; break; /* keypad right on pc ncsa telnet */
        case 'D': c = LTARROW; break;
        case 't': c = LTARROW; break; /* keypad left on pc ncsa telnet */
        case 'y': c = PGUP; break;  /* keypad on pc ncsa telnet */
        case 's': c = PGDOWN; break;  /* keypad on pc ncsa telnet */
        case 'w': c = HOME; break;  /* keypad on pc ncsa telnet */
        case 'q': c = END; break;  /* keypad on pc ncsa telnet */
        case 'M':
#ifdef USE_SLANG_MOUSE
	   if ((c == 27) && (b == '['))
	     {
		int mouse_x, mouse_y, button;
		
		mouse_link = -1;
		c = -1;
		if (-1 != sl_parse_mouse_event (&mouse_x, &mouse_y, &button))
		  {
		     if (button == 0)  /* left */
		       c = set_clicked_link (mouse_x, mouse_y);
		     else if (button == 2)   /* right */
		       {
			  /* Right button: go back to prev document.  
			   * The problem is that we need to determine 
			   * what to return to achieve this.
			   */
			  c = map_function_to_key (LYK_PREV_DOC);
		       }
		  }
	     }
	   else 
#endif
	     c = '\n'; /* kepad enter on pc ncsa telnet */
	   break; 

        case 'm':
#ifdef VMS
            if (b != 'O')
#endif /* VMS */
                c = '-';  /* keypad on pc ncsa telnet */
            break;
	case 'k':
	    if (b == 'O')
		c = '+';  /* keypad + on my xterminal :) */
	    break;
        case 'l':
#ifdef VMS
            if (b != 'O')
#endif /* VMS */
                c = '+';  /* keypad on pc ncsa telnet */
            break;
        case 'P':
#ifdef VMS
            if (b != 'O')
#endif /* VMS */
                c = F1;
            break;
        case 'u':
#ifdef VMS
            if (b != 'O')
#endif /* VMS */
                c = F1;  /* macintosh help button */
            break;
        case 'p':
#ifdef VMS
            if (b == 'O')
#endif /* VMS */
                c = '0';  /* keypad 0 */
            break;
        case '1':                           /** VT300  Find  **/
            if ((b == '[' || c == 155) && (d=GetChar()) == '~')
                c = FIND_KEY;
            break;
	case '2':
	    if (b == '[' || c == 155) {
	        if ((d=GetChar())=='~')     /** VT300 Insert **/
	            c = INSERT_KEY;
	        else if ((d == '8' ||
			  d == '9') &&
			 GetChar() == '~')
	         {
		    if (d == '8')            /** VT300  Help **/
	                c = F1;
	            else if (d == '9')       /** VT300   Do  **/
	                c = DO_KEY;
		    d = -1;
		 }
	    }
	    break;
	case '3':			     /** VT300 Delete **/
	    if ((b == '[' || c == 155) && (d=GetChar()) == '~')
	        c = REMOVE_KEY;
	    break;
        case '4':                            /** VT300 Select **/
            if ((b == '[' || c == 155) && (d=GetChar()) == '~')
                c = SELECT_KEY;
            break;
        case '5':                            /** VT300 PrevScreen **/
            if ((b == '[' || c == 155) && (d=GetChar()) == '~')
                c = PGUP;
            break;
        case '6':                            /** VT300 NextScreen **/
            if ((b == '[' || c == 155) && (d=GetChar()) == '~')
                c = PGDOWN;
            break;
        case '[':                            /** Linux F1-F5: ^[[[A etc. **/
            if (b == '[' || c == 155) {
		if ((d=GetChar()) == 'A')
		    c = F1;
		break;
	    }
	default:
	   if (TRACE) {
		fprintf(stderr,"Unknown key sequence: %d:%d:%d\n",c,b,a);
		sleep(MessageSecs);
	   }
        }
	if (isdigit(a) && (b == '[' || c == 155) && d != -1 && d != '~')
	    d = GetChar();
    }
#if HAVE_KEYPAD
    else {

	/* convert keypad() mode keys into Lynx defined keys
	 */

	switch(c) {
	case KEY_DOWN:	           /* The four arrow keys ... */
	   c=DNARROW;
	   break;
	case KEY_UP:	
	   c=UPARROW;
	   break;
	case KEY_LEFT:	
	   c=LTARROW;
	   break;
	case KEY_RIGHT:	           /* ... */
	   c=RTARROW;
	   break;
	case KEY_HOME:	           /* Home key (upward+left arrow) */
	   c=HOME;
	   break;
	case KEY_CLEAR:	           /* Clear screen */
	   c=18; /* CTRL-R */
	   break;
	case KEY_NPAGE:	           /* Next page */
	   c=PGDOWN;
	   break;
	case KEY_PPAGE:	           /* Previous page */
	   c=PGUP;
	   break;
	case KEY_LL:	           /* home down or bottom (lower left) */
	   c=END;
	   break;
                                        /* The keypad is arranged like this:*/
                                        /*    a1    up    a3   */
                                        /*   left   b2  right  */
                                        /*    c1   down   c3   */
	case KEY_A1:	           /* upper left of keypad */
	   c=HOME;
	   break;
	case KEY_A3:	           /* upper right of keypad */
	   c=PGUP;
	   break;
	case KEY_B2:	           /* center of keypad */
	   c=DO_NOTHING;
	   break;
	case KEY_C1:	           /* lower left of keypad */
	   c=END;
	   break;
	case KEY_C3:	           /* lower right of keypad */
	   c=PGDOWN;
	   break;
#ifdef KEY_END
	case KEY_END:	           /* end key           001 */
	   c=END;
	   break;
#endif /* KEY_END */
#ifdef KEY_HELP
	case KEY_HELP:	           /* help key          001 */
	   c=F1;
	   break;
#endif /* KEY_HELP */
#ifdef KEY_BACKSPACE
	case KEY_BACKSPACE:
	   c=127;		   /* backspace key (delete, not Ctrl-H) */
	   break;
#endif /* KEY_BACKSPACE */

#ifdef NCURSES_MOUSE_VERSION
	case KEY_MOUSE:
	  {
#ifndef DOSPATH
           MEVENT event;
           int err;

	   c = -1;
	   mouse_link = -1;
	   err=getmouse(&event);
	   if (event.bstate & BUTTON1_CLICKED) {
	     c = set_clicked_link(event.x, event.y);
	   }
#else /* pdcurses version */
              int left,right;
              /* yes, I am assuming that my screen will be a certain width. */
              left = 6;
              right = LYcols-6;
              c = -1;
              mouse_link = -1;
              request_mouse_pos();
              if (Mouse_status.button[0] & BUTTON_CLICKED) {
                if (Mouse_status.y == (LYlines-1))
                       if (Mouse_status.x < left) c=LTARROW;
                       else if (Mouse_status.x > right) c='\b';
                       else c=PGDOWN;
                else if (Mouse_status.y == 0)
                       if (Mouse_status.x < left) c=LTARROW;
                       else if (Mouse_status.x > right) c='\b';
                       else c=PGUP;
                else c = set_clicked_link(Mouse_status.x, Mouse_status.y);
              }
#endif /* _WINDOWS */
	  }
	  break;
#endif /* NCURSES_MOUSE_VERSION */
	}
    }
#endif /* defined(HAVE_KEYPAD) */

    if (c > DO_NOTHING)
    /* Don't return raw values for KEYPAD symbols which we may have missed
     * in the switch above if they are obviously invalid when used as an
     * index into (e.g.) keypad[]. - kw
     */
       return (0);
    else
    return(c);
}


/*
 * display the current value of the string and allow the user
 * to edit it.
 */

#ifdef USE_SLANG
#define GetYX(y,x)   y = SLsmg_get_row(), x = SLsmg_get_column()
#else
#ifdef getyx
#define GetYX(y,x)   getyx(stdscr,y,x)
#else
#define GetYX(y,x)   y = stdscr->_cury, x = stdscr->_curx
#endif /* getyx */
#endif /* USE_SLANG */

#define EDREC    EditFieldData

/* shorthand to get rid of all most of the "edit->suchandsos" */
#define Buf      edit->buffer
#define Pos      edit->pos
#define StrLen   edit->strlen
#define MaxLen   edit->maxlen
#define DspWdth  edit->dspwdth
#define DspStart edit->xpan
#define Margin   edit->margin

PUBLIC void LYSetupEdit ARGS4(EDREC *,edit, char *,old, int,maxstr, int,maxdsp)
{
    /* Initialize edit record */

    GetYX(edit->sy, edit->sx);
    edit->pad   = ' ';
    edit->dirty = TRUE;
    edit->panon = FALSE;

    StrLen  = strlen(old);
    MaxLen  = maxstr;
    DspWdth = maxdsp;
    Margin  = 0;
    Pos = strlen(old);
    DspStart = 0;

    if (maxstr > maxdsp) {  /* Need panning? */
        if (DspWdth > 4)    /* Else "{}" take up precious screen space */
	    edit->panon = TRUE;

	/* Figure out margins. If too big we do a lot of unnecessary
	 * scrolling. If too small user doesn't have sufficient look-ahead.
	 * Let's say 25% for each margin, upper bound is 10 columns.
	 */
	Margin = DspWdth/4;
	if (Margin > 10)
	    Margin = 10;
    }
    strcpy(edit->buffer, old);
}

PUBLIC int LYEdit1 ARGS4(EDREC *,edit, int,ch, int,action, BOOL,maxMessage)
{   /* returns 0    character processed
     *         ch   otherwise
     */

    int i;
    int length;

    if (MaxLen <= 0)
        return(0); /* Be defensive */

    length=strlen(&Buf[0]);
    StrLen = length;

    switch (action) {
    case LYE_AIX:
        /*
	 * Hex 97.
	 * Fall through as a character for CJK.
	 * Otherwise, we treat this as LYE_ENTER.
	 */
	 if (HTCJK == NOCJK)
	     return(ch);
    case LYE_CHAR:
        /*
	 * ch is printable or ISO-8859-1 escape character.
	 */
	if (Pos <= (MaxLen) && StrLen < (MaxLen)) {
	    for(i = length; i >= Pos; i--)    /* Make room */
		Buf[i+1] = Buf[i];
	    Buf[length+1]='\0';
	    Buf[Pos] = (unsigned char) ch;
	    Pos++;
	} else if (maxMessage) {
	    _statusline(MAXLEN_REACHED_DEL_OR_MOV);
	}
	break;

    case LYE_BACKW:
        /*
	 * Backword.
	 * Definition of word is very naive: 1 or more a/n characters.
	 */
	while (Pos && !isalnum(Buf[Pos-1]))
	    Pos--;
	while (Pos &&  isalnum(Buf[Pos-1]))
	    Pos--;
	break;

    case LYE_FORWW:
        /*
	 * Word forward.
	 */
	while (isalnum(Buf[Pos]))
	    Pos++;   /* '\0' is not a/n */
	while (!isalnum(Buf[Pos]) && Buf[Pos])
	    Pos++ ;
	break;

    case LYE_ERASE:
        /*
	 * Erase the line to start fresh.
	 */
	 Buf[0] = '\0';
	 /* fall through */

    case LYE_BOL:
        /*
	 * Go to first column.
	 */
	Pos = 0;
	break;

    case LYE_EOL:
        /*
	 * Go to last column.
	 */
	Pos = length;
	break;

    case LYE_DELNW:
        /*
	 * Delete next word.
	 */
	{
	    int pos0 = Pos;
	    LYEdit1 (edit, 0, LYE_FORWW, FALSE);
	    while (Pos > pos0)
	        LYEdit1(edit, 0, LYE_DELP, FALSE);
	}
	break;

    case LYE_DELPW:
        /*
	 * Delete previous word.
	 */
	{
	    int pos0 = Pos;
	    LYEdit1 (edit, 0, LYE_BACKW, FALSE);
	    pos0 -= Pos;
	    while (pos0--)
	        LYEdit1(edit, 0, LYE_DELN, FALSE);
	}
	break;

    case LYE_DELN:
        /*
	 * Delete next character
	 */
	if (Pos >= length)
	    break;
	Pos++;
	/* fall through */

    case LYE_DELP:
        /*
	 * Delete preceding character.
	 */
	if (length == 0 || Pos == 0)
	    break;
	Pos--;
	for (i = Pos; i < length; i++)
	    Buf[i] = Buf[i+1];
	i--;
	Buf[i] = 0;
	break;

    case LYE_DELC:
        /*
	 * Delete current character.
	 */
	if (length == 0)
	    break;
	for (i = Pos; i < length; i++)
	    Buf[i] = Buf[i+1];
	i--;
	Buf[i] = 0;
	break;

    case LYE_FORW:
        /*
	 * Move cursor to the right.
	 */
	if (Pos < length)
	    Pos++;
	break;

    case LYE_BACK:
        /*
	 * Left-arrow move cursor to the left.
	 */
	if (Pos > 0)
	    Pos--;
	break;

    case LYE_UPPER:
	for (i = 0; Buf[i]; i++)
	   Buf[i] = TOUPPER(Buf[i]);
	break;

    case LYE_LOWER:
	for (i = 0; Buf[i]; i++)
	   Buf[i] = TOLOWER(Buf[i]);
	break;

    default:
	return(ch);
    }
    edit->dirty = TRUE;
    StrLen = strlen(&Buf[0]);
    return(0);
}


PUBLIC void LYRefreshEdit ARGS1(EDREC *,edit)
{
    int i;
    int length;
    int nrdisplayed;
    int padsize;
    char *str;
    char buffer[3];

    buffer[0] = buffer[1] = buffer[2] = '\0';
    if (!edit->dirty || (DspWdth == 0))
        return;
    edit->dirty = FALSE;

    length=strlen(&Buf[0]);
    edit->strlen = length;
/*
 *  Now we have:
 *                .--DspWdth---.
 *      +---------+=============+-----------+
 *      |         |M           M|           |   (M=margin)
 *      +---------+=============+-----------+
 *      0         DspStart                   length
 *
 *  Insertion point can be anywhere beween 0 and stringlength.
 *  Figure out new display starting point.
 *
 *   The first "if" below makes Lynx scroll several columns at a time when
 *   extending the string. Looks awful, but that way we can keep up with
 *   data entry at low baudrates.
 */
    if ((DspStart + DspWdth) <= length)
        if (Pos >= (DspStart + DspWdth) - Margin)
	    DspStart=(Pos - DspWdth) + Margin;

    if (Pos < DspStart + Margin) {
        DspStart = Pos - Margin;
	if (DspStart < 0)
	    DspStart = 0;
    }

    str = &Buf[DspStart];

    nrdisplayed = length-DspStart;
    if (nrdisplayed > DspWdth)
        nrdisplayed = DspWdth;

    move(edit->sy, edit->sx);
    if (edit->hidden) {
        for (i = 0; i < nrdisplayed; i++)
	    addch('*');
    } else {
        for (i = 0; i < nrdisplayed; i++)
	    if ((buffer[0] = str[i]) == 1 || buffer[0] == 2 ||
	        ((unsigned char)buffer[0] == 160 &&
		 !(HTPassHighCtrlRaw || HTCJK != NOCJK))) {
	        addch(' ');
	    } else {
		/* For CJK strings, by Masanobu Kimura */
		if (HTCJK != NOCJK && !isascii(buffer[0])) {
		    if (i < (nrdisplayed - 1))
		        buffer[1] = str[++i];
		    addstr(buffer);
		    buffer[1] = '\0';
		} else {
		    addstr(buffer);
		}
	    }
    }

    /*
     * Erase rest of input area.
     */
    padsize= DspWdth-nrdisplayed;
    while (padsize--)
        addch((unsigned char)edit->pad);

    /*
     * Scrolling indicators.
     */
    if (edit->panon) {
        if ((DspStart + nrdisplayed) < length) {
	    move(edit->sy, edit->sx+nrdisplayed-1);
	    addch('}');
	}
	if (DspStart) {
	    move(edit->sy, edit->sx);
	    addch('{');
	}
    }

    move(edit->sy, edit->sx + Pos - DspStart);
    refresh();
}


PUBLIC int LYgetstr ARGS4(char *,inputline, int,hidden,
			  int,bufsize, int,recall)
{
    extern BOOLEAN term_message;    /* Flag from terminate_message() AST */
#ifdef VMS
    extern BOOLEAN HadVMSInterrupt; /* Flag from cleanup_sig() AST       */
#endif
    int x, y, MaxStringSize;
    int ch;
    EditFieldData MyEdit;

    GetYX(y,x);                 /* Use screen from cursor position to eol */
    MaxStringSize = (bufsize < sizeof(MyEdit.buffer)) ?
    		    (bufsize - 1) : (sizeof(MyEdit.buffer) - 1);
    LYSetupEdit(&MyEdit, inputline, MaxStringSize, (LYcols-1)-x);
    MyEdit.hidden = hidden ;

    for (;;) {
again:
        LYRefreshEdit(&MyEdit);
        ch = LYgetch();
#ifdef VMS
	if (term_letter || term_options || term_message || HadVMSInterrupt) {
	    HadVMSInterrupt = FALSE;
	    ch = 7;
	}
#else
	if (term_letter || term_options || term_message)
	    ch = 7;
#endif /* VMS */
	if (recall && (ch == UPARROW || ch == DNARROW)) {
	    strcpy(inputline, MyEdit.buffer);
	    return(ch);
	}
	if (keymap[ch + 1] == LYK_REFRESH)
	    goto again;
        switch (EditBinding(ch)) {
	case LYE_TAB:
	    ch = '\t';
	    /* fall through */
	case LYE_AIX:
	    /*
	     * Hex 97.
	     * Treat as a character for CJK.
	     * Otherwise, we treat this as LYE_ENTER.
	     */
	    if (HTCJK != NOCJK && ch != '\t') {
	        LYLineEdit(&MyEdit,ch, FALSE);
		break;
	    }
	case LYE_ENTER:
	    /*
	     * Terminate the string and return.
	     */
	    strcpy(inputline, MyEdit.buffer);
            return(ch);
	    break;

        case LYE_ABORT:
	    /*
	     * Control-C or Control-G aborts.
	     */
	    inputline[0] = '\0';		   
	    return(-1);
            break;

        default:
            LYLineEdit(&MyEdit,ch, FALSE);
        }
    }
}

/*
 * LYstrstr will find the first occurence of the string pointed to by tarptr
 * in the string pointed to by chptr.  
 * It is a case insensitive search.
 */
PUBLIC char * LYstrstr ARGS2(char *,chptr, char *,tarptr)
{
    register char *tmpchptr, *tmptarptr;

    for(; *chptr != '\0'; chptr++) {
	if(TOUPPER(*chptr) == TOUPPER(*tarptr)) {	
	    /* see if they line up */ 
	    for(tmpchptr = chptr+1, tmptarptr = tarptr+1;
	        (TOUPPER(*tmpchptr) == TOUPPER(*tmptarptr) &&
		 *tmptarptr != '\0' && *tmpchptr != '\0');
	        tmpchptr++, tmptarptr++)
		   ; /* null body */ 
	    if(*tmptarptr == '\0') 
	  	return(chptr);
	}
    } /* end for */

    return(NULL);
}	

/*
 * LYno_attr_char_case_strstr will find the first occurence of the string 
 * pointed to by tarptr in the string pointed to by chptr.
 * It ignores the characters: LY_UNDERLINE_START_CHAR and
 * 			      LY_UNDERLINE_END_CHAR
 * 			      LY_BOLD_START_CHAR
 * 			      LY_BOLD_END_CHAR
 *			      if present in chptr.
 * It is a case insensitive search.
 */
PUBLIC char * LYno_attr_char_case_strstr ARGS2(char *,chptr, char *,tarptr)
{
    register char *tmpchptr, *tmptarptr;

    if (!chptr)
        return(NULL);

    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
        chptr++;

    for (; *chptr != '\0'; chptr++) {

        if (TOUPPER(*chptr) == TOUPPER(*tarptr)) {

            /* see if they line up */
	    tmpchptr = chptr+1;
	    tmptarptr = tarptr+1;

	    if (*tmptarptr == '\0')  /* one char target */
		 return(chptr);

	    while (1) {
		 if (!IsSpecialAttrChar(*tmpchptr)) {

                    if (TOUPPER(*tmpchptr) != TOUPPER(*tmptarptr))
			break;

		    tmpchptr++;
		    tmptarptr++;

		 } else {
		    tmpchptr++;
		 }

                 if (*tmptarptr == '\0')
		     return(chptr);

		 if (*tmpchptr == '\0')
		     break;
	    }
        }
    } /* end for */

    return(NULL);
}

/*
 * LYno_attr_char_strstr will find the first occurence of the string
 * pointed to by tarptr in the string pointed to by chptr.
 * It ignores the characters: LY_UNDERLINE_START_CHAR and
 *                            LY_UNDERLINE_END_CHAR
 *                            LY_BOLD_START_CHAR
 *                            LY_BOLD_END_CHAR
 *			      if present in chptr.
 * It is a case sensitive search.
 */
PUBLIC char * LYno_attr_char_strstr ARGS2(char *,chptr, char *,tarptr)
{
    register char *tmpchptr, *tmptarptr;

    if (!chptr)
        return(NULL);

    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
        chptr++;

    for (; *chptr != '\0'; chptr++) {

        if ((*chptr) == (*tarptr)) {

            /* see if they line up */
            tmpchptr = chptr+1;
            tmptarptr = tarptr+1;

	    if (*tmptarptr == '\0')  /* one char target */
		 return(chptr);

            while (1) {
		 if (!IsSpecialAttrChar(*tmpchptr)) {

                    if ((*tmpchptr) != (*tmptarptr))
                        break;

                    tmpchptr++;
                    tmptarptr++;

                 } else {
                    tmpchptr++;
                 }

                 if (*tmptarptr == '\0')
                     return(chptr);

                 if (*tmpchptr == '\0')
                     break;
            }
        }
    } /* end for */

    return(NULL);
}

#ifdef EXP_CHARTRANS

#define IS_UTFEXTRA(ch) (utf_flag && ((unsigned char)(ch)&0xc0) == 0x80)
/*
 * LYno_attr_mbcs_case_strstr will find the first occurence of the string 
 * pointed to by tarptr in the string pointed to by chptr.
 * It takes account of MultiByte Character Sequences (UTF8).
 * The physical lenght of the displayed string up to the end of the target
 * string is returned in *nendp if the search is successful.
 * It ignores the characters: LY_UNDERLINE_START_CHAR and
 * 			      LY_UNDERLINE_END_CHAR
 * 			      LY_BOLD_START_CHAR
 * 			      LY_BOLD_END_CHAR[B
 *			      if present in chptr.
 * It assumes UTF8 if utf_flag is set.
 * It is a case insensitive search.
 */
PUBLIC char * LYno_attr_mbcs_case_strstr ARGS4(char *,chptr, char *,tarptr,
					       BOOL, utf_flag, int *,nendp)
{
    register char *tmpchptr, *tmptarptr;
    int len = 0;

    if (!chptr)
        return(NULL);

    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
        chptr++;

    for (; *chptr != '\0'; chptr++) {
	    
        if (TOUPPER(*chptr) == TOUPPER(*tarptr)) {
	    int tarlen = 0;
	    len++;

            /* see if they line up */
	    tmpchptr = chptr+1;
	    tmptarptr = tarptr+1;

	    if (*tmptarptr == '\0') { /* one char target */
		*nendp = len;
		 return(chptr);
	    }
	    while (1) {
		 if (!IsSpecialAttrChar(*tmpchptr)) {

                    if (TOUPPER(*tmpchptr) != TOUPPER(*tmptarptr))
			break;

		    if (!IS_UTFEXTRA(*tmptarptr))
			tarlen++;
		    tmpchptr++;
		    tmptarptr++;

		 } else {
		    tmpchptr++;
		 }

                 if (*tmptarptr == '\0') {
		     *nendp = len + tarlen;
		     return(chptr);
		 }
		 if (*tmpchptr == '\0')
		     break;
	    }
        } else if (!( IS_UTFEXTRA(*chptr) ||
		      IsSpecialAttrChar(*chptr))) {
	    len++;
	}
		       
    } /* end for */

    return(NULL);
}

/*
 * LYno_attr_mbcs_strstr will find the first occurence of the string
 * pointed to by tarptr in the string pointed to by chptr.
 * It takes account of MultiByte Character Sequences (UTF8).
 * The physical lenght of the displayed string up to the end of the target
 * string is returned in *nendp if the search is successful.
 * It ignores the characters: LY_UNDERLINE_START_CHAR and
 *                            LY_UNDERLINE_END_CHAR
 *                            LY_BOLD_START_CHAR
 *                            LY_BOLD_END_CHAR
 *			      if present in chptr.
 * It assumes UTF8 if utf_flag is set.
 * It is a case sensitive search.
 */
PUBLIC char * LYno_attr_mbcs_strstr ARGS4(char *,chptr, char *,tarptr,
					       BOOL, utf_flag, int *,nendp)
{
    register char *tmpchptr, *tmptarptr;
    int len = 0;

    if (!chptr)
        return(NULL);

    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
        chptr++;

    for (; *chptr != '\0'; chptr++) {

        if ((*chptr) == (*tarptr)) {
	    int tarlen = 0;
	    len++;

            /* see if they line up */
            tmpchptr = chptr+1;
            tmptarptr = tarptr+1;

	    if (*tmptarptr == '\0') { /* one char target */
		*nendp = len + 1;
		 return(chptr);
	    }
            while (1) {
		 if (!IsSpecialAttrChar(*tmpchptr)) {

                    if ((*tmpchptr) != (*tmptarptr))
                        break;

		    if (!IS_UTFEXTRA(*tmptarptr))
			tarlen++;
                    tmpchptr++;
                    tmptarptr++;

                 } else {
                    tmpchptr++;
                 }

                 if (*tmptarptr == '\0') {
		     *nendp = len + tarlen;
		     return(chptr);
		 }
                 if (*tmpchptr == '\0')
                     break;
            }
        } else if (!( IS_UTFEXTRA(*chptr) ||
		      IsSpecialAttrChar(*chptr))) {
	    len++;
        }
    } /* end for */

    return(NULL);
}
#endif /* EXP_CHARTRANS */

/*      Allocate a new copy of a string, and returns it
*/
PUBLIC char * SNACopy ARGS3 (char **,dest, CONST char *,src, int,n)
{
  FREE(*dest);
  if (src) {
    *dest = (char *) calloc (1, n + 1);
    if (*dest == NULL) {
	fprintf(stderr,"Tried to calloc %d bytes\n",n);
	outofmem(__FILE__, "SNACopy");
    }
    strncpy (*dest, src, n);
    *(*dest + n) = '\0'; /* terminate */
  }
  return *dest;
}

/*      String Allocate and Concatenate
*/
PUBLIC char * SNACat ARGS3 (char **,dest, CONST char *,src, int,n)
{
  if (src && *src) {
    if (*dest) {
      int length = strlen (*dest);
      *dest = (char *) realloc (*dest, length + n + 1);
      if (*dest == NULL)
          outofmem(__FILE__, "SNACat");
      strncpy (*dest + length, src, n);
      *(*dest + length + n) = '\0'; /* terminate */
    } else {
      *dest = (char *) calloc (1, strlen(src) + 1);
      if (*dest == NULL)
          outofmem(__FILE__, "SNACat");
      strncpy (*dest, src, n);
      *dest[n] = '\0'; /* terminate */
    }
  }
  return *dest;
}