1 /*
2  * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  *
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 #include "enter.h"
20 
21 #include <termios.h>
22 #include <sys/types.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 
30 #ifndef URLVIEW
31 
32 /* global vars used for the string-history routines */
33 static char **Hist = NULL;
34 static short HistCur = 0;
35 static short HistLast = 0;
36 
mutt_init_history(void)37 void mutt_init_history (void)
38 {
39   int i;
40   static int OldSize = 0;
41 
42   if (Hist)
43   {
44     for (i = 0 ; i < OldSize ; i ++)
45       safe_free ((void **) &Hist[i]);
46     safe_free ((void **) &Hist);
47   }
48 
49   if (HistSize)
50     Hist = safe_calloc (HistSize, sizeof (char *));
51   HistCur = 0;
52   HistLast = 0;
53   OldSize = HistSize;
54 }
55 
sh_add(char * s)56 static void sh_add (char *s)
57 {
58   int prev;
59 
60   if (!HistSize)
61     return; /* disabled */
62 
63   if (*s)
64   {
65     prev = HistLast - 1;
66     if (prev < 0) prev = HistSize - 1;
67     if (!Hist[prev] || strcmp (Hist[prev], s) != 0)
68     {
69       safe_free ((void **) &Hist[HistLast]);
70       Hist[HistLast++] = safe_strdup (s);
71       if (HistLast > HistSize - 1)
72 	HistLast = 0;
73     }
74   }
75   HistCur = HistLast; /* reset to the last entry */
76 }
77 
sh_next(void)78 static char *sh_next (void)
79 {
80   int next;
81 
82   if (!HistSize)
83     return (""); /* disabled */
84 
85   next = HistCur + 1;
86   if (next > HistLast - 1)
87     next = 0;
88   if (Hist[next])
89     HistCur = next;
90   return (Hist[HistCur] ? Hist[HistCur] : "");
91 }
92 
sh_prev(void)93 static char *sh_prev (void)
94 {
95   int prev;
96 
97   if (!HistSize)
98     return (""); /* disabled */
99 
100   prev = HistCur - 1;
101   if (prev < 0)
102   {
103     prev = HistLast - 1;
104     if (prev < 0)
105     {
106       prev = HistSize - 1;
107       while (prev > 0 && Hist[prev] == NULL)
108 	prev--;
109     }
110   }
111   if (Hist[prev])
112     HistCur = prev;
113   return (Hist[HistCur] ? Hist[HistCur] : "");
114 }
115 #endif /* ! URLVIEW */
116 
117 /* redraw flags for mutt_enter_string() */
118 enum
119 {
120   M_REDRAW_INIT = 1,	/* recalculate lengths */
121   M_REDRAW_LINE,	/* redraw entire line */
122   M_REDRAW_EOL,		/* redraw from current position to eol */
123   M_REDRAW_PREV_EOL	/* redraw from curpos-1 to eol */
124 };
125 
126 /* Returns:
127  *	1 need to redraw the screen and call me again
128  *	0 if input was given
129  * 	-1 if abort.
130  *
131  */
mutt_enter_string(unsigned char * buf,size_t buflen,int y,int x,int flags)132 int mutt_enter_string (unsigned char *buf, size_t buflen, int y, int x,
133 		       int flags)
134 {
135   int curpos = 0;		/* the location of the cursor */
136   int lastchar = 0; 		/* offset of the last char in the string */
137   int begin = 0;		/* first character displayed on the line */
138   int ch;			/* last typed character */
139   int width = COLS - x - 1;	/* width of field */
140   int redraw = M_REDRAW_INIT;	/* when/what to redraw */
141   int pass = (flags == M_PASS);
142   int j;
143 
144   FOREVER
145   {
146     if (redraw)
147     {
148       if (redraw == M_REDRAW_INIT)
149       {
150 	/* full redraw */
151 	lastchar = curpos = strlen ((char *) buf);
152 	begin = lastchar - width;
153       }
154       if (begin < 0)
155 	begin = 0;
156       switch (redraw)
157       {
158 	case M_REDRAW_PREV_EOL:
159 	  j = curpos - 1;
160 	  break;
161 	case M_REDRAW_EOL:
162 	  j = curpos;
163 	  break;
164 	default:
165 	  j = begin;
166       }
167       move (y, x + j - begin);
168       for (; j < lastchar && j < begin + width; j++)
169 	addch (buf[j]);
170       clrtoeol ();
171       if (redraw != M_REDRAW_INIT)
172 	move (y, x + curpos - begin);
173       redraw = 0;
174     }
175     refresh ();
176 
177     /* first look to see if a keypress is an editor operation.  km_dokey()
178      * returns 0 if there is no entry in the keymap, so restore the last
179      * keypress and continue normally.
180      */
181     if ((ch = km_dokey (MENU_EDITOR)) == -1)
182     {
183       buf[curpos] = 0;
184       return (-1);
185     }
186 
187     if (ch != 0)
188     {
189       switch (ch)
190       {
191 	case OP_EDITOR_BACKSPACE:
192 	  if (curpos == 0)
193 	  {
194 	    BEEP ();
195 	    break;
196 	  }
197 	  for (j = curpos ; j < lastchar ; j++)
198 	    buf[j - 1] = buf[j];
199 	  curpos--;
200 	  lastchar--;
201 	  if (!pass)
202 	  {
203 	    if (curpos > begin)
204 	    {
205 	      if (lastchar == curpos)
206 	      {
207 		move (y, x + curpos - begin);
208 		delch ();
209 	      }
210 	      else
211 		redraw = M_REDRAW_EOL;
212 	    }
213 	    else
214 	    {
215 	      begin -= width / 2;
216 	      redraw = M_REDRAW_LINE;
217 	    }
218 	  }
219 	  break;
220 	case OP_EDITOR_BOL:
221 	  /* reposition the cursor at the begininning of the line */
222 	  curpos = 0;
223 	  if (!pass)
224 	  {
225 	    if (begin)
226 	    {
227 	      /* the first char is not displayed, so readjust */
228 	      begin = 0;
229 	      redraw = M_REDRAW_LINE;
230 	    }
231 	    else
232 	      move (y, x);
233 	  }
234 	  break;
235 	case OP_EDITOR_EOL:
236 	  curpos = lastchar;
237 	  if (!pass)
238 	  {
239 	    if (lastchar < begin + width)
240 	      move (y, x + lastchar - begin);
241 	    else
242 	    {
243 	      begin = lastchar - width / 2;
244 	      redraw = M_REDRAW_LINE;
245 	    }
246 	  }
247 	  break;
248 	case OP_EDITOR_KILL_LINE:
249 	  lastchar = curpos = 0;
250 	  if (!pass)
251 	  {
252 	    begin = 0;
253 	    redraw = M_REDRAW_LINE;
254 	  }
255 	  break;
256 	case OP_EDITOR_KILL_EOL:
257 	  lastchar = curpos;
258 	  if (!pass)
259 	    clrtoeol ();
260 	  break;
261 	case OP_EDITOR_BACKWARD_CHAR:
262 	  if (curpos == 0)
263 	  {
264 	    BEEP ();
265 	  }
266 	  else
267 	  {
268 	    curpos--;
269 	    if (!pass)
270 	    {
271 	      if (curpos < begin)
272 	      {
273 		begin -= width / 2;
274 		redraw = M_REDRAW_LINE;
275 	      }
276 	      else
277 		move (y, x + curpos - begin);
278 	    }
279 	  }
280 	  break;
281 	case OP_EDITOR_FORWARD_CHAR:
282 	  if (curpos == lastchar)
283 	  {
284 	    BEEP ();
285 	  }
286 	  else
287 	  {
288 	    curpos++;
289 	    if (!pass)
290 	    {
291 	      if (curpos >= begin + width)
292 	      {
293 		begin = curpos - width / 2;
294 		redraw = M_REDRAW_LINE;
295 	      }
296 	      else
297 		move (y, x + curpos - begin);
298 	    }
299 	  }
300 	  break;
301 	case OP_EDITOR_DELETE_CHAR:
302 	  if (curpos != lastchar)
303 	  {
304 	    for (j = curpos; j < lastchar; j++)
305 	      buf[j] = buf[j + 1];
306 	    lastchar--;
307 	    if (!pass)
308 	      redraw = M_REDRAW_EOL;
309 	  }
310 	  else
311 	    BEEP ();
312 	  break;
313 	case OP_EDITOR_KILL_WORD:
314 	  /* delete to begining of word */
315 	  if (curpos != 0)
316 	  {
317 	    j = curpos;
318 	    while (j > 0 && ISSPACE (buf[j - 1]))
319 	      j--;
320 	    if (j > 0)
321 	    {
322 	      if (isalnum (buf[j - 1]))
323 	      {
324 		for (j--; j > 0 && isalnum (buf[j - 1]); j--)
325 		  ;
326 	      }
327 	      else
328 		j--;
329 	    }
330 	    ch = j; /* save current position */
331 	    while (curpos < lastchar)
332 	      buf[j++] = buf[curpos++];
333 	    lastchar = j;
334 	    curpos = ch; /* restore current position */
335 	    /* update screen */
336 	    if (!pass)
337 	    {
338 	      if (curpos < begin)
339 	      {
340 		begin = curpos - width / 2;
341 		redraw = M_REDRAW_LINE;
342 	      }
343 	      else
344 		redraw = M_REDRAW_EOL;
345 	    }
346 	  }
347 	  break;
348 	default:
349 	  BEEP ();
350       }
351     }
352     else
353     {
354       /* use the raw keypress */
355       ch = LastKey;
356 
357       if (CI_is_return (ch))
358       {
359 	buf[lastchar] = 0;
360 	return (0);
361       }
362       else if (IsPrint (ch) && ((size_t)(lastchar + 1) < buflen))
363       {
364 	for (j = lastchar; j > curpos; j--)
365 	  buf[j] = buf[j - 1];
366 	buf[curpos++] = ch;
367 	lastchar++;
368 
369 	if (!pass)
370 	{
371 	  if (curpos >= begin + width)
372 	  {
373 	    begin = curpos - width / 2;
374 	    redraw = M_REDRAW_LINE;
375 	  }
376 	  else if (curpos == lastchar)
377 	    addch (ch);
378 	  else
379 	    redraw = M_REDRAW_PREV_EOL;
380 	}
381       }
382       else
383       {
384 	mutt_flushinp ();
385 	BEEP ();
386       }
387     }
388   }
389   /* not reached */
390 }
391