enter.c 17 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2
 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3
 * Copyright (C) 2000 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
Thomas Roessler's avatar
Thomas Roessler committed
18 19 20 21 22 23
 */ 

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

#include <string.h>

/* redraw flags for mutt_enter_string() */
enum
{
31 32
  M_REDRAW_INIT = 1,	/* go to end of line and redraw */
  M_REDRAW_LINE		/* redraw entire line */
Thomas Roessler's avatar
Thomas Roessler committed
33 34
};

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

47 48 49
/* combining mark / non-spacing character */
#define COMB_CHAR(wc) (IsWPrint (wc) && !wcwidth (wc))

50 51 52 53 54 55 56 57 58 59
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)
{
60
  int n = wcwidth (wc);
61
  if (IsWPrint (wc) && n > 0)
62 63 64 65 66 67
    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);
68 69 70 71 72 73 74 75 76 77 78 79
}

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;  
}

80
static void my_wcstombs (char *dest, size_t dlen, const wchar_t *src, size_t slen)
81 82
{
  mbstate_t st;
83
  size_t k;
84

85
  /* First convert directly into the destination buffer */
86
  memset (&st, 0, sizeof (st));
87
  for (; slen && dlen >= MB_LEN_MAX; dest += k, dlen -= k, src++, slen--)
88 89
    if ((k = wcrtomb (dest, *src, &st)) == (size_t)(-1))
      break;
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

  /* If this works, we can stop now */
  if (dlen >= MB_LEN_MAX) {
    wcrtomb (dest, 0, &st);
    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 */
  }
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
}

size_t my_mbstowcs (wchar_t **pwbuf, size_t *pwbuflen, size_t i, char *buf)
{
  wchar_t wc;
  mbstate_t st;
  size_t k;
  wchar_t *wbuf;
  size_t wbuflen;

  wbuf = *pwbuf, wbuflen = *pwbuflen;
  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 ((void **) &wbuf, wbuflen * sizeof (*wbuf));
    }
    wbuf[i++] = wc;
  }
  *pwbuf = wbuf, *pwbuflen = wbuflen;
  return i;
}

/*
144
 * Replace part of the wchar_t buffer, from FROM to CURPOS, by BUF.
145 146
 */

147
static void replace_part (ENTER_STATE *state, size_t from, char *buf)
148 149
{
  /* Save the suffix */
150
  size_t savelen = state->lastchar - state->curpos;
151
  wchar_t *savebuf = safe_calloc (savelen, sizeof (wchar_t));
152
  memcpy (savebuf, state->wbuf + state->curpos, savelen * sizeof (wchar_t));
153 154

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

  /* Make space for suffix */
158
  if (state->curpos + savelen > state->wbuflen)
159
  {
160
    state->wbuflen = state->curpos + savelen;
161
    safe_realloc ((void **) &state->wbuf, state->wbuflen * sizeof (wchar_t));
162 163 164
  }

  /* Restore suffix */
165 166
  memcpy (state->wbuf + state->curpos, savebuf, savelen * sizeof (wchar_t));
  state->lastchar = state->curpos + savelen;
167

168
  FREE (&savebuf);
169 170 171 172
}

/*
 * Returns:
Thomas Roessler's avatar
Thomas Roessler committed
173 174 175 176
 *	1 need to redraw the screen and call me again
 *	0 if input was given
 * 	-1 if abort.
 */
177

178 179 180 181 182 183 184 185 186
int  mutt_enter_string(char *buf, size_t buflen, int y, int x, int flags)
{
  int rv;
  ENTER_STATE *es = mutt_new_enter_state ();
  rv = _mutt_enter_string (buf, buflen, y, x, flags, 0, NULL, NULL, es);
  mutt_free_enter_state (&es);
  return rv;
}

187
int _mutt_enter_string (char *buf, size_t buflen, int y, int x,
188 189
			int flags, int multiple, char ***files, int *numfiles,
			ENTER_STATE *state)
Thomas Roessler's avatar
Thomas Roessler committed
190
{
191 192 193
  int width = COLS - x - 1;
  int redraw;
  int pass = (flags & M_PASS);
Thomas Roessler's avatar
Thomas Roessler committed
194
  int first = 1;
195 196 197 198 199
  int ch, w, r;
  size_t i;
  wchar_t *tempbuf = 0;
  size_t templen = 0;
  history_class_t hclass;
200 201
  wchar_t wc;
  mbstate_t mbstate;
202

203
  int rv = 0;
204
  memset (&mbstate, 0, sizeof (mbstate));
205 206
  
  if (state->wbuf)
207 208 209 210 211
  {
    /* Coming back after return 1 */
    redraw = M_REDRAW_LINE;
  }
  else
212
  {
213
    /* Initialise wbuf from buf */
214
    state->wbuflen = 0;
215
    state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
216
    redraw = M_REDRAW_INIT;
217 218
  }

219
  if (flags & (M_FILE | M_EFILE))
Thomas Roessler's avatar
Thomas Roessler committed
220
    hclass = HC_FILE;
221
  else if (flags & M_CMD)
Thomas Roessler's avatar
Thomas Roessler committed
222
    hclass = HC_CMD;
223
  else if (flags & M_ALIAS)
Thomas Roessler's avatar
Thomas Roessler committed
224
    hclass = HC_ALIAS;
225
  else if (flags & M_COMMAND)
226
    hclass = HC_COMMAND;
227 228
  else if (flags & M_PATTERN)
    hclass = HC_PATTERN;
Thomas Roessler's avatar
Thomas Roessler committed
229 230
  else 
    hclass = HC_OTHER;
231 232
    
  for (;;)
Thomas Roessler's avatar
Thomas Roessler committed
233
  {
234
    if (redraw && !pass)
Thomas Roessler's avatar
Thomas Roessler committed
235 236 237
    {
      if (redraw == M_REDRAW_INIT)
      {
238
	/* Go to end of line */
239 240
	state->curpos = state->lastchar;
	state->begin = width_ceiling (state->wbuf, state->lastchar, my_wcswidth (state->wbuf, state->lastchar) - width + 1);
241
      } 
242 243
      if (state->curpos < state->begin ||
	  my_wcswidth (state->wbuf + state->begin, state->curpos - state->begin) >= width)
244
	state->begin = width_ceiling (state->wbuf, state->lastchar, my_wcswidth (state->wbuf, state->curpos) - width / 2);
245 246
      move (y, x);
      w = 0;
247
      for (i = state->begin; i < state->lastchar; i++)
Thomas Roessler's avatar
Thomas Roessler committed
248
      {
249
	w += my_wcwidth (state->wbuf[i]);
250
	if (w > width)
Thomas Roessler's avatar
Thomas Roessler committed
251
	  break;
252
	my_addwch (state->wbuf[i]);
253
      }
Thomas Roessler's avatar
Thomas Roessler committed
254
      clrtoeol ();
255
      move (y, x + my_wcswidth (state->wbuf + state->begin, state->curpos - state->begin));
Thomas Roessler's avatar
Thomas Roessler committed
256 257 258
    }
    mutt_refresh ();

259
    if ((ch = _km_dokey (MENU_EDITOR, pass ? M_KM_UNBUFFERED : 0)) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
260
    {
261 262
      rv = -1; 
      goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
263 264
    }

265
    if (ch != OP_NULL)
Thomas Roessler's avatar
Thomas Roessler committed
266
    {
267
      first = 0;
268
      if (ch != OP_EDITOR_COMPLETE)
269
	state->tabs = 0;
270
      redraw = M_REDRAW_LINE;
Thomas Roessler's avatar
Thomas Roessler committed
271 272 273
      switch (ch)
      {
	case OP_EDITOR_HISTORY_UP:
274
	  state->curpos = state->lastchar;
275
	  replace_part (state, 0, mutt_history_prev (hclass));
276
	  redraw = M_REDRAW_INIT;
Thomas Roessler's avatar
Thomas Roessler committed
277
	  break;
278

Thomas Roessler's avatar
Thomas Roessler committed
279
	case OP_EDITOR_HISTORY_DOWN:
280
	  state->curpos = state->lastchar;
Thomas Roessler's avatar
Thomas Roessler committed
281
	  replace_part (state, 0, mutt_history_next (hclass));
282
	  redraw = M_REDRAW_INIT;
Thomas Roessler's avatar
Thomas Roessler committed
283
	  break;
284

Thomas Roessler's avatar
Thomas Roessler committed
285
	case OP_EDITOR_BACKSPACE:
286
	  if (state->curpos == 0)
Thomas Roessler's avatar
Thomas Roessler committed
287
	    BEEP ();
288
	  else
Thomas Roessler's avatar
Thomas Roessler committed
289
	  {
290
	    i = state->curpos;
291
	    while (i && COMB_CHAR (state->wbuf[i - 1]))
292 293 294
	      --i;
	    if (i)
	      --i;
295
	    memmove (state->wbuf + i, state->wbuf + state->curpos, (state->lastchar - state->curpos) * sizeof (wchar_t));
296
	    state->lastchar -= state->curpos - i;
297
	    state->curpos = i;
Thomas Roessler's avatar
Thomas Roessler committed
298 299
	  }
	  break;
300

Thomas Roessler's avatar
Thomas Roessler committed
301
	case OP_EDITOR_BOL:
302
	  state->curpos = 0;
Thomas Roessler's avatar
Thomas Roessler committed
303
	  break;
304

Thomas Roessler's avatar
Thomas Roessler committed
305
	case OP_EDITOR_EOL:
306
	  redraw= M_REDRAW_INIT;
Thomas Roessler's avatar
Thomas Roessler committed
307
	  break;
308

Thomas Roessler's avatar
Thomas Roessler committed
309
	case OP_EDITOR_KILL_LINE:
310
	  state->curpos = state->lastchar = 0;
Thomas Roessler's avatar
Thomas Roessler committed
311
	  break;
312

Thomas Roessler's avatar
Thomas Roessler committed
313
	case OP_EDITOR_KILL_EOL:
314
	  state->lastchar = state->curpos;
Thomas Roessler's avatar
Thomas Roessler committed
315
	  break;
316

Thomas Roessler's avatar
Thomas Roessler committed
317
	case OP_EDITOR_BACKWARD_CHAR:
318
	  if (state->curpos == 0)
Thomas Roessler's avatar
Thomas Roessler committed
319 320 321
	    BEEP ();
	  else
	  {
322
	    while (state->curpos && COMB_CHAR (state->wbuf[state->curpos - 1]))
323 324 325
	      state->curpos--;
	    if (state->curpos)
	      state->curpos--;
Thomas Roessler's avatar
Thomas Roessler committed
326 327
	  }
	  break;
328

Thomas Roessler's avatar
Thomas Roessler committed
329
	case OP_EDITOR_FORWARD_CHAR:
330
	  if (state->curpos == state->lastchar)
Thomas Roessler's avatar
Thomas Roessler committed
331 332 333
	    BEEP ();
	  else
	  {
334
	    ++state->curpos;
335
	    while (state->curpos < state->lastchar && COMB_CHAR (state->wbuf[state->curpos]))
336
	      ++state->curpos;
Thomas Roessler's avatar
Thomas Roessler committed
337 338
	  }
	  break;
339

340
	case OP_EDITOR_BACKWARD_WORD:
341
	  if (state->curpos == 0)
342 343 344
	    BEEP ();
	  else
	  {
345
	    while (state->curpos && iswspace (state->wbuf[state->curpos - 1]))
346
	      --state->curpos;
347
	    while (state->curpos && !iswspace (state->wbuf[state->curpos - 1]))
348
	      --state->curpos;
349 350 351 352
	  }
	  break;

	case OP_EDITOR_FORWARD_WORD:
353
	  if (state->curpos == state->lastchar)
354 355 356
	    BEEP ();
	  else
	  {
357
	    while (state->curpos < state->lastchar && iswspace (state->wbuf[state->curpos]))
358
	      ++state->curpos;
359
	    while (state->curpos < state->lastchar && !iswspace (state->wbuf[state->curpos]))
360
	      ++state->curpos;
361 362 363
	  }
	  break;

364 365 366
	case OP_EDITOR_CAPITALIZE_WORD:
	case OP_EDITOR_UPCASE_WORD:
	case OP_EDITOR_DOWNCASE_WORD:
367
	  if (state->curpos == state->lastchar)
368
	  {
369
	    BEEP ();
370 371
	    break;
	  }
372
	  while (state->curpos && !iswspace (state->wbuf[state->curpos]))
373
	    --state->curpos;
374
	  while (state->curpos < state->lastchar && iswspace (state->wbuf[state->curpos]))
375
	    ++state->curpos;
376
	  while (state->curpos < state->lastchar && !iswspace (state->wbuf[state->curpos]))
377
	  {
378
	    if (ch == OP_EDITOR_DOWNCASE_WORD)
379
	      state->wbuf[state->curpos] = towlower (state->wbuf[state->curpos]);
380
	    else
381
	    {
382
	      state->wbuf[state->curpos] = towupper (state->wbuf[state->curpos]);
383 384
	      if (ch == OP_EDITOR_CAPITALIZE_WORD)
		ch = OP_EDITOR_DOWNCASE_WORD;
385
	    }
386
	    state->curpos++;
387 388
	  }
	  break;
389

Thomas Roessler's avatar
Thomas Roessler committed
390
	case OP_EDITOR_DELETE_CHAR:
391
	  if (state->curpos == state->lastchar)
Thomas Roessler's avatar
Thomas Roessler committed
392
	    BEEP ();
393
	  else
Thomas Roessler's avatar
Thomas Roessler committed
394
	  {
395
	    i = state->curpos;
396
	    while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
397
	      ++i;
398
	    if (i < state->lastchar)
399
	      ++i;
400
	    while (i < state->lastchar && COMB_CHAR (state->wbuf[i]))
401
	      ++i;
402
	    memmove (state->wbuf + state->curpos, state->wbuf + i, (state->lastchar - i) * sizeof (wchar_t));
403
	    state->lastchar -= i - state->curpos;
Thomas Roessler's avatar
Thomas Roessler committed
404 405
	  }
	  break;
406

407 408 409 410
	case OP_EDITOR_KILL_WORD:
	  /* delete to begining of word */
	  if (state->curpos != 0)
	  {
411 412 413 414
	    i = state->curpos;
	    while (i && iswspace (state->wbuf[i - 1]))
	      --i;
	    if (i)
415
	    {
416
	      if (iswalnum (state->wbuf[i - 1]))
417
	      {
418
		for (--i; i && iswalnum (state->wbuf[i - 1]); i--)
419 420 421
		  ;
	      }
	      else
422
		--i;
423
	    }
424 425 426 427
	    memmove (state->wbuf + i, state->wbuf + state->curpos,
		     (state->lastchar - state->curpos) * sizeof (wchar_t));
	    state->lastchar += i - state->curpos;
	    state->curpos = i;
428 429 430 431 432
	  }
	  break;

	case OP_EDITOR_KILL_EOW:
	  /* delete to end of word */
433 434
	  for (i = state->curpos;
	       i < state->lastchar && iswspace (state->wbuf[i]); i++)
435
	    ;
436
	  for (; i < state->lastchar && !iswspace (state->wbuf[i]); i++)
437
	    ;
438 439 440
	  memmove (state->wbuf + state->curpos, state->wbuf + i,
		   (state->lastchar - i) * sizeof (wchar_t));
	  state->lastchar += state->curpos - i;
441
	  break;
442

Thomas Roessler's avatar
Thomas Roessler committed
443 444 445 446
	case OP_EDITOR_BUFFY_CYCLE:
	  if (flags & M_EFILE)
	  {
	    first = 1; /* clear input if user types a real key later */
447
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
448
	    mutt_buffy (buf, buflen);
449
	    state->curpos = state->lastchar = my_mbstowcs (&state->wbuf, &state->wbuflen, 0, buf);
Thomas Roessler's avatar
Thomas Roessler committed
450 451 452 453 454 455 456
	    break;
	  }
	  else if (!(flags & M_FILE))
	    goto self_insert;
	  /* fall through to completion routine (M_FILE) */

	case OP_EDITOR_COMPLETE:
457
	  state->tabs++;
Thomas Roessler's avatar
Thomas Roessler committed
458 459
	  if (flags & M_CMD)
	  {
460
	    for (i = state->curpos; i && state->wbuf[i-1] != ' '; i--)
461
	      ;
462
	    my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
463
	    if (tempbuf && templen == state->lastchar - i &&
464
		!memcmp (tempbuf, state->wbuf + i, (state->lastchar - i) * sizeof (wchar_t)))
Thomas Roessler's avatar
Thomas Roessler committed
465
	    {
466
	      mutt_select_file (buf, buflen, (flags & M_EFILE) ? M_SEL_FOLDER : 0);
Thomas Roessler's avatar
Thomas Roessler committed
467
	      set_option (OPTNEEDREDRAW);
468
	      if (*buf)
469
		replace_part (state, i, buf);
470 471
	      rv = 1; 
	      goto bye;
472 473 474
	    }
	    if (!mutt_complete (buf, buflen))
	    {
475
	      templen = state->lastchar - i;
476
	      safe_realloc ((void **) &tempbuf, templen * sizeof (wchar_t));
Thomas Roessler's avatar
Thomas Roessler committed
477 478 479
	    }
	    else
	      BEEP ();
480

481
	    replace_part (state, i, buf);
Thomas Roessler's avatar
Thomas Roessler committed
482 483 484 485
	  }
	  else if (flags & M_ALIAS)
	  {
	    /* invoke the alias-menu to get more addresses */
486 487
	    for (i = state->curpos; i && state->wbuf[i-1] != ',' && 
		 state->wbuf[i-1] != ':'; i--)
488
	      ;
489
	    for (; i < state->lastchar && state->wbuf[i] == ' '; i++)
490
	      ;
491
	    my_wcstombs (buf, buflen, state->wbuf + i, state->curpos - i);
492
	    r = mutt_alias_complete (buf, buflen);
493
	    replace_part (state, i, buf);
494
	    if (!r)
495 496 497 498
	    {
	      rv = 1;
	      goto bye;
	    }
499
	    break;
Thomas Roessler's avatar
Thomas Roessler committed
500
	  }
Thomas Roessler's avatar
Thomas Roessler committed
501 502
	  else if (flags & M_COMMAND)
	  {
503
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
504
	    i = strlen (buf);
505
	    if (i && buf[i - 1] == '=' &&
506
		mutt_var_value_complete (buf, buflen, i))
507 508
	      state->tabs = 0;
	    else if (!mutt_command_complete (buf, buflen, i, state->tabs))
509
	      BEEP ();
510
	    replace_part (state, 0, buf);
Thomas Roessler's avatar
Thomas Roessler committed
511
	  }
Thomas Roessler's avatar
Thomas Roessler committed
512 513
	  else if (flags & (M_FILE | M_EFILE))
	  {
514
	    my_wcstombs (buf, buflen, state->wbuf, state->curpos);
Thomas Roessler's avatar
Thomas Roessler committed
515 516

	    /* see if the path has changed from the last time */
517
	    if ((!tempbuf && !state->lastchar) || (tempbuf && templen == state->lastchar &&
518
		!memcmp (tempbuf, state->wbuf, state->lastchar * sizeof (wchar_t))))
Thomas Roessler's avatar
Thomas Roessler committed
519
	    {
520 521 522
	      _mutt_select_file (buf, buflen, 
				 ((flags & M_EFILE) ? M_SEL_FOLDER : 0) | (multiple ? M_SEL_MULTI : 0), 
				 files, numfiles);
Thomas Roessler's avatar
Thomas Roessler committed
523
	      set_option (OPTNEEDREDRAW);
524
	      if (*buf)
Thomas Roessler's avatar
Thomas Roessler committed
525
	      {
526 527 528
		mutt_pretty_mailbox (buf);
		if (!pass)
		  mutt_history_add (hclass, buf);
529 530
		rv = 0;
		goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
531
	      }
532 533

	      /* file selection cancelled */
534 535
	      rv = 1;
	      goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
536 537
	    }

538 539
	    if (!mutt_complete (buf, buflen))
	    {
540
	      templen = state->lastchar;
541 542
	      safe_realloc ((void **) &tempbuf, templen * sizeof (wchar_t));
	      memcpy (tempbuf, state->wbuf, templen * sizeof (wchar_t));
543
	    }
Thomas Roessler's avatar
Thomas Roessler committed
544 545
	    else
	      BEEP (); /* let the user know that nothing matched */
546
	    replace_part (state, 0, buf);
Thomas Roessler's avatar
Thomas Roessler committed
547 548 549 550 551 552 553 554 555
	  }
	  else
	    goto self_insert;
	  break;

	case OP_EDITOR_COMPLETE_QUERY:
	  if (flags & M_ALIAS)
	  {
	    /* invoke the query-menu to get more addresses */
556
	    if ((i = state->curpos))
Thomas Roessler's avatar
Thomas Roessler committed
557
	    {
558
	      for (; i && state->wbuf[i - 1] != ','; i--)
559
		;
560
	      for (; i < state->curpos && state->wbuf[i] == ' '; i++)
561 562
		;
	    }
563 564 565 566 567

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

568 569
	    rv = 1; 
	    goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
570 571 572 573 574
	  }
	  else
	    goto self_insert;

	case OP_EDITOR_QUOTE_CHAR:
575
	  {
576 577 578 579 580 581 582 583
	    event_t event;
	    /*ADDCH (LastKey);*/
	    event = mutt_getch ();
	    if (event.ch != -1)
	    {
	      LastKey = event.ch;
	      goto self_insert;
	    }
584 585
	  }

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
	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
604 605 606 607 608 609
	default:
	  BEEP ();
      }
    }
    else
    {
610
      
Thomas Roessler's avatar
Thomas Roessler committed
611 612
self_insert:

613
      state->tabs = 0;
Thomas Roessler's avatar
Thomas Roessler committed
614 615 616
      /* use the raw keypress */
      ch = LastKey;

617 618 619 620 621 622 623
#ifdef KEY_ENTER
      /* treat ENTER the same as RETURN */
      if (ch == KEY_ENTER)
	ch = '\r';
#endif

      /* quietly ignore all other function keys */
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
      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;
	}
      }

Thomas Roessler's avatar
Thomas Roessler committed
643 644 645
      if (first && (flags & M_CLEAR))
      {
	first = 0;
646
	if (IsWPrint (wc)) /* why? */
647
	  state->curpos = state->lastchar = 0;
Thomas Roessler's avatar
Thomas Roessler committed
648 649
      }

650
      if (wc == '\r' || wc == '\n')
Thomas Roessler's avatar
Thomas Roessler committed
651
      {
652
	/* Convert from wide characters */
653
	my_wcstombs (buf, buflen, state->wbuf, state->lastchar);
Thomas Roessler's avatar
Thomas Roessler committed
654
	if (!pass)
655 656
	  mutt_history_add (hclass, buf);

657 658 659 660
	if (multiple)
	{
	  char **tfiles;
	  *numfiles = 1;
661
	  tfiles = safe_calloc (*numfiles, sizeof (char *));
662 663
	  mutt_expand_path (buf, buflen);
	  tfiles[0] = safe_strdup (buf);
664 665
	  *files = tfiles;
	}
666 667
	rv = 0; 
	goto bye;
Thomas Roessler's avatar
Thomas Roessler committed
668
      }
669
      else if (wc && (wc < ' ' || IsWPrint (wc))) /* why? */
Thomas Roessler's avatar
Thomas Roessler committed
670
      {
671
	if (state->lastchar >= state->wbuflen)
Thomas Roessler's avatar
Thomas Roessler committed
672
	{
673
	  state->wbuflen = state->lastchar + 20;
674
	  safe_realloc ((void **) &state->wbuf, state->wbuflen * sizeof (wchar_t));
Thomas Roessler's avatar
Thomas Roessler committed
675
	}
676
	memmove (state->wbuf + state->curpos + 1, state->wbuf + state->curpos, (state->lastchar - state->curpos) * sizeof (wchar_t));
677
	state->wbuf[state->curpos++] = wc;
678
	state->lastchar++;
Thomas Roessler's avatar
Thomas Roessler committed
679 680 681 682 683 684 685 686
      }
      else
      {
	mutt_flushinp ();
	BEEP ();
      }
    }
  }
687 688 689
  
  bye:
  
690
  FREE (&tempbuf);
691 692 693 694 695 696 697
  return rv;
}

void mutt_free_enter_state (ENTER_STATE **esp)
{
  if (!esp) return;
  
698 699
  FREE (&(*esp)->wbuf);
  FREE (esp);
Thomas Roessler's avatar
Thomas Roessler committed
700
}
701 702 703 704 705 706 707

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