enter.c 20.7 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2
 * Copyright (C) 1996-2000,2007,2011,2013 Michael R. Elkins <me@mutt.org>
3
 * Copyright (C) 2000-2001 Edmund Grimley Evans <edmundo@rano.org>
Thomas Roessler's avatar
Thomas Roessler committed
4 5 6 7 8 9 10 11 12 13 14 15 16
 * 
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
17
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Thomas Roessler's avatar
Thomas Roessler committed
18 19
 */ 

20 21 22 23
#if HAVE_CONFIG_H
# include "config.h"
#endif

Thomas Roessler's avatar
Thomas Roessler committed
24 25 26 27
#include "mutt.h"
#include "mutt_menu.h"
#include "mutt_curses.h"
#include "keymap.h"
Thomas Roessler's avatar
Thomas Roessler committed
28
#include "history.h"
Thomas Roessler's avatar
Thomas Roessler committed
29 30 31 32 33 34

#include <string.h>

/* redraw flags for mutt_enter_string() */
enum
{
35 36
  MUTT_REDRAW_INIT = 1,	/* go to end of line and redraw */
  MUTT_REDRAW_LINE		/* redraw entire line */
Thomas Roessler's avatar
Thomas Roessler committed
37 38
};

39 40
static int my_wcwidth (wchar_t wc)
{
41
  int n = wcwidth (wc);
42
  if (IsWPrint (wc) && n > 0)
43 44 45 46 47 48
    return n;
  if (!(wc & ~0x7f))
    return 2;
  if (!(wc & ~0xffff))
    return 6;
  return 10;
49 50
}

51 52 53
/* combining mark / non-spacing character */
#define COMB_CHAR(wc) (IsWPrint (wc) && !wcwidth (wc))

54 55 56 57 58 59 60 61 62 63
static int my_wcswidth (const wchar_t *s, size_t n)
{
  int w = 0;
  while (n--)
    w += my_wcwidth (*s++);
  return w;
}

static int my_addwch (wchar_t wc)
{
64
  int n = wcwidth (wc);
65
  if (IsWPrint (wc) && n > 0)
66 67 68 69 70 71
    return mutt_addwch (wc);
  if (!(wc & ~0x7f))
    return printw ("^%c", ((int)wc + 0x40) & 0x7f);
  if (!(wc & ~0xffff))
    return printw ("\\u%04x", (int)wc);
  return printw ("\\u%08x", (int)wc);
72 73 74 75 76 77 78 79 80 81 82 83
}

static size_t width_ceiling (const wchar_t *s, size_t n, int w1)
{
  const wchar_t *s0 = s;
  int w = 0;
  for (; n; s++, n--)
    if ((w += my_wcwidth (*s)) > w1)
      break;
  return s - s0;  
}

84
static void my_wcstombs (char *dest, size_t dlen, const wchar_t *src, size_t slen)
85 86
{
  mbstate_t st;
87
  size_t k;
88

89
  /* First convert directly into the destination buffer */
90
  memset (&st, 0, sizeof (st));
91
  for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
92 93
    if ((k = wcrtomb (dest, *src, &st)) == (size_t)(-1))
      break;
94 95 96

  /* If this works, we can stop now */
  if (dlen >= MB_LEN_MAX) {
Michael Elkins's avatar
Michael Elkins committed
97
    wcrtomb (dest, 0, &st);
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
    return;
  }

  /* Otherwise convert any remaining data into a local buffer */
  {
    char buf[3 * MB_LEN_MAX];
    char *p = buf;

    for (; slen && p - buf < dlen; p += k, src++, slen--)
      if ((k = wcrtomb (p, *src, &st)) == (size_t)(-1))
	break;
    p += wcrtomb (p, 0, &st);

    /* If it fits into the destination buffer, we can stop now */
    if (p - buf <= dlen) {
      memcpy (dest, buf, p - buf);
      return;
    }

    /* Otherwise we truncate the string in an ugly fashion */
    memcpy (dest, buf, dlen);
    dest[dlen - 1] = '\0'; /* assume original dlen > 0 */
  }
121 122
}

123
static size_t my_mbstowcs (wchar_t **pwbuf, size_t *pwbuflen, size_t i, char *buf)
124 125 126 127 128 129 130 131
{
  wchar_t wc;
  mbstate_t st;
  size_t k;
  wchar_t *wbuf;
  size_t wbuflen;

  wbuf = *pwbuf, wbuflen = *pwbuflen;
132 133
  
  while (*buf)
134
  {
135 136 137 138 139 140 141 142 143 144 145 146
    memset (&st, 0, sizeof (st));
    for (; (k = mbrtowc (&wc, buf, MB_LEN_MAX, &st)) &&
	 k != (size_t)(-1) && k != (size_t)(-2); buf += k)
    {
      if (i >= wbuflen)
      {
	wbuflen = i + 20;
	safe_realloc (&wbuf, wbuflen * sizeof (*wbuf));
      }
      wbuf[i++] = wc;
    }
    if (*buf && (k == (size_t) -1 || k == (size_t) -2))
147
    {
148 149 150 151 152 153 154
      if (i >= wbuflen) 
      {
	wbuflen = i + 20;
	safe_realloc (&wbuf, wbuflen * sizeof (*wbuf));
      }
      wbuf[i++] = replacement_char();
      buf++;
155 156 157 158 159 160 161
    }
  }
  *pwbuf = wbuf, *pwbuflen = wbuflen;
  return i;
}

/*
162
 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
163 164
 */

165
static void replace_part (ENTER_STATE *state, size_t from, char *buf)
166 167
{
  /* Save the suffix */
168
  size_t savelen = state->lastchar - state->curpos;
169 170 171 172 173 174 175
  wchar_t *savebuf = NULL;

  if (savelen)
  {
    savebuf = safe_calloc (savelen, sizeof (wchar_t));
    memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
  }
176 177

  /* Convert to wide characters */
178
  state->curpos = my_mbstowcs (&state->wbuf, &state->wbuflen, from, buf);
179

180
  if (savelen)
181
  {
182 183 184 185 186 187 188 189 190 191
    /* Make space for suffix */
    if (state->curpos + savelen > state->wbuflen)
    {
      state->wbuflen = state->curpos + savelen;
      safe_realloc (&state->wbuf, state->wbuflen * sizeof (wchar_t));
    }

    /* Restore suffix */
    memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
    FREE (&savebuf);
192 193
  }

194
  state->lastchar = state->curpos + savelen;
195 196
}

197 198 199
/*
 * Return 1 if the character is not typically part of a pathname
 */
200
static inline int is_shell_char(wchar_t ch)
201
{
202
  static const wchar_t shell_chars[] = L"<>&()$?*;{}| "; /* ! not included because it can be part of a pathname in Mutt */
203 204 205
  return wcschr(shell_chars, ch) != NULL;
}

206 207 208 209 210
/* This function is for very basic input, currently used only by the
 * built-in editor.  It does not handle screen redrawing on resizes
 * well, because there is no active menu for the built-in editor.
 * Most callers should prefer mutt_get_field() instead.
 *
211
 * Returns:
Thomas Roessler's avatar
Thomas Roessler committed
212 213 214
 *	0 if input was given
 * 	-1 if abort.
 */
215
int  mutt_enter_string(char *buf, size_t buflen, int col, int flags)
216 217 218
{
  int rv;
  ENTER_STATE *es = mutt_new_enter_state ();
219 220
  do
  {
221
#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
222 223 224 225 226 227
    if (SigWinch)
    {
      SigWinch = 0;
      mutt_resize_screen ();
      clearok(stdscr, TRUE);
    }
228
#endif
229 230 231
    rv = _mutt_enter_string (buf, buflen, col, flags, 0, NULL, NULL, es);
  }
  while (rv == 1);
232 233 234 235
  mutt_free_enter_state (&es);
  return rv;
}

236 237 238 239 240 241
/*
 * Returns:
 *      1 need to redraw the screen and call me again
 *	0 if input was given
 * 	-1 if abort.
 */
242
int _mutt_enter_string (char *buf, size_t buflen, int col,
243 244
			int flags, int multiple, char ***files, int *numfiles,
			ENTER_STATE *state)
Thomas Roessler's avatar
Thomas Roessler committed
245
{
246
  int width = MuttMessageWindow->cols - col - 1;
247
  int redraw;
248
  int pass = (flags & MUTT_PASS);
Thomas Roessler's avatar
Thomas Roessler committed
249
  int first = 1;
250 251 252 253 254
  int ch, w, r;
  size_t i;
  wchar_t *tempbuf = 0;
  size_t templen = 0;
  history_class_t hclass;
255 256
  wchar_t wc;
  mbstate_t mbstate;
257

258
  int rv = 0;
259
  memset (&mbstate, 0, sizeof (mbstate));
260 261
  
  if (state->wbuf)
262 263
  {
    /* Coming back after return 1 */
264
    redraw = MUTT_REDRAW_LINE;
265
    first = 0;
266 267
  }
  else
268
  {
269
    /* Initialise wbuf from buf */
270
    state->wbuflen = 0;
271
    state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
272
    redraw = MUTT_REDRAW_INIT;
273 274
  }

275
  if (flags & MUTT_FILE)
Thomas Roessler's avatar
Thomas Roessler committed
276
    hclass = HC_FILE;
277
  else if (flags & MUTT_EFILE)
278
    hclass = HC_MBOX;
279
  else if (flags & MUTT_CMD)
Thomas Roessler's avatar
Thomas Roessler committed
280
    hclass = HC_CMD;
281
  else if (flags & MUTT_ALIAS)
Thomas Roessler's avatar
Thomas Roessler committed
282
    hclass = HC_ALIAS;
283
  else if (flags & MUTT_COMMAND)
284
    hclass = HC_COMMAND;
285
  else if (flags & MUTT_PATTERN)
286
    hclass = HC_PATTERN;
Thomas Roessler's avatar
Thomas Roessler committed
287 288
  else 
    hclass = HC_OTHER;
289 290
    
  for (;;)
Thomas Roessler's avatar
Thomas Roessler committed
291
  {
292
    if (redraw && !pass)
Thomas Roessler's avatar
Thomas Roessler committed
293
    {
294
      if (redraw == MUTT_REDRAW_INIT)
Thomas Roessler's avatar
Thomas Roessler committed
295
      {
296
	/* Go to end of line */
297 298
	state->curpos = state->lastchar;
	state->begin = width_ceiling (state->wbuf, state->lastchar, my_wcswidth (state->wbuf, state->lastchar) - width + 1);
299
      } 
300 301
      if (state->curpos < state->begin ||
	  my_wcswidth (state->wbuf + state->begin, state->curpos - state->begin) >= width)
302
	state->begin = width_ceiling (state->wbuf, state->lastchar, my_wcswidth (state->wbuf, state->curpos) - width / 2);
303
      mutt_window_move (MuttMessageWindow, 0, col);
304
      w = 0;
305
      for (i = state->begin; i < state->lastchar; i++)
Thomas Roessler's avatar
Thomas Roessler committed
306
      {
307
	w += my_wcwidth (state->wbuf[i]);
308
	if (w > width)
Thomas Roessler's avatar
Thomas Roessler committed
309
	  break;
310
	my_addwch (state->wbuf[i]);
311
      }
312 313 314
      mutt_window_clrtoeol (MuttMessageWindow);
      mutt_window_move (MuttMessageWindow, 0,
          col + my_wcswidth (state->wbuf + state->begin, state->curpos - state->begin));
Thomas Roessler's avatar
Thomas Roessler committed
315 316 317
    }
    mutt_refresh ();

318
    if ((ch = km_dokey (MENU_EDITOR)) < 0)
Thomas Roessler's avatar
Thomas Roessler committed
319
    {
320
      rv = (SigWinch && ch == -2) ? 1 : -1;
321
      goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
322 323
    }

324
    if (ch != OP_NULL)
Thomas Roessler's avatar
Thomas Roessler committed
325
    {
326
      first = 0;
327
      if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY)
328
	state->tabs = 0;
329
      redraw = MUTT_REDRAW_LINE;
Thomas Roessler's avatar
Thomas Roessler committed
330 331 332
      switch (ch)
      {
	case OP_EDITOR_HISTORY_UP:
333
	  state->curpos = state->lastchar;
334 335 336 337 338
	  if (mutt_history_at_scratch (hclass))
	  {
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
	    mutt_history_save_scratch (hclass, buf);
	  }
339
	  replace_part (state, 0, mutt_history_prev (hclass));
340
	  redraw = MUTT_REDRAW_INIT;
Thomas Roessler's avatar
Thomas Roessler committed
341
	  break;
342

Thomas Roessler's avatar
Thomas Roessler committed
343
	case OP_EDITOR_HISTORY_DOWN:
344
	  state->curpos = state->lastchar;
345 346 347 348 349
	  if (mutt_history_at_scratch (hclass))
	  {
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
	    mutt_history_save_scratch (hclass, buf);
	  }
Thomas Roessler's avatar
Thomas Roessler committed
350
	  replace_part (state, 0, mutt_history_next (hclass));
351
	  redraw = MUTT_REDRAW_INIT;
Thomas Roessler's avatar
Thomas Roessler committed
352
	  break;
353

354 355 356 357 358 359 360 361 362
        case OP_EDITOR_HISTORY_SEARCH:
          state->curpos = state->lastchar;
          my_wcstombs (buf, buflen, state->wbuf, state->curpos);
          mutt_history_complete (buf, buflen, hclass);
          replace_part (state, 0, buf);
          rv = 1;
          goto bye;
          break;

Thomas Roessler's avatar
Thomas Roessler committed
363
	case OP_EDITOR_BACKSPACE:
364
	  if (state->curpos == 0)
Thomas Roessler's avatar
Thomas Roessler committed
365
	    BEEP ();
366
	  else
Thomas Roessler's avatar
Thomas Roessler committed
367
	  {
368
	    i = state->curpos;
369
	    while (i && COMB_CHAR (state->wbuf[i - 1]))
370 371 372
	      --i;
	    if (i)
	      --i;
373
	    memmove (state->wbuf + i, state->wbuf + state->curpos, (state->lastchar - state->curpos) * sizeof (wchar_t));
374
	    state->lastchar -= state->curpos - i;
375
	    state->curpos = i;
Thomas Roessler's avatar
Thomas Roessler committed
376 377
	  }
	  break;
378

Thomas Roessler's avatar
Thomas Roessler committed
379
	case OP_EDITOR_BOL:
380
	  state->curpos = 0;
Thomas Roessler's avatar
Thomas Roessler committed
381
	  break;
382

Thomas Roessler's avatar
Thomas Roessler committed
383
	case OP_EDITOR_EOL:
384
	  redraw= MUTT_REDRAW_INIT;
Thomas Roessler's avatar
Thomas Roessler committed
385
	  break;
386

Thomas Roessler's avatar
Thomas Roessler committed
387
	case OP_EDITOR_KILL_LINE:
388
	  state->curpos = state->lastchar = 0;
Thomas Roessler's avatar
Thomas Roessler committed
389
	  break;
390

Thomas Roessler's avatar
Thomas Roessler committed
391
	case OP_EDITOR_KILL_EOL:
392
	  state->lastchar = state->curpos;
Thomas Roessler's avatar
Thomas Roessler committed
393
	  break;
394

Thomas Roessler's avatar
Thomas Roessler committed
395
	case OP_EDITOR_BACKWARD_CHAR:
396
	  if (state->curpos == 0)
Thomas Roessler's avatar
Thomas Roessler committed
397 398 399
	    BEEP ();
	  else
	  {
400
	    while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
401 402 403
	      state->curpos--;
	    if (state->curpos)
	      state->curpos--;
Thomas Roessler's avatar
Thomas Roessler committed
404 405
	  }
	  break;
406

Thomas Roessler's avatar
Thomas Roessler committed
407
	case OP_EDITOR_FORWARD_CHAR:
408
	  if (state->curpos == state->lastchar)
Thomas Roessler's avatar
Thomas Roessler committed
409 410 411
	    BEEP ();
	  else
	  {
412
	    ++state->curpos;
413
	    while (state->curpos < state->lastchar && COMB_CHAR (state->wbuf[state->curpos]))
414
	      ++state->curpos;
Thomas Roessler's avatar
Thomas Roessler committed
415 416
	  }
	  break;
417

418
	case OP_EDITOR_BACKWARD_WORD:
419
	  if (state->curpos == 0)
420 421 422
	    BEEP ();
	  else
	  {
423
	    while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
424
	      --state->curpos;
425
	    while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
426
	      --state->curpos;
427 428 429 430
	  }
	  break;

	case OP_EDITOR_FORWARD_WORD:
431
	  if (state->curpos == state->lastchar)
432 433 434
	    BEEP ();
	  else
	  {
435
	    while (state->curpos < state->lastchar && iswspace (state->wbuf[state->curpos]))
436
	      ++state->curpos;
437
	    while (state->curpos < state->lastchar && !iswspace (state->wbuf[state->curpos]))
438
	      ++state->curpos;
439 440 441
	  }
	  break;

442 443 444
	case OP_EDITOR_CAPITALIZE_WORD:
	case OP_EDITOR_UPCASE_WORD:
	case OP_EDITOR_DOWNCASE_WORD:
445
	  if (state->curpos == state->lastchar)
446
	  {
447
	    BEEP ();
448 449
	    break;
	  }
450
	  while (state->curpos && !iswspace (state->wbuf[state->curpos]))
451
	    --state->curpos;
452
	  while (state->curpos < state->lastchar && iswspace (state->wbuf[state->curpos]))
453
	    ++state->curpos;
454
	  while (state->curpos < state->lastchar && !iswspace (state->wbuf[state->curpos]))
455
	  {
456
	    if (ch == OP_EDITOR_DOWNCASE_WORD)
457
	      state->wbuf[state->curpos] = towlower (state->wbuf[state->curpos]);
458
	    else
459
	    {
460
	      state->wbuf[state->curpos] = towupper (state->wbuf[state->curpos]);
461 462
	      if (ch == OP_EDITOR_CAPITALIZE_WORD)
		ch = OP_EDITOR_DOWNCASE_WORD;
463
	    }
464
	    state->curpos++;
465 466
	  }
	  break;
467

Thomas Roessler's avatar
Thomas Roessler committed
468
	case OP_EDITOR_DELETE_CHAR:
469
	  if (state->curpos == state->lastchar)
Thomas Roessler's avatar
Thomas Roessler committed
470
	    BEEP ();
471
	  else
Thomas Roessler's avatar
Thomas Roessler committed
472
	  {
473
	    i = state->curpos;
474
	    while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
475
	      ++i;
476
	    if (i < state->lastchar)
477
	      ++i;
478
	    while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
479
	      ++i;
480
	    memmove (state->wbuf + state->curpos, state->wbuf + i, (state->lastchar - i) * sizeof (wchar_t));
481
	    state->lastchar -= i - state->curpos;
Thomas Roessler's avatar
Thomas Roessler committed
482 483
	  }
	  break;
484

485
	case OP_EDITOR_KILL_WORD:
486
	  /* delete to beginning of word */
487 488
	  if (state->curpos != 0)
	  {
489 490 491 492
	    i = state->curpos;
	    while (i && iswspace (state->wbuf[i - 1]))
	      --i;
	    if (i)
493
	    {
494
	      if (iswalnum (state->wbuf[i - 1]))
495
	      {
496
		for (--i; i && iswalnum (state->wbuf[i - 1]); i--)
497 498 499
		  ;
	      }
	      else
500
		--i;
501
	    }
502 503 504 505
	    memmove (state->wbuf + i, state->wbuf + state->curpos,
		     (state->lastchar - state->curpos) * sizeof (wchar_t));
	    state->lastchar += i - state->curpos;
	    state->curpos = i;
506 507 508 509 510
	  }
	  break;

	case OP_EDITOR_KILL_EOW:
	  /* delete to end of word */
511 512

	  /* first skip over whitespace */
513 514
	  for (i = state->curpos;
	       i < state->lastchar && iswspace (state->wbuf[i]); i++)
515
	    ;
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533

	  /* if there are any characters left.. */
	  if (i < state->lastchar)
	  {
	    /* if the current character is alphanumeric.. */
	    if (iswalnum (state->wbuf[i]))
	    {
	      /* skip over the rest of the word consistent of only alphanumerics */
	      for (; i < state->lastchar && iswalnum (state->wbuf[i]); i++)
		;
	    }
	    else
	    {
	      /* skip over one non-alphanumeric character */
	      ++i;
	    }
	  }

534 535 536
	  memmove (state->wbuf + state->curpos, state->wbuf + i,
		   (state->lastchar - i) * sizeof (wchar_t));
	  state->lastchar += state->curpos - i;
537
	  break;
538

Thomas Roessler's avatar
Thomas Roessler committed
539
	case OP_EDITOR_BUFFY_CYCLE:
540
	  if (flags & MUTT_EFILE)
Thomas Roessler's avatar
Thomas Roessler committed
541 542
	  {
	    first = 1; /* clear input if user types a real key later */
543
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
544
	    mutt_buffy (buf, buflen);
545
	    state->curpos = state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
Thomas Roessler's avatar
Thomas Roessler committed
546 547
	    break;
	  }
548
	  else if (!(flags & MUTT_FILE))
Thomas Roessler's avatar
Thomas Roessler committed
549
	    goto self_insert;
550
	  /* fall through to completion routine (MUTT_FILE) */
Thomas Roessler's avatar
Thomas Roessler committed
551 552

	case OP_EDITOR_COMPLETE:
553
	case OP_EDITOR_COMPLETE_QUERY:
554
	  state->tabs++;
555
	  if (flags & MUTT_CMD)
Thomas Roessler's avatar
Thomas Roessler committed
556
	  {
557
	    for (i = state->curpos; i && !is_shell_char(state->wbuf[i-1]); i--)
558
	      ;
559
	    my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
560
	    if (tempbuf && templen == state->lastchar - i &&
561
		!memcmp (tempbuf, state->wbuf + i, (state->lastchar - i) * sizeof (wchar_t)))
Thomas Roessler's avatar
Thomas Roessler committed
562
	    {
563
	      mutt_select_file (buf, buflen, (flags & MUTT_EFILE) ? MUTT_SEL_FOLDER : 0);
564
	      if (*buf)
565
		replace_part (state, i, buf);
566 567
	      rv = 1; 
	      goto bye;
568 569 570
	    }
	    if (!mutt_complete (buf, buflen))
	    {
571
	      templen = state->lastchar - i;
572
	      safe_realloc (&tempbuf, templen * sizeof (wchar_t));
Thomas Roessler's avatar
Thomas Roessler committed
573 574 575
	    }
	    else
	      BEEP ();
576

577
	    replace_part (state, i, buf);
Thomas Roessler's avatar
Thomas Roessler committed
578
	  }
579
	  else if (flags & MUTT_ALIAS && ch == OP_EDITOR_COMPLETE)
Thomas Roessler's avatar
Thomas Roessler committed
580 581
	  {
	    /* invoke the alias-menu to get more addresses */
582 583
	    for (i = state->curpos; i && state->wbuf[i-1] != ',' && 
		 state->wbuf[i-1] != ':'; i--)
584
	      ;
585
	    for (; i < state->lastchar && state->wbuf[i] == ' '; i++)
586
	      ;
587
	    my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
588
	    r = mutt_alias_complete (buf, buflen);
589
	    replace_part (state, i, buf);
590
	    if (!r)
591 592 593 594
	    {
	      rv = 1;
	      goto bye;
	    }
595
	    break;
Thomas Roessler's avatar
Thomas Roessler committed
596
	  }
David Champion's avatar
David Champion committed
597 598 599 600 601 602 603 604
	  else if (flags & MUTT_LABEL && ch == OP_EDITOR_COMPLETE)
	  {
	    for (i = state->curpos; i && state->wbuf[i-1] != ',' && 
		 state->wbuf[i-1] != ':'; i--)
	      ;
	    for (; i < state->lastchar && state->wbuf[i] == ' '; i++)
	      ;
	    my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
605
	    r = mutt_label_complete (buf, buflen, state->tabs);
David Champion's avatar
David Champion committed
606 607 608 609 610 611 612 613
	    replace_part (state, i, buf);
	    if (!r)
	    {
	      rv = 1;
	      goto bye;
	    }
	    break;
	  }
614 615 616 617
          else if (flags & MUTT_PATTERN && ch == OP_EDITOR_COMPLETE)
          {
            for (i = state->curpos; i && state->wbuf[i-1] != '~'; i--)
              ;
618
            if (i && i < state->curpos && state->wbuf[i-1] == '~' && state->wbuf[i] == 'y')
619 620 621
            {
              i++;
              my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
622
              r = mutt_label_complete (buf, buflen, state->tabs);
623 624 625 626 627 628 629 630 631 632 633
              replace_part (state, i, buf);
              if (!r)
              {
                rv = 1;
                goto bye;
              }
            }
            else
              goto self_insert;
            break;
          }
634
	  else if (flags & MUTT_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY)
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	  {
	    /* invoke the query-menu to get more addresses */
	    if ((i = state->curpos))
	    {
	      for (; i && state->wbuf[i - 1] != ','; i--)
		;
	      for (; i < state->curpos && state->wbuf[i] == ' '; i++)
		;
	    }

	    my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
	    mutt_query_complete (buf, buflen);
	    replace_part (state, i, buf);

	    rv = 1; 
	    goto bye;
	  }
652
	  else if (flags & MUTT_COMMAND)
Thomas Roessler's avatar
Thomas Roessler committed
653
	  {
654
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
655
	    i = strlen (buf);
656
	    if (i && buf[i - 1] == '=' &&
657
		mutt_var_value_complete (buf, buflen, i))
658 659
	      state->tabs = 0;
	    else if (!mutt_command_complete (buf, buflen, i, state->tabs))
660
	      BEEP ();
661
	    replace_part (state, 0, buf);
Thomas Roessler's avatar
Thomas Roessler committed
662
	  }
663
	  else if (flags & (MUTT_FILE | MUTT_EFILE))
Thomas Roessler's avatar
Thomas Roessler committed
664
	  {
665
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
Thomas Roessler's avatar
Thomas Roessler committed
666 667

	    /* see if the path has changed from the last time */
668
	    if ((!tempbuf && !state->lastchar) || (tempbuf && templen == state->lastchar &&
669
		!memcmp (tempbuf, state->wbuf, state->lastchar * sizeof (wchar_t))))
Thomas Roessler's avatar
Thomas Roessler committed
670
	    {
671
	      _mutt_select_file (buf, buflen, 
672
				 ((flags & MUTT_EFILE) ? MUTT_SEL_FOLDER : 0) | (multiple ? MUTT_SEL_MULTI : 0), 
673
				 files, numfiles);
674
	      if (*buf)
Thomas Roessler's avatar
Thomas Roessler committed
675
	      {
676
		mutt_pretty_mailbox (buf, buflen);
677
		if (!pass)
678
		  mutt_history_add (hclass, buf, 1);
679 680
		rv = 0;
		goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
681
	      }
682 683

	      /* file selection cancelled */
684 685
	      rv = 1;
	      goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
686 687
	    }

688 689
	    if (!mutt_complete (buf, buflen))
	    {
690
	      templen = state->lastchar;
691
	      safe_realloc (&tempbuf, templen * sizeof (wchar_t));
692
	      memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
693
	    }
Thomas Roessler's avatar
Thomas Roessler committed
694 695
	    else
	      BEEP (); /* let the user know that nothing matched */
696
	    replace_part (state, 0, buf);
Thomas Roessler's avatar
Thomas Roessler committed
697 698 699 700 701 702
	  }
	  else
	    goto self_insert;
	  break;

	case OP_EDITOR_QUOTE_CHAR:
703
	  {
704 705
	    event_t event;
	    /*ADDCH (LastKey);*/
706 707 708 709
            do
            {
              event = mutt_getch ();
            } while (event.ch == -2);
710
	    if (event.ch >= 0)
711 712 713 714
	    {
	      LastKey = event.ch;
	      goto self_insert;
	    }
715 716
	  }

717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
	case OP_EDITOR_TRANSPOSE_CHARS:
	  if (state->lastchar < 2)
	    BEEP ();
	  else
	{
	    wchar_t t;

	    if (state->curpos == 0)
	      state->curpos = 2;
	    else if (state->curpos < state->lastchar)
	      ++state->curpos;

	    t = state->wbuf[state->curpos - 2];
	    state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1];
	    state->wbuf[state->curpos - 1] = t;
	  }
	  break;

Thomas Roessler's avatar
Thomas Roessler committed
735 736 737 738 739 740
	default:
	  BEEP ();
      }
    }
    else
    {
741
      
Thomas Roessler's avatar
Thomas Roessler committed
742 743
self_insert:

744
      state->tabs = 0;
Thomas Roessler's avatar
Thomas Roessler committed
745 746 747
      /* use the raw keypress */
      ch = LastKey;

748 749 750 751 752 753 754
#ifdef KEY_ENTER
      /* treat ENTER the same as RETURN */
      if (ch == KEY_ENTER)
	ch = '\r';
#endif

      /* quietly ignore all other function keys */
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
      if (ch & ~0xff)
	continue;

      /* gather the octets into a wide character */
      {
	char c;
	size_t k;

	c = ch;
	k = mbrtowc (&wc, &c, 1, &mbstate);
	if (k == (size_t)(-2))
	  continue;
	else if (k && k != 1)
	{
	  memset (&mbstate, 0, sizeof (mbstate));
	  continue;
	}
      }

774
      if (first && (flags & MUTT_CLEAR))
Thomas Roessler's avatar
Thomas Roessler committed
775 776
      {
	first = 0;
777
	if (IsWPrint (wc)) /* why? */
778
	  state->curpos = state->lastchar = 0;
Thomas Roessler's avatar
Thomas Roessler committed
779 780
      }

781
      if (wc == '\r' || wc == '\n')
Thomas Roessler's avatar
Thomas Roessler committed
782
      {
783
	/* Convert from wide characters */
784
	my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
Thomas Roessler's avatar
Thomas Roessler committed
785
	if (!pass)
786
	  mutt_history_add (hclass, buf, 1);
787

788 789 790 791
	if (multiple)
	{
	  char **tfiles;
	  *numfiles = 1;
792
	  tfiles = safe_calloc (*numfiles, sizeof (char *));
793 794
	  mutt_expand_path (buf, buflen);
	  tfiles[0] = safe_strdup (buf);
795 796
	  *files = tfiles;
	}
797 798
	rv = 0; 
	goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
799
      }
800
      else if (wc && (wc < ' ' || IsWPrint (wc))) /* why? */
Thomas Roessler's avatar
Thomas Roessler committed
801
      {
802
	if (state->lastchar >= state->wbuflen)
Thomas Roessler's avatar
Thomas Roessler committed
803
	{
804
	  state->wbuflen = state->lastchar + 20;
805
	  safe_realloc (&state->wbuf, state->wbuflen * sizeof (wchar_t));
Thomas Roessler's avatar
Thomas Roessler committed
806
	}
807
	memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos, (state->lastchar - state->curpos) * sizeof (wchar_t));
808
	state->wbuf[state->curpos++] = wc;
809
	state->lastchar++;
Thomas Roessler's avatar
Thomas Roessler committed
810 811 812 813 814 815 816 817
      }
      else
      {
	mutt_flushinp ();
	BEEP ();
      }
    }
  }
818 819
  
  bye:
820

821
  mutt_reset_history_state (hclass);
822
  FREE (&tempbuf);
823 824 825 826 827 828 829
  return rv;
}

void mutt_free_enter_state (ENTER_STATE **esp)
{
  if (!esp) return;
  
830
  FREE (&(*esp)->wbuf);
831
  FREE (esp);		/* __FREE_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
832
}
833 834 835 836 837 838 839

/*
 * TODO:
 * very narrow screen might crash it
 * sort out the input side
 * unprintable chars
 */