winio.c 42.5 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
		const shortcut *s
191
#ifndef DISABLE_TABCOMP
192
		, int *list
193
#endif
194
		)
Chris Allegretta's avatar
Chris Allegretta committed
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
{
    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;

    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
212

213
#if !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE)
214
    currshortcut = s;
215
216
#endif

Chris Allegretta's avatar
Chris Allegretta committed
217
    /* Get the input! */
218

Chris Allegretta's avatar
Chris Allegretta committed
219
    nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
220

221
222
    /* Make sure any editor screen updates are displayed before getting
       input */
223
224
    wrefresh(edit);

Chris Allegretta's avatar
Chris Allegretta committed
225
    while ((kbinput = wgetch(bottomwin)) != 13) {
226
	for (t = s; t != NULL; t = t->next) {
227
228
229
230
#ifdef DEBUG
	    fprintf(stderr, _("Aha! \'%c\' (%d)\n"), kbinput, kbinput);
#endif

231
	    if (kbinput == t->val && kbinput < 32) {
232

233
#ifndef DISABLE_HELP
234
235
		/* Have to do this here, it would be too late to do it
		   in statusq() */
Chris Allegretta's avatar
Chris Allegretta committed
236
		if (kbinput == NANO_HELP_KEY || kbinput == NANO_HELP_FKEY) {
237
238
239
240
		    do_help();
		    break;
		}
#endif
241
		return t->val;
Chris Allegretta's avatar
Chris Allegretta committed
242
243
	    }
	}
Chris Allegretta's avatar
Chris Allegretta committed
244
	assert(0 <= x && x <= xend && xend == strlen(answer));
Chris Allegretta's avatar
Chris Allegretta committed
245

Chris Allegretta's avatar
Chris Allegretta committed
246
247
248
	if (kbinput != '\t')
	    tabbed = 0;

Chris Allegretta's avatar
Chris Allegretta committed
249
	switch (kbinput) {
250

Chris Allegretta's avatar
Chris Allegretta committed
251
	    /* Stuff we want to equate with <enter>, ASCII 13 */
252
	case 343:
253
254
	    ungetch(13);	/* Enter on iris-ansi $TERM, sometimes */
	    break;
Chris Allegretta's avatar
Chris Allegretta committed
255
	    /* Stuff we want to ignore */
256
257
258
#ifdef PDCURSES
	case 541:
	case 542:
Chris Allegretta's avatar
Chris Allegretta committed
259
	case 543:		/* Right ctrl again */
260
	case 544:
Chris Allegretta's avatar
Chris Allegretta committed
261
	case 545:		/* Right alt again */
262
263
	    break;
#endif
264
#ifndef DISABLE_MOUSE
265
266
267
268
269
#ifdef NCURSES_MOUSE_VERSION
	case KEY_MOUSE:
	    do_mouse();
	    break;
#endif
270
#endif
271
	case NANO_HOME_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
272
	case KEY_HOME:
Chris Allegretta's avatar
Chris Allegretta committed
273
	    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
274
	    break;
275
	case NANO_END_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
276
	case KEY_END:
Chris Allegretta's avatar
Chris Allegretta committed
277
	    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
278
279
	    break;
	case KEY_RIGHT:
280
	case NANO_FORWARD_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
281
282
283
284
	    if (x < xend)
		x++;
	    break;
	case NANO_CONTROL_D:
Chris Allegretta's avatar
Chris Allegretta committed
285
286
287
	    if (x < xend) {
		memmove(answer + x, answer + x + 1, xend - x);
		xend--;
Chris Allegretta's avatar
Chris Allegretta committed
288
289
290
291
	    }
	    break;
	case NANO_CONTROL_K:
	case NANO_CONTROL_U:
Chris Allegretta's avatar
Chris Allegretta committed
292
293
294
	    null_at(&answer, 0);
	    xend = 0;
	    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
295
296
297
298
	    break;
	case KEY_BACKSPACE:
	case 127:
	case NANO_CONTROL_H:
Chris Allegretta's avatar
Chris Allegretta committed
299
300
	    if (x > 0) {
		memmove(answer + x - 1, answer + x, xend - x + 1);
Chris Allegretta's avatar
Chris Allegretta committed
301
		x--;
Chris Allegretta's avatar
Chris Allegretta committed
302
303
		xend--;
	    }
Chris Allegretta's avatar
Chris Allegretta committed
304
	    break;
305
#ifndef DISABLE_TABCOMP
Chris Allegretta's avatar
Chris Allegretta committed
306
	case NANO_CONTROL_I:
307
	    if (allowtabs) {
Chris Allegretta's avatar
Chris Allegretta committed
308
309
310
311
		int shift = 0;

		answer = input_tab(answer, x, &tabbed, &shift, list);
		xend = strlen(answer);
312
		x += shift;
Chris Allegretta's avatar
Chris Allegretta committed
313
314
		if (x > xend)
		    x = xend;
315
	    }
Chris Allegretta's avatar
Chris Allegretta committed
316
	    break;
317
#endif
Chris Allegretta's avatar
Chris Allegretta committed
318
	case KEY_LEFT:
319
	case NANO_BACK_KEY:
Chris Allegretta's avatar
Chris Allegretta committed
320
	    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
321
322
323
324
325
326
		x--;
	    break;
	case KEY_UP:
	case KEY_DOWN:
	    break;

327
328
329
	case KEY_DC:
	    goto do_deletekey;

Chris Allegretta's avatar
Chris Allegretta committed
330
331
	case 27:
	    switch (kbinput = wgetch(edit)) {
332
	    case 'O':
Chris Allegretta's avatar
Chris Allegretta committed
333
		switch (kbinput = wgetch(edit)) {
334
		case 'F':
Chris Allegretta's avatar
Chris Allegretta committed
335
		    x = xend;
Chris Allegretta's avatar
Chris Allegretta committed
336
		    break;
337
		case 'H':
Chris Allegretta's avatar
Chris Allegretta committed
338
		    x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
339
340
341
		    break;
		}
		break;
342
	    case '[':
Chris Allegretta's avatar
Chris Allegretta committed
343
344
345
346
347
348
		switch (kbinput = wgetch(edit)) {
		case 'C':
		    if (x < xend)
			x++;
		    break;
		case 'D':
Chris Allegretta's avatar
Chris Allegretta committed
349
		    if (x > 0)
Chris Allegretta's avatar
Chris Allegretta committed
350
351
			x--;
		    break;
352
353
		case '1':
		case '7':
Chris Allegretta's avatar
Chris Allegretta committed
354
		    x = 0;
355
356
357
		    goto skip_tilde;
		case '3':
		  do_deletekey:
Chris Allegretta's avatar
Chris Allegretta committed
358
359
360
		    if (x < xend) {
			memmove(answer + x, answer + x + 1, xend - x);
			xend--;
Chris Allegretta's avatar
Chris Allegretta committed
361
		    }
362
363
364
		    goto skip_tilde;
		case '4':
		case '8':
Chris Allegretta's avatar
Chris Allegretta committed
365
		    x = xend;
366
367
		    goto skip_tilde;
		  skip_tilde:
Chris Allegretta's avatar
Chris Allegretta committed
368
369
		    nodelay(edit, TRUE);
		    kbinput = wgetch(edit);
370
		    if (kbinput == '~' || kbinput == ERR)
Chris Allegretta's avatar
Chris Allegretta committed
371
372
373
374
			kbinput = -1;
		    nodelay(edit, FALSE);
		    break;
		}
Chris Allegretta's avatar
Chris Allegretta committed
375
		break;
376
377
	    default:

378
		for (t = s; t != NULL; t = t->next) {
379
#ifdef DEBUG
Chris Allegretta's avatar
Chris Allegretta committed
380
381
		    fprintf(stderr, _("Aha! \'%c\' (%d)\n"), kbinput,
			    kbinput);
382
#endif
383
		    if (kbinput == t->val || kbinput == t->val - 32) {
384
385
386
			/* 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... */
387
			return t->val;
388
389
		    }
		}
Chris Allegretta's avatar
Chris Allegretta committed
390
391
392
393
394
395
	    }
	    break;

	default:
	    if (kbinput < 32)
		break;
Chris Allegretta's avatar
Chris Allegretta committed
396
397
398
399
	    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
400
401
402
403
404
405
	    x++;

#ifdef DEBUG
	    fprintf(stderr, _("input \'%c\' (%d)\n"), kbinput, kbinput);
#endif
	}
Chris Allegretta's avatar
Chris Allegretta committed
406
	nanoget_repaint(buf, answer, x);
Chris Allegretta's avatar
Chris Allegretta committed
407
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
408
    } /* while (kbinput ...) */
Chris Allegretta's avatar
Chris Allegretta committed
409

Chris Allegretta's avatar
Chris Allegretta committed
410
    /* In Pico mode, just check for a blank answer here */
Chris Allegretta's avatar
Chris Allegretta committed
411
    if (ISSET(PICO_MODE) && answer[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
412
413
414
415
416
	return -2;
    else
	return 0;
}

417
418
419
420
421
422
423
424
425
426
/* 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
427
void titlebar(const char *path)
Chris Allegretta's avatar
Chris Allegretta committed
428
429
{
    int namelen, space;
Chris Allegretta's avatar
Chris Allegretta committed
430
    const char *what = path;
Chris Allegretta's avatar
Chris Allegretta committed
431
432
433

    if (path == NULL)
	what = filename;
Chris Allegretta's avatar
Chris Allegretta committed
434
435

    wattron(topwin, A_REVERSE);
436

Chris Allegretta's avatar
Chris Allegretta committed
437
    mvwaddstr(topwin, 0, 0, hblank);
438
    mvwaddnstr(topwin, 0, 2, VERMSG, COLS - 3);
Chris Allegretta's avatar
Chris Allegretta committed
439

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

Chris Allegretta's avatar
Chris Allegretta committed
442
    namelen = strlen(what);
Chris Allegretta's avatar
Chris Allegretta committed
443

444
445
    if (space > 0) {
        if (what[0] == '\0')
Chris Allegretta's avatar
Chris Allegretta committed
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
      	    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
462
	}
463
    } /* If we don't have space, we shouldn't bother */
Chris Allegretta's avatar
Chris Allegretta committed
464
    if (ISSET(MODIFIED))
Chris Allegretta's avatar
Chris Allegretta committed
465
	mvwaddnstr(topwin, 0, COLS - 11, _(" Modified "), 11);
Chris Allegretta's avatar
Chris Allegretta committed
466
    else if (ISSET(VIEW_MODE))
Chris Allegretta's avatar
Chris Allegretta committed
467
	mvwaddnstr(topwin, 0, COLS - 11, _(" View "), 11);
468

Chris Allegretta's avatar
Chris Allegretta committed
469
    wattroff(topwin, A_REVERSE);
470

Chris Allegretta's avatar
Chris Allegretta committed
471
472
473
474
    wrefresh(topwin);
    reset_cursor();
}

475
void bottombars(const shortcut *s)
Chris Allegretta's avatar
Chris Allegretta committed
476
{
477
    int i, j, numcols;
478
    char keystr[4];
479
480
    int slen;

Chris Allegretta's avatar
Chris Allegretta committed
481
482
483
    if (ISSET(NO_HELP))
	return;

484
485
486
487
488
489
490
491
    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
492

493
    blank_bottomwin();
494

495
496
    for (i = 0; i < numcols; i++) {
	for (j = 0; j <= 1; j++) {
Chris Allegretta's avatar
Chris Allegretta committed
497

498
	    wmove(bottomwin, 1 + j, i * (COLS / numcols));
499

500
501
502
#ifndef NANO_SMALL
	    if (s->val == NANO_CONTROL_SPACE)
		strcpy(keystr, "^ ");
503
	    else
504
505
506
507
508
509
510
511
#endif /* !NANO_SMALL */
	    if (s->val > 0) {
		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);
512

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

515
516
517
518
	    s = s->next;
	    if (s == NULL)
		goto break_completely_out;
	}	
Chris Allegretta's avatar
Chris Allegretta committed
519
    }
520

Chris Allegretta's avatar
Chris Allegretta committed
521
  break_completely_out:
Chris Allegretta's avatar
Chris Allegretta committed
522
523
524
    wrefresh(bottomwin);
}

525
526
527
528
529
/* 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
530
{
531
532
533
534
535
536
537
538
539
540
541
    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
542
543
544
    }
}

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

547
548
#ifndef NDEBUG
int check_linenumbers(const filestruct *fileptr)
Chris Allegretta's avatar
Chris Allegretta committed
549
{
550
551
    int check_line = 0;
    const filestruct *filetmp;
552

553
554
555
    for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
	check_line++;
    return check_line;
556
}
557
#endif
558

559
560
561
 /* 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
562
int get_page_start(int column)
Chris Allegretta's avatar
Chris Allegretta committed
563
564
565
566
567
{
    assert(COLS > 9);
    return column < COLS - 1 ? 0 : column - 7 - (column - 8) % (COLS - 9);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/* 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;

    /* Yuck.  This condition can be true after open_file when opening the
     * first file. */
    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
590

591
592
593
/* 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. */
594
595
596
597
598
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
599
{
600
601
602
#ifdef DEBUG
    fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno,
		current->lineno);
603
604
605
#endif

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

Chris Allegretta's avatar
Chris Allegretta committed
609
#ifdef ENABLE_COLOR
610
    if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
	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. */
633
	    if (tmpcolor->end == NULL) {
634
635
636
637
638
639
640
641
642
643
644
645
646
		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
		     * k == 0.  If regexec returns non-zero, there are
		     * no more matches in the line. */
		    if (regexec(&start_regexp, &fileptr->data[k], 1,
				&startmatch, k == 0 ? 0 : REG_NOTBOL))
647
			break;
648
649
650
		    /* Translate the match to the beginning of the line. */
		    startmatch.rm_so += k;
		    startmatch.rm_eo += k;
651
652
		    if (startmatch.rm_so == startmatch.rm_eo) {
			startmatch.rm_eo++;
653
			statusbar(_("Refusing 0 length regex match"));
654
		    } else if (startmatch.rm_so < start + COLS &&
655
656
657
658
659
660
661
662
663
664
665
666
667
668
				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
669
		}
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
	    } 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 */
720
			break;
721
722
723
724
725
726
		    start_col++;
		    if (regexec(&start_regexp,
			    start_line->data + start_col, 1, &startmatch,
			    REG_NOTBOL))
			/* No later start on this line. */
			goto step_two;
727
		}
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
		/* 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;
		while (end_line != NULL &&
			regexec(&end_regexp, end_line->data, 1,
				&endmatch, 0))
		    end_line = end_line->next;

		/* No end found, or it is too early. */
		if (end_line == NULL ||
			end_line->lineno < fileptr->lineno ||
			(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;
777
		    }
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
		    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);
798
			}
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
		    } 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;
			while (end_line != NULL &&
				regexec(&end_regexp, end_line->data, 1,
				&endmatch, 0))
			    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;
816
817
			}
		    }
818
819
		    start_col = startmatch.rm_so + 1;
		} /* while start_col < start + COLS */
820

821
822
823
  skip_step_two:
		regfree(&end_regexp);
	    } /* if (tmp_color->end != NULL) */
824

825
826
827
828
829
	    regfree(&start_regexp);
	    wattroff(edit, A_BOLD);
	    wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
	} /* for tmpcolor in colorstrings */
    }
Chris Allegretta's avatar
Chris Allegretta committed
830
#endif				/* ENABLE_COLOR */
831

832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
#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
857

858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
	    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) {
873
	    wattron(edit, A_REVERSE);
874
875
876
	    assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS);
	    mvwaddnstr(edit, yval, x_start,
			fileptr->data + start + x_start, paintlen);
877
	    wattroff(edit, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
878
	}
879
    }
880
#endif /* !NANO_SMALL */
Chris Allegretta's avatar
Chris Allegretta committed
881
882
}

883
884
885
886
/* 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. */
887
void update_line(filestruct *fileptr, int index)
Chris Allegretta's avatar
Chris Allegretta committed
888
{
889
890
891
892
893
894
895
896
897
898
899
900
901
    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
902

Chris Allegretta's avatar
Chris Allegretta committed
903
904
    if (!fileptr)
	return;
905

906
    line = fileptr->lineno - edittop->lineno;
Chris Allegretta's avatar
Chris Allegretta committed
907

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

911
912
    if (line < 0 || line >= editwinrows)
	return;
913

914
915
916
917
918
919
920
921
922
923
924
925
926
    /* First, blank out the line (at a minimum) */
    mvwaddstr(edit, line, 0, hblank);

    original = fileptr->data;
    converted = charalloc(strlenpt(original) + 1);
    
    /* Next, convert all the tabs to spaces, so everything else is easy. 
     * 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
927
928

    pos = 0;
929
930
    for (; *original != '\0'; original++) {
	if (*original == '\t')
931
	    do {
932
		converted[pos++] = ' ';
933
	    } while (pos % tabsize);
934
935
936
937
938
939
940
941
942
943
944
945
946
	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;
947
    }
948
    converted[pos] = '\0';
Chris Allegretta's avatar
Chris Allegretta committed
949

Chris Allegretta's avatar
Chris Allegretta committed
950
    /* Now, paint the line */
951
952
953
954
955
956
957
958
959
960
    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
961

962
    if (page_start > 0)
Chris Allegretta's avatar
Chris Allegretta committed
963
	mvwaddch(edit, line, 0, '$');
964
965
    if (pos > page_start + COLS)
	mvwaddch(edit, line, COLS - 1, '$');
Chris Allegretta's avatar
Chris Allegretta committed
966
967
}

968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
/* 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
990
991
992
993
994
995
void center_cursor(void)
{
    current_y = editwinrows / 2;
    wmove(edit, current_y, current_x);
}

Chris Allegretta's avatar
Chris Allegretta committed
996
/* Refresh the screen without changing the position of lines. */
Chris Allegretta's avatar
Chris Allegretta committed
997
998
void edit_refresh(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
999
    static int noloop = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1000
    int nlines = 0, currentcheck = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1001

Chris Allegretta's avatar
Chris Allegretta committed
1002
1003
1004
    /* 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
1005
1006
    if (current == NULL)
	return;
Chris Allegretta's avatar
Chris Allegretta committed
1007
1008
    if (edittop == NULL)
	edittop = current;
Chris Allegretta's avatar
Chris Allegretta committed
1009

1010
    /* Don't make the cursor jump around the screen whilst updating */
1011
1012
    leaveok(edit, TRUE);

Chris Allegretta's avatar
Chris Allegretta committed
1013
1014
1015
1016
    editbot = edittop;
    while (nlines < editwinrows) {
	update_line(editbot, current_x);
	if (editbot == current)
1017
1018
	    currentcheck = 1;

1019
	nlines++;
Chris Allegretta's avatar
Chris Allegretta committed
1020
1021
1022
1023

	if (editbot->next == NULL)
	    break;
	editbot = editbot->next;
Chris Allegretta's avatar
Chris Allegretta committed
1024
    }
1025

Chris Allegretta's avatar
Chris Allegretta committed
1026
1027
    /* If noloop == 1, then we already did an edit_update without finishing
       this function.  So we don't run edit_update again */
Chris Allegretta's avatar
Chris Allegretta committed
1028
1029
    if (!currentcheck && !noloop) {
		/* Then current has run off the screen... */
1030
	edit_update(current, CENTER);
Chris Allegretta's avatar
Chris Allegretta committed
1031
	noloop = 1;
1032
    } else if (noloop)
Chris Allegretta's avatar
Chris Allegretta committed
1033
	noloop = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1034

Chris Allegretta's avatar
Chris Allegretta committed
1035
1036
1037
1038
    while (nlines < editwinrows) {
	mvwaddstr(edit, nlines, 0, hblank);
	nlines++;
    }
1039
1040

    /* What the hell are we expecting to update the screen if this isn't 
Chris Allegretta's avatar
Chris Allegretta committed
1041
       here? Luck?? */
1042
    wrefresh(edit);
1043
    leaveok(edit, FALSE);
Chris Allegretta's avatar
Chris Allegretta committed
1044
1045
}

1046
/*
1047
 * Same as above, but touch the window first, so everything is redrawn.
1048
1049
1050
1051
1052
1053
1054
1055
 */
void edit_refresh_clearok(void)
{
    clearok(edit, TRUE);
    edit_refresh();
    clearok(edit, FALSE);
}

Chris Allegretta's avatar
Chris Allegretta committed
1056
/*
1057
 * Nice generic routine to update the edit buffer, given a pointer to the
Chris Allegretta's avatar
Chris Allegretta committed
1058
1059
 * file struct =) 
 */
Chris Allegretta's avatar
Chris Allegretta committed
1060
void edit_update(filestruct *fileptr, topmidbotnone location)
Chris Allegretta's avatar
Chris Allegretta committed
1061
1062
1063
1064
{
    if (fileptr == NULL)
	return;

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

Chris Allegretta's avatar
Chris Allegretta committed
1068
1069
1070
1071
	for (; goal >= 0 && fileptr->prev != NULL; goal--)
	    fileptr = fileptr->prev;
    }
    edittop = fileptr;
1072
    fix_editbot();
Chris Allegretta's avatar
Chris Allegretta committed
1073
1074
1075
1076
1077
1078
1079

    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
1080
 * otherwise, the valid shortcut key caught.  Def is any editable text we
Chris Allegretta's avatar
Chris Allegretta committed
1081
 * want to put up by default.
1082
1083
 *
 * New arg tabs tells whether or not to allow tab completion.
Chris Allegretta's avatar
Chris Allegretta committed
1084
 */
Chris Allegretta's avatar
Chris Allegretta committed
1085
1086
int statusq(int tabs, const shortcut *s, const char *def,
		const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1087
1088
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1089
    char *foo = charalloc(COLS - 3);
1090
    int ret;
1091
#ifndef DISABLE_TABCOMP
1092
    int list = 0;
1093
1094
#endif

1095
    bottombars(s);
Chris Allegretta's avatar
Chris Allegretta committed
1096
1097

    va_start(ap, msg);
Chris Allegretta's avatar
Chris Allegretta committed
1098
    vsnprintf(foo, COLS - 4, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1099
    va_end(ap);
Chris Allegretta's avatar
Chris Allegretta committed
1100
    foo[COLS - 4] = '\0';
1101

1102
#ifndef DISABLE_TABCOMP
Chris Allegretta's avatar
Chris Allegretta committed
1103
    ret = nanogetstr(tabs, foo, def, s, &list);
1104
#else
Chris Allegretta's avatar
Chris Allegretta committed
1105
    ret = nanogetstr(tabs, foo, def, s);
1106
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1107
    free(foo);
Chris Allegretta's avatar
Chris Allegretta committed
1108
1109
1110
1111
1112
1113
1114
1115
1116

    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
1117
1118
	ret = -1;
	break;
Chris Allegretta's avatar
Chris Allegretta committed
1119
    default:
1120
	blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1121
1122
1123
1124
1125
1126
    }

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

Chris Allegretta's avatar
Chris Allegretta committed
1127
1128
1129
1130
1131
1132
1133
1134
#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
1135
1136
1137
1138
    return ret;
}

/*
1139
1140
1141
 * Ask a simple yes/no question on the statusbar.  Returns 1 for Y, 0
 * for N, 2 for All (if all is non-zero when passed in) and -1 for
 * abort (^C).
Chris Allegretta's avatar
Chris Allegretta committed
1142
 */
Chris Allegretta's avatar
Chris Allegretta committed
1143
int do_yesno(int all, int leavecursor, const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1144
1145
1146
{
    va_list ap;
    char foo[133];
1147
    int kbinput, ok = -1, i;
Chris Allegretta's avatar
Chris Allegretta committed
1148
1149
1150
    const char *yesstr;		/* String of yes characters accepted */
    const char *nostr;		/* Same for no */
    const char *allstr;		/* And all, surprise! */
1151
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
1152
1153
1154
1155
1156
#ifdef NCURSES_MOUSE_VERSION
    MEVENT mevent;
#endif
#endif

1157
    /* Yes, no and all are strings of any length.  Each string consists of
Chris Allegretta's avatar
Chris Allegretta committed
1158
1159
       all characters accepted as a valid character for that value.
       The first value will be the one displayed in the shortcuts. */
1160
1161
1162
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");
Chris Allegretta's avatar
Chris Allegretta committed
1163
1164

    /* Write the bottom of the screen */
1165
    blank_bottomwin();
1166

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

Chris Allegretta's avatar
Chris Allegretta committed
1171
	wmove(bottomwin, 1, 0);
1172

1173
	sprintf(shortstr, " %c", yesstr[0]);
1174
	onekey(shortstr, _("Yes"), 16);
1175
1176

	if (all) {
1177
	    shortstr[1] = allstr[0];
1178
	    onekey(shortstr, _("All"), 16);
1179
	}
Chris Allegretta's avatar
Chris Allegretta committed
1180
	wmove(bottomwin, 2, 0);
1181

1182
	shortstr[1] = nostr[0];
1183
	onekey(shortstr, _("No"), 16);
1184

1185
	onekey("^C", _("Cancel"), 16);
Chris Allegretta's avatar
Chris Allegretta committed
1186
1187
1188
1189
    }
    va_start(ap, msg);
    vsnprintf(foo, 132, msg, ap);
    va_end(ap);
1190

Chris Allegretta's avatar
Chris Allegretta committed
1191
    wattron(bottomwin, A_REVERSE);
1192
1193

    blank_statusbar();
Chris Allegretta's avatar
Chris Allegretta committed
1194
    mvwaddstr(bottomwin, 0, 0, foo);
1195

Chris Allegretta's avatar
Chris Allegretta committed
1196
    wattroff(bottomwin, A_REVERSE);
1197

Chris Allegretta's avatar
Chris Allegretta committed
1198
1199
1200
1201
1202
1203
1204
1205
1206
    wrefresh(bottomwin);

    if (leavecursor == 1)
	reset_cursor();

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

	switch (kbinput) {
1207
#ifndef DISABLE_MOUSE
Chris Allegretta's avatar
Chris Allegretta committed
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
#ifdef NCURSES_MOUSE_VERSION
	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
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
		   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
1233
1234
1235
1236
	    }
	    break;
#endif
#endif
Chris Allegretta's avatar
Chris Allegretta committed
1237
1238
1239
	case NANO_CONTROL_C:
	    ok = -2;
	    break;
1240
1241
	default:

1242
	    /* Look for the kbinput in the yes, no and (optimally) all str */
Chris Allegretta's avatar
Chris Allegretta committed
1243
	    for (i = 0; yesstr[i] != 0 && yesstr[i] != kbinput; i++);
1244
	    if (yesstr[i] != 0) {
1245
		ok = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1246
		break;
1247
1248
	    }

Chris Allegretta's avatar
Chris Allegretta committed
1249
	    for (i = 0; nostr[i] != 0 && nostr[i] != kbinput; i++);
1250
1251
	    if (nostr[i] != 0) {
		ok = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1252
		break;
1253
1254
1255
	    }

	    if (all) {
Chris Allegretta's avatar
Chris Allegretta committed
1256
		for (i = 0; allstr[i] != 0 && allstr[i] != kbinput; i++);
1257
1258
		if (allstr[i] != 0) {
		    ok = 2;
Chris Allegretta's avatar
Chris Allegretta committed
1259
		    break;
1260
1261
		}
	    }
Chris Allegretta's avatar
Chris Allegretta committed
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
	}
    }

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

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

1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
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
1296
void statusbar(const char *msg, ...)
Chris Allegretta's avatar
Chris Allegretta committed
1297
1298
{
    va_list ap;
Chris Allegretta's avatar
Chris Allegretta committed
1299
    char *foo;
Chris Allegretta's avatar
Chris Allegretta committed
1300
    int start_x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1301
1302
1303
1304
    size_t foo_len;

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

    va_start(ap, msg);
Chris Allegretta's avatar
Chris Allegretta committed
1307
    vsnprintf(foo, COLS - 3, msg, ap);
Chris Allegretta's avatar
Chris Allegretta committed
1308
1309
    va_end(ap);

Chris Allegretta's avatar
Chris Allegretta committed
1310
1311
1312
    foo[COLS - 4] = '\0';
    foo_len = strlen(foo);
    start_x = (COLS - foo_len - 4) / 2;
Chris Allegretta's avatar
Chris Allegretta committed
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322

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

Chris Allegretta's avatar
Chris Allegretta committed
1326
    wattroff(bottomwin, A_REVERSE);
1327

Chris Allegretta's avatar
Chris Allegretta committed
1328
1329
1330
1331
1332
1333
1334
1335
    wrefresh(bottomwin);

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

1336
int do_cursorpos(int constant)
Chris Allegretta's avatar
Chris Allegretta committed
1337
1338
{
    filestruct *fileptr;
1339
    float linepct = 0.0, bytepct = 0.0, colpct = 0.0;
1340
    long i = 0, j = 0;
1341
    static long old_i = -1, old_totsize = -1;
Chris Allegretta's avatar
Chris Allegretta committed
1342
1343
1344
1345

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

1346
1347
1348
1349
1350
1351
    if (old_i == -1)
	old_i = i;

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

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

1354
1355
1356
    for (fileptr = fileage; fileptr != current && fileptr != NULL;
	 fileptr = fileptr->next)
	i += strlen(fileptr->data) + 1;
1357

1358
1359
    if (fileptr == NULL)
	return -1;
1360

1361
    i += current_x;
1362

1363
    j = totsize;
1364

1365
1366
    if (totsize > 0)
	bytepct = 100 * i / totsize;
1367
1368
1369

    if (totlines > 0)
	 linepct = 100 * current->lineno / totlines;
Chris Allegretta's avatar
Chris Allegretta committed
1370
1371
1372
1373
1374
1375

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

1376
1377
1378
1379
    /* 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
1380
	statusbar(_
1381
		  ("line %d/%d (%.0f%%), col %ld/%ld (%.0f%%), char %ld/%ld (%.0f%%)"),
1382
		  current->lineno, totlines, linepct, xplustabs() + 1, 
Chris Allegretta's avatar
Chris Allegretta committed
1383
		  strlenpt(current->data) + 1, colpct, i, j, bytepct);
1384
1385
1386
1387
1388
    }

    old_i = i;
    old_totsize = totsize;

Chris Allegretta's avatar
Chris Allegretta committed
1389
1390
1391
1392
    reset_cursor();
    return 1;
}

1393
1394
1395
1396
1397
int do_cursorpos_void(void)
{
    return do_cursorpos(0);
}

1398
1399
/* Our shortcut-list-compliant help function, which is
 * better than nothing, and dynamic! */
Chris Allegretta's avatar
Chris Allegretta committed
1400
1401
int do_help(void)
{
1402
#ifndef DISABLE_HELP
1403
    int i, j, row = 0, page = 1, kbinput = 0, no_more = 0, kp, kp2;
Chris Allegretta's avatar
Chris Allegretta committed
1404
    int no_help_flag = 0;
Chris Allegretta's avatar
Chris Allegretta committed
1405
    const shortcut *oldshortcut;
Chris Allegretta's avatar
Chris Allegretta committed
1406
1407
1408

    blank_edit();
    curs_set(0);
1409
    wattroff(bottomwin, A_REVERSE);
Chris Allegretta's avatar
Chris Allegretta committed
1410
1411
    blank_statusbar();

1412
    /* set help_text as the string to display */
1413
    help_init();
1414
    assert(help_text != NULL);
1415
1416
1417

    oldshortcut = currshortcut;

1418
    currshortcut = help_list;
1419

1420
    kp = keypad_on(edit, 1);
1421
    kp2 = keypad_on(bottomwin, 1);
1422

Chris Allegretta's avatar
Chris Allegretta committed
1423
1424
    if (ISSET(NO_HELP)) {

1425
	/* Well, if we're going to do this, we should at least
1426
	   do it the right way */
Chris Allegretta's avatar
Chris Allegretta committed
1427
	no_help_flag = 1;
Chris Allegretta's avatar
Chris Allegretta committed
1428
	UNSET(NO_HELP);
1429
	window_init();
1430
	bottombars(help_list);
1431

Chris Allegretta's avatar
Chris Allegretta committed
1432
    } else
1433
	bottombars(help_list);
Chris Allegretta's avatar
Chris Allegretta committed
1434
1435

    do {
1436
1437
	const char *ptr = help_text;

Chris Allegretta's avatar
Chris Allegretta committed
1438
	switch (kbinput) {
1439
#ifndef DISABLE_MOUSE
1440
#ifdef NCURSES_MOUSE_VERSION
Chris Allegretta's avatar
Chris Allegretta committed
1441
1442
1443
	case KEY_MOUSE:
	    do_mouse();
	    break;
1444
1445
#endif
#endif
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
	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
1472
1473
1474
	case NANO_NEXTPAGE_KEY:
	case NANO_NEXTPAGE_FKEY:
	case KEY_NPAGE:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1475
	  do_pagedownkey:
Chris Allegretta's avatar
Chris Allegretta committed
1476
1477
1478
1479
1480
1481
1482
1483
	    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
1484
	  do_pageupkey:
Chris Allegretta's avatar
Chris Allegretta committed
1485
1486
1487
1488
1489
1490
1491
1492
	    if (page > 1) {
		no_more = 0;
		blank_edit();
		page--;
	    }
	    break;
	}

1493
	/* Calculate where in the text we should be, based on the page */
Chris Allegretta's avatar
Chris Allegretta committed
1494
1495
1496
	for (i = 1; i < page; i++) {
	    row = 0;
	    j = 0;
1497
1498

	    while (row < editwinrows - 2 && *ptr != '\0') {
Chris Allegretta's avatar
Chris Allegretta committed
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
		if (*ptr == '\n' || j == COLS - 5) {
		    j = 0;
		    row++;
		}
		ptr++;
		j++;
	    }
	}

	i = 0;
	j = 0;
	while (i < editwinrows && *ptr != '\0') {
1511
	    const char *end = ptr;
Chris Allegretta's avatar
Chris Allegretta committed
1512
1513
1514
1515
1516
1517
	    while (*end != '\n' && *end != '\0' && j != COLS - 5) {
		end++;
		j++;
	    }
	    if (j == COLS - 5) {

1518
		/* Don't print half a word if we've run out of space */
Chris Allegretta's avatar
Chris Allegretta committed
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
		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
1535
1536
    } while ((kbinput = wgetch(edit)) != NANO_EXIT_KEY &&
	     kbinput != NANO_EXIT_FKEY);
Chris Allegretta's avatar
Chris Allegretta committed
1537

1538
1539
    currshortcut = oldshortcut;

Chris Allegretta's avatar
Chris Allegretta committed
1540
    if (no_help_flag) {
1541
	blank_bottombars();
Chris Allegretta's avatar
Chris Allegretta committed
1542
	wrefresh(bottomwin);
Chris Allegretta's avatar
Chris Allegretta committed
1543
	SET(NO_HELP);
1544
	window_init();
Chris Allegretta's avatar
Chris Allegretta committed
1545
    } else
1546
	bottombars(currshortcut);
Chris Allegretta's avatar
Chris Allegretta committed
1547
1548
1549

    curs_set(1);
    edit_refresh();
1550
    kp = keypad_on(edit, kp);
1551
    kp2 = keypad_on(bottomwin, kp2);
1552

1553
1554
1555
1556
1557
    /* 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
1558
1559
1560
1561
#elif defined(DISABLE_HELP)
    nano_disabled_msg();
#endif

Chris Allegretta's avatar
Chris Allegretta committed
1562
1563
1564
    return 1;
}

1565
int keypad_on(WINDOW * win, int newval)
1566
{
1567
1568
1569
1570
1571
1572
1573
1574
1575
/* 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
1576
}
1577

1578
/* Highlight the current word being replaced or spell checked. */
Chris Allegretta's avatar
Chris Allegretta committed
1579
void do_replace_highlight(int highlight_flag, const char *word)
Chris Allegretta's avatar
Chris Allegretta committed
1580
1581
{
    char *highlight_word = NULL;
Chris Allegretta's avatar
Chris Allegretta committed
1582
    int x, y, word_len;
Chris Allegretta's avatar
Chris Allegretta committed
1583

Chris Allegretta's avatar
Chris Allegretta committed
1584
1585
    highlight_word =
	mallocstrcpy(highlight_word, &current->data[current_x]);
Chris Allegretta's avatar
Chris Allegretta committed
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596

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

1598
    /* adjust output when word extends beyond screen */
Chris Allegretta's avatar
Chris Allegretta committed
1599
1600

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

Chris Allegretta's avatar
Chris Allegretta committed
1603
    if ((COLS - (y - x) + word_len) > COLS) {
Chris Allegretta's avatar
Chris Allegretta committed
1604
1605
1606
1607
1608
1609
1610
	highlight_word[y - x - 1] = '$';
	highlight_word[y - x] = '\0';
    }

    /* OK display the output */

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

Chris Allegretta's avatar
Chris Allegretta committed
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
    if (highlight_flag)
	wattron(edit, A_REVERSE);

    waddstr(edit, highlight_word);

    if (highlight_flag)
	wattroff(edit, A_REVERSE);

    free(highlight_word);
}

1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
/* 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 */

1661
#ifdef NANO_EXTRA
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1662
#define CREDIT_LEN 53
1663
1664
#define XLCREDIT_LEN 8

1665
1666
void do_credits(void)
{
Chris Allegretta's avatar
Chris Allegretta committed
1667
    int i, j = 0, k, place = 0, start_x;
1668

1669
1670
    const char *what;
    const char *xlcredits[XLCREDIT_LEN];
1671

1672
    const char *credits[CREDIT_LEN] = { 
1673
1674
	"0",				/* "The nano text editor" */
	"1",				/* "version" */
Chris Allegretta's avatar
Chris Allegretta committed
1675
1676
	VERSION,
	"",
1677
	"2",				/* "Brought to you by:" */
Chris Allegretta's avatar
Chris Allegretta committed
1678
1679
1680
1681
1682
	"Chris Allegretta",
	"Jordi Mallach",
	"Adam Rogoyski",
	"Rob Siemborski",
	"Rocco Corsi",
1683
	"David Lawrence Ramsey",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1684
	"David Benbennick",
Chris Allegretta's avatar
Chris Allegretta committed
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
	"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",
	"",
1700
	"3",				/* "Special thanks to:" */
Chris Allegretta's avatar
Chris Allegretta committed
1701
1702
1703
1704
1705
1706
	"Plattsburgh State University",
	"Benet Laboratories",
	"Amy Allegretta",
	"Linda Young",
	"Jeremy Robichaud",
	"Richard Kolb II",
1707
	"4",				/* "The Free Software Foundation" */
Chris Allegretta's avatar
Chris Allegretta committed
1708
	"Linus Torvalds",
1709
	"5",				/* "For ncurses:" */
1710
1711
1712
1713
	"Thomas Dickey",
	"Pavel Curtis",
	"Zeyd Ben-Halim",
	"Eric S. Raymond",
1714
1715
	"6",				/* "and anyone else we forgot..." */
	"7",				/* "Thank you for using nano!\n" */
Chris Allegretta's avatar
Chris Allegretta committed
1716
1717
1718
	"", "", "", "",
	"(c) 1999-2002 Chris Allegretta",
	"", "", "", "",
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1719
	"http://www.nano-editor.org/"
1720
1721
    };

1722
1723
1724
1725
1726
1727
1728
1729
1730
    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");

1731
1732
1733
1734
    curs_set(0);
    nodelay(edit, TRUE);
    blank_bottombars();
    mvwaddstr(topwin, 0, 0, hblank);
Chris Allegretta's avatar
Chris Allegretta committed
1735
1736
    blank_edit();
    wrefresh(edit);
1737
1738
1739
1740
    wrefresh(bottomwin);
    wrefresh(topwin);

    while (wgetch(edit) == ERR) {
Chris Allegretta's avatar
Chris Allegretta committed
1741
1742
	for (k = 0; k <= 1; k++) {
	    blank_edit();
Chris Allegretta's avatar
Chris Allegretta committed
1743
1744
	    for (i = editwinrows / 2 - 1; i >= (editwinrows / 2 - 1 - j);
		 i--) {
Chris Allegretta's avatar
Chris Allegretta committed
1745
1746
		mvwaddstr(edit, i * 2 - k, 0, hblank);

1747
		if (place - (editwinrows / 2 - 1 - i) < CREDIT_LEN) {
Chris Allegretta's avatar
Chris Allegretta committed
1748
		    what = credits[place - (editwinrows / 2 - 1 - i)];
1749
1750
1751
1752
1753
1754
1755
1756

		    /* 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
1757
1758
		    what = "";

1759
		start_x = COLS / 2 - strlen(what) / 2 - 1;
Chris Allegretta's avatar
Chris Allegretta committed
1760
1761
1762
1763
		mvwaddstr(edit, i * 2 - k, start_x, what);
	    }
	    usleep(700000);
	    wrefresh(edit);
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
	}
	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
1778
}
1779
#endif