winio.c 44 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-2002 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

Chris Allegretta's avatar
Chris Allegretta committed
37
38
39
40
41
int do_first_line(void)
{
    current = fileage;
    placewewant = 0;
    current_x = 0;
42
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
43
44
45
46
47
48
49
50
    return 1;
}

int do_last_line(void)
{
    current = filebot;
    placewewant = 0;
    current_x = 0;
51
    edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
52
53
54
    return 1;
}

Chris Allegretta's avatar
Chris Allegretta committed
55
56
57
58
/* 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
59
{
Chris Allegretta's avatar
Chris Allegretta committed
60
    return strnlenpt(current->data, current_x);
Chris Allegretta's avatar
Chris Allegretta committed
61
62
}

Chris Allegretta's avatar
Chris Allegretta committed
63
64
/* 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
65
{
Chris Allegretta's avatar
Chris Allegretta committed
66
67
68
69
70
71
72
73
74
75
76
77
    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
78
	else if (is_cntrl_char((int)*c))
Chris Allegretta's avatar
Chris Allegretta committed
79
80
81
82
83
84
	    length += 2;
	else
	    length++;
    }
    assert(length == strnlenpt(fileptr->data, i));
    assert(i <= strlen(fileptr->data));
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
85

Chris Allegretta's avatar
Chris Allegretta committed
86
87
    if (length > xplus)
	i--;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
88

Chris Allegretta's avatar
Chris Allegretta committed
89
#ifdef DEBUG
Chris Allegretta's avatar
Chris Allegretta committed
90
    fprintf(stderr, _("actual_x for xplus=%d returns %d\n"), xplus, i);
Chris Allegretta's avatar
Chris Allegretta committed
91
#endif
Chris Allegretta's avatar
Chris Allegretta committed
92

Chris Allegretta's avatar
Chris Allegretta committed
93
    return i;
94
95
}

Chris Allegretta's avatar
Chris Allegretta committed
96
97
/* A strlen with tabs factored in, similar to xplustabs(). */
size_t strnlenpt(const char *buf, size_t size)
98
{
Chris Allegretta's avatar
Chris Allegretta committed
99
100
101
102
103
104
    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
105
	    else if (is_cntrl_char((int)*buf))
Chris Allegretta's avatar
Chris Allegretta committed
106
		length += 2;
Chris Allegretta's avatar
Chris Allegretta committed
107
	    else
Chris Allegretta's avatar
Chris Allegretta committed
108
109
110
		length++;
	}
    return length;
Chris Allegretta's avatar
Chris Allegretta committed
111
112
}

Chris Allegretta's avatar
Chris Allegretta committed
113
size_t strlenpt(const char *buf)
114
{
Chris Allegretta's avatar
Chris Allegretta committed
115
    return strnlenpt(buf, -1);
116
117
}

Chris Allegretta's avatar
Chris Allegretta committed
118
119
void blank_bottombars(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
120
121
122
123
    if (!no_help()) {
	mvwaddstr(bottomwin, 1, 0, hblank);
	mvwaddstr(bottomwin, 2, 0, hblank);
    }
Chris Allegretta's avatar
Chris Allegretta committed
124
125
}

126
127
128
129
130
131
132
133
134
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
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
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
164
165
166
167
168
/* Repaint the statusbar when getting a character in nanogetstr.  buf
 * should be no longer than COLS - 4.
 *
 * Note that we must turn on A_REVERSE here, since do_help turns it
 * off! */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
169
void nanoget_repaint(const char *buf, const char *inputbuf, int x)
170
{
Chris Allegretta's avatar
Chris Allegretta committed
171
    int len = strlen(buf) + 2;
172
    int wid = COLS - len;
173

Chris Allegretta's avatar
Chris Allegretta committed
174
175
176
    assert(wid >= 2);
    assert(0 <= x && x <= strlen(inputbuf));

177
    wattron(bottomwin, A_REVERSE);
178
    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
179
180
181
182
183
    mvwaddstr(bottomwin, 0, 0, buf);
    waddch(bottomwin, ':');
    waddch(bottomwin, x < wid ? ' ' : '$');
    waddnstr(bottomwin, &inputbuf[wid * (x / wid)], wid);
    wmove(bottomwin, 0, (x % wid) + len);
184
    wattroff(bottomwin, A_REVERSE);
185
186
}

Chris Allegretta's avatar
Chris Allegretta committed
187
188
/* Get the input from the kb; this should only be called from
 * statusq(). */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
189
int nanogetstr(int allowtabs, const char *buf, const char *def,
190
191
192
#ifndef NANO_SMALL
		historyheadtype *history_list,
#endif
193
		const shortcut *s
194
#ifndef DISABLE_TABCOMP
195
		, int *list
196
#endif
197
		)
Chris Allegretta's avatar
Chris Allegretta committed
198
199
200
201
202
203
204
205
206
207
{
    int kbinput;
    int x;
	/* the cursor position in 'answer' */
    int xend;
	/* length of 'answer', the status bar text */
    int tabbed = 0;
	/* used by input_tab() */
    const shortcut *t;

208
209
210
211
212
213
#ifndef NANO_SMALL
   /* for history */
    char *history = NULL;
    char *complete = NULL;
    int last_kbinput = 0;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
214
215
216
217
218
219
220
    xend = strlen(def);
    x = xend;
    answer = (char *)nrealloc(answer, xend + 1);
    if (xend > 0)
	strcpy(answer, def);
    else
	answer[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
221

222
#if !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
223
    currshortcut = s;
224
225
#endif

Chris Allegretta's avatar
Chris Allegretta committed
226
    /* Get the input! */
227

Chris Allegretta's avatar
Chris Allegretta committed
228
    nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
229

230
231
    /* Make sure any editor screen updates are displayed before getting
       input */
232
233
    wrefresh(edit);

Chris Allegretta's avatar
Chris Allegretta committed
234
    while ((kbinput = wgetch(bottomwin)) != 13) {
235
	for (t = s; t != NULL; t = t->next) {
236
237
238
239
#ifdef DEBUG
	    fprintf(stderr, _("Aha! \'%c\' (%d)\n"), kbinput, kbinput);
#endif

240
	    if (kbinput == t->val && kbinput < 32) {
241

242
#ifndef DISABLE_HELP
243
244
		/* Have to do this here, it would be too late to do it
		   in statusq() */
Chris Allegretta's avatar
Chris Allegretta committed
245
		if (kbinput == NANO_HELP_KEY || kbinput == NANO_HELP_FKEY) {
246
247
248
249
		    do_help();
		    break;
		}
#endif
250
		return t->val;
Chris Allegretta's avatar
Chris Allegretta committed
251
252
	    }
	}
Chris Allegretta's avatar
Chris Allegretta committed
253
	assert(0 <= x && x <= xend && xend == strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
254

Chris Allegretta's avatar
Chris Allegretta committed
255
256
257
	if (kbinput != '\t')
	    tabbed = 0;

Chris Allegretta's avatar
Chris Allegretta committed
258
	switch (kbinput) {
259

Chris Allegretta's avatar
Chris Allegretta committed
260
	    /* Stuff we want to equate with <enter>, ASCII 13 */
261
	case 343:
262
263
	    ungetch(13);	/* Enter on iris-ansi $TERM, sometimes */
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
264
	    /* Stuff we want to ignore */
265
266
267
#ifdef PDCURSES
	case 541:
	case 542:
Chris Allegretta's avatar
Chris Allegretta committed
268
	case 543:		/* Right ctrl again */
269
	case 544:
Chris Allegretta's avatar
Chris Allegretta committed
270
	case 545:		/* Right alt again */
271
272
	    break;
#endif
273
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
274
275
276
	case KEY_MOUSE:
	    do_mouse();
	    break;
277
#endif
278
	case NANO_HOME_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
279
	case KEY_HOME:
Chris Allegretta's avatar
Chris Allegretta committed
280
	    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
281
	    break;
282
	case NANO_END_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
283
	case KEY_END:
Chris Allegretta's avatar
Chris Allegretta committed
284
	    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
285
286
	    break;
	case KEY_RIGHT:
287
	case NANO_FORWARD_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
288
289
290
291
	    if (x < xend)
		x++;
	    break;
	case NANO_CONTROL_D:
Chris Allegretta's avatar
Chris Allegretta committed
292
293
294
	    if (x < xend) {
		memmove(answer + x, answer + x + 1, xend - x);
		xend--;
Chris Allegretta's avatar
Chris Allegretta committed
295
296
297
298
	    }
	    break;
	case NANO_CONTROL_K:
	case NANO_CONTROL_U:
Chris Allegretta's avatar
Chris Allegretta committed
299
300
301
	    null_at(&answer, 0);
	    xend = 0;
	    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
302
303
304
305
	    break;
	case KEY_BACKSPACE:
	case 127:
	case NANO_CONTROL_H:
Chris Allegretta's avatar
Chris Allegretta committed
306
307
	    if (x > 0) {
		memmove(answer + x - 1, answer + x, xend - x + 1);
Chris Allegretta's avatar
Chris Allegretta committed
308
		x--;
Chris Allegretta's avatar
Chris Allegretta committed
309
310
		xend--;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
311
312
	    break;
	case NANO_CONTROL_I:
313
314
315
316
317
318
319
#ifndef NANO_SMALL
	    /* tab history completion */
	    if (history_list) {
		if ((!complete) || (last_kbinput != NANO_CONTROL_I)) {
		    history_list->current = (historytype *)history_list;
		    history_list->len = strlen(answer);
		}
Chris Allegretta's avatar
Chris Allegretta committed
320

321
322
323
		if (history_list->len) {
		    complete = get_history_completion(history_list, answer);
		    xend = strlen(complete);
Chris Allegretta's avatar
Chris Allegretta committed
324
		    x = xend;
325
326
		    answer = mallocstrcpy(answer, complete);
		}
327
	    }
328
329
330
#ifndef DISABLE_TABCOMP
	    else {
#endif
331
#endif
332
333
334
335
336
337
338
339
340
341
342
343
344
#ifndef DISABLE_TABCOMP
		if (allowtabs) {
		    int shift = 0;

		    answer = input_tab(answer, x, &tabbed, &shift, list);
		    xend = strlen(answer);
		    x += shift;
		    if (x > xend)
			x = xend;
		}
	    }
#endif
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
345
	case KEY_LEFT:
346
	case NANO_BACK_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
347
	    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
348
349
350
		x--;
	    break;
	case KEY_UP:
351
352
353
354
355
356
357
358
359
360
361
362
363
364
#ifndef NANO_SMALL
	    if (history_list) {
		/* get older search from the history list */
		if ((history = get_history_older(history_list))) {
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
	    break;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
365
	case KEY_DOWN:
366
367
368
369
370
371
372
373
374
375
376
377
378
#ifndef NANO_SMALL
	    if (history_list) {
		/* get newer search from the history list */
		if ((history = get_history_newer(history_list))) {
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
379
	    break;
380
381
382
	case KEY_DC:
	    goto do_deletekey;

Chris Allegretta's avatar
Chris Allegretta committed
383
384
	case 27:
	    switch (kbinput = wgetch(edit)) {
385
	    case 'O':
Chris Allegretta's avatar
Chris Allegretta committed
386
		switch (kbinput = wgetch(edit)) {
387
		case 'F':
Chris Allegretta's avatar
Chris Allegretta committed
388
		    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
389
		    break;
390
		case 'H':
Chris Allegretta's avatar
Chris Allegretta committed
391
		    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
392
393
394
		    break;
		}
		break;
395
	    case '[':
Chris Allegretta's avatar
Chris Allegretta committed
396
397
398
399
400
401
		switch (kbinput = wgetch(edit)) {
		case 'C':
		    if (x < xend)
			x++;
		    break;
		case 'D':
Chris Allegretta's avatar
Chris Allegretta committed
402
		    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
403
404
			x--;
		    break;
405
406
		case '1':
		case '7':
Chris Allegretta's avatar
Chris Allegretta committed
407
		    x = 0;
408
409
410
		    goto skip_tilde;
		case '3':
		  do_deletekey:
Chris Allegretta's avatar
Chris Allegretta committed
411
412
413
		    if (x < xend) {
			memmove(answer + x, answer + x + 1, xend - x);
			xend--;
Chris Allegretta's avatar
Chris Allegretta committed
414
		    }
415
416
417
		    goto skip_tilde;
		case '4':
		case '8':
Chris Allegretta's avatar
Chris Allegretta committed
418
		    x = xend;
419
420
		    goto skip_tilde;
		  skip_tilde:
Chris Allegretta's avatar
Chris Allegretta committed
421
422
		    nodelay(edit, TRUE);
		    kbinput = wgetch(edit);
423
		    if (kbinput == '~' || kbinput == ERR)
Chris Allegretta's avatar
Chris Allegretta committed
424
425
426
427
			kbinput = -1;
		    nodelay(edit, FALSE);
		    break;
		}
Chris Allegretta's avatar
Chris Allegretta committed
428
		break;
429
430
	    default:

431
		for (t = s; t != NULL; t = t->next) {
432
#ifdef DEBUG
Chris Allegretta's avatar
Chris Allegretta committed
433
434
		    fprintf(stderr, _("Aha! \'%c\' (%d)\n"), kbinput,
			    kbinput);
435
#endif
436
		    if (kbinput == t->val || kbinput == t->val - 32) {
437
438
439
			/* 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... */
440
			return t->val;
441
442
		    }
		}
Chris Allegretta's avatar
Chris Allegretta committed
443
444
445
446
447
448
	    }
	    break;

	default:
	    if (kbinput < 32)
		break;
Chris Allegretta's avatar
Chris Allegretta committed
449
450
451
452
	    answer = nrealloc(answer, xend + 2);
	    memmove(answer + x + 1, answer + x, xend - x + 1);
	    xend++;
	    answer[x] = kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
453
454
455
456
457
	    x++;

#ifdef DEBUG
	    fprintf(stderr, _("input \'%c\' (%d)\n"), kbinput, kbinput);
#endif
458
	} /* switch (kbinput) */
459
#ifndef NANO_SMALL
460
	last_kbinput = kbinput;
461
#endif
Chris Allegretta's avatar
Chris Allegretta committed
462
	nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
463
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
464
    } /* while (kbinput ...) */
Chris Allegretta's avatar
Chris Allegretta committed
465

Chris Allegretta's avatar
Chris Allegretta committed
466
    /* In Pico mode, just check for a blank answer here */
467
    if (answer[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
468
469
470
471
472
	return -2;
    else
	return 0;
}

473
474
475
476
477
478
479
480
481
482
/* If modified is not already set, set it and update titlebar. */
void set_modified(void)
{
    if (!ISSET(MODIFIED)) {
	SET(MODIFIED);
	titlebar(NULL);
	wrefresh(topwin);
    }
}

Chris Allegretta's avatar
Chris Allegretta committed
483
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
484
485
{
    int namelen, space;
Chris Allegretta's avatar
Chris Allegretta committed
486
    const char *what = path;
Chris Allegretta's avatar
Chris Allegretta committed
487
488
489

    if (path == NULL)
	what = filename;
Chris Allegretta's avatar
Chris Allegretta committed
490
491

    wattron(topwin, A_REVERSE);
492

Chris Allegretta's avatar
Chris Allegretta committed
493
    mvwaddstr(topwin, 0, 0, hblank);
494
    mvwaddnstr(topwin, 0, 2, VERMSG, COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
495

Chris Allegretta's avatar
Chris Allegretta committed
496
    space = COLS - sizeof(VERMSG) - 22;
Chris Allegretta's avatar
Chris Allegretta committed
497

Chris Allegretta's avatar
Chris Allegretta committed
498
    namelen = strlen(what);
Chris Allegretta's avatar
Chris Allegretta committed
499

500
501
    if (space > 0) {
        if (what[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
      	    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
518
	}
519
    } /* If we don't have space, we shouldn't bother */
Chris Allegretta's avatar
Chris Allegretta committed
520
    if (ISSET(MODIFIED))
Chris Allegretta's avatar
Chris Allegretta committed
521
	mvwaddnstr(topwin, 0, COLS - 11, _(" Modified "), 11);
Chris Allegretta's avatar
Chris Allegretta committed
522
    else if (ISSET(VIEW_MODE))
Chris Allegretta's avatar
Chris Allegretta committed
523
	mvwaddnstr(topwin, 0, COLS - 11, _(" View "), 11);
524

Chris Allegretta's avatar
Chris Allegretta committed
525
    wattroff(topwin, A_REVERSE);
526

Chris Allegretta's avatar
Chris Allegretta committed
527
528
529
530
    wrefresh(topwin);
    reset_cursor();
}

531
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
532
{
533
    int i, j, numcols;
534
    char keystr[4];
535
536
    int slen;

Chris Allegretta's avatar
Chris Allegretta committed
537
538
539
    if (ISSET(NO_HELP))
	return;

540
541
542
543
544
545
546
547
    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
548

549
    blank_bottomwin();
550

551
552
    for (i = 0; i < numcols; i++) {
	for (j = 0; j <= 1; j++) {
Chris Allegretta's avatar
Chris Allegretta committed
553

554
	    wmove(bottomwin, 1 + j, i * (COLS / numcols));
555

556
	    /* Yucky sentinel values we can't handle a better way */
557
558
	    if (s->val == NANO_CONTROL_SPACE)
		strcpy(keystr, "^ ");
559
#ifndef NANO_SMALL
560
561
562
563
	    else if (s->val == KEY_UP)
		strcpy(keystr, _("Up"));
#endif /* NANO_SMALL */
	    else if (s->val > 0) {
564
565
566
567
568
569
		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);
570

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

573
574
575
	    s = s->next;
	    if (s == NULL)
		goto break_completely_out;
576
	}
Chris Allegretta's avatar
Chris Allegretta committed
577
    }
578

Chris Allegretta's avatar
Chris Allegretta committed
579
  break_completely_out:
Chris Allegretta's avatar
Chris Allegretta committed
580
581
582
    wrefresh(bottomwin);
}

583
584
585
586
587
/* 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. */
void onekey(const char *keystroke, const char *desc, int len)
Chris Allegretta's avatar
Chris Allegretta committed
588
{
589

590
591
592
593
594
595
596
597
598
599
600
    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
601
602
603
    }
}

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

606
607
#ifndef NDEBUG
int check_linenumbers(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
608
{
609
610
    int check_line = 0;
    const filestruct *filetmp;
611

612
613
614
    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;
    return check_line;
615
}
616
#endif
617

618
619
620
 /* 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
621
int get_page_start(int column)
Chris Allegretta's avatar
Chris Allegretta committed
622
623
624
625
626
{
    assert(COLS > 9);
    return column < COLS - 1 ? 0 : column - 7 - (column - 8) % (COLS - 9);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
627
628
629
630
631
632
633
/* 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;

634
635
    /* Yuck.  This condition can be true after open_file() when opening
     * the first file. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
636
637
638
639
640
641
642
643
644
645
646
647
648
    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
649

650
651
652
/* 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. */
653
654
655
656
657
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
658
{
659
660
661
#ifdef DEBUG
    fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno,
		current->lineno);
662
663
664
#endif

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

Chris Allegretta's avatar
Chris Allegretta committed
668
#ifdef ENABLE_COLOR
669
    if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
	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. */
	    regex_t start_regexp;	/* Compiled search regexp */
	    regmatch_t startmatch;	/* match position for start_regexp*/
	    regmatch_t endmatch;	/* match position for end_regexp*/

	    regcomp(&start_regexp, tmpcolor->start, REG_EXTENDED);

	    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. */
692
	    if (tmpcolor->end == NULL) {
693
694
695
696
697
698
699
700
701
		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
702
703
		     * k == 0.  If regexec returns nonzero, there are no
		     * more matches in the line. */
704
705
		    if (regexec(&start_regexp, &fileptr->data[k], 1,
				&startmatch, k == 0 ? 0 : REG_NOTBOL))
706
			break;
707
708
709
		    /* Translate the match to the beginning of the line. */
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
710
711
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
712
			statusbar(_("Refusing 0 length regex match"));
713
		    } else if (startmatch.rm_so < start + COLS &&
714
715
716
717
718
719
720
721
722
723
724
725
726
727
				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
728
		}
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
	    } 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. */

		regex_t end_regexp;	/* Compiled search regexp */
		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? */

		regcomp(&end_regexp, tmpcolor->end, REG_EXTENDED);

		while (start_line != NULL &&
			regexec(&start_regexp, start_line->data, 1,
				&startmatch, 0)) {
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
		    if (!regexec(&end_regexp, start_line->data, 1,
				&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;
		    if (regexec(&end_regexp,
			    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 */
779
			break;
780
781
782
783
784
785
		    start_col++;
		    if (regexec(&start_regexp,
			    start_line->data + start_col, 1, &startmatch,
			    REG_NOTBOL))
			/* No later start on this line. */
			goto step_two;
786
		}
787
788
789
790
791
792
793
		/* 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;
794
795
		while (end_line != NULL && regexec(&end_regexp, end_line->data,
				1, &endmatch, 0))
796
797
798
		    end_line = end_line->next;

		/* No end found, or it is too early. */
799
		if (end_line == NULL || end_line->lineno < fileptr->lineno ||
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
			(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) {
		    if (regexec(&start_regexp, fileptr->data + start_col, 1,
				&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;
834
		    }
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
		    if (!regexec(&end_regexp, fileptr->data + startmatch.rm_eo,
				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);
855
			}
856
857
858
859
860
		    } 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;
861
862
			while (end_line != NULL && regexec(&end_regexp,
				end_line->data, 1, &endmatch, 0))
863
864
865
866
867
868
869
870
871
			    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;
872
873
			}
		    }
874
875
		    start_col = startmatch.rm_so + 1;
		} /* while start_col < start + COLS */
876

877
878
879
  skip_step_two:
		regfree(&end_regexp);
	    } /* if (tmp_color->end != NULL) */
880

881
882
883
884
885
	    regfree(&start_regexp);
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
	} /* for tmpcolor in colorstrings */
    }
Chris Allegretta's avatar
Chris Allegretta committed
886
#endif				/* ENABLE_COLOR */
887

888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
#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
913

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
	    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) {
929
	    wattron(edit, A_REVERSE);
930
931
932
	    assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS);
	    mvwaddnstr(edit, yval, x_start,
			fileptr->data + start + x_start, paintlen);
933
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
934
	}
935
    }
936
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
937
938
}

939
940
941
942
/* 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. */
943
void update_line(filestruct *fileptr, int index)
Chris Allegretta's avatar
Chris Allegretta committed
944
{
945
946
947
948
949
950
951
952
953
954
955
956
957
    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
958

959
    if (fileptr == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
960
	return;
961

962
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
963

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

967
968
    if (line < 0 || line >= editwinrows)
	return;
969

970
971
972
973
974
    /* First, blank out the line (at a minimum) */
    mvwaddstr(edit, line, 0, hblank);

    original = fileptr->data;
    converted = charalloc(strlenpt(original) + 1);
975
976

    /* Next, convert all the tabs to spaces, so everything else is easy.
977
978
979
980
981
982
     * 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
983
984

    pos = 0;
985
986
    for (; *original != '\0'; original++) {
	if (*original == '\t')
987
	    do {
988
		converted[pos++] = ' ';
989
	    } while (pos % tabsize);
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
	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;
1003
    }
1004
    converted[pos] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1005

Chris Allegretta's avatar
Chris Allegretta committed
1006
    /* Now, paint the line */
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
    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
1017

1018
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
1019
	mvwaddch(edit, line, 0, '$');
1020
1021
    if (pos > page_start + COLS)
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
1022
1023
}

1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
/* This function updates current, based on where current_y is;
 * reset_cursor() does the opposite. */
void update_cursor(void)
{
    int i = 0;

#ifdef DEBUG
    fprintf(stderr, _("Moved to (%d, %d) in edit buffer\n"), current_y,
	    current_x);
#endif

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

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

Chris Allegretta's avatar
Chris Allegretta committed
1046
1047
1048
1049
1050
1051
void center_cursor(void)
{
    current_y = editwinrows / 2;
    wmove(edit, current_y, current_x);
}

Chris Allegretta's avatar
Chris Allegretta committed
1052
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
1053
1054
void edit_refresh(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1055
1056
1057
    /* 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
1058
1059
    if (current == NULL)
	return;
Chris Allegretta's avatar
Chris Allegretta committed
1060
1061
    if (edittop == NULL)
	edittop = current;
Chris Allegretta's avatar
Chris Allegretta committed
1062

1063
1064
1065
1066
1067
    if (current->lineno >= edittop->lineno + editwinrows)
	/* 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. */
1068
	edit_update(current, CENTER);
1069
1070
    else {
	int nlines = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1071

1072
1073
	/* Don't make the cursor jump around the screen whilst updating */
	leaveok(edit, TRUE);
1074

1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
	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
	   isn't here?  Luck?? */
	wrefresh(edit);
	leaveok(edit, FALSE);
    }
Chris Allegretta's avatar
Chris Allegretta committed
1092
1093
}

1094
/*
1095
 * Same as above, but touch the window first, so everything is redrawn.
1096
1097
1098
1099
1100
1101
1102
1103
 */
void edit_refresh_clearok(void)
{
    clearok(edit, TRUE);
    edit_refresh();
    clearok(edit, FALSE);
}

Chris Allegretta's avatar
Chris Allegretta committed
1104
/*
1105
 * Nice generic routine to update the edit buffer, given a pointer to the
Chris Allegretta's avatar
Chris Allegretta committed
1106
1107
 * file struct =) 
 */
Chris Allegretta's avatar
Chris Allegretta committed
1108
void edit_update(filestruct *fileptr, topmidbotnone location)
Chris Allegretta's avatar
Chris Allegretta committed
1109
1110
1111
1112
{
    if (fileptr == NULL)
	return;

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

Chris Allegretta's avatar
Chris Allegretta committed
1116
1117
1118
1119
	for (; goal >= 0 && fileptr->prev != NULL; goal--)
	    fileptr = fileptr->prev;
    }
    edittop = fileptr;
1120
    fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
1121
1122
1123
1124
1125
1126
1127

    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
1128
 * otherwise, the valid shortcut key caught.  Def is any editable text we
Chris Allegretta's avatar
Chris Allegretta committed
1129
 * want to put up by default.
1130
1131
 *
 * New arg tabs tells whether or not to allow tab completion.
Chris Allegretta's avatar
Chris Allegretta committed
1132
 */
Chris Allegretta's avatar
Chris Allegretta committed
1133
int statusq(int tabs, const shortcut *s, const char *def,
1134
1135
1136
#ifndef NANO_SMALL
		historyheadtype *which_history,
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1137
		const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1138
1139
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1140
    char *foo = charalloc(COLS - 3);
1141
    int ret;
1142
#ifndef DISABLE_TABCOMP
1143
    int list = 0;
1144
1145
#endif

1146
    bottombars(s);
Chris Allegretta's avatar
Chris Allegretta committed
1147
1148

    va_start(ap, msg);
Chris Allegretta's avatar
Chris Allegretta committed
1149
    vsnprintf(foo, COLS - 4, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1150
    va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
1151
    foo[COLS - 4] = '\0';
1152

1153
1154
1155
1156
1157
    ret = nanogetstr(tabs, foo, def,
#ifndef NANO_SMALL
		which_history,
#endif
		s
1158
#ifndef DISABLE_TABCOMP
1159
		, &list
1160
#endif
1161
		);
Chris Allegretta's avatar
Chris Allegretta committed
1162
    free(foo);
Chris Allegretta's avatar
Chris Allegretta committed
1163
1164
1165
1166
1167
1168
1169
1170
1171

    switch (ret) {
    case NANO_FIRSTLINE_KEY:
	do_first_line();
	break;
    case NANO_LASTLINE_KEY:
	do_last_line();
	break;
    case NANO_CANCEL_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1172
1173
	ret = -1;
	break;
Chris Allegretta's avatar
Chris Allegretta committed
1174
    default:
1175
	blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1176
1177
1178
1179
1180
1181
    }

#ifdef DEBUG
    fprintf(stderr, _("I got \"%s\"\n"), answer);
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1182
1183
1184
1185
1186
1187
1188
1189
#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
1190
1191
1192
1193
    return ret;
}

/*
1194
 * Ask a simple yes/no question on the statusbar.  Returns 1 for Y, 0
1195
1196
 * for N, 2 for All (if all is nonzero when passed in) and -1 for abort
 * (^C).
Chris Allegretta's avatar
Chris Allegretta committed
1197
 */
Chris Allegretta's avatar
Chris Allegretta committed
1198
int do_yesno(int all, int leavecursor, const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1199
1200
1201
{
    va_list ap;
    char foo[133];
1202
    int kbinput, ok = -1, i;
Chris Allegretta's avatar
Chris Allegretta committed
1203
1204
1205
    const char *yesstr;		/* String of yes characters accepted */
    const char *nostr;		/* Same for no */
    const char *allstr;		/* And all, surprise! */
1206
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta's avatar
Chris Allegretta committed
1207
1208
1209
    MEVENT mevent;
#endif

1210
    /* Yes, no and all are strings of any length.  Each string consists of
Chris Allegretta's avatar
Chris Allegretta committed
1211
1212
       all characters accepted as a valid character for that value.
       The first value will be the one displayed in the shortcuts. */
1213
1214
1215
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
1216
1217

    /* Write the bottom of the screen */
1218
    blank_bottomwin();
1219

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

Chris Allegretta's avatar
Chris Allegretta committed
1224
	wmove(bottomwin, 1, 0);
1225

1226
	sprintf(shortstr, " %c", yesstr[0]);
1227
	onekey(shortstr, _("Yes"), 16);
1228
1229

	if (all) {
1230
	    shortstr[1] = allstr[0];
1231
	    onekey(shortstr, _("All"), 16);
1232
	}
Chris Allegretta's avatar
Chris Allegretta committed
1233
	wmove(bottomwin, 2, 0);
1234

1235
	shortstr[1] = nostr[0];
1236
	onekey(shortstr, _("No"), 16);
1237

1238
	onekey("^C", _("Cancel"), 16);
Chris Allegretta's avatar
Chris Allegretta committed
1239
1240
1241
1242
    }
    va_start(ap, msg);
    vsnprintf(foo, 132, msg, ap);
    va_end(ap);
1243

Chris Allegretta's avatar
Chris Allegretta committed
1244
    wattron(bottomwin, A_REVERSE);
1245
1246

    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1247
    mvwaddstr(bottomwin, 0, 0, foo);
1248

Chris Allegretta's avatar
Chris Allegretta committed
1249
    wattroff(bottomwin, A_REVERSE);
1250

Chris Allegretta's avatar
Chris Allegretta committed
1251
1252
1253
1254
1255
1256
1257
1258
1259
    wrefresh(bottomwin);

    if (leavecursor == 1)
	reset_cursor();

    while (ok == -1) {
	kbinput = wgetch(edit);

	switch (kbinput) {
1260
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta's avatar
Chris Allegretta committed
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
	case KEY_MOUSE:

	    /* Look ma!  We get to duplicate lots of code from do_mouse!! */
	    if (getmouse(&mevent) == ERR)
		break;
	    if (!wenclose(bottomwin, mevent.y, mevent.x) || ISSET(NO_HELP))
		break;
	    mevent.y -= editwinrows + 3;
	    if (mevent.y < 0)
		break;
	    else {

		/* Rather than a bunch of if statements, set up a matrix
Chris Allegretta's avatar
Chris Allegretta committed
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
		   of possible return keystrokes based on the x and y
		   values */ 
		char yesnosquare[2][2];
		yesnosquare[0][0] = yesstr[0];
		if (all)
		    yesnosquare[0][1] = allstr[0];
		else
		    yesnosquare[0][1] = '\0';
		yesnosquare[1][0] = nostr[0];
		yesnosquare[1][1] = NANO_CONTROL_C;
		ungetch(yesnosquare[mevent.y][mevent.x / (COLS / 6)]);
Chris Allegretta's avatar
Chris Allegretta committed
1285
1286
1287
	    }
	    break;
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1288
1289
1290
	case NANO_CONTROL_C:
	    ok = -2;
	    break;
1291
1292
	default:

1293
	    /* Look for the kbinput in the yes, no and (optimally) all str */
Chris Allegretta's avatar
Chris Allegretta committed
1294
	    for (i = 0; yesstr[i] != 0 && yesstr[i] != kbinput; i++);
1295
	    if (yesstr[i] != 0) {
1296
		ok = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1297
		break;
1298
1299
	    }

Chris Allegretta's avatar
Chris Allegretta committed
1300
	    for (i = 0; nostr[i] != 0 && nostr[i] != kbinput; i++);
1301
1302
	    if (nostr[i] != 0) {
		ok = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1303
		break;
1304
1305
1306
	    }

	    if (all) {
Chris Allegretta's avatar
Chris Allegretta committed
1307
		for (i = 0; allstr[i] != 0 && allstr[i] != kbinput; i++);
1308
1309
		if (allstr[i] != 0) {
		    ok = 2;
Chris Allegretta's avatar
Chris Allegretta committed
1310
		    break;
1311
1312
		}
	    }
Chris Allegretta's avatar
Chris Allegretta committed
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
	}
    }

    /* Then blank the screen */
    blank_statusbar_refresh();

    if (ok == -2)
	return -1;
    else
	return ok;
}

1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
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
1347
void statusbar(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1348
1349
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1350
    char *foo;
Chris Allegretta's avatar
Chris Allegretta committed
1351
    int start_x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1352
1353
1354
1355
    size_t foo_len;

    assert(COLS >= 4);
    foo = charalloc(COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
1356
1357

    va_start(ap, msg);
Chris Allegretta's avatar
Chris Allegretta committed
1358
    vsnprintf(foo, COLS - 3, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1359
1360
    va_end(ap);

Chris Allegretta's avatar
Chris Allegretta committed
1361
1362
1363
    foo[COLS - 4] = '\0';
    foo_len = strlen(foo);
    start_x = (COLS - foo_len - 4) / 2;
Chris Allegretta's avatar
Chris Allegretta committed
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373

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

Chris Allegretta's avatar
Chris Allegretta committed
1377
    wattroff(bottomwin, A_REVERSE);
1378

Chris Allegretta's avatar
Chris Allegretta committed
1379
1380
1381
1382
1383
1384
1385
1386
    wrefresh(bottomwin);

    if (ISSET(CONSTUPDATE))
	statblank = 1;
    else
	statblank = 25;
}

1387
int do_cursorpos(int constant)
Chris Allegretta's avatar
Chris Allegretta committed
1388
1389
{
    filestruct *fileptr;
1390
    float linepct = 0.0, bytepct = 0.0, colpct = 0.0;
1391
    long i = 0, j = 0;
1392
    static long old_i = -1, old_totsize = -1;
Chris Allegretta's avatar
Chris Allegretta committed
1393
1394
1395
1396

    if (current == NULL || fileage == NULL)
	return 0;

1397
1398
1399
1400
1401
1402
    if (old_i == -1)
	old_i = i;

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

Chris Allegretta's avatar
Chris Allegretta committed
1403
    colpct = 100 * (xplustabs() + 1) / (strlenpt(current->data) + 1);
Chris Allegretta's avatar
Chris Allegretta committed
1404

1405
1406
1407
    for (fileptr = fileage; fileptr != current && fileptr != NULL;
	 fileptr = fileptr->next)
	i += strlen(fileptr->data) + 1;
1408

1409
1410
    if (fileptr == NULL)
	return -1;
1411

1412
    i += current_x;
1413

1414
    j = totsize;
1415

1416
1417
    if (totsize > 0)
	bytepct = 100 * i / totsize;
1418
1419
1420

    if (totlines > 0)
	 linepct = 100 * current->lineno / totlines;
Chris Allegretta's avatar
Chris Allegretta committed
1421
1422
1423
1424
1425
1426

#ifdef DEBUG
    fprintf(stderr, _("do_cursorpos: linepct = %f, bytepct = %f\n"),
	    linepct, bytepct);
#endif

1427
1428
1429
1430
    /* if constant is zero, display the position on the statusbar
       unconditionally; otherwise, only display the position when the
       character values have changed */
    if (!constant || (old_i != i || old_totsize != totsize)) {
Chris Allegretta's avatar
Chris Allegretta committed
1431
	statusbar(_
1432
		  ("line %d/%d (%.0f%%), col %ld/%ld (%.0f%%), char %ld/%ld (%.0f%%)"),
1433
		  current->lineno, totlines, linepct, xplustabs() + 1, 
Chris Allegretta's avatar
Chris Allegretta committed
1434
		  strlenpt(current->data) + 1, colpct, i, j, bytepct);
1435
1436
1437
1438
1439
    }

    old_i = i;
    old_totsize = totsize;

Chris Allegretta's avatar
Chris Allegretta committed
1440
1441
1442
1443
    reset_cursor();
    return 1;
}

1444
1445
1446
1447
1448
int do_cursorpos_void(void)
{
    return do_cursorpos(0);
}

1449
1450
/* Our shortcut-list-compliant help function, which is
 * better than nothing, and dynamic! */
Chris Allegretta's avatar
Chris Allegretta committed
1451
1452
int do_help(void)
{
1453
#ifndef DISABLE_HELP
1454
    int i, j, row = 0, page = 1, kbinput = 0, no_more = 0, kp, kp2;
Chris Allegretta's avatar
Chris Allegretta committed
1455
    int no_help_flag = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1456
    const shortcut *oldshortcut;
Chris Allegretta's avatar
Chris Allegretta committed
1457
1458
1459

    blank_edit();
    curs_set(0);
1460
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
1461
1462
    blank_statusbar();

1463
    /* set help_text as the string to display */
1464
    help_init();
1465
    assert(help_text != NULL);
1466
1467
1468

    oldshortcut = currshortcut;

1469
    currshortcut = help_list;
1470

1471
    kp = keypad_on(edit, 1);
1472
    kp2 = keypad_on(bottomwin, 1);
1473

Chris Allegretta's avatar
Chris Allegretta committed
1474
1475
    if (ISSET(NO_HELP)) {

1476
	/* Well, if we're going to do this, we should at least
1477
	   do it the right way */
Chris Allegretta's avatar
Chris Allegretta committed
1478
	no_help_flag = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1479
	UNSET(NO_HELP);
1480
	window_init();
1481
	bottombars(help_list);
1482

Chris Allegretta's avatar
Chris Allegretta committed
1483
    } else
1484
	bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
1485
1486

    do {
1487
1488
	const char *ptr = help_text;

Chris Allegretta's avatar
Chris Allegretta committed
1489
	switch (kbinput) {
1490
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta's avatar
Chris Allegretta committed
1491
1492
1493
	case KEY_MOUSE:
	    do_mouse();
	    break;
1494
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
	case 27:
	    kbinput = wgetch(edit);
	    switch(kbinput) {
	    case '[':
		kbinput = wgetch(edit);
		switch(kbinput) {
		    case '5':	/* Alt-[-5 = Page Up */
			wgetch(edit);
			goto do_pageupkey;
			break;
		    case 'V':	/* Alt-[-V = Page Up in Hurd Console */
		    case 'I':	/* Alt-[-I = Page Up - FreeBSD Console */
			goto do_pageupkey;
			break;
		    case '6':	/* Alt-[-6 = Page Down */
			wgetch(edit);
			goto do_pagedownkey;
			break;
		    case 'U':	/* Alt-[-U = Page Down in Hurd Console */
		    case 'G':	/* Alt-[-G = Page Down - FreeBSD Console */
			goto do_pagedownkey;
			break;
		}
		break;
	    }
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
1521
1522
1523
	case NANO_NEXTPAGE_KEY:
	case NANO_NEXTPAGE_FKEY:
	case KEY_NPAGE:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1524
	  do_pagedownkey:
Chris Allegretta's avatar
Chris Allegretta committed
1525
1526
1527
1528
1529
1530
1531
1532
	    if (!no_more) {
		blank_edit();
		page++;
	    }
	    break;
	case NANO_PREVPAGE_KEY:
	case NANO_PREVPAGE_FKEY:
	case KEY_PPAGE:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1533
	  do_pageupkey:
Chris Allegretta's avatar
Chris Allegretta committed
1534
1535
1536
1537
1538
1539
1540
1541
	    if (page > 1) {
		no_more = 0;
		blank_edit();
		page--;
	    }
	    break;
	}

1542
	/* Calculate where in the text we should be, based on the page */
Chris Allegretta's avatar
Chris Allegretta committed
1543
1544
1545
	for (i = 1; i < page; i++) {
	    row = 0;
	    j = 0;
1546
1547

	    while (row < editwinrows - 2 && *ptr != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
		if (*ptr == '\n' || j == COLS - 5) {
		    j = 0;
		    row++;
		}
		ptr++;
		j++;
	    }
	}

	i = 0;
	j = 0;
	while (i < editwinrows && *ptr != '\0') {
1560
	    const char *end = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
1561
1562
1563
1564
1565
1566
	    while (*end != '\n' && *end != '\0' && j != COLS - 5) {
		end++;
		j++;
	    }
	    if (j == COLS - 5) {

1567
		/* Don't print half a word if we've run out of space */
Chris Allegretta's avatar
Chris Allegretta committed
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
		while (*end != ' ' && *end != '\0') {
		    end--;
		    j--;
		}
	    }
	    mvwaddnstr(edit, i, 0, ptr, j);
	    j = 0;
	    i++;
	    if (*end == '\n')
		end++;
	    ptr = end;
	}
	if (*ptr == '\0') {
	    no_more = 1;
	    continue;
	}
Chris Allegretta's avatar
Chris Allegretta committed
1584
1585
    } while ((kbinput = wgetch(edit)) != NANO_EXIT_KEY &&
	     kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
1586

1587
1588
    currshortcut = oldshortcut;

Chris Allegretta's avatar
Chris Allegretta committed
1589
    if (no_help_flag) {
1590
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
1591
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
1592
	SET(NO_HELP);
1593
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
1594
    } else
1595
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
1596
1597
1598

    curs_set(1);
    edit_refresh();
1599
    kp = keypad_on(edit, kp);
1600
    kp2 = keypad_on(bottomwin, kp2);
1601

1602
1603
1604
1605
1606
    /* 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
1607
1608
1609
1610
#elif defined(DISABLE_HELP)
    nano_disabled_msg();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1611
1612
1613
    return 1;
}

1614
int keypad_on(WINDOW *win, int newval)
1615
{
1616
1617
1618
1619
1620
1621
1622
1623
1624
/* This is taken right from aumix.  Don't sue me. */
#ifdef HAVE_USEKEYPAD
    int old = win->_use_keypad;
    keypad(win, newval);
    return old;
#else
    keypad(win, newval);
    return 1;
#endif /* HAVE_USEKEYPAD */
Robert Siemborski's avatar
Robert Siemborski committed
1625
}
1626

1627
/* Highlight the current word being replaced or spell checked. */
Chris Allegretta's avatar
Chris Allegretta committed
1628
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
1629
1630
{
    char *highlight_word = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
1631
    int x, y, word_len;
Chris Allegretta's avatar
Chris Allegretta committed
1632

Chris Allegretta's avatar
Chris Allegretta committed
1633
1634
    highlight_word =
	mallocstrcpy(highlight_word, &current->data[current_x]);
Chris Allegretta's avatar
Chris Allegretta committed
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645

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

1647
    /* adjust output when word extends beyond screen */
Chris Allegretta's avatar
Chris Allegretta committed
1648
1649

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

Chris Allegretta's avatar
Chris Allegretta committed
1652
    if ((COLS - (y - x) + word_len) > COLS) {
Chris Allegretta's avatar
Chris Allegretta committed
1653
1654
1655
1656
1657
1658
1659
	highlight_word[y - x - 1] = '$';
	highlight_word[y - x] = '\0';
    }

    /* OK display the output */

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

Chris Allegretta's avatar
Chris Allegretta committed
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
    if (highlight_flag)
	wattron(edit, A_REVERSE);

    waddstr(edit, highlight_word);

    if (highlight_flag)
	wattroff(edit, A_REVERSE);

    free(highlight_word);
}

1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
/* 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)
	fprintf(stderr, _("Dumping file buffer to stderr...\n"));
    else if (inptr == cutbuffer)
	fprintf(stderr, _("Dumping cutbuffer to stderr...\n"));
    else
	fprintf(stderr, _("Dumping a buffer to stderr...\n"));

    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 */

1710
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1711
#define CREDIT_LEN 53
1712
1713
#define XLCREDIT_LEN 8

1714
1715
void do_credits(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1716
    int i, j = 0, k, place = 0, start_x;
1717

1718
1719
    const char *what;
    const char *xlcredits[XLCREDIT_LEN];
1720

1721
    const char *credits[CREDIT_LEN] = { 
1722
1723
	"0",				/* "The nano text editor" */
	"1",				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
1724
1725
	VERSION,
	"",
1726
	"2",				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
1727
1728
1729
1730
1731
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
1732
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1733
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
	"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",
	"",
1749
	"3",				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
1750
1751
1752
1753
1754
1755
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
1756
	"4",				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
1757
	"Linus Torvalds",
1758
	"5",				/* "For ncurses:" */
1759
1760
1761
1762
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
1763
1764
	"6",				/* "and anyone else we forgot..." */
	"7",				/* "Thank you for using nano!\n" */
Chris Allegretta's avatar
Chris Allegretta committed
1765
1766
1767
	"", "", "", "",
	"(c) 1999-2002 Chris Allegretta",
	"", "", "", "",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1768
	"http://www.nano-editor.org/"
1769
1770
    };

1771
1772
1773
1774
1775
1776
1777
1778
1779
    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");

1780
1781
1782
1783
    curs_set(0);
    nodelay(edit, TRUE);
    blank_bottombars();
    mvwaddstr(topwin, 0, 0, hblank);
Chris Allegretta's avatar
Chris Allegretta committed
1784
1785
    blank_edit();
    wrefresh(edit);
1786
1787
1788
1789
    wrefresh(bottomwin);
    wrefresh(topwin);

    while (wgetch(edit) == ERR) {
Chris Allegretta's avatar
Chris Allegretta committed
1790
1791
	for (k = 0; k <= 1; k++) {
	    blank_edit();
Chris Allegretta's avatar
Chris Allegretta committed
1792
1793
	    for (i = editwinrows / 2 - 1; i >= (editwinrows / 2 - 1 - j);
		 i--) {
Chris Allegretta's avatar
Chris Allegretta committed
1794
1795
		mvwaddstr(edit, i * 2 - k, 0, hblank);

1796
		if (place - (editwinrows / 2 - 1 - i) < CREDIT_LEN) {
Chris Allegretta's avatar
Chris Allegretta committed
1797
		    what = credits[place - (editwinrows / 2 - 1 - i)];
1798
1799
1800
1801
1802
1803
1804
1805

		    /* 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
1806
1807
		    what = "";

1808
		start_x = COLS / 2 - strlen(what) / 2 - 1;
Chris Allegretta's avatar
Chris Allegretta committed
1809
1810
1811
1812
		mvwaddstr(edit, i * 2 - k, start_x, what);
	    }
	    usleep(700000);
	    wrefresh(edit);
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
	}
	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
1827
}
1828
#endif