winio.c 51.9 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   winio.c                                                              *
 *                                                                        *
5
 *   Copyright (C) 1999-2003 Chris Allegretta                             *
Chris Allegretta's avatar
Chris Allegretta committed
6
7
 *   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 *
8
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
9
10
11
12
13
14
15
16
17
18
19
20
21
 *   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          *
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
 *                                                                        *
 **************************************************************************/

22
23
#include "config.h"

Chris Allegretta's avatar
Chris Allegretta committed
24
25
#include <stdarg.h>
#include <string.h>
26
#include <stdlib.h>
27
#include <unistd.h>
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
28
#include <ctype.h>
29
#include <assert.h>
Chris Allegretta's avatar
Chris Allegretta committed
30
31
32
33
#include "proto.h"
#include "nano.h"

static int statblank = 0;	/* Number of keystrokes left after
34
				   we call statusbar(), before we
Chris Allegretta's avatar
Chris Allegretta committed
35
				   actually blank the statusbar */
36

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/* Read in a single input character.  If it's ignored, swallow it and go
 * on.  Otherwise, try to translate it from ASCII and extended (keypad)
 * input.  Assume nodelay(win) is FALSE. */
int get_kbinput(WINDOW *win, int *meta, int rebind_delete)
{
    int kbinput, retval;

    kbinput = get_ignored_kbinput(win);
    retval = get_accepted_kbinput(win, kbinput, meta, rebind_delete);

    return retval;
}

/* Read in a string of input characters (e. g. an escape sequence)
 * verbatim, and return the length of the string in kbinput_len.  Assume
 * nodelay(win) is FALSE. */
char *get_verbatim_kbinput(WINDOW *win, int *kbinput_len)
{
    char *verbatim_kbinput;
    int kbinput = wgetch(win);

    verbatim_kbinput = charalloc(1);
    verbatim_kbinput[0] = kbinput;
    *kbinput_len = 1;

    if (kbinput >= '0' && kbinput <= '2')
	/* Entering a three-digit decimal ASCII code from 000-255 in
	 * verbatim mode will produce the corresponding ASCII
	 * character. */
	verbatim_kbinput[0] = (char)get_ascii_kbinput(win, kbinput);
    else {
	nodelay(win, TRUE);
	while ((kbinput = wgetch(win)) != ERR) {
	    *kbinput_len++;
	    verbatim_kbinput = charealloc(verbatim_kbinput, *kbinput_len);
	    verbatim_kbinput[*kbinput_len - 1] = (char)kbinput;
	}
	nodelay(win, FALSE);
    }

#ifdef DEBUG
    fprintf(stderr, "get_verbatim_kbinput(): verbatim_kbinput = %s\n", verbatim_kbinput);
#endif
    return verbatim_kbinput;
}

/* Swallow input characters that should be quietly ignored, and return
 * the first input character that shouldn't be. */
int get_ignored_kbinput(WINDOW *win)
{
    int kbinput;

    while (1) {
	kbinput = wgetch(win);
	switch (kbinput) {
	    case ERR:
	    case KEY_RESIZE:
#ifdef PDCURSES
	    case KEY_SHIFT_L:
	    case KEY_SHIFT_R:
	    case KEY_CONTROL_L:
	    case KEY_CONTROL_R:
	    case KEY_ALT_L:
	    case KEY_ALT_R:
#endif
#ifdef DEBUG
		fprintf(stderr, "get_ignored_kbinput(): kbinput = %d\n", kbinput);
#endif
		break;
	    default:
		return kbinput;
	}
    }
}

/* Translate acceptable ASCII and extended (keypad) input.  Set meta to
 * 1 if we get a Meta sequence.  Assume nodelay(win) is FALSE. */
int get_accepted_kbinput(WINDOW *win, int kbinput, int *meta,
	int rebind_delete)
{
    *meta = 0;

    switch (kbinput) {
	case NANO_CONTROL_3: /* Escape */
	    switch (kbinput = wgetch(win)) {
		case NANO_CONTROL_3: /* Escape */
		    kbinput = wgetch(win);
		    /* Esc Esc [three-digit decimal ASCII code from
		     * 000-255] == [corresponding ASCII character];
		       Esc Esc 2 obviously can't be Ctrl-2 here */
		    if (kbinput >= '0' && kbinput <= '2')
			kbinput = get_ascii_kbinput(win, kbinput);
		    /* Esc Esc [character] == Ctrl-[character];
		     * Ctrl-Space (Ctrl-2) == Ctrl-@ == Ctrl-` */
		    else if (kbinput == ' ' || kbinput == '@' || kbinput == '`')
			kbinput = NANO_CONTROL_SPACE;
		    /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-_) */
		    else if (kbinput >= '3' && kbinput <= '7')
			kbinput -= 24;
		    /* Ctrl-8 (Ctrl-?) */
		    else if (kbinput == '8' || kbinput == '?')
			kbinput = NANO_CONTROL_8;
		    /* Ctrl-A to Ctrl-_ */
		    else if (kbinput >= 'A' && kbinput <= '_')
			kbinput -= 64;
		    /* Ctrl-A to Ctrl-Z */
		    else if (kbinput >= 'a' && kbinput <= 'z')
			kbinput -= 96;
		    break;
		/* Terminal breakage, part 1: We shouldn't get an escape
		 * sequence here for terminals that support Delete, but
		 * we do sometimes on FreeBSD.  Thank you, Wouter van
		 * Hemel. */
		case '[':
		    nodelay(win, TRUE);
		    kbinput = wgetch(win);
		    switch (kbinput) {
			case ERR:
			    kbinput = '[';
			    *meta = 1;
			    break;
			default:
			    kbinput = get_escape_seq_kbinput(win, kbinput);
		    }
		    nodelay(win, FALSE);
		    break;
		case '`':
		    /* Esc Space == Esc ` */
		    kbinput = ' ';
		    break;
		default:
		    /* Esc [character] == Meta-[character] */
		    if (isupper(kbinput))
			kbinput = tolower(kbinput);
		    *meta = 1;
	    }
	    break;
	case KEY_DOWN:
	    kbinput = NANO_DOWN_KEY;
	    break;
	case KEY_UP:
	    kbinput = NANO_UP_KEY;
	    break;
	case KEY_LEFT:
	    kbinput = NANO_BACK_KEY;
	    break;
	case KEY_RIGHT:
	    kbinput = NANO_FORWARD_KEY;
	    break;
	case KEY_HOME:
	    kbinput = NANO_HOME_KEY;
	    break;
	case KEY_BACKSPACE:
	    kbinput = NANO_BACKSPACE_KEY;
	    break;
	case KEY_DC:
	    /* Terminal breakage, part 2: We should only get KEY_DC when
	     * hitting Delete, but we get it when hitting Backspace
	     * sometimes on FreeBSD.  Thank you, Lee Nelson. */
	    kbinput = (rebind_delete) ? NANO_BACKSPACE_KEY : NANO_DELETE_KEY;
	    break;
	case KEY_IC:
	    kbinput = NANO_INSERTFILE_KEY;
	    break;
	case KEY_NPAGE:
	    kbinput = NANO_NEXTPAGE_KEY;
	    break;
	case KEY_PPAGE:
	    kbinput = NANO_PREVPAGE_KEY;
	    break;
	case KEY_ENTER:
	    kbinput = NANO_ENTER_KEY;
	    break;
	case KEY_END:
	    kbinput = NANO_END_KEY;
	    break;
	case KEY_SUSPEND:
	    kbinput = NANO_SUSPEND_KEY;
	    break;
    }
#ifdef DEBUG
    fprintf(stderr, "get_accepted_kbinput(): kbinput = %d, meta = %d\n", kbinput, *meta);
#endif
    return kbinput;
}

/* Translate a three-digit decimal ASCII code from 000-255 into the
 * corresponding ASCII character. */
int get_ascii_kbinput(WINDOW *win, int kbinput)
{
    int retval;

    switch (kbinput) {
	case '0':
	case '1':
	case '2':
	    retval = (kbinput - 48) * 100;
	    break;
	default:
	    return kbinput;
    }

    kbinput = wgetch(win);
    switch (kbinput) {
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	    retval += (kbinput - 48) * 10;
	    break;
	case '6':
	case '7':
	case '8':
	case '9':
	    if (retval < 200) {
		retval += (kbinput - 48) * 10;
		break;
	    }
	default:
	    return kbinput;
    }

    kbinput = wgetch(win);
    switch (kbinput) {
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	    retval += kbinput - 48;
	    break;
	case '6':
	case '7':
	case '8':
	case '9':
	    if (retval < 250) {
		retval += kbinput - 48;
		break;
	    }
	default:
	    return kbinput;
    }

#ifdef DEBUG
    fprintf(stderr, "get_ascii_kbinput(): kbinput = %d\n", kbinput);
#endif
    return retval;
}

/* Translate common escape sequences for some keys.  These are generated
 * when the terminal doesn't support those keys.  Assume nodelay(win) is
 * TRUE. */
int get_escape_seq_kbinput(WINDOW *win, int kbinput)
{
    switch (kbinput) {
	case '3':
	    /* Esc [ 3 ~ == kdch1 on many terminals. */
	    kbinput = get_skip_tilde_kbinput(win, kbinput, NANO_DELETE_KEY);
	    break;
    }
    return kbinput;
}

/* If there is no next character, return the passed-in error value.  If
 * the next character's a tilde, eat it and return the passed-in
 * return value.  Otherwise, return the next character.  Assume
 * nodelay(win) is TRUE. */
int get_skip_tilde_kbinput(WINDOW *win, int errval, int retval)
{
    int kbinput = wgetch(win);
    switch (kbinput) {
	case ERR:
	    return errval;
	case '~':
	    return retval;
	default:
	    return kbinput;
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
320
321
322
323
324
int do_first_line(void)
{
    current = fileage;
    placewewant = 0;
    current_x = 0;
325
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
326
327
328
329
330
331
332
333
    return 1;
}

int do_last_line(void)
{
    current = filebot;
    placewewant = 0;
    current_x = 0;
334
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
335
336
337
    return 1;
}

Chris Allegretta's avatar
Chris Allegretta committed
338
339
340
341
/* Return the placewewant associated with current_x.  That is, xplustabs
 * is the zero-based column position of the cursor.  Value is no smaller
 * than current_x. */
size_t xplustabs(void)
Chris Allegretta's avatar
Chris Allegretta committed
342
{
Chris Allegretta's avatar
Chris Allegretta committed
343
    return strnlenpt(current->data, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
344
345
}

Chris Allegretta's avatar
Chris Allegretta committed
346
347
/* Return what current_x should be, given xplustabs() for the line. */
size_t actual_x(const filestruct *fileptr, size_t xplus)
Chris Allegretta's avatar
Chris Allegretta committed
348
{
Chris Allegretta's avatar
Chris Allegretta committed
349
350
351
352
353
354
355
356
357
358
359
360
    size_t i = 0;
	/* the position in fileptr->data, returned */
    size_t length = 0;
	/* the screen display width to data[i] */
    char *c;
	/* fileptr->data + i */

    assert(fileptr != NULL && fileptr->data != NULL);

    for (c = fileptr->data; length < xplus && *c != '\0'; i++, c++) {
	if (*c == '\t')
	    length += tabsize - length % tabsize;
Chris Allegretta's avatar
Chris Allegretta committed
361
	else if (is_cntrl_char((int)*c))
Chris Allegretta's avatar
Chris Allegretta committed
362
363
364
365
366
367
	    length += 2;
	else
	    length++;
    }
    assert(length == strnlenpt(fileptr->data, i));
    assert(i <= strlen(fileptr->data));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
368

Chris Allegretta's avatar
Chris Allegretta committed
369
370
    if (length > xplus)
	i--;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
371

Chris Allegretta's avatar
Chris Allegretta committed
372
#ifdef DEBUG
373
    fprintf(stderr, "actual_x for xplus=%d returns %d\n", xplus, i);
Chris Allegretta's avatar
Chris Allegretta committed
374
#endif
Chris Allegretta's avatar
Chris Allegretta committed
375

Chris Allegretta's avatar
Chris Allegretta committed
376
    return i;
377
378
}

Chris Allegretta's avatar
Chris Allegretta committed
379
380
/* A strlen with tabs factored in, similar to xplustabs(). */
size_t strnlenpt(const char *buf, size_t size)
381
{
Chris Allegretta's avatar
Chris Allegretta committed
382
383
384
385
386
387
    size_t length = 0;

    if (buf != NULL)
	for (; *buf != '\0' && size != 0; size--, buf++) {
	    if (*buf == '\t')
		length += tabsize - (length % tabsize);
Chris Allegretta's avatar
Chris Allegretta committed
388
	    else if (is_cntrl_char((int)*buf))
Chris Allegretta's avatar
Chris Allegretta committed
389
		length += 2;
Chris Allegretta's avatar
Chris Allegretta committed
390
	    else
Chris Allegretta's avatar
Chris Allegretta committed
391
392
393
		length++;
	}
    return length;
Chris Allegretta's avatar
Chris Allegretta committed
394
395
}

Chris Allegretta's avatar
Chris Allegretta committed
396
size_t strlenpt(const char *buf)
397
{
Chris Allegretta's avatar
Chris Allegretta committed
398
    return strnlenpt(buf, -1);
399
400
}

Chris Allegretta's avatar
Chris Allegretta committed
401
402
void blank_bottombars(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
403
404
405
406
    if (!no_help()) {
	mvwaddstr(bottomwin, 1, 0, hblank);
	mvwaddstr(bottomwin, 2, 0, hblank);
    }
Chris Allegretta's avatar
Chris Allegretta committed
407
408
}

409
410
411
412
413
414
415
416
417
void blank_bottomwin(void)
{
    if (ISSET(NO_HELP))
	return;

    mvwaddstr(bottomwin, 1, 0, hblank);
    mvwaddstr(bottomwin, 2, 0, hblank);
}

Chris Allegretta's avatar
Chris Allegretta committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
void blank_edit(void)
{
    int i;
    for (i = 0; i <= editwinrows - 1; i++)
	mvwaddstr(edit, i, 0, hblank);
}


void blank_statusbar(void)
{
    mvwaddstr(bottomwin, 0, 0, hblank);
}

void blank_statusbar_refresh(void)
{
    blank_statusbar();
    wrefresh(bottomwin);
}

void check_statblank(void)
{
    if (statblank > 1)
	statblank--;
    else if (statblank == 1 && !ISSET(CONSTUPDATE)) {
	statblank--;
	blank_statusbar_refresh();
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
447
/* Repaint the statusbar when getting a character in nanogetstr().  buf
Chris Allegretta's avatar
Chris Allegretta committed
448
449
 * should be no longer than COLS - 4.
 *
Chris Allegretta's avatar
Chris Allegretta committed
450
 * Note that we must turn on A_REVERSE here, since do_help() turns it
Chris Allegretta's avatar
Chris Allegretta committed
451
 * off! */
452
void nanoget_repaint(const char *buf, const char *inputbuf, int x)
453
{
Chris Allegretta's avatar
Chris Allegretta committed
454
    int len = strlen(buf) + 2;
455
    int wid = COLS - len;
456

Chris Allegretta's avatar
Chris Allegretta committed
457
458
459
    assert(wid >= 2);
    assert(0 <= x && x <= strlen(inputbuf));

460
    wattron(bottomwin, A_REVERSE);
461
    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
462
463
464
465
466
    mvwaddstr(bottomwin, 0, 0, buf);
    waddch(bottomwin, ':');
    waddch(bottomwin, x < wid ? ' ' : '$');
    waddnstr(bottomwin, &inputbuf[wid * (x / wid)], wid);
    wmove(bottomwin, 0, (x % wid) + len);
467
    wattroff(bottomwin, A_REVERSE);
468
469
}

Chris Allegretta's avatar
Chris Allegretta committed
470
471
/* Get the input from the kb; this should only be called from
 * statusq(). */
472
int nanogetstr(int allowtabs, const char *buf, const char *def,
473
474
475
#ifndef NANO_SMALL
		historyheadtype *history_list,
#endif
476
		const shortcut *s
477
#ifndef DISABLE_TABCOMP
478
		, int *list
479
#endif
480
		)
Chris Allegretta's avatar
Chris Allegretta committed
481
482
{
    int kbinput;
483
    int meta;
Chris Allegretta's avatar
Chris Allegretta committed
484
    static int x = -1;
Chris Allegretta's avatar
Chris Allegretta committed
485
486
487
488
489
490
491
	/* the cursor position in 'answer' */
    int xend;
	/* length of 'answer', the status bar text */
    int tabbed = 0;
	/* used by input_tab() */
    const shortcut *t;

492
493
494
#ifndef NANO_SMALL
   /* for history */
    char *history = NULL;
495
    char *currentbuf = NULL;
496
    char *complete = NULL;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
497
498
499
500
501
502
503
504
505
    int last_kbinput = 0;

    /* This variable is used in the search history code.  use_cb == 0 
       means that we're using the existing history and ignoring
       currentbuf.  use_cb == 1 means that the entry in answer should be
       moved to currentbuf or restored from currentbuf to answer. 
       use_cb == 2 means that the entry in currentbuf should be moved to
       answer or restored from answer to currentbuf. */
    int use_cb = 0;
506
#endif
Chris Allegretta's avatar
Chris Allegretta committed
507
    xend = strlen(def);
Chris Allegretta's avatar
Chris Allegretta committed
508
509
510
511
512

    /* Only put x at the end of the string if it's uninitialized or if
       it would be past the end of the string as it is.  Otherwise,
       leave it alone.  This is so the cursor position stays at the same
       place if a prompt-changing toggle is pressed. */
513
    if (x == -1 || x > xend || resetstatuspos)
Chris Allegretta's avatar
Chris Allegretta committed
514
515
	x = xend;

Chris Allegretta's avatar
Chris Allegretta committed
516
    answer = charealloc(answer, xend + 1);
Chris Allegretta's avatar
Chris Allegretta committed
517
518
519
520
    if (xend > 0)
	strcpy(answer, def);
    else
	answer[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
521

522
#if !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
523
    currshortcut = s;
524
525
#endif

Chris Allegretta's avatar
Chris Allegretta committed
526
    /* Get the input! */
527

Chris Allegretta's avatar
Chris Allegretta committed
528
    nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
529

530
531
    /* Make sure any editor screen updates are displayed before getting
       input */
532
533
    wrefresh(edit);

534
    while ((kbinput = get_kbinput(bottomwin, &meta, ISSET(REBIND_DELETE))) != NANO_ENTER_KEY) {
535
	for (t = s; t != NULL; t = t->next) {
536
#ifdef DEBUG
537
	    fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, kbinput);
538
539
#endif

540
	    if (kbinput == t->val && kbinput < 32) {
541

542
#ifndef DISABLE_HELP
543
544
		/* Have to do this here, it would be too late to do it
		   in statusq() */
Chris Allegretta's avatar
Chris Allegretta committed
545
		if (kbinput == NANO_HELP_KEY || kbinput == NANO_HELP_FKEY) {
546
547
548
549
		    do_help();
		    break;
		}
#endif
Chris Allegretta's avatar
Chris Allegretta committed
550

551
		return t->val;
Chris Allegretta's avatar
Chris Allegretta committed
552
553
	    }
	}
Chris Allegretta's avatar
Chris Allegretta committed
554
	assert(0 <= x && x <= xend && xend == strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
555

Chris Allegretta's avatar
Chris Allegretta committed
556
557
558
	if (kbinput != '\t')
	    tabbed = 0;

Chris Allegretta's avatar
Chris Allegretta committed
559
	switch (kbinput) {
560
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
561
562
563
	case KEY_MOUSE:
	    do_mouse();
	    break;
564
#endif
565
	case NANO_HOME_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
566
	    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
567
	    break;
568
	case NANO_END_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
569
	    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
570
	    break;
571
	case NANO_FORWARD_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
572
573
574
	    if (x < xend)
		x++;
	    break;
575
	case NANO_DELETE_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
576
577
578
	    if (x < xend) {
		memmove(answer + x, answer + x + 1, xend - x);
		xend--;
Chris Allegretta's avatar
Chris Allegretta committed
579
580
	    }
	    break;
581
582
	case NANO_CUT_KEY:
	case NANO_UNCUT_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
583
584
585
	    null_at(&answer, 0);
	    xend = 0;
	    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
586
	    break;
587
	case NANO_BACKSPACE_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
588
589
	    if (x > 0) {
		memmove(answer + x - 1, answer + x, xend - x + 1);
Chris Allegretta's avatar
Chris Allegretta committed
590
		x--;
Chris Allegretta's avatar
Chris Allegretta committed
591
592
		xend--;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
593
	    break;
594
	case NANO_TAB_KEY:
595
596
#ifndef NANO_SMALL
	    /* tab history completion */
Chris Allegretta's avatar
Chris Allegretta committed
597
	    if (history_list != NULL) {
598
		if (!complete || last_kbinput != NANO_TAB_KEY) {
599
600
601
		    history_list->current = (historytype *)history_list;
		    history_list->len = strlen(answer);
		}
Chris Allegretta's avatar
Chris Allegretta committed
602

Chris Allegretta's avatar
Chris Allegretta committed
603
		if (history_list->len > 0) {
604
605
		    complete = get_history_completion(history_list, answer);
		    xend = strlen(complete);
Chris Allegretta's avatar
Chris Allegretta committed
606
		    x = xend;
607
608
		    answer = mallocstrcpy(answer, complete);
		}
609
	    }
610
#ifndef DISABLE_TABCOMP
611
612
	    else
#endif
613
614
#endif
#ifndef DISABLE_TABCOMP
615
616
617
618
619
620
621
622
	    if (allowtabs) {
		int shift = 0;

		answer = input_tab(answer, x, &tabbed, &shift, list);
		xend = strlen(answer);
		x += shift;
		if (x > xend)
		    x = xend;
623
624
625
	    }
#endif
	    break;
626
	case NANO_BACK_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
627
	    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
628
629
		x--;
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
630
	case NANO_UP_KEY:
631
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
632
	    if (history_list != NULL) {
633

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
634
635
636
637
638
639
640
		/* if currentbuf is NULL, or if use_cb is 1, currentbuf
		   isn't NULL, and currentbuf is different from answer,
		   it means that we're scrolling up at the top of the
		   search history, and we need to save the current
		   answer in currentbuf; do this and reset use_cb to
		   0 */
		if (currentbuf == NULL || (use_cb == 1 && strcmp(currentbuf, answer))) {
641
		    currentbuf = mallocstrcpy(currentbuf, answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
642
		    use_cb = 0;
643
644
		}

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
		/* if currentbuf isn't NULL, use_cb is 2, and currentbuf 
		   is different from answer, it means that we're
		   scrolling up at the bottom of the search history, and
		   we need to make the string in currentbuf the current
		   answer; do this, blow away currentbuf since we don't
		   need it anymore, and reset use_cb to 0 */
		if (currentbuf != NULL && use_cb == 2 && strcmp(currentbuf, answer)) {
		    answer = mallocstrcpy(answer, currentbuf);
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
		    use_cb = 0;

		/* else get older search from the history list and save
		   it in answer; if there is no older search, blank out 
		   answer */
		} else if ((history = get_history_older(history_list)) != NULL) {
662
663
664
665
666
667
668
669
670
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
#endif
671
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
672
	case NANO_DOWN_KEY:
673
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
674
	    if (history_list != NULL) {
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
675
676
677

		/* get newer search from the history list and save it 
		   in answer */
Chris Allegretta's avatar
Chris Allegretta committed
678
		if ((history = get_history_newer(history_list)) != NULL) {
679
680
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
681

David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
682
683
684
685
686
687
688
689
690
		/* if there is no newer search, we're here */
		
		/* if currentbuf isn't NULL and use_cb isn't 2, it means 
		   that we're scrolling down at the bottom of the search
		   history and we need to make the string in currentbuf
		   the current answer; do this, blow away currentbuf
		   since we don't need it anymore, and set use_cb to
		   1 */
		} else if (currentbuf != NULL && use_cb != 2) {
691
		    answer = mallocstrcpy(answer, currentbuf);
Chris Allegretta's avatar
Chris Allegretta committed
692
693
694
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
695
696
697
698
		    use_cb = 1;

		/* otherwise, if currentbuf is NULL and use_cb isn't 2, 
		   it means that we're scrolling down at the bottom of
699
700
701
702
		   the search history and the current answer (if it's
		   not blank) needs to be saved in currentbuf; do this,
		   blank out answer (if necessary), and set use_cb to
		   2 */
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
703
		} else if (use_cb != 2) {
704
705
706
707
		    if (answer[0] != '\0') {
			currentbuf = mallocstrcpy(currentbuf, answer);
			answer = mallocstrcpy(answer, "");
		    }
708
		    xend = 0;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
709
		    use_cb = 2;
710
711
712
713
		}
		x = xend;
	    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
714
	    break;
715
716
	    default:

717
		for (t = s; t != NULL; t = t->next) {
718
#ifdef DEBUG
719
		    fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput,
Chris Allegretta's avatar
Chris Allegretta committed
720
			    kbinput);
721
#endif
722
		    if (meta == 1 && (kbinput == t->val || kbinput == t->val - 32))
Chris Allegretta's avatar
Chris Allegretta committed
723
724
725
726
			/* We hit an Alt key.  Do like above.  We don't
			   just ungetch() the letter and let it get
			   caught above cause that screws the
			   keypad... */
727
			return t->val;
728
		}
Chris Allegretta's avatar
Chris Allegretta committed
729
730
731

	    if (kbinput < 32)
		break;
David Lawrence Ramsey's avatar
   
David Lawrence Ramsey committed
732
	    answer = charealloc(answer, xend + 2);
Chris Allegretta's avatar
Chris Allegretta committed
733
734
735
	    memmove(answer + x + 1, answer + x, xend - x + 1);
	    xend++;
	    answer[x] = kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
736
737
738
	    x++;

#ifdef DEBUG
739
	    fprintf(stderr, "input \'%c\' (%d)\n", kbinput, kbinput);
Chris Allegretta's avatar
Chris Allegretta committed
740
#endif
741
	} /* switch (kbinput) */
742
#ifndef NANO_SMALL
743
	last_kbinput = kbinput;
744
#endif
Chris Allegretta's avatar
Chris Allegretta committed
745
	nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
746
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
747
    } /* while (kbinput ...) */
Chris Allegretta's avatar
Chris Allegretta committed
748

Chris Allegretta's avatar
Chris Allegretta committed
749
750
751
    /* We finished putting in an answer; reset x */
    x = -1;

Chris Allegretta's avatar
Chris Allegretta committed
752
    /* Just check for a blank answer here */
753
    if (answer[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
754
755
756
757
758
	return -2;
    else
	return 0;
}

759
760
761
762
763
764
765
766
767
768
/* If modified is not already set, set it and update titlebar. */
void set_modified(void)
{
    if (!ISSET(MODIFIED)) {
	SET(MODIFIED);
	titlebar(NULL);
	wrefresh(topwin);
    }
}

769
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
770
771
{
    int namelen, space;
772
    const char *what = path;
Chris Allegretta's avatar
Chris Allegretta committed
773
774
775

    if (path == NULL)
	what = filename;
Chris Allegretta's avatar
Chris Allegretta committed
776
777

    wattron(topwin, A_REVERSE);
778

Chris Allegretta's avatar
Chris Allegretta committed
779
    mvwaddstr(topwin, 0, 0, hblank);
780
    mvwaddnstr(topwin, 0, 2, VERMSG, COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
781

782
    space = COLS - sizeof(VERMSG) - 23;
Chris Allegretta's avatar
Chris Allegretta committed
783

Chris Allegretta's avatar
Chris Allegretta committed
784
    namelen = strlen(what);
Chris Allegretta's avatar
Chris Allegretta committed
785

786
787
    if (space > 0) {
        if (what[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
      	    mvwaddnstr(topwin, 0, COLS / 2 - 6, _("New Buffer"),
			COLS / 2 + COLS % 2 - 6);
        else if (namelen > space) {
	    if (path == NULL)
		waddstr(topwin, _("  File: ..."));
	    else
		waddstr(topwin, _("   DIR: ..."));
	    waddstr(topwin, &what[namelen - space]);
	} else {
	    if (path == NULL)
		mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1),
				_("File: "));
	    else
		mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1),
				_(" DIR: "));
	    waddstr(topwin, what);
Chris Allegretta's avatar
Chris Allegretta committed
804
	}
805
    } /* If we don't have space, we shouldn't bother */
Chris Allegretta's avatar
Chris Allegretta committed
806
    if (ISSET(MODIFIED))
Chris Allegretta's avatar
Chris Allegretta committed
807
	mvwaddnstr(topwin, 0, COLS - 11, _(" Modified "), 11);
Chris Allegretta's avatar
Chris Allegretta committed
808
    else if (ISSET(VIEW_MODE))
Chris Allegretta's avatar
Chris Allegretta committed
809
	mvwaddnstr(topwin, 0, COLS - 11, _(" View "), 11);
810

Chris Allegretta's avatar
Chris Allegretta committed
811
    wattroff(topwin, A_REVERSE);
812

Chris Allegretta's avatar
Chris Allegretta committed
813
814
815
816
    wrefresh(topwin);
    reset_cursor();
}

817
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
818
{
819
    int i, j, numcols;
820
    char keystr[9];
821
822
    int slen;

Chris Allegretta's avatar
Chris Allegretta committed
823
824
825
    if (ISSET(NO_HELP))
	return;

826
827
828
829
830
831
832
833
    if (s == main_list) {
	slen = MAIN_VISIBLE;
	assert(MAIN_VISIBLE <= length_of_list(s));
    } else
	slen = length_of_list(s);

    /* There will be this many columns of shortcuts */
    numcols = (slen + (slen % 2)) / 2;
Chris Allegretta's avatar
Chris Allegretta committed
834

835
    blank_bottomwin();
836

837
838
    for (i = 0; i < numcols; i++) {
	for (j = 0; j <= 1; j++) {
Chris Allegretta's avatar
Chris Allegretta committed
839

840
	    wmove(bottomwin, 1 + j, i * (COLS / numcols));
841

842
	    /* Yucky sentinel values we can't handle a better way */
843
844
	    if (s->val == NANO_CONTROL_SPACE)
		strcpy(keystr, "^ ");
845
#ifndef NANO_SMALL
846
	    else if (s->val == KEY_UP)
847
		strncpy(keystr, _("Up"), 8);
848
849
#endif /* NANO_SMALL */
	    else if (s->val > 0) {
850
851
852
853
854
855
		if (s->val < 64)
		    sprintf(keystr, "^%c", s->val + 64);
		else
		    sprintf(keystr, "M-%c", s->val - 32);
	    } else if (s->altval > 0)
		sprintf(keystr, "M-%c", s->altval);
856

857
	    onekey(keystr, s->desc, COLS / numcols);
Chris Allegretta's avatar
Chris Allegretta committed
858

859
860
861
	    s = s->next;
	    if (s == NULL)
		goto break_completely_out;
862
	}
Chris Allegretta's avatar
Chris Allegretta committed
863
    }
864

Chris Allegretta's avatar
Chris Allegretta committed
865
  break_completely_out:
Chris Allegretta's avatar
Chris Allegretta committed
866
867
868
    wrefresh(bottomwin);
}

869
870
871
872
/* Write a shortcut key to the help area at the bottom of the window. 
 * keystroke is e.g. "^G" and desc is e.g. "Get Help".
 * We are careful to write exactly len characters, even if len is
 * very small and keystroke and desc are long. */
873
void onekey(const char *keystroke, const char *desc, int len)
Chris Allegretta's avatar
Chris Allegretta committed
874
{
875

876
877
878
879
880
881
882
883
884
885
886
    wattron(bottomwin, A_REVERSE);
    waddnstr(bottomwin, keystroke, len);
    wattroff(bottomwin, A_REVERSE);
    len -= strlen(keystroke);
    if (len > 0) {
	waddch(bottomwin, ' ');
	len--;
	waddnstr(bottomwin, desc, len);
	len -= strlen(desc);
	for (; len > 0; len--)
	    waddch(bottomwin, ' ');
Chris Allegretta's avatar
Chris Allegretta committed
887
888
889
    }
}

890
/* And so start the display update routines. */
Chris Allegretta's avatar
Chris Allegretta committed
891

892
893
#ifndef NDEBUG
int check_linenumbers(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
894
{
895
896
    int check_line = 0;
    const filestruct *filetmp;
897

898
899
900
    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;
    return check_line;
901
}
902
#endif
903

904
905
906
 /* nano scrolls horizontally within a line in chunks.  This function
  * returns the column number of the first character displayed in the
  * window when the cursor is at the given column. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
907
int get_page_start(int column)
Chris Allegretta's avatar
Chris Allegretta committed
908
909
910
911
912
{
    assert(COLS > 9);
    return column < COLS - 1 ? 0 : column - 7 - (column - 8) % (COLS - 9);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
913
914
915
916
917
918
919
/* Resets current_y, based on the position of current, and puts the
 * cursor at (current_y, current_x). */
void reset_cursor(void)
{
    const filestruct *ptr = edittop;
    size_t x;

920
921
    /* Yuck.  This condition can be true after open_file() when opening
     * the first file. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
922
923
924
925
926
927
928
929
930
931
932
933
934
    if (edittop == NULL)
	return;

    current_y = 0;

    while (ptr != current && ptr != editbot && ptr->next != NULL) {
	ptr = ptr->next;
	current_y++;
    }

    x = xplustabs();
    wmove(edit, current_y, x - get_page_start(x));
}
Chris Allegretta's avatar
Chris Allegretta committed
935

936
937
938
/* edit_add() takes care of the job of actually painting a line into
 * the edit window.  Called only from update_line().  Expects a
 * converted-to-not-have-tabs line. */
939
940
941
942
943
void edit_add(const filestruct *fileptr, int yval, int start
#ifndef NANO_SMALL
		, int virt_mark_beginx,	int virt_cur_x
#endif
		)
Chris Allegretta's avatar
Chris Allegretta committed
944
{
945
946
947
#ifdef DEBUG
    fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno,
		current->lineno);
948
949
950
#endif

    /* Just paint the string in any case (we'll add color or reverse on
Chris Allegretta's avatar
Chris Allegretta committed
951
       just the text that needs it */
952
    mvwaddnstr(edit, yval, 0, &fileptr->data[start], COLS);
953

Chris Allegretta's avatar
Chris Allegretta committed
954
#ifdef ENABLE_COLOR
955
    if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
	const colortype *tmpcolor = colorstrings;

	for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
	    int x_start;
		/* Starting column for mvwaddnstr.  Zero-based. */
	    int paintlen;
		/* number of chars to paint on this line.  There are COLS
		 * characters on a whole line. */
	    regmatch_t startmatch;	/* match position for start_regexp*/
	    regmatch_t endmatch;	/* match position for end_regexp*/

	    if (tmpcolor->bright)
		wattron(edit, A_BOLD);
	    wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
	    /* Two notes about regexec.  Return value 0 means there is a
	     * match.  Also, rm_eo is the first non-matching character
	     * after the match. */

	    /* First case, tmpcolor is a single-line expression. */
975
	    if (tmpcolor->end == NULL) {
976
977
978
979
980
981
982
983
984
		size_t k = 0;

		/* We increment k by rm_eo, to move past the end of the
		   last match.  Even though two matches may overlap, we
		   want to ignore them, so that we can highlight C-strings
		   correctly. */
		while (k < start + COLS) {
		    /* Note the fifth parameter to regexec.  It says not to
		     * match the beginning-of-line character unless
985
986
		     * k == 0.  If regexec returns nonzero, there are no
		     * more matches in the line. */
987
		    if (regexec(&tmpcolor->start, &fileptr->data[k], 1,
988
				&startmatch, k == 0 ? 0 : REG_NOTBOL))
989
			break;
990
991
992
		    /* Translate the match to the beginning of the line. */
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
993
994
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
995
			statusbar(_("Refusing 0 length regex match"));
996
		    } else if (startmatch.rm_so < start + COLS &&
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
				startmatch.rm_eo > start) {
			x_start = startmatch.rm_so - start;
			if (x_start < 0)
			    x_start = 0;
			paintlen = startmatch.rm_eo - start - x_start;
			if (paintlen > COLS - x_start)
			    paintlen = COLS - x_start;

			assert(0 <= x_start && 0 < paintlen &&
				x_start + paintlen <= COLS);
			mvwaddnstr(edit, yval, x_start,
				fileptr->data + start + x_start, paintlen);
 		    }
		    k = startmatch.rm_eo;
Chris Allegretta's avatar
Chris Allegretta committed
1011
		}
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
	    } else {
		/* This is a multi-line regexp.  There are two steps. 
		 * First, we have to see if the beginning of the line is
		 * colored by a start on an earlier line, and an end on
		 * this line or later.
		 *
		 * We find the first line before fileptr matching the
		 * start.  If every match on that line is followed by an
		 * end, then go to step two.  Otherwise, find the next line
		 * after start_line matching the end.  If that line is not
		 * before fileptr, then paint the beginning of this line. */

		const filestruct *start_line = fileptr->prev;
		    /* the first line before fileptr matching start*/
		regoff_t start_col;
		    /* where it starts in that line */
		const filestruct *end_line;
		int searched_later_lines = 0;
		    /* Used in step 2.  Have we looked for an end on
		     * lines after fileptr? */

		while (start_line != NULL &&
1034
			regexec(&tmpcolor->start, start_line->data, 1,
1035
1036
1037
				&startmatch, 0)) {
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
1038
		    if (!regexec(tmpcolor->end, start_line->data, 1,
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
				&endmatch, 0))
			goto step_two;
		    start_line = start_line->prev;
		}
		/* No start found, so skip to the next step. */
		if (start_line == NULL)
		    goto step_two;
		/* Now start_line is the first line before fileptr
		 * containing a start match.  Is there a start on this
		 * line not followed by an end on this line? */

		start_col = 0;
		while (1) {
		    start_col += startmatch.rm_so;
		    startmatch.rm_eo -= startmatch.rm_so;
1054
		    if (regexec(tmpcolor->end,
1055
1056
1057
1058
			    start_line->data + start_col + startmatch.rm_eo,
			    1, &endmatch,
			    start_col + startmatch.rm_eo == 0 ? 0 : REG_NOTBOL))
			/* No end found after this start */
1059
			break;
1060
		    start_col++;
1061
		    if (regexec(&tmpcolor->start,
1062
1063
1064
1065
			    start_line->data + start_col, 1, &startmatch,
			    REG_NOTBOL))
			/* No later start on this line. */
			goto step_two;
1066
		}
1067
1068
1069
1070
1071
1072
1073
		/* Indeed, there is a start not followed on this line by an
		 * end. */

		/* We have already checked that there is no end before
		 * fileptr and after the start.  Is there an end after
		 * the start at all?  We don't paint unterminated starts. */
		end_line = fileptr;
1074
1075
		while (end_line != NULL &&
			regexec(tmpcolor->end, end_line->data, 1, &endmatch, 0))
1076
1077
1078
		    end_line = end_line->next;

		/* No end found, or it is too early. */
1079
		if (end_line == NULL || end_line->lineno < fileptr->lineno ||
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
			(end_line == fileptr && endmatch.rm_eo <= start))
		    goto step_two;

		/* Now paint the start of fileptr. */
		paintlen = end_line != fileptr
				? COLS : endmatch.rm_eo - start;
		if (paintlen > COLS)
		    paintlen = COLS;

		assert(0 < paintlen && paintlen <= COLS);
		mvwaddnstr(edit, yval, 0, fileptr->data + start, paintlen);

		/* We have already painted the whole line. */
		if (paintlen == COLS)
		    goto skip_step_two;

  step_two:	/* Second step, we look for starts on this line. */
		start_col = 0;
		while (start_col < start + COLS) {
1099
		    if (regexec(&tmpcolor->start, fileptr->data + start_col, 1,
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
				&startmatch, start_col == 0 ? 0 : REG_NOTBOL)
			    || start_col + startmatch.rm_so >= start + COLS)
			/* No more starts on this line. */
			break;
		    /* Translate the match to be relative to the
		     * beginning of the line. */
		    startmatch.rm_so += start_col;
		    startmatch.rm_eo += start_col;

		    x_start = startmatch.rm_so - start;
		    if (x_start < 0) {
			x_start = 0;
			startmatch.rm_so = start;
1113
		    }
1114
		    if (!regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo,
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
				1, &endmatch,
				startmatch.rm_eo == 0 ? 0 : REG_NOTBOL)) {
			/* Translate the end match to be relative to the
			   beginning of the line. */
			endmatch.rm_so += startmatch.rm_eo;
			endmatch.rm_eo += startmatch.rm_eo;
			/* There is an end on this line.  But does it
			   appear on this page, and is the match more than
			   zero characters long? */
			if (endmatch.rm_eo > start &&
				endmatch.rm_eo > startmatch.rm_so) {
			    paintlen = endmatch.rm_eo - start - x_start;
			    if (x_start + paintlen > COLS)
				paintlen = COLS - x_start;

			    assert(0 <= x_start && 0 < paintlen &&
				    x_start + paintlen <= COLS);
			    mvwaddnstr(edit, yval, x_start,
				fileptr->data + start + x_start, paintlen);
1134
			}
1135
1136
1137
1138
1139
		    } else if (!searched_later_lines) {
			searched_later_lines = 1;
			/* There is no end on this line.  But we haven't
			 * yet looked for one on later lines. */
			end_line = fileptr->next;
1140
			while (end_line != NULL && regexec(tmpcolor->end,
1141
				end_line->data, 1, &endmatch, 0))
1142
1143
1144
1145
1146
1147
1148
1149
1150
			    end_line = end_line->next;
			if (end_line != NULL) {
			    assert(0 <= x_start && x_start < COLS);
			    mvwaddnstr(edit, yval, x_start,
			    		fileptr->data + start + x_start,
			    		COLS - x_start);
			    /* We painted to the end of the line, so
			     * don't bother checking any more starts. */
			    break;
1151
1152
			}
		    }
1153
1154
1155
		    start_col = startmatch.rm_so + 1;
		} /* while start_col < start + COLS */
	    } /* if (tmp_color->end != NULL) */
1156

1157
  skip_step_two:
1158
1159
1160
1161
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
	} /* for tmpcolor in colorstrings */
    }
Chris Allegretta's avatar
Chris Allegretta committed
1162
#endif				/* ENABLE_COLOR */
1163

1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
#ifndef NANO_SMALL
    if (ISSET(MARK_ISSET)
	    && (fileptr->lineno <= mark_beginbuf->lineno
		|| fileptr->lineno <= current->lineno)
	    && (fileptr->lineno >= mark_beginbuf->lineno
		|| fileptr->lineno >= current->lineno)) {
	/* fileptr is at least partially selected. */

	int x_start;
	    /* Starting column for mvwaddnstr.  Zero-based. */
	int paintlen;
	    /* number of chars to paint on this line.  There are COLS
	     * characters on a whole line. */

	if (mark_beginbuf == fileptr && current == fileptr) {
	    x_start = virt_mark_beginx < virt_cur_x ? virt_mark_beginx
	    					    : virt_cur_x;
	    paintlen = abs(virt_mark_beginx - virt_cur_x);
	} else {
	    if (mark_beginbuf->lineno < fileptr->lineno ||
		    current->lineno < fileptr->lineno)
		x_start = 0;
	    else
		x_start = mark_beginbuf == fileptr ? virt_mark_beginx
						   : virt_cur_x;
Chris Allegretta's avatar
Chris Allegretta committed
1189

1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
	    if (mark_beginbuf->lineno > fileptr->lineno ||
		    current->lineno > fileptr->lineno)
		paintlen = start + COLS;
	    else
		paintlen = mark_beginbuf == fileptr ? virt_mark_beginx
						    : virt_cur_x;
	}
	x_start -= start;
	if (x_start < 0) {
	    paintlen += x_start;
	    x_start = 0;
	}
	if (x_start + paintlen > COLS)
	    paintlen = COLS - x_start;
	if (paintlen > 0) {
1205
	    wattron(edit, A_REVERSE);
1206
1207
1208
	    assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS);
	    mvwaddnstr(edit, yval, x_start,
			fileptr->data + start + x_start, paintlen);
1209
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
1210
	}
1211
    }
1212
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
1213
1214
}

1215
1216
1217
1218
/* Just update one line in the edit buffer.  Basically a wrapper for
 * edit_add().  If fileptr != current, then index is considered 0.
 * The line will be displayed starting with fileptr->data[index].
 * Likely args are current_x or 0. */
1219
void update_line(filestruct *fileptr, int index)
Chris Allegretta's avatar
Chris Allegretta committed
1220
{
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
    int line;
	/* line in the edit window for CURSES calls */
#ifndef NANO_SMALL
    int virt_cur_x;
    int virt_mark_beginx;
#endif
    char *original;
	/* The original string fileptr->data. */
    char *converted;
	/* fileptr->data converted to have tabs and control characters
	 * expanded. */
    size_t pos;
    size_t page_start;
Chris Allegretta's avatar
Chris Allegretta committed
1234

1235
    if (fileptr == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
1236
	return;
1237

1238
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
1239

1240
1241
    /* We assume the line numbers are valid.  Is that really true? */
    assert(line < 0 || line == check_linenumbers(fileptr));
1242

1243
1244
    if (line < 0 || line >= editwinrows)
	return;
1245

1246
1247
1248
1249
1250
    /* First, blank out the line (at a minimum) */
    mvwaddstr(edit, line, 0, hblank);

    original = fileptr->data;
    converted = charalloc(strlenpt(original) + 1);
1251
1252

    /* Next, convert all the tabs to spaces, so everything else is easy.
1253
1254
1255
1256
1257
1258
     * Note the internal speller sends us index == -1. */
    index = fileptr == current && index > 0 ? strnlenpt(original, index) : 0;
#ifndef NANO_SMALL
    virt_cur_x = fileptr == current ? strnlenpt(original, current_x) : current_x;
    virt_mark_beginx = fileptr == mark_beginbuf ? strnlenpt(original, mark_beginx) : mark_beginx;
#endif
1259
1260

    pos = 0;
1261
1262
    for (; *original != '\0'; original++) {
	if (*original == '\t')
1263
	    do {
1264
		converted[pos++] = ' ';
1265
	    } while (pos % tabsize);
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
	else if (is_cntrl_char(*original)) {
	    converted[pos++] = '^';
	    if (*original == 127)
		converted[pos++] = '?';
	    else if (*original == '\n')
		/* Treat newlines (ASCII 10's) embedded in a line as encoded
	   	 * nulls (ASCII 0's); the line in question should be run
		 * through unsunder() before reaching here */
		converted[pos++] = '@';
	    else
		converted[pos++] = *original + 64;
	} else
	    converted[pos++] = *original;
1279
    }
1280
    converted[pos] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1281

Chris Allegretta's avatar
Chris Allegretta committed
1282
    /* Now, paint the line */
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
    original = fileptr->data;
    fileptr->data = converted;
    page_start = get_page_start(index);
    edit_add(fileptr, line, page_start
#ifndef NANO_SMALL
		, virt_mark_beginx, virt_cur_x
#endif
		);
    free(converted);
    fileptr->data = original;
Chris Allegretta's avatar
Chris Allegretta committed
1293

1294
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
1295
	mvwaddch(edit, line, 0, '$');
1296
1297
    if (pos > page_start + COLS)
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
1298
1299
}

1300
1301
1302
1303
1304
1305
1306
/* This function updates current, based on where current_y is;
 * reset_cursor() does the opposite. */
void update_cursor(void)
{
    int i = 0;

#ifdef DEBUG
1307
    fprintf(stderr, "Moved to (%d, %d) in edit buffer\n", current_y,
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
	    current_x);
#endif

    current = edittop;
    while (i < current_y && current->next != NULL) {
	current = current->next;
	i++;
    }

#ifdef DEBUG
1318
    fprintf(stderr, "current->data = \"%s\"\n", current->data);
1319
1320
1321
#endif
}

Chris Allegretta's avatar
Chris Allegretta committed
1322
1323
1324
1325
1326
1327
void center_cursor(void)
{
    current_y = editwinrows / 2;
    wmove(edit, current_y, current_x);
}

Chris Allegretta's avatar
Chris Allegretta committed
1328
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
1329
1330
void edit_refresh(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1331
1332
1333
    /* Neither of these conditions should occur, but they do.  edittop is
     * NULL when you open an existing file on the command line, and
     * ENABLE_COLOR is defined.  Yuck. */
Chris Allegretta's avatar
Chris Allegretta committed
1334
1335
    if (current == NULL)
	return;
Chris Allegretta's avatar
Chris Allegretta committed
1336
1337
    if (edittop == NULL)
	edittop = current;
Chris Allegretta's avatar
Chris Allegretta committed
1338

1339
1340
    if (current->lineno < edittop->lineno ||
	    current->lineno >= edittop->lineno + editwinrows)
1341
1342
1343
1344
	/* Note that edit_update() changes edittop so that
	 * current->lineno = edittop->lineno + editwinrows / 2.  Thus
	 * when it then calls edit_refresh(), there is no danger of
	 * getting an infinite loop. */
1345
	edit_update(current, CENTER);
1346
1347
    else {
	int nlines = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1348

1349
1350
	/* Don't make the cursor jump around the screen whilst updating */
	leaveok(edit, TRUE);
1351

1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
	editbot = edittop;
	while (nlines < editwinrows) {
	    update_line(editbot, current_x);
	    nlines++;
	    if (editbot->next == NULL)
		break;
	    editbot = editbot->next;
	}
	while (nlines < editwinrows) {
	    mvwaddstr(edit, nlines, 0, hblank);
	    nlines++;
	}
	/* What the hell are we expecting to update the screen if this
Chris Allegretta's avatar
Chris Allegretta committed
1365
	   isn't here?  Luck? */
1366
1367
1368
	wrefresh(edit);
	leaveok(edit, FALSE);
    }
Chris Allegretta's avatar
Chris Allegretta committed
1369
1370
}

Chris Allegretta's avatar
Chris Allegretta committed
1371
1372
/* Same as above, but touch the window first, so everything is
 * redrawn. */
1373
1374
1375
1376
1377
1378
1379
void edit_refresh_clearok(void)
{
    clearok(edit, TRUE);
    edit_refresh();
    clearok(edit, FALSE);
}

Chris Allegretta's avatar
Chris Allegretta committed
1380
/*
1381
 * Nice generic routine to update the edit buffer, given a pointer to the
Chris Allegretta's avatar
Chris Allegretta committed
1382
1383
 * file struct =) 
 */
Chris Allegretta's avatar
Chris Allegretta committed
1384
void edit_update(filestruct *fileptr, topmidbotnone location)
Chris Allegretta's avatar
Chris Allegretta committed
1385
1386
1387
1388
{
    if (fileptr == NULL)
	return;

Chris Allegretta's avatar
Chris Allegretta committed
1389
1390
    if (location != TOP) {
	int goal = location == NONE ? current_y - 1 : editwinrows / 2;
Chris Allegretta's avatar
Chris Allegretta committed
1391

Chris Allegretta's avatar
Chris Allegretta committed
1392
1393
1394
1395
	for (; goal >= 0 && fileptr->prev != NULL; goal--)
	    fileptr = fileptr->prev;
    }
    edittop = fileptr;
1396
    fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
1397
1398
1399
1400
1401
1402
1403

    edit_refresh();
}

/*
 * Ask a question on the statusbar.  Answer will be stored in answer
 * global.  Returns -1 on aborted enter, -2 on a blank string, and 0
1404
 * otherwise, the valid shortcut key caught.  Def is any editable text we
Chris Allegretta's avatar
Chris Allegretta committed
1405
 * want to put up by default.
1406
1407
 *
 * New arg tabs tells whether or not to allow tab completion.
Chris Allegretta's avatar
Chris Allegretta committed
1408
 */
Chris Allegretta's avatar
Chris Allegretta committed
1409
int statusq(int tabs, const shortcut *s, const char *def,
1410
1411
1412
#ifndef NANO_SMALL
		historyheadtype *which_history,
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1413
		const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1414
1415
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1416
    char *foo = charalloc(COLS - 3);
1417
    int ret;
1418
#ifndef DISABLE_TABCOMP
1419
    int list = 0;
1420
1421
#endif

1422
    bottombars(s);
Chris Allegretta's avatar
Chris Allegretta committed
1423
1424

    va_start(ap, msg);
Chris Allegretta's avatar
Chris Allegretta committed
1425
    vsnprintf(foo, COLS - 4, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1426
    va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
1427
    foo[COLS - 4] = '\0';
1428

1429
1430
1431
1432
1433
    ret = nanogetstr(tabs, foo, def,
#ifndef NANO_SMALL
		which_history,
#endif
		s
1434
#ifndef DISABLE_TABCOMP
1435
		, &list
1436
#endif
1437
		);
Chris Allegretta's avatar
Chris Allegretta committed
1438
    free(foo);
1439
    resetstatuspos = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1440
1441
1442
1443

    switch (ret) {
    case NANO_FIRSTLINE_KEY:
	do_first_line();
1444
	resetstatuspos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1445
1446
1447
	break;
    case NANO_LASTLINE_KEY:
	do_last_line();
1448
	resetstatuspos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1449
1450
	break;
    case NANO_CANCEL_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1451
	ret = -1;
1452
	resetstatuspos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1453
	break;
Chris Allegretta's avatar
Chris Allegretta committed
1454
    }
Chris Allegretta's avatar
Chris Allegretta committed
1455
    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1456
1457

#ifdef DEBUG
1458
    fprintf(stderr, "I got \"%s\"\n", answer);
Chris Allegretta's avatar
Chris Allegretta committed
1459
1460
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1461
1462
1463
1464
1465
1466
1467
1468
#ifndef DISABLE_TABCOMP
	/* if we've done tab completion, there might be a list of
	   filename matches on the edit window at this point; make sure
	   they're cleared off */
	if (list)
	    edit_refresh();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1469
1470
1471
1472
    return ret;
}

/*
1473
 * Ask a simple yes/no question on the statusbar.  Returns 1 for Y, 0
1474
1475
 * for N, 2 for All (if all is nonzero when passed in) and -1 for abort
 * (^C).
Chris Allegretta's avatar
Chris Allegretta committed
1476
 */
Chris Allegretta's avatar
Chris Allegretta committed
1477
int do_yesno(int all, int leavecursor, const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1478
1479
{
    va_list ap;
1480
1481
    char *foo;
    int ok = -2;
Chris Allegretta's avatar
Chris Allegretta committed
1482
1483
1484
    const char *yesstr;		/* String of yes characters accepted */
    const char *nostr;		/* Same for no */
    const char *allstr;		/* And all, surprise! */
Chris Allegretta's avatar
Chris Allegretta committed
1485

1486
    /* Yes, no and all are strings of any length.  Each string consists of
Chris Allegretta's avatar
Chris Allegretta committed
1487
1488
       all characters accepted as a valid character for that value.
       The first value will be the one displayed in the shortcuts. */
1489
1490
1491
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
1492

Jordi Mallach's avatar
   
Jordi Mallach committed
1493
    /* Remove gettext call for keybindings until we clear the thing up */
Chris Allegretta's avatar
Chris Allegretta committed
1494
    if (!ISSET(NO_HELP)) {
1495
1496
	char shortstr[3];		/* Temp string for Y, N, A */

1497
1498
	/* Write the bottom of the screen */
	blank_bottombars();
1499

1500
	sprintf(shortstr, " %c", yesstr[0]);
1501
	wmove(bottomwin, 1, 0);
1502
	onekey(shortstr, _("Yes"), 16);
1503
1504

	if (all) {
1505
	    wmove(bottomwin, 1, 16);
1506
	    shortstr[1] = allstr[0];
1507
	    onekey(shortstr, _("All"), 16);
1508
1509
	}

1510
	wmove(bottomwin, 2, 0);
1511
	shortstr[1] = nostr[0];
1512
	onekey(shortstr, _("No"), 16);
1513

1514
	wmove(bottomwin, 2, 16);
1515
	onekey("^C", _("Cancel"), 16);
Chris Allegretta's avatar
Chris Allegretta committed
1516
    }
1517
1518

    foo = charalloc(COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1519
    va_start(ap, msg);
1520
    vsnprintf(foo, COLS, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1521
    va_end(ap);
1522
    foo[COLS - 1] = '\0';
1523

Chris Allegretta's avatar
Chris Allegretta committed
1524
    wattron(bottomwin, A_REVERSE);
1525
1526

    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1527
    mvwaddstr(bottomwin, 0, 0, foo);
1528
    free(foo);
1529

Chris Allegretta's avatar
Chris Allegretta committed
1530
    wattroff(bottomwin, A_REVERSE);
1531

Chris Allegretta's avatar
Chris Allegretta committed
1532
1533
    wrefresh(bottomwin);

1534
1535
    do {
	int kbinput = wgetch(edit);
1536
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
1537
	MEVENT mevent;
Chris Allegretta's avatar
Chris Allegretta committed
1538
#endif
1539

1540
1541
	if (kbinput == NANO_CONTROL_C)
	    ok = -1;
1542
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
	/* Look ma!  We get to duplicate lots of code from do_mouse!! */
	else if (kbinput == KEY_MOUSE && getmouse(&mevent) != ERR &&
		wenclose(bottomwin, mevent.y, mevent.x) &&
		!ISSET(NO_HELP) && mevent.x < 32 &&
		mevent.y >= editwinrows + 3) {
	    int x = mevent.x /= 16;
		/* Did we click in the first column of shortcuts, or the
		   second? */
	    int y = mevent.y - editwinrows - 3;
		/* Did we click in the first row of shortcuts? */

	    assert(0 <= x && x <= 1 && 0 <= y && y <= 1);
	    /* x = 0 means they clicked Yes or No.
	       y = 0 means Yes or All. */
	    ok = -2 * x * y + x - y + 1;

	    if (ok == 2 && !all)
		ok = -2;
Chris Allegretta's avatar
Chris Allegretta committed
1561
	}
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
#endif
	/* Look for the kbinput in the yes, no and (optionally) all str */
	else if (strchr(yesstr, kbinput) != NULL)
	    ok = 1;
	else if (strchr(nostr, kbinput) != NULL)
	    ok = 0;
	else if (all && strchr(allstr, kbinput) != NULL)
	    ok = 2;
    } while (ok == -2);

    /* Then blank the statusbar. */
Chris Allegretta's avatar
Chris Allegretta committed
1573
1574
    blank_statusbar_refresh();

1575
    return ok;
Chris Allegretta's avatar
Chris Allegretta committed
1576
1577
}

1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
int total_refresh(void)
{
    clearok(edit, TRUE);
    clearok(topwin, TRUE);
    clearok(bottomwin, TRUE);
    wnoutrefresh(edit);
    wnoutrefresh(topwin);
    wnoutrefresh(bottomwin);
    doupdate();
    clearok(edit, FALSE);
    clearok(topwin, FALSE);
    clearok(bottomwin, FALSE);
    edit_refresh();
    titlebar(NULL);
    return 1;
}

void display_main_list(void)
{
    bottombars(main_list);
}

Chris Allegretta's avatar
Chris Allegretta committed
1600
void statusbar(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1601
1602
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1603
    char *foo;
Chris Allegretta's avatar
Chris Allegretta committed
1604
    int start_x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1605
1606
    size_t foo_len;

1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
    va_start(ap, msg);

    /* Curses mode is turned off.  If we use wmove() now, it will muck up
       the terminal settings.  So we just use vfprintf(). */
    if (curses_ended) {
	vfprintf(stderr, msg, ap);
	va_end(ap);
	return;
    }

Chris Allegretta's avatar
Chris Allegretta committed
1617
1618
    assert(COLS >= 4);
    foo = charalloc(COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
1619

Chris Allegretta's avatar
Chris Allegretta committed
1620
    vsnprintf(foo, COLS - 3, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1621
1622
    va_end(ap);

Chris Allegretta's avatar
Chris Allegretta committed
1623
1624
1625
    foo[COLS - 4] = '\0';
    foo_len = strlen(foo);
    start_x = (COLS - foo_len - 4) / 2;
Chris Allegretta's avatar
Chris Allegretta committed
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635

    /* Blank out line */
    blank_statusbar();

    wmove(bottomwin, 0, start_x);

    wattron(bottomwin, A_REVERSE);

    waddstr(bottomwin, "[ ");
    waddstr(bottomwin, foo);
Chris Allegretta's avatar
Chris Allegretta committed
1636
    free(foo);
Chris Allegretta's avatar
Chris Allegretta committed
1637
    waddstr(bottomwin, " ]");
1638

Chris Allegretta's avatar
Chris Allegretta committed
1639
    wattroff(bottomwin, A_REVERSE);
1640

Chris Allegretta's avatar
Chris Allegretta committed
1641
1642
    wrefresh(bottomwin);

1643
1644
    SET(DISABLE_CURPOS);
    statblank = 26;
Chris Allegretta's avatar
Chris Allegretta committed
1645
1646
}

1647
1648
1649
1650
1651
1652
1653
1654
/*
 * If constant is false, the user typed ^C so we unconditionally display
 * the cursor position.  Otherwise, we display it only if the character
 * position changed, and DISABLE_CURPOS is not set.
 *
 * If constant and DISABLE_CURPOS is set, we unset it and update old_i and
 * old_totsize.  That way, we leave the current statusbar alone, but next
 * time we will display. */
1655
int do_cursorpos(int constant)
Chris Allegretta's avatar
Chris Allegretta committed
1656
{
1657
1658
1659
1660
    const filestruct *fileptr;
    unsigned long i = 0;
    static unsigned long old_i = 0;
    static long old_totsize = -1;
Chris Allegretta's avatar
Chris Allegretta committed
1661

1662
    assert(current != NULL && fileage != NULL && totlines != 0);
1663
1664
1665
1666

    if (old_totsize == -1)
	old_totsize = totsize;

1667
1668
    for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
	assert(fileptr != NULL);
1669
	i += strlen(fileptr->data) + 1;
1670
    }
1671
    i += current_x;
1672

1673
1674
1675
1676
1677
1678
    if (constant && ISSET(DISABLE_CURPOS)) {
	UNSET(DISABLE_CURPOS);
	old_i = i;
	old_totsize = totsize;
	return 0;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1679

1680
    /* if constant is false, display the position on the statusbar
1681
1682
       unconditionally; otherwise, only display the position when the
       character values have changed */
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
    if (!constant || old_i != i || old_totsize != totsize) {
	unsigned long xpt = xplustabs() + 1;
	unsigned long cur_len = strlenpt(current->data) + 1;
	int linepct = 100 * current->lineno / totlines;
	int colpct = 100 * xpt / cur_len;
	int bytepct = totsize == 0 ? 0 : 100 * i / totsize;

	statusbar(
	    _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%ld (%d%%)"),
		    current->lineno, totlines, linepct,
		    xpt, cur_len, colpct,
		    i, totsize, bytepct);
	UNSET(DISABLE_CURPOS);
1696
1697
1698
1699
1700
    }

    old_i = i;
    old_totsize = totsize;

Chris Allegretta's avatar
Chris Allegretta committed
1701
    reset_cursor();
1702
    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
1703
1704
}

1705
1706
1707
1708
1709
int do_cursorpos_void(void)
{
    return do_cursorpos(0);
}

1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
/* Calculate the next line of help_text, starting at ptr. */
int line_len(const char *ptr)
{
    int j = 0;

    while (*ptr != '\n' && *ptr != '\0' && j < COLS - 5) {
	ptr++;
	j++;
    }
    if (j == COLS - 5) {
	/* Don't wrap at the first of two spaces following a period. */
	if (*ptr == ' ' && *(ptr + 1) == ' ')
	    j++;
	/* Don't print half a word if we've run out of space */
	while (*ptr != ' ' && j > 0) {
	    ptr--;
	    j--;
	}
	/* Word longer than COLS - 5 chars just gets broken */
	if (j == 0)
	    j = COLS - 5;
    }
    assert(j >= 0 && j <= COLS - 4 && (j > 0 || *ptr == '\n'));
    return j;
}

1736
1737
/* Our shortcut-list-compliant help function, which is
 * better than nothing, and dynamic! */
Chris Allegretta's avatar
Chris Allegretta committed
1738
1739
int do_help(void)
{
1740
#ifndef DISABLE_HELP
1741
    int i, page = 0, kbinput = -1, meta, no_more = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1742
    int no_help_flag = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1743
    const shortcut *oldshortcut;
Chris Allegretta's avatar
Chris Allegretta committed
1744
1745
1746

    blank_edit();
    curs_set(0);
1747
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
1748
1749
    blank_statusbar();

1750
    /* set help_text as the string to display */
1751
    help_init();
1752
    assert(help_text != NULL);
1753
1754
1755

    oldshortcut = currshortcut;

1756
    currshortcut = help_list;
1757

Chris Allegretta's avatar
Chris Allegretta committed
1758
1759
    if (ISSET(NO_HELP)) {

1760
	/* Well, if we're going to do this, we should at least
1761
	   do it the right way */
Chris Allegretta's avatar
Chris Allegretta committed
1762
	no_help_flag = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1763
	UNSET(NO_HELP);
1764
	window_init();
1765
	bottombars(help_list);
1766

Chris Allegretta's avatar
Chris Allegretta committed
1767
    } else
1768
	bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
1769
1770

    do {
1771
	const char *ptr = help_text;
1772

Chris Allegretta's avatar
Chris Allegretta committed
1773
	switch (kbinput) {
1774
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta's avatar
Chris Allegretta committed
1775
1776
1777
	case KEY_MOUSE:
	    do_mouse();
	    break;
1778
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1779
1780
1781
1782
1783
1784
1785
1786
1787
	case NANO_NEXTPAGE_KEY:
	case NANO_NEXTPAGE_FKEY:
	    if (!no_more) {
		blank_edit();
		page++;
	    }
	    break;
	case NANO_PREVPAGE_KEY:
	case NANO_PREVPAGE_FKEY:
1788
	    if (page > 0) {
Chris Allegretta's avatar
Chris Allegretta committed
1789
1790
1791
1792
1793
1794
1795
		no_more = 0;
		blank_edit();
		page--;
	    }
	    break;
	}

1796
	/* Calculate where in the text we should be, based on the page */
1797
1798
1799
	for (i = 1; i < page * (editwinrows - 1); i++) {
	    ptr += line_len(ptr);
	    if (*ptr == '\n')
Chris Allegretta's avatar
Chris Allegretta committed
1800
1801
1802
		ptr++;
	}

1803
1804
	for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
	    int j = line_len(ptr);
Chris Allegretta's avatar
Chris Allegretta committed
1805
1806

	    mvwaddnstr(edit, i, 0, ptr, j);
1807
1808
1809
	    ptr += j;
	    if (*ptr == '\n')
		ptr++;
Chris Allegretta's avatar
Chris Allegretta committed
1810
	}
1811

Chris Allegretta's avatar
Chris Allegretta committed
1812
1813
1814
1815
	if (*ptr == '\0') {
	    no_more = 1;
	    continue;
	}
1816
    } while ((kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE))) != NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
1817

1818
1819
    currshortcut = oldshortcut;

Chris Allegretta's avatar
Chris Allegretta committed
1820
    if (no_help_flag) {
1821
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
1822
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
1823
	SET(NO_HELP);
1824
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
1825
    } else
1826
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
1827
1828
1829

    curs_set(1);
    edit_refresh();
1830

1831
1832
1833
1834
1835
    /* The help_init() at the beginning allocated help_text, which has
       now been written to screen. */
    free(help_text);
    help_text = NULL;

Chris Allegretta's avatar
Chris Allegretta committed
1836
1837
1838
1839
#elif defined(DISABLE_HELP)
    nano_disabled_msg();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1840
1841
1842
    return 1;
}

1843
/* Highlight the current word being replaced or spell checked. */
Chris Allegretta's avatar
Chris Allegretta committed
1844
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
1845
1846
{
    char *highlight_word = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
1847
    int x, y, word_len;
Chris Allegretta's avatar
Chris Allegretta committed
1848

Chris Allegretta's avatar
Chris Allegretta committed
1849
1850
    highlight_word =
	mallocstrcpy(highlight_word, &current->data[current_x]);
Chris Allegretta's avatar
Chris Allegretta committed
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861

#ifdef HAVE_REGEX_H
    if (ISSET(USE_REGEXP))
	/* if we're using regexps, the highlight is the length of the
	   search result, not the length of the regexp string */
	word_len = regmatches[0].rm_eo - regmatches[0].rm_so;
    else
#endif
	word_len = strlen(word);

    highlight_word[word_len] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1862

1863
    /* adjust output when word extends beyond screen */
Chris Allegretta's avatar
Chris Allegretta committed
1864
1865

    x = xplustabs();
Chris Allegretta's avatar
Chris Allegretta committed
1866
    y = get_page_start(x) + COLS;
Chris Allegretta's avatar
Chris Allegretta committed
1867

Chris Allegretta's avatar
Chris Allegretta committed
1868
    if ((COLS - (y - x) + word_len) > COLS) {
Chris Allegretta's avatar
Chris Allegretta committed
1869
1870
1871
1872
1873
1874
1875
	highlight_word[y - x - 1] = '$';
	highlight_word[y - x] = '\0';
    }

    /* OK display the output */

    reset_cursor();
Chris Allegretta's avatar
Chris Allegretta committed
1876

Chris Allegretta's avatar
Chris Allegretta committed
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
    if (highlight_flag)
	wattron(edit, A_REVERSE);

    waddstr(edit, highlight_word);

    if (highlight_flag)
	wattroff(edit, A_REVERSE);

    free(highlight_word);
}

1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
/* Fix editbot, based on the assumption that edittop is correct. */
void fix_editbot(void)
{
    int i;

    editbot = edittop;
    for (i = 0; i < editwinrows && editbot->next != NULL; i++)
	editbot = editbot->next;
}

#ifdef DEBUG
/* Dump the current file structure to stderr */
void dump_buffer(const filestruct *inptr) {
    if (inptr == fileage)
1902
	fprintf(stderr, "Dumping file buffer to stderr...\n");
1903
    else if (inptr == cutbuffer)
1904
	fprintf(stderr, "Dumping cutbuffer to stderr...\n");
1905
    else
1906
	fprintf(stderr, "Dumping a buffer to stderr...\n");
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925

    while (inptr != NULL) {
	fprintf(stderr, "(%d) %s\n", inptr->lineno, inptr->data);
	inptr = inptr->next;
    }
}
#endif /* DEBUG */

#ifdef DEBUG
void dump_buffer_reverse(void) {
    const filestruct *fileptr = filebot;

    while (fileptr != NULL) {
	fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data);
	fileptr = fileptr->prev;
    }
}
#endif /* DEBUG */

1926
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1927
#define CREDIT_LEN 53
1928
1929
#define XLCREDIT_LEN 8

1930
1931
void do_credits(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1932
    int i, j = 0, k, place = 0, start_x;
1933

1934
1935
    const char *what;
    const char *xlcredits[XLCREDIT_LEN];
1936

1937
    const char *credits[CREDIT_LEN] = { 
1938
1939
	"0",				/* "The nano text editor" */
	"1",				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
1940
1941
	VERSION,
	"",
1942
	"2",				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
1943
1944
1945
1946
1947
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
1948
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1949
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
	"Ken Tyler",
	"Sven Guckes",
	"Florian Knig",
	"Pauli Virtanen",
	"Daniele Medri",
	"Clement Laforet",
	"Tedi Heriyanto",
	"Bill Soudan",
	"Christian Weisgerber",
	"Erik Andersen",
	"Big Gaute",
	"Joshua Jensen",
	"Ryan Krebs",
	"Albert Chin",
	"",
1965
	"3",				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
1966
1967
1968
1969
1970
1971
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
1972
	"4",				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
1973
	"Linus Torvalds",
1974
	"5",				/* "For ncurses:" */
1975
1976
1977
1978
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
1979
1980
	"6",				/* "and anyone else we forgot..." */
	"7",				/* "Thank you for using nano!\n" */
Chris Allegretta's avatar
Chris Allegretta committed
1981
1982
1983
	"", "", "", "",
	"(c) 1999-2002 Chris Allegretta",
	"", "", "", "",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1984
	"http://www.nano-editor.org/"
1985
1986
    };

1987
1988
1989
1990
1991
1992
1993
1994
1995
    xlcredits[0] = _("The nano text editor");
    xlcredits[1] = _("version ");
    xlcredits[2] = _("Brought to you by:");
    xlcredits[3] = _("Special thanks to:");
    xlcredits[4] = _("The Free Software Foundation");
    xlcredits[5] = _("For ncurses:");
    xlcredits[6] = _("and anyone else we forgot...");
    xlcredits[7] = _("Thank you for using nano!\n");

1996
1997
1998
1999
    curs_set(0);
    nodelay(edit, TRUE);
    blank_bottombars();
    mvwaddstr(topwin, 0, 0, hblank);
Chris Allegretta's avatar
Chris Allegretta committed
2000
2001
    blank_edit();
    wrefresh(edit);
2002
2003
2004
2005
    wrefresh(bottomwin);
    wrefresh(topwin);

    while (wgetch(edit) == ERR) {
Chris Allegretta's avatar
Chris Allegretta committed
2006
2007
	for (k = 0; k <= 1; k++) {
	    blank_edit();
Chris Allegretta's avatar
Chris Allegretta committed
2008
2009
	    for (i = editwinrows / 2 - 1; i >= (editwinrows / 2 - 1 - j);
		 i--) {
Chris Allegretta's avatar
Chris Allegretta committed
2010
2011
		mvwaddstr(edit, i * 2 - k, 0, hblank);

2012
		if (place - (editwinrows / 2 - 1 - i) < CREDIT_LEN) {
Chris Allegretta's avatar
Chris Allegretta committed
2013
		    what = credits[place - (editwinrows / 2 - 1 - i)];
2014
2015
2016
2017
2018
2019
2020
2021

		    /* God I've missed hacking.  If what is exactly
			1 char long, it's a sentinel for a translated
			string, so use that instead.  This means no
			thanking people with 1 character long names ;-) */
		    if (strlen(what) == 1)
			what = xlcredits[atoi(what)];
		} else
Chris Allegretta's avatar
Chris Allegretta committed
2022
2023
		    what = "";

2024
		start_x = COLS / 2 - strlen(what) / 2 - 1;
Chris Allegretta's avatar
Chris Allegretta committed
2025
2026
2027
2028
		mvwaddstr(edit, i * 2 - k, start_x, what);
	    }
	    usleep(700000);
	    wrefresh(edit);
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
	}
	if (j < editwinrows / 2 - 1)
	    j++;

	place++;

	if (place >= CREDIT_LEN + editwinrows / 2)
	    break;
    }

    nodelay(edit, FALSE);
    curs_set(1);
    display_main_list();
    total_refresh();
Chris Allegretta's avatar
Chris Allegretta committed
2043
}
2044
#endif