blob: ea0486f730e0e55e8e7ecf14a9422d03d8d72e1c [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 Allegrettaa2ea1932000-06-06 05:53:49 +000045#ifdef HAVE_TERMIOS_H
46#include <termios.h>
47#endif
48
49#ifdef HAVE_TERMIO_H
50#include <termio.h>
51#endif
52
53#ifdef HAVE_GETOPT_H
54#include <getopt.h>
55#endif
56
Chris Allegretta6fe61492001-05-21 12:56:25 +000057#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +000058static int fill = 0; /* Fill - where to wrap lines, basically */
Chris Allegretta6fe61492001-05-21 12:56:25 +000059#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +000060
Chris Allegretta6df90f52002-07-19 01:08:59 +000061static struct termios oldterm; /* The user's original term settings */
Chris Allegretta88520c92001-05-05 17:45:54 +000062static struct sigaction act; /* For all our fun signal handlers */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000063
Chris Allegretta08020882001-01-29 23:37:54 +000064static sigjmp_buf jmpbuf; /* Used to return to mainloop after SIGWINCH */
65
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000066/* What we do when we're all set to exit */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +000067RETSIGTYPE finish(int sigage)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000068{
Chris Allegretta5beed502003-01-05 20:41:21 +000069
70#ifndef NANO_SMALL
71 free_history(&search_history);
72 free_history(&replace_history);
73#endif
74
Chris Allegrettac08f50d2001-01-06 18:12:43 +000075 keypad(edit, TRUE);
76 keypad(bottomwin, TRUE);
77
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000078 if (!ISSET(NO_HELP)) {
79 mvwaddstr(bottomwin, 1, 0, hblank);
80 mvwaddstr(bottomwin, 2, 0, hblank);
Chris Allegretta4da1fc62000-06-21 03:00:43 +000081 } else
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000082 mvwaddstr(bottomwin, 0, 0, hblank);
Chris Allegretta6b58acd2001-04-12 03:01:53 +000083
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000084 wrefresh(bottomwin);
85 endwin();
86
87 /* Restore the old term settings */
Chris Allegretta4da1fc62000-06-21 03:00:43 +000088 tcsetattr(0, TCSANOW, &oldterm);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000089
Chris Allegretta6232d662002-05-12 19:52:15 +000090#ifdef DEBUG
Chris Allegrettaf5de33a2002-02-27 04:14:16 +000091 thanks_for_all_the_fish();
Chris Allegretta6232d662002-05-12 19:52:15 +000092#endif
Chris Allegrettaf5de33a2002-02-27 04:14:16 +000093
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000094 exit(sigage);
95}
96
97/* Die (gracefully?) */
Chris Allegretta6df90f52002-07-19 01:08:59 +000098void die(const char *msg, ...)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +000099{
100 va_list ap;
101
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000102 /* Restore the old term settings */
Chris Allegretta4da1fc62000-06-21 03:00:43 +0000103 tcsetattr(0, TCSANOW, &oldterm);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000104
105 clear();
106 refresh();
107 resetty();
108 endwin();
109
Chris Allegretta6df90f52002-07-19 01:08:59 +0000110 va_start(ap, msg);
111 vfprintf(stderr, msg, ap);
112 va_end(ap);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000113
Chris Allegretta32da4562002-01-02 15:12:21 +0000114 /* save the currently loaded file if it's been modified */
115 if (ISSET(MODIFIED))
116 die_save_file(filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000117
Chris Allegretta355fbe52001-07-14 19:32:47 +0000118#ifdef ENABLE_MULTIBUFFER
Chris Allegretta32da4562002-01-02 15:12:21 +0000119 /* then save all of the other modified loaded files, if any */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000120 if (open_files != NULL) {
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000121 openfilestruct *tmp;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000122
123 tmp = open_files;
124
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000125 while (open_files->prev != NULL)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000126 open_files = open_files->prev;
127
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000128 while (open_files->next != NULL) {
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000129
130 /* if we already saved the file above (i. e. if it was the
131 currently loaded file), don't save it again */
132 if (tmp != open_files) {
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000133 /* make sure open_files->fileage and fileage, and
134 open_files->filebot and filebot, are in sync; they
135 might not be if lines have been cut from the top or
136 bottom of the file */
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000137 fileage = open_files->fileage;
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000138 filebot = open_files->filebot;
Chris Allegretta32da4562002-01-02 15:12:21 +0000139 /* save the file if it's been modified */
Chris Allegretta4dc03d52002-05-11 03:04:44 +0000140 if (open_files->file_flags & MODIFIED)
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000141 die_save_file(open_files->filename);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000142 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000143 open_files = open_files->next;
144 }
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000145 }
146#endif
147
Chris Allegretta6df90f52002-07-19 01:08:59 +0000148 exit(1); /* We have a problem: exit w/ errorlevel(1) */
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000149}
150
Chris Allegretta6df90f52002-07-19 01:08:59 +0000151void die_save_file(const char *die_filename)
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000152{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000153 char *ret;
Chris Allegretta48b06702002-02-22 04:30:50 +0000154 int i = -1;
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000155
Chris Allegretta6df90f52002-07-19 01:08:59 +0000156 /* If we can't save, we have REAL bad problems, but we might as well
157 TRY. */
158 if (die_filename[0] == '\0')
159 ret = get_next_filename("nano.save");
Chris Allegretta48b06702002-02-22 04:30:50 +0000160 else {
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000161 char *buf = charalloc(strlen(die_filename) + 6);
Chris Allegretta6df90f52002-07-19 01:08:59 +0000162
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000163 strcpy(buf, die_filename);
164 strcat(buf, ".save");
Chris Allegretta48b06702002-02-22 04:30:50 +0000165 ret = get_next_filename(buf);
Chris Allegretta7162e3d2002-04-06 05:02:14 +0000166 free(buf);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000167 }
Chris Allegretta6df90f52002-07-19 01:08:59 +0000168 if (ret[0] != '\0')
169 i = write_file(ret, 1, 0, 0);
Chris Allegretta2d7893d2001-07-11 02:08:33 +0000170
Chris Allegretta3dbb2782000-12-02 04:36:50 +0000171 if (i != -1)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000172 fprintf(stderr, _("\nBuffer written to %s\n"), ret);
Chris Allegretta3dbb2782000-12-02 04:36:50 +0000173 else
Chris Allegretta6df90f52002-07-19 01:08:59 +0000174 fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), ret);
Chris Allegretta48b06702002-02-22 04:30:50 +0000175
176 free(ret);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000177}
178
Chris Allegrettae61e8302001-01-14 05:18:27 +0000179/* Die with an error message that the screen was too small if, well, the
Chris Allegretta6df90f52002-07-19 01:08:59 +0000180 * screen is too small. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000181void die_too_small(void)
Chris Allegrettae61e8302001-01-14 05:18:27 +0000182{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000183 die(_("Window size is too small for nano...\n"));
Chris Allegrettae61e8302001-01-14 05:18:27 +0000184}
185
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000186void print_view_warning(void)
187{
188 statusbar(_("Key illegal in VIEW mode"));
189}
190
Chris Allegretta56214c62001-09-27 02:46:53 +0000191/* Initialize global variables - no better way for now. If
Chris Allegretta6df90f52002-07-19 01:08:59 +0000192 * save_cutbuffer is nonzero, don't set cutbuffer to NULL. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000193void global_init(int save_cutbuffer)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000194{
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000195 current_x = 0;
196 current_y = 0;
Chris Allegrettae61e8302001-01-14 05:18:27 +0000197
198 if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
199 die_too_small();
200
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000201 fileage = NULL;
Chris Allegretta56214c62001-09-27 02:46:53 +0000202 if (!save_cutbuffer)
203 cutbuffer = NULL;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000204 current = NULL;
205 edittop = NULL;
206 editbot = NULL;
207 totlines = 0;
Chris Allegretta56214c62001-09-27 02:46:53 +0000208 totsize = 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000209 placewewant = 0;
Chris Allegrettae61e8302001-01-14 05:18:27 +0000210
Chris Allegretta6fe61492001-05-21 12:56:25 +0000211#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +0000212 fill = wrap_at;
Chris Allegrettadffa2072002-07-24 01:02:26 +0000213 if (fill <= 0)
Chris Allegretta6df90f52002-07-19 01:08:59 +0000214 fill += COLS;
Chris Allegrettae61e8302001-01-14 05:18:27 +0000215 if (fill < MIN_FILL_LENGTH)
216 die_too_small();
Chris Allegretta6fe61492001-05-21 12:56:25 +0000217#endif
Chris Allegrettae61e8302001-01-14 05:18:27 +0000218
Chris Allegretta88b09152001-05-17 11:35:43 +0000219 hblank = charalloc(COLS + 1);
Chris Allegretta0a06e072001-01-23 02:35:04 +0000220 memset(hblank, ' ', COLS);
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +0000221 hblank[COLS] = '\0';
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000222}
223
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000224void window_init(void)
225{
226 if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
227 die_too_small();
228
229 /* Set up the main text window */
230 edit = newwin(editwinrows, COLS, 2, 0);
231
232 /* And the other windows */
233 topwin = newwin(2, COLS, 0, 0);
234 bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
235
236#ifdef PDCURSES
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000237 /* Oops, I guess we need this again. Moved here so the keypad still
238 works after a Meta-X, for example */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000239 keypad(edit, TRUE);
240 keypad(bottomwin, TRUE);
241#endif
242}
243
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000244#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000245void mouse_init(void)
246{
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000247 if (ISSET(USE_MOUSE)) {
248 keypad_on(edit, 1);
249 keypad_on(bottomwin, 1);
250
251 mousemask(BUTTON1_RELEASED, NULL);
252 mouseinterval(50);
253 } else
254 mousemask(0, NULL);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000255}
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000256#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000257
258#ifndef DISABLE_HELP
259/* This function allocates help_text, and stores the help string in it.
260 * help_text should be NULL initially. */
261void help_init(void)
262{
263 size_t allocsize = 1; /* space needed for help_text */
264 char *ptr = NULL;
265#ifndef NANO_SMALL
266 const toggle *t;
267#endif
268 const shortcut *s;
269
270 /* First set up the initial help text for the current function */
271 if (currshortcut == whereis_list || currshortcut == replace_list
272 || currshortcut == replace_list_2)
273 ptr = _("Search Command Help Text\n\n "
274 "Enter the words or characters you would like to search "
275 "for, then hit enter. If there is a match for the text you "
276 "entered, the screen will be updated to the location of the "
277 "nearest match for the search string.\n\n "
Chris Allegretta37d594c2003-01-05 22:00:41 +0000278 "The previous search string will be shown in brackets after "
279 "the Search: prompt. Hitting Enter without entering any text "
280 "will perform the previous search.\n\n The following function "
281 "keys are available in Search mode:\n\n");
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000282 else if (currshortcut == goto_list)
283 ptr = _("Go To Line Help Text\n\n "
284 "Enter the line number that you wish to go to and hit "
285 "Enter. If there are fewer lines of text than the "
286 "number you entered, you will be brought to the last line "
287 "of the file.\n\n The following function keys are "
288 "available in Go To Line mode:\n\n");
289 else if (currshortcut == insertfile_list)
290 ptr = _("Insert File Help Text\n\n "
291 "Type in the name of a file to be inserted into the current "
292 "file buffer at the current cursor location.\n\n "
293 "If you have compiled nano with multiple file buffer "
294 "support, and enable multiple buffers with the -F "
295 "or --multibuffer command line flags, the Meta-F toggle, or "
296 "a nanorc file, inserting a file will cause it to be "
297 "loaded into a separate buffer (use Meta-< and > to switch "
298 "between file buffers).\n\n If you need another blank "
299 "buffer, do not enter any filename, or type in a "
300 "nonexistent filename at the prompt and press "
301 "Enter.\n\n The following function keys are "
302 "available in Insert File mode:\n\n");
303 else if (currshortcut == writefile_list)
304 ptr = _("Write File Help Text\n\n "
305 "Type the name that you wish to save the current file "
306 "as and hit Enter to save the file.\n\n If you have "
307 "selected text with Ctrl-^, you will be prompted to "
308 "save only the selected portion to a separate file. To "
309 "reduce the chance of overwriting the current file with "
310 "just a portion of it, the current filename is not the "
311 "default in this mode.\n\n The following function keys "
312 "are available in Write File mode:\n\n");
313#ifndef DISABLE_BROWSER
314 else if (currshortcut == browser_list)
315 ptr = _("File Browser Help Text\n\n "
316 "The file browser is used to visually browse the "
317 "directory structure to select a file for reading "
318 "or writing. You may use the arrow keys or Page Up/"
319 "Down to browse through the files, and S or Enter to "
320 "choose the selected file or enter the selected "
321 "directory. To move up one level, select the directory "
322 "called \"..\" at the top of the file list.\n\n The "
323 "following function keys are available in the file "
324 "browser:\n\n");
325 else if (currshortcut == gotodir_list)
326 ptr = _("Browser Go To Directory Help Text\n\n "
327 "Enter the name of the directory you would like to "
328 "browse to.\n\n If tab completion has not been disabled, "
329 "you can use the TAB key to (attempt to) automatically "
330 "complete the directory name.\n\n The following function "
331 "keys are available in Browser Go To Directory mode:\n\n");
332#endif
333 else if (currshortcut == spell_list)
334 ptr = _("Spell Check Help Text\n\n "
335 "The spell checker checks the spelling of all text "
336 "in the current file. When an unknown word is "
337 "encountered, it is highlighted and a replacement can "
338 "be edited. It will then prompt to replace every "
339 "instance of the given misspelled word in the "
340 "current file.\n\n The following other functions are "
341 "available in Spell Check mode:\n\n");
342#ifndef NANO_SMALL
343 else if (currshortcut == extcmd_list)
344 ptr = _("External Command Help Text\n\n "
345 "This menu allows you to insert the output of a command "
346 "run by the shell into the current buffer (or a new "
347 "buffer in multibuffer mode).\n\n The following keys are "
348 "available in this mode:\n\n");
349#endif
350 else /* Default to the main help list */
351 ptr = _(" nano help text\n\n "
352 "The nano editor is designed to emulate the functionality and "
353 "ease-of-use of the UW Pico text editor. There are four main "
354 "sections of the editor: The top line shows the program "
355 "version, the current filename being edited, and whether "
356 "or not the file has been modified. Next is the main editor "
357 "window showing the file being edited. The status line is "
358 "the third line from the bottom and shows important messages. "
359 "The bottom two lines show the most commonly used shortcuts "
360 "in the editor.\n\n "
361 "The notation for shortcuts is as follows: Control-key "
362 "sequences are notated with a caret (^) symbol and are entered "
363 "with the Control (Ctrl) key. Escape-key sequences are notated "
364 "with the Meta (M) symbol and can be entered using either the "
365 "Esc, Alt or Meta key depending on your keyboard setup. The "
366 "following keystrokes are available in the main editor window. "
367 "Alternative keys are shown in parentheses:\n\n");
368
369 allocsize += strlen(ptr);
370
371 /* The space needed for the shortcut lists, at most COLS characters,
372 * plus '\n'. */
373 allocsize += (COLS + 1) * length_of_list(currshortcut);
374
375#ifndef NANO_SMALL
376 /* If we're on the main list, we also count the toggle help text.
377 * Each line has "M-%c\t\t\t", which fills 24 columns, plus at most
378 * COLS - 24 characters, plus '\n'.*/
379 if (currshortcut == main_list)
380 for (t = toggles; t != NULL; t = t->next)
381 allocsize += COLS - 17;
382#endif /* !NANO_SMALL */
383
384 /* help_text has been freed and set to NULL unless the user resized
385 * while in the help screen. */
386 free(help_text);
387
388 /* Allocate space for the help text */
389 help_text = charalloc(allocsize);
390
391 /* Now add the text we want */
392 strcpy(help_text, ptr);
393 ptr = help_text + strlen(help_text);
394
395 /* Now add our shortcut info */
396 for (s = currshortcut; s != NULL; s = s->next) {
397 /* true if the character in s->altval is shown in first column */
398 int meta_shortcut = 0;
399
400 if (s->val > 0 && s->val < 32)
401 ptr += sprintf(ptr, "^%c", s->val + 64);
402#ifndef NANO_SMALL
403 else if (s->val == NANO_CONTROL_SPACE)
404 ptr += sprintf(ptr, "^%.6s", _("Space"));
405 else if (s->altval == NANO_ALT_SPACE) {
406 meta_shortcut = 1;
407 ptr += sprintf(ptr, "M-%.5s", _("Space"));
408 }
409#endif
410 else if (s->altval > 0) {
411 meta_shortcut = 1;
412 ptr += sprintf(ptr, "M-%c", s->altval -
413 (('A' <= s->altval && s->altval <= 'Z') ||
414 'a' <= s->altval ? 32 : 0));
415 }
416 /* Hack */
417 else if (s->val >= 'a') {
418 meta_shortcut = 1;
419 ptr += sprintf(ptr, "M-%c", s->val - 32);
420 }
421
422 *(ptr++) = '\t';
423
424 if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
425 ptr += sprintf(ptr, "(F%d)", s->misc1 - KEY_F0);
426
427 *(ptr++) = '\t';
428
429 if (!meta_shortcut && s->altval > 0)
430 ptr += sprintf(ptr, "(M-%c)", s->altval -
431 (('A' <= s->altval && s->altval <= 'Z') || 'a' <= s->altval
432 ? 32 : 0));
433
434 *(ptr++) = '\t';
435
436 assert(s->help != NULL);
437 ptr += sprintf(ptr, "%.*s\n", COLS - 24, s->help);
438 }
439
440#ifndef NANO_SMALL
441 /* And the toggles... */
442 if (currshortcut == main_list)
443 for (t = toggles; t != NULL; t = t->next) {
444 ptr += sprintf(ptr, "M-%c\t\t\t", t->val - 32);
445 assert(t->desc != NULL);
446 ptr += sprintf(ptr, _("%.*s enable/disable\n"), COLS - 24, t->desc);
447 }
448#endif /* !NANO_SMALL */
449
450 /* If all went well, we didn't overwrite the allocated space for
451 help_text. */
452 assert(strlen(help_text) < allocsize);
453}
454#endif
455
456/* Create a new filestruct node. Note that we specifically do not set
457 * prevnode->next equal to the new line. */
458filestruct *make_new_node(filestruct *prevnode)
459{
460 filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
461
462 newnode->data = NULL;
463 newnode->prev = prevnode;
464 newnode->next = NULL;
465 newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1;
466
467 return newnode;
468}
469
David Lawrence Ramseyc5967552002-06-21 03:20:06 +0000470/* Make a copy of a node to a pointer (space will be malloc()ed). */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000471filestruct *copy_node(const filestruct *src)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000472{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000473 filestruct *dst = (filestruct *)nmalloc(sizeof(filestruct));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000474
Chris Allegretta6df90f52002-07-19 01:08:59 +0000475 assert(src != NULL);
476
Chris Allegretta88b09152001-05-17 11:35:43 +0000477 dst->data = charalloc(strlen(src->data) + 1);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000478 dst->next = src->next;
479 dst->prev = src->prev;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000480 strcpy(dst->data, src->data);
481 dst->lineno = src->lineno;
482
483 return dst;
484}
485
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000486/* Splice a node into an existing filestruct. */
487void splice_node(filestruct *begin, filestruct *newnode, filestruct *end)
488{
489 if (newnode != NULL) {
490 newnode->next = end;
491 newnode->prev = begin;
492 }
493 if (begin != NULL)
494 begin->next = newnode;
495 if (end != NULL)
496 end->prev = newnode;
497}
498
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000499/* Unlink a node from the rest of the filestruct. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000500void unlink_node(const filestruct *fileptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000501{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000502 assert(fileptr != NULL);
503
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000504 if (fileptr->prev != NULL)
505 fileptr->prev->next = fileptr->next;
506
507 if (fileptr->next != NULL)
508 fileptr->next->prev = fileptr->prev;
509}
510
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000511/* Delete a node from the filestruct. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000512void delete_node(filestruct *fileptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000513{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000514 if (fileptr != NULL) {
515 if (fileptr->data != NULL)
516 free(fileptr->data);
517 free(fileptr);
518 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000519}
520
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000521/* Okay, now let's duplicate a whole struct! */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000522filestruct *copy_filestruct(const filestruct *src)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000523{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000524 filestruct *head; /* copy of src, top of the copied list */
525 filestruct *prev; /* temp that traverses the list */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000526
Chris Allegretta6df90f52002-07-19 01:08:59 +0000527 assert(src != NULL);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000528
Chris Allegretta6df90f52002-07-19 01:08:59 +0000529 prev = copy_node(src);
530 prev->prev = NULL;
531 head = prev;
532 src = src->next;
533 while (src != NULL) {
534 prev->next = copy_node(src);
535 prev->next->prev = prev;
536 prev = prev->next;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000537
Chris Allegretta6df90f52002-07-19 01:08:59 +0000538 src = src->next;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000539 }
540
Chris Allegretta6df90f52002-07-19 01:08:59 +0000541 prev->next = NULL;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000542 return head;
543}
544
Chris Allegrettaf2387fb2002-04-10 02:31:20 +0000545/* Frees a filestruct. */
Chris Allegretta6df90f52002-07-19 01:08:59 +0000546void free_filestruct(filestruct *src)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000547{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000548 if (src != NULL) {
549 while (src->next != NULL) {
550 src = src->next;
551 delete_node(src->prev);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000552#ifdef DEBUG
Chris Allegretta6df90f52002-07-19 01:08:59 +0000553 fprintf(stderr, _("delete_node(): free'd a node, YAY!\n"));
554#endif
555 }
556 delete_node(src);
557#ifdef DEBUG
558 fprintf(stderr, _("delete_node(): free'd last node.\n"));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000559#endif
560 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000561}
562
Chris Allegretta6df90f52002-07-19 01:08:59 +0000563void renumber_all(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000564{
565 filestruct *temp;
Chris Allegrettabef12972002-03-06 03:30:40 +0000566 int i = 1;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000567
Chris Allegretta6df90f52002-07-19 01:08:59 +0000568 assert(fileage == NULL || fileage != fileage->next);
569 for (temp = fileage; temp != NULL; temp = temp->next)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000570 temp->lineno = i++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000571}
572
Chris Allegretta6df90f52002-07-19 01:08:59 +0000573void renumber(filestruct *fileptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000574{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000575 if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000576 renumber_all();
Chris Allegretta6df90f52002-07-19 01:08:59 +0000577 else {
578 int lineno = fileptr->prev->lineno;
579
580 assert(fileptr != fileptr->next);
581 for (; fileptr != NULL; fileptr = fileptr->next)
582 fileptr->lineno = ++lineno;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000583 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000584}
585
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000586/* Print one usage string to the screen, removes lots of duplicate
Chris Allegretta6df90f52002-07-19 01:08:59 +0000587 * strings to translate and takes out the parts that shouldn't be
588 * translatable (the flag names). */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000589void print1opt(const char *shortflag, const char *longflag,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000590 const char *desc)
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000591{
592 printf(" %s\t", shortflag);
593 if (strlen(shortflag) < 8)
594 printf("\t");
595
596#ifdef HAVE_GETOPT_LONG
597 printf("%s\t", longflag);
598 if (strlen(longflag) < 8)
599 printf("\t\t");
600 else if (strlen(longflag) < 16)
601 printf("\t");
602#endif
603
604 printf("%s\n", desc);
605}
606
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000607void usage(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000608{
609#ifdef HAVE_GETOPT_LONG
Chris Allegretta6df90f52002-07-19 01:08:59 +0000610 printf(_("Usage: nano [+LINE] [GNU long option] [option] [file]\n\n"));
611 printf(_("Option\t\tLong option\t\tMeaning\n"));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000612#else
Chris Allegretta6df90f52002-07-19 01:08:59 +0000613 printf(_("Usage: nano [+LINE] [option] [file]\n\n"));
614 printf(_("Option\t\tMeaning\n"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000615#endif /* HAVE_GETOPT_LONG */
616
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000617 print1opt("-h, -?", "--help", _("Show this message"));
Chris Allegretta6df90f52002-07-19 01:08:59 +0000618 print1opt(_("+LINE"), "", _("Start at line number LINE"));
Chris Allegretta7004c282001-09-22 00:42:10 +0000619#ifndef NANO_SMALL
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000620 print1opt("-B", "--backup", _("Backup existing files on save"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000621 print1opt("-D", "--dos", _("Write file in DOS format"));
Chris Allegretta7004c282001-09-22 00:42:10 +0000622#endif
Chris Allegretta355fbe52001-07-14 19:32:47 +0000623#ifdef ENABLE_MULTIBUFFER
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000624 print1opt("-F", "--multibuffer", _("Enable multiple file buffers"));
Chris Allegretta355fbe52001-07-14 19:32:47 +0000625#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +0000626#ifdef ENABLE_NANORC
627 print1opt("-I", "--ignorercfiles", _("Don't look at nanorc files"));
628#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000629 print1opt("-K", "--keypad", _("Use alternate keypad routines"));
Chris Allegretta8fa1e282001-09-22 04:20:25 +0000630#ifndef NANO_SMALL
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000631 print1opt("-M", "--mac", _("Write file in Mac format"));
632 print1opt("-N", "--noconvert", _("Don't convert files from DOS/Mac format"));
Chris Allegretta8fa1e282001-09-22 04:20:25 +0000633#endif
Chris Allegrettae4f940d2002-03-03 22:36:36 +0000634#ifndef DISABLE_JUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +0000635 print1opt(_("-Q [str]"), _("--quotestr=[str]"), _("Quoting string, default \"> \""));
Chris Allegrettae4f940d2002-03-03 22:36:36 +0000636#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +0000637#ifdef HAVE_REGEX_H
638 print1opt("-R", "--regexp", _("Do regular expression searches"));
639#endif
Chris Allegretta3e3ae942001-09-22 19:02:04 +0000640#ifndef NANO_SMALL
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000641 print1opt("-S", "--smooth", _("Smooth scrolling"));
Chris Allegretta3e3ae942001-09-22 19:02:04 +0000642#endif
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000643 print1opt(_("-T [num]"), _("--tabsize=[num]"), _("Set width of a tab to num"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000644 print1opt("-V", "--version", _("Print version information and exit"));
Chris Allegretta09900ff2002-05-04 04:23:30 +0000645#ifdef ENABLE_COLOR
646 print1opt(_("-Y [str]"), _("--syntax [str]"), _("Syntax definition to use"));
647#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000648 print1opt("-c", "--const", _("Constantly show cursor position"));
Chris Allegrettad19e9912000-07-12 18:14:51 +0000649#ifndef NANO_SMALL
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000650 print1opt("-i", "--autoindent", _("Automatically indent new lines"));
651 print1opt("-k", "--cut", _("Let ^K cut from cursor to end of line"));
Chris Allegrettad19e9912000-07-12 18:14:51 +0000652#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000653 print1opt("-l", "--nofollow", _("Don't follow symbolic links, overwrite"));
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000654#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000655 print1opt("-m", "--mouse", _("Enable mouse"));
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000656#endif
Chris Allegrettae1f14522001-09-19 03:19:43 +0000657#ifndef DISABLE_OPERATINGDIR
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000658 print1opt(_("-o [dir]"), _("--operatingdir=[dir]"), _("Set operating directory"));
Chris Allegrettae1f14522001-09-19 03:19:43 +0000659#endif
Chris Allegretta6cd143d2003-01-05 23:35:44 +0000660 print1opt(_("-p"), _("--preserve"), _("Preserve XON (^Q) and XOFF (^S) keys"));
Chris Allegretta6fe61492001-05-21 12:56:25 +0000661#ifndef DISABLE_WRAPJUSTIFY
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000662 print1opt(_("-r [#cols]"), _("--fill=[#cols]"), _("Set fill cols to (wrap lines at) #cols"));
Chris Allegretta6fe61492001-05-21 12:56:25 +0000663#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +0000664#ifndef DISABLE_SPELLER
Chris Allegrettad1ec7b02002-03-09 20:05:26 +0000665 print1opt(_("-s [prog]"), _("--speller=[prog]"), _("Enable alternate speller"));
Rocco Corsiaf5c3022001-01-12 07:51:05 +0000666#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000667 print1opt("-t", "--tempfile", _("Auto save on exit, don't prompt"));
668 print1opt("-v", "--view", _("View (read only) mode"));
Chris Allegrettacef7fbb2001-04-02 05:36:08 +0000669#ifndef DISABLE_WRAPPING
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000670 print1opt("-w", "--nowrap", _("Don't wrap long lines"));
Chris Allegrettacef7fbb2001-04-02 05:36:08 +0000671#endif
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000672 print1opt("-x", "--nohelp", _("Don't show help window"));
673 print1opt("-z", "--suspend", _("Enable suspend"));
Chris Allegretta6df90f52002-07-19 01:08:59 +0000674
675 /* this is a special case */
676 printf(" %s\t\t\t%s\n","-a, -b, -e, -f, -g, -j", _("(ignored, for Pico compatibility)"));
Chris Allegretta3fc5d572002-03-09 18:51:58 +0000677
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000678 exit(0);
679}
680
David Lawrence Ramsey0341b582002-08-21 16:10:37 +0000681void version(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000682{
Chris Allegrettac46dd812001-02-14 14:28:27 +0000683 printf(_(" GNU nano version %s (compiled %s, %s)\n"),
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000684 VERSION, __TIME__, __DATE__);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000685 printf(_
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +0000686 (" Email: nano@nano-editor.org Web: http://www.nano-editor.org/"));
Chris Allegretta8a0de3b2000-11-24 20:45:14 +0000687 printf(_("\n Compiled options:"));
Chris Allegrettaff269f82000-12-01 18:46:01 +0000688
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000689#ifdef DEBUG
690 printf(" --enable-debug");
691#endif
Chris Allegretta8a0de3b2000-11-24 20:45:14 +0000692#ifdef NANO_EXTRA
693 printf(" --enable-extra");
Jordi Mallach5285ca92002-01-17 12:40:33 +0000694#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +0000695#ifdef NANO_SMALL
696 printf(" --enable-tiny");
697#else
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000698#ifdef DISABLE_BROWSER
Chris Allegretta6636dc32001-01-05 05:41:07 +0000699 printf(" --disable-browser");
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000700#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000701#ifdef DISABLE_HELP
702 printf(" --disable-help");
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000703#endif
704#ifdef DISABLE_JUSTIFY
Chris Allegrettaff269f82000-12-01 18:46:01 +0000705 printf(" --disable-justify");
Chris Allegretta6b58acd2001-04-12 03:01:53 +0000706#endif
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000707#if defined(DISABLE_MOUSE) || !defined(NCURSES_MOUSE_VERSION)
Chris Allegretta84de5522001-04-12 14:51:48 +0000708 printf(" --disable-mouse");
Chris Allegrettab7d00ef2000-12-18 05:36:51 +0000709#endif
Chris Allegretta2a15c582002-10-25 01:51:13 +0000710#ifndef ENABLE_NLS
711 printf(" --disable-nls");
712#endif
Chris Allegrettae1f14522001-09-19 03:19:43 +0000713#ifdef DISABLE_OPERATINGDIR
714 printf(" --disable-operatingdir");
715#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000716#ifdef DISABLE_SPELLER
717 printf(" --disable-speller");
718#endif
719#ifdef DISABLE_TABCOMP
720 printf(" --disable-tabcomp");
721#endif
Chris Allegretta84de5522001-04-12 14:51:48 +0000722#endif /* NANO_SMALL */
Chris Allegrettacef7fbb2001-04-02 05:36:08 +0000723#ifdef DISABLE_WRAPPING
724 printf(" --disable-wrapping");
725#endif
David Lawrence Ramseydc60b722002-10-25 16:08:53 +0000726#ifdef DISABLE_ROOTWRAP
727 printf(" --disable-wrapping-as-root");
728#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000729#ifdef ENABLE_COLOR
730 printf(" --enable-color");
731#endif
732#ifdef ENABLE_MULTIBUFFER
733 printf(" --enable-multibuffer");
734#endif
735#ifdef ENABLE_NANORC
736 printf(" --enable-nanorc");
737#endif
Chris Allegretta8a0de3b2000-11-24 20:45:14 +0000738#ifdef USE_SLANG
739 printf(" --with-slang");
740#endif
741 printf("\n");
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000742}
743
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000744/* Stuff we do when we abort from programs and want to clean up the
745 * screen. This doesn't do much right now. */
746void do_early_abort(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000747{
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000748 blank_statusbar_refresh();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000749}
750
751int no_help(void)
752{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000753 return ISSET(NO_HELP) ? 2 : 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000754}
755
Chris Allegrettad865da12002-07-29 23:46:38 +0000756#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP) || defined(NANO_SMALL)
Chris Allegrettaff269f82000-12-01 18:46:01 +0000757void nano_disabled_msg(void)
758{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000759 statusbar(_("Sorry, support for this function has been disabled"));
Chris Allegrettaff269f82000-12-01 18:46:01 +0000760}
Chris Allegretta4eb7aa02000-12-01 18:57:11 +0000761#endif
Chris Allegrettaff269f82000-12-01 18:46:01 +0000762
Chris Allegretta6cd143d2003-01-05 23:35:44 +0000763void do_preserve_msg(void)
764{
765 fprintf(stderr, _("\nThe -p flag now invokes the Pico \"preserve\" flag. The Pico compatibility\n"));
766 fprintf(stderr, _("flag been removed as nano is now fully Pico compatible. Please see the nano\n"));
767 fprintf(stderr, _("FAQ for more info on this change...\n\n"));
768 fprintf(stderr, _("Press return to continue\n"));
769 while (getchar() != '\n');
770}
771
772
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000773#ifndef NANO_SMALL
774static int pid; /* This is the PID of the newly forked process
775 * below. It must be global since the signal
776 * handler needs it. */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000777RETSIGTYPE cancel_fork(int signal)
778{
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000779 if (kill(pid, SIGKILL) == -1)
780 nperror("kill");
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000781}
782
783int open_pipe(const char *command)
784{
785 int fd[2];
786 FILE *f;
787 struct sigaction oldaction, newaction;
788 /* original and temporary handlers for SIGINT */
789#ifdef _POSIX_VDISABLE
790 struct termios term, newterm;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000791#endif /* _POSIX_VDISABLE */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000792 int cancel_sigs = 0;
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +0000793 /* cancel_sigs == 1 means that sigaction() failed without changing
794 * the signal handlers. cancel_sigs == 2 means the signal handler
795 * was changed, but the tcsetattr didn't succeed.
796 *
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000797 * I use this variable since it is important to put things back when
798 * we finish, even if we get errors. */
799
800 /* Make our pipes. */
801
802 if (pipe(fd) == -1) {
803 statusbar(_("Could not pipe"));
804 return 1;
805 }
806
807 /* Fork a child. */
808
809 if ((pid = fork()) == 0) {
810 close(fd[0]);
811 dup2(fd[1], fileno(stdout));
812 dup2(fd[1], fileno(stderr));
813 /* If execl() returns at all, there was an error. */
814
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +0000815 execl("/bin/sh", "sh", "-c", command, 0);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000816 exit(0);
817 }
818
819 /* Else continue as parent. */
820
821 close(fd[1]);
822
823 if (pid == -1) {
824 close(fd[0]);
825 statusbar(_("Could not fork"));
826 return 1;
827 }
828
829 /* Before we start reading the forked command's output, we set
830 * things up so that ^C will cancel the new process. */
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +0000831 if (sigaction(SIGINT, NULL, &newaction) == -1) {
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000832 cancel_sigs = 1;
833 nperror("sigaction");
834 } else {
835 newaction.sa_handler = cancel_fork;
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +0000836 if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000837 cancel_sigs = 1;
838 nperror("sigaction");
839 }
840 }
841 /* Note that now oldaction is the previous SIGINT signal handler,
842 * to be restored later. */
843
844 /* See if the platform supports disabling individual control
845 * characters. */
846#ifdef _POSIX_VDISABLE
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000847 if (cancel_sigs == 0 && tcgetattr(0, &term) == -1) {
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000848 cancel_sigs = 2;
849 nperror("tcgetattr");
850 }
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000851 if (cancel_sigs == 0) {
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000852 newterm = term;
853 /* Grab oldterm's VINTR key :-) */
854 newterm.c_cc[VINTR] = oldterm.c_cc[VINTR];
855 if (tcsetattr(0, TCSANOW, &newterm) == -1) {
856 cancel_sigs = 2;
857 nperror("tcsetattr");
858 }
859 }
860#endif /* _POSIX_VDISABLE */
861
862 f = fdopen(fd[0], "rb");
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000863 if (f == NULL)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000864 nperror("fdopen");
865
866 read_file(f, "stdin", 0);
867 /* if multibuffer mode is on, we could be here in view mode; if so,
868 don't set the modification flag */
869 if (!ISSET(VIEW_MODE))
870 set_modified();
871
872 if (wait(NULL) == -1)
873 nperror("wait");
874
875#ifdef _POSIX_VDISABLE
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000876 if (cancel_sigs == 0 && tcsetattr(0, TCSANOW, &term) == -1)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000877 nperror("tcsetattr");
878#endif /* _POSIX_VDISABLE */
879
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +0000880 if (cancel_sigs != 1 && sigaction(SIGINT, &oldaction, NULL) == -1)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000881 nperror("sigaction");
882
883 return 0;
884}
885#endif /* NANO_SMALL */
886
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +0000887#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000888void do_mouse(void)
889{
890 MEVENT mevent;
891 int currslen;
892 const shortcut *s = currshortcut;
893
894 if (getmouse(&mevent) == ERR)
895 return;
896
897 /* If mouse not in edit or bottom window, return */
898 if (wenclose(edit, mevent.y, mevent.x)) {
899
900 /* Don't let people screw with the marker when they're in a
901 * subfunction. */
902 if (currshortcut != main_list)
903 return;
904
905 /* Subtract out size of topwin. Perhaps we need a constant
906 * somewhere? */
907 mevent.y -= 2;
908
909 /* Selecting where the cursor is sets the mark. Selecting
910 * beyond the line length with the cursor at the end of the line
911 * sets the mark as well. */
912 if ((mevent.y == current_y) &&
913 ((mevent.x == current_x) || (current_x == strlen(current->data)
914 && (mevent.x >
915 strlen(current->data))))) {
916 if (ISSET(VIEW_MODE)) {
917 print_view_warning();
918 return;
919 }
920 do_mark();
921 } else if (mevent.y > current_y) {
922 while (mevent.y > current_y) {
923 if (current->next != NULL)
924 current = current->next;
925 else
926 break;
927 current_y++;
928 }
929 } else if (mevent.y < current_y) {
930 while (mevent.y < current_y) {
931 if (current->prev != NULL)
932 current = current->prev;
933 else
934 break;
935 current_y--;
936 }
937 }
938 current_x = actual_x(current, mevent.x);
939 placewewant = current_x;
940 update_cursor();
941 edit_refresh();
942 } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
943 int i, k;
944
945 if (currshortcut == main_list)
946 currslen = MAIN_VISIBLE;
947 else
948 currslen = length_of_list(currshortcut);
949
950 if (currslen < 2)
951 k = COLS / 6;
952 else
953 k = COLS / ((currslen + (currslen %2)) / 2);
954
955 /* Determine what shortcut list was clicked */
956 mevent.y -= (editwinrows + 3);
957
958 if (mevent.y < 0) /* They clicked on the statusbar */
959 return;
960
961 /* Don't select stuff beyond list length */
962 if (mevent.x / k >= currslen)
963 return;
964
965 for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
966 s = s->next;
967
968 /* And ungetch that value */
969 ungetch(s->val);
970
971 /* And if it's an alt-key sequence, we should probably send alt
972 too ;-) */
973 if (s->val >= 'a' && s->val <= 'z')
974 ungetch(27);
975 }
976}
977#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +0000978
Chris Allegretta6df90f52002-07-19 01:08:59 +0000979/* The user typed a printable character; add it to the edit buffer. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000980void do_char(char ch)
981{
Chris Allegretta6df90f52002-07-19 01:08:59 +0000982 size_t current_len = strlen(current->data);
983#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
984 int refresh = 0;
985 /* Do we have to run edit_refresh(), or can we get away with
986 * update_line()? */
987#endif
988
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000989 /* magic-line: when a character is inserted on the current magic line,
990 * it means we need a new one! */
Chris Allegrettabd9e7c32000-10-26 01:44:42 +0000991 if (filebot == current && current->data[0] == '\0') {
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000992 new_magicline();
Chris Allegretta28a0f892000-07-05 22:47:54 +0000993 fix_editbot();
Robert Siemborski63b3d7e2000-07-04 22:15:39 +0000994 }
995
Chris Allegretta6df90f52002-07-19 01:08:59 +0000996 /* more dangerousness fun =) */
997 current->data = nrealloc(current->data, current_len + 2);
998 assert(current_x <= current_len);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +0000999 memmove(&current->data[current_x + 1],
1000 &current->data[current_x],
Chris Allegretta6df90f52002-07-19 01:08:59 +00001001 current_len - current_x + 1);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001002 current->data[current_x] = ch;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001003 totsize++;
1004 set_modified();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001005
Chris Allegretta6df90f52002-07-19 01:08:59 +00001006#ifndef NANO_SMALL
1007 /* note that current_x has not yet been incremented */
1008 if (current == mark_beginbuf && current_x < mark_beginx)
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001009 mark_beginx++;
Chris Allegrettab2cd10d2002-01-20 00:54:42 +00001010#endif
1011
Chris Allegretta6df90f52002-07-19 01:08:59 +00001012 do_right();
1013
Chris Allegrettacef7fbb2001-04-02 05:36:08 +00001014#ifndef DISABLE_WRAPPING
Chris Allegrettadffa2072002-07-24 01:02:26 +00001015 if (!ISSET(NO_WRAP) && ch != '\t')
Chris Allegretta6df90f52002-07-19 01:08:59 +00001016 refresh = do_wrap(current);
Chris Allegrettacef7fbb2001-04-02 05:36:08 +00001017#endif
1018
Chris Allegretta6df90f52002-07-19 01:08:59 +00001019#ifdef ENABLE_COLOR
1020 refresh = 1;
1021#endif
1022
1023#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
1024 if (refresh)
1025 edit_refresh();
1026#endif
1027
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001028 check_statblank();
1029 UNSET(KEEP_CUTBUFFER);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001030}
1031
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001032int do_backspace(void)
1033{
1034 int refresh = 0;
1035 if (current_x > 0) {
1036 assert(current_x <= strlen(current->data));
1037 /* Let's get dangerous */
1038 memmove(&current->data[current_x - 1], &current->data[current_x],
1039 strlen(current->data) - current_x + 1);
1040#ifdef DEBUG
1041 fprintf(stderr, _("current->data now = \"%s\"\n"), current->data);
1042#endif
1043 align(&current->data);
1044#ifndef NANO_SMALL
1045 if (current_x <= mark_beginx && mark_beginbuf == current)
1046 mark_beginx--;
1047#endif
1048 do_left();
1049#ifdef ENABLE_COLOR
1050 refresh = 1;
1051#endif
1052 } else {
1053 filestruct *previous;
1054 const filestruct *tmp;
1055
1056 if (current == fileage)
1057 return 0; /* Can't delete past top of file */
1058
1059 previous = current->prev;
1060 current_x = strlen(previous->data);
1061 placewewant = strlenpt(previous->data);
1062#ifndef NANO_SMALL
1063 if (current == mark_beginbuf) {
1064 mark_beginx += current_x;
1065 mark_beginbuf = previous;
1066 }
1067#endif
1068 previous->data = nrealloc(previous->data,
1069 current_x + strlen(current->data) + 1);
1070 strcpy(previous->data + current_x, current->data);
1071
1072 unlink_node(current);
1073 delete_node(current);
1074 tmp = current;
1075 current = (previous->next ? previous->next : previous);
1076 renumber(current);
1077 /* We had to renumber before doing update_line. */
1078 if (tmp == edittop)
1079 page_up();
1080
1081 /* Ooops, sanity check */
1082 if (tmp == filebot) {
1083 filebot = current;
1084 editbot = current;
1085
1086 /* Recreate the magic line if we're deleting it AND if the
1087 line we're on now is NOT blank. if it is blank we
1088 can just use IT for the magic line. This is how Pico
1089 appears to do it, in any case. */
1090 if (current->data[0] != '\0') {
1091 new_magicline();
1092 fix_editbot();
1093 }
1094 }
1095
1096 current = previous;
1097 if (current_y > 0)
1098 current_y--;
1099 totlines--;
1100#ifdef DEBUG
1101 fprintf(stderr, _("After, data = \"%s\"\n"), current->data);
1102#endif
1103 UNSET(KEEP_CUTBUFFER);
1104 refresh = 1;
1105 }
1106
1107 totsize--;
1108 set_modified();
1109 if (refresh)
1110 edit_refresh();
1111 return 1;
1112}
1113
1114int do_delete(void)
1115{
1116 int refresh = 0;
1117
1118 /* blbf -> blank line before filebot (see below) */
1119 int blbf = 0;
1120
1121 if (current->next == filebot && current->data[0] == '\0')
1122 blbf = 1;
1123
1124 placewewant = xplustabs();
1125
1126 if (current_x != strlen(current->data)) {
1127 /* Let's get dangerous */
1128 memmove(&current->data[current_x], &current->data[current_x + 1],
1129 strlen(current->data) - current_x);
1130
1131 align(&current->data);
1132#ifdef ENABLE_COLOR
1133 refresh = 1;
1134#endif
1135 } else if (current->next != NULL && (current->next != filebot || blbf)) {
1136 /* We can delete the line before filebot only if it is blank: it
1137 becomes the new magic line then. */
1138
1139 filestruct *foo;
1140
1141 current->data = nrealloc(current->data,
1142 strlen(current->data) +
1143 strlen(current->next->data) + 1);
1144 strcat(current->data, current->next->data);
1145
1146 foo = current->next;
1147 if (filebot == foo) {
1148 filebot = current;
1149 editbot = current;
1150 }
1151
1152 unlink_node(foo);
1153 delete_node(foo);
1154 renumber(current);
1155 totlines--;
1156 refresh = 1;
1157 } else
1158 return 0;
1159
1160 totsize--;
1161 set_modified();
1162 UNSET(KEEP_CUTBUFFER);
1163 update_line(current, current_x);
1164 if (refresh)
1165 edit_refresh();
1166 return 1;
1167}
1168
1169int do_tab(void)
1170{
1171 do_char('\t');
1172 return 1;
1173}
1174
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001175/* Someone hits return *gasp!* */
Chris Allegretta6df90f52002-07-19 01:08:59 +00001176int do_enter(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001177{
Chris Allegrettae3167732001-03-18 16:59:34 +00001178 filestruct *newnode;
Chris Allegretta68532c32001-09-17 13:49:33 +00001179 char *tmp;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001180
Chris Allegretta6df90f52002-07-19 01:08:59 +00001181 newnode = make_new_node(current);
1182 assert(current != NULL && current->data != NULL);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001183 tmp = &current->data[current_x];
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001184
Chris Allegrettaff989832001-09-17 13:48:00 +00001185#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001186 /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001187 if (ISSET(AUTOINDENT)) {
Chris Allegrettadab017e2002-04-23 10:56:06 +00001188 int extra = 0;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001189 const char *spc = current->data;
1190
1191 while (*spc == ' ' || *spc == '\t') {
Chris Allegrettadab017e2002-04-23 10:56:06 +00001192 extra++;
1193 spc++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001194 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001195 /* If current_x < extra, then we are breaking the line in the
1196 * indentation. Autoindenting should add only current_x
1197 * characters of indentation. */
Chris Allegrettadab017e2002-04-23 10:56:06 +00001198 if (current_x < extra)
1199 extra = current_x;
1200 else
1201 current_x = extra;
1202 totsize += extra;
1203
1204 newnode->data = charalloc(strlen(tmp) + extra + 1);
1205 strncpy(newnode->data, current->data, extra);
1206 strcpy(&newnode->data[extra], tmp);
Chris Allegrettaff989832001-09-17 13:48:00 +00001207 } else
1208#endif
1209 {
Chris Allegrettadab017e2002-04-23 10:56:06 +00001210 current_x = 0;
Chris Allegretta88b09152001-05-17 11:35:43 +00001211 newnode->data = charalloc(strlen(tmp) + 1);
Chris Allegrettae3167732001-03-18 16:59:34 +00001212 strcpy(newnode->data, tmp);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001213 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001214 *tmp = '\0';
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001215
Chris Allegretta6df90f52002-07-19 01:08:59 +00001216 if (current->next == NULL) {
Chris Allegrettae3167732001-03-18 16:59:34 +00001217 filebot = newnode;
1218 editbot = newnode;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001219 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001220 splice_node(current, newnode, current->next);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001221
1222 totsize++;
1223 renumber(current);
Chris Allegrettae3167732001-03-18 16:59:34 +00001224 current = newnode;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001225 align(&current->data);
1226
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001227 /* The logic here is as follows:
1228 * -> If we are at the bottom of the buffer, we want to recenter
Chris Allegretta88520c92001-05-05 17:45:54 +00001229 * (read: rebuild) the screen and forcibly move the cursor.
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001230 * -> otherwise, we want simply to redraw the screen and update
1231 * where we think the cursor is.
1232 */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001233 if (current_y == editwinrows - 1) {
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001234#ifndef NANO_SMALL
1235 if (ISSET(SMOOTHSCROLL))
1236 edit_update(current, NONE);
1237 else
1238#endif
1239 edit_update(current, CENTER);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00001240 reset_cursor();
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001241 } else {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001242 current_y++;
Robert Siemborskidd53ec22000-07-04 02:35:19 +00001243 edit_refresh();
1244 update_cursor();
1245 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001246
1247 totlines++;
1248 set_modified();
1249
Chris Allegrettab0ae3932000-06-15 23:39:14 +00001250 placewewant = xplustabs();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001251 return 1;
1252}
1253
Chris Allegrettaad3f4782001-10-02 03:54:13 +00001254#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001255int do_next_word(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001256{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001257 filestruct *old = current;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001258
Chris Allegretta6df90f52002-07-19 01:08:59 +00001259 assert(current != NULL && current->data != NULL);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001260
Chris Allegretta6df90f52002-07-19 01:08:59 +00001261 /* Skip letters in this word first. */
1262 while (current->data[current_x] != '\0' &&
1263 isalnum((int)current->data[current_x]))
1264 current_x++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001265
Chris Allegretta6df90f52002-07-19 01:08:59 +00001266 for (; current != NULL; current = current->next) {
1267 while (current->data[current_x] != '\0' &&
1268 !isalnum((int)current->data[current_x]))
1269 current_x++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001270
Chris Allegretta6df90f52002-07-19 01:08:59 +00001271 if (current->data[current_x] != '\0')
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001272 break;
1273
Chris Allegretta6df90f52002-07-19 01:08:59 +00001274 current_x = 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001275 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00001276 if (current == NULL)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001277 current = filebot;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001278
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001279 placewewant = xplustabs();
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001280
Chris Allegrettad865da12002-07-29 23:46:38 +00001281 if (current->lineno >= editbot->lineno) {
1282 /* If we're on the last line, don't center the screen. */
1283 if (current->lineno == filebot->lineno)
1284 edit_refresh();
1285 else
1286 edit_update(current, CENTER);
1287 }
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001288 else {
Chris Allegretta6df90f52002-07-19 01:08:59 +00001289 /* If we've jumped lines, refresh the old line. We can't just
1290 use current->prev here, because we may have skipped over some
1291 blank lines, in which case the previous line is the wrong
1292 one. */
1293 if (current != old) {
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001294 update_line(old, 0);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001295 /* If the mark was set, then the lines between old and
1296 current have to be updated too. */
1297 if (ISSET(MARK_ISSET)) {
1298 while (old->next != current) {
1299 old = old->next;
1300 update_line(old, 0);
1301 }
1302 }
1303 }
Chris Allegretta9e2934f2000-12-01 23:49:48 +00001304 update_line(current, current_x);
1305 }
Chris Allegretta6232d662002-05-12 19:52:15 +00001306 return 0;
1307}
1308
Chris Allegretta6df90f52002-07-19 01:08:59 +00001309/* The same thing for backwards. */
1310int do_prev_word(void)
Chris Allegretta76e291b2001-10-14 19:05:10 +00001311{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001312 filestruct *old = current;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001313
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001314 assert(current != NULL && current->data != NULL);
Chris Allegretta76e291b2001-10-14 19:05:10 +00001315
Chris Allegretta6df90f52002-07-19 01:08:59 +00001316 /* Skip letters in this word first. */
1317 while (current_x >= 0 && isalnum((int)current->data[current_x]))
1318 current_x--;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001319
Chris Allegretta6df90f52002-07-19 01:08:59 +00001320 for (; current != NULL; current = current->prev) {
1321 while (current_x >= 0 && !isalnum((int)current->data[current_x]))
1322 current_x--;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001323
Chris Allegretta6df90f52002-07-19 01:08:59 +00001324 if (current_x >= 0)
1325 break;
Chris Allegretta76e291b2001-10-14 19:05:10 +00001326
Chris Allegretta6df90f52002-07-19 01:08:59 +00001327 if (current->prev != NULL)
1328 current_x = strlen(current->prev->data);
Chris Allegretta76e291b2001-10-14 19:05:10 +00001329 }
Chris Allegretta76e291b2001-10-14 19:05:10 +00001330
Chris Allegretta6df90f52002-07-19 01:08:59 +00001331 if (current != NULL) {
1332 while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
1333 current_x--;
1334 } else {
1335 current = fileage;
1336 current_x = 0;
1337 }
1338
Chris Allegretta76e291b2001-10-14 19:05:10 +00001339 placewewant = xplustabs();
1340
Chris Allegrettad865da12002-07-29 23:46:38 +00001341 if (current->lineno <= edittop->lineno) {
1342 /* If we're on the first line, don't center the screen. */
1343 if (current->lineno == fileage->lineno)
1344 edit_refresh();
1345 else
1346 edit_update(current, CENTER);
1347 }
Chris Allegretta76e291b2001-10-14 19:05:10 +00001348 else {
Chris Allegretta6df90f52002-07-19 01:08:59 +00001349 /* If we've jumped lines, refresh the old line. We can't just
1350 use current->prev here, because we may have skipped over some
1351 blank lines, in which case the previous line is the wrong
1352 one. */
1353 if (current != old) {
Chris Allegretta76e291b2001-10-14 19:05:10 +00001354 update_line(old, 0);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001355 /* If the mark was set, then the lines between old and
1356 current have to be updated too. */
1357 if (ISSET(MARK_ISSET)) {
1358 while (old->prev != current) {
1359 old = old->prev;
1360 update_line(old, 0);
1361 }
1362 }
1363 }
Chris Allegretta76e291b2001-10-14 19:05:10 +00001364 update_line(current, current_x);
1365 }
Chris Allegretta6232d662002-05-12 19:52:15 +00001366 return 0;
1367}
Chris Allegretta6df90f52002-07-19 01:08:59 +00001368#endif /* !NANO_SMALL */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001369
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001370int do_mark(void)
1371{
1372#ifdef NANO_SMALL
1373 nano_disabled_msg();
1374#else
1375 if (!ISSET(MARK_ISSET)) {
1376 statusbar(_("Mark Set"));
1377 SET(MARK_ISSET);
1378 mark_beginbuf = current;
1379 mark_beginx = current_x;
1380 } else {
1381 statusbar(_("Mark UNset"));
1382 UNSET(MARK_ISSET);
1383 edit_refresh();
1384 }
1385#endif
1386 return 1;
1387}
1388
1389void wrap_reset(void)
1390{
1391 UNSET(SAMELINEWRAP);
1392}
1393
Chris Allegrettacef7fbb2001-04-02 05:36:08 +00001394#ifndef DISABLE_WRAPPING
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001395/* We wrap the given line. Precondition: we assume the cursor has been
Chris Allegretta6df90f52002-07-19 01:08:59 +00001396 * moved forward since the last typed character. Return value:
1397 * whether we wrapped. */
1398int do_wrap(filestruct *inptr)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001399{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001400 size_t len = strlen(inptr->data); /* length of the line we wrap */
1401 int i = 0; /* generic loop variable */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001402 int wrap_loc = -1; /* index of inptr->data where we wrap */
1403 int word_back = -1;
1404#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001405 const char *indentation = NULL;
1406 /* indentation to prepend to the new line */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001407 int indent_len = 0; /* strlen(indentation) */
1408#endif
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001409 const char *after_break; /* text after the wrap point */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001410 int after_break_len; /* strlen(after_break) */
1411 int wrapping = 0; /* do we prepend to the next line? */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001412 const char *wrap_line = NULL;
1413 /* the next line, minus indentation */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001414 int wrap_line_len = 0; /* strlen(wrap_line) */
1415 char *newline = NULL; /* the line we create */
1416 int new_line_len = 0; /* eventual length of newline */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001417
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001418/* There are three steps. First, we decide where to wrap. Then, we
1419 * create the new wrap line. Finally, we clean up. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001420
Chris Allegretta67ca2aa2002-09-19 23:19:34 +00001421/* Step 1, finding where to wrap. We are going to add a new-line
1422 * after a white-space character. In this step, we set wrap_loc as the
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001423 * location of this replacement.
1424 *
1425 * Where should we break the line? We need the last "legal wrap point"
1426 * such that the last word before it ended at or before fill. If there
1427 * is no such point, we settle for the first legal wrap point.
1428 *
Chris Allegretta67ca2aa2002-09-19 23:19:34 +00001429 * A "legal wrap point" is a white-space character that is not followed by
1430 * white-space.
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001431 *
1432 * If there is no legal wrap point or we found the last character of the
1433 * line, we should return without wrapping.
1434 *
1435 * Note that the initial indentation does not count as a legal wrap
1436 * point if we are going to auto-indent!
1437 *
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001438 * Note that the code below could be optimised, by not calling strnlenpt()
1439 * so often. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001440
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001441#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001442 if (ISSET(AUTOINDENT))
1443 i = indent_length(inptr->data);
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001444#endif
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001445 wrap_line = inptr->data + i;
1446 for(; i < len; i++, wrap_line++) {
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001447 /* record where the last word ended */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001448 if (*wrap_line != ' ' && *wrap_line != '\t')
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001449 word_back = i;
1450 /* if we have found a "legal wrap point" and the current word
1451 * extends too far, then we stop */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001452 if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001453 break;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001454 /* we record the latest "legal wrap point" */
Chris Allegretta67ca2aa2002-09-19 23:19:34 +00001455 if (word_back != i && wrap_line[1] != ' ' && wrap_line[1] != '\t')
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001456 wrap_loc = i;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001457 }
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001458 if (wrap_loc < 0 || i == len)
Chris Allegretta6df90f52002-07-19 01:08:59 +00001459 return 0;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001460
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001461/* Step 2, making the new wrap line. It will consist of indentation +
1462 * after_break + " " + wrap_line (although indentation and wrap_line are
1463 * conditional on flags and #defines). */
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001464
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001465 /* after_break is the text that will be moved to the next line. */
1466 after_break = inptr->data + wrap_loc + 1;
1467 after_break_len = len - wrap_loc - 1;
1468 assert(after_break_len == strlen(after_break));
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001469
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001470 /* new_line_len will later be increased by the lengths of indentation
1471 * and wrap_line. */
1472 new_line_len = after_break_len;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001473
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001474 /* We prepend the wrapped text to the next line, if the flag is set,
1475 * and there is a next line, and prepending would not make the line
1476 * too long. */
Adam Rogoyski0223d6f2000-06-17 20:36:35 +00001477 if (ISSET(SAMELINEWRAP) && inptr->next) {
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001478 wrap_line = inptr->next->data;
1479 wrap_line_len = strlen(wrap_line);
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001480
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001481 /* +1 for the space between after_break and wrap_line */
1482 if ((new_line_len + 1 + wrap_line_len) <= fill) {
1483 wrapping = 1;
1484 new_line_len += (1 + wrap_line_len);
1485 }
1486 }
Chris Allegretta56214c62001-09-27 02:46:53 +00001487
Chris Allegrettaff989832001-09-17 13:48:00 +00001488#ifndef NANO_SMALL
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001489 if (ISSET(AUTOINDENT)) {
Chris Allegretta81dea022002-09-21 02:19:45 +00001490 /* Indentation comes from the next line if wrapping, else from
1491 * this line. */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001492 indentation = (wrapping ? wrap_line : inptr->data);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001493 indent_len = indent_length(indentation);
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001494 if (wrapping)
Chris Allegretta81dea022002-09-21 02:19:45 +00001495 /* The wrap_line text should not duplicate indentation.
1496 * Note in this case we need not increase new_line_len. */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001497 wrap_line += indent_len;
1498 else
1499 new_line_len += indent_len;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001500 }
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001501#endif
Adam Rogoyski1e9183f2001-03-13 18:36:03 +00001502
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001503 /* Now we allocate the new line and copy into it. */
1504 newline = charalloc(new_line_len + 1); /* +1 for \0 */
1505 *newline = '\0';
1506
1507#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001508 if (ISSET(AUTOINDENT)) {
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001509 strncpy(newline, indentation, indent_len);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001510 newline[indent_len] = '\0';
1511 }
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001512#endif
1513 strcat(newline, after_break);
Chris Allegretta67ca2aa2002-09-19 23:19:34 +00001514 /* We end the old line after wrap_loc. Note this does not eat the
Chris Allegretta81dea022002-09-21 02:19:45 +00001515 * space. */
Chris Allegretta67ca2aa2002-09-19 23:19:34 +00001516 null_at(&inptr->data, wrap_loc + 1);
1517 totsize++;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001518 if (wrapping) {
Chris Allegretta67ca2aa2002-09-19 23:19:34 +00001519 /* In this case, totsize increases by 1 since we add a space
Chris Allegretta81dea022002-09-21 02:19:45 +00001520 * between after_break and wrap_line. If the line already ends
Chris Allegretta43000922002-09-21 15:41:33 +00001521 * in a tab or a space, we don't add a space and decrement
1522 * totsize to account for that. */
David Lawrence Ramsey7c4222c2002-09-26 22:49:56 +00001523 if (!isspace(newline[strlen(newline) - 1]))
Chris Allegretta81dea022002-09-21 02:19:45 +00001524 strcat(newline, " ");
1525 else
1526 totsize--;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001527 strcat(newline, wrap_line);
1528 free(inptr->next->data);
1529 inptr->next->data = newline;
1530 } else {
1531 filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
Chris Allegretta6df90f52002-07-19 01:08:59 +00001532
Chris Allegretta67ca2aa2002-09-19 23:19:34 +00001533 /* In this case, the file size changes by +1 for the new line, and
Chris Allegretta81dea022002-09-21 02:19:45 +00001534 * +indent_len for the new indentation. */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001535#ifndef NANO_SMALL
1536 totsize += indent_len;
1537#endif
1538 totlines++;
1539 temp->data = newline;
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001540 temp->prev = inptr;
1541 temp->next = inptr->next;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001542 temp->prev->next = temp;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001543 /* If temp->next is NULL, then temp is the last line of the
1544 * file, so we must set filebot. */
1545 if (temp->next != NULL)
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001546 temp->next->prev = temp;
1547 else
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001548 filebot = temp;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001549 }
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001550
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001551/* Step 3, clean up. Here we reposition the cursor and mark, and do some
1552 * other sundry things. */
1553
1554 /* later wraps of this line will be prepended to the next line. */
1555 SET(SAMELINEWRAP);
1556
1557 /* Each line knows its line number. We recalculate these if we
1558 * inserted a new line. */
1559 if (!wrapping)
1560 renumber(inptr);
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001561
Chris Allegretta6df90f52002-07-19 01:08:59 +00001562 /* If the cursor was after the break point, we must move it. */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001563 if (current_x > wrap_loc) {
Chris Allegretta6df90f52002-07-19 01:08:59 +00001564 current = current->next;
1565 current_x -=
Chris Allegrettaff989832001-09-17 13:48:00 +00001566#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00001567 -indent_len +
Chris Allegrettaff989832001-09-17 13:48:00 +00001568#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00001569 wrap_loc + 1;
1570 wrap_reset();
1571 placewewant = xplustabs();
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001572 }
1573
Chris Allegretta6df90f52002-07-19 01:08:59 +00001574#ifndef NANO_SMALL
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001575 /* If the mark was on this line after the wrap point, we move it down.
Chris Allegretta6df90f52002-07-19 01:08:59 +00001576 * If it was on the next line and we wrapped, we must move it
1577 * right. */
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001578 if (mark_beginbuf == inptr && mark_beginx > wrap_loc) {
1579 mark_beginbuf = inptr->next;
Chris Allegrettadffa2072002-07-24 01:02:26 +00001580 mark_beginx -= wrap_loc - indent_len + 1;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001581 } else if (wrapping && mark_beginbuf == inptr->next)
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001582 mark_beginx += after_break_len;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001583#endif /* !NANO_SMALL */
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001584
Chris Allegretta7162e3d2002-04-06 05:02:14 +00001585 /* Place the cursor. */
Adam Rogoyski77f36de2000-06-07 03:56:54 +00001586 reset_cursor();
Chris Allegretta6df90f52002-07-19 01:08:59 +00001587
1588 return 1;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001589}
Chris Allegretta6df90f52002-07-19 01:08:59 +00001590#endif /* !DISABLE_WRAPPING */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001591
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001592#ifndef DISABLE_SPELLER
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001593int do_int_spell_fix(const char *word)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001594{
Chris Allegretta6df90f52002-07-19 01:08:59 +00001595 char *save_search;
1596 char *save_replace;
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001597 filestruct *begin;
Chris Allegretta1d8fa1f2002-12-10 00:53:21 +00001598 int i = 0, j = 0, beginx, beginx_top, reverse_search_set, case_sens_set;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001599#ifndef NANO_SMALL
1600 int mark_set;
1601#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001602
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001603 /* save where we are */
1604 begin = current;
1605 beginx = current_x + 1;
1606
Chris Allegretta23b74b22002-01-21 20:32:22 +00001607 /* Make sure Spell Check goes forward only */
1608 reverse_search_set = ISSET(REVERSE_SEARCH);
1609 UNSET(REVERSE_SEARCH);
1610
Chris Allegretta1d8fa1f2002-12-10 00:53:21 +00001611 case_sens_set = ISSET(CASE_SENSITIVE);
1612 SET(CASE_SENSITIVE);
1613
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001614#ifndef NANO_SMALL
1615 /* Make sure the marking highlight is off during Spell Check */
1616 mark_set = ISSET(MARK_ISSET);
1617 UNSET(MARK_ISSET);
1618#endif
1619
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001620 /* save the current search/replace strings */
1621 search_init_globals();
Chris Allegretta6df90f52002-07-19 01:08:59 +00001622 save_search = last_search;
1623 save_replace = last_replace;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001624
1625 /* set search/replace strings to mis-spelt word */
Chris Allegretta6df90f52002-07-19 01:08:59 +00001626 last_search = mallocstrcpy(NULL, word);
1627 last_replace = mallocstrcpy(NULL, word);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001628
1629 /* start from the top of file */
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001630 current = fileage;
1631 current_x = beginx_top = -1;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001632
1633 search_last_line = FALSE;
1634
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001635 while (1) {
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001636 /* make sure word is still mis-spelt (i.e. when multi-errors) */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001637 if (findnextstr(TRUE, FALSE, fileage, beginx_top, word)) {
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001638
Chris Allegretta6df90f52002-07-19 01:08:59 +00001639 /* find whole words only */
1640 if (!is_whole_word(current_x, current->data, word))
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001641 continue;
Chris Allegrettad00e6df2000-11-29 04:33:26 +00001642
Chris Allegrettabfd2f562002-12-10 03:03:20 +00001643 edit_update(current, current_x);
Chris Allegretta6df90f52002-07-19 01:08:59 +00001644 do_replace_highlight(TRUE, word);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001645
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001646 /* allow replace word to be corrected */
Chris Allegretta5beed502003-01-05 20:41:21 +00001647 i = statusq(0, spell_list, last_replace, 0, _("Edit a replacement"));
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001648
Chris Allegretta6df90f52002-07-19 01:08:59 +00001649 do_replace_highlight(FALSE, word);
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001650
1651 /* start from the start of this line again */
1652 current = fileage;
1653 current_x = beginx_top;
1654
1655 search_last_line = FALSE;
1656
Chris Allegretta6df90f52002-07-19 01:08:59 +00001657 if (strcmp(word, answer)) {
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001658 j = i;
Chris Allegretta6df90f52002-07-19 01:08:59 +00001659 do_replace_loop(word, fileage, &beginx_top, TRUE, &j);
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001660 }
Chris Allegretta80838272001-12-02 06:03:22 +00001661 }
Chris Allegretta1bc0c7e2002-01-08 15:00:24 +00001662 break;
Rocco Corsi562964d2002-01-13 03:18:03 +00001663 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001664
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001665 /* restore the search/replace strings */
Chris Allegrettabef12972002-03-06 03:30:40 +00001666 free(last_search); last_search=save_search;
1667 free(last_replace); last_replace=save_replace;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001668
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001669 /* restore where we were */
1670 current = begin;
1671 current_x = beginx - 1;
1672
Chris Allegretta23b74b22002-01-21 20:32:22 +00001673 /* restore Search/Replace direction */
1674 if (reverse_search_set)
1675 SET(REVERSE_SEARCH);
1676
Chris Allegretta1d8fa1f2002-12-10 00:53:21 +00001677 if (!case_sens_set)
1678 UNSET(CASE_SENSITIVE);
1679
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001680#ifndef NANO_SMALL
1681 /* restore marking highlight */
1682 if (mark_set)
1683 SET(MARK_ISSET);
1684#endif
1685
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001686 if (i == -1)
1687 return FALSE;
1688
1689 return TRUE;
1690}
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001691
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001692/* Integrated spell checking using 'spell' program. Return value: NULL
1693 * for normal termination, otherwise the error string. */
Chris Allegretta334a9402002-12-16 04:25:53 +00001694char *do_int_speller(char *tempfile_name)
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001695{
Chris Allegretta271e9722000-11-10 18:15:43 +00001696 char *read_buff, *read_buff_ptr, *read_buff_word;
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001697 size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001698 int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001699 pid_t pid_spell, pid_sort, pid_uniq;
1700 int spell_status, sort_status, uniq_status;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001701
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001702 /* Create all three pipes up front */
Chris Allegretta271e9722000-11-10 18:15:43 +00001703
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001704 if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 || pipe(uniq_fd) == -1)
1705 return _("Could not create pipe");
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001706
Chris Allegretta2a7a9a22002-12-10 00:16:27 +00001707 statusbar(_("Creating misspelled word list, please wait..."));
Chris Allegretta271e9722000-11-10 18:15:43 +00001708 /* A new process to run spell in */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001709
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001710 if ((pid_spell = fork()) == 0) {
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001711
1712 /* Child continues, (i.e. future spell process) */
1713
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001714 close(spell_fd[0]);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001715
Chris Allegretta271e9722000-11-10 18:15:43 +00001716 /* replace the standard in with the tempfile */
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001717 if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
1718 goto close_pipes_and_exit;
1719
1720 if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
1721 goto close_pipes_and_exit;
1722
Chris Allegretta271e9722000-11-10 18:15:43 +00001723 close(tempfile_fd);
1724
1725 /* send spell's standard out to the pipe */
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001726 if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
1727 goto close_pipes_and_exit;
Chris Allegretta271e9722000-11-10 18:15:43 +00001728
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001729 close(spell_fd[1]);
Chris Allegretta271e9722000-11-10 18:15:43 +00001730
1731 /* Start spell program, we are using the PATH here!?!? */
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001732 execlp("spell", "spell", NULL);
1733
Chris Allegretta271e9722000-11-10 18:15:43 +00001734 /* Should not be reached, if spell is found!!! */
Chris Allegretta271e9722000-11-10 18:15:43 +00001735 exit(1);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001736 }
1737
1738 /* Parent continues here */
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001739 close(spell_fd[1]);
1740
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001741 /* A new process to run sort in */
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001742 if ((pid_sort = fork()) == 0) {
1743
1744 /* Child continues, (i.e. future spell process) */
1745 /* replace the standard in with output of the old pipe */
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001746 if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
1747 goto close_pipes_and_exit;
1748
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001749 close(spell_fd[0]);
1750
1751 /* send sort's standard out to the new pipe */
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001752 if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
1753 goto close_pipes_and_exit;
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001754
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001755 close(sort_fd[1]);
1756
1757 /* Start sort program. Use -f to remove mixed case without having
1758 to have ANOTHER pipe for tr. If this isn't portable, let me know. */
1759 execlp("sort", "sort", "-f", NULL);
1760
1761 /* Should not be reached, if sort is found */
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001762 exit(1);
1763 }
1764
1765 close(sort_fd[1]);
1766
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001767 /* A new process to run uniq in */
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001768 if ((pid_uniq = fork()) == 0) {
1769
1770 /* Child continues, (i.e. future uniq process) */
1771 /* replace the standard in with output of the old pipe */
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001772 if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
1773 goto close_pipes_and_exit;
1774
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001775 close(sort_fd[0]);
1776
1777 /* send uniq's standard out to the new pipe */
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001778 if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
1779 goto close_pipes_and_exit;
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001780
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001781 close(uniq_fd[1]);
1782
1783 /* Start uniq program, we are using PATH */
1784 execlp("uniq", "uniq", NULL);
1785
1786 /* Should not be reached, if uniq is found */
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001787 exit(1);
1788 }
1789
1790 close(uniq_fd[1]);
Chris Allegretta271e9722000-11-10 18:15:43 +00001791
1792 /* Child process was not forked successfully */
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001793 if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
1794 close(uniq_fd[0]);
Chris Allegretta334a9402002-12-16 04:25:53 +00001795 return _("Could not fork");
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001796 }
1797
Chris Allegretta271e9722000-11-10 18:15:43 +00001798 /* Get system pipe buffer size */
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001799 if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
1800 close(uniq_fd[0]);
Chris Allegretta334a9402002-12-16 04:25:53 +00001801 return _("Could not get size of pipe buffer");
Chris Allegretta271e9722000-11-10 18:15:43 +00001802 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001803
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001804 /* Read-in the returned spelling errors */
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001805 read_buff_read = 0;
1806 read_buff_size = pipe_buff_size + 1;
1807 read_buff = read_buff_ptr = charalloc(read_buff_size);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001808
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001809 while ((bytesread = read(uniq_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001810 read_buff_read += bytesread;
1811 read_buff_size += pipe_buff_size;
1812 read_buff = read_buff_ptr = nrealloc(read_buff, read_buff_size);
1813 read_buff_ptr += read_buff_read;
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001814
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001815 }
Chris Allegretta271e9722000-11-10 18:15:43 +00001816
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001817 *read_buff_ptr = (char)NULL;
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001818 close(uniq_fd[0]);
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001819
1820 /* Process the spelling errors */
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001821 read_buff_word = read_buff_ptr = read_buff;
1822
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001823 while (*read_buff_ptr != '\0') {
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001824
1825 if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001826 *read_buff_ptr = (char)NULL;
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001827 if (read_buff_word != read_buff_ptr) {
1828 if (!do_int_spell_fix(read_buff_word)) {
1829 read_buff_word = read_buff_ptr;
1830 break;
1831 }
1832 }
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001833 read_buff_word = read_buff_ptr + 1;
1834 }
Chris Allegrettaf21f3fc2002-03-25 03:26:27 +00001835 read_buff_ptr++;
1836 }
1837
1838 /* special case where last word doesn't end with \n or \r */
1839 if (read_buff_word != read_buff_ptr)
1840 do_int_spell_fix(read_buff_word);
1841
Chris Allegretta271e9722000-11-10 18:15:43 +00001842 free(read_buff);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001843 replace_abort();
Chris Allegretta35908f82002-12-10 00:55:32 +00001844 edit_update(current, current_x);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001845
Chris Allegretta271e9722000-11-10 18:15:43 +00001846 /* Process end of spell process */
1847
Chris Allegretta334a9402002-12-16 04:25:53 +00001848 waitpid(pid_spell, &spell_status, 0);
1849 waitpid(pid_sort, &sort_status, 0);
1850 waitpid(pid_uniq, &uniq_status, 0);
Chris Allegretta5ad92ac2002-12-09 00:58:51 +00001851
Chris Allegretta334a9402002-12-16 04:25:53 +00001852 if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
1853 return _("Error invoking \"spell\"");
Chris Allegretta271e9722000-11-10 18:15:43 +00001854
Chris Allegretta334a9402002-12-16 04:25:53 +00001855 if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status))
1856 return _("Error invoking \"sort -f\"");
1857
1858 if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
1859 return _("Error invoking \"uniq\"");
1860
1861 /* Otherwise... */
1862 return NULL;
Chris Allegretta3f1b6852003-01-12 23:54:05 +00001863
1864close_pipes_and_exit:
1865
1866 /* Don't leak any handles */
1867 close(tempfile_fd);
1868 close(spell_fd[0]);
1869 close(spell_fd[1]);
1870 close(sort_fd[0]);
1871 close(sort_fd[1]);
1872 close(uniq_fd[0]);
1873 close(uniq_fd[1]);
1874 exit(1);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001875}
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001876
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001877/* External spell checking. Return value: NULL for normal termination,
1878 * otherwise the error string. */
Chris Allegretta334a9402002-12-16 04:25:53 +00001879char *do_alt_speller(char *tempfile_name)
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001880{
Chris Allegrettab3655b42001-10-22 03:15:31 +00001881 int alt_spell_status, lineno_cur = current->lineno;
1882 int x_cur = current_x, y_cur = current_y, pww_cur = placewewant;
Chris Allegretta271e9722000-11-10 18:15:43 +00001883 pid_t pid_spell;
Chris Allegretta169ee842001-01-26 01:57:32 +00001884 char *ptr;
1885 static int arglen = 3;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001886 static char **spellargs = (char **)NULL;
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001887#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001888 int mark_set = ISSET(MARK_ISSET);
1889 int mbb_lineno_cur = 0;
1890 /* We're going to close the current file, and open the output of
1891 the alternate spell command. The line that mark_beginbuf
1892 points to will be freed, so we save the line number and restore
1893 afterwards. */
1894
1895 if (mark_set) {
1896 mbb_lineno_cur = mark_beginbuf->lineno;
1897 UNSET(MARK_ISSET);
1898 }
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001899#endif
1900
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001901 endwin();
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001902
Chris Allegrettae434b452001-01-27 19:25:00 +00001903 /* Set up an argument list to pass the execvp function */
1904 if (spellargs == NULL) {
1905 spellargs = nmalloc(arglen * sizeof(char *));
Chris Allegretta271e9722000-11-10 18:15:43 +00001906
Chris Allegrettae434b452001-01-27 19:25:00 +00001907 spellargs[0] = strtok(alt_speller, " ");
1908 while ((ptr = strtok(NULL, " ")) != NULL) {
1909 arglen++;
1910 spellargs = nrealloc(spellargs, arglen * sizeof(char *));
1911 spellargs[arglen - 3] = ptr;
Chris Allegretta169ee842001-01-26 01:57:32 +00001912 }
Chris Allegrettae434b452001-01-27 19:25:00 +00001913 spellargs[arglen - 1] = NULL;
1914 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001915 spellargs[arglen - 2] = tempfile_name;
Chris Allegrettae434b452001-01-27 19:25:00 +00001916
1917 /* Start a new process for the alternate speller */
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001918 if ((pid_spell = fork()) == 0) {
Chris Allegretta88520c92001-05-05 17:45:54 +00001919 /* Start alternate spell program; we are using the PATH here!?!? */
Chris Allegretta169ee842001-01-26 01:57:32 +00001920 execvp(spellargs[0], spellargs);
Chris Allegretta271e9722000-11-10 18:15:43 +00001921
1922 /* Should not be reached, if alternate speller is found!!! */
Chris Allegretta271e9722000-11-10 18:15:43 +00001923 exit(1);
1924 }
1925
1926 /* Could not fork?? */
Chris Allegretta271e9722000-11-10 18:15:43 +00001927 if (pid_spell < 0)
Chris Allegretta334a9402002-12-16 04:25:53 +00001928 return _("Could not fork");
Chris Allegretta271e9722000-11-10 18:15:43 +00001929
1930 /* Wait for alternate speller to complete */
1931
1932 wait(&alt_spell_status);
Chris Allegretta334a9402002-12-16 04:25:53 +00001933 if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0) {
1934 char *altspell_error = NULL;
1935 char *invoke_error = _("Could not invoke \"%s\"");
1936 int msglen = strlen(invoke_error) + strlen(alt_speller) + 2;
1937
1938 altspell_error = charalloc(msglen);
1939 snprintf(altspell_error, msglen, invoke_error, alt_speller);
1940 return altspell_error;
1941 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001942
Chris Allegretta8f6c0692000-07-19 01:16:18 +00001943 refresh();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001944 free_filestruct(fileage);
Chris Allegretta56214c62001-09-27 02:46:53 +00001945 global_init(1);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00001946 open_file(tempfile_name, 0, 1);
Rocco Corsi4dfaf932001-04-20 01:59:55 +00001947
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001948#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001949 if (mark_set) {
1950 do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
1951 mark_beginbuf = current;
1952 mark_beginx = current_x;
1953 /* In case the line got shorter, assign mark_beginx. */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001954 SET(MARK_ISSET);
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00001955 }
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00001956#endif
1957
Chris Allegretta1b3381b2001-09-28 21:59:01 +00001958 /* go back to the old position, mark the file as modified, and make
1959 sure that the titlebar is refreshed */
1960 do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00001961 set_modified();
Chris Allegrettae1f14522001-09-19 03:19:43 +00001962 clearok(topwin, FALSE);
1963 titlebar(NULL);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00001964
Chris Allegretta334a9402002-12-16 04:25:53 +00001965 return NULL;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001966}
1967#endif
1968
1969int do_spell(void)
1970{
Rocco Corsiaf5c3022001-01-12 07:51:05 +00001971#ifdef DISABLE_SPELLER
Chris Allegrettaff269f82000-12-01 18:46:01 +00001972 nano_disabled_msg();
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001973 return TRUE;
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001974#else
Chris Allegretta334a9402002-12-16 04:25:53 +00001975 char *temp, *spell_msg = _("Generic error");
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001976
Chris Allegrettaa8c22572002-02-15 19:17:02 +00001977 if ((temp = safe_tempnam(0, "nano.")) == NULL) {
Chris Allegretta271e9722000-11-10 18:15:43 +00001978 statusbar(_("Could not create a temporary filename: %s"),
Chris Allegretta6b58acd2001-04-12 03:01:53 +00001979 strerror(errno));
Chris Allegretta271e9722000-11-10 18:15:43 +00001980 return 0;
1981 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001982
Chris Allegrettaecc3d7f2001-06-05 23:24:55 +00001983 if (write_file(temp, 1, 0, 0) == -1) {
Chris Allegretta3dbb2782000-12-02 04:36:50 +00001984 statusbar(_("Spell checking failed: unable to write temp file!"));
Chris Allegrettabef12972002-03-06 03:30:40 +00001985 free(temp);
Chris Allegretta271e9722000-11-10 18:15:43 +00001986 return 0;
Chris Allegretta3dbb2782000-12-02 04:36:50 +00001987 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00001988
Chris Allegrettae1f14522001-09-19 03:19:43 +00001989#ifdef ENABLE_MULTIBUFFER
1990 /* update the current open_files entry before spell-checking, in case
Chris Allegretta4dc03d52002-05-11 03:04:44 +00001991 any problems occur */
Chris Allegretta48b06702002-02-22 04:30:50 +00001992 add_open_file(1);
Chris Allegrettae1f14522001-09-19 03:19:43 +00001993#endif
1994
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00001995 if (alt_speller != NULL)
Chris Allegretta334a9402002-12-16 04:25:53 +00001996 spell_msg = do_alt_speller(temp);
Chris Allegretta271e9722000-11-10 18:15:43 +00001997 else
Chris Allegretta334a9402002-12-16 04:25:53 +00001998 spell_msg = do_int_speller(temp);
Chris Allegretta271e9722000-11-10 18:15:43 +00001999 remove(temp);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00002000
Chris Allegretta334a9402002-12-16 04:25:53 +00002001 if (spell_msg == NULL) {
Chris Allegretta27eb13f2000-11-05 16:52:21 +00002002 statusbar(_("Finished checking spelling"));
Chris Allegretta334a9402002-12-16 04:25:53 +00002003 return 1;
2004 } else {
2005 statusbar(_("Spell checking failed: %s"), spell_msg);
2006 return 0;
2007 }
Chris Allegretta27eb13f2000-11-05 16:52:21 +00002008
Chris Allegrettabef12972002-03-06 03:30:40 +00002009 free(temp);
Chris Allegretta27eb13f2000-11-05 16:52:21 +00002010
Chris Allegrettadbc12b22000-07-03 03:10:14 +00002011#endif
Chris Allegretta67105eb2000-07-03 03:18:32 +00002012}
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002013
Chris Allegrettad865da12002-07-29 23:46:38 +00002014#if !defined(DISABLE_WRAPPING) && !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
Chris Allegretta6df90f52002-07-19 01:08:59 +00002015/* The "indentation" of a line is the white-space between the quote part
2016 * and the non-white-space of the line. */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002017size_t indent_length(const char *line)
2018{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002019 size_t len = 0;
2020
2021 assert(line != NULL);
2022 while (*line == ' ' || *line == '\t') {
2023 line++;
2024 len++;
2025 }
2026 return len;
2027}
Chris Allegrettadffa2072002-07-24 01:02:26 +00002028#endif /* !DISABLE_WRAPPING && !NANO_SMALL || !DISABLE_JUSTIFY */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002029
Rocco Corsiaf5c3022001-01-12 07:51:05 +00002030#ifndef DISABLE_JUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +00002031/* justify_format() replaces Tab by Space and multiple spaces by 1 (except
2032 * it maintains 2 after a . ! or ?). Note the terminating \0
2033 * counts as a space.
2034 *
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +00002035 * If !changes_allowed and justify_format() needs to make a change, it
Chris Allegretta6df90f52002-07-19 01:08:59 +00002036 * returns 1, otherwise returns 0.
2037 *
2038 * If changes_allowed, justify_format() might make line->data
2039 * shorter, and change the actual pointer with null_at().
2040 *
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +00002041 * justify_format() will not look at the first skip characters of line.
Chris Allegretta6df90f52002-07-19 01:08:59 +00002042 * skip should be at most strlen(line->data). The skip+1st character must
2043 * not be whitespace. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002044int justify_format(int changes_allowed, filestruct *line, size_t skip)
2045{
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002046 const char *punct = ".?!";
2047 const char *brackets = "'\")}]>";
Chris Allegretta6df90f52002-07-19 01:08:59 +00002048 char *back, *front;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002049
Chris Allegretta6df90f52002-07-19 01:08:59 +00002050 /* These four asserts are assumptions about the input data. */
2051 assert(line != NULL);
2052 assert(line->data != NULL);
2053 assert(skip <= strlen(line->data));
2054 assert(line->data[skip] != ' ' && line->data[skip] != '\t');
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002055
Chris Allegretta6df90f52002-07-19 01:08:59 +00002056 back = line->data + skip;
2057 front = back;
2058 for (; *front; front++) {
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00002059 int remove_space = 0;
2060 /* Do we want to remove this space? */
2061
Chris Allegretta6df90f52002-07-19 01:08:59 +00002062 if (*front == '\t') {
2063 if (!changes_allowed)
2064 return 1;
2065 *front = ' ';
2066 }
2067 /* these tests are safe since line->data + skip is not a space */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002068 if (*front == ' ' && *(front - 1) == ' ') {
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00002069 const char *bob = front - 2;
2070
2071 remove_space = 1;
2072 for (bob = front - 2; bob >= line->data + skip; bob--) {
2073 if (strchr(punct, *bob) != NULL) {
2074 remove_space = 0;
2075 break;
2076 }
2077 if (strchr(brackets, *bob) == NULL)
2078 break;
2079 }
2080 }
2081
2082 if (remove_space) {
Chris Allegretta6df90f52002-07-19 01:08:59 +00002083 /* Now *front is a space we want to remove. We do that by
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00002084 * simply failing to assign it to *back. */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002085 if (!changes_allowed)
2086 return 1;
2087#ifndef NANO_SMALL
2088 if (mark_beginbuf == line && back - line->data < mark_beginx)
2089 mark_beginx--;
2090#endif
2091 } else {
2092 *back = *front;
2093 back++;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002094 }
2095 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002096
2097 /* Remove spaces from the end of the line, except maintain 1 after a
2098 * sentence punctuation. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002099 while (line->data < back && *(back - 1) == ' ')
Chris Allegretta6df90f52002-07-19 01:08:59 +00002100 back--;
2101 if (line->data < back && *back == ' ' &&
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002102 strchr(punct, *(back - 1)) != NULL)
Chris Allegretta6df90f52002-07-19 01:08:59 +00002103 back++;
2104 if (!changes_allowed && back != front)
2105 return 1;
2106
2107 /* This assert merely documents a fact about the loop above. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002108 assert(changes_allowed != 0 || back == front);
Chris Allegretta6df90f52002-07-19 01:08:59 +00002109
2110 /* Now back is the new end of line->data. */
2111 if (back != front) {
2112 totsize += back - line->data - strlen(line->data);
2113 null_at(&line->data, back - line->data);
2114#ifndef NANO_SMALL
2115 if (mark_beginbuf == line && back - line->data < mark_beginx)
2116 mark_beginx = back - line->data;
2117#endif
2118 }
2119 return 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002120}
Chris Allegretta6df90f52002-07-19 01:08:59 +00002121
2122/* The "quote part" of a line is the largest initial substring matching
2123 * the quote string. This function returns the length of the quote part
2124 * of the given line.
2125 *
2126 * Note that if !HAVE_REGEX_H then we match concatenated copies of
2127 * quotestr. */
2128#ifdef HAVE_REGEX_H
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002129size_t quote_length(const char *line, const regex_t *qreg)
2130{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002131 regmatch_t matches;
2132 int rc = regexec(qreg, line, 1, &matches, 0);
2133
2134 if (rc == REG_NOMATCH || matches.rm_so == (regoff_t) -1)
2135 return 0;
2136 /* matches.rm_so should be 0, since the quote string should start with
2137 * the caret ^. */
2138 return matches.rm_eo;
2139}
2140#else /* !HAVE_REGEX_H */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002141size_t quote_length(const char *line)
2142{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002143 size_t qdepth = 0;
2144 size_t qlen = strlen(quotestr);
2145
2146 /* Compute quote depth level */
2147 while (!strcmp(line + qdepth, quotestr))
2148 qdepth += qlen;
2149 return qdepth;
2150}
2151#endif /* !HAVE_REGEX_H */
2152
Chris Allegretta6df90f52002-07-19 01:08:59 +00002153/* a_line and b_line are lines of text. The quotation part of a_line is
2154 * the first a_quote characters. Check that the quotation part of
2155 * b_line is the same. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002156int quotes_match(const char *a_line, size_t a_quote,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002157 IFREG(const char *b_line, const regex_t *qreg))
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002158{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002159 /* Here is the assumption about a_quote: */
2160 assert(a_quote == quote_length(IFREG(a_line, qreg)));
Chris Allegrettad865da12002-07-29 23:46:38 +00002161 return a_quote == quote_length(IFREG(b_line, qreg)) &&
Chris Allegretta6df90f52002-07-19 01:08:59 +00002162 !strncmp(a_line, b_line, a_quote);
2163}
2164
2165/* We assume a_line and b_line have no quote part. Then, we return whether
2166 * b_line could follow a_line in a paragraph. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002167size_t indents_match(const char *a_line, size_t a_indent,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002168 const char *b_line, size_t b_indent)
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002169{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002170 assert(a_indent == indent_length(a_line));
2171 assert(b_indent == indent_length(b_line));
2172
2173 return b_indent <= a_indent && !strncmp(a_line, b_line, b_indent);
2174}
2175
2176/* Put the next par_len lines, starting with first_line, in the cut
2177 * buffer. We assume there are enough lines after first_line. We leave
2178 * copies of the lines in place, too. We return the new copy of
2179 * first_line. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002180filestruct *backup_lines(filestruct *first_line, size_t par_len,
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002181 size_t quote_len)
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002182{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002183 /* We put the original lines, not copies, into the cut buffer, just
2184 * out of a misguided sense of consistency, so if you un-cut, you
2185 * get the actual same paragraph back, not a copy. */
2186 filestruct *alice = first_line;
2187
2188 set_modified();
2189 cutbuffer = NULL;
2190 for(; par_len > 0; par_len--) {
2191 filestruct *bob = copy_node(alice);
2192
2193 if (alice == first_line)
2194 first_line = bob;
2195 if (alice == current)
2196 current = bob;
2197 if (alice == edittop)
2198 edittop = bob;
2199#ifndef NANO_SMALL
2200 if (alice == mark_beginbuf)
2201 mark_beginbuf = bob;
2202#endif
2203 justify_format(1, bob,
2204 quote_len + indent_length(bob->data + quote_len));
2205
2206 assert(alice != NULL && bob != NULL);
2207 add_to_cutbuffer(alice);
2208 splice_node(bob->prev, bob, bob->next);
2209 alice = bob->next;
2210 }
2211 return first_line;
2212}
2213
2214/* We are trying to break a chunk off line. We find the last space such
2215 * that the display length to there is at most goal + 1. If there is
2216 * no such space, and force is not 0, then we find the first space.
2217 * Anyway, we then take the last space in that group of spaces. The
2218 * terminating '\0' counts as a space. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002219int break_line(const char *line, int goal, int force)
2220{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002221 /* Note that we use int instead of size_t, since goal is at most COLS,
2222 * the screen width, which will always be reasonably small. */
2223 int space_loc = -1;
2224 /* Current tentative return value. Index of the last space we
2225 * found with short enough display width. */
2226 int cur_loc = 0;
2227 /* Current index in line */
2228
2229 assert(line != NULL);
2230 for(; *line != '\0' && goal >= 0; line++, cur_loc++) {
2231 if (*line == ' ')
2232 space_loc = cur_loc;
2233 assert(*line != '\t');
2234
Chris Allegrettacf287c82002-07-20 13:57:41 +00002235 if (is_cntrl_char(*line))
Chris Allegretta6df90f52002-07-19 01:08:59 +00002236 goal -= 2;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002237 else
2238 goal--;
2239 }
2240 if (goal >= 0)
2241 /* In fact, the whole line displays shorter than goal. */
2242 return cur_loc;
2243 if (space_loc == -1) {
2244 /* No space found short enough. */
2245 if (force)
2246 for(; *line != '\0'; line++, cur_loc++)
2247 if (*line == ' ' && *(line + 1) != ' ')
2248 return cur_loc;
2249 return -1;
2250 }
2251 /* Perhaps the character after space_loc is a space. But because
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +00002252 * of justify_format(), there can be only two adjacent. */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002253 if (*(line - cur_loc + space_loc + 1) == ' ' ||
2254 *(line - cur_loc + space_loc + 1) == '\0')
2255 space_loc++;
2256 return space_loc;
2257}
2258#endif /* !DISABLE_JUSTIFY */
2259
2260/* This function justifies the current paragraph. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002261int do_justify(void)
2262{
Rocco Corsiaf5c3022001-01-12 07:51:05 +00002263#ifdef DISABLE_JUSTIFY
Chris Allegrettaff269f82000-12-01 18:46:01 +00002264 nano_disabled_msg();
2265 return 1;
2266#else
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002267
Chris Allegretta6df90f52002-07-19 01:08:59 +00002268/* To explain the justifying algorithm, I first need to define some
2269 * phrases about paragraphs and quotation:
2270 * A line of text consists of a "quote part", followed by an
2271 * "indentation part", followed by text. The functions quote_length()
2272 * and indent_length() calculate these parts.
2273 *
2274 * A line is "part of a paragraph" if it has a part not in the quote
2275 * part or the indentation.
2276 *
2277 * A line is "the beginning of a paragraph" if it is part of a paragraph
2278 * and
2279 * 1) it is the top line of the file, or
2280 * 2) the line above it is not part of a paragraph, or
2281 * 3) the line above it does not have precisely the same quote
2282 * part, or
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002283 * 4) the indentation of this line is not an initial substring of the
Chris Allegretta6df90f52002-07-19 01:08:59 +00002284 * indentation of the previous line, or
2285 * 5) this line has no quote part and some indentation, and
2286 * AUTOINDENT is not set.
2287 * The reason for number 5) is that if AUTOINDENT is not set, then an
2288 * indented line is expected to start a paragraph, like in books. Thus,
2289 * nano can justify an indented paragraph only if AUTOINDENT is turned
2290 * on.
2291 *
2292 * A contiguous set of lines is a "paragraph" if each line is part of
2293 * a paragraph and only the first line is the beginning of a paragraph.
Chris Allegrettad4fa0d32002-03-05 19:55:55 +00002294 */
Robert Siemborski60cd6aa2001-01-21 23:23:48 +00002295
Chris Allegretta6df90f52002-07-19 01:08:59 +00002296 size_t quote_len;
2297 /* Length of the initial quotation of the paragraph we justify. */
2298 size_t par_len;
2299 /* Number of lines in that paragraph. */
2300 filestruct *first_mod_line = NULL;
2301 /* Will be the first line of the resulting justified paragraph
2302 * that differs from the original. For restoring after uncut. */
2303 filestruct *last_par_line = current;
2304 /* Will be the last line of the result, also for uncut. */
2305 filestruct *cutbuffer_save = cutbuffer;
2306 /* When the paragraph gets modified, all lines from the changed
2307 * one down are stored in the cut buffer. We back up the original
2308 * to restore it later. */
2309
2310 /* We save these global variables to be restored if the user
2311 * unjustifies. Note we don't need to save totlines. */
2312 int current_x_save = current_x;
2313 int current_y_save = current_y;
2314 filestruct *current_save = current;
2315 int flags_save = flags;
2316 long totsize_save = totsize;
2317 filestruct *edittop_save = edittop;
2318 filestruct *editbot_save = editbot;
2319#ifndef NANO_SMALL
2320 filestruct *mark_beginbuf_save = mark_beginbuf;
2321 int mark_beginx_save = mark_beginx;
2322#endif
2323
2324 size_t indent_len; /* generic indentation length */
2325 filestruct *line; /* generic line of text */
2326 size_t i; /* generic loop variable */
2327
2328#ifdef HAVE_REGEX_H
2329 regex_t qreg; /* qreg is the compiled quotation regexp.
Chris Allegrettad865da12002-07-29 23:46:38 +00002330 * We no longer care about quotestr. */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002331 int rc = regcomp(&qreg, quotestr, REG_EXTENDED);
2332
2333 if (rc) {
2334 size_t size = regerror(rc, &qreg, NULL, 0);
2335 char *strerror = charalloc(size);
2336
2337 regerror(rc, &qreg, strerror, size);
2338 statusbar(_("Bad quote string %s: %s"), quotestr, strerror);
2339 free(strerror);
2340 return -1;
2341 }
2342#endif
2343
2344 /* Here is an assumption that is always true anyway. */
2345 assert(current != NULL);
2346
2347/* Here we find the first line of the paragraph to justify. If the
2348 * current line is in a paragraph, then we move back to the first line.
2349 * Otherwise we move down to the first line that is in a paragraph. */
2350 quote_len = quote_length(IFREG(current->data, &qreg));
2351 indent_len = indent_length(current->data + quote_len);
2352
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002353 current_x = 0;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002354 if (current->data[quote_len + indent_len] != '\0') {
2355 /* This line is part of a paragraph. So we must search back to
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002356 * the first line of this paragraph. First we check items 1) and
2357 * 3) above. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002358 while (current->prev != NULL && quotes_match(current->data,
Chris Allegretta6df90f52002-07-19 01:08:59 +00002359 quote_len, IFREG(current->prev->data, &qreg))) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002360 size_t temp_id_len =
Chris Allegretta6df90f52002-07-19 01:08:59 +00002361 indent_length(current->prev->data + quote_len);
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002362 /* The indentation length of the previous line. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002363
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002364 /* Is this line the beginning of a paragraph, according to
2365 items 2), 5), or 4) above? If so, stop. */
2366 if (current->prev->data[quote_len + temp_id_len] == '\0' ||
2367 (quote_len == 0 && indent_len > 0
2368#ifndef NANO_SMALL
2369 && !ISSET(AUTOINDENT)
2370#endif
2371 ) ||
2372 !indents_match(current->prev->data + quote_len,
2373 temp_id_len, current->data + quote_len, indent_len))
2374 break;
2375 indent_len = temp_id_len;
2376 current = current->prev;
2377 current_y--;
Chris Allegretta7162e3d2002-04-06 05:02:14 +00002378 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002379 } else {
Chris Allegrettad865da12002-07-29 23:46:38 +00002380 /* This line is not part of a paragraph. Move down until we get
2381 * to a non "blank" line. */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002382 do {
2383 /* There is no next paragraph, so nothing to justify. */
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002384 if (current->next == NULL) {
2385 placewewant = 0;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002386 if (current_y > editwinrows - 1)
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002387 edit_update(current, CENTER);
2388 else
2389 edit_refresh();
Chris Allegretta6df90f52002-07-19 01:08:59 +00002390 return 0;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002391 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002392 current = current->next;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002393 current_y++;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002394 quote_len = quote_length(IFREG(current->data, &qreg));
2395 indent_len = indent_length(current->data + quote_len);
2396 } while (current->data[quote_len + indent_len] == '\0');
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002397 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002398/* Now current is the first line of the paragraph, and quote_len
2399 * is the quotation length of that line. */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002400
Chris Allegretta6df90f52002-07-19 01:08:59 +00002401/* Next step, compute par_len, the number of lines in this paragraph. */
2402 line = current;
2403 par_len = 1;
2404 indent_len = indent_length(line->data + quote_len);
2405
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002406 while (line->next != NULL && quotes_match(current->data, quote_len,
Chris Allegretta6df90f52002-07-19 01:08:59 +00002407 IFREG(line->next->data, &qreg))) {
2408 size_t temp_id_len = indent_length(line->next->data + quote_len);
2409
2410 if (!indents_match(line->data + quote_len, indent_len,
2411 line->next->data + quote_len, temp_id_len) ||
2412 line->next->data[quote_len + temp_id_len] == '\0' ||
2413 (quote_len == 0 && temp_id_len > 0
2414#ifndef NANO_SMALL
2415 && !ISSET(AUTOINDENT)
2416#endif
2417 ))
2418 break;
2419 indent_len = temp_id_len;
2420 line = line->next;
2421 par_len++;
2422 }
2423#ifdef HAVE_REGEX_H
2424 /* We no longer need to check quotation. */
2425 regfree(&qreg);
2426#endif
2427/* Now par_len is the number of lines in this paragraph. Should never
2428 * call quotes_match() or quote_length() again. */
2429
2430/* Next step, we loop through the lines of this paragraph, justifying
2431 * each one individually. */
2432 for(; par_len > 0; current_y++, par_len--) {
2433 size_t line_len;
2434 size_t display_len;
2435 /* The width of current in screen columns. */
2436 int break_pos;
2437 /* Where we will break the line. */
2438
2439 indent_len = indent_length(current->data + quote_len) +
2440 quote_len;
2441 /* justify_format() removes excess spaces from the line, and
2442 * changes tabs to spaces. The first argument, 0, means don't
2443 * change the line, just say whether there are changes to be
2444 * made. If there are, we do backup_lines(), which copies the
2445 * original paragraph to the cutbuffer for unjustification, and
David Lawrence Ramsey0084eaa2002-11-04 16:05:42 +00002446 * then calls justify_format() on the remaining lines. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002447 if (first_mod_line == NULL && justify_format(0, current, indent_len))
Chris Allegretta6df90f52002-07-19 01:08:59 +00002448 first_mod_line = backup_lines(current, par_len, quote_len);
2449
2450 line_len = strlen(current->data);
2451 display_len = strlenpt(current->data);
2452
2453 if (display_len > fill) {
2454 /* The line is too long. Try to wrap it to the next. */
2455 break_pos = break_line(current->data + indent_len,
2456 fill - strnlenpt(current->data, indent_len),
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002457 1);
Chris Allegretta6df90f52002-07-19 01:08:59 +00002458 if (break_pos == -1 || break_pos + indent_len == line_len)
2459 /* We can't break the line, or don't need to, so just go
2460 * on to the next. */
2461 goto continue_loc;
2462 break_pos += indent_len;
2463 assert(break_pos < line_len);
2464 /* If we haven't backed up the paragraph, do it now. */
2465 if (first_mod_line == NULL)
2466 first_mod_line = backup_lines(current, par_len, quote_len);
2467 if (par_len == 1) {
2468 /* There is no next line in this paragraph. We make a new
2469 * line and copy text after break_pos into it. */
2470 splice_node(current, make_new_node(current),
2471 current->next);
2472 current->next->data = charalloc(indent_len + line_len -
2473 break_pos);
2474 strncpy(current->next->data, current->data,
2475 indent_len);
2476 strcpy(current->next->data + indent_len,
2477 current->data + break_pos + 1);
2478 assert(strlen(current->next->data) ==
2479 indent_len + line_len - break_pos - 1);
2480 totlines++;
2481 totsize += indent_len;
2482 par_len++;
2483 } else {
2484 size_t next_line_len = strlen(current->next->data);
2485
2486 indent_len = quote_len +
2487 indent_length(current->next->data + quote_len);
2488 current->next->data = (char *)nrealloc(current->next->data,
2489 sizeof(char) * (next_line_len + line_len -
2490 break_pos + 1));
2491
2492 memmove(current->next->data + indent_len + line_len - break_pos,
2493 current->next->data + indent_len,
2494 next_line_len - indent_len + 1);
2495 strcpy(current->next->data + indent_len,
2496 current->data + break_pos + 1);
2497 current->next->data[indent_len + line_len - break_pos - 1]
2498 = ' ';
2499#ifndef NANO_SMALL
2500 if (mark_beginbuf == current->next) {
2501 if (mark_beginx < indent_len)
2502 mark_beginx = indent_len;
2503 mark_beginx += line_len - break_pos;
2504 }
2505#endif
2506 }
2507#ifndef NANO_SMALL
2508 if (mark_beginbuf == current && mark_beginx > break_pos) {
2509 mark_beginbuf = current->next;
2510 mark_beginx -= break_pos + 1 - indent_len;
2511 }
2512#endif
2513 null_at(&current->data, break_pos);
2514 current = current->next;
2515 } else if (display_len < fill && par_len > 1) {
2516 size_t next_line_len = strlen(current->next->data);
2517
2518 indent_len = quote_len +
2519 indent_length(current->next->data + quote_len);
2520 break_pos = break_line(current->next->data + indent_len,
2521 fill - display_len - 1, 0);
2522 if (break_pos == -1)
2523 /* We can't pull a word from the next line up to this one,
2524 * so just go on. */
2525 goto continue_loc;
2526
2527 /* If we haven't backed up the paragraph, do it now. */
2528 if (first_mod_line == NULL)
2529 first_mod_line = backup_lines(current, par_len, quote_len);
2530 current->data = (char *)nrealloc(current->data,
2531 line_len + break_pos + 2);
2532 current->data[line_len] = ' ';
2533 strncpy(current->data + line_len + 1,
2534 current->next->data + indent_len, break_pos);
2535 current->data[line_len + break_pos + 1] = '\0';
2536#ifndef NANO_SMALL
2537 if (mark_beginbuf == current->next) {
2538 if (mark_beginx < indent_len + break_pos) {
2539 mark_beginbuf = current;
2540 if (mark_beginx <= indent_len)
2541 mark_beginx = line_len + 1;
2542 else
2543 mark_beginx = line_len + 1 + mark_beginx - indent_len;
2544 } else
2545 mark_beginx -= break_pos + 1;
2546 }
2547#endif
2548 if (indent_len + break_pos == next_line_len) {
2549 line = current->next;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002550
2551 /* Don't destroy edittop! */
2552 if (line == edittop)
2553 edittop = current;
2554
Chris Allegretta6df90f52002-07-19 01:08:59 +00002555 unlink_node(line);
2556 delete_node(line);
2557 totlines--;
2558 totsize -= indent_len;
2559 current_y--;
2560 } else {
2561 memmove(current->next->data + indent_len,
2562 current->next->data + indent_len + break_pos + 1,
2563 next_line_len - break_pos - indent_len);
2564 null_at(&current->next->data,
2565 next_line_len - break_pos);
2566 current = current->next;
2567 }
2568 } else
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002569 continue_loc:
Chris Allegretta6df90f52002-07-19 01:08:59 +00002570 current = current->next;
2571 }
2572/* We are now done justifying the paragraph. There are cleanup things to
2573 * do, and we check for unjustify. */
2574
2575 /* totlines, totsize, and current_y have been maintained above. We
2576 * now set last_par_line to the new end of the paragraph, update
2577 * fileage, set current_x. Also, edit_refresh() needs the line
2578 * numbers to be right, so we renumber(). */
2579 last_par_line = current->prev;
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002580 if (first_mod_line != NULL) {
2581 if (first_mod_line->prev == NULL)
2582 fileage = first_mod_line;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002583 renumber(first_mod_line);
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002584 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002585
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002586 if (current_y > editwinrows - 1)
Chris Allegretta6df90f52002-07-19 01:08:59 +00002587 edit_update(current, CENTER);
2588 else
2589 edit_refresh();
2590
Chris Allegretta9149e612000-11-27 00:23:41 +00002591 statusbar(_("Can now UnJustify!"));
Chris Allegretta07798352000-11-27 22:58:23 +00002592 /* Change the shortcut list to display the unjustify code */
2593 shortcut_init(1);
2594 display_main_list();
Chris Allegretta9149e612000-11-27 00:23:41 +00002595 reset_cursor();
2596
Chris Allegretta6df90f52002-07-19 01:08:59 +00002597 /* Now get a keystroke and see if it's unjustify; if not, unget the
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002598 * keystroke and return. */
Chris Allegretta5f071802001-05-06 02:34:31 +00002599
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002600#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta6df90f52002-07-19 01:08:59 +00002601 /* If it was a mouse click, parse it with do_mouse() and it might
2602 * become the unjustify key. Else give it back to the input stream. */
2603 if ((i = wgetch(edit)) == KEY_MOUSE)
Chris Allegretta5f071802001-05-06 02:34:31 +00002604 do_mouse();
2605 else
Chris Allegretta6df90f52002-07-19 01:08:59 +00002606 ungetch(i);
Chris Allegretta5f071802001-05-06 02:34:31 +00002607#endif
Chris Allegretta5f071802001-05-06 02:34:31 +00002608
Chris Allegretta6df90f52002-07-19 01:08:59 +00002609 if ((i = wgetch(edit)) != NANO_UNJUSTIFY_KEY) {
2610 ungetch(i);
2611 /* Did we back up anything at all? */
2612 if (cutbuffer != cutbuffer_save)
2613 free_filestruct(cutbuffer);
2614 placewewant = 0;
Chris Allegretta00ae5df2001-02-05 18:24:33 +00002615 } else {
Chris Allegretta9149e612000-11-27 00:23:41 +00002616 /* Else restore the justify we just did (ungrateful user!) */
Chris Allegretta6df90f52002-07-19 01:08:59 +00002617 current = current_save;
2618 current_x = current_x_save;
2619 current_y = current_y_save;
2620 edittop = edittop_save;
2621 editbot = editbot_save;
2622 if (first_mod_line != NULL) {
2623 filestruct *cutbottom = get_cutbottom();
Chris Allegrettad022eac2000-11-27 02:50:49 +00002624
Chris Allegretta6df90f52002-07-19 01:08:59 +00002625 /* Splice the cutbuffer back into the file. */
2626 cutbottom->next = last_par_line->next;
2627 cutbottom->next->prev = cutbottom;
2628 /* The line numbers after the end of the paragraph have
2629 * been changed, so we change them back. */
2630 renumber(cutbottom->next);
2631 if (first_mod_line->prev != NULL) {
2632 cutbuffer->prev = first_mod_line->prev;
2633 cutbuffer->prev->next = cutbuffer;
2634 } else
2635 fileage = cutbuffer;
2636 cutbuffer = NULL;
2637
2638 last_par_line->next = NULL;
2639 free_filestruct(first_mod_line);
2640
2641 /* Restore global variables from before justify */
2642 totsize = totsize_save;
2643 totlines = filebot->lineno;
2644#ifndef NANO_SMALL
2645 mark_beginbuf = mark_beginbuf_save;
2646 mark_beginx = mark_beginx_save;
2647#endif
2648 flags = flags_save;
2649 if (!ISSET(MODIFIED)) {
2650 titlebar(NULL);
2651 wrefresh(topwin);
2652 }
2653 }
2654 edit_refresh();
Chris Allegretta9149e612000-11-27 00:23:41 +00002655 }
Chris Allegretta6df90f52002-07-19 01:08:59 +00002656 cutbuffer = cutbuffer_save;
2657 blank_statusbar_refresh();
2658 /* display shortcut list without UnCut */
Chris Allegretta4a9c8582000-11-27 22:59:40 +00002659 shortcut_init(0);
2660 display_main_list();
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002661
Chris Allegretta6df90f52002-07-19 01:08:59 +00002662 return 0;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002663#endif
2664}
2665
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002666int do_exit(void)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002667{
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002668 int i;
Chris Allegretta13fd44b2002-01-02 13:59:11 +00002669
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002670 if (!ISSET(MODIFIED)) {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002671
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002672#ifdef ENABLE_MULTIBUFFER
2673 if (!close_open_file()) {
2674 display_main_list();
2675 return 1;
David Lawrence Ramseyc5967552002-06-21 03:20:06 +00002676 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002677 else
David Lawrence Ramseyc5967552002-06-21 03:20:06 +00002678#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002679 finish(0);
Chris Allegretta756f2202000-09-01 13:32:47 +00002680 }
2681
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002682 if (ISSET(TEMP_OPT)) {
2683 i = 1;
2684 } else {
2685 i = do_yesno(0, 0,
2686 _
2687 ("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
2688 }
2689
2690#ifdef DEBUG
2691 dump_buffer(fileage);
2692#endif
2693
2694 if (i == 1) {
2695 if (do_writeout(filename, 1, 0) > 0) {
2696
2697#ifdef ENABLE_MULTIBUFFER
2698 if (!close_open_file()) {
2699 display_main_list();
2700 return 1;
2701 }
2702 else
2703#endif
2704 finish(0);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00002705 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002706 } else if (i == 0) {
2707
2708#ifdef ENABLE_MULTIBUFFER
2709 if (!close_open_file()) {
2710 display_main_list();
2711 return 1;
2712 }
2713 else
2714#endif
2715 finish(0);
2716 } else
2717 statusbar(_("Cancelled"));
2718
2719 display_main_list();
2720 return 1;
2721}
2722
2723void signal_init(void)
2724{
2725#ifdef _POSIX_VDISABLE
2726 struct termios term;
2727#endif
2728
2729 /* Trap SIGINT and SIGQUIT cuz we want them to do useful things. */
2730 memset(&act, 0, sizeof(struct sigaction));
2731 act.sa_handler = SIG_IGN;
2732 sigaction(SIGINT, &act, NULL);
2733
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00002734 /* Trap SIGHUP and SIGTERM cuz we want to write the file out. */
2735 act.sa_handler = handle_hupterm;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002736 sigaction(SIGHUP, &act, NULL);
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00002737 sigaction(SIGTERM, &act, NULL);
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002738
2739#ifndef NANO_SMALL
2740 act.sa_handler = handle_sigwinch;
2741 sigaction(SIGWINCH, &act, NULL);
2742#endif
2743
2744#ifdef _POSIX_VDISABLE
2745 tcgetattr(0, &term);
2746
Chris Allegretta6cd143d2003-01-05 23:35:44 +00002747 if (!ISSET(PRESERVE)) {
2748 /* Ignore ^S and ^Q, much to Chris' chagrin */
2749 term.c_cc[VSTOP] = _POSIX_VDISABLE;
2750 term.c_cc[VSTART] = _POSIX_VDISABLE;
2751 }
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002752#ifdef VDSUSP
2753 term.c_cc[VDSUSP] = _POSIX_VDISABLE;
2754#endif /* VDSUSP */
2755
2756#endif /* _POSIX_VDISABLE */
2757
2758 if (!ISSET(SUSPEND)) {
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00002759 /* Insane! */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002760#ifdef _POSIX_VDISABLE
2761 term.c_cc[VSUSP] = _POSIX_VDISABLE;
2762#else
2763 act.sa_handler = SIG_IGN;
2764 sigaction(SIGTSTP, &act, NULL);
2765#endif
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002766 } else {
2767 /* If we don't do this, it seems other stuff interrupts the
2768 suspend handler! Try using nano with mutt without this
2769 line. */
2770 sigfillset(&act.sa_mask);
2771
2772 act.sa_handler = do_suspend;
2773 sigaction(SIGTSTP, &act, NULL);
2774
2775 act.sa_handler = do_cont;
2776 sigaction(SIGCONT, &act, NULL);
2777 }
2778
2779#ifdef _POSIX_VDISABLE
2780 tcsetattr(0, TCSANOW, &term);
2781#endif
2782}
2783
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00002784/* Handler for SIGHUP and SIGTERM */
2785RETSIGTYPE handle_hupterm(int signal)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002786{
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00002787 die(_("Received SIGHUP or SIGTERM"));
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002788}
2789
2790/* What do we do when we catch the suspend signal */
2791RETSIGTYPE do_suspend(int signal)
2792{
2793 endwin();
2794 printf("\n\n\n\n\nUse \"fg\" to return to nano\n");
2795 fflush(stdout);
2796
2797 /* Restore the terminal settings for the disabled keys */
2798 tcsetattr(0, TCSANOW, &oldterm);
2799
2800 /* We used to re-enable the default SIG_DFL and raise SIGTSTP, but
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002801 then we could be (and were) interrupted in the middle of the call.
2802 So we do it the mutt way instead */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002803 kill(0, SIGSTOP);
2804}
2805
2806/* Restore the suspend handler when we come back into the prog */
2807RETSIGTYPE do_cont(int signal)
2808{
2809 /* Now we just update the screen instead of having to reenable the
2810 SIGTSTP handler. */
2811
2812 doupdate();
2813 /* The Hurd seems to need this, otherwise a ^Y after a ^Z will
2814 start suspending again. */
2815 signal_init();
2816
2817#ifndef NANO_SMALL
2818 /* Perhaps the user resized the window while we slept. */
2819 handle_sigwinch(0);
2820#endif
2821}
2822
2823#ifndef NANO_SMALL
2824void handle_sigwinch(int s)
2825{
2826 const char *tty = ttyname(0);
2827 int fd;
2828 int result = 0;
2829 struct winsize win;
2830
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002831 if (tty == NULL)
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002832 return;
2833 fd = open(tty, O_RDWR);
2834 if (fd == -1)
2835 return;
2836 result = ioctl(fd, TIOCGWINSZ, &win);
2837 close(fd);
2838 if (result == -1)
2839 return;
2840
2841 /* Could check whether the COLS or LINES changed, and return
2842 * otherwise. EXCEPT, that COLS and LINES are ncurses global
2843 * variables, and in some cases ncurses has already updated them.
2844 * But not in all cases, argh. */
2845 COLS = win.ws_col;
2846 LINES = win.ws_row;
2847 if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
2848 die_too_small();
2849
2850#ifndef DISABLE_WRAPJUSTIFY
2851 fill = wrap_at;
2852 if (fill <= 0)
2853 fill += COLS;
2854 if (fill < MIN_FILL_LENGTH)
2855 die_too_small();
2856#endif
2857
2858 hblank = nrealloc(hblank, COLS + 1);
2859 memset(hblank, ' ', COLS);
2860 hblank[COLS] = '\0';
2861
2862#ifdef HAVE_RESIZETERM
2863 resizeterm(LINES, COLS);
2864#ifdef HAVE_WRESIZE
2865 if (wresize(topwin, 2, COLS) == ERR)
2866 die(_("Cannot resize top win"));
2867 if (mvwin(topwin, 0, 0) == ERR)
2868 die(_("Cannot move top win"));
2869 if (wresize(edit, editwinrows, COLS) == ERR)
2870 die(_("Cannot resize edit win"));
2871 if (mvwin(edit, 2, 0) == ERR)
2872 die(_("Cannot move edit win"));
2873 if (wresize(bottomwin, 3 - no_help(), COLS) == ERR)
2874 die(_("Cannot resize bottom win"));
2875 if (mvwin(bottomwin, LINES - 3 + no_help(), 0) == ERR)
2876 die(_("Cannot move bottom win"));
2877#endif /* HAVE_WRESIZE */
2878#endif /* HAVE_RESIZETERM */
2879
2880 fix_editbot();
2881
2882 if (current_y > editwinrows - 1)
2883 edit_update(editbot, CENTER);
2884 erase();
2885
2886 /* Do these b/c width may have changed... */
2887 refresh();
2888 titlebar(NULL);
2889 edit_refresh();
2890 display_main_list();
2891 blank_statusbar();
2892 total_refresh();
2893
2894 /* Turn cursor back on for sure */
2895 curs_set(1);
2896
2897 /* Jump back to main loop */
2898 siglongjmp(jmpbuf, 1);
2899}
Chris Allegrettadab017e2002-04-23 10:56:06 +00002900#endif /* !NANO_SMALL */
David Lawrence Ramseyc5967552002-06-21 03:20:06 +00002901
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00002902/* If the NumLock key has made the keypad go awry, print an error
2903 message; hopefully we can address it later. */
2904void print_numlock_warning(void)
2905{
2906 static int didmsg = 0;
2907 if (!didmsg) {
2908 statusbar(_
2909 ("NumLock glitch detected. Keypad will malfunction with NumLock off"));
2910 didmsg = 1;
2911 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002912}
2913
Chris Allegrettadab017e2002-04-23 10:56:06 +00002914#ifndef NANO_SMALL
David Lawrence Ramsey0341b582002-08-21 16:10:37 +00002915void do_toggle(const toggle *which)
Chris Allegretta756f2202000-09-01 13:32:47 +00002916{
Chris Allegretta6df90f52002-07-19 01:08:59 +00002917 int enabled;
Chris Allegrettaf0f63a82000-09-02 18:44:21 +00002918
Chris Allegretta658399a2001-06-14 02:54:22 +00002919 /* Even easier! */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002920 TOGGLE(which->flag);
Chris Allegretta2a42af12000-09-12 23:02:49 +00002921
Chris Allegrettaa8c22572002-02-15 19:17:02 +00002922 switch (which->val) {
Chris Allegretta756f2202000-09-01 13:32:47 +00002923 case TOGGLE_SUSPEND_KEY:
2924 signal_init();
2925 break;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002926#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta756f2202000-09-01 13:32:47 +00002927 case TOGGLE_MOUSE_KEY:
2928 mouse_init();
2929 break;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002930#endif
Chris Allegretta756f2202000-09-01 13:32:47 +00002931 case TOGGLE_NOHELP_KEY:
Chris Allegretta2a42af12000-09-12 23:02:49 +00002932 wclear(bottomwin);
2933 wrefresh(bottomwin);
2934 window_init();
Chris Allegrettaaffeda82000-12-18 04:03:48 +00002935 fix_editbot();
Chris Allegretta2a42af12000-09-12 23:02:49 +00002936 edit_refresh();
2937 display_main_list();
Chris Allegretta756f2202000-09-01 13:32:47 +00002938 break;
Chris Allegretta99e30e12001-09-23 02:45:27 +00002939 case TOGGLE_DOS_KEY:
2940 UNSET(MAC_FILE);
2941 break;
2942 case TOGGLE_MAC_KEY:
2943 UNSET(DOS_FILE);
2944 break;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002945#ifdef ENABLE_COLOR
Chris Allegretta1dd0bc92002-10-13 18:43:45 +00002946 case TOGGLE_SYNTAX_KEY:
2947 edit_refresh();
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002948 break;
2949#endif
Chris Allegretta756f2202000-09-01 13:32:47 +00002950 }
Chris Allegretta2a42af12000-09-12 23:02:49 +00002951
Chris Allegretta6df90f52002-07-19 01:08:59 +00002952 /* We are assuming here that shortcut_init() above didn't free and
2953 * reallocate the toggles. */
2954 enabled = ISSET(which->flag);
2955 if (which->val == TOGGLE_NOHELP_KEY || which->val == TOGGLE_WRAP_KEY)
2956 enabled = !enabled;
2957 statusbar("%s %s", which->desc,
2958 enabled ? _("enabled") : _("disabled"));
Chris Allegretta756f2202000-09-01 13:32:47 +00002959}
Chris Allegrettadab017e2002-04-23 10:56:06 +00002960#endif /* !NANO_SMALL */
Chris Allegretta756f2202000-09-01 13:32:47 +00002961
Chris Allegretta1748cd12001-01-13 17:22:54 +00002962/* This function returns the correct keystroke, given the A,B,C or D
2963 input key. This is a common sequence of many terms which send
2964 Esc-O-[A-D] or Esc-[-[A-D]. */
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002965int abcd(int input)
Chris Allegretta1748cd12001-01-13 17:22:54 +00002966{
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002967 switch (input) {
2968 case 'A':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002969 case 'a':
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002970 return KEY_UP;
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002971 case 'B':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002972 case 'b':
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002973 return KEY_DOWN;
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002974 case 'C':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002975 case 'c':
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002976 return KEY_RIGHT;
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002977 case 'D':
Chris Allegretta316e4d92001-04-28 16:31:19 +00002978 case 'd':
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00002979 return KEY_LEFT;
Chris Allegretta6b58acd2001-04-12 03:01:53 +00002980 default:
2981 return 0;
Chris Allegretta1748cd12001-01-13 17:22:54 +00002982 }
2983}
2984
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00002985int main(int argc, char *argv[])
2986{
2987 int optchr;
2988 int kbinput; /* Input from keyboard */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00002989 int startline = 0; /* Line to try and start at */
Chris Allegretta08020882001-01-29 23:37:54 +00002990 int keyhandled; /* Have we handled the keystroke yet? */
Chris Allegretta9caa1932002-02-15 20:08:05 +00002991 int modify_control_seq;
Chris Allegretta6df90f52002-07-19 01:08:59 +00002992 const shortcut *s;
Chris Allegretta6cd143d2003-01-05 23:35:44 +00002993#ifdef HAVE_GETOPT_LONG
2994 int preserveopt = 0; /* Did the cmdline include --preserve? */
2995#endif
Chris Allegretta40ecbad2002-03-06 15:27:44 +00002996#ifndef NANO_SMALL
Chris Allegretta6df90f52002-07-19 01:08:59 +00002997 const toggle *t;
Chris Allegretta40ecbad2002-03-06 15:27:44 +00002998#endif
Chris Allegretta0357c4d2001-09-19 02:59:25 +00002999
Chris Allegretta9b4055c2002-03-29 16:00:59 +00003000#ifdef _POSIX_VDISABLE
3001 struct termios term;
3002#endif
3003
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003004#ifdef HAVE_GETOPT_LONG
3005 int option_index = 0;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003006 const struct option long_options[] = {
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003007 {"help", 0, 0, 'h'},
3008#ifdef ENABLE_MULTIBUFFER
3009 {"multibuffer", 0, 0, 'F'},
3010#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00003011#ifdef ENABLE_NANORC
3012 {"ignorercfiles", 0, 0, 'I'},
3013#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003014 {"keypad", 0, 0, 'K'},
3015#ifndef DISABLE_JUSTIFY
3016 {"quotestr", 1, 0, 'Q'},
3017#endif
Chris Allegretta805c26d2000-09-06 13:39:17 +00003018#ifdef HAVE_REGEX_H
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003019 {"regexp", 0, 0, 'R'},
Chris Allegretta47805612000-07-07 02:35:34 +00003020#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003021 {"tabsize", 1, 0, 'T'},
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003022 {"version", 0, 0, 'V'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003023#ifdef ENABLE_COLOR
3024 {"syntax", 1, 0, 'Y'},
3025#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003026 {"const", 0, 0, 'c'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003027 {"nofollow", 0, 0, 'l'},
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003028#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003029 {"mouse", 0, 0, 'm'},
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003030#endif
Chris Allegrettae1f14522001-09-19 03:19:43 +00003031#ifndef DISABLE_OPERATINGDIR
3032 {"operatingdir", 1, 0, 'o'},
3033#endif
Chris Allegretta6cd143d2003-01-05 23:35:44 +00003034 {"preserve", 0, 0, 'p'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003035#ifndef DISABLE_WRAPJUSTIFY
3036 {"fill", 1, 0, 'r'},
Chris Allegretta2d7893d2001-07-11 02:08:33 +00003037#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003038#ifndef DISABLE_SPELLER
3039 {"speller", 1, 0, 's'},
3040#endif
3041 {"tempfile", 0, 0, 't'},
3042 {"view", 0, 0, 'v'},
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00003043#ifndef DISABLE_WRAPPING
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003044 {"nowrap", 0, 0, 'w'},
David Lawrence Ramsey2ab03f62002-10-17 02:19:31 +00003045#endif
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003046 {"nohelp", 0, 0, 'x'},
3047 {"suspend", 0, 0, 'z'},
Chris Allegretta3e3ae942001-09-22 19:02:04 +00003048#ifndef NANO_SMALL
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003049 {"backup", 0, 0, 'B'},
3050 {"dos", 0, 0, 'D'},
3051 {"mac", 0, 0, 'M'},
3052 {"noconvert", 0, 0, 'N'},
Chris Allegretta3e3ae942001-09-22 19:02:04 +00003053 {"smooth", 0, 0, 'S'},
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003054 {"autoindent", 0, 0, 'i'},
3055 {"cut", 0, 0, 'k'},
Chris Allegretta3e3ae942001-09-22 19:02:04 +00003056#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003057 {0, 0, 0, 0}
3058 };
3059#endif
3060
Chris Allegretta8bc03b62001-02-09 02:57:52 +00003061#ifdef ENABLE_NLS
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003062 setlocale(LC_ALL, "");
3063 bindtextdomain(PACKAGE, LOCALEDIR);
3064 textdomain(PACKAGE);
3065#endif
3066
Chris Allegretta8d8e0122001-04-18 04:28:54 +00003067#ifdef ENABLE_NANORC
Chris Allegretta1c5c3382002-07-23 00:33:07 +00003068 {
3069 /* scan through the options and handle -I/--ignorercfiles
3070 first, so that it's handled before we call do_rcfile() and
3071 read the other options; don't use getopt()/getopt_long()
3072 here, because there's no way to reset it properly
Chris Allegretta6cd143d2003-01-05 23:35:44 +00003073 afterward. Also check for the --preserve flag, and report
3074 error if people are still using --pico. */
Chris Allegretta1c5c3382002-07-23 00:33:07 +00003075 int i;
3076 for (i = 1; i < argc; i++) {
3077 if (!strcmp(argv[i], "--"))
3078 break;
3079 else if (!strcmp(argv[i], "-I"))
3080 SET(NO_RCFILE);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003081#ifdef HAVE_GETOPT_LONG
Chris Allegretta1c5c3382002-07-23 00:33:07 +00003082 else if (!strcmp(argv[i], "--ignorercfiles"))
3083 SET(NO_RCFILE);
Chris Allegretta6cd143d2003-01-05 23:35:44 +00003084 else if (!strcmp(argv[i], "--preserve"))
3085 preserveopt = 1;
3086 else if (!strcmp(argv[i], "--pico"))
3087 do_preserve_msg();
Chris Allegretta6df90f52002-07-19 01:08:59 +00003088#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00003089 }
3090 }
Chris Allegretta5beed502003-01-05 20:41:21 +00003091
Chris Allegretta1c5c3382002-07-23 00:33:07 +00003092 if (!ISSET(NO_RCFILE))
3093 do_rcfile();
David Lawrence Ramseydc60b722002-10-25 16:08:53 +00003094#else
3095#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
3096 /* if we don't have rcfile support, we're root, and
3097 --disable-wrapping-as-root is used, turn wrapping off */
3098 if (geteuid() == 0)
3099 SET(NO_WRAP);
3100#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00003101#endif /* ENABLE_NANORC */
3102
3103#ifdef HAVE_GETOPT_LONG
3104 while ((optchr = getopt_long(argc, argv, "h?BDFIKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz",
3105 long_options, &option_index)) != EOF) {
3106#else
3107 while ((optchr =
3108 getopt(argc, argv, "h?BDFIKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz")) != EOF) {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003109#endif
3110
3111 switch (optchr) {
Chris Allegretta2d7893d2001-07-11 02:08:33 +00003112
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003113 case 'h':
3114 case '?':
3115 usage();
3116 exit(0);
3117 case 'a':
3118 case 'b':
3119 case 'e':
3120 case 'f':
3121 case 'g':
3122 case 'j':
3123 /* Pico compatibility flags */
3124 break;
Chris Allegretta7004c282001-09-22 00:42:10 +00003125#ifndef NANO_SMALL
David Lawrence Ramsey5db0cdc2002-06-28 22:45:14 +00003126 case 'B':
3127 SET(BACKUP_FILE);
3128 break;
Chris Allegretta7004c282001-09-22 00:42:10 +00003129 case 'D':
3130 SET(DOS_FILE);
3131 break;
3132#endif
Chris Allegretta355fbe52001-07-14 19:32:47 +00003133#ifdef ENABLE_MULTIBUFFER
Chris Allegretta307d4c82001-07-15 20:25:33 +00003134 case 'F':
Chris Allegretta355fbe52001-07-14 19:32:47 +00003135 SET(MULTIBUFFER);
Chris Allegretta2d7893d2001-07-11 02:08:33 +00003136 break;
3137#endif
Chris Allegretta6df90f52002-07-19 01:08:59 +00003138#ifdef ENABLE_NANORC
Chris Allegretta6df90f52002-07-19 01:08:59 +00003139 case 'I':
3140 break;
3141#endif
Chris Allegretta48bd3782002-01-03 21:26:34 +00003142 case 'K':
3143 SET(ALT_KEYPAD);
3144 break;
Chris Allegretta8fa1e282001-09-22 04:20:25 +00003145#ifndef NANO_SMALL
3146 case 'M':
3147 SET(MAC_FILE);
3148 break;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003149 case 'N':
3150 SET(NO_CONVERT);
Chris Allegretta6724a7e2000-06-19 23:19:07 +00003151 break;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003152#endif
Chris Allegrettae4f940d2002-03-03 22:36:36 +00003153#ifndef DISABLE_JUSTIFY
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003154 case 'Q':
Chris Allegrettae4f940d2002-03-03 22:36:36 +00003155 quotestr = optarg;
3156 break;
Chris Allegrettae4f940d2002-03-03 22:36:36 +00003157#endif
Chris Allegretta805c26d2000-09-06 13:39:17 +00003158#ifdef HAVE_REGEX_H
Chris Allegretta9fc8d432000-07-07 01:49:52 +00003159 case 'R':
3160 SET(USE_REGEXP);
3161 break;
Chris Allegretta47805612000-07-07 02:35:34 +00003162#endif
Chris Allegretta3e3ae942001-09-22 19:02:04 +00003163#ifndef NANO_SMALL
3164 case 'S':
3165 SET(SMOOTHSCROLL);
3166 break;
3167#endif
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003168 case 'T':
Chris Allegretta6df90f52002-07-19 01:08:59 +00003169 {
3170 int i;
3171 char *first_error;
3172
3173 /* Using strtol instead of atoi lets us accept 0 while
3174 * checking other errors. */
3175 i = (int)strtol(optarg, &first_error, 10);
3176 if (errno == ERANGE || *optarg == '\0' || *first_error != '\0') {
3177 usage();
3178 exit(1);
3179 } else
3180 tabsize = i;
3181 if (tabsize <= 0) {
3182 fprintf(stderr, _("Tab size is too small for nano...\n"));
3183 exit(1);
3184 }
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003185 }
3186 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003187 case 'V':
3188 version();
3189 exit(0);
Chris Allegretta09900ff2002-05-04 04:23:30 +00003190#ifdef ENABLE_COLOR
3191 case 'Y':
3192 syntaxstr = mallocstrcpy(syntaxstr, optarg);
3193 break;
3194#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003195 case 'c':
3196 SET(CONSTUPDATE);
3197 break;
Chris Allegrettaff989832001-09-17 13:48:00 +00003198#ifndef NANO_SMALL
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003199 case 'i':
3200 SET(AUTOINDENT);
3201 break;
Chris Allegretta627de192000-07-12 02:09:17 +00003202 case 'k':
3203 SET(CUT_TO_END);
3204 break;
Chris Allegrettad19e9912000-07-12 18:14:51 +00003205#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003206 case 'l':
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003207 SET(NOFOLLOW_SYMLINKS);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003208 break;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003209#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003210 case 'm':
3211 SET(USE_MOUSE);
3212 break;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003213#endif
Chris Allegrettae1f14522001-09-19 03:19:43 +00003214#ifndef DISABLE_OPERATINGDIR
3215 case 'o':
Chris Allegretta7c27be42002-05-05 23:03:54 +00003216 operating_dir = mallocstrcpy(operating_dir, optarg);
Chris Allegrettae1f14522001-09-19 03:19:43 +00003217 break;
3218#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003219 case 'p':
Chris Allegretta6cd143d2003-01-05 23:35:44 +00003220 SET(PRESERVE);
3221#ifdef HAVE_GETOPT_LONG
3222 if (!preserveopt)
3223 do_preserve_msg();
3224#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003225 break;
Chris Allegretta6fe61492001-05-21 12:56:25 +00003226#ifndef DISABLE_WRAPJUSTIFY
Chris Allegretta6df90f52002-07-19 01:08:59 +00003227 case 'r':
3228 {
3229 int i;
3230 char *first_error;
3231
3232 /* Using strtol instead of atoi lets us accept 0 while
3233 * checking other errors. */
3234 i = (int)strtol(optarg, &first_error, 10);
3235 if (errno == ERANGE || *optarg == '\0' || *first_error != '\0') {
3236 usage();
3237 exit(1);
3238 } else
3239 wrap_at = i;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003240 }
3241 break;
Chris Allegretta6fe61492001-05-21 12:56:25 +00003242#endif
Rocco Corsiaf5c3022001-01-12 07:51:05 +00003243#ifndef DISABLE_SPELLER
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003244 case 's':
Chris Allegretta7c27be42002-05-05 23:03:54 +00003245 alt_speller = mallocstrcpy(alt_speller, optarg);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003246 break;
Rocco Corsiaf5c3022001-01-12 07:51:05 +00003247#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003248 case 't':
Chris Allegretta30885552000-07-14 01:20:12 +00003249 SET(TEMP_OPT);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003250 break;
3251 case 'v':
3252 SET(VIEW_MODE);
3253 break;
David Lawrence Ramsey95e0cf52003-01-02 16:32:20 +00003254#ifndef DISABLE_WRAPPING
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003255 case 'w':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003256 SET(NO_WRAP);
3257 break;
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003258#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003259 case 'x':
3260 SET(NO_HELP);
3261 break;
3262 case 'z':
3263 SET(SUSPEND);
3264 break;
3265 default:
3266 usage();
Chris Allegretta4da1fc62000-06-21 03:00:43 +00003267 exit(0);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003268 }
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003269 }
3270
David Lawrence Ramseye21adfa2002-09-13 18:14:04 +00003271#ifndef DISABLE_OPERATINGDIR
3272 /* Set up the operating directory. This entails chdir()ing there, so
3273 that file reads and writes will be based there. */
3274 init_operating_dir();
3275#endif
3276
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003277 /* Clear the filename we'll be using */
3278 filename = charalloc(1);
3279 filename[0] = '\0';
3280
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003281
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003282 /* See if there's a non-option in argv (first non-option is the
3283 filename, if +LINE is not given) */
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003284 if (argc > 1 && argc > optind) {
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003285 /* Look for the +line flag... */
3286 if (argv[optind][0] == '+') {
3287 startline = atoi(&argv[optind][1]);
3288 optind++;
Chris Allegretta6df90f52002-07-19 01:08:59 +00003289 if (argc > optind)
Chris Allegretta1a6e9042000-12-14 13:56:28 +00003290 filename = mallocstrcpy(filename, argv[optind]);
Chris Allegretta1a6e9042000-12-14 13:56:28 +00003291 } else
3292 filename = mallocstrcpy(filename, argv[optind]);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003293 }
3294
Chris Allegretta9b4055c2002-03-29 16:00:59 +00003295 /* First back up the old settings so they can be restored, duh */
3296 tcgetattr(0, &oldterm);
3297
3298#ifdef _POSIX_VDISABLE
3299 term = oldterm;
3300 term.c_cc[VINTR] = _POSIX_VDISABLE;
3301 term.c_cc[VQUIT] = _POSIX_VDISABLE;
3302 term.c_lflag &= ~IEXTEN;
3303 tcsetattr(0, TCSANOW, &term);
3304#endif
Chris Allegretta6ca01b12002-03-29 15:38:17 +00003305
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003306 /* now ncurses init stuff... */
3307 initscr();
3308 savetty();
3309 nonl();
3310 cbreak();
3311 noecho();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003312
3313 /* Set up some global variables */
Chris Allegretta56214c62001-09-27 02:46:53 +00003314 global_init(0);
Chris Allegretta07798352000-11-27 22:58:23 +00003315 shortcut_init(0);
Chris Allegretta9b4055c2002-03-29 16:00:59 +00003316 signal_init();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003317
3318#ifdef DEBUG
3319 fprintf(stderr, _("Main: set up windows\n"));
3320#endif
3321
Chris Allegretta2a42af12000-09-12 23:02:49 +00003322 window_init();
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003323#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegretta756f2202000-09-01 13:32:47 +00003324 mouse_init();
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003325#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003326
Chris Allegretta48bd3782002-01-03 21:26:34 +00003327 if (!ISSET(ALT_KEYPAD)) {
3328 keypad(edit, TRUE);
3329 keypad(bottomwin, TRUE);
3330 }
3331
Chris Allegretta5beed502003-01-05 20:41:21 +00003332#ifndef NANO_SMALL
3333 history_init();
3334#endif
3335
Chris Allegretta08893e02001-11-29 02:42:27 +00003336#ifdef ENABLE_COLOR
3337 do_colorinit();
Chris Allegretta08893e02001-11-29 02:42:27 +00003338#endif /* ENABLE_COLOR */
3339
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003340#ifdef DEBUG
3341 fprintf(stderr, _("Main: bottom win\n"));
3342#endif
Chris Allegretta88520c92001-05-05 17:45:54 +00003343 /* Set up bottom of window */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003344 display_main_list();
3345
3346#ifdef DEBUG
3347 fprintf(stderr, _("Main: open file\n"));
3348#endif
3349
Chris Allegretta31c76662000-11-21 06:20:20 +00003350 /* Now we check to see if argv[optind] is non-null to determine if
3351 we're dealing with a new file or not, not argc == 1... */
3352 if (argv[optind] == NULL)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003353 new_file();
3354 else
3355 open_file(filename, 0, 0);
3356
Chris Allegretta4dc03d52002-05-11 03:04:44 +00003357 titlebar(NULL);
3358
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003359 if (startline > 0)
Chris Allegretta2d7893d2001-07-11 02:08:33 +00003360 do_gotoline(startline, 0);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003361 else
Chris Allegretta234a34d2000-07-29 04:33:38 +00003362 edit_update(fileage, CENTER);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003363
Chris Allegretta08020882001-01-29 23:37:54 +00003364 /* return here after a sigwinch */
Chris Allegretta6b58acd2001-04-12 03:01:53 +00003365 sigsetjmp(jmpbuf, 1);
Chris Allegretta08020882001-01-29 23:37:54 +00003366
3367 /* Fix clobber-age */
3368 kbinput = 0;
3369 keyhandled = 0;
3370 modify_control_seq = 0;
3371
Robert Siemborski6967eec2000-07-08 14:23:32 +00003372 edit_refresh();
3373 reset_cursor();
3374
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003375 while (1) {
Chris Allegretta9239d742000-09-06 15:19:18 +00003376
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003377#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
Chris Allegretta6b58acd2001-04-12 03:01:53 +00003378 currshortcut = main_list;
Chris Allegretta6fe61492001-05-21 12:56:25 +00003379#endif
Chris Allegretta6b58acd2001-04-12 03:01:53 +00003380
Chris Allegretta9239d742000-09-06 15:19:18 +00003381#ifndef _POSIX_VDISABLE
3382 /* We're going to have to do it the old way, i.e. on cygwin */
3383 raw();
3384#endif
3385
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003386 kbinput = wgetch(edit);
Chris Allegrettac08f50d2001-01-06 18:12:43 +00003387#ifdef DEBUG
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003388 fprintf(stderr, _("AHA! %c (%d)\n"), kbinput, kbinput);
Chris Allegrettac08f50d2001-01-06 18:12:43 +00003389#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003390 if (kbinput == 27) { /* Grab Alt-key stuff first */
3391 switch (kbinput = wgetch(edit)) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003392 /* Alt-O, suddenly very important ;) */
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003393 case 'O':
Chris Allegretta16e41682000-09-11 22:33:54 +00003394 kbinput = wgetch(edit);
Chris Allegretta316e4d92001-04-28 16:31:19 +00003395 if ((kbinput <= 'D' && kbinput >= 'A') ||
3396 (kbinput <= 'd' && kbinput >= 'a'))
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003397 kbinput = abcd(kbinput);
Chris Allegretta201d9bf2001-01-14 03:17:53 +00003398 else if (kbinput <= 'z' && kbinput >= 'j')
3399 print_numlock_warning();
3400 else if (kbinput <= 'S' && kbinput >= 'P')
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003401 kbinput = KEY_F(kbinput - 79);
Chris Allegretta16e41682000-09-11 22:33:54 +00003402#ifdef DEBUG
3403 else {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003404 fprintf(stderr, _("I got Alt-O-%c! (%d)\n"),
3405 kbinput, kbinput);
3406 break;
Chris Allegretta16e41682000-09-11 22:33:54 +00003407 }
3408#endif
3409 break;
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003410 case 27:
3411 /* If we get Alt-Alt, the next keystroke should be the same as a
3412 control sequence */
3413 modify_control_seq = 1;
3414 keyhandled = 1;
3415 break;
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003416 case '[':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003417 switch (kbinput = wgetch(edit)) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003418 case '1': /* Alt-[-1-[0-5,7-9] = F1-F8 in X at least */
Chris Allegretta16e41682000-09-11 22:33:54 +00003419 kbinput = wgetch(edit);
3420 if (kbinput >= '1' && kbinput <= '5') {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003421 kbinput = KEY_F(kbinput - 48);
3422 wgetch(edit);
3423 } else if (kbinput >= '7' && kbinput <= '9') {
3424 kbinput = KEY_F(kbinput - 49);
3425 wgetch(edit);
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003426 } else if (kbinput == '~')
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003427 kbinput = KEY_HOME;
Chris Allegretta16e41682000-09-11 22:33:54 +00003428#ifdef DEBUG
3429 else {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003430 fprintf(stderr, _("I got Alt-[-1-%c! (%d)\n"),
3431 kbinput, kbinput);
3432 break;
Chris Allegretta16e41682000-09-11 22:33:54 +00003433 }
3434#endif
Chris Allegretta16e41682000-09-11 22:33:54 +00003435 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003436 case '2': /* Alt-[-2-[0,1,3,4] = F9-F12 in many terms */
Chris Allegretta16e41682000-09-11 22:33:54 +00003437 kbinput = wgetch(edit);
Chris Allegretta16e41682000-09-11 22:33:54 +00003438 switch (kbinput) {
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003439 case '0':
3440 kbinput = KEY_F(9);
3441 wgetch(edit);
3442 break;
3443 case '1':
3444 kbinput = KEY_F(10);
3445 wgetch(edit);
3446 break;
3447 case '3':
3448 kbinput = KEY_F(11);
3449 wgetch(edit);
3450 break;
3451 case '4':
3452 kbinput = KEY_F(12);
3453 wgetch(edit);
3454 break;
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003455 case '~':
3456 goto do_insertkey;
Chris Allegretta16e41682000-09-11 22:33:54 +00003457#ifdef DEBUG
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003458 default:
3459 fprintf(stderr, _("I got Alt-[-2-%c! (%d)\n"),
3460 kbinput, kbinput);
3461 break;
Chris Allegretta16e41682000-09-11 22:33:54 +00003462#endif
Chris Allegretta16e41682000-09-11 22:33:54 +00003463 }
3464 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003465 case '3': /* Alt-[-3 = Delete? */
Chris Allegretta16e41682000-09-11 22:33:54 +00003466 kbinput = NANO_DELETE_KEY;
3467 wgetch(edit);
3468 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003469 case '4': /* Alt-[-4 = End? */
Chris Allegretta16e41682000-09-11 22:33:54 +00003470 kbinput = NANO_END_KEY;
3471 wgetch(edit);
3472 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003473 case '5': /* Alt-[-5 = Page Up */
Chris Allegretta16e41682000-09-11 22:33:54 +00003474 kbinput = KEY_PPAGE;
3475 wgetch(edit);
3476 break;
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003477 case 'V': /* Alt-[-V = Page Up in Hurd Console */
Chris Allegretta7bf72742001-10-28 04:29:55 +00003478 case 'I': /* Alt-[-I = Page Up - FreeBSD Console */
3479 kbinput = KEY_PPAGE;
3480 break;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003481 case '6': /* Alt-[-6 = Page Down */
Chris Allegretta16e41682000-09-11 22:33:54 +00003482 kbinput = KEY_NPAGE;
3483 wgetch(edit);
3484 break;
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003485 case 'U': /* Alt-[-U = Page Down in Hurd Console */
Chris Allegretta7bf72742001-10-28 04:29:55 +00003486 case 'G': /* Alt-[-G = Page Down - FreeBSD Console */
3487 kbinput = KEY_NPAGE;
3488 break;
Chris Allegrettab26ecb52001-07-04 16:27:05 +00003489 case '7':
3490 kbinput = KEY_HOME;
3491 wgetch(edit);
3492 break;
3493 case '8':
3494 kbinput = KEY_END;
3495 wgetch(edit);
3496 break;
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003497 case '9': /* Alt-[-9 = Delete in Hurd Console */
3498 kbinput = KEY_DC;
3499 break;
Chris Allegretta32da4562002-01-02 15:12:21 +00003500 case '@': /* Alt-[-@ = Insert in Hurd Console */
3501 case 'L': /* Alt-[-L = Insert - FreeBSD Console */
3502 goto do_insertkey;
3503 case '[': /* Alt-[-[-[A-E], F1-F5 in Linux console */
Chris Allegretta16e41682000-09-11 22:33:54 +00003504 kbinput = wgetch(edit);
Chris Allegrettab26ecb52001-07-04 16:27:05 +00003505 if (kbinput >= 'A' && kbinput <= 'E')
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003506 kbinput = KEY_F(kbinput - 64);
Chris Allegretta16e41682000-09-11 22:33:54 +00003507 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003508 case 'A':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003509 case 'B':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003510 case 'C':
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003511 case 'D':
Chris Allegretta316e4d92001-04-28 16:31:19 +00003512 case 'a':
3513 case 'b':
3514 case 'c':
3515 case 'd':
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003516 kbinput = abcd(kbinput);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003517 break;
3518 case 'H':
3519 kbinput = KEY_HOME;
3520 break;
3521 case 'F':
Chris Allegretta9b8b3702001-11-19 05:09:15 +00003522 case 'Y': /* End Key in Hurd Console */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003523 kbinput = KEY_END;
3524 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003525 default:
3526#ifdef DEBUG
3527 fprintf(stderr, _("I got Alt-[-%c! (%d)\n"),
3528 kbinput, kbinput);
3529#endif
3530 break;
3531 }
3532 break;
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003533
Chris Allegretta355fbe52001-07-14 19:32:47 +00003534#ifdef ENABLE_MULTIBUFFER
Chris Allegretta819e3db2001-07-11 02:37:19 +00003535 case NANO_OPENPREV_KEY:
Chris Allegretta180a5692002-01-02 14:30:33 +00003536 case NANO_OPENPREV_ALTKEY:
Chris Allegretta6df90f52002-07-19 01:08:59 +00003537 open_prevfile_void();
Chris Allegretta819e3db2001-07-11 02:37:19 +00003538 keyhandled = 1;
3539 break;
3540 case NANO_OPENNEXT_KEY:
Chris Allegretta180a5692002-01-02 14:30:33 +00003541 case NANO_OPENNEXT_ALTKEY:
Chris Allegretta6df90f52002-07-19 01:08:59 +00003542 open_nextfile_void();
Chris Allegretta819e3db2001-07-11 02:37:19 +00003543 keyhandled = 1;
3544 break;
Chris Allegretta9cf9e062001-07-11 12:06:13 +00003545#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003546 default:
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003547 /* Check for the altkey defs.... */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003548 for (s = main_list; s != NULL; s = s->next)
3549 if (kbinput == s->altval ||
Chris Allegretta6232d662002-05-12 19:52:15 +00003550 kbinput == s->altval - 32) {
3551 if (ISSET(VIEW_MODE) && !s->viewok)
3552 print_view_warning();
3553 else
3554 s->func();
3555 keyhandled = 1;
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003556 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003557 }
Chris Allegretta756f2202000-09-01 13:32:47 +00003558#ifndef NANO_SMALL
3559 /* And for toggle switches */
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003560 for (t = toggles; t != NULL && !keyhandled; t = t->next)
3561 if (kbinput == t->val ||
3562 (t->val > 'a' &&
3563 kbinput == t->val - 32)) {
3564 do_toggle(t);
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003565 keyhandled = 1;
3566 break;
Chris Allegretta756f2202000-09-01 13:32:47 +00003567 }
3568#endif
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003569#ifdef DEBUG
3570 fprintf(stderr, _("I got Alt-%c! (%d)\n"), kbinput,
3571 kbinput);
3572#endif
3573 break;
3574 }
3575 }
Chris Allegrettacf287c82002-07-20 13:57:41 +00003576 /* If modify_control_seq is set, we received an Alt-Alt
3577 sequence before this, so we make this key a control sequence
3578 by subtracting 32, 64, or 96, depending on its value. */
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003579 if (!keyhandled && modify_control_seq) {
Chris Allegrettacf287c82002-07-20 13:57:41 +00003580 if (kbinput == ' ')
3581 kbinput -= 32;
3582 else if (kbinput >= 'A' && kbinput < 'a')
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003583 kbinput -= 64;
3584 else if (kbinput >= 'a' && kbinput <= 'z')
3585 kbinput -= 96;
3586
3587 modify_control_seq = 0;
3588 }
3589
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003590 /* Look through the main shortcut list to see if we've hit a
3591 shortcut key */
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00003592
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003593#if !defined(DISABLE_BROWSER) || !defined (DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003594 for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
Chris Allegrettaf5de33a2002-02-27 04:14:16 +00003595#else
3596 for (s = main_list; s != NULL && !keyhandled; s = s->next) {
3597#endif
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003598 if (kbinput == s->val ||
3599 (s->misc1 && kbinput == s->misc1) ||
3600 (s->misc2 && kbinput == s->misc2)) {
3601 if (ISSET(VIEW_MODE) && !s->viewok)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003602 print_view_warning();
3603 else
Chris Allegrettaa8c22572002-02-15 19:17:02 +00003604 s->func();
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003605 keyhandled = 1;
David Lawrence Ramseyad40fdb2002-09-06 20:35:28 +00003606 /* rarely, the value of s can change after s->func(),
3607 leading to problems; get around this by breaking out
3608 explicitly once we successfully handle a shortcut */
3609 break;
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003610 }
3611 }
Chris Allegrettae42df732002-10-15 00:27:55 +00003612
3613#ifdef _POSIX_VDISABLE
3614 /* Don't even think about changing this string */
3615 if (kbinput == 19)
3616 statusbar(_("XOFF ignored, mumble mumble."));
Chris Allegretta6cd143d2003-01-05 23:35:44 +00003617 if (kbinput == 17)
3618 statusbar(_("XON ignored, mumble mumble."));
Chris Allegrettae42df732002-10-15 00:27:55 +00003619#endif
Chris Allegretta51b3eec2000-12-18 02:23:50 +00003620 /* If we're in raw mode or using Alt-Alt-x, we have to catch
3621 Control-S and Control-Q */
Chris Allegretta9239d742000-09-06 15:19:18 +00003622 if (kbinput == 17 || kbinput == 19)
3623 keyhandled = 1;
3624
Chris Allegretta9caa1932002-02-15 20:08:05 +00003625 /* Catch ^Z by hand when triggered also
3626 407 == ^Z in Linux console when keypad() is used? */
3627 if (kbinput == 26 || kbinput == 407) {
Chris Allegretta9239d742000-09-06 15:19:18 +00003628 if (ISSET(SUSPEND))
3629 do_suspend(0);
3630 keyhandled = 1;
3631 }
Chris Allegretta9239d742000-09-06 15:19:18 +00003632
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003633 /* Hack, make insert key do something useful, like insert file */
3634 if (kbinput == KEY_IC) {
3635 do_insertkey:
3636
3637#ifdef ENABLE_MULTIBUFFER
Chris Allegretta32da4562002-01-02 15:12:21 +00003638 /* do_insertfile_void() contains the logic needed to
3639 handle view mode with the view mode/multibuffer
3640 exception, so use it here */
3641 do_insertfile_void();
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003642#else
Chris Allegretta6df90f52002-07-19 01:08:59 +00003643 if (!ISSET(VIEW_MODE))
3644 do_insertfile_void();
3645 else
3646 print_view_warning();
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003647#endif
3648
Chris Allegretta1c27d3e2001-10-02 02:56:45 +00003649 keyhandled = 1;
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003650 }
3651
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003652 /* Last gasp, stuff that's not in the main lists */
3653 if (!keyhandled)
3654 switch (kbinput) {
David Lawrence Ramsey9b13ff32002-12-22 16:30:00 +00003655#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003656 case KEY_MOUSE:
3657 do_mouse();
3658 break;
3659#endif
Chris Allegrettaad3f4782001-10-02 03:54:13 +00003660
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003661 case 0: /* Erg */
Chris Allegrettace78c1e2001-09-23 01:18:03 +00003662 case -1: /* Stuff that we don't want to do squat */
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003663 case 410: /* Must ignore this, it gets sent when we resize */
Chris Allegrettab3655b42001-10-22 03:15:31 +00003664 case 29: /* Ctrl-] */
Chris Allegrettaad1dacc2000-09-21 04:25:45 +00003665#ifdef PDCURSES
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003666 case 541: /* ???? */
3667 case 542: /* Control and alt in Windows *shrug* */
Chris Allegretta72623582000-11-29 23:43:28 +00003668 case 543: /* Right ctrl key */
Chris Allegrettaad1dacc2000-09-21 04:25:45 +00003669 case 544:
Chris Allegretta72623582000-11-29 23:43:28 +00003670 case 545: /* Right alt key */
Chris Allegrettaad1dacc2000-09-21 04:25:45 +00003671#endif
Chris Allegrettabd9e7c32000-10-26 01:44:42 +00003672
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003673 break;
3674 default:
3675#ifdef DEBUG
David Lawrence Ramseyf21cd102002-06-13 00:40:19 +00003676 fprintf(stderr, _("I got %c (%d)!\n"), kbinput, kbinput);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003677#endif
3678 /* We no longer stop unhandled sequences so that people with
3679 odd character sets can type... */
3680
3681 if (ISSET(VIEW_MODE)) {
3682 print_view_warning();
3683 break;
3684 }
3685 do_char(kbinput);
3686 }
Chris Allegretta7fdbd052001-10-02 00:55:38 +00003687 if (ISSET(DISABLE_CURPOS))
3688 UNSET(DISABLE_CURPOS);
3689 else if (ISSET(CONSTUPDATE))
Chris Allegretta2084acc2001-11-29 03:43:08 +00003690 do_cursorpos(1);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003691
3692 reset_cursor();
3693 wrefresh(edit);
3694 keyhandled = 0;
3695 }
3696
3697 getchar();
3698 finish(0);
Chris Allegrettaa2ea1932000-06-06 05:53:49 +00003699}