"src/winio.c" did not exist on "e42df73644c82c657323d47a299f06cf44fdac39"
winio.c 52 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, TOP);
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 || kbinput == 127)) {
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
	    if (kbinput < 32 || kbinput == 127)
Chris Allegretta's avatar
Chris Allegretta committed
731
		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 =) 
 */
1384
void edit_update(filestruct *fileptr, topmidnone location)
Chris Allegretta's avatar
Chris Allegretta committed
1385
1386
1387
1388
{
    if (fileptr == NULL)
	return;

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

1392
	for (; goal > 0 && fileptr->prev != NULL; goal--)
Chris Allegretta's avatar
Chris Allegretta committed
1393
1394
1395
	    fileptr = fileptr->prev;
    }
    edittop = fileptr;
Chris Allegretta's avatar
Chris Allegretta committed
1396
1397
1398
1399
1400
1401
    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
1402
 * otherwise, the valid shortcut key caught.  Def is any editable text we
Chris Allegretta's avatar
Chris Allegretta committed
1403
 * want to put up by default.
1404
1405
 *
 * New arg tabs tells whether or not to allow tab completion.
Chris Allegretta's avatar
Chris Allegretta committed
1406
 */
Chris Allegretta's avatar
Chris Allegretta committed
1407
int statusq(int tabs, const shortcut *s, const char *def,
1408
1409
1410
#ifndef NANO_SMALL
		historyheadtype *which_history,
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1411
		const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1412
1413
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1414
    char *foo = charalloc(COLS - 3);
1415
    int ret;
1416
#ifndef DISABLE_TABCOMP
1417
    int list = 0;
1418
1419
#endif

1420
    bottombars(s);
Chris Allegretta's avatar
Chris Allegretta committed
1421
1422

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

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

    switch (ret) {
    case NANO_FIRSTLINE_KEY:
	do_first_line();
1442
	resetstatuspos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1443
1444
1445
	break;
    case NANO_LASTLINE_KEY:
	do_last_line();
1446
	resetstatuspos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1447
	break;
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
#ifndef DISABLE_JUSTIFY
    case NANO_PARABEGIN_KEY:
	do_para_begin();
	resetstatuspos = 1;
	break;
    case NANO_PARAEND_KEY:
	do_para_end();
	resetstatuspos = 1;
	break;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1458
    case NANO_CANCEL_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1459
	ret = -1;
1460
	resetstatuspos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1461
	break;
Chris Allegretta's avatar
Chris Allegretta committed
1462
    }
Chris Allegretta's avatar
Chris Allegretta committed
1463
    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1464
1465

#ifdef DEBUG
1466
    fprintf(stderr, "I got \"%s\"\n", answer);
Chris Allegretta's avatar
Chris Allegretta committed
1467
1468
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1469
1470
1471
1472
1473
1474
1475
1476
#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
1477
1478
1479
1480
    return ret;
}

/*
1481
 * Ask a simple yes/no question on the statusbar.  Returns 1 for Y, 0
1482
1483
 * for N, 2 for All (if all is nonzero when passed in) and -1 for abort
 * (^C).
Chris Allegretta's avatar
Chris Allegretta committed
1484
 */
Chris Allegretta's avatar
Chris Allegretta committed
1485
int do_yesno(int all, int leavecursor, const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1486
1487
{
    va_list ap;
1488
1489
    char *foo;
    int ok = -2;
Chris Allegretta's avatar
Chris Allegretta committed
1490
1491
1492
    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
1493

1494
    /* Yes, no and all are strings of any length.  Each string consists of
Chris Allegretta's avatar
Chris Allegretta committed
1495
1496
       all characters accepted as a valid character for that value.
       The first value will be the one displayed in the shortcuts. */
1497
1498
1499
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
1500

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

1505
1506
	/* Write the bottom of the screen */
	blank_bottombars();
1507

1508
	sprintf(shortstr, " %c", yesstr[0]);
1509
	wmove(bottomwin, 1, 0);
1510
	onekey(shortstr, _("Yes"), 16);
1511
1512

	if (all) {
1513
	    wmove(bottomwin, 1, 16);
1514
	    shortstr[1] = allstr[0];
1515
	    onekey(shortstr, _("All"), 16);
1516
1517
	}

1518
	wmove(bottomwin, 2, 0);
1519
	shortstr[1] = nostr[0];
1520
	onekey(shortstr, _("No"), 16);
1521

1522
	wmove(bottomwin, 2, 16);
1523
	onekey("^C", _("Cancel"), 16);
Chris Allegretta's avatar
Chris Allegretta committed
1524
    }
1525
1526

    foo = charalloc(COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1527
    va_start(ap, msg);
1528
    vsnprintf(foo, COLS, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1529
    va_end(ap);
1530
    foo[COLS - 1] = '\0';
1531

Chris Allegretta's avatar
Chris Allegretta committed
1532
    wattron(bottomwin, A_REVERSE);
1533
1534

    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1535
    mvwaddstr(bottomwin, 0, 0, foo);
1536
    free(foo);
1537

Chris Allegretta's avatar
Chris Allegretta committed
1538
    wattroff(bottomwin, A_REVERSE);
1539

Chris Allegretta's avatar
Chris Allegretta committed
1540
1541
    wrefresh(bottomwin);

1542
1543
    do {
	int kbinput = wgetch(edit);
1544
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
1545
	MEVENT mevent;
Chris Allegretta's avatar
Chris Allegretta committed
1546
#endif
1547

1548
1549
	if (kbinput == NANO_CONTROL_C)
	    ok = -1;
1550
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
	/* 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
1569
	}
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
#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
1581
1582
    blank_statusbar_refresh();

1583
    return ok;
Chris Allegretta's avatar
Chris Allegretta committed
1584
1585
}

1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
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
1608
void statusbar(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1609
1610
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1611
    char *foo;
Chris Allegretta's avatar
Chris Allegretta committed
1612
    int start_x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1613
1614
    size_t foo_len;

1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
    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
1625
1626
    assert(COLS >= 4);
    foo = charalloc(COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
1627

Chris Allegretta's avatar
Chris Allegretta committed
1628
    vsnprintf(foo, COLS - 3, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1629
1630
    va_end(ap);

Chris Allegretta's avatar
Chris Allegretta committed
1631
1632
1633
    foo[COLS - 4] = '\0';
    foo_len = strlen(foo);
    start_x = (COLS - foo_len - 4) / 2;
Chris Allegretta's avatar
Chris Allegretta committed
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643

    /* 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
1644
    free(foo);
Chris Allegretta's avatar
Chris Allegretta committed
1645
    waddstr(bottomwin, " ]");
1646

Chris Allegretta's avatar
Chris Allegretta committed
1647
    wattroff(bottomwin, A_REVERSE);
1648

Chris Allegretta's avatar
Chris Allegretta committed
1649
1650
    wrefresh(bottomwin);

1651
1652
    SET(DISABLE_CURPOS);
    statblank = 26;
Chris Allegretta's avatar
Chris Allegretta committed
1653
1654
}

1655
1656
1657
1658
1659
1660
1661
1662
/*
 * 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. */
1663
int do_cursorpos(int constant)
Chris Allegretta's avatar
Chris Allegretta committed
1664
{
1665
1666
1667
1668
    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
1669

1670
    assert(current != NULL && fileage != NULL && totlines != 0);
1671
1672
1673
1674

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

1675
1676
    for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
	assert(fileptr != NULL);
1677
	i += strlen(fileptr->data) + 1;
1678
    }
1679
    i += current_x;
1680

1681
1682
1683
1684
1685
1686
    if (constant && ISSET(DISABLE_CURPOS)) {
	UNSET(DISABLE_CURPOS);
	old_i = i;
	old_totsize = totsize;
	return 0;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1687

1688
    /* if constant is false, display the position on the statusbar
1689
1690
       unconditionally; otherwise, only display the position when the
       character values have changed */
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
    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);
1704
1705
1706
1707
1708
    }

    old_i = i;
    old_totsize = totsize;

Chris Allegretta's avatar
Chris Allegretta committed
1709
    reset_cursor();
1710
    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
1711
1712
}

1713
1714
1715
1716
1717
int do_cursorpos_void(void)
{
    return do_cursorpos(0);
}

1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
/* 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;
}

1744
1745
/* Our shortcut-list-compliant help function, which is
 * better than nothing, and dynamic! */
Chris Allegretta's avatar
Chris Allegretta committed
1746
1747
int do_help(void)
{
1748
#ifndef DISABLE_HELP
1749
    int i, page = 0, kbinput = -1, meta, no_more = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1750
    int no_help_flag = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1751
    const shortcut *oldshortcut;
Chris Allegretta's avatar
Chris Allegretta committed
1752
1753
1754

    blank_edit();
    curs_set(0);
1755
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
1756
1757
    blank_statusbar();

1758
    /* set help_text as the string to display */
1759
    help_init();
1760
    assert(help_text != NULL);
1761
1762
1763

    oldshortcut = currshortcut;

1764
    currshortcut = help_list;
1765

Chris Allegretta's avatar
Chris Allegretta committed
1766
1767
    if (ISSET(NO_HELP)) {

1768
	/* Well, if we're going to do this, we should at least
1769
	   do it the right way */
Chris Allegretta's avatar
Chris Allegretta committed
1770
	no_help_flag = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1771
	UNSET(NO_HELP);
1772
	window_init();
1773
	bottombars(help_list);
1774

Chris Allegretta's avatar
Chris Allegretta committed
1775
    } else
1776
	bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
1777
1778

    do {
1779
	const char *ptr = help_text;
1780

Chris Allegretta's avatar
Chris Allegretta committed
1781
	switch (kbinput) {
1782
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta's avatar
Chris Allegretta committed
1783
1784
1785
	case KEY_MOUSE:
	    do_mouse();
	    break;
1786
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1787
1788
1789
1790
1791
1792
1793
1794
1795
	case NANO_NEXTPAGE_KEY:
	case NANO_NEXTPAGE_FKEY:
	    if (!no_more) {
		blank_edit();
		page++;
	    }
	    break;
	case NANO_PREVPAGE_KEY:
	case NANO_PREVPAGE_FKEY:
1796
	    if (page > 0) {
Chris Allegretta's avatar
Chris Allegretta committed
1797
1798
1799
1800
1801
1802
1803
		no_more = 0;
		blank_edit();
		page--;
	    }
	    break;
	}

1804
	/* Calculate where in the text we should be, based on the page */
1805
1806
1807
	for (i = 1; i < page * (editwinrows - 1); i++) {
	    ptr += line_len(ptr);
	    if (*ptr == '\n')
Chris Allegretta's avatar
Chris Allegretta committed
1808
1809
1810
		ptr++;
	}

1811
1812
	for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
	    int j = line_len(ptr);
Chris Allegretta's avatar
Chris Allegretta committed
1813
1814

	    mvwaddnstr(edit, i, 0, ptr, j);
1815
1816
1817
	    ptr += j;
	    if (*ptr == '\n')
		ptr++;
Chris Allegretta's avatar
Chris Allegretta committed
1818
	}
1819

Chris Allegretta's avatar
Chris Allegretta committed
1820
1821
1822
1823
	if (*ptr == '\0') {
	    no_more = 1;
	    continue;
	}
1824
    } while ((kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE))) != NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
1825

1826
1827
    currshortcut = oldshortcut;

Chris Allegretta's avatar
Chris Allegretta committed
1828
    if (no_help_flag) {
1829
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
1830
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
1831
	SET(NO_HELP);
1832
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
1833
    } else
1834
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
1835
1836
1837

    curs_set(1);
    edit_refresh();
1838

1839
1840
1841
1842
1843
    /* 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
1844
1845
1846
1847
#elif defined(DISABLE_HELP)
    nano_disabled_msg();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1848
1849
1850
    return 1;
}

1851
/* Highlight the current word being replaced or spell checked. */
Chris Allegretta's avatar
Chris Allegretta committed
1852
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
1853
1854
{
    char *highlight_word = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
1855
    int x, y, word_len;
Chris Allegretta's avatar
Chris Allegretta committed
1856

Chris Allegretta's avatar
Chris Allegretta committed
1857
1858
    highlight_word =
	mallocstrcpy(highlight_word, &current->data[current_x]);
Chris Allegretta's avatar
Chris Allegretta committed
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869

#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
1870

1871
    /* adjust output when word extends beyond screen */
Chris Allegretta's avatar
Chris Allegretta committed
1872
1873

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

Chris Allegretta's avatar
Chris Allegretta committed
1876
    if ((COLS - (y - x) + word_len) > COLS) {
Chris Allegretta's avatar
Chris Allegretta committed
1877
1878
1879
1880
1881
1882
1883
	highlight_word[y - x - 1] = '$';
	highlight_word[y - x] = '\0';
    }

    /* OK display the output */

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

Chris Allegretta's avatar
Chris Allegretta committed
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
    if (highlight_flag)
	wattron(edit, A_REVERSE);

    waddstr(edit, highlight_word);

    if (highlight_flag)
	wattroff(edit, A_REVERSE);

    free(highlight_word);
}

1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
/* 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)
1910
	fprintf(stderr, "Dumping file buffer to stderr...\n");
1911
    else if (inptr == cutbuffer)
1912
	fprintf(stderr, "Dumping cutbuffer to stderr...\n");
1913
    else
1914
	fprintf(stderr, "Dumping a buffer to stderr...\n");
1915
1916
1917
1918
1919
1920
1921
1922
1923

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

#ifdef DEBUG
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1924
1925
void dump_buffer_reverse(void)
{
1926
1927
1928
1929
1930
1931
1932
1933
1934
    const filestruct *fileptr = filebot;

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

1935
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1936
#define CREDIT_LEN 53
1937
1938
#define XLCREDIT_LEN 8

1939
1940
void do_credits(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1941
    int i, j = 0, k, place = 0, start_x;
1942

1943
1944
    const char *what;
    const char *xlcredits[XLCREDIT_LEN];
1945

1946
    const char *credits[CREDIT_LEN] = { 
1947
1948
	"0",				/* "The nano text editor" */
	"1",				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
1949
1950
	VERSION,
	"",
1951
	"2",				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
1952
1953
1954
1955
1956
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
1957
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1958
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
	"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",
	"",
1974
	"3",				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
1975
1976
1977
1978
1979
1980
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
1981
	"4",				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
1982
	"Linus Torvalds",
1983
	"5",				/* "For ncurses:" */
1984
1985
1986
1987
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
1988
1989
	"6",				/* "and anyone else we forgot..." */
	"7",				/* "Thank you for using nano!\n" */
Chris Allegretta's avatar
Chris Allegretta committed
1990
	"", "", "", "",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1991
	"(c) 1999-2003 Chris Allegretta",
Chris Allegretta's avatar
Chris Allegretta committed
1992
	"", "", "", "",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1993
	"http://www.nano-editor.org/"
1994
1995
    };

1996
1997
1998
1999
2000
2001
2002
2003
2004
    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");

2005
2006
2007
2008
    curs_set(0);
    nodelay(edit, TRUE);
    blank_bottombars();
    mvwaddstr(topwin, 0, 0, hblank);
Chris Allegretta's avatar
Chris Allegretta committed
2009
2010
    blank_edit();
    wrefresh(edit);
2011
2012
2013
2014
    wrefresh(bottomwin);
    wrefresh(topwin);

    while (wgetch(edit) == ERR) {
Chris Allegretta's avatar
Chris Allegretta committed
2015
2016
	for (k = 0; k <= 1; k++) {
	    blank_edit();
Chris Allegretta's avatar
Chris Allegretta committed
2017
2018
	    for (i = editwinrows / 2 - 1; i >= (editwinrows / 2 - 1 - j);
		 i--) {
Chris Allegretta's avatar
Chris Allegretta committed
2019
2020
		mvwaddstr(edit, i * 2 - k, 0, hblank);

2021
		if (place - (editwinrows / 2 - 1 - i) < CREDIT_LEN) {
Chris Allegretta's avatar
Chris Allegretta committed
2022
		    what = credits[place - (editwinrows / 2 - 1 - i)];
2023
2024
2025
2026
2027
2028
2029
2030

		    /* 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
2031
2032
		    what = "";

2033
		start_x = COLS / 2 - strlen(what) / 2 - 1;
Chris Allegretta's avatar
Chris Allegretta committed
2034
2035
2036
2037
		mvwaddstr(edit, i * 2 - k, start_x, what);
	    }
	    usleep(700000);
	    wrefresh(edit);
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
	}
	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
2052
}
2053
#endif