winio.c 45.2 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

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
/* Repaint the statusbar when getting a character in nanogetstr().  buf
Chris Allegretta's avatar
Chris Allegretta committed
165
166
 * should be no longer than COLS - 4.
 *
Chris Allegretta's avatar
Chris Allegretta committed
167
 * Note that we must turn on A_REVERSE here, since do_help() turns it
Chris Allegretta's avatar
Chris Allegretta committed
168
 * 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
		, int resetpos)
Chris Allegretta's avatar
Chris Allegretta committed
198
199
{
    int kbinput;
Chris Allegretta's avatar
Chris Allegretta committed
200
    static int x = -1;
Chris Allegretta's avatar
Chris Allegretta committed
201
202
203
204
205
206
207
	/* 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
#ifndef NANO_SMALL
   /* for history */
    char *history = NULL;
211
    char *currentbuf = NULL;
212
    char *complete = NULL;
213
    int last_kbinput = 0, ret2cb = 0;
214
#endif
Chris Allegretta's avatar
Chris Allegretta committed
215
    xend = strlen(def);
Chris Allegretta's avatar
Chris Allegretta committed
216
217
218
219
220

    /* 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. */
221
    if (x == -1 || x > xend || resetpos)
Chris Allegretta's avatar
Chris Allegretta committed
222
223
	x = xend;

Chris Allegretta's avatar
Chris Allegretta committed
224
225
226
227
228
    answer = (char *)nrealloc(answer, xend + 1);
    if (xend > 0)
	strcpy(answer, def);
    else
	answer[0] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
229

230
#if !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
231
    currshortcut = s;
232
233
#endif

Chris Allegretta's avatar
Chris Allegretta committed
234
    /* Get the input! */
235

Chris Allegretta's avatar
Chris Allegretta committed
236
    nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
237

238
239
    /* Make sure any editor screen updates are displayed before getting
       input */
240
241
    wrefresh(edit);

Chris Allegretta's avatar
Chris Allegretta committed
242
    while ((kbinput = wgetch(bottomwin)) != 13) {
243
	for (t = s; t != NULL; t = t->next) {
244
245
246
247
#ifdef DEBUG
	    fprintf(stderr, _("Aha! \'%c\' (%d)\n"), kbinput, kbinput);
#endif

248
	    if (kbinput == t->val && kbinput < 32) {
249

250
#ifndef DISABLE_HELP
251
252
		/* Have to do this here, it would be too late to do it
		   in statusq() */
Chris Allegretta's avatar
Chris Allegretta committed
253
		if (kbinput == NANO_HELP_KEY || kbinput == NANO_HELP_FKEY) {
254
255
256
257
		    do_help();
		    break;
		}
#endif
Chris Allegretta's avatar
Chris Allegretta committed
258

259
		return t->val;
Chris Allegretta's avatar
Chris Allegretta committed
260
261
	    }
	}
Chris Allegretta's avatar
Chris Allegretta committed
262
	assert(0 <= x && x <= xend && xend == strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
263

Chris Allegretta's avatar
Chris Allegretta committed
264
265
266
	if (kbinput != '\t')
	    tabbed = 0;

Chris Allegretta's avatar
Chris Allegretta committed
267
	switch (kbinput) {
268

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

Chris Allegretta's avatar
Chris Allegretta committed
330
		if (history_list->len > 0) {
331
332
		    complete = get_history_completion(history_list, answer);
		    xend = strlen(complete);
Chris Allegretta's avatar
Chris Allegretta committed
333
		    x = xend;
334
335
		    answer = mallocstrcpy(answer, complete);
		}
336
	    }
337
#ifndef DISABLE_TABCOMP
338
339
	    else
#endif
340
341
#endif
#ifndef DISABLE_TABCOMP
342
343
344
345
346
347
348
349
	    if (allowtabs) {
		int shift = 0;

		answer = input_tab(answer, x, &tabbed, &shift, list);
		xend = strlen(answer);
		x += shift;
		if (x > xend)
		    x = xend;
350
351
352
	    }
#endif
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
353
	case KEY_LEFT:
354
	case NANO_BACK_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
355
	    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
356
357
358
		x--;
	    break;
	case KEY_UP:
Chris Allegretta's avatar
Chris Allegretta committed
359
	case NANO_UP_KEY:
360
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
361
	    if (history_list != NULL) {
362

Chris Allegretta's avatar
Chris Allegretta committed
363
364
365
		/* If there's no previous temp holder, or if we already
		   arrowed back down to it and (possibly edited it),
		   update the holder */
366
367
368
369
370
		if (currentbuf == NULL || (ret2cb == 1 && strcmp(currentbuf, answer))) {
		    currentbuf = mallocstrcpy(currentbuf, answer);
		    ret2cb = 0;
		}

371
		/* get older search from the history list */
Chris Allegretta's avatar
Chris Allegretta committed
372
		if ((history = get_history_older(history_list)) != NULL) {
373
374
375
376
377
378
379
380
381
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
#endif
382
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
383
	case KEY_DOWN:
Chris Allegretta's avatar
Chris Allegretta committed
384
	case NANO_DOWN_KEY:
385
#ifndef NANO_SMALL
Chris Allegretta's avatar
Chris Allegretta committed
386
	    if (history_list != NULL) {
387
		/* get newer search from the history list */
Chris Allegretta's avatar
Chris Allegretta committed
388
		if ((history = get_history_newer(history_list)) != NULL) {
389
390
		    answer = mallocstrcpy(answer, history);
		    xend = strlen(history);
391

Chris Allegretta's avatar
Chris Allegretta committed
392
393
		/* else if we ran out of history, regurgitate the temporary
		   buffer and blow away currentbuf */
394
395
		} else if (currentbuf != NULL) {
		    answer = mallocstrcpy(answer, currentbuf);
Chris Allegretta's avatar
Chris Allegretta committed
396
397
398
		    free(currentbuf);
		    currentbuf = NULL;
		    xend = strlen(answer);
399
		    ret2cb = 1;
400
401
402
403
404
405
406
		} else {
		    answer = mallocstrcpy(answer, "");
		    xend = 0;
		}
		x = xend;
	    }
#endif
Chris Allegretta's avatar
Chris Allegretta committed
407
	    break;
408
409
	case KEY_DC:
	    goto do_deletekey;
Chris Allegretta's avatar
Chris Allegretta committed
410
411
	case 27:
	    switch (kbinput = wgetch(edit)) {
412
	    case 'O':
Chris Allegretta's avatar
Chris Allegretta committed
413
		switch (kbinput = wgetch(edit)) {
414
		case 'F':
Chris Allegretta's avatar
Chris Allegretta committed
415
		    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
416
		    break;
417
		case 'H':
Chris Allegretta's avatar
Chris Allegretta committed
418
		    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
419
420
421
		    break;
		}
		break;
422
	    case '[':
Chris Allegretta's avatar
Chris Allegretta committed
423
424
425
426
427
428
		switch (kbinput = wgetch(edit)) {
		case 'C':
		    if (x < xend)
			x++;
		    break;
		case 'D':
Chris Allegretta's avatar
Chris Allegretta committed
429
		    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
430
431
			x--;
		    break;
432
433
		case '1':
		case '7':
Chris Allegretta's avatar
Chris Allegretta committed
434
		    x = 0;
435
436
437
		    goto skip_tilde;
		case '3':
		  do_deletekey:
Chris Allegretta's avatar
Chris Allegretta committed
438
439
440
		    if (x < xend) {
			memmove(answer + x, answer + x + 1, xend - x);
			xend--;
Chris Allegretta's avatar
Chris Allegretta committed
441
		    }
442
443
444
		    goto skip_tilde;
		case '4':
		case '8':
Chris Allegretta's avatar
Chris Allegretta committed
445
		    x = xend;
446
		  skip_tilde:
Chris Allegretta's avatar
Chris Allegretta committed
447
448
		    nodelay(edit, TRUE);
		    kbinput = wgetch(edit);
449
		    if (kbinput == '~' || kbinput == ERR)
Chris Allegretta's avatar
Chris Allegretta committed
450
451
452
453
			kbinput = -1;
		    nodelay(edit, FALSE);
		    break;
		}
Chris Allegretta's avatar
Chris Allegretta committed
454
		break;
455
456
	    default:

457
		for (t = s; t != NULL; t = t->next) {
458
#ifdef DEBUG
Chris Allegretta's avatar
Chris Allegretta committed
459
460
		    fprintf(stderr, _("Aha! \'%c\' (%d)\n"), kbinput,
			    kbinput);
461
#endif
Chris Allegretta's avatar
Chris Allegretta committed
462
463
464
465
466
		    if (kbinput == t->val || kbinput == t->val - 32)
			/* 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... */
467
			return t->val;
468
		}
Chris Allegretta's avatar
Chris Allegretta committed
469
470
471
472
473
474
	    }
	    break;

	default:
	    if (kbinput < 32)
		break;
Chris Allegretta's avatar
Chris Allegretta committed
475
476
477
478
	    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
479
480
481
482
483
	    x++;

#ifdef DEBUG
	    fprintf(stderr, _("input \'%c\' (%d)\n"), kbinput, kbinput);
#endif
484
	} /* switch (kbinput) */
485
#ifndef NANO_SMALL
486
	last_kbinput = kbinput;
487
#endif
Chris Allegretta's avatar
Chris Allegretta committed
488
	nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
489
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
490
    } /* while (kbinput ...) */
Chris Allegretta's avatar
Chris Allegretta committed
491

Chris Allegretta's avatar
Chris Allegretta committed
492
493
494
    /* We finished putting in an answer; reset x */
    x = -1;

Chris Allegretta's avatar
Chris Allegretta committed
495
    /* Just check for a blank answer here */
496
    if (answer[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
497
498
499
500
501
	return -2;
    else
	return 0;
}

502
503
504
505
506
507
508
509
510
511
/* 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
512
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
513
514
{
    int namelen, space;
Chris Allegretta's avatar
Chris Allegretta committed
515
    const char *what = path;
Chris Allegretta's avatar
Chris Allegretta committed
516
517
518

    if (path == NULL)
	what = filename;
Chris Allegretta's avatar
Chris Allegretta committed
519
520

    wattron(topwin, A_REVERSE);
521

Chris Allegretta's avatar
Chris Allegretta committed
522
    mvwaddstr(topwin, 0, 0, hblank);
523
    mvwaddnstr(topwin, 0, 2, VERMSG, COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
524

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

Chris Allegretta's avatar
Chris Allegretta committed
527
    namelen = strlen(what);
Chris Allegretta's avatar
Chris Allegretta committed
528

529
530
    if (space > 0) {
        if (what[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
      	    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
547
	}
548
    } /* If we don't have space, we shouldn't bother */
Chris Allegretta's avatar
Chris Allegretta committed
549
    if (ISSET(MODIFIED))
Chris Allegretta's avatar
Chris Allegretta committed
550
	mvwaddnstr(topwin, 0, COLS - 11, _(" Modified "), 11);
Chris Allegretta's avatar
Chris Allegretta committed
551
    else if (ISSET(VIEW_MODE))
Chris Allegretta's avatar
Chris Allegretta committed
552
	mvwaddnstr(topwin, 0, COLS - 11, _(" View "), 11);
553

Chris Allegretta's avatar
Chris Allegretta committed
554
    wattroff(topwin, A_REVERSE);
555

Chris Allegretta's avatar
Chris Allegretta committed
556
557
558
559
    wrefresh(topwin);
    reset_cursor();
}

560
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
561
{
562
    int i, j, numcols;
563
    char keystr[9];
564
565
    int slen;

Chris Allegretta's avatar
Chris Allegretta committed
566
567
568
    if (ISSET(NO_HELP))
	return;

569
570
571
572
573
574
575
576
    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
577

578
    blank_bottomwin();
579

580
581
    for (i = 0; i < numcols; i++) {
	for (j = 0; j <= 1; j++) {
Chris Allegretta's avatar
Chris Allegretta committed
582

583
	    wmove(bottomwin, 1 + j, i * (COLS / numcols));
584

585
	    /* Yucky sentinel values we can't handle a better way */
586
587
	    if (s->val == NANO_CONTROL_SPACE)
		strcpy(keystr, "^ ");
588
#ifndef NANO_SMALL
589
	    else if (s->val == KEY_UP)
590
		strncpy(keystr, _("Up"), 8);
591
592
#endif /* NANO_SMALL */
	    else if (s->val > 0) {
593
594
595
596
597
598
		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);
599

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

602
603
604
	    s = s->next;
	    if (s == NULL)
		goto break_completely_out;
605
	}
Chris Allegretta's avatar
Chris Allegretta committed
606
    }
607

Chris Allegretta's avatar
Chris Allegretta committed
608
  break_completely_out:
Chris Allegretta's avatar
Chris Allegretta committed
609
610
611
    wrefresh(bottomwin);
}

612
613
614
615
616
/* 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
617
{
618

619
620
621
622
623
624
625
626
627
628
629
    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
630
631
632
    }
}

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

635
636
#ifndef NDEBUG
int check_linenumbers(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
637
{
638
639
    int check_line = 0;
    const filestruct *filetmp;
640

641
642
643
    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;
    return check_line;
644
}
645
#endif
646

647
648
649
 /* 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
650
int get_page_start(int column)
Chris Allegretta's avatar
Chris Allegretta committed
651
652
653
654
655
{
    assert(COLS > 9);
    return column < COLS - 1 ? 0 : column - 7 - (column - 8) % (COLS - 9);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
656
657
658
659
660
661
662
/* 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;

663
664
    /* Yuck.  This condition can be true after open_file() when opening
     * the first file. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
665
666
667
668
669
670
671
672
673
674
675
676
677
    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
678

679
680
681
/* 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. */
682
683
684
685
686
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
687
{
688
689
690
#ifdef DEBUG
    fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno,
		current->lineno);
691
692
693
#endif

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

Chris Allegretta's avatar
Chris Allegretta committed
697
#ifdef ENABLE_COLOR
698
    if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
	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. */
718
	    if (tmpcolor->end == NULL) {
719
720
721
722
723
724
725
726
727
		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
728
729
		     * k == 0.  If regexec returns nonzero, there are no
		     * more matches in the line. */
730
		    if (regexec(&tmpcolor->start, &fileptr->data[k], 1,
731
				&startmatch, k == 0 ? 0 : REG_NOTBOL))
732
			break;
733
734
735
		    /* Translate the match to the beginning of the line. */
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
736
737
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
738
			statusbar(_("Refusing 0 length regex match"));
739
		    } else if (startmatch.rm_so < start + COLS &&
740
741
742
743
744
745
746
747
748
749
750
751
752
753
				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
754
		}
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
	    } 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 &&
777
			regexec(&tmpcolor->start, start_line->data, 1,
778
779
780
				&startmatch, 0)) {
		    /* If there is an end on this line, there is no need
		     * to look for starts on earlier lines. */
781
		    if (!regexec(tmpcolor->end, start_line->data, 1,
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
				&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;
797
		    if (regexec(tmpcolor->end,
798
799
800
801
			    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 */
802
			break;
803
		    start_col++;
804
		    if (regexec(&tmpcolor->start,
805
806
807
808
			    start_line->data + start_col, 1, &startmatch,
			    REG_NOTBOL))
			/* No later start on this line. */
			goto step_two;
809
		}
810
811
812
813
814
815
816
		/* 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;
817
818
		while (end_line != NULL &&
			regexec(tmpcolor->end, end_line->data, 1, &endmatch, 0))
819
820
821
		    end_line = end_line->next;

		/* No end found, or it is too early. */
822
		if (end_line == NULL || end_line->lineno < fileptr->lineno ||
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
			(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) {
843
		    if (regexec(&tmpcolor->start, fileptr->data + start_col, 1,
844
845
846
847
848
849
850
851
852
853
854
855
856
				&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;
857
		    }
858
		    if (!regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo,
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
				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);
878
			}
879
880
881
882
883
		    } 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;
884
			while (end_line != NULL && regexec(tmpcolor->end,
885
				end_line->data, 1, &endmatch, 0))
886
887
888
889
890
891
892
893
894
			    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;
895
896
			}
		    }
897
898
899
		    start_col = startmatch.rm_so + 1;
		} /* while start_col < start + COLS */
	    } /* if (tmp_color->end != NULL) */
900

901
  skip_step_two:
902
903
904
905
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
	} /* for tmpcolor in colorstrings */
    }
Chris Allegretta's avatar
Chris Allegretta committed
906
#endif				/* ENABLE_COLOR */
907

908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
#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
933

934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
	    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) {
949
	    wattron(edit, A_REVERSE);
950
951
952
	    assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS);
	    mvwaddnstr(edit, yval, x_start,
			fileptr->data + start + x_start, paintlen);
953
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
954
	}
955
    }
956
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
957
958
}

959
960
961
962
/* 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. */
963
void update_line(filestruct *fileptr, int index)
Chris Allegretta's avatar
Chris Allegretta committed
964
{
965
966
967
968
969
970
971
972
973
974
975
976
977
    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
978

979
    if (fileptr == NULL)
Chris Allegretta's avatar
Chris Allegretta committed
980
	return;
981

982
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
983

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

987
988
    if (line < 0 || line >= editwinrows)
	return;
989

990
991
992
993
994
    /* First, blank out the line (at a minimum) */
    mvwaddstr(edit, line, 0, hblank);

    original = fileptr->data;
    converted = charalloc(strlenpt(original) + 1);
995
996

    /* Next, convert all the tabs to spaces, so everything else is easy.
997
998
999
1000
1001
1002
     * 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
1003
1004

    pos = 0;
1005
1006
    for (; *original != '\0'; original++) {
	if (*original == '\t')
1007
	    do {
1008
		converted[pos++] = ' ';
1009
	    } while (pos % tabsize);
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
	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;
1023
    }
1024
    converted[pos] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
1025

Chris Allegretta's avatar
Chris Allegretta committed
1026
    /* Now, paint the line */
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
    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
1037

1038
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
1039
	mvwaddch(edit, line, 0, '$');
1040
1041
    if (pos > page_start + COLS)
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
1042
1043
}

1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
/* 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
1062
    fprintf(stderr, "current->data = \"%s\"\n", current->data);
1063
1064
1065
#endif
}

Chris Allegretta's avatar
Chris Allegretta committed
1066
1067
1068
1069
1070
1071
void center_cursor(void)
{
    current_y = editwinrows / 2;
    wmove(edit, current_y, current_x);
}

Chris Allegretta's avatar
Chris Allegretta committed
1072
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
1073
1074
void edit_refresh(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1075
1076
1077
    /* 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
1078
1079
    if (current == NULL)
	return;
Chris Allegretta's avatar
Chris Allegretta committed
1080
1081
    if (edittop == NULL)
	edittop = current;
Chris Allegretta's avatar
Chris Allegretta committed
1082

1083
1084
    if (current->lineno < edittop->lineno ||
	    current->lineno >= edittop->lineno + editwinrows)
1085
1086
1087
1088
	/* 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. */
1089
	edit_update(current, CENTER);
1090
1091
    else {
	int nlines = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1092

1093
1094
	/* Don't make the cursor jump around the screen whilst updating */
	leaveok(edit, TRUE);
1095

1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
	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
1109
	   isn't here?  Luck? */
1110
1111
1112
	wrefresh(edit);
	leaveok(edit, FALSE);
    }
Chris Allegretta's avatar
Chris Allegretta committed
1113
1114
}

Chris Allegretta's avatar
Chris Allegretta committed
1115
1116
/* Same as above, but touch the window first, so everything is
 * redrawn. */
1117
1118
1119
1120
1121
1122
1123
void edit_refresh_clearok(void)
{
    clearok(edit, TRUE);
    edit_refresh();
    clearok(edit, FALSE);
}

Chris Allegretta's avatar
Chris Allegretta committed
1124
/*
1125
 * Nice generic routine to update the edit buffer, given a pointer to the
Chris Allegretta's avatar
Chris Allegretta committed
1126
1127
 * file struct =) 
 */
Chris Allegretta's avatar
Chris Allegretta committed
1128
void edit_update(filestruct *fileptr, topmidbotnone location)
Chris Allegretta's avatar
Chris Allegretta committed
1129
1130
1131
1132
{
    if (fileptr == NULL)
	return;

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

Chris Allegretta's avatar
Chris Allegretta committed
1136
1137
1138
1139
	for (; goal >= 0 && fileptr->prev != NULL; goal--)
	    fileptr = fileptr->prev;
    }
    edittop = fileptr;
1140
    fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
1141
1142
1143
1144
1145
1146
1147

    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
1148
 * otherwise, the valid shortcut key caught.  Def is any editable text we
Chris Allegretta's avatar
Chris Allegretta committed
1149
 * want to put up by default.
1150
1151
 *
 * New arg tabs tells whether or not to allow tab completion.
Chris Allegretta's avatar
Chris Allegretta committed
1152
 */
Chris Allegretta's avatar
Chris Allegretta committed
1153
int statusq(int tabs, const shortcut *s, const char *def,
1154
1155
1156
#ifndef NANO_SMALL
		historyheadtype *which_history,
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1157
		const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1158
1159
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1160
    char *foo = charalloc(COLS - 3);
1161
    int ret;
1162
#ifndef DISABLE_TABCOMP
1163
    int list = 0;
1164
#endif
1165
1166
    static int resetpos = 0;	/* Do we need to scrap the cursor position 
				   on the statusbar? */
1167

1168
    bottombars(s);
Chris Allegretta's avatar
Chris Allegretta committed
1169
1170

    va_start(ap, msg);
Chris Allegretta's avatar
Chris Allegretta committed
1171
    vsnprintf(foo, COLS - 4, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1172
    va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
1173
    foo[COLS - 4] = '\0';
1174

1175
1176
1177
1178
1179
    ret = nanogetstr(tabs, foo, def,
#ifndef NANO_SMALL
		which_history,
#endif
		s
1180
#ifndef DISABLE_TABCOMP
1181
		, &list
1182
#endif
1183
		, resetpos);
Chris Allegretta's avatar
Chris Allegretta committed
1184
    free(foo);
1185
    resetpos = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1186
1187
1188
1189

    switch (ret) {
    case NANO_FIRSTLINE_KEY:
	do_first_line();
1190
	resetpos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1191
1192
1193
	break;
    case NANO_LASTLINE_KEY:
	do_last_line();
1194
	resetpos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1195
1196
	break;
    case NANO_CANCEL_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
1197
	ret = -1;
1198
	resetpos = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1199
	break;
Chris Allegretta's avatar
Chris Allegretta committed
1200
    default:
1201
	blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1202
1203
1204
1205
1206
1207
    }

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

Chris Allegretta's avatar
Chris Allegretta committed
1208
1209
1210
1211
1212
1213
1214
1215
#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
1216
1217
1218
1219
    return ret;
}

/*
1220
 * Ask a simple yes/no question on the statusbar.  Returns 1 for Y, 0
1221
1222
 * for N, 2 for All (if all is nonzero when passed in) and -1 for abort
 * (^C).
Chris Allegretta's avatar
Chris Allegretta committed
1223
 */
Chris Allegretta's avatar
Chris Allegretta committed
1224
int do_yesno(int all, int leavecursor, const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1225
1226
{
    va_list ap;
1227
1228
    char *foo;
    int ok = -2;
Chris Allegretta's avatar
Chris Allegretta committed
1229
1230
1231
    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
1232

1233
    /* Yes, no and all are strings of any length.  Each string consists of
Chris Allegretta's avatar
Chris Allegretta committed
1234
1235
       all characters accepted as a valid character for that value.
       The first value will be the one displayed in the shortcuts. */
1236
1237
1238
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
1239

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

1244
1245
	/* Write the bottom of the screen */
	blank_bottombars();
1246

1247
	sprintf(shortstr, " %c", yesstr[0]);
1248
	wmove(bottomwin, 1, 0);
1249
	onekey(shortstr, _("Yes"), 16);
1250
1251

	if (all) {
1252
	    wmove(bottomwin, 1, 16);
1253
	    shortstr[1] = allstr[0];
1254
	    onekey(shortstr, _("All"), 16);
1255
1256
	}

1257
	wmove(bottomwin, 2, 0);
1258
	shortstr[1] = nostr[0];
1259
	onekey(shortstr, _("No"), 16);
1260

1261
	wmove(bottomwin, 2, 16);
1262
	onekey("^C", _("Cancel"), 16);
Chris Allegretta's avatar
Chris Allegretta committed
1263
    }
1264
1265

    foo = charalloc(COLS);
Chris Allegretta's avatar
Chris Allegretta committed
1266
    va_start(ap, msg);
1267
    vsnprintf(foo, COLS, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1268
    va_end(ap);
1269
    foo[COLS - 1] = '\0';
1270

Chris Allegretta's avatar
Chris Allegretta committed
1271
    wattron(bottomwin, A_REVERSE);
1272
1273

    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1274
    mvwaddstr(bottomwin, 0, 0, foo);
1275
    free(foo);
1276

Chris Allegretta's avatar
Chris Allegretta committed
1277
    wattroff(bottomwin, A_REVERSE);
1278

Chris Allegretta's avatar
Chris Allegretta committed
1279
1280
    wrefresh(bottomwin);

1281
1282
1283
1284
    do {
	int kbinput = wgetch(edit);
#ifndef DISABLE_MOUSE
	MEVENT mevent;
Chris Allegretta's avatar
Chris Allegretta committed
1285
#endif
1286

1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
	if (kbinput == NANO_CONTROL_C)
	    ok = -1;
#ifndef DISABLE_MOUSE
	/* 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
1308
	}
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
#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
1320
1321
    blank_statusbar_refresh();

1322
    return ok;
Chris Allegretta's avatar
Chris Allegretta committed
1323
1324
}

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
    size_t foo_len;

1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
    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
1364
1365
    assert(COLS >= 4);
    foo = charalloc(COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
1366

Chris Allegretta's avatar
Chris Allegretta committed
1367
    vsnprintf(foo, COLS - 3, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1368
1369
    va_end(ap);

Chris Allegretta's avatar
Chris Allegretta committed
1370
1371
1372
    foo[COLS - 4] = '\0';
    foo_len = strlen(foo);
    start_x = (COLS - foo_len - 4) / 2;
Chris Allegretta's avatar
Chris Allegretta committed
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382

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

Chris Allegretta's avatar
Chris Allegretta committed
1386
    wattroff(bottomwin, A_REVERSE);
1387

Chris Allegretta's avatar
Chris Allegretta committed
1388
1389
    wrefresh(bottomwin);

1390
1391
    SET(DISABLE_CURPOS);
    statblank = 26;
Chris Allegretta's avatar
Chris Allegretta committed
1392
1393
}

1394
1395
1396
1397
1398
1399
1400
1401
/*
 * 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. */
1402
int do_cursorpos(int constant)
Chris Allegretta's avatar
Chris Allegretta committed
1403
{
1404
1405
1406
1407
    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
1408

1409
    assert(current != NULL && fileage != NULL && totlines != 0);
1410
1411
1412
1413

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

1414
1415
    for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
	assert(fileptr != NULL);
1416
	i += strlen(fileptr->data) + 1;
1417
    }
1418
    i += current_x;
1419

1420
1421
1422
1423
1424
1425
    if (constant && ISSET(DISABLE_CURPOS)) {
	UNSET(DISABLE_CURPOS);
	old_i = i;
	old_totsize = totsize;
	return 0;
    }
Chris Allegretta's avatar
Chris Allegretta committed
1426

1427
    /* if constant is false, display the position on the statusbar
1428
1429
       unconditionally; otherwise, only display the position when the
       character values have changed */
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
    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);
1443
1444
1445
1446
1447
    }

    old_i = i;
    old_totsize = totsize;

Chris Allegretta's avatar
Chris Allegretta committed
1448
    reset_cursor();
1449
    return 0;
Chris Allegretta's avatar
Chris Allegretta committed
1450
1451
}

1452
1453
1454
1455
1456
int do_cursorpos_void(void)
{
    return do_cursorpos(0);
}

1457
1458
/* Our shortcut-list-compliant help function, which is
 * better than nothing, and dynamic! */
Chris Allegretta's avatar
Chris Allegretta committed
1459
1460
int do_help(void)
{
1461
#ifndef DISABLE_HELP
1462
    int i, j, row = 0, page = 1, kbinput = 0, no_more = 0, kp, kp2;
Chris Allegretta's avatar
Chris Allegretta committed
1463
    int no_help_flag = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1464
    const shortcut *oldshortcut;
Chris Allegretta's avatar
Chris Allegretta committed
1465
1466
1467

    blank_edit();
    curs_set(0);
1468
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
1469
1470
    blank_statusbar();

1471
    /* set help_text as the string to display */
1472
    help_init();
1473
    assert(help_text != NULL);
1474
1475
1476

    oldshortcut = currshortcut;

1477
    currshortcut = help_list;
1478

1479
    kp = keypad_on(edit, 1);
1480
    kp2 = keypad_on(bottomwin, 1);
1481

Chris Allegretta's avatar
Chris Allegretta committed
1482
1483
    if (ISSET(NO_HELP)) {

1484
	/* Well, if we're going to do this, we should at least
1485
	   do it the right way */
Chris Allegretta's avatar
Chris Allegretta committed
1486
	no_help_flag = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1487
	UNSET(NO_HELP);
1488
	window_init();
1489
	bottombars(help_list);
1490

Chris Allegretta's avatar
Chris Allegretta committed
1491
    } else
1492
	bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
1493
1494

    do {
1495
1496
	const char *ptr = help_text;

Chris Allegretta's avatar
Chris Allegretta committed
1497
	switch (kbinput) {
1498
#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta's avatar
Chris Allegretta committed
1499
1500
1501
	case KEY_MOUSE:
	    do_mouse();
	    break;
1502
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
	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
1529
1530
1531
	case NANO_NEXTPAGE_KEY:
	case NANO_NEXTPAGE_FKEY:
	case KEY_NPAGE:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1532
	  do_pagedownkey:
Chris Allegretta's avatar
Chris Allegretta committed
1533
1534
1535
1536
1537
1538
1539
1540
	    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
1541
	  do_pageupkey:
Chris Allegretta's avatar
Chris Allegretta committed
1542
1543
1544
1545
1546
1547
1548
1549
	    if (page > 1) {
		no_more = 0;
		blank_edit();
		page--;
	    }
	    break;
	}

1550
	/* Calculate where in the text we should be, based on the page */
Chris Allegretta's avatar
Chris Allegretta committed
1551
1552
1553
	for (i = 1; i < page; i++) {
	    row = 0;
	    j = 0;
1554
1555

	    while (row < editwinrows - 2 && *ptr != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
		if (*ptr == '\n' || j == COLS - 5) {
		    j = 0;
		    row++;
		}
		ptr++;
		j++;
	    }
	}

	i = 0;
	j = 0;
	while (i < editwinrows && *ptr != '\0') {
1568
	    const char *end = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
1569
1570
1571
1572
1573
1574
	    while (*end != '\n' && *end != '\0' && j != COLS - 5) {
		end++;
		j++;
	    }
	    if (j == COLS - 5) {

1575
		/* Don't print half a word if we've run out of space */
Chris Allegretta's avatar
Chris Allegretta committed
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
		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
1592
1593
    } while ((kbinput = wgetch(edit)) != NANO_EXIT_KEY &&
	     kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
1594

1595
1596
    currshortcut = oldshortcut;

Chris Allegretta's avatar
Chris Allegretta committed
1597
    if (no_help_flag) {
1598
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
1599
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
1600
	SET(NO_HELP);
1601
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
1602
    } else
1603
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
1604
1605
1606

    curs_set(1);
    edit_refresh();
1607
    kp = keypad_on(edit, kp);
1608
    kp2 = keypad_on(bottomwin, kp2);
1609

1610
1611
1612
1613
1614
    /* 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
1615
1616
1617
1618
#elif defined(DISABLE_HELP)
    nano_disabled_msg();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1619
1620
1621
    return 1;
}

1622
int keypad_on(WINDOW *win, int newval)
1623
{
1624
1625
1626
1627
1628
1629
1630
1631
1632
/* 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
1633
}
1634

1635
/* Highlight the current word being replaced or spell checked. */
Chris Allegretta's avatar
Chris Allegretta committed
1636
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
1637
1638
{
    char *highlight_word = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
1639
    int x, y, word_len;
Chris Allegretta's avatar
Chris Allegretta committed
1640

Chris Allegretta's avatar
Chris Allegretta committed
1641
1642
    highlight_word =
	mallocstrcpy(highlight_word, &current->data[current_x]);
Chris Allegretta's avatar
Chris Allegretta committed
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653

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

1655
    /* adjust output when word extends beyond screen */
Chris Allegretta's avatar
Chris Allegretta committed
1656
1657

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

Chris Allegretta's avatar
Chris Allegretta committed
1660
    if ((COLS - (y - x) + word_len) > COLS) {
Chris Allegretta's avatar
Chris Allegretta committed
1661
1662
1663
1664
1665
1666
1667
	highlight_word[y - x - 1] = '$';
	highlight_word[y - x] = '\0';
    }

    /* OK display the output */

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

Chris Allegretta's avatar
Chris Allegretta committed
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
    if (highlight_flag)
	wattron(edit, A_REVERSE);

    waddstr(edit, highlight_word);

    if (highlight_flag)
	wattroff(edit, A_REVERSE);

    free(highlight_word);
}

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
1710
1711
1712
1713
1714
1715
1716
1717
/* 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 */

1718
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1719
#define CREDIT_LEN 53
1720
1721
#define XLCREDIT_LEN 8

1722
1723
void do_credits(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1724
    int i, j = 0, k, place = 0, start_x;
1725

1726
1727
    const char *what;
    const char *xlcredits[XLCREDIT_LEN];
1728

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

1779
1780
1781
1782
1783
1784
1785
1786
1787
    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");

1788
1789
1790
1791
    curs_set(0);
    nodelay(edit, TRUE);
    blank_bottombars();
    mvwaddstr(topwin, 0, 0, hblank);
Chris Allegretta's avatar
Chris Allegretta committed
1792
1793
    blank_edit();
    wrefresh(edit);
1794
1795
1796
1797
    wrefresh(bottomwin);
    wrefresh(topwin);

    while (wgetch(edit) == ERR) {
Chris Allegretta's avatar
Chris Allegretta committed
1798
1799
	for (k = 0; k <= 1; k++) {
	    blank_edit();
Chris Allegretta's avatar
Chris Allegretta committed
1800
1801
	    for (i = editwinrows / 2 - 1; i >= (editwinrows / 2 - 1 - j);
		 i--) {
Chris Allegretta's avatar
Chris Allegretta committed
1802
1803
		mvwaddstr(edit, i * 2 - k, 0, hblank);

1804
		if (place - (editwinrows / 2 - 1 - i) < CREDIT_LEN) {
Chris Allegretta's avatar
Chris Allegretta committed
1805
		    what = credits[place - (editwinrows / 2 - 1 - i)];
1806
1807
1808
1809
1810
1811
1812
1813

		    /* 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
1814
1815
		    what = "";

1816
		start_x = COLS / 2 - strlen(what) / 2 - 1;
Chris Allegretta's avatar
Chris Allegretta committed
1817
1818
1819
1820
		mvwaddstr(edit, i * 2 - k, start_x, what);
	    }
	    usleep(700000);
	    wrefresh(edit);
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
	}
	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
1835
}
1836
#endif