blob: 521850806717df104aa2ae4ef6e9b4b98f3c8999 [file] [log] [blame]
Chris Allegretta11b00112000-08-06 21:13:45 +00001/* $Id$ */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002/**************************************************************************
3 * nano.c *
4 * *
Jordi Mallach8ae57892002-01-04 17:57:40 +00005 * Copyright (C) 1999-2002 Chris Allegretta *
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00006 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
Chris Allegretta3a24f3f2001-10-24 11:33:54 +00008 * the Free Software Foundation; either version 2, or (at your option) *
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00009 * any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
19 * *
20 **************************************************************************/
21
Chris Allegretta6efda542001-04-28 18:03:52 +000022#include "config.h"
23
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000024#include <stdio.h>
25#include <stdlib.h>
26#include <stdarg.h>
27#include <signal.h>
Chris Allegretta08020882001-01-29 23:37:54 +000028#include <setjmp.h>
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000029#include <unistd.h>
30#include <string.h>
31#include <fcntl.h>
32#include <sys/stat.h>
33#include <sys/ioctl.h>
34#include <sys/param.h>
Chris Allegretta27eb13f2000-11-05 16:52:21 +000035#include <sys/types.h>
36#include <sys/wait.h>
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000037#include <errno.h>
38#include <ctype.h>
39#include <locale.h>
40#include <limits.h>
Adam Rogoyski77f36de2000-06-07 03:56:54 +000041#include <assert.h>
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000042#include "proto.h"
43#include "nano.h"
44
Chris Allegretta6232d662002-05-12 19:52:15 +000045#ifdef ENABLE_NLS
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000046#include <libintl.h>
47#define _(string) gettext(string)
48#else
49#define _(string) (string)
50#endif
51
52#ifdef HAVE_TERMIOS_H
53#include <termios.h>
54#endif
55
56#ifdef HAVE_TERMIO_H
57#include <termio.h>
58#endif
59
60#ifdef HAVE_GETOPT_H
61#include <getopt.h>
62#endif
63
Chris Allegretta6fe61492001-05-21 12:56:25 +000064#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +000065static int fill = 0; /* Fill - where to wrap lines, basically */
Chris Allegretta6fe61492001-05-21 12:56:25 +000066#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +000067
Chris Allegretta6df90f52002-07-19 01:08:59 +000068static struct termios oldterm; /* The user's original term settings */
Chris Allegretta88520c92001-05-05 17:45:54 +000069static struct sigaction act; /* For all our fun signal handlers */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000070
Chris Allegretta08020882001-01-29 23:37:54 +000071static sigjmp_buf jmpbuf; /* Used to return to mainloop after SIGWINCH */
72
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000073/* What we do when we're all set to exit */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +000074RETSIGTYPE finish(int sigage)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000075{
Chris Allegrettac08f50d2001-01-06 18:12:43 +000076 keypad(edit, TRUE);
77 keypad(bottomwin, TRUE);
78
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000079 if (!ISSET(NO_HELP)) {
80 mvwaddstr(bottomwin, 1, 0, hblank);
81 mvwaddstr(bottomwin, 2, 0, hblank);
Chris Allegretta4da1fc62000-06-21 03:00:43 +000082 } else
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000083 mvwaddstr(bottomwin, 0, 0, hblank);
Chris Allegretta6b58acd2001-04-12 03:01:53 +000084
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000085 wrefresh(bottomwin);
86 endwin();
87
88 /* Restore the old term settings */
Chris Allegretta4da1fc62000-06-21 03:00:43 +000089 tcsetattr(0, TCSANOW, &oldterm);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000090
Chris Allegretta6232d662002-05-12 19:52:15 +000091#ifdef DEBUG
Chris Allegrettaf5de33a2002-02-27 04:14:16 +000092 thanks_for_all_the_fish();
Chris Allegretta6232d662002-05-12 19:52:15 +000093#endif
Chris Allegrettaf5de33a2002-02-27 04:14:16 +000094
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000095 exit(sigage);
96}
97
98/* Die (gracefully?) */
Chris Allegretta6df90f52002-07-19 01:08:59 +000099void die(const char *msg, ...)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000100{
101 va_list ap;
102
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000103 /* Restore the old term settings */
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000104 tcsetattr(0, TCSANOW, &oldterm);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000105
106 clear();
107 refresh();
108 resetty();
109 endwin();
110
Chris Allegretta6df90f52002-07-19 01:08:59 +0000111 va_start(ap, msg);
112 vfprintf(stderr, msg, ap);
113 va_end(ap);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000114
Chris Allegretta32da4562002-01-02 15:12:21 +0000115 /* save the currently loaded file if it's been modified */
116 if (ISSET(MODIFIED))
117 die_save_file(filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000118
Chris Allegretta355fbe52001-07-14 19:32:47 +0000119#ifdef ENABLE_MULTIBUFFER
Chris Allegretta32da4562002-01-02 15:12:21 +0000120 /* then save all of the other modified loaded files, if any */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000121 if (open_files) {
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000122 openfilestruct *tmp;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000123
124 tmp = open_files;
125
126 while (open_files->prev)
127 open_files = open_files->prev;
128
129 while (open_files->next) {
130
131 /* if we already saved the file above (i. e. if it was the
132 currently loaded file), don't save it again */
133 if (tmp != open_files) {
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000134 /* make sure open_files->fileage and fileage, and
135 open_files->filebot and filebot, are in sync; they
136 might not be if lines have been cut from the top or
137 bottom of the file */
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000138 fileage = open_files->fileage;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000139 filebot = open_files->filebot;
Chris Allegretta32da4562002-01-02 15:12:21 +0000140 /* save the file if it's been modified */
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000141 if (open_files->file_flags & MODIFIED)
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000142 die_save_file(open_files->filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000143 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000144 open_files = open_files->next;
145 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000146 }
147#endif
148
Chris Allegretta6df90f52002-07-19 01:08:59 +0000149 exit(1); /* We have a problem: exit w/ errorlevel(1) */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000150}
151
Chris Allegretta6df90f52002-07-19 01:08:59 +0000152void die_save_file(const char *die_filename)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000153{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000154 char *ret;
Chris Allegretta48b06702002-02-22 04:30:50 +0000155 int i = -1;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000156
Chris Allegretta6df90f52002-07-19 01:08:59 +0000157 /* If we can't save, we have REAL bad problems, but we might as well
158 TRY. */
159 if (die_filename[0] == '\0')
160 ret = get_next_filename("nano.save");
Chris Allegretta48b06702002-02-22 04:30:50 +0000161 else {
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000162 char *buf = charalloc(strlen(die_filename) + 6);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000163
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000164 strcpy(buf, die_filename);
165 strcat(buf, ".save");
Chris Allegretta48b06702002-02-22 04:30:50 +0000166 ret = get_next_filename(buf);
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000167 free(buf);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000168 }
Chris Allegretta6df90f52002-07-19 01:08:59 +0000169 if (ret[0] != '\0')
170 i = write_file(ret, 1, 0, 0);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000171
Chris Allegretta3dbb2782000-12-02 04:36:50 +0000172 if (i != -1)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000173 fprintf(stderr, _("\nBuffer written to %s\n"), ret);
Chris Allegretta3dbb2782000-12-02 04:36:50 +0000174 else
Chris Allegretta6df90f52002-07-19 01:08:59 +0000175 fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), ret);
Chris Allegretta48b06702002-02-22 04:30:50 +0000176
177 free(ret);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000178}
179
Chris Allegrettae61e8302001-01-14 05:18:27 +0000180/* Die with an error message that the screen was too small if, well, the
Chris Allegretta6df90f52002-07-19 01:08:59 +0000181 * screen is too small. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000182void die_too_small(void)
Chris Allegrettae61e8302001-01-14 05:18:27 +0000183{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000184 die(_("Window size is too small for nano...\n"));
Chris Allegrettae61e8302001-01-14 05:18:27 +0000185}
186
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000187void print_view_warning(void)
188{
189 statusbar(_("Key illegal in VIEW mode"));
190}
191
Chris Allegretta56214c62001-09-27 02:46:53 +0000192/* Initialize global variables - no better way for now. If
Chris Allegretta6df90f52002-07-19 01:08:59 +0000193 * save_cutbuffer is nonzero, don't set cutbuffer to NULL. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000194void global_init(int save_cutbuffer)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000195{
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000196 current_x = 0;
197 current_y = 0;
Chris Allegrettae61e8302001-01-14 05:18:27 +0000198
199 if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
200 die_too_small();
201
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000202 fileage = NULL;
Chris Allegretta56214c62001-09-27 02:46:53 +0000203 if (!save_cutbuffer)
204 cutbuffer = NULL;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000205 current = NULL;
206 edittop = NULL;
207 editbot = NULL;
208 totlines = 0;
Chris Allegretta56214c62001-09-27 02:46:53 +0000209 totsize = 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000210 placewewant = 0;
Chris Allegrettae61e8302001-01-14 05:18:27 +0000211
Chris Allegretta6fe61492001-05-21 12:56:25 +0000212#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +0000213 fill = wrap_at;
Chris Allegrettadffa2072002-07-24 01:02:26 +0000214 if (fill <= 0)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000215 fill += COLS;
Chris Allegrettae61e8302001-01-14 05:18:27 +0000216 if (fill < MIN_FILL_LENGTH)
217 die_too_small();
Chris Allegretta6fe61492001-05-21 12:56:25 +0000218#endif
Chris Allegrettae61e8302001-01-14 05:18:27 +0000219
Chris Allegretta88b09152001-05-17 11:35:43 +0000220 hblank = charalloc(COLS + 1);
Chris Allegretta0a06e072001-01-23 02:35:04 +0000221 memset(hblank, ' ', COLS);
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000222 hblank[COLS] = '\0';
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000223}
224
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000225void window_init(void)
226{
227 if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
228 die_too_small();
229
230 /* Set up the main text window */
231 edit = newwin(editwinrows, COLS, 2, 0);
232
233 /* And the other windows */
234 topwin = newwin(2, COLS, 0, 0);
235 bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
236
237#ifdef PDCURSES
238 /* Oops, I guess we need this again.
239 Moved here so the keypad still works after a Meta-X, for example */
240 keypad(edit, TRUE);
241 keypad(bottomwin, TRUE);
242#endif
243}
244
245void mouse_init(void)
246{
247#ifndef DISABLE_MOUSE
248#ifdef NCURSES_MOUSE_VERSION
249 if (ISSET(USE_MOUSE)) {
250 keypad_on(edit, 1);
251 keypad_on(bottomwin, 1);
252
253 mousemask(BUTTON1_RELEASED, NULL);
254 mouseinterval(50);
255 } else
256 mousemask(0, NULL);
257#endif
258#endif
259}
260
261#ifndef DISABLE_HELP
262/* This function allocates help_text, and stores the help string in it.
263 * help_text should be NULL initially. */
264void help_init(void)
265{
266 size_t allocsize = 1; /* space needed for help_text */
267 char *ptr = NULL;
268#ifndef NANO_SMALL
269 const toggle *t;
270#endif
271 const shortcut *s;
272
273 /* First set up the initial help text for the current function */
274 if (currshortcut == whereis_list || currshortcut == replace_list
275 || currshortcut == replace_list_2)
276 ptr = _("Search Command Help Text\n\n "
277 "Enter the words or characters you would like to search "
278 "for, then hit enter. If there is a match for the text you "
279 "entered, the screen will be updated to the location of the "
280 "nearest match for the search string.\n\n "
281 "If using Pico Mode via the -p or --pico flags, the "
282 "Meta-P toggle, or a nanorc file, the previous search "
283 "string will be shown in brackets after the Search: prompt. "
284 "Hitting Enter without entering any text will perform the "
285 "previous search. Otherwise, the previous string will be "
286 "placed before the cursor, and can be edited or deleted "
287 "before hitting enter.\n\n The following function keys are "
288 "available in Search mode:\n\n");
289 else if (currshortcut == goto_list)
290 ptr = _("Go To Line Help Text\n\n "
291 "Enter the line number that you wish to go to and hit "
292 "Enter. If there are fewer lines of text than the "
293 "number you entered, you will be brought to the last line "
294 "of the file.\n\n The following function keys are "
295 "available in Go To Line mode:\n\n");
296 else if (currshortcut == insertfile_list)
297 ptr = _("Insert File Help Text\n\n "
298 "Type in the name of a file to be inserted into the current "
299 "file buffer at the current cursor location.\n\n "
300 "If you have compiled nano with multiple file buffer "
301 "support, and enable multiple buffers with the -F "
302 "or --multibuffer command line flags, the Meta-F toggle, or "
303 "a nanorc file, inserting a file will cause it to be "
304 "loaded into a separate buffer (use Meta-< and > to switch "
305 "between file buffers).\n\n If you need another blank "
306 "buffer, do not enter any filename, or type in a "
307 "nonexistent filename at the prompt and press "
308 "Enter.\n\n The following function keys are "
309 "available in Insert File mode:\n\n");
310 else if (currshortcut == writefile_list)
311 ptr = _("Write File Help Text\n\n "
312 "Type the name that you wish to save the current file "
313 "as and hit Enter to save the file.\n\n If you have "
314 "selected text with Ctrl-^, you will be prompted to "
315 "save only the selected portion to a separate file. To "
316 "reduce the chance of overwriting the current file with "
317 "just a portion of it, the current filename is not the "
318 "default in this mode.\n\n The following function keys "
319 "are available in Write File mode:\n\n");
320#ifndef DISABLE_BROWSER
321 else if (currshortcut == browser_list)
322 ptr = _("File Browser Help Text\n\n "
323 "The file browser is used to visually browse the "
324 "directory structure to select a file for reading "
325 "or writing. You may use the arrow keys or Page Up/"
326 "Down to browse through the files, and S or Enter to "
327 "choose the selected file or enter the selected "
328 "directory. To move up one level, select the directory "
329 "called \"..\" at the top of the file list.\n\n The "
330 "following function keys are available in the file "
331 "browser:\n\n");
332 else if (currshortcut == gotodir_list)
333 ptr = _("Browser Go To Directory Help Text\n\n "
334 "Enter the name of the directory you would like to "
335 "browse to.\n\n If tab completion has not been disabled, "
336 "you can use the TAB key to (attempt to) automatically "
337 "complete the directory name.\n\n The following function "
338 "keys are available in Browser Go To Directory mode:\n\n");
339#endif
340 else if (currshortcut == spell_list)
341 ptr = _("Spell Check Help Text\n\n "
342 "The spell checker checks the spelling of all text "
343 "in the current file. When an unknown word is "
344 "encountered, it is highlighted and a replacement can "
345 "be edited. It will then prompt to replace every "
346 "instance of the given misspelled word in the "
347 "current file.\n\n The following other functions are "
348 "available in Spell Check mode:\n\n");
349#ifndef NANO_SMALL
350 else if (currshortcut == extcmd_list)
351 ptr = _("External Command Help Text\n\n "
352 "This menu allows you to insert the output of a command "
353 "run by the shell into the current buffer (or a new "
354 "buffer in multibuffer mode).\n\n The following keys are "
355 "available in this mode:\n\n");
356#endif
357 else /* Default to the main help list */
358 ptr = _(" nano help text\n\n "
359 "The nano editor is designed to emulate the functionality and "
360 "ease-of-use of the UW Pico text editor. There are four main "
361 "sections of the editor: The top line shows the program "
362 "version, the current filename being edited, and whether "
363 "or not the file has been modified. Next is the main editor "
364 "window showing the file being edited. The status line is "
365 "the third line from the bottom and shows important messages. "
366 "The bottom two lines show the most commonly used shortcuts "
367 "in the editor.\n\n "
368 "The notation for shortcuts is as follows: Control-key "
369 "sequences are notated with a caret (^) symbol and are entered "
370 "with the Control (Ctrl) key. Escape-key sequences are notated "
371 "with the Meta (M) symbol and can be entered using either the "
372 "Esc, Alt or Meta key depending on your keyboard setup. The "
373 "following keystrokes are available in the main editor window. "
374 "Alternative keys are shown in parentheses:\n\n");
375
376 allocsize += strlen(ptr);
377
378 /* The space needed for the shortcut lists, at most COLS characters,
379 * plus '\n'. */
380 allocsize += (COLS + 1) * length_of_list(currshortcut);
381
382#ifndef NANO_SMALL
383 /* If we're on the main list, we also count the toggle help text.
384 * Each line has "M-%c\t\t\t", which fills 24 columns, plus at most
385 * COLS - 24 characters, plus '\n'.*/
386 if (currshortcut == main_list)
387 for (t = toggles; t != NULL; t = t->next)
388 allocsize += COLS - 17;
389#endif /* !NANO_SMALL */
390
391 /* help_text has been freed and set to NULL unless the user resized
392 * while in the help screen. */
393 free(help_text);
394
395 /* Allocate space for the help text */
396 help_text = charalloc(allocsize);
397
398 /* Now add the text we want */
399 strcpy(help_text, ptr);
400 ptr = help_text + strlen(help_text);
401
402 /* Now add our shortcut info */
403 for (s = currshortcut; s != NULL; s = s->next) {
404 /* true if the character in s->altval is shown in first column */
405 int meta_shortcut = 0;
406
407 if (s->val > 0 && s->val < 32)
408 ptr += sprintf(ptr, "^%c", s->val + 64);
409#ifndef NANO_SMALL
410 else if (s->val == NANO_CONTROL_SPACE)
411 ptr += sprintf(ptr, "^%.6s", _("Space"));
412 else if (s->altval == NANO_ALT_SPACE) {
413 meta_shortcut = 1;
414 ptr += sprintf(ptr, "M-%.5s", _("Space"));
415 }
416#endif
417 else if (s->altval > 0) {
418 meta_shortcut = 1;
419 ptr += sprintf(ptr, "M-%c", s->altval -
420 (('A' <= s->altval && s->altval <= 'Z') ||
421 'a' <= s->altval ? 32 : 0));
422 }
423 /* Hack */
424 else if (s->val >= 'a') {
425 meta_shortcut = 1;
426 ptr += sprintf(ptr, "M-%c", s->val - 32);
427 }
428
429 *(ptr++) = '\t';
430
431 if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
432 ptr += sprintf(ptr, "(F%d)", s->misc1 - KEY_F0);
433
434 *(ptr++) = '\t';
435
436 if (!meta_shortcut && s->altval > 0)
437 ptr += sprintf(ptr, "(M-%c)", s->altval -
438 (('A' <= s->altval && s->altval <= 'Z') || 'a' <= s->altval
439 ? 32 : 0));
440
441 *(ptr++) = '\t';
442
443 assert(s->help != NULL);
444 ptr += sprintf(ptr, "%.*s\n", COLS - 24, s->help);
445 }
446
447#ifndef NANO_SMALL
448 /* And the toggles... */
449 if (currshortcut == main_list)
450 for (t = toggles; t != NULL; t = t->next) {
451 ptr += sprintf(ptr, "M-%c\t\t\t", t->val - 32);
452 assert(t->desc != NULL);
453 ptr += sprintf(ptr, _("%.*s enable/disable\n"), COLS - 24, t->desc);
454 }
455#endif /* !NANO_SMALL */
456
457 /* If all went well, we didn't overwrite the allocated space for
458 help_text. */
459 assert(strlen(help_text) < allocsize);
460}
461#endif
462
463/* Create a new filestruct node. Note that we specifically do not set
464 * prevnode->next equal to the new line. */
465filestruct *make_new_node(filestruct *prevnode)
466{
467 filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
468
469 newnode->data = NULL;
470 newnode->prev = prevnode;
471 newnode->next = NULL;
472 newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1;
473
474 return newnode;
475}
476
David Lawrence Ramseyc5967552002-06-21 03:20:06 +0000477/* Make a copy of a node to a pointer (space will be malloc()ed). */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000478filestruct *copy_node(const filestruct *src)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000479{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000480 filestruct *dst = (filestruct *)nmalloc(sizeof(filestruct));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000481
Chris Allegretta6df90f52002-07-19 01:08:59 +0000482 assert(src != NULL);
483
Chris Allegretta88b09152001-05-17 11:35:43 +0000484 dst->data = charalloc(strlen(src->data) + 1);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000485 dst->next = src->next;
486 dst->prev = src->prev;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000487 strcpy(dst->data, src->data);
488 dst->lineno = src->lineno;
489
490 return dst;
491}
492
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000493/* Splice a node into an existing filestruct. */
494void splice_node(filestruct *begin, filestruct *newnode, filestruct *end)
495{
496 if (newnode != NULL) {
497 newnode->next = end;
498 newnode->prev = begin;
499 }
500 if (begin != NULL)
501 begin->next = newnode;
502 if (end != NULL)
503 end->prev = newnode;
504}
505
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000506/* Unlink a node from the rest of the filestruct. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000507void unlink_node(const filestruct *fileptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000508{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000509 assert(fileptr != NULL);
510
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000511 if (fileptr->prev != NULL)
512 fileptr->prev->next = fileptr->next;
513
514 if (fileptr->next != NULL)
515 fileptr->next->prev = fileptr->prev;
516}
517
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000518/* Delete a node from the filestruct. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000519void delete_node(filestruct *fileptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000520{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000521 if (fileptr != NULL) {
522 if (fileptr->data != NULL)
523 free(fileptr->data);
524 free(fileptr);
525 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000526}
527
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000528/* Okay, now let's duplicate a whole struct! */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000529filestruct *copy_filestruct(const filestruct *src)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000530{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000531 filestruct *head; /* copy of src, top of the copied list */
532 filestruct *prev; /* temp that traverses the list */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000533
Chris Allegretta6df90f52002-07-19 01:08:59 +0000534 assert(src != NULL);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000535
Chris Allegretta6df90f52002-07-19 01:08:59 +0000536 prev = copy_node(src);
537 prev->prev = NULL;
538 head = prev;
539 src = src->next;
540 while (src != NULL) {
541 prev->next = copy_node(src);
542 prev->next->prev = prev;
543 prev = prev->next;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000544
Chris Allegretta6df90f52002-07-19 01:08:59 +0000545 src = src->next;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000546 }
547
Chris Allegretta6df90f52002-07-19 01:08:59 +0000548 prev->next = NULL;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000549 return head;
550}
551
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000552/* Frees a filestruct. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000553void free_filestruct(filestruct *src)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000554{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000555 if (src != NULL) {
556 while (src->next != NULL) {
557 src = src->next;
558 delete_node(src->prev);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000559#ifdef DEBUG
Chris Allegretta6df90f52002-07-19 01:08:59 +0000560 fprintf(stderr, _("delete_node(): free'd a node, YAY!\n"));
561#endif
562 }
563 delete_node(src);
564#ifdef DEBUG
565 fprintf(stderr, _("delete_node(): free'd last node.\n"));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000566#endif
567 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000568}
569
Chris Allegretta6df90f52002-07-19 01:08:59 +0000570void renumber_all(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000571{
572 filestruct *temp;
Chris Allegrettabef12972002-03-06 03:30:40 +0000573 int i = 1;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000574
Chris Allegretta6df90f52002-07-19 01:08:59 +0000575 assert(fileage == NULL || fileage != fileage->next);
576 for (temp = fileage; temp != NULL; temp = temp->next)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000577 temp->lineno = i++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000578}
579
Chris Allegretta6df90f52002-07-19 01:08:59 +0000580void renumber(filestruct *fileptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000581{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000582 if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000583 renumber_all();
Chris Allegretta6df90f52002-07-19 01:08:59 +0000584 else {
585 int lineno = fileptr->prev->lineno;
586
587 assert(fileptr != fileptr->next);
588 for (; fileptr != NULL; fileptr = fileptr->next)
589 fileptr->lineno = ++lineno;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000590 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000591}
592
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000593/* Print one usage string to the screen, removes lots of duplicate
Chris Allegretta6df90f52002-07-19 01:08:59 +0000594 * strings to translate and takes out the parts that shouldn't be
595 * translatable (the flag names). */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000596void print1opt(const char *shortflag, const char *longflag,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000597 const char *desc)
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000598{
599 printf(" %s\t", shortflag);
600 if (strlen(shortflag) < 8)
601 printf("\t");
602
603#ifdef HAVE_GETOPT_LONG
604 printf("%s\t", longflag);
605 if (strlen(longflag) < 8)
606 printf("\t\t");
607 else if (strlen(longflag) < 16)
608 printf("\t");
609#endif
610
611 printf("%s\n", desc);
612}
613
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000614void usage(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000615{
616#ifdef HAVE_GETOPT_LONG
Chris Allegretta6df90f52002-07-19 01:08:59 +0000617 printf(_("Usage: nano [+LINE] [GNU long option] [option] [file]\n\n"));
618 printf(_("Option\t\tLong option\t\tMeaning\n"));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000619#else
Chris Allegretta6df90f52002-07-19 01:08:59 +0000620 printf(_("Usage: nano [+LINE] [option] [file]\n\n"));
621 printf(_("Option\t\tMeaning\n"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000622#endif /* HAVE_GETOPT_LONG */
623
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000624 print1opt("-h, -?", "--help", _("Show this message"));
Chris Allegretta6df90f52002-07-19 01:08:59 +0000625 print1opt(_("+LINE"), "", _("Start at line number LINE"));
Chris Allegretta7004c282001-09-22 00:42:10 +0000626#ifndef NANO_SMALL
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000627 print1opt("-B", "--backup", _("Backup existing files on save"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000628 print1opt("-D", "--dos", _("Write file in DOS format"));
Chris Allegretta7004c282001-09-22 00:42:10 +0000629#endif
Chris Allegretta355fbe52001-07-14 19:32:47 +0000630#ifdef ENABLE_MULTIBUFFER
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000631 print1opt("-F", "--multibuffer", _("Enable multiple file buffers"));
Chris Allegretta355fbe52001-07-14 19:32:47 +0000632#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +0000633#ifdef ENABLE_NANORC
634 print1opt("-I", "--ignorercfiles", _("Don't look at nanorc files"));
635#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000636 print1opt("-K", "--keypad", _("Use alternate keypad routines"));
Chris Allegretta8fa1e282001-09-22 04:20:25 +0000637#ifndef NANO_SMALL
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000638 print1opt("-M", "--mac", _("Write file in Mac format"));
639 print1opt("-N", "--noconvert", _("Don't convert files from DOS/Mac format"));
Chris Allegretta8fa1e282001-09-22 04:20:25 +0000640#endif
Chris Allegrettae4f940d2002-03-03 22:36:36 +0000641#ifndef DISABLE_JUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +0000642 print1opt(_("-Q [str]"), _("--quotestr=[str]"), _("Quoting string, default \"> \""));
Chris Allegrettae4f940d2002-03-03 22:36:36 +0000643#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000644#ifdef HAVE_REGEX_H
645 print1opt("-R", "--regexp", _("Do regular expression searches"));
646#endif
Chris Allegretta3e3ae942001-09-22 19:02:04 +0000647#ifndef NANO_SMALL
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000648 print1opt("-S", "--smooth", _("Smooth scrolling"));
Chris Allegretta3e3ae942001-09-22 19:02:04 +0000649#endif
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000650 print1opt(_("-T [num]"), _("--tabsize=[num]"), _("Set width of a tab to num"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000651 print1opt("-V", "--version", _("Print version information and exit"));
Chris Allegretta09900ff2002-05-04 04:23:30 +0000652#ifdef ENABLE_COLOR
653 print1opt(_("-Y [str]"), _("--syntax [str]"), _("Syntax definition to use"));
654#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000655 print1opt("-c", "--const", _("Constantly show cursor position"));
Chris Allegrettad19e9912000-07-12 18:14:51 +0000656#ifndef NANO_SMALL
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000657 print1opt("-i", "--autoindent", _("Automatically indent new lines"));
658 print1opt("-k", "--cut", _("Let ^K cut from cursor to end of line"));
Chris Allegrettad19e9912000-07-12 18:14:51 +0000659#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000660 print1opt("-l", "--nofollow", _("Don't follow symbolic links, overwrite"));
Chris Allegretta84de5522001-04-12 14:51:48 +0000661#ifndef DISABLE_MOUSE
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000662#ifdef NCURSES_MOUSE_VERSION
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000663 print1opt("-m", "--mouse", _("Enable mouse"));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000664#endif
665#endif
Chris Allegrettae1f14522001-09-19 03:19:43 +0000666#ifndef DISABLE_OPERATINGDIR
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000667 print1opt(_("-o [dir]"), _("--operatingdir=[dir]"), _("Set operating directory"));
Chris Allegrettae1f14522001-09-19 03:19:43 +0000668#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000669 print1opt("-p", "--pico", _("Emulate Pico as closely as possible"));
Chris Allegretta6fe61492001-05-21 12:56:25 +0000670#ifndef DISABLE_WRAPJUSTIFY
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000671 print1opt(_("-r [#cols]"), _("--fill=[#cols]"), _("Set fill cols to (wrap lines at) #cols"));
Chris Allegretta6fe61492001-05-21 12:56:25 +0000672#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +0000673#ifndef DISABLE_SPELLER
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000674 print1opt(_("-s [prog]"), _("--speller=[prog]"), _("Enable alternate speller"));
Rocco Corsiaf5c3022001-01-12 07:51:05 +0000675#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000676 print1opt("-t", "--tempfile", _("Auto save on exit, don't prompt"));
677 print1opt("-v", "--view", _("View (read only) mode"));
Chris Allegrettacef7fbb2001-04-02 05:36:08 +0000678#ifndef DISABLE_WRAPPING
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000679 print1opt("-w", "--nowrap", _("Don't wrap long lines"));
Chris Allegrettacef7fbb2001-04-02 05:36:08 +0000680#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000681 print1opt("-x", "--nohelp", _("Don't show help window"));
682 print1opt("-z", "--suspend", _("Enable suspend"));
Chris Allegretta6df90f52002-07-19 01:08:59 +0000683
684 /* this is a special case */
685 printf(" %s\t\t\t%s\n","-a, -b, -e, -f, -g, -j", _("(ignored, for Pico compatibility)"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000686
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000687 exit(0);
688}
689
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000690void version(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000691{
Chris Allegrettac46dd812001-02-14 14:28:27 +0000692 printf(_(" GNU nano version %s (compiled %s, %s)\n"),
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000693 VERSION, __TIME__, __DATE__);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000694 printf(_
Chris Allegretta8a0de3b2000-11-24 20:45:14 +0000695 (" Email: nano@nano-editor.org Web: http://www.nano-editor.org"));
Chris Allegretta8a0de3b2000-11-24 20:45:14 +0000696 printf(_("\n Compiled options:"));
Chris Allegrettaff269f82000-12-01 18:46:01 +0000697
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000698#ifdef DEBUG
699 printf(" --enable-debug");
700#endif
Chris Allegretta8a0de3b2000-11-24 20:45:14 +0000701#ifdef NANO_EXTRA
702 printf(" --enable-extra");
Jordi Mallach5285ca92002-01-17 12:40:33 +0000703#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +0000704#ifdef NANO_SMALL
705 printf(" --enable-tiny");
706#else
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000707#ifdef DISABLE_BROWSER
Chris Allegretta6636dc32001-01-05 05:41:07 +0000708 printf(" --disable-browser");
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000709#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000710#ifdef DISABLE_HELP
711 printf(" --disable-help");
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000712#endif
713#ifdef DISABLE_JUSTIFY
Chris Allegrettaff269f82000-12-01 18:46:01 +0000714 printf(" --disable-justify");
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000715#endif
Chris Allegretta84de5522001-04-12 14:51:48 +0000716#ifdef DISABLE_MOUSE
717 printf(" --disable-mouse");
Chris Allegrettab7d00ef2000-12-18 05:36:51 +0000718#endif
Chris Allegrettae1f14522001-09-19 03:19:43 +0000719#ifdef DISABLE_OPERATINGDIR
720 printf(" --disable-operatingdir");
721#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000722#ifdef DISABLE_SPELLER
723 printf(" --disable-speller");
724#endif
725#ifdef DISABLE_TABCOMP
726 printf(" --disable-tabcomp");
727#endif
Chris Allegretta84de5522001-04-12 14:51:48 +0000728#endif /* NANO_SMALL */
Chris Allegrettacef7fbb2001-04-02 05:36:08 +0000729#ifdef DISABLE_WRAPPING
730 printf(" --disable-wrapping");
731#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000732#ifdef ENABLE_COLOR
733 printf(" --enable-color");
734#endif
735#ifdef ENABLE_MULTIBUFFER
736 printf(" --enable-multibuffer");
737#endif
738#ifdef ENABLE_NANORC
739 printf(" --enable-nanorc");
740#endif
741#ifdef ENABLE_UNDO
742 printf(" --enable-undo");
743#endif
Chris Allegretta8a0de3b2000-11-24 20:45:14 +0000744#ifdef USE_SLANG
745 printf(" --with-slang");
746#endif
747 printf("\n");
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000748}
749
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000750/* Stuff we do when we abort from programs and want to clean up the
751 * screen. This doesn't do much right now. */
752void do_early_abort(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000753{
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000754 blank_statusbar_refresh();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000755}
756
757int no_help(void)
758{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000759 return ISSET(NO_HELP) ? 2 : 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000760}
761
Chris Allegrettad865da12002-07-29 23:46:38 +0000762#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP) || defined(NANO_SMALL)
Chris Allegrettaff269f82000-12-01 18:46:01 +0000763void nano_disabled_msg(void)
764{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000765 statusbar(_("Sorry, support for this function has been disabled"));
Chris Allegrettaff269f82000-12-01 18:46:01 +0000766}
Chris Allegretta4eb7aa02000-12-01 18:57:11 +0000767#endif
Chris Allegrettaff269f82000-12-01 18:46:01 +0000768
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000769#ifndef NANO_SMALL
770static int pid; /* This is the PID of the newly forked process
771 * below. It must be global since the signal
772 * handler needs it. */
773
774RETSIGTYPE cancel_fork(int signal)
775{
776 if (kill(pid, SIGKILL)==-1) nperror("kill");
777}
778
779int open_pipe(const char *command)
780{
781 int fd[2];
782 FILE *f;
783 struct sigaction oldaction, newaction;
784 /* original and temporary handlers for SIGINT */
785#ifdef _POSIX_VDISABLE
786 struct termios term, newterm;
787#endif /* _POSIX_VDISABLE */
788 int cancel_sigs = 0;
789 /* cancel_sigs==1 means that sigaction failed without changing the
790 * signal handlers. cancel_sigs==2 means the signal handler was
791 * changed, but the tcsetattr didn't succeed.
792 * I use this variable since it is important to put things back when
793 * we finish, even if we get errors. */
794
795 /* Make our pipes. */
796
797 if (pipe(fd) == -1) {
798 statusbar(_("Could not pipe"));
799 return 1;
800 }
801
802 /* Fork a child. */
803
804 if ((pid = fork()) == 0) {
805 close(fd[0]);
806 dup2(fd[1], fileno(stdout));
807 dup2(fd[1], fileno(stderr));
808 /* If execl() returns at all, there was an error. */
809
810 execl("/bin/sh","sh","-c",command,0);
811 exit(0);
812 }
813
814 /* Else continue as parent. */
815
816 close(fd[1]);
817
818 if (pid == -1) {
819 close(fd[0]);
820 statusbar(_("Could not fork"));
821 return 1;
822 }
823
824 /* Before we start reading the forked command's output, we set
825 * things up so that ^C will cancel the new process. */
826 if (sigaction(SIGINT, NULL, &newaction)==-1) {
827 cancel_sigs = 1;
828 nperror("sigaction");
829 } else {
830 newaction.sa_handler = cancel_fork;
831 if (sigaction(SIGINT, &newaction, &oldaction)==-1) {
832 cancel_sigs = 1;
833 nperror("sigaction");
834 }
835 }
836 /* Note that now oldaction is the previous SIGINT signal handler,
837 * to be restored later. */
838
839 /* See if the platform supports disabling individual control
840 * characters. */
841#ifdef _POSIX_VDISABLE
842 if (!cancel_sigs && tcgetattr(0, &term) == -1) {
843 cancel_sigs = 2;
844 nperror("tcgetattr");
845 }
846 if (!cancel_sigs) {
847 newterm = term;
848 /* Grab oldterm's VINTR key :-) */
849 newterm.c_cc[VINTR] = oldterm.c_cc[VINTR];
850 if (tcsetattr(0, TCSANOW, &newterm) == -1) {
851 cancel_sigs = 2;
852 nperror("tcsetattr");
853 }
854 }
855#endif /* _POSIX_VDISABLE */
856
857 f = fdopen(fd[0], "rb");
858 if (!f)
859 nperror("fdopen");
860
861 read_file(f, "stdin", 0);
862 /* if multibuffer mode is on, we could be here in view mode; if so,
863 don't set the modification flag */
864 if (!ISSET(VIEW_MODE))
865 set_modified();
866
867 if (wait(NULL) == -1)
868 nperror("wait");
869
870#ifdef _POSIX_VDISABLE
871 if (!cancel_sigs && tcsetattr(0, TCSANOW, &term) == -1)
872 nperror("tcsetattr");
873#endif /* _POSIX_VDISABLE */
874
875 if (cancel_sigs!=1 && sigaction(SIGINT, &oldaction, NULL) == -1)
876 nperror("sigaction");
877
878 return 0;
879}
880#endif /* NANO_SMALL */
881
882#ifndef DISABLE_MOUSE
883#ifdef NCURSES_MOUSE_VERSION
884void do_mouse(void)
885{
886 MEVENT mevent;
887 int currslen;
888 const shortcut *s = currshortcut;
889
890 if (getmouse(&mevent) == ERR)
891 return;
892
893 /* If mouse not in edit or bottom window, return */
894 if (wenclose(edit, mevent.y, mevent.x)) {
895
896 /* Don't let people screw with the marker when they're in a
897 * subfunction. */
898 if (currshortcut != main_list)
899 return;
900
901 /* Subtract out size of topwin. Perhaps we need a constant
902 * somewhere? */
903 mevent.y -= 2;
904
905 /* Selecting where the cursor is sets the mark. Selecting
906 * beyond the line length with the cursor at the end of the line
907 * sets the mark as well. */
908 if ((mevent.y == current_y) &&
909 ((mevent.x == current_x) || (current_x == strlen(current->data)
910 && (mevent.x >
911 strlen(current->data))))) {
912 if (ISSET(VIEW_MODE)) {
913 print_view_warning();
914 return;
915 }
916 do_mark();
917 } else if (mevent.y > current_y) {
918 while (mevent.y > current_y) {
919 if (current->next != NULL)
920 current = current->next;
921 else
922 break;
923 current_y++;
924 }
925 } else if (mevent.y < current_y) {
926 while (mevent.y < current_y) {
927 if (current->prev != NULL)
928 current = current->prev;
929 else
930 break;
931 current_y--;
932 }
933 }
934 current_x = actual_x(current, mevent.x);
935 placewewant = current_x;
936 update_cursor();
937 edit_refresh();
938 } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
939 int i, k;
940
941 if (currshortcut == main_list)
942 currslen = MAIN_VISIBLE;
943 else
944 currslen = length_of_list(currshortcut);
945
946 if (currslen < 2)
947 k = COLS / 6;
948 else
949 k = COLS / ((currslen + (currslen %2)) / 2);
950
951 /* Determine what shortcut list was clicked */
952 mevent.y -= (editwinrows + 3);
953
954 if (mevent.y < 0) /* They clicked on the statusbar */
955 return;
956
957 /* Don't select stuff beyond list length */
958 if (mevent.x / k >= currslen)
959 return;
960
961 for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
962 s = s->next;
963
964 /* And ungetch that value */
965 ungetch(s->val);
966
967 /* And if it's an alt-key sequence, we should probably send alt
968 too ;-) */
969 if (s->val >= 'a' && s->val <= 'z')
970 ungetch(27);
971 }
972}
973#endif
974#endif
975
Chris Allegretta6df90f52002-07-19 01:08:59 +0000976/* The user typed a printable character; add it to the edit buffer. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000977void do_char(char ch)
978{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000979 size_t current_len = strlen(current->data);
980#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
981 int refresh = 0;
982 /* Do we have to run edit_refresh(), or can we get away with
983 * update_line()? */
984#endif
985
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000986 /* magic-line: when a character is inserted on the current magic line,
987 * it means we need a new one! */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000988 if (filebot == current && current->data[0] == '\0') {
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000989 new_magicline();
Chris Allegretta28a0f892000-07-05 22:47:54 +0000990 fix_editbot();
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000991 }
992
Chris Allegretta6df90f52002-07-19 01:08:59 +0000993 /* more dangerousness fun =) */
994 current->data = nrealloc(current->data, current_len + 2);
995 assert(current_x <= current_len);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000996 memmove(&current->data[current_x + 1],
997 &current->data[current_x],
Chris Allegretta6df90f52002-07-19 01:08:59 +0000998 current_len - current_x + 1);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000999 current->data[current_x] = ch;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001000 totsize++;
1001 set_modified();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001002
Chris Allegretta6df90f52002-07-19 01:08:59 +00001003#ifndef NANO_SMALL
1004 /* note that current_x has not yet been incremented */
1005 if (current == mark_beginbuf && current_x < mark_beginx)
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001006 mark_beginx++;
Chris Allegrettab2cd10d2002-01-20 00:54:42 +00001007#endif
1008
Chris Allegretta6df90f52002-07-19 01:08:59 +00001009 do_right();
1010
Chris Allegrettacef7fbb2001-04-02 05:36:08 +00001011#ifndef DISABLE_WRAPPING
Chris Allegrettadffa2072002-07-24 01:02:26 +00001012 if (!ISSET(NO_WRAP) && ch != '\t')
Chris Allegretta6df90f52002-07-19 01:08:59 +00001013 refresh = do_wrap(current);
Chris Allegrettacef7fbb2001-04-02 05:36:08 +00001014#endif
1015
Chris Allegretta6df90f52002-07-19 01:08:59 +00001016#ifdef ENABLE_COLOR
1017 refresh = 1;
1018#endif
1019
1020#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
1021 if (refresh)
1022 edit_refresh();
1023#endif
1024
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001025 check_statblank();
1026 UNSET(KEEP_CUTBUFFER);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001027}
1028
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001029int do_backspace(void)
1030{
1031 int refresh = 0;
1032 if (current_x > 0) {
1033 assert(current_x <= strlen(current->data));
1034 /* Let's get dangerous */
1035 memmove(&current->data[current_x - 1], &current->data[current_x],
1036 strlen(current->data) - current_x + 1);
1037#ifdef DEBUG
1038 fprintf(stderr, _("current->data now = \"%s\"\n"), current->data);
1039#endif
1040 align(&current->data);
1041#ifndef NANO_SMALL
1042 if (current_x <= mark_beginx && mark_beginbuf == current)
1043 mark_beginx--;
1044#endif
1045 do_left();
1046#ifdef ENABLE_COLOR
1047 refresh = 1;
1048#endif
1049 } else {
1050 filestruct *previous;
1051 const filestruct *tmp;
1052
1053 if (current == fileage)
1054 return 0; /* Can't delete past top of file */
1055
1056 previous = current->prev;
1057 current_x = strlen(previous->data);
1058 placewewant = strlenpt(previous->data);
1059#ifndef NANO_SMALL
1060 if (current == mark_beginbuf) {
1061 mark_beginx += current_x;
1062 mark_beginbuf = previous;
1063 }
1064#endif
1065 previous->data = nrealloc(previous->data,
1066 current_x + strlen(current->data) + 1);
1067 strcpy(previous->data + current_x, current->data);
1068
1069 unlink_node(current);
1070 delete_node(current);
1071 tmp = current;
1072 current = (previous->next ? previous->next : previous);
1073 renumber(current);
1074 /* We had to renumber before doing update_line. */
1075 if (tmp == edittop)
1076 page_up();
1077
1078 /* Ooops, sanity check */
1079 if (tmp == filebot) {
1080 filebot = current;
1081 editbot = current;
1082
1083 /* Recreate the magic line if we're deleting it AND if the
1084 line we're on now is NOT blank. if it is blank we
1085 can just use IT for the magic line. This is how Pico
1086 appears to do it, in any case. */
1087 if (current->data[0] != '\0') {
1088 new_magicline();
1089 fix_editbot();
1090 }
1091 }
1092
1093 current = previous;
1094 if (current_y > 0)
1095 current_y--;
1096 totlines--;
1097#ifdef DEBUG
1098 fprintf(stderr, _("After, data = \"%s\"\n"), current->data);
1099#endif
1100 UNSET(KEEP_CUTBUFFER);
1101 refresh = 1;
1102 }
1103
1104 totsize--;
1105 set_modified();
1106 if (refresh)
1107 edit_refresh();
1108 return 1;
1109}
1110
1111int do_delete(void)
1112{
1113 int refresh = 0;
1114
1115 /* blbf -> blank line before filebot (see below) */
1116 int blbf = 0;
1117
1118 if (current->next == filebot && current->data[0] == '\0')
1119 blbf = 1;
1120
1121 placewewant = xplustabs();
1122
1123 if (current_x != strlen(current->data)) {
1124 /* Let's get dangerous */
1125 memmove(&current->data[current_x], &current->data[current_x + 1],
1126 strlen(current->data) - current_x);
1127
1128 align(&current->data);
1129#ifdef ENABLE_COLOR
1130 refresh = 1;
1131#endif
1132 } else if (current->next != NULL && (current->next != filebot || blbf)) {
1133 /* We can delete the line before filebot only if it is blank: it
1134 becomes the new magic line then. */
1135
1136 filestruct *foo;
1137
1138 current->data = nrealloc(current->data,
1139 strlen(current->data) +
1140 strlen(current->next->data) + 1);
1141 strcat(current->data, current->next->data);
1142
1143 foo = current->next;
1144 if (filebot == foo) {
1145 filebot = current;
1146 editbot = current;
1147 }
1148
1149 unlink_node(foo);
1150 delete_node(foo);
1151 renumber(current);
1152 totlines--;
1153 refresh = 1;
1154 } else
1155 return 0;
1156
1157 totsize--;
1158 set_modified();
1159 UNSET(KEEP_CUTBUFFER);
1160 update_line(current, current_x);
1161 if (refresh)
1162 edit_refresh();
1163 return 1;
1164}
1165
1166int do_tab(void)
1167{
1168 do_char('\t');
1169 return 1;
1170}
1171
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001172/* Someone hits return *gasp!* */
Chris Allegretta6df90f52002-07-19 01:08:59 +00001173int do_enter(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001174{
Chris Allegrettae3167732001-03-18 16:59:34 +00001175 filestruct *newnode;
Chris Allegretta68532c32001-09-17 13:49:33 +00001176 char *tmp;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001177
Chris Allegretta6df90f52002-07-19 01:08:59 +00001178 newnode = make_new_node(current);
1179 assert(current != NULL && current->data != NULL);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001180 tmp = &current->data[current_x];
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001181
Chris Allegrettaff989832001-09-17 13:48:00 +00001182#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001183 /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001184 if (ISSET(AUTOINDENT)) {
Chris Allegrettadab017e2002-04-23 10:56:06 +00001185 int extra = 0;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001186 const char *spc = current->data;
1187
1188 while (*spc == ' ' || *spc == '\t') {
Chris Allegrettadab017e2002-04-23 10:56:06 +00001189 extra++;
1190 spc++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001191 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001192 /* If current_x < extra, then we are breaking the line in the
1193 * indentation. Autoindenting should add only current_x
1194 * characters of indentation. */
Chris Allegrettadab017e2002-04-23 10:56:06 +00001195 if (current_x < extra)
1196 extra = current_x;
1197 else
1198 current_x = extra;
1199 totsize += extra;
1200
1201 newnode->data = charalloc(strlen(tmp) + extra + 1);
1202 strncpy(newnode->data, current->data, extra);
1203 strcpy(&newnode->data[extra], tmp);
Chris Allegrettaff989832001-09-17 13:48:00 +00001204 } else
1205#endif
1206 {
Chris Allegrettadab017e2002-04-23 10:56:06 +00001207 current_x = 0;
Chris Allegretta88b09152001-05-17 11:35:43 +00001208 newnode->data = charalloc(strlen(tmp) + 1);
Chris Allegrettae3167732001-03-18 16:59:34 +00001209 strcpy(newnode->data, tmp);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001210 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001211 *tmp = '\0';
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001212
Chris Allegretta6df90f52002-07-19 01:08:59 +00001213 if (current->next == NULL) {
Chris Allegrettae3167732001-03-18 16:59:34 +00001214 filebot = newnode;
1215 editbot = newnode;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001216 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001217 splice_node(current, newnode, current->next);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001218
1219 totsize++;
1220 renumber(current);
Chris Allegrettae3167732001-03-18 16:59:34 +00001221 current = newnode;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001222 align(&current->data);
1223
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001224 /* The logic here is as follows:
1225 * -> If we are at the bottom of the buffer, we want to recenter
Chris Allegretta88520c92001-05-05 17:45:54 +00001226 * (read: rebuild) the screen and forcibly move the cursor.
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001227 * -> otherwise, we want simply to redraw the screen and update
1228 * where we think the cursor is.
1229 */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001230 if (current_y == editwinrows - 1) {
Chris Allegretta234a34d2000-07-29 04:33:38 +00001231 edit_update(current, CENTER);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00001232 reset_cursor();
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001233 } else {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001234 current_y++;
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001235 edit_refresh();
1236 update_cursor();
1237 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001238
1239 totlines++;
1240 set_modified();
1241
Chris Allegrettab0ae3932000-06-15 23:39:14 +00001242 placewewant = xplustabs();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001243 return 1;
1244}
1245
Chris Allegrettaad3f4782001-10-02 03:54:13 +00001246#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001247int do_next_word(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001248{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001249 filestruct *old = current;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001250
Chris Allegretta6df90f52002-07-19 01:08:59 +00001251 assert(current != NULL && current->data != NULL);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001252
Chris Allegretta6df90f52002-07-19 01:08:59 +00001253 /* Skip letters in this word first. */
1254 while (current->data[current_x] != '\0' &&
1255 isalnum((int)current->data[current_x]))
1256 current_x++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001257
Chris Allegretta6df90f52002-07-19 01:08:59 +00001258 for (; current != NULL; current = current->next) {
1259 while (current->data[current_x] != '\0' &&
1260 !isalnum((int)current->data[current_x]))
1261 current_x++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001262
Chris Allegretta6df90f52002-07-19 01:08:59 +00001263 if (current->data[current_x] != '\0')
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001264 break;
1265
Chris Allegretta6df90f52002-07-19 01:08:59 +00001266 current_x = 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001267 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001268 if (current == NULL)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001269 current = filebot;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001270
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001271 placewewant = xplustabs();
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001272
Chris Allegrettad865da12002-07-29 23:46:38 +00001273 if (current->lineno >= editbot->lineno) {
1274 /* If we're on the last line, don't center the screen. */
1275 if (current->lineno == filebot->lineno)
1276 edit_refresh();
1277 else
1278 edit_update(current, CENTER);
1279 }
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001280 else {
Chris Allegretta6df90f52002-07-19 01:08:59 +00001281 /* If we've jumped lines, refresh the old line. We can't just
1282 use current->prev here, because we may have skipped over some
1283 blank lines, in which case the previous line is the wrong
1284 one. */
1285 if (current != old) {
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001286 update_line(old, 0);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001287 /* If the mark was set, then the lines between old and
1288 current have to be updated too. */
1289 if (ISSET(MARK_ISSET)) {
1290 while (old->next != current) {
1291 old = old->next;
1292 update_line(old, 0);
1293 }
1294 }
1295 }
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001296 update_line(current, current_x);
1297 }
Chris Allegretta6232d662002-05-12 19:52:15 +00001298 return 0;
1299}
1300
Chris Allegretta6df90f52002-07-19 01:08:59 +00001301/* The same thing for backwards. */
1302int do_prev_word(void)
Chris Allegretta76e291b2001-10-14 19:05:10 +00001303{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001304 filestruct *old = current;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001305
Chris Allegretta6df90f52002-07-19 01:08:59 +00001306 assert(current != NULL);
Chris Allegretta76e291b2001-10-14 19:05:10 +00001307
Chris Allegretta6df90f52002-07-19 01:08:59 +00001308 /* Skip letters in this word first. */
1309 while (current_x >= 0 && isalnum((int)current->data[current_x]))
1310 current_x--;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001311
Chris Allegretta6df90f52002-07-19 01:08:59 +00001312 for (; current != NULL; current = current->prev) {
1313 while (current_x >= 0 && !isalnum((int)current->data[current_x]))
1314 current_x--;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001315
Chris Allegretta6df90f52002-07-19 01:08:59 +00001316 if (current_x >= 0)
1317 break;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001318
Chris Allegretta6df90f52002-07-19 01:08:59 +00001319 if (current->prev != NULL)
1320 current_x = strlen(current->prev->data);
Chris Allegretta76e291b2001-10-14 19:05:10 +00001321 }
Chris Allegretta76e291b2001-10-14 19:05:10 +00001322
Chris Allegretta6df90f52002-07-19 01:08:59 +00001323 if (current != NULL) {
1324 while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
1325 current_x--;
1326 } else {
1327 current = fileage;
1328 current_x = 0;
1329 }
1330
Chris Allegretta76e291b2001-10-14 19:05:10 +00001331 placewewant = xplustabs();
1332
Chris Allegrettad865da12002-07-29 23:46:38 +00001333 if (current->lineno <= edittop->lineno) {
1334 /* If we're on the first line, don't center the screen. */
1335 if (current->lineno == fileage->lineno)
1336 edit_refresh();
1337 else
1338 edit_update(current, CENTER);
1339 }
Chris Allegretta76e291b2001-10-14 19:05:10 +00001340 else {
Chris Allegretta6df90f52002-07-19 01:08:59 +00001341 /* If we've jumped lines, refresh the old line. We can't just
1342 use current->prev here, because we may have skipped over some
1343 blank lines, in which case the previous line is the wrong
1344 one. */
1345 if (current != old) {
Chris Allegretta76e291b2001-10-14 19:05:10 +00001346 update_line(old, 0);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001347 /* If the mark was set, then the lines between old and
1348 current have to be updated too. */
1349 if (ISSET(MARK_ISSET)) {
1350 while (old->prev != current) {
1351 old = old->prev;
1352 update_line(old, 0);
1353 }
1354 }
1355 }
Chris Allegretta76e291b2001-10-14 19:05:10 +00001356 update_line(current, current_x);
1357 }
Chris Allegretta6232d662002-05-12 19:52:15 +00001358 return 0;
1359}
Chris Allegretta6df90f52002-07-19 01:08:59 +00001360#endif /* !NANO_SMALL */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001361
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001362int do_mark(void)
1363{
1364#ifdef NANO_SMALL
1365 nano_disabled_msg();
1366#else
1367 if (!ISSET(MARK_ISSET)) {
1368 statusbar(_("Mark Set"));
1369 SET(MARK_ISSET);
1370 mark_beginbuf = current;
1371 mark_beginx = current_x;
1372 } else {
1373 statusbar(_("Mark UNset"));
1374 UNSET(MARK_ISSET);
1375 edit_refresh();
1376 }
1377#endif
1378 return 1;
1379}
1380
1381void wrap_reset(void)
1382{
1383 UNSET(SAMELINEWRAP);
1384}
1385
Chris Allegrettacef7fbb2001-04-02 05:36:08 +00001386#ifndef DISABLE_WRAPPING
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001387/* We wrap the given line. Precondition: we assume the cursor has been
Chris Allegretta6df90f52002-07-19 01:08:59 +00001388 * moved forward since the last typed character. Return value:
1389 * whether we wrapped. */
1390int do_wrap(filestruct *inptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001391{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001392 size_t len = strlen(inptr->data); /* length of the line we wrap */
1393 int i = 0; /* generic loop variable */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001394 int wrap_loc = -1; /* index of inptr->data where we wrap */
1395 int word_back = -1;
1396#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001397 const char *indentation = NULL;
1398 /* indentation to prepend to the new line */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001399 int indent_len = 0; /* strlen(indentation) */
1400#endif
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001401 const char *after_break; /* text after the wrap point */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001402 int after_break_len; /* strlen(after_break) */
1403 int wrapping = 0; /* do we prepend to the next line? */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001404 const char *wrap_line = NULL;
1405 /* the next line, minus indentation */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001406 int wrap_line_len = 0; /* strlen(wrap_line) */
1407 char *newline = NULL; /* the line we create */
1408 int new_line_len = 0; /* eventual length of newline */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001409
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001410/* There are three steps. First, we decide where to wrap. Then, we
1411 * create the new wrap line. Finally, we clean up. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001412
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001413/* Step 1, finding where to wrap. We are going to replace a white-space
1414 * character with a new-line. In this step, we set wrap_loc as the
1415 * location of this replacement.
1416 *
1417 * Where should we break the line? We need the last "legal wrap point"
1418 * such that the last word before it ended at or before fill. If there
1419 * is no such point, we settle for the first legal wrap point.
1420 *
1421 * A "legal wrap point" is a white-space character that is not the last
1422 * typed character and is not followed by white-space.
1423 *
1424 * If there is no legal wrap point or we found the last character of the
1425 * line, we should return without wrapping.
1426 *
1427 * Note that the initial indentation does not count as a legal wrap
1428 * point if we are going to auto-indent!
1429 *
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001430 * Note that the code below could be optimised, by not calling strnlenpt()
1431 * so often. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001432
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001433#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001434 if (ISSET(AUTOINDENT))
1435 i = indent_length(inptr->data);
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001436#endif
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001437 wrap_line = inptr->data + i;
1438 for(; i < len; i++, wrap_line++) {
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001439 /* record where the last word ended */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001440 if (*wrap_line != ' ' && *wrap_line != '\t')
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001441 word_back = i;
1442 /* if we have found a "legal wrap point" and the current word
1443 * extends too far, then we stop */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001444 if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001445 break;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001446 /* we record the latest "legal wrap point" */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001447 if (i != current_x - 1 && word_back != i &&
1448 wrap_line[1] != ' ' && wrap_line[1] != '\t')
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001449 wrap_loc = i;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001450 }
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001451 if (wrap_loc < 0 || i == len)
Chris Allegretta6df90f52002-07-19 01:08:59 +00001452 return 0;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001453
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001454/* Step 2, making the new wrap line. It will consist of indentation +
1455 * after_break + " " + wrap_line (although indentation and wrap_line are
1456 * conditional on flags and #defines). */
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001457
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001458 /* after_break is the text that will be moved to the next line. */
1459 after_break = inptr->data + wrap_loc + 1;
1460 after_break_len = len - wrap_loc - 1;
1461 assert(after_break_len == strlen(after_break));
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001462
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001463 /* new_line_len will later be increased by the lengths of indentation
1464 * and wrap_line. */
1465 new_line_len = after_break_len;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001466
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001467 /* We prepend the wrapped text to the next line, if the flag is set,
1468 * and there is a next line, and prepending would not make the line
1469 * too long. */
Adam Rogoyski0223d6f2000-06-17 20:36:35 +00001470 if (ISSET(SAMELINEWRAP) && inptr->next) {
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001471 wrap_line = inptr->next->data;
1472 wrap_line_len = strlen(wrap_line);
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001473
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001474 /* +1 for the space between after_break and wrap_line */
1475 if ((new_line_len + 1 + wrap_line_len) <= fill) {
1476 wrapping = 1;
1477 new_line_len += (1 + wrap_line_len);
1478 }
1479 }
Chris Allegretta56214c62001-09-27 02:46:53 +00001480
Chris Allegrettaff989832001-09-17 13:48:00 +00001481#ifndef NANO_SMALL
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001482 if (ISSET(AUTOINDENT)) {
1483 /* indentation comes from the next line if wrapping, else from
1484 * this line */
1485 indentation = (wrapping ? wrap_line : inptr->data);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001486 indent_len = indent_length(indentation);
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001487 if (wrapping)
1488 /* The wrap_line text should not duplicate indentation. Note
1489 * in this case we need not increase new_line_len. */
1490 wrap_line += indent_len;
1491 else
1492 new_line_len += indent_len;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001493 }
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001494#endif
Adam Rogoyski1e9183f2001-03-13 18:36:03 +00001495
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001496 /* Now we allocate the new line and copy into it. */
1497 newline = charalloc(new_line_len + 1); /* +1 for \0 */
1498 *newline = '\0';
1499
1500#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001501 if (ISSET(AUTOINDENT)) {
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001502 strncpy(newline, indentation, indent_len);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001503 newline[indent_len] = '\0';
1504 }
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001505#endif
1506 strcat(newline, after_break);
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001507 /* We end the old line at wrap_loc. Note this eats the space. */
1508 null_at(&inptr->data, wrap_loc);
1509 if (wrapping) {
1510 /* In this case, totsize does not change. We ate a space in the
1511 * null_at() above, but we add a space between after_break and
1512 * wrap_line below. */
1513 strcat(newline, " ");
1514 strcat(newline, wrap_line);
1515 free(inptr->next->data);
1516 inptr->next->data = newline;
1517 } else {
1518 filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
Chris Allegretta6df90f52002-07-19 01:08:59 +00001519
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001520 /* In this case, the file size changes by -1 for the eaten
1521 * space, +1 for the new line, and +indent_len for the new
1522 * indentation. */
1523#ifndef NANO_SMALL
1524 totsize += indent_len;
1525#endif
1526 totlines++;
1527 temp->data = newline;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001528 temp->prev = inptr;
1529 temp->next = inptr->next;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001530 temp->prev->next = temp;
1531 /* If !temp->next, then temp is the last line of the file, so we
1532 * must set filebot */
1533 if (temp->next)
1534 temp->next->prev = temp;
1535 else
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001536 filebot = temp;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001537 }
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001538
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001539/* Step 3, clean up. Here we reposition the cursor and mark, and do some
1540 * other sundry things. */
1541
1542 /* later wraps of this line will be prepended to the next line. */
1543 SET(SAMELINEWRAP);
1544
1545 /* Each line knows its line number. We recalculate these if we
1546 * inserted a new line. */
1547 if (!wrapping)
1548 renumber(inptr);
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001549
Chris Allegretta6df90f52002-07-19 01:08:59 +00001550 /* If the cursor was after the break point, we must move it. */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001551 if (current_x > wrap_loc) {
Chris Allegretta6df90f52002-07-19 01:08:59 +00001552 current = current->next;
1553 current_x -=
Chris Allegrettaff989832001-09-17 13:48:00 +00001554#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001555 -indent_len +
Chris Allegrettaff989832001-09-17 13:48:00 +00001556#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00001557 wrap_loc + 1;
1558 wrap_reset();
1559 placewewant = xplustabs();
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001560 }
1561
Chris Allegretta6df90f52002-07-19 01:08:59 +00001562#ifndef NANO_SMALL
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001563 /* If the mark was on this line after the wrap point, we move it down.
Chris Allegretta6df90f52002-07-19 01:08:59 +00001564 * If it was on the next line and we wrapped, we must move it
1565 * right. */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001566 if (mark_beginbuf == inptr && mark_beginx > wrap_loc) {
1567 mark_beginbuf = inptr->next;
Chris Allegrettadffa2072002-07-24 01:02:26 +00001568 mark_beginx -= wrap_loc - indent_len + 1;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001569 } else if (wrapping && mark_beginbuf == inptr->next)
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001570 mark_beginx += after_break_len;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001571#endif /* !NANO_SMALL */
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001572
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001573 /* Place the cursor. */
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001574 reset_cursor();
Chris Allegretta6df90f52002-07-19 01:08:59 +00001575
1576 return 1;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001577}
Chris Allegretta6df90f52002-07-19 01:08:59 +00001578#endif /* !DISABLE_WRAPPING */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001579
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001580#ifndef DISABLE_SPELLER
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001581int do_int_spell_fix(const char *word)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001582{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001583 char *save_search;
1584 char *save_replace;
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001585 filestruct *begin;
Chris Allegretta23b74b22002-01-21 20:32:22 +00001586 int i = 0, j = 0, beginx, beginx_top, reverse_search_set;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001587#ifndef NANO_SMALL
1588 int mark_set;
1589#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001590
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001591 /* save where we are */
1592 begin = current;
1593 beginx = current_x + 1;
1594
Chris Allegretta23b74b22002-01-21 20:32:22 +00001595 /* Make sure Spell Check goes forward only */
1596 reverse_search_set = ISSET(REVERSE_SEARCH);
1597 UNSET(REVERSE_SEARCH);
1598
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001599#ifndef NANO_SMALL
1600 /* Make sure the marking highlight is off during Spell Check */
1601 mark_set = ISSET(MARK_ISSET);
1602 UNSET(MARK_ISSET);
1603#endif
1604
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001605 /* save the current search/replace strings */
1606 search_init_globals();
Chris Allegretta6df90f52002-07-19 01:08:59 +00001607 save_search = last_search;
1608 save_replace = last_replace;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001609
1610 /* set search/replace strings to mis-spelt word */
Chris Allegretta6df90f52002-07-19 01:08:59 +00001611 last_search = mallocstrcpy(NULL, word);
1612 last_replace = mallocstrcpy(NULL, word);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001613
1614 /* start from the top of file */
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001615 current = fileage;
1616 current_x = beginx_top = -1;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001617
1618 search_last_line = FALSE;
1619
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001620 edit_update(fileage, TOP);
1621
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001622 while (1) {
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001623 /* make sure word is still mis-spelt (i.e. when multi-errors) */
Chris Allegretta6df90f52002-07-19 01:08:59 +00001624 if (findnextstr(TRUE, FALSE, fileage, beginx_top, word) != NULL) {
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001625
Chris Allegretta6df90f52002-07-19 01:08:59 +00001626 /* find whole words only */
1627 if (!is_whole_word(current_x, current->data, word))
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001628 continue;
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001629
Chris Allegretta6df90f52002-07-19 01:08:59 +00001630 do_replace_highlight(TRUE, word);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001631
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001632 /* allow replace word to be corrected */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001633 i = statusq(0, spell_list, last_replace, _("Edit a replacement"));
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001634
Chris Allegretta6df90f52002-07-19 01:08:59 +00001635 do_replace_highlight(FALSE, word);
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001636
1637 /* start from the start of this line again */
1638 current = fileage;
1639 current_x = beginx_top;
1640
1641 search_last_line = FALSE;
1642
Chris Allegretta6df90f52002-07-19 01:08:59 +00001643 if (strcmp(word, answer)) {
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001644 j = i;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001645 do_replace_loop(word, fileage, &beginx_top, TRUE, &j);
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001646 }
Chris Allegretta80838272001-12-02 06:03:22 +00001647 }
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001648 break;
Rocco Corsi562964d2002-01-13 03:18:03 +00001649 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001650
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001651 /* restore the search/replace strings */
Chris Allegrettabef12972002-03-06 03:30:40 +00001652 free(last_search); last_search=save_search;
1653 free(last_replace); last_replace=save_replace;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001654
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001655 /* restore where we were */
1656 current = begin;
1657 current_x = beginx - 1;
1658
Chris Allegretta23b74b22002-01-21 20:32:22 +00001659 /* restore Search/Replace direction */
1660 if (reverse_search_set)
1661 SET(REVERSE_SEARCH);
1662
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001663#ifndef NANO_SMALL
1664 /* restore marking highlight */
1665 if (mark_set)
1666 SET(MARK_ISSET);
1667#endif
1668
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001669 edit_update(current, CENTER);
1670
1671 if (i == -1)
1672 return FALSE;
1673
1674 return TRUE;
1675}
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001676
Chris Allegretta6df90f52002-07-19 01:08:59 +00001677/* Integrated spell checking using 'spell' program. */
Chris Allegretta271e9722000-11-10 18:15:43 +00001678int do_int_speller(char *tempfile_name)
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001679{
Chris Allegretta271e9722000-11-10 18:15:43 +00001680 char *read_buff, *read_buff_ptr, *read_buff_word;
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001681 size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001682 int in_fd[2], tempfile_fd, spell_status;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001683 pid_t pid_spell;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001684
Chris Allegretta271e9722000-11-10 18:15:43 +00001685 /* Create a pipe to spell program */
1686
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001687 if (pipe(in_fd) == -1)
1688 return FALSE;
1689
Chris Allegretta271e9722000-11-10 18:15:43 +00001690 /* A new process to run spell in */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001691
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001692 if ((pid_spell = fork()) == 0) {
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001693
1694 /* Child continues, (i.e. future spell process) */
1695
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001696 close(in_fd[0]);
1697
Chris Allegretta271e9722000-11-10 18:15:43 +00001698 /* replace the standard in with the tempfile */
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001699
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001700 if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1) {
Chris Allegretta271e9722000-11-10 18:15:43 +00001701 close(in_fd[1]);
1702 exit(1);
1703 }
1704
1705 if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) {
Chris Allegretta271e9722000-11-10 18:15:43 +00001706 close(tempfile_fd);
1707 close(in_fd[1]);
1708 exit(1);
1709 }
1710 close(tempfile_fd);
1711
1712 /* send spell's standard out to the pipe */
1713
1714 if (dup2(in_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
Chris Allegretta271e9722000-11-10 18:15:43 +00001715 close(in_fd[1]);
1716 exit(1);
1717 }
1718 close(in_fd[1]);
1719
1720 /* Start spell program, we are using the PATH here!?!? */
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001721 execlp("spell", "spell", NULL);
1722
Chris Allegretta271e9722000-11-10 18:15:43 +00001723 /* Should not be reached, if spell is found!!! */
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001724
Chris Allegretta271e9722000-11-10 18:15:43 +00001725 exit(1);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001726 }
1727
1728 /* Parent continues here */
1729
Chris Allegretta271e9722000-11-10 18:15:43 +00001730 close(in_fd[1]);
1731
1732 /* Child process was not forked successfully */
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001733
1734 if (pid_spell < 0) {
Chris Allegretta271e9722000-11-10 18:15:43 +00001735 close(in_fd[0]);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001736 return FALSE;
1737 }
1738
Chris Allegretta271e9722000-11-10 18:15:43 +00001739 /* Get system pipe buffer size */
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001740
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001741 if ((pipe_buff_size = fpathconf(in_fd[0], _PC_PIPE_BUF)) < 1) {
Chris Allegretta271e9722000-11-10 18:15:43 +00001742 close(in_fd[0]);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001743 return FALSE;
Chris Allegretta271e9722000-11-10 18:15:43 +00001744 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001745
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001746 /* Read-in the returned spelling errors */
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001747
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001748 read_buff_read = 0;
1749 read_buff_size = pipe_buff_size + 1;
1750 read_buff = read_buff_ptr = charalloc(read_buff_size);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001751
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001752 while ((bytesread = read(in_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001753 read_buff_read += bytesread;
1754 read_buff_size += pipe_buff_size;
1755 read_buff = read_buff_ptr = nrealloc(read_buff, read_buff_size);
1756 read_buff_ptr += read_buff_read;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001757 }
Chris Allegretta271e9722000-11-10 18:15:43 +00001758
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001759 *read_buff_ptr = (char) NULL;
Chris Allegretta271e9722000-11-10 18:15:43 +00001760 close(in_fd[0]);
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001761
1762 /* Process the spelling errors */
1763
1764 read_buff_word = read_buff_ptr = read_buff;
1765
1766 while (*read_buff_ptr) {
1767
1768 if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
1769 *read_buff_ptr = (char) NULL;
1770 if (read_buff_word != read_buff_ptr) {
1771 if (!do_int_spell_fix(read_buff_word)) {
1772 read_buff_word = read_buff_ptr;
1773 break;
1774 }
1775 }
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001776 read_buff_word = read_buff_ptr + 1;
1777 }
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001778 read_buff_ptr++;
1779 }
1780
1781 /* special case where last word doesn't end with \n or \r */
1782 if (read_buff_word != read_buff_ptr)
1783 do_int_spell_fix(read_buff_word);
1784
Chris Allegretta271e9722000-11-10 18:15:43 +00001785 free(read_buff);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001786 replace_abort();
1787
Chris Allegretta271e9722000-11-10 18:15:43 +00001788 /* Process end of spell process */
1789
1790 wait(&spell_status);
1791 if (WIFEXITED(spell_status)) {
1792 if (WEXITSTATUS(spell_status) != 0)
1793 return FALSE;
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001794 } else
Chris Allegretta271e9722000-11-10 18:15:43 +00001795 return FALSE;
1796
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001797 return TRUE;
1798}
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001799
Chris Allegretta6df90f52002-07-19 01:08:59 +00001800/* External spell checking. */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001801int do_alt_speller(char *tempfile_name)
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001802{
Chris Allegrettab3655b42001-10-22 03:15:31 +00001803 int alt_spell_status, lineno_cur = current->lineno;
1804 int x_cur = current_x, y_cur = current_y, pww_cur = placewewant;
Chris Allegretta271e9722000-11-10 18:15:43 +00001805 pid_t pid_spell;
Chris Allegretta169ee842001-01-26 01:57:32 +00001806 char *ptr;
1807 static int arglen = 3;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001808 static char **spellargs = (char **)NULL;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001809#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001810 int mark_set = ISSET(MARK_ISSET);
1811 int mbb_lineno_cur = 0;
1812 /* We're going to close the current file, and open the output of
1813 the alternate spell command. The line that mark_beginbuf
1814 points to will be freed, so we save the line number and restore
1815 afterwards. */
1816
1817 if (mark_set) {
1818 mbb_lineno_cur = mark_beginbuf->lineno;
1819 UNSET(MARK_ISSET);
1820 }
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001821#endif
1822
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001823 endwin();
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001824
Chris Allegrettae434b452001-01-27 19:25:00 +00001825 /* Set up an argument list to pass the execvp function */
1826 if (spellargs == NULL) {
1827 spellargs = nmalloc(arglen * sizeof(char *));
Chris Allegretta271e9722000-11-10 18:15:43 +00001828
Chris Allegrettae434b452001-01-27 19:25:00 +00001829 spellargs[0] = strtok(alt_speller, " ");
1830 while ((ptr = strtok(NULL, " ")) != NULL) {
1831 arglen++;
1832 spellargs = nrealloc(spellargs, arglen * sizeof(char *));
1833 spellargs[arglen - 3] = ptr;
Chris Allegretta169ee842001-01-26 01:57:32 +00001834 }
Chris Allegrettae434b452001-01-27 19:25:00 +00001835 spellargs[arglen - 1] = NULL;
1836 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001837 spellargs[arglen - 2] = tempfile_name;
Chris Allegrettae434b452001-01-27 19:25:00 +00001838
1839 /* Start a new process for the alternate speller */
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001840 if ((pid_spell = fork()) == 0) {
Chris Allegretta88520c92001-05-05 17:45:54 +00001841 /* Start alternate spell program; we are using the PATH here!?!? */
Chris Allegretta169ee842001-01-26 01:57:32 +00001842 execvp(spellargs[0], spellargs);
Chris Allegretta271e9722000-11-10 18:15:43 +00001843
1844 /* Should not be reached, if alternate speller is found!!! */
Chris Allegretta271e9722000-11-10 18:15:43 +00001845 exit(1);
1846 }
1847
1848 /* Could not fork?? */
Chris Allegretta271e9722000-11-10 18:15:43 +00001849 if (pid_spell < 0)
1850 return FALSE;
1851
1852 /* Wait for alternate speller to complete */
1853
1854 wait(&alt_spell_status);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001855 if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0)
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001856 return FALSE;
1857
Chris Allegretta8f6c0692000-07-19 01:16:18 +00001858 refresh();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001859 free_filestruct(fileage);
Chris Allegretta56214c62001-09-27 02:46:53 +00001860 global_init(1);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001861 open_file(tempfile_name, 0, 1);
Rocco Corsi4dfaf932001-04-20 01:59:55 +00001862
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001863#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001864 if (mark_set) {
1865 do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
1866 mark_beginbuf = current;
1867 mark_beginx = current_x;
1868 /* In case the line got shorter, assign mark_beginx. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001869 SET(MARK_ISSET);
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001870 }
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001871#endif
1872
Chris Allegretta1b3381b2001-09-28 21:59:01 +00001873 /* go back to the old position, mark the file as modified, and make
1874 sure that the titlebar is refreshed */
1875 do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001876 set_modified();
Chris Allegrettae1f14522001-09-19 03:19:43 +00001877 clearok(topwin, FALSE);
1878 titlebar(NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001879
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001880 return TRUE;
1881}
1882#endif
1883
1884int do_spell(void)
1885{
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001886#ifdef DISABLE_SPELLER
Chris Allegrettaff269f82000-12-01 18:46:01 +00001887 nano_disabled_msg();
1888 return (TRUE);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001889#else
Chris Allegretta271e9722000-11-10 18:15:43 +00001890 char *temp;
1891 int spell_res;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001892
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001893 if ((temp = safe_tempnam(0, "nano.")) == NULL) {
Chris Allegretta271e9722000-11-10 18:15:43 +00001894 statusbar(_("Could not create a temporary filename: %s"),
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001895 strerror(errno));
Chris Allegretta271e9722000-11-10 18:15:43 +00001896 return 0;
1897 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001898
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001899 if (write_file(temp, 1, 0, 0) == -1) {
Chris Allegretta3dbb2782000-12-02 04:36:50 +00001900 statusbar(_("Spell checking failed: unable to write temp file!"));
Chris Allegrettabef12972002-03-06 03:30:40 +00001901 free(temp);
Chris Allegretta271e9722000-11-10 18:15:43 +00001902 return 0;
Chris Allegretta3dbb2782000-12-02 04:36:50 +00001903 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001904
Chris Allegrettae1f14522001-09-19 03:19:43 +00001905#ifdef ENABLE_MULTIBUFFER
1906 /* update the current open_files entry before spell-checking, in case
Chris Allegretta4dc03d52002-05-11 03:04:44 +00001907 any problems occur */
Chris Allegretta48b06702002-02-22 04:30:50 +00001908 add_open_file(1);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001909#endif
1910
Chris Allegretta271e9722000-11-10 18:15:43 +00001911 if (alt_speller)
1912 spell_res = do_alt_speller(temp);
1913 else
1914 spell_res = do_int_speller(temp);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001915
Chris Allegretta271e9722000-11-10 18:15:43 +00001916 remove(temp);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001917
1918 if (spell_res)
1919 statusbar(_("Finished checking spelling"));
1920 else
1921 statusbar(_("Spell checking failed"));
1922
Chris Allegrettabef12972002-03-06 03:30:40 +00001923 free(temp);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001924 return spell_res;
1925
Chris Allegrettadbc12b22000-07-03 03:10:14 +00001926#endif
Chris Allegretta67105eb2000-07-03 03:18:32 +00001927}
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001928
Chris Allegrettad865da12002-07-29 23:46:38 +00001929#if !defined(DISABLE_WRAPPING) && !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
Chris Allegretta6df90f52002-07-19 01:08:59 +00001930/* The "indentation" of a line is the white-space between the quote part
1931 * and the non-white-space of the line. */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001932size_t indent_length(const char *line)
1933{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001934 size_t len = 0;
1935
1936 assert(line != NULL);
1937 while (*line == ' ' || *line == '\t') {
1938 line++;
1939 len++;
1940 }
1941 return len;
1942}
Chris Allegrettadffa2072002-07-24 01:02:26 +00001943#endif /* !DISABLE_WRAPPING && !NANO_SMALL || !DISABLE_JUSTIFY */
Chris Allegretta6df90f52002-07-19 01:08:59 +00001944
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001945#ifndef DISABLE_JUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +00001946/* justify_format() replaces Tab by Space and multiple spaces by 1 (except
1947 * it maintains 2 after a . ! or ?). Note the terminating \0
1948 * counts as a space.
1949 *
1950 * If !changes_allowed and justify_format needs to make a change, it
1951 * returns 1, otherwise returns 0.
1952 *
1953 * If changes_allowed, justify_format() might make line->data
1954 * shorter, and change the actual pointer with null_at().
1955 *
1956 * justify_format will not look at the first skip characters of line.
1957 * skip should be at most strlen(line->data). The skip+1st character must
1958 * not be whitespace. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001959int justify_format(int changes_allowed, filestruct *line, size_t skip)
1960{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001961 char *back, *front;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001962
Chris Allegretta6df90f52002-07-19 01:08:59 +00001963 /* These four asserts are assumptions about the input data. */
1964 assert(line != NULL);
1965 assert(line->data != NULL);
1966 assert(skip <= strlen(line->data));
1967 assert(line->data[skip] != ' ' && line->data[skip] != '\t');
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001968
Chris Allegretta6df90f52002-07-19 01:08:59 +00001969 back = line->data + skip;
1970 front = back;
1971 for (; *front; front++) {
1972 if (*front == '\t') {
1973 if (!changes_allowed)
1974 return 1;
1975 *front = ' ';
1976 }
1977 /* these tests are safe since line->data + skip is not a space */
Chris Allegrettad865da12002-07-29 23:46:38 +00001978 if (*front == ' ' && *(front - 1) == ' ' && *(front - 2) != '.' &&
1979 *(front - 2) != '!' && *(front - 2) != '?') {
Chris Allegretta6df90f52002-07-19 01:08:59 +00001980 /* Now *front is a space we want to remove. We do that by
1981 * simply failing to assign it to *back */
1982 if (!changes_allowed)
1983 return 1;
1984#ifndef NANO_SMALL
1985 if (mark_beginbuf == line && back - line->data < mark_beginx)
1986 mark_beginx--;
1987#endif
1988 } else {
1989 *back = *front;
1990 back++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001991 }
1992 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001993
1994 /* Remove spaces from the end of the line, except maintain 1 after a
1995 * sentence punctuation. */
1996 while (line->data < back && *(back-1) == ' ')
1997 back--;
1998 if (line->data < back && *back == ' ' &&
1999 (*(back-1) == '.' || *(back-1) == '!' || *(back-1) == '?'))
2000 back++;
2001 if (!changes_allowed && back != front)
2002 return 1;
2003
2004 /* This assert merely documents a fact about the loop above. */
Chris Allegrettad865da12002-07-29 23:46:38 +00002005 assert(changes_allowed || back == front);
Chris Allegretta6df90f52002-07-19 01:08:59 +00002006
2007 /* Now back is the new end of line->data. */
2008 if (back != front) {
2009 totsize += back - line->data - strlen(line->data);
2010 null_at(&line->data, back - line->data);
2011#ifndef NANO_SMALL
2012 if (mark_beginbuf == line && back - line->data < mark_beginx)
2013 mark_beginx = back - line->data;
2014#endif
2015 }
2016 return 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002017}
Chris Allegretta6df90f52002-07-19 01:08:59 +00002018
2019/* The "quote part" of a line is the largest initial substring matching
2020 * the quote string. This function returns the length of the quote part
2021 * of the given line.
2022 *
2023 * Note that if !HAVE_REGEX_H then we match concatenated copies of
2024 * quotestr. */
2025#ifdef HAVE_REGEX_H
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002026size_t quote_length(const char *line, const regex_t *qreg)
2027{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002028 regmatch_t matches;
2029 int rc = regexec(qreg, line, 1, &matches, 0);
2030
2031 if (rc == REG_NOMATCH || matches.rm_so == (regoff_t) -1)
2032 return 0;
2033 /* matches.rm_so should be 0, since the quote string should start with
2034 * the caret ^. */
2035 return matches.rm_eo;
2036}
2037#else /* !HAVE_REGEX_H */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002038size_t quote_length(const char *line)
2039{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002040 size_t qdepth = 0;
2041 size_t qlen = strlen(quotestr);
2042
2043 /* Compute quote depth level */
2044 while (!strcmp(line + qdepth, quotestr))
2045 qdepth += qlen;
2046 return qdepth;
2047}
2048#endif /* !HAVE_REGEX_H */
2049
Chris Allegretta6df90f52002-07-19 01:08:59 +00002050/* a_line and b_line are lines of text. The quotation part of a_line is
2051 * the first a_quote characters. Check that the quotation part of
2052 * b_line is the same. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002053int quotes_match(const char *a_line, size_t a_quote,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002054 IFREG(const char *b_line, const regex_t *qreg))
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002055{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002056 /* Here is the assumption about a_quote: */
2057 assert(a_quote == quote_length(IFREG(a_line, qreg)));
Chris Allegrettad865da12002-07-29 23:46:38 +00002058 return a_quote == quote_length(IFREG(b_line, qreg)) &&
Chris Allegretta6df90f52002-07-19 01:08:59 +00002059 !strncmp(a_line, b_line, a_quote);
2060}
2061
2062/* We assume a_line and b_line have no quote part. Then, we return whether
2063 * b_line could follow a_line in a paragraph. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002064size_t indents_match(const char *a_line, size_t a_indent,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002065 const char *b_line, size_t b_indent)
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002066{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002067 assert(a_indent == indent_length(a_line));
2068 assert(b_indent == indent_length(b_line));
2069
2070 return b_indent <= a_indent && !strncmp(a_line, b_line, b_indent);
2071}
2072
2073/* Put the next par_len lines, starting with first_line, in the cut
2074 * buffer. We assume there are enough lines after first_line. We leave
2075 * copies of the lines in place, too. We return the new copy of
2076 * first_line. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002077filestruct *backup_lines(filestruct *first_line, size_t par_len,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002078 size_t quote_len)
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002079{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002080 /* We put the original lines, not copies, into the cut buffer, just
2081 * out of a misguided sense of consistency, so if you un-cut, you
2082 * get the actual same paragraph back, not a copy. */
2083 filestruct *alice = first_line;
2084
2085 set_modified();
2086 cutbuffer = NULL;
2087 for(; par_len > 0; par_len--) {
2088 filestruct *bob = copy_node(alice);
2089
2090 if (alice == first_line)
2091 first_line = bob;
2092 if (alice == current)
2093 current = bob;
2094 if (alice == edittop)
2095 edittop = bob;
2096#ifndef NANO_SMALL
2097 if (alice == mark_beginbuf)
2098 mark_beginbuf = bob;
2099#endif
2100 justify_format(1, bob,
2101 quote_len + indent_length(bob->data + quote_len));
2102
2103 assert(alice != NULL && bob != NULL);
2104 add_to_cutbuffer(alice);
2105 splice_node(bob->prev, bob, bob->next);
2106 alice = bob->next;
2107 }
2108 return first_line;
2109}
2110
2111/* We are trying to break a chunk off line. We find the last space such
2112 * that the display length to there is at most goal + 1. If there is
2113 * no such space, and force is not 0, then we find the first space.
2114 * Anyway, we then take the last space in that group of spaces. The
2115 * terminating '\0' counts as a space. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002116int break_line(const char *line, int goal, int force)
2117{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002118 /* Note that we use int instead of size_t, since goal is at most COLS,
2119 * the screen width, which will always be reasonably small. */
2120 int space_loc = -1;
2121 /* Current tentative return value. Index of the last space we
2122 * found with short enough display width. */
2123 int cur_loc = 0;
2124 /* Current index in line */
2125
2126 assert(line != NULL);
2127 for(; *line != '\0' && goal >= 0; line++, cur_loc++) {
2128 if (*line == ' ')
2129 space_loc = cur_loc;
2130 assert(*line != '\t');
2131
Chris Allegrettacf287c82002-07-20 13:57:41 +00002132 if (is_cntrl_char(*line))
Chris Allegretta6df90f52002-07-19 01:08:59 +00002133 goal -= 2;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002134 else
2135 goal--;
2136 }
2137 if (goal >= 0)
2138 /* In fact, the whole line displays shorter than goal. */
2139 return cur_loc;
2140 if (space_loc == -1) {
2141 /* No space found short enough. */
2142 if (force)
2143 for(; *line != '\0'; line++, cur_loc++)
2144 if (*line == ' ' && *(line + 1) != ' ')
2145 return cur_loc;
2146 return -1;
2147 }
2148 /* Perhaps the character after space_loc is a space. But because
2149 * of justify_format, there can be only two adjacent. */
2150 if (*(line - cur_loc + space_loc + 1) == ' ' ||
2151 *(line - cur_loc + space_loc + 1) == '\0')
2152 space_loc++;
2153 return space_loc;
2154}
2155#endif /* !DISABLE_JUSTIFY */
2156
2157/* This function justifies the current paragraph. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002158int do_justify(void)
2159{
Rocco Corsiaf5c3022001-01-12 07:51:05 +00002160#ifdef DISABLE_JUSTIFY
Chris Allegrettaff269f82000-12-01 18:46:01 +00002161 nano_disabled_msg();
2162 return 1;
2163#else
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002164
Chris Allegretta6df90f52002-07-19 01:08:59 +00002165/* To explain the justifying algorithm, I first need to define some
2166 * phrases about paragraphs and quotation:
2167 * A line of text consists of a "quote part", followed by an
2168 * "indentation part", followed by text. The functions quote_length()
2169 * and indent_length() calculate these parts.
2170 *
2171 * A line is "part of a paragraph" if it has a part not in the quote
2172 * part or the indentation.
2173 *
2174 * A line is "the beginning of a paragraph" if it is part of a paragraph
2175 * and
2176 * 1) it is the top line of the file, or
2177 * 2) the line above it is not part of a paragraph, or
2178 * 3) the line above it does not have precisely the same quote
2179 * part, or
2180 * 4) the indentation of this line is not a subset of the
2181 * indentation of the previous line, or
2182 * 5) this line has no quote part and some indentation, and
2183 * AUTOINDENT is not set.
2184 * The reason for number 5) is that if AUTOINDENT is not set, then an
2185 * indented line is expected to start a paragraph, like in books. Thus,
2186 * nano can justify an indented paragraph only if AUTOINDENT is turned
2187 * on.
2188 *
2189 * A contiguous set of lines is a "paragraph" if each line is part of
2190 * a paragraph and only the first line is the beginning of a paragraph.
Chris Allegrettad4fa0d32002-03-05 19:55:55 +00002191 */
Robert Siemborski60cd6aa2001-01-21 23:23:48 +00002192
Chris Allegretta6df90f52002-07-19 01:08:59 +00002193 size_t quote_len;
2194 /* Length of the initial quotation of the paragraph we justify. */
2195 size_t par_len;
2196 /* Number of lines in that paragraph. */
2197 filestruct *first_mod_line = NULL;
2198 /* Will be the first line of the resulting justified paragraph
2199 * that differs from the original. For restoring after uncut. */
2200 filestruct *last_par_line = current;
2201 /* Will be the last line of the result, also for uncut. */
2202 filestruct *cutbuffer_save = cutbuffer;
2203 /* When the paragraph gets modified, all lines from the changed
2204 * one down are stored in the cut buffer. We back up the original
2205 * to restore it later. */
2206
2207 /* We save these global variables to be restored if the user
2208 * unjustifies. Note we don't need to save totlines. */
2209 int current_x_save = current_x;
2210 int current_y_save = current_y;
2211 filestruct *current_save = current;
2212 int flags_save = flags;
2213 long totsize_save = totsize;
2214 filestruct *edittop_save = edittop;
2215 filestruct *editbot_save = editbot;
2216#ifndef NANO_SMALL
2217 filestruct *mark_beginbuf_save = mark_beginbuf;
2218 int mark_beginx_save = mark_beginx;
2219#endif
2220
2221 size_t indent_len; /* generic indentation length */
2222 filestruct *line; /* generic line of text */
2223 size_t i; /* generic loop variable */
2224
2225#ifdef HAVE_REGEX_H
2226 regex_t qreg; /* qreg is the compiled quotation regexp.
Chris Allegrettad865da12002-07-29 23:46:38 +00002227 * We no longer care about quotestr. */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002228 int rc = regcomp(&qreg, quotestr, REG_EXTENDED);
2229
2230 if (rc) {
2231 size_t size = regerror(rc, &qreg, NULL, 0);
2232 char *strerror = charalloc(size);
2233
2234 regerror(rc, &qreg, strerror, size);
2235 statusbar(_("Bad quote string %s: %s"), quotestr, strerror);
2236 free(strerror);
2237 return -1;
2238 }
2239#endif
2240
2241 /* Here is an assumption that is always true anyway. */
2242 assert(current != NULL);
2243
2244/* Here we find the first line of the paragraph to justify. If the
2245 * current line is in a paragraph, then we move back to the first line.
2246 * Otherwise we move down to the first line that is in a paragraph. */
2247 quote_len = quote_length(IFREG(current->data, &qreg));
2248 indent_len = indent_length(current->data + quote_len);
2249
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002250 current_x = 0;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002251 if (current->data[quote_len + indent_len] != '\0') {
2252 /* This line is part of a paragraph. So we must search back to
2253 * the first line of this paragraph. */
2254 if (quote_len > 0 || indent_len == 0
2255#ifndef NANO_SMALL
2256 || ISSET(AUTOINDENT)
2257#endif
2258 ) {
2259 /* We don't justify indented paragraphs unless AUTOINDENT is
2260 * turned on. See 5) above. */
2261 while (current->prev && quotes_match(current->data,
2262 quote_len, IFREG(current->prev->data, &qreg))) {
2263 /* indentation length of the previous line */
2264 size_t temp_id_len =
2265 indent_length(current->prev->data + quote_len);
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002266
Chris Allegretta6df90f52002-07-19 01:08:59 +00002267 if (!indents_match(current->prev->data + quote_len,
2268 temp_id_len, current->data + quote_len,
2269 indent_len) ||
2270 current->prev->data[quote_len + temp_id_len] == '\0')
2271 break;
2272 indent_len = temp_id_len;
2273 current = current->prev;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002274 current_y--;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00002275 }
2276 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002277 } else {
Chris Allegrettad865da12002-07-29 23:46:38 +00002278 /* This line is not part of a paragraph. Move down until we get
2279 * to a non "blank" line. */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002280 do {
2281 /* There is no next paragraph, so nothing to justify. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002282 if (current->next == NULL) {
2283 placewewant = 0;
2284 if (current_y > editwinrows - 4)
2285 edit_update(current, CENTER);
2286 else
2287 edit_refresh();
Chris Allegretta6df90f52002-07-19 01:08:59 +00002288 return 0;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002289 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002290 current = current->next;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002291 current_y++;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002292 quote_len = quote_length(IFREG(current->data, &qreg));
2293 indent_len = indent_length(current->data + quote_len);
2294 } while (current->data[quote_len + indent_len] == '\0');
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002295 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002296/* Now current is the first line of the paragraph, and quote_len
2297 * is the quotation length of that line. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002298
Chris Allegretta6df90f52002-07-19 01:08:59 +00002299/* Next step, compute par_len, the number of lines in this paragraph. */
2300 line = current;
2301 par_len = 1;
2302 indent_len = indent_length(line->data + quote_len);
2303
2304 while (line->next && quotes_match(current->data, quote_len,
2305 IFREG(line->next->data, &qreg))) {
2306 size_t temp_id_len = indent_length(line->next->data + quote_len);
2307
2308 if (!indents_match(line->data + quote_len, indent_len,
2309 line->next->data + quote_len, temp_id_len) ||
2310 line->next->data[quote_len + temp_id_len] == '\0' ||
2311 (quote_len == 0 && temp_id_len > 0
2312#ifndef NANO_SMALL
2313 && !ISSET(AUTOINDENT)
2314#endif
2315 ))
2316 break;
2317 indent_len = temp_id_len;
2318 line = line->next;
2319 par_len++;
2320 }
2321#ifdef HAVE_REGEX_H
2322 /* We no longer need to check quotation. */
2323 regfree(&qreg);
2324#endif
2325/* Now par_len is the number of lines in this paragraph. Should never
2326 * call quotes_match() or quote_length() again. */
2327
2328/* Next step, we loop through the lines of this paragraph, justifying
2329 * each one individually. */
2330 for(; par_len > 0; current_y++, par_len--) {
2331 size_t line_len;
2332 size_t display_len;
2333 /* The width of current in screen columns. */
2334 int break_pos;
2335 /* Where we will break the line. */
2336
2337 indent_len = indent_length(current->data + quote_len) +
2338 quote_len;
2339 /* justify_format() removes excess spaces from the line, and
2340 * changes tabs to spaces. The first argument, 0, means don't
2341 * change the line, just say whether there are changes to be
2342 * made. If there are, we do backup_lines(), which copies the
2343 * original paragraph to the cutbuffer for unjustification, and
2344 * then calls justify_format on the remaining lines. */
2345 if (first_mod_line == NULL &&
2346 justify_format(0, current, indent_len))
2347 first_mod_line = backup_lines(current, par_len, quote_len);
2348
2349 line_len = strlen(current->data);
2350 display_len = strlenpt(current->data);
2351
2352 if (display_len > fill) {
2353 /* The line is too long. Try to wrap it to the next. */
2354 break_pos = break_line(current->data + indent_len,
2355 fill - strnlenpt(current->data, indent_len),
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002356 1);
Chris Allegretta6df90f52002-07-19 01:08:59 +00002357 if (break_pos == -1 || break_pos + indent_len == line_len)
2358 /* We can't break the line, or don't need to, so just go
2359 * on to the next. */
2360 goto continue_loc;
2361 break_pos += indent_len;
2362 assert(break_pos < line_len);
2363 /* If we haven't backed up the paragraph, do it now. */
2364 if (first_mod_line == NULL)
2365 first_mod_line = backup_lines(current, par_len, quote_len);
2366 if (par_len == 1) {
2367 /* There is no next line in this paragraph. We make a new
2368 * line and copy text after break_pos into it. */
2369 splice_node(current, make_new_node(current),
2370 current->next);
2371 current->next->data = charalloc(indent_len + line_len -
2372 break_pos);
2373 strncpy(current->next->data, current->data,
2374 indent_len);
2375 strcpy(current->next->data + indent_len,
2376 current->data + break_pos + 1);
2377 assert(strlen(current->next->data) ==
2378 indent_len + line_len - break_pos - 1);
2379 totlines++;
2380 totsize += indent_len;
2381 par_len++;
2382 } else {
2383 size_t next_line_len = strlen(current->next->data);
2384
2385 indent_len = quote_len +
2386 indent_length(current->next->data + quote_len);
2387 current->next->data = (char *)nrealloc(current->next->data,
2388 sizeof(char) * (next_line_len + line_len -
2389 break_pos + 1));
2390
2391 memmove(current->next->data + indent_len + line_len - break_pos,
2392 current->next->data + indent_len,
2393 next_line_len - indent_len + 1);
2394 strcpy(current->next->data + indent_len,
2395 current->data + break_pos + 1);
2396 current->next->data[indent_len + line_len - break_pos - 1]
2397 = ' ';
2398#ifndef NANO_SMALL
2399 if (mark_beginbuf == current->next) {
2400 if (mark_beginx < indent_len)
2401 mark_beginx = indent_len;
2402 mark_beginx += line_len - break_pos;
2403 }
2404#endif
2405 }
2406#ifndef NANO_SMALL
2407 if (mark_beginbuf == current && mark_beginx > break_pos) {
2408 mark_beginbuf = current->next;
2409 mark_beginx -= break_pos + 1 - indent_len;
2410 }
2411#endif
2412 null_at(&current->data, break_pos);
2413 current = current->next;
2414 } else if (display_len < fill && par_len > 1) {
2415 size_t next_line_len = strlen(current->next->data);
2416
2417 indent_len = quote_len +
2418 indent_length(current->next->data + quote_len);
2419 break_pos = break_line(current->next->data + indent_len,
2420 fill - display_len - 1, 0);
2421 if (break_pos == -1)
2422 /* We can't pull a word from the next line up to this one,
2423 * so just go on. */
2424 goto continue_loc;
2425
2426 /* If we haven't backed up the paragraph, do it now. */
2427 if (first_mod_line == NULL)
2428 first_mod_line = backup_lines(current, par_len, quote_len);
2429 current->data = (char *)nrealloc(current->data,
2430 line_len + break_pos + 2);
2431 current->data[line_len] = ' ';
2432 strncpy(current->data + line_len + 1,
2433 current->next->data + indent_len, break_pos);
2434 current->data[line_len + break_pos + 1] = '\0';
2435#ifndef NANO_SMALL
2436 if (mark_beginbuf == current->next) {
2437 if (mark_beginx < indent_len + break_pos) {
2438 mark_beginbuf = current;
2439 if (mark_beginx <= indent_len)
2440 mark_beginx = line_len + 1;
2441 else
2442 mark_beginx = line_len + 1 + mark_beginx - indent_len;
2443 } else
2444 mark_beginx -= break_pos + 1;
2445 }
2446#endif
2447 if (indent_len + break_pos == next_line_len) {
2448 line = current->next;
2449 unlink_node(line);
2450 delete_node(line);
2451 totlines--;
2452 totsize -= indent_len;
2453 current_y--;
2454 } else {
2455 memmove(current->next->data + indent_len,
2456 current->next->data + indent_len + break_pos + 1,
2457 next_line_len - break_pos - indent_len);
2458 null_at(&current->next->data,
2459 next_line_len - break_pos);
2460 current = current->next;
2461 }
2462 } else
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002463 continue_loc:
Chris Allegretta6df90f52002-07-19 01:08:59 +00002464 current = current->next;
2465 }
2466/* We are now done justifying the paragraph. There are cleanup things to
2467 * do, and we check for unjustify. */
2468
2469 /* totlines, totsize, and current_y have been maintained above. We
2470 * now set last_par_line to the new end of the paragraph, update
2471 * fileage, set current_x. Also, edit_refresh() needs the line
2472 * numbers to be right, so we renumber(). */
2473 last_par_line = current->prev;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002474 if (first_mod_line != NULL) {
2475 if (first_mod_line->prev == NULL)
2476 fileage = first_mod_line;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002477 renumber(first_mod_line);
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002478 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002479
2480 if (current_y > editwinrows - 4)
2481 edit_update(current, CENTER);
2482 else
2483 edit_refresh();
2484
Chris Allegretta9149e612000-11-27 00:23:41 +00002485 statusbar(_("Can now UnJustify!"));
Chris Allegretta07798352000-11-27 22:58:23 +00002486 /* Change the shortcut list to display the unjustify code */
2487 shortcut_init(1);
2488 display_main_list();
Chris Allegretta9149e612000-11-27 00:23:41 +00002489 reset_cursor();
2490
Chris Allegretta6df90f52002-07-19 01:08:59 +00002491 /* Now get a keystroke and see if it's unjustify; if not, unget the
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002492 * keystroke and return. */
Chris Allegretta5f071802001-05-06 02:34:31 +00002493
2494#ifndef DISABLE_MOUSE
2495#ifdef NCURSES_MOUSE_VERSION
Chris Allegretta6df90f52002-07-19 01:08:59 +00002496 /* If it was a mouse click, parse it with do_mouse() and it might
2497 * become the unjustify key. Else give it back to the input stream. */
2498 if ((i = wgetch(edit)) == KEY_MOUSE)
Chris Allegretta5f071802001-05-06 02:34:31 +00002499 do_mouse();
2500 else
Chris Allegretta6df90f52002-07-19 01:08:59 +00002501 ungetch(i);
Chris Allegretta5f071802001-05-06 02:34:31 +00002502#endif
2503#endif
2504
Chris Allegretta6df90f52002-07-19 01:08:59 +00002505 if ((i = wgetch(edit)) != NANO_UNJUSTIFY_KEY) {
2506 ungetch(i);
2507 /* Did we back up anything at all? */
2508 if (cutbuffer != cutbuffer_save)
2509 free_filestruct(cutbuffer);
2510 placewewant = 0;
Chris Allegretta00ae5df2001-02-05 18:24:33 +00002511 } else {
Chris Allegretta9149e612000-11-27 00:23:41 +00002512 /* Else restore the justify we just did (ungrateful user!) */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002513 current = current_save;
2514 current_x = current_x_save;
2515 current_y = current_y_save;
2516 edittop = edittop_save;
2517 editbot = editbot_save;
2518 if (first_mod_line != NULL) {
2519 filestruct *cutbottom = get_cutbottom();
Chris Allegrettad022eac2000-11-27 02:50:49 +00002520
Chris Allegretta6df90f52002-07-19 01:08:59 +00002521 /* Splice the cutbuffer back into the file. */
2522 cutbottom->next = last_par_line->next;
2523 cutbottom->next->prev = cutbottom;
2524 /* The line numbers after the end of the paragraph have
2525 * been changed, so we change them back. */
2526 renumber(cutbottom->next);
2527 if (first_mod_line->prev != NULL) {
2528 cutbuffer->prev = first_mod_line->prev;
2529 cutbuffer->prev->next = cutbuffer;
2530 } else
2531 fileage = cutbuffer;
2532 cutbuffer = NULL;
2533
2534 last_par_line->next = NULL;
2535 free_filestruct(first_mod_line);
2536
2537 /* Restore global variables from before justify */
2538 totsize = totsize_save;
2539 totlines = filebot->lineno;
2540#ifndef NANO_SMALL
2541 mark_beginbuf = mark_beginbuf_save;
2542 mark_beginx = mark_beginx_save;
2543#endif
2544 flags = flags_save;
2545 if (!ISSET(MODIFIED)) {
2546 titlebar(NULL);
2547 wrefresh(topwin);
2548 }
2549 }
2550 edit_refresh();
Chris Allegretta9149e612000-11-27 00:23:41 +00002551 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002552 cutbuffer = cutbuffer_save;
2553 blank_statusbar_refresh();
2554 /* display shortcut list without UnCut */
Chris Allegretta4a9c8582000-11-27 22:59:40 +00002555 shortcut_init(0);
2556 display_main_list();
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002557
Chris Allegretta6df90f52002-07-19 01:08:59 +00002558 return 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002559#endif
2560}
2561
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002562int do_exit(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002563{
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002564 int i;
Chris Allegretta13fd44b2002-01-02 13:59:11 +00002565
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002566 if (!ISSET(MODIFIED)) {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002567
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002568#ifdef ENABLE_MULTIBUFFER
2569 if (!close_open_file()) {
2570 display_main_list();
2571 return 1;
David Lawrence Ramseyc5967552002-06-21 03:20:06 +00002572 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002573 else
David Lawrence Ramseyc5967552002-06-21 03:20:06 +00002574#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002575 finish(0);
Chris Allegretta756f2202000-09-01 13:32:47 +00002576 }
2577
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002578 if (ISSET(TEMP_OPT)) {
2579 i = 1;
2580 } else {
2581 i = do_yesno(0, 0,
2582 _
2583 ("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
2584 }
2585
2586#ifdef DEBUG
2587 dump_buffer(fileage);
2588#endif
2589
2590 if (i == 1) {
2591 if (do_writeout(filename, 1, 0) > 0) {
2592
2593#ifdef ENABLE_MULTIBUFFER
2594 if (!close_open_file()) {
2595 display_main_list();
2596 return 1;
2597 }
2598 else
2599#endif
2600 finish(0);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00002601 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002602 } else if (i == 0) {
2603
2604#ifdef ENABLE_MULTIBUFFER
2605 if (!close_open_file()) {
2606 display_main_list();
2607 return 1;
2608 }
2609 else
2610#endif
2611 finish(0);
2612 } else
2613 statusbar(_("Cancelled"));
2614
2615 display_main_list();
2616 return 1;
2617}
2618
2619void signal_init(void)
2620{
2621#ifdef _POSIX_VDISABLE
2622 struct termios term;
2623#endif
2624
2625 /* Trap SIGINT and SIGQUIT cuz we want them to do useful things. */
2626 memset(&act, 0, sizeof(struct sigaction));
2627 act.sa_handler = SIG_IGN;
2628 sigaction(SIGINT, &act, NULL);
2629
2630 /* Trap SIGHUP cuz we want to write the file out. */
2631 act.sa_handler = handle_hup;
2632 sigaction(SIGHUP, &act, NULL);
2633
2634#ifndef NANO_SMALL
2635 act.sa_handler = handle_sigwinch;
2636 sigaction(SIGWINCH, &act, NULL);
2637#endif
2638
2639#ifdef _POSIX_VDISABLE
2640 tcgetattr(0, &term);
2641
2642#ifdef VDSUSP
2643 term.c_cc[VDSUSP] = _POSIX_VDISABLE;
2644#endif /* VDSUSP */
2645
2646#endif /* _POSIX_VDISABLE */
2647
2648 if (!ISSET(SUSPEND)) {
2649
2650/* Insane! */
2651#ifdef _POSIX_VDISABLE
2652 term.c_cc[VSUSP] = _POSIX_VDISABLE;
2653#else
2654 act.sa_handler = SIG_IGN;
2655 sigaction(SIGTSTP, &act, NULL);
2656#endif
2657
2658 } else {
2659 /* If we don't do this, it seems other stuff interrupts the
2660 suspend handler! Try using nano with mutt without this
2661 line. */
2662 sigfillset(&act.sa_mask);
2663
2664 act.sa_handler = do_suspend;
2665 sigaction(SIGTSTP, &act, NULL);
2666
2667 act.sa_handler = do_cont;
2668 sigaction(SIGCONT, &act, NULL);
2669 }
2670
2671#ifdef _POSIX_VDISABLE
2672 tcsetattr(0, TCSANOW, &term);
2673#endif
2674}
2675
2676/* Handler for SIGHUP */
2677RETSIGTYPE handle_hup(int signal)
2678{
2679 die(_("Received SIGHUP"));
2680}
2681
2682/* What do we do when we catch the suspend signal */
2683RETSIGTYPE do_suspend(int signal)
2684{
2685 endwin();
2686 printf("\n\n\n\n\nUse \"fg\" to return to nano\n");
2687 fflush(stdout);
2688
2689 /* Restore the terminal settings for the disabled keys */
2690 tcsetattr(0, TCSANOW, &oldterm);
2691
2692 /* We used to re-enable the default SIG_DFL and raise SIGTSTP, but
2693 then we could be (and were) interrupted in the middle of the call.
2694 So we do it the mutt way instead */
2695 kill(0, SIGSTOP);
2696}
2697
2698/* Restore the suspend handler when we come back into the prog */
2699RETSIGTYPE do_cont(int signal)
2700{
2701 /* Now we just update the screen instead of having to reenable the
2702 SIGTSTP handler. */
2703
2704 doupdate();
2705 /* The Hurd seems to need this, otherwise a ^Y after a ^Z will
2706 start suspending again. */
2707 signal_init();
2708
2709#ifndef NANO_SMALL
2710 /* Perhaps the user resized the window while we slept. */
2711 handle_sigwinch(0);
2712#endif
2713}
2714
2715#ifndef NANO_SMALL
2716void handle_sigwinch(int s)
2717{
2718 const char *tty = ttyname(0);
2719 int fd;
2720 int result = 0;
2721 struct winsize win;
2722
2723 if (!tty)
2724 return;
2725 fd = open(tty, O_RDWR);
2726 if (fd == -1)
2727 return;
2728 result = ioctl(fd, TIOCGWINSZ, &win);
2729 close(fd);
2730 if (result == -1)
2731 return;
2732
2733 /* Could check whether the COLS or LINES changed, and return
2734 * otherwise. EXCEPT, that COLS and LINES are ncurses global
2735 * variables, and in some cases ncurses has already updated them.
2736 * But not in all cases, argh. */
2737 COLS = win.ws_col;
2738 LINES = win.ws_row;
2739 if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
2740 die_too_small();
2741
2742#ifndef DISABLE_WRAPJUSTIFY
2743 fill = wrap_at;
2744 if (fill <= 0)
2745 fill += COLS;
2746 if (fill < MIN_FILL_LENGTH)
2747 die_too_small();
2748#endif
2749
2750 hblank = nrealloc(hblank, COLS + 1);
2751 memset(hblank, ' ', COLS);
2752 hblank[COLS] = '\0';
2753
2754#ifdef HAVE_RESIZETERM
2755 resizeterm(LINES, COLS);
2756#ifdef HAVE_WRESIZE
2757 if (wresize(topwin, 2, COLS) == ERR)
2758 die(_("Cannot resize top win"));
2759 if (mvwin(topwin, 0, 0) == ERR)
2760 die(_("Cannot move top win"));
2761 if (wresize(edit, editwinrows, COLS) == ERR)
2762 die(_("Cannot resize edit win"));
2763 if (mvwin(edit, 2, 0) == ERR)
2764 die(_("Cannot move edit win"));
2765 if (wresize(bottomwin, 3 - no_help(), COLS) == ERR)
2766 die(_("Cannot resize bottom win"));
2767 if (mvwin(bottomwin, LINES - 3 + no_help(), 0) == ERR)
2768 die(_("Cannot move bottom win"));
2769#endif /* HAVE_WRESIZE */
2770#endif /* HAVE_RESIZETERM */
2771
2772 fix_editbot();
2773
2774 if (current_y > editwinrows - 1)
2775 edit_update(editbot, CENTER);
2776 erase();
2777
2778 /* Do these b/c width may have changed... */
2779 refresh();
2780 titlebar(NULL);
2781 edit_refresh();
2782 display_main_list();
2783 blank_statusbar();
2784 total_refresh();
2785
2786 /* Turn cursor back on for sure */
2787 curs_set(1);
2788
2789 /* Jump back to main loop */
2790 siglongjmp(jmpbuf, 1);
2791}
Chris Allegrettadab017e2002-04-23 10:56:06 +00002792#endif /* !NANO_SMALL */
David Lawrence Ramseyc5967552002-06-21 03:20:06 +00002793
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002794/* If the NumLock key has made the keypad go awry, print an error
2795 message; hopefully we can address it later. */
2796void print_numlock_warning(void)
2797{
2798 static int didmsg = 0;
2799 if (!didmsg) {
2800 statusbar(_
2801 ("NumLock glitch detected. Keypad will malfunction with NumLock off"));
2802 didmsg = 1;
2803 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002804}
2805
Chris Allegrettadab017e2002-04-23 10:56:06 +00002806#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002807void do_toggle(const toggle *which)
Chris Allegretta756f2202000-09-01 13:32:47 +00002808{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002809 int enabled;
Chris Allegrettaf0f63a82000-09-02 18:44:21 +00002810
Chris Allegretta658399a2001-06-14 02:54:22 +00002811 /* Even easier! */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002812 TOGGLE(which->flag);
Chris Allegretta2a42af12000-09-12 23:02:49 +00002813
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002814 switch (which->val) {
Chris Allegretta756f2202000-09-01 13:32:47 +00002815 case TOGGLE_PICOMODE_KEY:
Chris Allegretta07798352000-11-27 22:58:23 +00002816 shortcut_init(0);
Chris Allegrettac1049ac2001-08-17 00:03:46 +00002817 SET(CLEAR_BACKUPSTRING);
Chris Allegretta756f2202000-09-01 13:32:47 +00002818 display_main_list();
2819 break;
2820 case TOGGLE_SUSPEND_KEY:
2821 signal_init();
2822 break;
2823 case TOGGLE_MOUSE_KEY:
2824 mouse_init();
2825 break;
2826 case TOGGLE_NOHELP_KEY:
Chris Allegretta2a42af12000-09-12 23:02:49 +00002827 wclear(bottomwin);
2828 wrefresh(bottomwin);
2829 window_init();
Chris Allegrettaaffeda82000-12-18 04:03:48 +00002830 fix_editbot();
Chris Allegretta2a42af12000-09-12 23:02:49 +00002831 edit_refresh();
2832 display_main_list();
Chris Allegretta756f2202000-09-01 13:32:47 +00002833 break;
Chris Allegretta99e30e12001-09-23 02:45:27 +00002834 case TOGGLE_DOS_KEY:
2835 UNSET(MAC_FILE);
2836 break;
2837 case TOGGLE_MAC_KEY:
2838 UNSET(DOS_FILE);
2839 break;
Chris Allegretta756f2202000-09-01 13:32:47 +00002840 }
Chris Allegretta2a42af12000-09-12 23:02:49 +00002841
Chris Allegretta6df90f52002-07-19 01:08:59 +00002842 /* We are assuming here that shortcut_init() above didn't free and
2843 * reallocate the toggles. */
2844 enabled = ISSET(which->flag);
2845 if (which->val == TOGGLE_NOHELP_KEY || which->val == TOGGLE_WRAP_KEY)
2846 enabled = !enabled;
2847 statusbar("%s %s", which->desc,
2848 enabled ? _("enabled") : _("disabled"));
Chris Allegretta756f2202000-09-01 13:32:47 +00002849}
Chris Allegrettadab017e2002-04-23 10:56:06 +00002850#endif /* !NANO_SMALL */
Chris Allegretta756f2202000-09-01 13:32:47 +00002851
Chris Allegretta1748cd12001-01-13 17:22:54 +00002852/* This function returns the correct keystroke, given the A,B,C or D
2853 input key. This is a common sequence of many terms which send
2854 Esc-O-[A-D] or Esc-[-[A-D]. */
2855int ABCD(int input)
2856{
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002857 switch (input) {
2858 case 'A':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002859 case 'a':
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002860 return (KEY_UP);
2861 case 'B':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002862 case 'b':
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002863 return (KEY_DOWN);
2864 case 'C':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002865 case 'c':
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002866 return (KEY_RIGHT);
2867 case 'D':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002868 case 'd':
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002869 return (KEY_LEFT);
2870 default:
2871 return 0;
Chris Allegretta1748cd12001-01-13 17:22:54 +00002872 }
2873}
2874
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002875int main(int argc, char *argv[])
2876{
2877 int optchr;
2878 int kbinput; /* Input from keyboard */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00002879 int startline = 0; /* Line to try and start at */
Chris Allegretta08020882001-01-29 23:37:54 +00002880 int keyhandled; /* Have we handled the keystroke yet? */
Chris Allegretta9caa1932002-02-15 20:08:05 +00002881 int modify_control_seq;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002882 const char *argv0;
2883 const shortcut *s;
Chris Allegretta40ecbad2002-03-06 15:27:44 +00002884#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00002885 const toggle *t;
Chris Allegretta40ecbad2002-03-06 15:27:44 +00002886#endif
Chris Allegretta0357c4d2001-09-19 02:59:25 +00002887
Chris Allegretta9b4055c2002-03-29 16:00:59 +00002888#ifdef _POSIX_VDISABLE
2889 struct termios term;
2890#endif
2891
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002892#ifdef HAVE_GETOPT_LONG
2893 int option_index = 0;
2894 struct option long_options[] = {
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002895 {"help", 0, 0, 'h'},
2896#ifdef ENABLE_MULTIBUFFER
2897 {"multibuffer", 0, 0, 'F'},
2898#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00002899#ifdef ENABLE_NANORC
2900 {"ignorercfiles", 0, 0, 'I'},
2901#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002902 {"keypad", 0, 0, 'K'},
2903#ifndef DISABLE_JUSTIFY
2904 {"quotestr", 1, 0, 'Q'},
2905#endif
Chris Allegretta805c26d2000-09-06 13:39:17 +00002906#ifdef HAVE_REGEX_H
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00002907 {"regexp", 0, 0, 'R'},
Chris Allegretta47805612000-07-07 02:35:34 +00002908#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002909 {"tabsize", 1, 0, 'T'},
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002910 {"version", 0, 0, 'V'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002911#ifdef ENABLE_COLOR
2912 {"syntax", 1, 0, 'Y'},
2913#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002914 {"const", 0, 0, 'c'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002915 {"nofollow", 0, 0, 'l'},
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002916 {"mouse", 0, 0, 'm'},
Chris Allegrettae1f14522001-09-19 03:19:43 +00002917#ifndef DISABLE_OPERATINGDIR
2918 {"operatingdir", 1, 0, 'o'},
2919#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002920 {"pico", 0, 0, 'p'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002921#ifndef DISABLE_WRAPJUSTIFY
2922 {"fill", 1, 0, 'r'},
Chris Allegretta2d7893d2001-07-11 02:08:33 +00002923#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002924#ifndef DISABLE_SPELLER
2925 {"speller", 1, 0, 's'},
2926#endif
2927 {"tempfile", 0, 0, 't'},
2928 {"view", 0, 0, 'v'},
2929 {"nowrap", 0, 0, 'w'},
2930 {"nohelp", 0, 0, 'x'},
2931 {"suspend", 0, 0, 'z'},
Chris Allegretta3e3ae942001-09-22 19:02:04 +00002932#ifndef NANO_SMALL
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002933 {"backup", 0, 0, 'B'},
2934 {"dos", 0, 0, 'D'},
2935 {"mac", 0, 0, 'M'},
2936 {"noconvert", 0, 0, 'N'},
Chris Allegretta3e3ae942001-09-22 19:02:04 +00002937 {"smooth", 0, 0, 'S'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002938 {"autoindent", 0, 0, 'i'},
2939 {"cut", 0, 0, 'k'},
Chris Allegretta3e3ae942001-09-22 19:02:04 +00002940#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002941 {0, 0, 0, 0}
2942 };
2943#endif
2944
2945 /* Flag inits... */
2946 SET(FOLLOW_SYMLINKS);
2947
2948#ifndef NANO_SMALL
Chris Allegretta8bc03b62001-02-09 02:57:52 +00002949#ifdef ENABLE_NLS
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002950 setlocale(LC_ALL, "");
2951 bindtextdomain(PACKAGE, LOCALEDIR);
2952 textdomain(PACKAGE);
2953#endif
Chris Allegretta8bc03b62001-02-09 02:57:52 +00002954#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002955
Chris Allegretta8d8e0122001-04-18 04:28:54 +00002956#ifdef ENABLE_NANORC
Chris Allegretta1c5c3382002-07-23 00:33:07 +00002957 {
2958 /* scan through the options and handle -I/--ignorercfiles
2959 first, so that it's handled before we call do_rcfile() and
2960 read the other options; don't use getopt()/getopt_long()
2961 here, because there's no way to reset it properly
2962 afterward */
2963 int i;
2964 for (i = 1; i < argc; i++) {
2965 if (!strcmp(argv[i], "--"))
2966 break;
2967 else if (!strcmp(argv[i], "-I"))
2968 SET(NO_RCFILE);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002969#ifdef HAVE_GETOPT_LONG
Chris Allegretta1c5c3382002-07-23 00:33:07 +00002970 else if (!strcmp(argv[i], "--ignorercfiles"))
2971 SET(NO_RCFILE);
Chris Allegretta6df90f52002-07-19 01:08:59 +00002972#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00002973 }
2974 }
Chris Allegretta1c5c3382002-07-23 00:33:07 +00002975 if (!ISSET(NO_RCFILE))
2976 do_rcfile();
Chris Allegretta6df90f52002-07-19 01:08:59 +00002977#endif /* ENABLE_NANORC */
2978
2979#ifdef HAVE_GETOPT_LONG
2980 while ((optchr = getopt_long(argc, argv, "h?BDFIKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz",
2981 long_options, &option_index)) != EOF) {
2982#else
2983 while ((optchr =
2984 getopt(argc, argv, "h?BDFIKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz")) != EOF) {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002985#endif
2986
2987 switch (optchr) {
Chris Allegretta2d7893d2001-07-11 02:08:33 +00002988
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00002989 case 'h':
2990 case '?':
2991 usage();
2992 exit(0);
2993 case 'a':
2994 case 'b':
2995 case 'e':
2996 case 'f':
2997 case 'g':
2998 case 'j':
2999 /* Pico compatibility flags */
3000 break;
Chris Allegretta7004c282001-09-22 00:42:10 +00003001#ifndef NANO_SMALL
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003002 case 'B':
3003 SET(BACKUP_FILE);
3004 break;
Chris Allegretta7004c282001-09-22 00:42:10 +00003005 case 'D':
3006 SET(DOS_FILE);
3007 break;
3008#endif
Chris Allegretta355fbe52001-07-14 19:32:47 +00003009#ifdef ENABLE_MULTIBUFFER
Chris Allegretta307d4c82001-07-15 20:25:33 +00003010 case 'F':
Chris Allegretta355fbe52001-07-14 19:32:47 +00003011 SET(MULTIBUFFER);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00003012 break;
3013#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00003014#ifdef ENABLE_NANORC
Chris Allegretta6df90f52002-07-19 01:08:59 +00003015 case 'I':
3016 break;
3017#endif
Chris Allegretta48bd3782002-01-03 21:26:34 +00003018 case 'K':
3019 SET(ALT_KEYPAD);
3020 break;
Chris Allegretta8fa1e282001-09-22 04:20:25 +00003021#ifndef NANO_SMALL
3022 case 'M':
3023 SET(MAC_FILE);
3024 break;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003025 case 'N':
3026 SET(NO_CONVERT);
Chris Allegretta6724a7e2000-06-19 23:19:07 +00003027 break;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003028#endif
Chris Allegrettae4f940d2002-03-03 22:36:36 +00003029 case 'Q':
3030#ifndef DISABLE_JUSTIFY
3031 quotestr = optarg;
3032 break;
3033#else
Chris Allegretta6df90f52002-07-19 01:08:59 +00003034 usage();
3035 exit(1);
Chris Allegrettae4f940d2002-03-03 22:36:36 +00003036#endif
Chris Allegretta805c26d2000-09-06 13:39:17 +00003037#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +00003038 case 'R':
3039 SET(USE_REGEXP);
3040 break;
Chris Allegretta47805612000-07-07 02:35:34 +00003041#endif
Chris Allegretta3e3ae942001-09-22 19:02:04 +00003042#ifndef NANO_SMALL
3043 case 'S':
3044 SET(SMOOTHSCROLL);
3045 break;
3046#endif
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003047 case 'T':
Chris Allegretta6df90f52002-07-19 01:08:59 +00003048 {
3049 int i;
3050 char *first_error;
3051
3052 /* Using strtol instead of atoi lets us accept 0 while
3053 * checking other errors. */
3054 i = (int)strtol(optarg, &first_error, 10);
3055 if (errno == ERANGE || *optarg == '\0' || *first_error != '\0') {
3056 usage();
3057 exit(1);
3058 } else
3059 tabsize = i;
3060 if (tabsize <= 0) {
3061 fprintf(stderr, _("Tab size is too small for nano...\n"));
3062 exit(1);
3063 }
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003064 }
3065 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003066 case 'V':
3067 version();
3068 exit(0);
Chris Allegretta09900ff2002-05-04 04:23:30 +00003069#ifdef ENABLE_COLOR
3070 case 'Y':
3071 syntaxstr = mallocstrcpy(syntaxstr, optarg);
3072 break;
3073#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003074 case 'c':
3075 SET(CONSTUPDATE);
3076 break;
Chris Allegrettaff989832001-09-17 13:48:00 +00003077#ifndef NANO_SMALL
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003078 case 'i':
3079 SET(AUTOINDENT);
3080 break;
Chris Allegretta627de192000-07-12 02:09:17 +00003081 case 'k':
3082 SET(CUT_TO_END);
3083 break;
Chris Allegrettad19e9912000-07-12 18:14:51 +00003084#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003085 case 'l':
3086 UNSET(FOLLOW_SYMLINKS);
3087 break;
3088 case 'm':
3089 SET(USE_MOUSE);
3090 break;
Chris Allegrettae1f14522001-09-19 03:19:43 +00003091#ifndef DISABLE_OPERATINGDIR
3092 case 'o':
Chris Allegretta7c27be42002-05-05 23:03:54 +00003093 operating_dir = mallocstrcpy(operating_dir, optarg);
Chris Allegrettae1f14522001-09-19 03:19:43 +00003094
3095 /* make sure we're inside the operating directory */
Chris Allegretta6df90f52002-07-19 01:08:59 +00003096 if (check_operating_dir(".", 0) && chdir(operating_dir) == -1) {
3097 free(operating_dir);
3098 operating_dir = NULL;
Chris Allegrettae1f14522001-09-19 03:19:43 +00003099 }
3100 break;
3101#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003102 case 'p':
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +00003103 SET(PICO_MODE);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003104 break;
Chris Allegretta6fe61492001-05-21 12:56:25 +00003105#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +00003106 case 'r':
3107 {
3108 int i;
3109 char *first_error;
3110
3111 /* Using strtol instead of atoi lets us accept 0 while
3112 * checking other errors. */
3113 i = (int)strtol(optarg, &first_error, 10);
3114 if (errno == ERANGE || *optarg == '\0' || *first_error != '\0') {
3115 usage();
3116 exit(1);
3117 } else
3118 wrap_at = i;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003119 }
3120 break;
Chris Allegretta6fe61492001-05-21 12:56:25 +00003121#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +00003122#ifndef DISABLE_SPELLER
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003123 case 's':
Chris Allegretta7c27be42002-05-05 23:03:54 +00003124 alt_speller = mallocstrcpy(alt_speller, optarg);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003125 break;
Rocco Corsiaf5c3022001-01-12 07:51:05 +00003126#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003127 case 't':
Chris Allegretta30885552000-07-14 01:20:12 +00003128 SET(TEMP_OPT);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003129 break;
3130 case 'v':
3131 SET(VIEW_MODE);
3132 break;
3133 case 'w':
Chris Allegrettacef7fbb2001-04-02 05:36:08 +00003134#ifdef DISABLE_WRAPPING
3135 usage();
3136 exit(0);
3137#else
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003138 SET(NO_WRAP);
3139 break;
Chris Allegretta6df90f52002-07-19 01:08:59 +00003140#endif /* DISABLE_WRAPPING */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003141 case 'x':
3142 SET(NO_HELP);
3143 break;
3144 case 'z':
3145 SET(SUSPEND);
3146 break;
3147 default:
3148 usage();
Chris Allegretta4da1fc62000-06-21 03:00:43 +00003149 exit(0);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003150 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003151 }
3152
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003153 /* Clear the filename we'll be using */
3154 filename = charalloc(1);
3155 filename[0] = '\0';
3156
3157 /* See if we were invoked with the name "pico" */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003158 argv0 = strrchr(argv[0], '/');
3159 if ((argv0 && strstr(argv0, "pico"))
3160 || (!argv0 && strstr(argv[0], "pico")))
Chris Allegrettabf9a8cc2000-11-17 01:37:39 +00003161 SET(PICO_MODE);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003162
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003163 /* See if there's a non-option in argv (first non-option is the
3164 filename, if +LINE is not given) */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003165 if (argc > 1 && argc > optind) {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003166 /* Look for the +line flag... */
3167 if (argv[optind][0] == '+') {
3168 startline = atoi(&argv[optind][1]);
3169 optind++;
Chris Allegretta6df90f52002-07-19 01:08:59 +00003170 if (argc > optind)
Chris Allegretta1a6e9042000-12-14 13:56:28 +00003171 filename = mallocstrcpy(filename, argv[optind]);
Chris Allegretta1a6e9042000-12-14 13:56:28 +00003172 } else
3173 filename = mallocstrcpy(filename, argv[optind]);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003174 }
3175
Chris Allegretta9b4055c2002-03-29 16:00:59 +00003176 /* First back up the old settings so they can be restored, duh */
3177 tcgetattr(0, &oldterm);
3178
3179#ifdef _POSIX_VDISABLE
3180 term = oldterm;
3181 term.c_cc[VINTR] = _POSIX_VDISABLE;
3182 term.c_cc[VQUIT] = _POSIX_VDISABLE;
3183 term.c_lflag &= ~IEXTEN;
3184 tcsetattr(0, TCSANOW, &term);
3185#endif
Chris Allegretta6ca01b12002-03-29 15:38:17 +00003186
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003187 /* now ncurses init stuff... */
3188 initscr();
3189 savetty();
3190 nonl();
3191 cbreak();
3192 noecho();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003193
3194 /* Set up some global variables */
Chris Allegretta56214c62001-09-27 02:46:53 +00003195 global_init(0);
Chris Allegretta07798352000-11-27 22:58:23 +00003196 shortcut_init(0);
Chris Allegretta9b4055c2002-03-29 16:00:59 +00003197 signal_init();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003198
3199#ifdef DEBUG
3200 fprintf(stderr, _("Main: set up windows\n"));
3201#endif
3202
Chris Allegretta2a42af12000-09-12 23:02:49 +00003203 window_init();
Chris Allegretta756f2202000-09-01 13:32:47 +00003204 mouse_init();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003205
Chris Allegretta48bd3782002-01-03 21:26:34 +00003206 if (!ISSET(ALT_KEYPAD)) {
3207 keypad(edit, TRUE);
3208 keypad(bottomwin, TRUE);
3209 }
3210
Chris Allegretta08893e02001-11-29 02:42:27 +00003211#ifdef ENABLE_COLOR
3212 do_colorinit();
Chris Allegretta08893e02001-11-29 02:42:27 +00003213#endif /* ENABLE_COLOR */
3214
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003215#ifdef DEBUG
3216 fprintf(stderr, _("Main: bottom win\n"));
3217#endif
Chris Allegretta88520c92001-05-05 17:45:54 +00003218 /* Set up bottom of window */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003219 display_main_list();
3220
3221#ifdef DEBUG
3222 fprintf(stderr, _("Main: open file\n"));
3223#endif
3224
Chris Allegretta31c76662000-11-21 06:20:20 +00003225 /* Now we check to see if argv[optind] is non-null to determine if
3226 we're dealing with a new file or not, not argc == 1... */
3227 if (argv[optind] == NULL)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003228 new_file();
3229 else
3230 open_file(filename, 0, 0);
3231
Chris Allegretta4dc03d52002-05-11 03:04:44 +00003232 titlebar(NULL);
3233
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003234 if (startline > 0)
Chris Allegretta2d7893d2001-07-11 02:08:33 +00003235 do_gotoline(startline, 0);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003236 else
Chris Allegretta234a34d2000-07-29 04:33:38 +00003237 edit_update(fileage, CENTER);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003238
Chris Allegretta08020882001-01-29 23:37:54 +00003239 /* return here after a sigwinch */
Chris Allegretta6b58acd2001-04-12 03:01:53 +00003240 sigsetjmp(jmpbuf, 1);
Chris Allegretta08020882001-01-29 23:37:54 +00003241
3242 /* Fix clobber-age */
3243 kbinput = 0;
3244 keyhandled = 0;
3245 modify_control_seq = 0;
3246
Robert Siemborski6967eec2000-07-08 14:23:32 +00003247 edit_refresh();
3248 reset_cursor();
3249
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003250 while (1) {
Chris Allegretta9239d742000-09-06 15:19:18 +00003251
Chris Allegretta6fe61492001-05-21 12:56:25 +00003252#ifndef DISABLE_MOUSE
Chris Allegretta6b58acd2001-04-12 03:01:53 +00003253 currshortcut = main_list;
Chris Allegretta6fe61492001-05-21 12:56:25 +00003254#endif
Chris Allegretta6b58acd2001-04-12 03:01:53 +00003255
Chris Allegretta9239d742000-09-06 15:19:18 +00003256#ifndef _POSIX_VDISABLE
3257 /* We're going to have to do it the old way, i.e. on cygwin */
3258 raw();
3259#endif
3260
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003261 kbinput = wgetch(edit);
Chris Allegrettac08f50d2001-01-06 18:12:43 +00003262#ifdef DEBUG
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003263 fprintf(stderr, _("AHA! %c (%d)\n"), kbinput, kbinput);
Chris Allegrettac08f50d2001-01-06 18:12:43 +00003264#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003265 if (kbinput == 27) { /* Grab Alt-key stuff first */
3266 switch (kbinput = wgetch(edit)) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003267 /* Alt-O, suddenly very important ;) */
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003268 case 'O':
Chris Allegretta16e41682000-09-11 22:33:54 +00003269 kbinput = wgetch(edit);
Chris Allegretta316e4d92001-04-28 16:31:19 +00003270 if ((kbinput <= 'D' && kbinput >= 'A') ||
3271 (kbinput <= 'd' && kbinput >= 'a'))
Chris Allegretta6b58acd2001-04-12 03:01:53 +00003272 kbinput = ABCD(kbinput);
Chris Allegretta201d9bf2001-01-14 03:17:53 +00003273 else if (kbinput <= 'z' && kbinput >= 'j')
3274 print_numlock_warning();
3275 else if (kbinput <= 'S' && kbinput >= 'P')
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003276 kbinput = KEY_F(kbinput - 79);
Chris Allegretta16e41682000-09-11 22:33:54 +00003277#ifdef DEBUG
3278 else {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003279 fprintf(stderr, _("I got Alt-O-%c! (%d)\n"),
3280 kbinput, kbinput);
3281 break;
Chris Allegretta16e41682000-09-11 22:33:54 +00003282 }
3283#endif
3284 break;
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003285 case 27:
3286 /* If we get Alt-Alt, the next keystroke should be the same as a
3287 control sequence */
3288 modify_control_seq = 1;
3289 keyhandled = 1;
3290 break;
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003291 case '[':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003292 switch (kbinput = wgetch(edit)) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003293 case '1': /* Alt-[-1-[0-5,7-9] = F1-F8 in X at least */
Chris Allegretta16e41682000-09-11 22:33:54 +00003294 kbinput = wgetch(edit);
3295 if (kbinput >= '1' && kbinput <= '5') {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003296 kbinput = KEY_F(kbinput - 48);
3297 wgetch(edit);
3298 } else if (kbinput >= '7' && kbinput <= '9') {
3299 kbinput = KEY_F(kbinput - 49);
3300 wgetch(edit);
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003301 } else if (kbinput == '~')
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003302 kbinput = KEY_HOME;
Chris Allegretta16e41682000-09-11 22:33:54 +00003303#ifdef DEBUG
3304 else {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003305 fprintf(stderr, _("I got Alt-[-1-%c! (%d)\n"),
3306 kbinput, kbinput);
3307 break;
Chris Allegretta16e41682000-09-11 22:33:54 +00003308 }
3309#endif
Chris Allegretta16e41682000-09-11 22:33:54 +00003310 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003311 case '2': /* Alt-[-2-[0,1,3,4] = F9-F12 in many terms */
Chris Allegretta16e41682000-09-11 22:33:54 +00003312 kbinput = wgetch(edit);
Chris Allegretta16e41682000-09-11 22:33:54 +00003313 switch (kbinput) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003314 case '0':
3315 kbinput = KEY_F(9);
3316 wgetch(edit);
3317 break;
3318 case '1':
3319 kbinput = KEY_F(10);
3320 wgetch(edit);
3321 break;
3322 case '3':
3323 kbinput = KEY_F(11);
3324 wgetch(edit);
3325 break;
3326 case '4':
3327 kbinput = KEY_F(12);
3328 wgetch(edit);
3329 break;
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003330 case '~':
3331 goto do_insertkey;
Chris Allegretta16e41682000-09-11 22:33:54 +00003332#ifdef DEBUG
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003333 default:
3334 fprintf(stderr, _("I got Alt-[-2-%c! (%d)\n"),
3335 kbinput, kbinput);
3336 break;
Chris Allegretta16e41682000-09-11 22:33:54 +00003337#endif
Chris Allegretta16e41682000-09-11 22:33:54 +00003338 }
3339 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003340 case '3': /* Alt-[-3 = Delete? */
Chris Allegretta16e41682000-09-11 22:33:54 +00003341 kbinput = NANO_DELETE_KEY;
3342 wgetch(edit);
3343 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003344 case '4': /* Alt-[-4 = End? */
Chris Allegretta16e41682000-09-11 22:33:54 +00003345 kbinput = NANO_END_KEY;
3346 wgetch(edit);
3347 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003348 case '5': /* Alt-[-5 = Page Up */
Chris Allegretta16e41682000-09-11 22:33:54 +00003349 kbinput = KEY_PPAGE;
3350 wgetch(edit);
3351 break;
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003352 case 'V': /* Alt-[-V = Page Up in Hurd Console */
Chris Allegretta7bf72742001-10-28 04:29:55 +00003353 case 'I': /* Alt-[-I = Page Up - FreeBSD Console */
3354 kbinput = KEY_PPAGE;
3355 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003356 case '6': /* Alt-[-6 = Page Down */
Chris Allegretta16e41682000-09-11 22:33:54 +00003357 kbinput = KEY_NPAGE;
3358 wgetch(edit);
3359 break;
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003360 case 'U': /* Alt-[-U = Page Down in Hurd Console */
Chris Allegretta7bf72742001-10-28 04:29:55 +00003361 case 'G': /* Alt-[-G = Page Down - FreeBSD Console */
3362 kbinput = KEY_NPAGE;
3363 break;
Chris Allegrettab26ecb52001-07-04 16:27:05 +00003364 case '7':
3365 kbinput = KEY_HOME;
3366 wgetch(edit);
3367 break;
3368 case '8':
3369 kbinput = KEY_END;
3370 wgetch(edit);
3371 break;
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003372 case '9': /* Alt-[-9 = Delete in Hurd Console */
3373 kbinput = KEY_DC;
3374 break;
Chris Allegretta32da4562002-01-02 15:12:21 +00003375 case '@': /* Alt-[-@ = Insert in Hurd Console */
3376 case 'L': /* Alt-[-L = Insert - FreeBSD Console */
3377 goto do_insertkey;
3378 case '[': /* Alt-[-[-[A-E], F1-F5 in Linux console */
Chris Allegretta16e41682000-09-11 22:33:54 +00003379 kbinput = wgetch(edit);
Chris Allegrettab26ecb52001-07-04 16:27:05 +00003380 if (kbinput >= 'A' && kbinput <= 'E')
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003381 kbinput = KEY_F(kbinput - 64);
Chris Allegretta16e41682000-09-11 22:33:54 +00003382 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003383 case 'A':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003384 case 'B':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003385 case 'C':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003386 case 'D':
Chris Allegretta316e4d92001-04-28 16:31:19 +00003387 case 'a':
3388 case 'b':
3389 case 'c':
3390 case 'd':
Chris Allegretta1748cd12001-01-13 17:22:54 +00003391 kbinput = ABCD(kbinput);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003392 break;
3393 case 'H':
3394 kbinput = KEY_HOME;
3395 break;
3396 case 'F':
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003397 case 'Y': /* End Key in Hurd Console */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003398 kbinput = KEY_END;
3399 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003400 default:
3401#ifdef DEBUG
3402 fprintf(stderr, _("I got Alt-[-%c! (%d)\n"),
3403 kbinput, kbinput);
3404#endif
3405 break;
3406 }
3407 break;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003408
Chris Allegretta355fbe52001-07-14 19:32:47 +00003409#ifdef ENABLE_MULTIBUFFER
Chris Allegretta819e3db2001-07-11 02:37:19 +00003410 case NANO_OPENPREV_KEY:
Chris Allegretta180a5692002-01-02 14:30:33 +00003411 case NANO_OPENPREV_ALTKEY:
Chris Allegretta6df90f52002-07-19 01:08:59 +00003412 open_prevfile_void();
Chris Allegretta819e3db2001-07-11 02:37:19 +00003413 keyhandled = 1;
3414 break;
3415 case NANO_OPENNEXT_KEY:
Chris Allegretta180a5692002-01-02 14:30:33 +00003416 case NANO_OPENNEXT_ALTKEY:
Chris Allegretta6df90f52002-07-19 01:08:59 +00003417 open_nextfile_void();
Chris Allegretta819e3db2001-07-11 02:37:19 +00003418 keyhandled = 1;
3419 break;
Chris Allegretta9cf9e062001-07-11 12:06:13 +00003420#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003421 default:
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003422 /* Check for the altkey defs.... */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003423 for (s = main_list; s != NULL; s = s->next)
3424 if (kbinput == s->altval ||
Chris Allegretta6232d662002-05-12 19:52:15 +00003425 kbinput == s->altval - 32) {
3426 if (ISSET(VIEW_MODE) && !s->viewok)
3427 print_view_warning();
3428 else
3429 s->func();
3430 keyhandled = 1;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003431 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003432 }
Chris Allegretta756f2202000-09-01 13:32:47 +00003433#ifndef NANO_SMALL
3434 /* And for toggle switches */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003435 for (t = toggles; t != NULL && !keyhandled; t = t->next)
3436 if (kbinput == t->val ||
3437 (t->val > 'a' &&
3438 kbinput == t->val - 32)) {
3439 do_toggle(t);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003440 keyhandled = 1;
3441 break;
Chris Allegretta756f2202000-09-01 13:32:47 +00003442 }
3443#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003444#ifdef DEBUG
3445 fprintf(stderr, _("I got Alt-%c! (%d)\n"), kbinput,
3446 kbinput);
3447#endif
3448 break;
3449 }
3450 }
Chris Allegrettacf287c82002-07-20 13:57:41 +00003451 /* If modify_control_seq is set, we received an Alt-Alt
3452 sequence before this, so we make this key a control sequence
3453 by subtracting 32, 64, or 96, depending on its value. */
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003454 if (!keyhandled && modify_control_seq) {
Chris Allegrettacf287c82002-07-20 13:57:41 +00003455 if (kbinput == ' ')
3456 kbinput -= 32;
3457 else if (kbinput >= 'A' && kbinput < 'a')
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003458 kbinput -= 64;
3459 else if (kbinput >= 'a' && kbinput <= 'z')
3460 kbinput -= 96;
3461
3462 modify_control_seq = 0;
3463 }
3464
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003465 /* Look through the main shortcut list to see if we've hit a
3466 shortcut key */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00003467
Chris Allegrettaf5de33a2002-02-27 04:14:16 +00003468#if !defined(DISABLE_BROWSER) || !defined(DISABLE_MOUSE) || !defined (DISABLE_HELP)
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003469 for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
Chris Allegrettaf5de33a2002-02-27 04:14:16 +00003470#else
3471 for (s = main_list; s != NULL && !keyhandled; s = s->next) {
3472#endif
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003473 if (kbinput == s->val ||
3474 (s->misc1 && kbinput == s->misc1) ||
3475 (s->misc2 && kbinput == s->misc2)) {
3476 if (ISSET(VIEW_MODE) && !s->viewok)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003477 print_view_warning();
3478 else
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003479 s->func();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003480 keyhandled = 1;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00003481 /* rarely, the value of s can change after s->func(),
3482 leading to problems; get around this by breaking out
3483 explicitly once we successfully handle a shortcut */
3484 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003485 }
3486 }
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003487 /* If we're in raw mode or using Alt-Alt-x, we have to catch
3488 Control-S and Control-Q */
Chris Allegretta9239d742000-09-06 15:19:18 +00003489 if (kbinput == 17 || kbinput == 19)
3490 keyhandled = 1;
3491
Chris Allegretta9caa1932002-02-15 20:08:05 +00003492 /* Catch ^Z by hand when triggered also
3493 407 == ^Z in Linux console when keypad() is used? */
3494 if (kbinput == 26 || kbinput == 407) {
Chris Allegretta9239d742000-09-06 15:19:18 +00003495 if (ISSET(SUSPEND))
3496 do_suspend(0);
3497 keyhandled = 1;
3498 }
Chris Allegretta9239d742000-09-06 15:19:18 +00003499
Chris Allegretta1c27d3e2001-10-02 02:56:45 +00003500#ifndef USE_SLANG
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003501 /* Hack, make insert key do something useful, like insert file */
3502 if (kbinput == KEY_IC) {
Chris Allegretta1c27d3e2001-10-02 02:56:45 +00003503#else
3504 if (0) {
3505#endif
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003506 do_insertkey:
3507
3508#ifdef ENABLE_MULTIBUFFER
Chris Allegretta32da4562002-01-02 15:12:21 +00003509 /* do_insertfile_void() contains the logic needed to
3510 handle view mode with the view mode/multibuffer
3511 exception, so use it here */
3512 do_insertfile_void();
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003513#else
Chris Allegretta6df90f52002-07-19 01:08:59 +00003514 if (!ISSET(VIEW_MODE))
3515 do_insertfile_void();
3516 else
3517 print_view_warning();
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003518#endif
3519
Chris Allegretta1c27d3e2001-10-02 02:56:45 +00003520 keyhandled = 1;
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003521 }
3522
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003523 /* Last gasp, stuff that's not in the main lists */
3524 if (!keyhandled)
3525 switch (kbinput) {
Chris Allegretta84de5522001-04-12 14:51:48 +00003526#ifndef DISABLE_MOUSE
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003527#ifdef NCURSES_MOUSE_VERSION
3528 case KEY_MOUSE:
3529 do_mouse();
3530 break;
3531#endif
3532#endif
Chris Allegrettaad3f4782001-10-02 03:54:13 +00003533
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003534 case 0: /* Erg */
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003535 case -1: /* Stuff that we don't want to do squat */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003536 case 410: /* Must ignore this, it gets sent when we resize */
Chris Allegrettab3655b42001-10-22 03:15:31 +00003537 case 29: /* Ctrl-] */
Chris Allegrettaad1dacc2000-09-21 04:25:45 +00003538#ifdef PDCURSES
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003539 case 541: /* ???? */
3540 case 542: /* Control and alt in Windows *shrug* */
Chris Allegretta72623582000-11-29 23:43:28 +00003541 case 543: /* Right ctrl key */
Chris Allegrettaad1dacc2000-09-21 04:25:45 +00003542 case 544:
Chris Allegretta72623582000-11-29 23:43:28 +00003543 case 545: /* Right alt key */
Chris Allegrettaad1dacc2000-09-21 04:25:45 +00003544#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003545
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003546 break;
3547 default:
3548#ifdef DEBUG
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003549 fprintf(stderr, _("I got %c (%d)!\n"), kbinput, kbinput);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003550#endif
3551 /* We no longer stop unhandled sequences so that people with
3552 odd character sets can type... */
3553
3554 if (ISSET(VIEW_MODE)) {
3555 print_view_warning();
3556 break;
3557 }
3558 do_char(kbinput);
3559 }
Chris Allegretta7fdbd052001-10-02 00:55:38 +00003560 if (ISSET(DISABLE_CURPOS))
3561 UNSET(DISABLE_CURPOS);
3562 else if (ISSET(CONSTUPDATE))
Chris Allegretta2084acc2001-11-29 03:43:08 +00003563 do_cursorpos(1);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003564
3565 reset_cursor();
3566 wrefresh(edit);
3567 keyhandled = 0;
3568 }
3569
3570 getchar();
3571 finish(0);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003572}