hblank[COLS] = '\0';
}
+void window_init(void)
+{
+ if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
+ die_too_small();
+
+ /* Set up the main text window */
+ edit = newwin(editwinrows, COLS, 2, 0);
+
+ /* And the other windows */
+ topwin = newwin(2, COLS, 0, 0);
+ bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
+
+#ifdef PDCURSES
+ /* Oops, I guess we need this again.
+ Moved here so the keypad still works after a Meta-X, for example */
+ keypad(edit, TRUE);
+ keypad(bottomwin, TRUE);
+#endif
+}
+
+void mouse_init(void)
+{
+#ifndef DISABLE_MOUSE
+#ifdef NCURSES_MOUSE_VERSION
+ if (ISSET(USE_MOUSE)) {
+ keypad_on(edit, 1);
+ keypad_on(bottomwin, 1);
+
+ mousemask(BUTTON1_RELEASED, NULL);
+ mouseinterval(50);
+ } else
+ mousemask(0, NULL);
+#endif
+#endif
+}
+
+#ifndef DISABLE_HELP
+/* This function allocates help_text, and stores the help string in it.
+ * help_text should be NULL initially. */
+void help_init(void)
+{
+ size_t allocsize = 1; /* space needed for help_text */
+ char *ptr = NULL;
+#ifndef NANO_SMALL
+ const toggle *t;
+#endif
+ const shortcut *s;
+
+ /* First set up the initial help text for the current function */
+ if (currshortcut == whereis_list || currshortcut == replace_list
+ || currshortcut == replace_list_2)
+ ptr = _("Search Command Help Text\n\n "
+ "Enter the words or characters you would like to search "
+ "for, then hit enter. If there is a match for the text you "
+ "entered, the screen will be updated to the location of the "
+ "nearest match for the search string.\n\n "
+ "If using Pico Mode via the -p or --pico flags, the "
+ "Meta-P toggle, or a nanorc file, the previous search "
+ "string will be shown in brackets after the Search: prompt. "
+ "Hitting Enter without entering any text will perform the "
+ "previous search. Otherwise, the previous string will be "
+ "placed before the cursor, and can be edited or deleted "
+ "before hitting enter.\n\n The following function keys are "
+ "available in Search mode:\n\n");
+ else if (currshortcut == goto_list)
+ ptr = _("Go To Line Help Text\n\n "
+ "Enter the line number that you wish to go to and hit "
+ "Enter. If there are fewer lines of text than the "
+ "number you entered, you will be brought to the last line "
+ "of the file.\n\n The following function keys are "
+ "available in Go To Line mode:\n\n");
+ else if (currshortcut == insertfile_list)
+ ptr = _("Insert File Help Text\n\n "
+ "Type in the name of a file to be inserted into the current "
+ "file buffer at the current cursor location.\n\n "
+ "If you have compiled nano with multiple file buffer "
+ "support, and enable multiple buffers with the -F "
+ "or --multibuffer command line flags, the Meta-F toggle, or "
+ "a nanorc file, inserting a file will cause it to be "
+ "loaded into a separate buffer (use Meta-< and > to switch "
+ "between file buffers).\n\n If you need another blank "
+ "buffer, do not enter any filename, or type in a "
+ "nonexistent filename at the prompt and press "
+ "Enter.\n\n The following function keys are "
+ "available in Insert File mode:\n\n");
+ else if (currshortcut == writefile_list)
+ ptr = _("Write File Help Text\n\n "
+ "Type the name that you wish to save the current file "
+ "as and hit Enter to save the file.\n\n If you have "
+ "selected text with Ctrl-^, you will be prompted to "
+ "save only the selected portion to a separate file. To "
+ "reduce the chance of overwriting the current file with "
+ "just a portion of it, the current filename is not the "
+ "default in this mode.\n\n The following function keys "
+ "are available in Write File mode:\n\n");
+#ifndef DISABLE_BROWSER
+ else if (currshortcut == browser_list)
+ ptr = _("File Browser Help Text\n\n "
+ "The file browser is used to visually browse the "
+ "directory structure to select a file for reading "
+ "or writing. You may use the arrow keys or Page Up/"
+ "Down to browse through the files, and S or Enter to "
+ "choose the selected file or enter the selected "
+ "directory. To move up one level, select the directory "
+ "called \"..\" at the top of the file list.\n\n The "
+ "following function keys are available in the file "
+ "browser:\n\n");
+ else if (currshortcut == gotodir_list)
+ ptr = _("Browser Go To Directory Help Text\n\n "
+ "Enter the name of the directory you would like to "
+ "browse to.\n\n If tab completion has not been disabled, "
+ "you can use the TAB key to (attempt to) automatically "
+ "complete the directory name.\n\n The following function "
+ "keys are available in Browser Go To Directory mode:\n\n");
+#endif
+ else if (currshortcut == spell_list)
+ ptr = _("Spell Check Help Text\n\n "
+ "The spell checker checks the spelling of all text "
+ "in the current file. When an unknown word is "
+ "encountered, it is highlighted and a replacement can "
+ "be edited. It will then prompt to replace every "
+ "instance of the given misspelled word in the "
+ "current file.\n\n The following other functions are "
+ "available in Spell Check mode:\n\n");
+#ifndef NANO_SMALL
+ else if (currshortcut == extcmd_list)
+ ptr = _("External Command Help Text\n\n "
+ "This menu allows you to insert the output of a command "
+ "run by the shell into the current buffer (or a new "
+ "buffer in multibuffer mode).\n\n The following keys are "
+ "available in this mode:\n\n");
+#endif
+ else /* Default to the main help list */
+ ptr = _(" nano help text\n\n "
+ "The nano editor is designed to emulate the functionality and "
+ "ease-of-use of the UW Pico text editor. There are four main "
+ "sections of the editor: The top line shows the program "
+ "version, the current filename being edited, and whether "
+ "or not the file has been modified. Next is the main editor "
+ "window showing the file being edited. The status line is "
+ "the third line from the bottom and shows important messages. "
+ "The bottom two lines show the most commonly used shortcuts "
+ "in the editor.\n\n "
+ "The notation for shortcuts is as follows: Control-key "
+ "sequences are notated with a caret (^) symbol and are entered "
+ "with the Control (Ctrl) key. Escape-key sequences are notated "
+ "with the Meta (M) symbol and can be entered using either the "
+ "Esc, Alt or Meta key depending on your keyboard setup. The "
+ "following keystrokes are available in the main editor window. "
+ "Alternative keys are shown in parentheses:\n\n");
+
+ allocsize += strlen(ptr);
+
+ /* The space needed for the shortcut lists, at most COLS characters,
+ * plus '\n'. */
+ allocsize += (COLS + 1) * length_of_list(currshortcut);
+
+#ifndef NANO_SMALL
+ /* If we're on the main list, we also count the toggle help text.
+ * Each line has "M-%c\t\t\t", which fills 24 columns, plus at most
+ * COLS - 24 characters, plus '\n'.*/
+ if (currshortcut == main_list)
+ for (t = toggles; t != NULL; t = t->next)
+ allocsize += COLS - 17;
+#endif /* !NANO_SMALL */
+
+ /* help_text has been freed and set to NULL unless the user resized
+ * while in the help screen. */
+ free(help_text);
+
+ /* Allocate space for the help text */
+ help_text = charalloc(allocsize);
+
+ /* Now add the text we want */
+ strcpy(help_text, ptr);
+ ptr = help_text + strlen(help_text);
+
+ /* Now add our shortcut info */
+ for (s = currshortcut; s != NULL; s = s->next) {
+ /* true if the character in s->altval is shown in first column */
+ int meta_shortcut = 0;
+
+ if (s->val > 0 && s->val < 32)
+ ptr += sprintf(ptr, "^%c", s->val + 64);
+#ifndef NANO_SMALL
+ else if (s->val == NANO_CONTROL_SPACE)
+ ptr += sprintf(ptr, "^%.6s", _("Space"));
+ else if (s->altval == NANO_ALT_SPACE) {
+ meta_shortcut = 1;
+ ptr += sprintf(ptr, "M-%.5s", _("Space"));
+ }
+#endif
+ else if (s->altval > 0) {
+ meta_shortcut = 1;
+ ptr += sprintf(ptr, "M-%c", s->altval -
+ (('A' <= s->altval && s->altval <= 'Z') ||
+ 'a' <= s->altval ? 32 : 0));
+ }
+ /* Hack */
+ else if (s->val >= 'a') {
+ meta_shortcut = 1;
+ ptr += sprintf(ptr, "M-%c", s->val - 32);
+ }
+
+ *(ptr++) = '\t';
+
+ if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
+ ptr += sprintf(ptr, "(F%d)", s->misc1 - KEY_F0);
+
+ *(ptr++) = '\t';
+
+ if (!meta_shortcut && s->altval > 0)
+ ptr += sprintf(ptr, "(M-%c)", s->altval -
+ (('A' <= s->altval && s->altval <= 'Z') || 'a' <= s->altval
+ ? 32 : 0));
+
+ *(ptr++) = '\t';
+
+ assert(s->help != NULL);
+ ptr += sprintf(ptr, "%.*s\n", COLS - 24, s->help);
+ }
+
+#ifndef NANO_SMALL
+ /* And the toggles... */
+ if (currshortcut == main_list)
+ for (t = toggles; t != NULL; t = t->next) {
+ ptr += sprintf(ptr, "M-%c\t\t\t", t->val - 32);
+ assert(t->desc != NULL);
+ ptr += sprintf(ptr, _("%.*s enable/disable\n"), COLS - 24, t->desc);
+ }
+#endif /* !NANO_SMALL */
+
+ /* If all went well, we didn't overwrite the allocated space for
+ help_text. */
+ assert(strlen(help_text) < allocsize);
+}
+#endif
+
+/* Create a new filestruct node. Note that we specifically do not set
+ * prevnode->next equal to the new line. */
+filestruct *make_new_node(filestruct *prevnode)
+{
+ filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
+
+ newnode->data = NULL;
+ newnode->prev = prevnode;
+ newnode->next = NULL;
+ newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1;
+
+ return newnode;
+}
+
/* Make a copy of a node to a pointer (space will be malloc()ed). */
filestruct *copy_node(const filestruct *src)
{
return dst;
}
+/* Splice a node into an existing filestruct. */
+void splice_node(filestruct *begin, filestruct *newnode, filestruct *end)
+{
+ if (newnode != NULL) {
+ newnode->next = end;
+ newnode->prev = begin;
+ }
+ if (begin != NULL)
+ begin->next = newnode;
+ if (end != NULL)
+ end->prev = newnode;
+}
+
/* Unlink a node from the rest of the filestruct. */
void unlink_node(const filestruct *fileptr)
{
* strings to translate and takes out the parts that shouldn't be
* translatable (the flag names). */
void print1opt(const char *shortflag, const char *longflag,
- const char *desc)
+ const char *desc)
{
printf(" %s\t", shortflag);
if (strlen(shortflag) < 8)
(" Email: nano@nano-editor.org Web: http://www.nano-editor.org"));
printf(_("\n Compiled options:"));
+#ifdef DEBUG
+ printf(" --enable-debug");
+#endif
#ifdef NANO_EXTRA
printf(" --enable-extra");
#endif
-#ifdef ENABLE_MULTIBUFFER
- printf(" --enable-multibuffer");
-#endif
-#ifdef ENABLE_NANORC
- printf(" --enable-nanorc");
-#endif
-#ifdef ENABLE_COLOR
- printf(" --enable-color");
-#endif
-
#ifdef NANO_SMALL
printf(" --enable-tiny");
#else
#ifdef DISABLE_BROWSER
printf(" --disable-browser");
#endif
-#ifdef DISABLE_TABCOMP
- printf(" --disable-tabcomp");
+#ifdef DISABLE_HELP
+ printf(" --disable-help");
#endif
#ifdef DISABLE_JUSTIFY
printf(" --disable-justify");
#endif
-#ifdef DISABLE_SPELLER
- printf(" --disable-speller");
-#endif
-#ifdef DISABLE_HELP
- printf(" --disable-help");
-#endif
#ifdef DISABLE_MOUSE
printf(" --disable-mouse");
#endif
#ifdef DISABLE_OPERATINGDIR
printf(" --disable-operatingdir");
#endif
-#endif /* NANO_SMALL */
-#ifdef DISABLE_WRAPPING
- printf(" --disable-wrapping");
+#ifdef DISABLE_SPELLER
+ printf(" --disable-speller");
+#endif
+#ifdef DISABLE_TABCOMP
+ printf(" --disable-tabcomp");
+#endif
+#endif /* NANO_SMALL */
+#ifdef DISABLE_WRAPPING
+ printf(" --disable-wrapping");
+#endif
+#ifdef ENABLE_COLOR
+ printf(" --enable-color");
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ printf(" --enable-multibuffer");
+#endif
+#ifdef ENABLE_NANORC
+ printf(" --enable-nanorc");
+#endif
+#ifdef ENABLE_UNDO
+ printf(" --enable-undo");
#endif
#ifdef USE_SLANG
printf(" --with-slang");
printf("\n");
}
-/* Create a new filestruct node. Note that we specifically do not set
- * prevnode->next equal to the new line. */
-filestruct *make_new_node(filestruct *prevnode)
-{
- filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
-
- newnode->data = NULL;
- newnode->prev = prevnode;
- newnode->next = NULL;
- newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1;
-
- return newnode;
-}
-
-/* Splice a node into an existing filestruct. */
-void splice_node(filestruct *begin, filestruct *newnode, filestruct *end)
-{
- if (newnode != NULL) {
- newnode->next = end;
- newnode->prev = begin;
- }
- if (begin != NULL)
- begin->next = newnode;
- if (end != NULL)
- end->prev = newnode;
-}
-
-int do_mark(void)
+/* Stuff we do when we abort from programs and want to clean up the
+ * screen. This doesn't do much right now. */
+void do_early_abort(void)
{
-#ifdef NANO_SMALL
- nano_disabled_msg();
-#else
- if (!ISSET(MARK_ISSET)) {
- statusbar(_("Mark Set"));
- SET(MARK_ISSET);
- mark_beginbuf = current;
- mark_beginx = current_x;
- } else {
- statusbar(_("Mark UNset"));
- UNSET(MARK_ISSET);
- edit_refresh();
- }
-#endif
- return 1;
+ blank_statusbar_refresh();
}
int no_help(void)
}
#endif
-/* The user typed a printable character; add it to the edit buffer. */
-void do_char(char ch)
-{
- size_t current_len = strlen(current->data);
-#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
- int refresh = 0;
- /* Do we have to run edit_refresh(), or can we get away with
- * update_line()? */
-#endif
-
- /* magic-line: when a character is inserted on the current magic line,
- * it means we need a new one! */
- if (filebot == current && current->data[0] == '\0') {
- new_magicline();
- fix_editbot();
- }
-
- /* more dangerousness fun =) */
- current->data = nrealloc(current->data, current_len + 2);
- assert(current_x <= current_len);
- memmove(¤t->data[current_x + 1],
- ¤t->data[current_x],
- current_len - current_x + 1);
- current->data[current_x] = ch;
- totsize++;
- set_modified();
-
#ifndef NANO_SMALL
- /* note that current_x has not yet been incremented */
- if (current == mark_beginbuf && current_x < mark_beginx)
- mark_beginx++;
-#endif
-
- do_right();
-
-#ifndef DISABLE_WRAPPING
- if (!ISSET(NO_WRAP) && ch != '\t')
- refresh = do_wrap(current);
-#endif
-
-#ifdef ENABLE_COLOR
- refresh = 1;
-#endif
-
-#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
- if (refresh)
- edit_refresh();
-#endif
-
- check_statblank();
- UNSET(KEEP_CUTBUFFER);
-}
+static int pid; /* This is the PID of the newly forked process
+ * below. It must be global since the signal
+ * handler needs it. */
-/* Someone hits return *gasp!* */
-int do_enter(void)
+RETSIGTYPE cancel_fork(int signal)
{
- filestruct *newnode;
- char *tmp;
-
- newnode = make_new_node(current);
- assert(current != NULL && current->data != NULL);
- tmp = ¤t->data[current_x];
-
-#ifndef NANO_SMALL
- /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
- if (ISSET(AUTOINDENT)) {
- int extra = 0;
- const char *spc = current->data;
-
- while (*spc == ' ' || *spc == '\t') {
- extra++;
- spc++;
- }
- /* If current_x < extra, then we are breaking the line in the
- * indentation. Autoindenting should add only current_x
- * characters of indentation. */
- if (current_x < extra)
- extra = current_x;
- else
- current_x = extra;
- totsize += extra;
-
- newnode->data = charalloc(strlen(tmp) + extra + 1);
- strncpy(newnode->data, current->data, extra);
- strcpy(&newnode->data[extra], tmp);
- } else
-#endif
- {
- current_x = 0;
- newnode->data = charalloc(strlen(tmp) + 1);
- strcpy(newnode->data, tmp);
- }
- *tmp = '\0';
-
- if (current->next == NULL) {
- filebot = newnode;
- editbot = newnode;
- }
- splice_node(current, newnode, current->next);
-
- totsize++;
- renumber(current);
- current = newnode;
- align(¤t->data);
-
- /* The logic here is as follows:
- * -> If we are at the bottom of the buffer, we want to recenter
- * (read: rebuild) the screen and forcibly move the cursor.
- * -> otherwise, we want simply to redraw the screen and update
- * where we think the cursor is.
- */
- if (current_y == editwinrows - 1) {
- edit_update(current, CENTER);
- reset_cursor();
- } else {
- current_y++;
- edit_refresh();
- update_cursor();
- }
-
- totlines++;
- set_modified();
-
- placewewant = xplustabs();
- return 1;
+ if (kill(pid, SIGKILL)==-1) nperror("kill");
}
-#ifndef NANO_SMALL
-int do_next_word(void)
+int open_pipe(const char *command)
{
- filestruct *old = current;
-
- assert(current != NULL && current->data != NULL);
-
- /* Skip letters in this word first. */
- while (current->data[current_x] != '\0' &&
- isalnum((int)current->data[current_x]))
- current_x++;
-
- for (; current != NULL; current = current->next) {
- while (current->data[current_x] != '\0' &&
- !isalnum((int)current->data[current_x]))
- current_x++;
-
- if (current->data[current_x] != '\0')
- break;
-
- current_x = 0;
- }
- if (current == NULL)
- current = filebot;
+ int fd[2];
+ FILE *f;
+ struct sigaction oldaction, newaction;
+ /* original and temporary handlers for SIGINT */
+#ifdef _POSIX_VDISABLE
+ struct termios term, newterm;
+#endif /* _POSIX_VDISABLE */
+ int cancel_sigs = 0;
+ /* cancel_sigs==1 means that sigaction failed without changing the
+ * signal handlers. cancel_sigs==2 means the signal handler was
+ * changed, but the tcsetattr didn't succeed.
+ * I use this variable since it is important to put things back when
+ * we finish, even if we get errors. */
- placewewant = xplustabs();
+ /* Make our pipes. */
- if (current->lineno >= editbot->lineno) {
- /* If we're on the last line, don't center the screen. */
- if (current->lineno == filebot->lineno)
- edit_refresh();
- else
- edit_update(current, CENTER);
- }
- else {
- /* If we've jumped lines, refresh the old line. We can't just
- use current->prev here, because we may have skipped over some
- blank lines, in which case the previous line is the wrong
- one. */
- if (current != old) {
- update_line(old, 0);
- /* If the mark was set, then the lines between old and
- current have to be updated too. */
- if (ISSET(MARK_ISSET)) {
- while (old->next != current) {
- old = old->next;
- update_line(old, 0);
- }
- }
- }
- update_line(current, current_x);
+ if (pipe(fd) == -1) {
+ statusbar(_("Could not pipe"));
+ return 1;
}
- return 0;
-}
-
-/* The same thing for backwards. */
-int do_prev_word(void)
-{
- filestruct *old = current;
-
- assert(current != NULL);
-
- /* Skip letters in this word first. */
- while (current_x >= 0 && isalnum((int)current->data[current_x]))
- current_x--;
- for (; current != NULL; current = current->prev) {
- while (current_x >= 0 && !isalnum((int)current->data[current_x]))
- current_x--;
+ /* Fork a child. */
- if (current_x >= 0)
- break;
+ if ((pid = fork()) == 0) {
+ close(fd[0]);
+ dup2(fd[1], fileno(stdout));
+ dup2(fd[1], fileno(stderr));
+ /* If execl() returns at all, there was an error. */
+
+ execl("/bin/sh","sh","-c",command,0);
+ exit(0);
+ }
- if (current->prev != NULL)
- current_x = strlen(current->prev->data);
+ /* Else continue as parent. */
+
+ close(fd[1]);
+
+ if (pid == -1) {
+ close(fd[0]);
+ statusbar(_("Could not fork"));
+ return 1;
}
- if (current != NULL) {
- while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
- current_x--;
+ /* Before we start reading the forked command's output, we set
+ * things up so that ^C will cancel the new process. */
+ if (sigaction(SIGINT, NULL, &newaction)==-1) {
+ cancel_sigs = 1;
+ nperror("sigaction");
} else {
- current = fileage;
- current_x = 0;
+ newaction.sa_handler = cancel_fork;
+ if (sigaction(SIGINT, &newaction, &oldaction)==-1) {
+ cancel_sigs = 1;
+ nperror("sigaction");
+ }
}
+ /* Note that now oldaction is the previous SIGINT signal handler,
+ * to be restored later. */
- placewewant = xplustabs();
-
- if (current->lineno <= edittop->lineno) {
- /* If we're on the first line, don't center the screen. */
- if (current->lineno == fileage->lineno)
- edit_refresh();
- else
- edit_update(current, CENTER);
+ /* See if the platform supports disabling individual control
+ * characters. */
+#ifdef _POSIX_VDISABLE
+ if (!cancel_sigs && tcgetattr(0, &term) == -1) {
+ cancel_sigs = 2;
+ nperror("tcgetattr");
}
- else {
- /* If we've jumped lines, refresh the old line. We can't just
- use current->prev here, because we may have skipped over some
- blank lines, in which case the previous line is the wrong
- one. */
- if (current != old) {
- update_line(old, 0);
- /* If the mark was set, then the lines between old and
- current have to be updated too. */
- if (ISSET(MARK_ISSET)) {
- while (old->prev != current) {
- old = old->prev;
- update_line(old, 0);
- }
- }
+ if (!cancel_sigs) {
+ newterm = term;
+ /* Grab oldterm's VINTR key :-) */
+ newterm.c_cc[VINTR] = oldterm.c_cc[VINTR];
+ if (tcsetattr(0, TCSANOW, &newterm) == -1) {
+ cancel_sigs = 2;
+ nperror("tcsetattr");
}
- update_line(current, current_x);
}
+#endif /* _POSIX_VDISABLE */
+
+ f = fdopen(fd[0], "rb");
+ if (!f)
+ nperror("fdopen");
+
+ read_file(f, "stdin", 0);
+ /* if multibuffer mode is on, we could be here in view mode; if so,
+ don't set the modification flag */
+ if (!ISSET(VIEW_MODE))
+ set_modified();
+
+ if (wait(NULL) == -1)
+ nperror("wait");
+
+#ifdef _POSIX_VDISABLE
+ if (!cancel_sigs && tcsetattr(0, TCSANOW, &term) == -1)
+ nperror("tcsetattr");
+#endif /* _POSIX_VDISABLE */
+
+ if (cancel_sigs!=1 && sigaction(SIGINT, &oldaction, NULL) == -1)
+ nperror("sigaction");
+
return 0;
}
-#endif /* !NANO_SMALL */
+#endif /* NANO_SMALL */
-#ifndef DISABLE_WRAPPING
-/* We wrap the given line. Precondition: we assume the cursor has been
- * moved forward since the last typed character. Return value:
- * whether we wrapped. */
-int do_wrap(filestruct *inptr)
+#ifndef DISABLE_MOUSE
+#ifdef NCURSES_MOUSE_VERSION
+void do_mouse(void)
{
- size_t len = strlen(inptr->data); /* length of the line we wrap */
- int i = 0; /* generic loop variable */
- int wrap_loc = -1; /* index of inptr->data where we wrap */
- int word_back = -1;
-#ifndef NANO_SMALL
- const char *indentation = NULL;
- /* indentation to prepend to the new line */
- int indent_len = 0; /* strlen(indentation) */
-#endif
- const char *after_break; /* text after the wrap point */
- int after_break_len; /* strlen(after_break) */
- int wrapping = 0; /* do we prepend to the next line? */
- const char *wrap_line = NULL;
- /* the next line, minus indentation */
- int wrap_line_len = 0; /* strlen(wrap_line) */
- char *newline = NULL; /* the line we create */
- int new_line_len = 0; /* eventual length of newline */
+ MEVENT mevent;
+ int currslen;
+ const shortcut *s = currshortcut;
-/* There are three steps. First, we decide where to wrap. Then, we
- * create the new wrap line. Finally, we clean up. */
+ if (getmouse(&mevent) == ERR)
+ return;
-/* Step 1, finding where to wrap. We are going to replace a white-space
- * character with a new-line. In this step, we set wrap_loc as the
- * location of this replacement.
- *
- * Where should we break the line? We need the last "legal wrap point"
- * such that the last word before it ended at or before fill. If there
- * is no such point, we settle for the first legal wrap point.
- *
- * A "legal wrap point" is a white-space character that is not the last
- * typed character and is not followed by white-space.
- *
- * If there is no legal wrap point or we found the last character of the
- * line, we should return without wrapping.
- *
- * Note that the initial indentation does not count as a legal wrap
- * point if we are going to auto-indent!
- *
- * Note that the code below could be optimised, by not calling strnlenpt()
- * so often. */
+ /* If mouse not in edit or bottom window, return */
+ if (wenclose(edit, mevent.y, mevent.x)) {
-#ifndef NANO_SMALL
- if (ISSET(AUTOINDENT))
- i = indent_length(inptr->data);
-#endif
- wrap_line = inptr->data + i;
- for(; i < len; i++, wrap_line++) {
- /* record where the last word ended */
- if (*wrap_line != ' ' && *wrap_line != '\t')
- word_back = i;
- /* if we have found a "legal wrap point" and the current word
- * extends too far, then we stop */
- if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
- break;
- /* we record the latest "legal wrap point" */
- if (i != current_x - 1 && word_back != i &&
- wrap_line[1] != ' ' && wrap_line[1] != '\t')
- wrap_loc = i;
- }
- if (wrap_loc < 0 || i == len)
- return 0;
+ /* Don't let people screw with the marker when they're in a
+ * subfunction. */
+ if (currshortcut != main_list)
+ return;
-/* Step 2, making the new wrap line. It will consist of indentation +
- * after_break + " " + wrap_line (although indentation and wrap_line are
- * conditional on flags and #defines). */
+ /* Subtract out size of topwin. Perhaps we need a constant
+ * somewhere? */
+ mevent.y -= 2;
- /* after_break is the text that will be moved to the next line. */
- after_break = inptr->data + wrap_loc + 1;
- after_break_len = len - wrap_loc - 1;
- assert(after_break_len == strlen(after_break));
+ /* Selecting where the cursor is sets the mark. Selecting
+ * beyond the line length with the cursor at the end of the line
+ * sets the mark as well. */
+ if ((mevent.y == current_y) &&
+ ((mevent.x == current_x) || (current_x == strlen(current->data)
+ && (mevent.x >
+ strlen(current->data))))) {
+ if (ISSET(VIEW_MODE)) {
+ print_view_warning();
+ return;
+ }
+ do_mark();
+ } else if (mevent.y > current_y) {
+ while (mevent.y > current_y) {
+ if (current->next != NULL)
+ current = current->next;
+ else
+ break;
+ current_y++;
+ }
+ } else if (mevent.y < current_y) {
+ while (mevent.y < current_y) {
+ if (current->prev != NULL)
+ current = current->prev;
+ else
+ break;
+ current_y--;
+ }
+ }
+ current_x = actual_x(current, mevent.x);
+ placewewant = current_x;
+ update_cursor();
+ edit_refresh();
+ } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
+ int i, k;
- /* new_line_len will later be increased by the lengths of indentation
- * and wrap_line. */
- new_line_len = after_break_len;
+ if (currshortcut == main_list)
+ currslen = MAIN_VISIBLE;
+ else
+ currslen = length_of_list(currshortcut);
- /* We prepend the wrapped text to the next line, if the flag is set,
- * and there is a next line, and prepending would not make the line
- * too long. */
- if (ISSET(SAMELINEWRAP) && inptr->next) {
- wrap_line = inptr->next->data;
- wrap_line_len = strlen(wrap_line);
+ if (currslen < 2)
+ k = COLS / 6;
+ else
+ k = COLS / ((currslen + (currslen %2)) / 2);
- /* +1 for the space between after_break and wrap_line */
- if ((new_line_len + 1 + wrap_line_len) <= fill) {
- wrapping = 1;
- new_line_len += (1 + wrap_line_len);
- }
- }
+ /* Determine what shortcut list was clicked */
+ mevent.y -= (editwinrows + 3);
-#ifndef NANO_SMALL
- if (ISSET(AUTOINDENT)) {
- /* indentation comes from the next line if wrapping, else from
- * this line */
- indentation = (wrapping ? wrap_line : inptr->data);
- indent_len = indent_length(indentation);
- if (wrapping)
- /* The wrap_line text should not duplicate indentation. Note
- * in this case we need not increase new_line_len. */
- wrap_line += indent_len;
- else
- new_line_len += indent_len;
- }
-#endif
+ if (mevent.y < 0) /* They clicked on the statusbar */
+ return;
+
+ /* Don't select stuff beyond list length */
+ if (mevent.x / k >= currslen)
+ return;
- /* Now we allocate the new line and copy into it. */
- newline = charalloc(new_line_len + 1); /* +1 for \0 */
- *newline = '\0';
+ for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
+ s = s->next;
-#ifndef NANO_SMALL
- if (ISSET(AUTOINDENT)) {
- strncpy(newline, indentation, indent_len);
- newline[indent_len] = '\0';
+ /* And ungetch that value */
+ ungetch(s->val);
+
+ /* And if it's an alt-key sequence, we should probably send alt
+ too ;-) */
+ if (s->val >= 'a' && s->val <= 'z')
+ ungetch(27);
}
+}
#endif
- strcat(newline, after_break);
- /* We end the old line at wrap_loc. Note this eats the space. */
- null_at(&inptr->data, wrap_loc);
- if (wrapping) {
- /* In this case, totsize does not change. We ate a space in the
- * null_at() above, but we add a space between after_break and
- * wrap_line below. */
- strcat(newline, " ");
- strcat(newline, wrap_line);
- free(inptr->next->data);
- inptr->next->data = newline;
- } else {
- filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
-
- /* In this case, the file size changes by -1 for the eaten
- * space, +1 for the new line, and +indent_len for the new
- * indentation. */
-#ifndef NANO_SMALL
- totsize += indent_len;
#endif
- totlines++;
- temp->data = newline;
- temp->prev = inptr;
- temp->next = inptr->next;
- temp->prev->next = temp;
- /* If !temp->next, then temp is the last line of the file, so we
- * must set filebot */
- if (temp->next)
- temp->next->prev = temp;
- else
- filebot = temp;
- }
-/* Step 3, clean up. Here we reposition the cursor and mark, and do some
- * other sundry things. */
+/* The user typed a printable character; add it to the edit buffer. */
+void do_char(char ch)
+{
+ size_t current_len = strlen(current->data);
+#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
+ int refresh = 0;
+ /* Do we have to run edit_refresh(), or can we get away with
+ * update_line()? */
+#endif
- /* later wraps of this line will be prepended to the next line. */
- SET(SAMELINEWRAP);
+ /* magic-line: when a character is inserted on the current magic line,
+ * it means we need a new one! */
+ if (filebot == current && current->data[0] == '\0') {
+ new_magicline();
+ fix_editbot();
+ }
- /* Each line knows its line number. We recalculate these if we
- * inserted a new line. */
- if (!wrapping)
- renumber(inptr);
+ /* more dangerousness fun =) */
+ current->data = nrealloc(current->data, current_len + 2);
+ assert(current_x <= current_len);
+ memmove(¤t->data[current_x + 1],
+ ¤t->data[current_x],
+ current_len - current_x + 1);
+ current->data[current_x] = ch;
+ totsize++;
+ set_modified();
- /* If the cursor was after the break point, we must move it. */
- if (current_x > wrap_loc) {
- current = current->next;
- current_x -=
#ifndef NANO_SMALL
- -indent_len +
+ /* note that current_x has not yet been incremented */
+ if (current == mark_beginbuf && current_x < mark_beginx)
+ mark_beginx++;
#endif
- wrap_loc + 1;
- wrap_reset();
- placewewant = xplustabs();
- }
-#ifndef NANO_SMALL
- /* If the mark was on this line after the wrap point, we move it down.
- * If it was on the next line and we wrapped, we must move it
- * right. */
- if (mark_beginbuf == inptr && mark_beginx > wrap_loc) {
- mark_beginbuf = inptr->next;
- mark_beginx -= wrap_loc - indent_len + 1;
- } else if (wrapping && mark_beginbuf == inptr->next)
- mark_beginx += after_break_len;
-#endif /* !NANO_SMALL */
+ do_right();
- /* Place the cursor. */
- reset_cursor();
+#ifndef DISABLE_WRAPPING
+ if (!ISSET(NO_WRAP) && ch != '\t')
+ refresh = do_wrap(current);
+#endif
- return 1;
-}
-#endif /* !DISABLE_WRAPPING */
+#ifdef ENABLE_COLOR
+ refresh = 1;
+#endif
-/* Stuff we do when we abort from programs and want to clean up the
- * screen. This doesn't do much right now. */
-void do_early_abort(void)
-{
- blank_statusbar_refresh();
+#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
+ if (refresh)
+ edit_refresh();
+#endif
+
+ check_statblank();
+ UNSET(KEEP_CUTBUFFER);
}
int do_backspace(void)
return 1;
}
-void wrap_reset(void)
+int do_tab(void)
{
- UNSET(SAMELINEWRAP);
+ do_char('\t');
+ return 1;
}
-#ifndef DISABLE_SPELLER
-int do_int_spell_fix(const char *word)
+/* Someone hits return *gasp!* */
+int do_enter(void)
{
- char *save_search;
- char *save_replace;
- filestruct *begin;
- int i = 0, j = 0, beginx, beginx_top, reverse_search_set;
-#ifndef NANO_SMALL
- int mark_set;
-#endif
-
- /* save where we are */
- begin = current;
- beginx = current_x + 1;
+ filestruct *newnode;
+ char *tmp;
- /* Make sure Spell Check goes forward only */
- reverse_search_set = ISSET(REVERSE_SEARCH);
- UNSET(REVERSE_SEARCH);
+ newnode = make_new_node(current);
+ assert(current != NULL && current->data != NULL);
+ tmp = ¤t->data[current_x];
#ifndef NANO_SMALL
- /* Make sure the marking highlight is off during Spell Check */
- mark_set = ISSET(MARK_ISSET);
- UNSET(MARK_ISSET);
-#endif
-
- /* save the current search/replace strings */
- search_init_globals();
- save_search = last_search;
- save_replace = last_replace;
-
- /* set search/replace strings to mis-spelt word */
- last_search = mallocstrcpy(NULL, word);
- last_replace = mallocstrcpy(NULL, word);
-
- /* start from the top of file */
- current = fileage;
- current_x = beginx_top = -1;
-
- search_last_line = FALSE;
-
- edit_update(fileage, TOP);
-
- while (1) {
- /* make sure word is still mis-spelt (i.e. when multi-errors) */
- if (findnextstr(TRUE, FALSE, fileage, beginx_top, word) != NULL) {
-
- /* find whole words only */
- if (!is_whole_word(current_x, current->data, word))
- continue;
-
- do_replace_highlight(TRUE, word);
-
- /* allow replace word to be corrected */
- i = statusq(0, spell_list, last_replace, _("Edit a replacement"));
-
- do_replace_highlight(FALSE, word);
-
- /* start from the start of this line again */
- current = fileage;
- current_x = beginx_top;
-
- search_last_line = FALSE;
+ /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
+ if (ISSET(AUTOINDENT)) {
+ int extra = 0;
+ const char *spc = current->data;
- if (strcmp(word, answer)) {
- j = i;
- do_replace_loop(word, fileage, &beginx_top, TRUE, &j);
- }
+ while (*spc == ' ' || *spc == '\t') {
+ extra++;
+ spc++;
}
- break;
- }
-
- /* restore the search/replace strings */
- free(last_search); last_search=save_search;
- free(last_replace); last_replace=save_replace;
+ /* If current_x < extra, then we are breaking the line in the
+ * indentation. Autoindenting should add only current_x
+ * characters of indentation. */
+ if (current_x < extra)
+ extra = current_x;
+ else
+ current_x = extra;
+ totsize += extra;
- /* restore where we were */
- current = begin;
- current_x = beginx - 1;
+ newnode->data = charalloc(strlen(tmp) + extra + 1);
+ strncpy(newnode->data, current->data, extra);
+ strcpy(&newnode->data[extra], tmp);
+ } else
+#endif
+ {
+ current_x = 0;
+ newnode->data = charalloc(strlen(tmp) + 1);
+ strcpy(newnode->data, tmp);
+ }
+ *tmp = '\0';
- /* restore Search/Replace direction */
- if (reverse_search_set)
- SET(REVERSE_SEARCH);
+ if (current->next == NULL) {
+ filebot = newnode;
+ editbot = newnode;
+ }
+ splice_node(current, newnode, current->next);
-#ifndef NANO_SMALL
- /* restore marking highlight */
- if (mark_set)
- SET(MARK_ISSET);
-#endif
+ totsize++;
+ renumber(current);
+ current = newnode;
+ align(¤t->data);
- edit_update(current, CENTER);
+ /* The logic here is as follows:
+ * -> If we are at the bottom of the buffer, we want to recenter
+ * (read: rebuild) the screen and forcibly move the cursor.
+ * -> otherwise, we want simply to redraw the screen and update
+ * where we think the cursor is.
+ */
+ if (current_y == editwinrows - 1) {
+ edit_update(current, CENTER);
+ reset_cursor();
+ } else {
+ current_y++;
+ edit_refresh();
+ update_cursor();
+ }
- if (i == -1)
- return FALSE;
+ totlines++;
+ set_modified();
- return TRUE;
+ placewewant = xplustabs();
+ return 1;
}
-/* Integrated spell checking using 'spell' program. */
-int do_int_speller(char *tempfile_name)
+#ifndef NANO_SMALL
+int do_next_word(void)
{
- char *read_buff, *read_buff_ptr, *read_buff_word;
- size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
- int in_fd[2], tempfile_fd, spell_status;
- pid_t pid_spell;
-
- /* Create a pipe to spell program */
-
- if (pipe(in_fd) == -1)
- return FALSE;
-
- /* A new process to run spell in */
-
- if ((pid_spell = fork()) == 0) {
+ filestruct *old = current;
- /* Child continues, (i.e. future spell process) */
+ assert(current != NULL && current->data != NULL);
- close(in_fd[0]);
+ /* Skip letters in this word first. */
+ while (current->data[current_x] != '\0' &&
+ isalnum((int)current->data[current_x]))
+ current_x++;
- /* replace the standard in with the tempfile */
+ for (; current != NULL; current = current->next) {
+ while (current->data[current_x] != '\0' &&
+ !isalnum((int)current->data[current_x]))
+ current_x++;
- if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1) {
- close(in_fd[1]);
- exit(1);
- }
+ if (current->data[current_x] != '\0')
+ break;
- if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) {
- close(tempfile_fd);
- close(in_fd[1]);
- exit(1);
- }
- close(tempfile_fd);
+ current_x = 0;
+ }
+ if (current == NULL)
+ current = filebot;
- /* send spell's standard out to the pipe */
+ placewewant = xplustabs();
- if (dup2(in_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
- close(in_fd[1]);
- exit(1);
+ if (current->lineno >= editbot->lineno) {
+ /* If we're on the last line, don't center the screen. */
+ if (current->lineno == filebot->lineno)
+ edit_refresh();
+ else
+ edit_update(current, CENTER);
+ }
+ else {
+ /* If we've jumped lines, refresh the old line. We can't just
+ use current->prev here, because we may have skipped over some
+ blank lines, in which case the previous line is the wrong
+ one. */
+ if (current != old) {
+ update_line(old, 0);
+ /* If the mark was set, then the lines between old and
+ current have to be updated too. */
+ if (ISSET(MARK_ISSET)) {
+ while (old->next != current) {
+ old = old->next;
+ update_line(old, 0);
+ }
+ }
}
- close(in_fd[1]);
-
- /* Start spell program, we are using the PATH here!?!? */
- execlp("spell", "spell", NULL);
-
- /* Should not be reached, if spell is found!!! */
-
- exit(1);
+ update_line(current, current_x);
}
+ return 0;
+}
- /* Parent continues here */
+/* The same thing for backwards. */
+int do_prev_word(void)
+{
+ filestruct *old = current;
- close(in_fd[1]);
+ assert(current != NULL);
- /* Child process was not forked successfully */
+ /* Skip letters in this word first. */
+ while (current_x >= 0 && isalnum((int)current->data[current_x]))
+ current_x--;
- if (pid_spell < 0) {
- close(in_fd[0]);
- return FALSE;
- }
+ for (; current != NULL; current = current->prev) {
+ while (current_x >= 0 && !isalnum((int)current->data[current_x]))
+ current_x--;
- /* Get system pipe buffer size */
+ if (current_x >= 0)
+ break;
- if ((pipe_buff_size = fpathconf(in_fd[0], _PC_PIPE_BUF)) < 1) {
- close(in_fd[0]);
- return FALSE;
+ if (current->prev != NULL)
+ current_x = strlen(current->prev->data);
}
- /* Read-in the returned spelling errors */
-
- read_buff_read = 0;
- read_buff_size = pipe_buff_size + 1;
- read_buff = read_buff_ptr = charalloc(read_buff_size);
-
- while ((bytesread = read(in_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
- read_buff_read += bytesread;
- read_buff_size += pipe_buff_size;
- read_buff = read_buff_ptr = nrealloc(read_buff, read_buff_size);
- read_buff_ptr += read_buff_read;
+ if (current != NULL) {
+ while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
+ current_x--;
+ } else {
+ current = fileage;
+ current_x = 0;
}
- *read_buff_ptr = (char) NULL;
- close(in_fd[0]);
-
- /* Process the spelling errors */
-
- read_buff_word = read_buff_ptr = read_buff;
-
- while (*read_buff_ptr) {
+ placewewant = xplustabs();
- if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
- *read_buff_ptr = (char) NULL;
- if (read_buff_word != read_buff_ptr) {
- if (!do_int_spell_fix(read_buff_word)) {
- read_buff_word = read_buff_ptr;
- break;
+ if (current->lineno <= edittop->lineno) {
+ /* If we're on the first line, don't center the screen. */
+ if (current->lineno == fileage->lineno)
+ edit_refresh();
+ else
+ edit_update(current, CENTER);
+ }
+ else {
+ /* If we've jumped lines, refresh the old line. We can't just
+ use current->prev here, because we may have skipped over some
+ blank lines, in which case the previous line is the wrong
+ one. */
+ if (current != old) {
+ update_line(old, 0);
+ /* If the mark was set, then the lines between old and
+ current have to be updated too. */
+ if (ISSET(MARK_ISSET)) {
+ while (old->prev != current) {
+ old = old->prev;
+ update_line(old, 0);
}
}
- read_buff_word = read_buff_ptr + 1;
}
- read_buff_ptr++;
+ update_line(current, current_x);
}
+ return 0;
+}
+#endif /* !NANO_SMALL */
- /* special case where last word doesn't end with \n or \r */
- if (read_buff_word != read_buff_ptr)
- do_int_spell_fix(read_buff_word);
-
- free(read_buff);
- replace_abort();
-
- /* Process end of spell process */
-
- wait(&spell_status);
- if (WIFEXITED(spell_status)) {
- if (WEXITSTATUS(spell_status) != 0)
- return FALSE;
- } else
- return FALSE;
+int do_mark(void)
+{
+#ifdef NANO_SMALL
+ nano_disabled_msg();
+#else
+ if (!ISSET(MARK_ISSET)) {
+ statusbar(_("Mark Set"));
+ SET(MARK_ISSET);
+ mark_beginbuf = current;
+ mark_beginx = current_x;
+ } else {
+ statusbar(_("Mark UNset"));
+ UNSET(MARK_ISSET);
+ edit_refresh();
+ }
+#endif
+ return 1;
+}
- return TRUE;
+void wrap_reset(void)
+{
+ UNSET(SAMELINEWRAP);
}
-/* External spell checking. */
-int do_alt_speller(char *file_name)
+#ifndef DISABLE_WRAPPING
+/* We wrap the given line. Precondition: we assume the cursor has been
+ * moved forward since the last typed character. Return value:
+ * whether we wrapped. */
+int do_wrap(filestruct *inptr)
{
- int alt_spell_status, lineno_cur = current->lineno;
- int x_cur = current_x, y_cur = current_y, pww_cur = placewewant;
- pid_t pid_spell;
- char *ptr;
- static int arglen = 3;
- static char **spellargs = (char **)NULL;
+ size_t len = strlen(inptr->data); /* length of the line we wrap */
+ int i = 0; /* generic loop variable */
+ int wrap_loc = -1; /* index of inptr->data where we wrap */
+ int word_back = -1;
#ifndef NANO_SMALL
- int mark_set = ISSET(MARK_ISSET);
- int mbb_lineno_cur = 0;
- /* We're going to close the current file, and open the output of
- the alternate spell command. The line that mark_beginbuf
- points to will be freed, so we save the line number and restore
- afterwards. */
-
- if (mark_set) {
- mbb_lineno_cur = mark_beginbuf->lineno;
- UNSET(MARK_ISSET);
- }
+ const char *indentation = NULL;
+ /* indentation to prepend to the new line */
+ int indent_len = 0; /* strlen(indentation) */
#endif
+ const char *after_break; /* text after the wrap point */
+ int after_break_len; /* strlen(after_break) */
+ int wrapping = 0; /* do we prepend to the next line? */
+ const char *wrap_line = NULL;
+ /* the next line, minus indentation */
+ int wrap_line_len = 0; /* strlen(wrap_line) */
+ char *newline = NULL; /* the line we create */
+ int new_line_len = 0; /* eventual length of newline */
- endwin();
+/* There are three steps. First, we decide where to wrap. Then, we
+ * create the new wrap line. Finally, we clean up. */
- /* Set up an argument list to pass the execvp function */
- if (spellargs == NULL) {
- spellargs = nmalloc(arglen * sizeof(char *));
+/* Step 1, finding where to wrap. We are going to replace a white-space
+ * character with a new-line. In this step, we set wrap_loc as the
+ * location of this replacement.
+ *
+ * Where should we break the line? We need the last "legal wrap point"
+ * such that the last word before it ended at or before fill. If there
+ * is no such point, we settle for the first legal wrap point.
+ *
+ * A "legal wrap point" is a white-space character that is not the last
+ * typed character and is not followed by white-space.
+ *
+ * If there is no legal wrap point or we found the last character of the
+ * line, we should return without wrapping.
+ *
+ * Note that the initial indentation does not count as a legal wrap
+ * point if we are going to auto-indent!
+ *
+ * Note that the code below could be optimised, by not calling strnlenpt()
+ * so often. */
- spellargs[0] = strtok(alt_speller, " ");
- while ((ptr = strtok(NULL, " ")) != NULL) {
- arglen++;
- spellargs = nrealloc(spellargs, arglen * sizeof(char *));
- spellargs[arglen - 3] = ptr;
- }
- spellargs[arglen - 1] = NULL;
+#ifndef NANO_SMALL
+ if (ISSET(AUTOINDENT))
+ i = indent_length(inptr->data);
+#endif
+ wrap_line = inptr->data + i;
+ for(; i < len; i++, wrap_line++) {
+ /* record where the last word ended */
+ if (*wrap_line != ' ' && *wrap_line != '\t')
+ word_back = i;
+ /* if we have found a "legal wrap point" and the current word
+ * extends too far, then we stop */
+ if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
+ break;
+ /* we record the latest "legal wrap point" */
+ if (i != current_x - 1 && word_back != i &&
+ wrap_line[1] != ' ' && wrap_line[1] != '\t')
+ wrap_loc = i;
}
- spellargs[arglen - 2] = file_name;
-
- /* Start a new process for the alternate speller */
- if ((pid_spell = fork()) == 0) {
- /* Start alternate spell program; we are using the PATH here!?!? */
- execvp(spellargs[0], spellargs);
+ if (wrap_loc < 0 || i == len)
+ return 0;
- /* Should not be reached, if alternate speller is found!!! */
- exit(1);
- }
+/* Step 2, making the new wrap line. It will consist of indentation +
+ * after_break + " " + wrap_line (although indentation and wrap_line are
+ * conditional on flags and #defines). */
- /* Could not fork?? */
- if (pid_spell < 0)
- return FALSE;
+ /* after_break is the text that will be moved to the next line. */
+ after_break = inptr->data + wrap_loc + 1;
+ after_break_len = len - wrap_loc - 1;
+ assert(after_break_len == strlen(after_break));
- /* Wait for alternate speller to complete */
+ /* new_line_len will later be increased by the lengths of indentation
+ * and wrap_line. */
+ new_line_len = after_break_len;
- wait(&alt_spell_status);
- if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0)
- return FALSE;
+ /* We prepend the wrapped text to the next line, if the flag is set,
+ * and there is a next line, and prepending would not make the line
+ * too long. */
+ if (ISSET(SAMELINEWRAP) && inptr->next) {
+ wrap_line = inptr->next->data;
+ wrap_line_len = strlen(wrap_line);
- refresh();
- free_filestruct(fileage);
- global_init(1);
- open_file(file_name, 0, 1);
+ /* +1 for the space between after_break and wrap_line */
+ if ((new_line_len + 1 + wrap_line_len) <= fill) {
+ wrapping = 1;
+ new_line_len += (1 + wrap_line_len);
+ }
+ }
#ifndef NANO_SMALL
- if (mark_set) {
- do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
- mark_beginbuf = current;
- mark_beginx = current_x;
- /* In case the line got shorter, assign mark_beginx. */
- SET(MARK_ISSET);
+ if (ISSET(AUTOINDENT)) {
+ /* indentation comes from the next line if wrapping, else from
+ * this line */
+ indentation = (wrapping ? wrap_line : inptr->data);
+ indent_len = indent_length(indentation);
+ if (wrapping)
+ /* The wrap_line text should not duplicate indentation. Note
+ * in this case we need not increase new_line_len. */
+ wrap_line += indent_len;
+ else
+ new_line_len += indent_len;
}
#endif
- /* go back to the old position, mark the file as modified, and make
- sure that the titlebar is refreshed */
- do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
- set_modified();
- clearok(topwin, FALSE);
- titlebar(NULL);
-
- return TRUE;
-}
-#endif
-
-int do_spell(void)
-{
-#ifdef DISABLE_SPELLER
- nano_disabled_msg();
- return (TRUE);
-#else
- char *temp;
- int spell_res;
-
- if ((temp = safe_tempnam(0, "nano.")) == NULL) {
- statusbar(_("Could not create a temporary filename: %s"),
- strerror(errno));
- return 0;
- }
+ /* Now we allocate the new line and copy into it. */
+ newline = charalloc(new_line_len + 1); /* +1 for \0 */
+ *newline = '\0';
- if (write_file(temp, 1, 0, 0) == -1) {
- statusbar(_("Spell checking failed: unable to write temp file!"));
- free(temp);
- return 0;
+#ifndef NANO_SMALL
+ if (ISSET(AUTOINDENT)) {
+ strncpy(newline, indentation, indent_len);
+ newline[indent_len] = '\0';
}
-
-#ifdef ENABLE_MULTIBUFFER
- /* update the current open_files entry before spell-checking, in case
- any problems occur */
- add_open_file(1);
#endif
+ strcat(newline, after_break);
+ /* We end the old line at wrap_loc. Note this eats the space. */
+ null_at(&inptr->data, wrap_loc);
+ if (wrapping) {
+ /* In this case, totsize does not change. We ate a space in the
+ * null_at() above, but we add a space between after_break and
+ * wrap_line below. */
+ strcat(newline, " ");
+ strcat(newline, wrap_line);
+ free(inptr->next->data);
+ inptr->next->data = newline;
+ } else {
+ filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
- if (alt_speller)
- spell_res = do_alt_speller(temp);
- else
- spell_res = do_int_speller(temp);
+ /* In this case, the file size changes by -1 for the eaten
+ * space, +1 for the new line, and +indent_len for the new
+ * indentation. */
+#ifndef NANO_SMALL
+ totsize += indent_len;
+#endif
+ totlines++;
+ temp->data = newline;
+ temp->prev = inptr;
+ temp->next = inptr->next;
+ temp->prev->next = temp;
+ /* If !temp->next, then temp is the last line of the file, so we
+ * must set filebot */
+ if (temp->next)
+ temp->next->prev = temp;
+ else
+ filebot = temp;
+ }
- remove(temp);
+/* Step 3, clean up. Here we reposition the cursor and mark, and do some
+ * other sundry things. */
- if (spell_res)
- statusbar(_("Finished checking spelling"));
- else
- statusbar(_("Spell checking failed"));
+ /* later wraps of this line will be prepended to the next line. */
+ SET(SAMELINEWRAP);
- free(temp);
- return spell_res;
+ /* Each line knows its line number. We recalculate these if we
+ * inserted a new line. */
+ if (!wrapping)
+ renumber(inptr);
+ /* If the cursor was after the break point, we must move it. */
+ if (current_x > wrap_loc) {
+ current = current->next;
+ current_x -=
+#ifndef NANO_SMALL
+ -indent_len +
#endif
-}
+ wrap_loc + 1;
+ wrap_reset();
+ placewewant = xplustabs();
+ }
#ifndef NANO_SMALL
-static int pid; /* This is the PID of the newly forked process
- * below. It must be global since the signal
- * handler needs it. */
+ /* If the mark was on this line after the wrap point, we move it down.
+ * If it was on the next line and we wrapped, we must move it
+ * right. */
+ if (mark_beginbuf == inptr && mark_beginx > wrap_loc) {
+ mark_beginbuf = inptr->next;
+ mark_beginx -= wrap_loc - indent_len + 1;
+ } else if (wrapping && mark_beginbuf == inptr->next)
+ mark_beginx += after_break_len;
+#endif /* !NANO_SMALL */
-RETSIGTYPE cancel_fork(int signal)
-{
- if (kill(pid, SIGKILL)==-1) nperror("kill");
+ /* Place the cursor. */
+ reset_cursor();
+
+ return 1;
}
+#endif /* !DISABLE_WRAPPING */
-int open_pipe(const char *command)
+#ifndef DISABLE_SPELLER
+int do_int_spell_fix(const char *word)
{
- int fd[2];
- FILE *f;
- struct sigaction oldaction, newaction;
- /* original and temporary handlers for SIGINT */
-#ifdef _POSIX_VDISABLE
- struct termios term, newterm;
-#endif /* _POSIX_VDISABLE */
- int cancel_sigs = 0;
- /* cancel_sigs==1 means that sigaction failed without changing the
- * signal handlers. cancel_sigs==2 means the signal handler was
- * changed, but the tcsetattr didn't succeed.
- * I use this variable since it is important to put things back when
- * we finish, even if we get errors. */
-
- /* Make our pipes. */
+ char *save_search;
+ char *save_replace;
+ filestruct *begin;
+ int i = 0, j = 0, beginx, beginx_top, reverse_search_set;
+#ifndef NANO_SMALL
+ int mark_set;
+#endif
- if (pipe(fd) == -1) {
- statusbar(_("Could not pipe"));
- return 1;
- }
+ /* save where we are */
+ begin = current;
+ beginx = current_x + 1;
- /* Fork a child */
+ /* Make sure Spell Check goes forward only */
+ reverse_search_set = ISSET(REVERSE_SEARCH);
+ UNSET(REVERSE_SEARCH);
- if ((pid = fork()) == 0) {
- close(fd[0]);
- dup2(fd[1], fileno(stdout));
- dup2(fd[1], fileno(stderr));
- /* If execl() returns at all, there was an error. */
-
- execl("/bin/sh","sh","-c",command,0);
- exit(0);
- }
+#ifndef NANO_SMALL
+ /* Make sure the marking highlight is off during Spell Check */
+ mark_set = ISSET(MARK_ISSET);
+ UNSET(MARK_ISSET);
+#endif
- /* Else continue as parent */
+ /* save the current search/replace strings */
+ search_init_globals();
+ save_search = last_search;
+ save_replace = last_replace;
- close(fd[1]);
+ /* set search/replace strings to mis-spelt word */
+ last_search = mallocstrcpy(NULL, word);
+ last_replace = mallocstrcpy(NULL, word);
- if (pid == -1) {
- close(fd[0]);
- statusbar(_("Could not fork"));
- return 1;
- }
+ /* start from the top of file */
+ current = fileage;
+ current_x = beginx_top = -1;
- /* before we start reading the forked command's output, we set
- * things up so that ^C will cancel the new process */
- if (sigaction(SIGINT, NULL, &newaction)==-1) {
- cancel_sigs = 1;
- nperror("sigaction");
- } else {
- newaction.sa_handler = cancel_fork;
- if (sigaction(SIGINT, &newaction, &oldaction)==-1) {
- cancel_sigs = 1;
- nperror("sigaction");
- }
- }
- /* note that now oldaction is the previous SIGINT signal handler, to
- * be restored later */
+ search_last_line = FALSE;
- /* if the platform supports disabling individual control characters */
-#ifdef _POSIX_VDISABLE
- if (!cancel_sigs && tcgetattr(0, &term) == -1) {
- cancel_sigs = 2;
- nperror("tcgetattr");
- }
- if (!cancel_sigs) {
- newterm = term;
- /* Grab oldterm's VINTR key :-) */
- newterm.c_cc[VINTR] = oldterm.c_cc[VINTR];
- if (tcsetattr(0, TCSANOW, &newterm) == -1) {
- cancel_sigs = 2;
- nperror("tcsetattr");
- }
- }
-#endif /* _POSIX_VDISABLE */
+ edit_update(fileage, TOP);
- f = fdopen(fd[0], "rb");
- if (!f)
- nperror("fdopen");
-
- read_file(f, "stdin", 0);
- set_modified();
+ while (1) {
+ /* make sure word is still mis-spelt (i.e. when multi-errors) */
+ if (findnextstr(TRUE, FALSE, fileage, beginx_top, word) != NULL) {
- if (wait(NULL) == -1)
- nperror("wait");
+ /* find whole words only */
+ if (!is_whole_word(current_x, current->data, word))
+ continue;
-#ifdef _POSIX_VDISABLE
- if (!cancel_sigs && tcsetattr(0, TCSANOW, &term) == -1)
- nperror("tcsetattr");
-#endif /* _POSIX_VDISABLE */
+ do_replace_highlight(TRUE, word);
- if (cancel_sigs!=1 && sigaction(SIGINT, &oldaction, NULL) == -1)
- nperror("sigaction");
+ /* allow replace word to be corrected */
+ i = statusq(0, spell_list, last_replace, _("Edit a replacement"));
- return 0;
-}
-#endif /* NANO_SMALL */
+ do_replace_highlight(FALSE, word);
-int do_exit(void)
-{
- int i;
+ /* start from the start of this line again */
+ current = fileage;
+ current_x = beginx_top;
- if (!ISSET(MODIFIED)) {
+ search_last_line = FALSE;
-#ifdef ENABLE_MULTIBUFFER
- if (!close_open_file()) {
- display_main_list();
- return 1;
+ if (strcmp(word, answer)) {
+ j = i;
+ do_replace_loop(word, fileage, &beginx_top, TRUE, &j);
+ }
}
- else
-#endif
- finish(0);
+ break;
}
- if (ISSET(TEMP_OPT)) {
- i = 1;
- } else {
- i = do_yesno(0, 0,
- _
- ("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
- }
+ /* restore the search/replace strings */
+ free(last_search); last_search=save_search;
+ free(last_replace); last_replace=save_replace;
-#ifdef DEBUG
- dump_buffer(fileage);
-#endif
+ /* restore where we were */
+ current = begin;
+ current_x = beginx - 1;
- if (i == 1) {
- if (do_writeout(filename, 1, 0) > 0) {
+ /* restore Search/Replace direction */
+ if (reverse_search_set)
+ SET(REVERSE_SEARCH);
-#ifdef ENABLE_MULTIBUFFER
- if (!close_open_file()) {
- display_main_list();
- return 1;
- }
- else
+#ifndef NANO_SMALL
+ /* restore marking highlight */
+ if (mark_set)
+ SET(MARK_ISSET);
#endif
- finish(0);
- }
- } else if (i == 0) {
-#ifdef ENABLE_MULTIBUFFER
- if (!close_open_file()) {
- display_main_list();
- return 1;
- }
- else
-#endif
- finish(0);
- } else
- statusbar(_("Cancelled"));
+ edit_update(current, CENTER);
- display_main_list();
- return 1;
+ if (i == -1)
+ return FALSE;
+
+ return TRUE;
}
-#ifndef DISABLE_MOUSE
-#ifdef NCURSES_MOUSE_VERSION
-void do_mouse(void)
+/* Integrated spell checking using 'spell' program. */
+int do_int_speller(char *tempfile_name)
{
- MEVENT mevent;
- int currslen;
- const shortcut *s = currshortcut;
+ char *read_buff, *read_buff_ptr, *read_buff_word;
+ size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
+ int in_fd[2], tempfile_fd, spell_status;
+ pid_t pid_spell;
- if (getmouse(&mevent) == ERR)
- return;
+ /* Create a pipe to spell program */
- /* If mouse not in edit or bottom window, return */
- if (wenclose(edit, mevent.y, mevent.x)) {
+ if (pipe(in_fd) == -1)
+ return FALSE;
- /* Don't let people screw with the marker when they're in a
- * subfunction. */
- if (currshortcut != main_list)
- return;
+ /* A new process to run spell in */
- /* Subtract out size of topwin. Perhaps we need a constant
- * somewhere? */
- mevent.y -= 2;
+ if ((pid_spell = fork()) == 0) {
- /* Selecting where the cursor is sets the mark. Selecting
- * beyond the line length with the cursor at the end of the line
- * sets the mark as well. */
- if ((mevent.y == current_y) &&
- ((mevent.x == current_x) || (current_x == strlen(current->data)
- && (mevent.x >
- strlen(current->data))))) {
- if (ISSET(VIEW_MODE)) {
- print_view_warning();
- return;
- }
- do_mark();
- } else if (mevent.y > current_y) {
- while (mevent.y > current_y) {
- if (current->next != NULL)
- current = current->next;
- else
- break;
- current_y++;
- }
- } else if (mevent.y < current_y) {
- while (mevent.y < current_y) {
- if (current->prev != NULL)
- current = current->prev;
- else
- break;
- current_y--;
- }
- }
- current_x = actual_x(current, mevent.x);
- placewewant = current_x;
- update_cursor();
- edit_refresh();
- } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
- int i, k;
+ /* Child continues, (i.e. future spell process) */
- if (currshortcut == main_list)
- currslen = MAIN_VISIBLE;
- else
- currslen = length_of_list(currshortcut);
+ close(in_fd[0]);
- if (currslen < 2)
- k = COLS / 6;
- else
- k = COLS / ((currslen + (currslen %2)) / 2);
+ /* replace the standard in with the tempfile */
- /* Determine what shortcut list was clicked */
- mevent.y -= (editwinrows + 3);
+ if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1) {
+ close(in_fd[1]);
+ exit(1);
+ }
+
+ if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) {
+ close(tempfile_fd);
+ close(in_fd[1]);
+ exit(1);
+ }
+ close(tempfile_fd);
- if (mevent.y < 0) /* They clicked on the statusbar */
- return;
+ /* send spell's standard out to the pipe */
- /* Don't select stuff beyond list length */
- if (mevent.x / k >= currslen)
- return;
+ if (dup2(in_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ close(in_fd[1]);
+ exit(1);
+ }
+ close(in_fd[1]);
- for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
- s = s->next;
+ /* Start spell program, we are using the PATH here!?!? */
+ execlp("spell", "spell", NULL);
- /* And ungetch that value */
- ungetch(s->val);
+ /* Should not be reached, if spell is found!!! */
- /* And if it's an alt-key sequence, we should probably send alt
- too ;-) */
- if (s->val >= 'a' && s->val <= 'z')
- ungetch(27);
+ exit(1);
}
-}
-#endif
-#endif
-/* Handler for SIGHUP */
-RETSIGTYPE handle_hup(int signal)
-{
- die(_("Received SIGHUP"));
-}
+ /* Parent continues here */
-/* What do we do when we catch the suspend signal */
-RETSIGTYPE do_suspend(int signal)
-{
- endwin();
- printf("\n\n\n\n\nUse \"fg\" to return to nano\n");
- fflush(stdout);
+ close(in_fd[1]);
- /* Restore the terminal settings for the disabled keys */
- tcsetattr(0, TCSANOW, &oldterm);
+ /* Child process was not forked successfully */
- /* We used to re-enable the default SIG_DFL and raise SIGTSTP, but
- then we could be (and were) interrupted in the middle of the call.
- So we do it the mutt way instead */
- kill(0, SIGSTOP);
-}
+ if (pid_spell < 0) {
+ close(in_fd[0]);
+ return FALSE;
+ }
-/* Restore the suspend handler when we come back into the prog */
-RETSIGTYPE do_cont(int signal)
-{
- /* Now we just update the screen instead of having to reenable the
- SIGTSTP handler. */
+ /* Get system pipe buffer size */
- doupdate();
- /* The Hurd seems to need this, otherwise a ^Y after a ^Z will
- start suspending again. */
- signal_init();
+ if ((pipe_buff_size = fpathconf(in_fd[0], _PC_PIPE_BUF)) < 1) {
+ close(in_fd[0]);
+ return FALSE;
+ }
-#ifndef NANO_SMALL
- /* Perhaps the user resized the window while we slept. */
- handle_sigwinch(0);
-#endif
-}
+ /* Read-in the returned spelling errors */
-#ifndef NANO_SMALL
-void handle_sigwinch(int s)
-{
- const char *tty = ttyname(0);
- int fd;
- int result = 0;
- struct winsize win;
+ read_buff_read = 0;
+ read_buff_size = pipe_buff_size + 1;
+ read_buff = read_buff_ptr = charalloc(read_buff_size);
- if (!tty)
- return;
- fd = open(tty, O_RDWR);
- if (fd == -1)
- return;
- result = ioctl(fd, TIOCGWINSZ, &win);
- close(fd);
- if (result == -1)
- return;
+ while ((bytesread = read(in_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
+ read_buff_read += bytesread;
+ read_buff_size += pipe_buff_size;
+ read_buff = read_buff_ptr = nrealloc(read_buff, read_buff_size);
+ read_buff_ptr += read_buff_read;
+ }
- /* Could check whether the COLS or LINES changed, and return
- * otherwise. EXCEPT, that COLS and LINES are ncurses global
- * variables, and in some cases ncurses has already updated them.
- * But not in all cases, argh. */
- COLS = win.ws_col;
- LINES = win.ws_row;
- if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
- die_too_small();
+ *read_buff_ptr = (char) NULL;
+ close(in_fd[0]);
-#ifndef DISABLE_WRAPJUSTIFY
- fill = wrap_at;
- if (fill <= 0)
- fill += COLS;
- if (fill < MIN_FILL_LENGTH)
- die_too_small();
-#endif
+ /* Process the spelling errors */
- hblank = nrealloc(hblank, COLS + 1);
- memset(hblank, ' ', COLS);
- hblank[COLS] = '\0';
+ read_buff_word = read_buff_ptr = read_buff;
-#ifdef HAVE_RESIZETERM
- resizeterm(LINES, COLS);
-#ifdef HAVE_WRESIZE
- if (wresize(topwin, 2, COLS) == ERR)
- die(_("Cannot resize top win"));
- if (mvwin(topwin, 0, 0) == ERR)
- die(_("Cannot move top win"));
- if (wresize(edit, editwinrows, COLS) == ERR)
- die(_("Cannot resize edit win"));
- if (mvwin(edit, 2, 0) == ERR)
- die(_("Cannot move edit win"));
- if (wresize(bottomwin, 3 - no_help(), COLS) == ERR)
- die(_("Cannot resize bottom win"));
- if (mvwin(bottomwin, LINES - 3 + no_help(), 0) == ERR)
- die(_("Cannot move bottom win"));
-#endif /* HAVE_WRESIZE */
-#endif /* HAVE_RESIZETERM */
+ while (*read_buff_ptr) {
- fix_editbot();
+ if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
+ *read_buff_ptr = (char) NULL;
+ if (read_buff_word != read_buff_ptr) {
+ if (!do_int_spell_fix(read_buff_word)) {
+ read_buff_word = read_buff_ptr;
+ break;
+ }
+ }
+ read_buff_word = read_buff_ptr + 1;
+ }
+ read_buff_ptr++;
+ }
- if (current_y > editwinrows - 1)
- edit_update(editbot, CENTER);
- erase();
+ /* special case where last word doesn't end with \n or \r */
+ if (read_buff_word != read_buff_ptr)
+ do_int_spell_fix(read_buff_word);
- /* Do these b/c width may have changed... */
- refresh();
- titlebar(NULL);
- edit_refresh();
- display_main_list();
- blank_statusbar();
- total_refresh();
+ free(read_buff);
+ replace_abort();
- /* Turn cursor back on for sure */
- curs_set(1);
+ /* Process end of spell process */
- /* Jump back to main loop */
- siglongjmp(jmpbuf, 1);
+ wait(&spell_status);
+ if (WIFEXITED(spell_status)) {
+ if (WEXITSTATUS(spell_status) != 0)
+ return FALSE;
+ } else
+ return FALSE;
+
+ return TRUE;
}
-#endif
-void signal_init(void)
+/* External spell checking. */
+int do_alt_speller(char *tempfile_name)
{
-#ifdef _POSIX_VDISABLE
- struct termios term;
+ int alt_spell_status, lineno_cur = current->lineno;
+ int x_cur = current_x, y_cur = current_y, pww_cur = placewewant;
+ pid_t pid_spell;
+ char *ptr;
+ static int arglen = 3;
+ static char **spellargs = (char **)NULL;
+#ifndef NANO_SMALL
+ int mark_set = ISSET(MARK_ISSET);
+ int mbb_lineno_cur = 0;
+ /* We're going to close the current file, and open the output of
+ the alternate spell command. The line that mark_beginbuf
+ points to will be freed, so we save the line number and restore
+ afterwards. */
+
+ if (mark_set) {
+ mbb_lineno_cur = mark_beginbuf->lineno;
+ UNSET(MARK_ISSET);
+ }
#endif
- /* Trap SIGINT and SIGQUIT cuz we want them to do useful things. */
- memset(&act, 0, sizeof(struct sigaction));
- act.sa_handler = SIG_IGN;
- sigaction(SIGINT, &act, NULL);
+ endwin();
- /* Trap SIGHUP cuz we want to write the file out. */
- act.sa_handler = handle_hup;
- sigaction(SIGHUP, &act, NULL);
+ /* Set up an argument list to pass the execvp function */
+ if (spellargs == NULL) {
+ spellargs = nmalloc(arglen * sizeof(char *));
-#ifndef NANO_SMALL
- act.sa_handler = handle_sigwinch;
- sigaction(SIGWINCH, &act, NULL);
-#endif
+ spellargs[0] = strtok(alt_speller, " ");
+ while ((ptr = strtok(NULL, " ")) != NULL) {
+ arglen++;
+ spellargs = nrealloc(spellargs, arglen * sizeof(char *));
+ spellargs[arglen - 3] = ptr;
+ }
+ spellargs[arglen - 1] = NULL;
+ }
+ spellargs[arglen - 2] = tempfile_name;
-#ifdef _POSIX_VDISABLE
- tcgetattr(0, &term);
+ /* Start a new process for the alternate speller */
+ if ((pid_spell = fork()) == 0) {
+ /* Start alternate spell program; we are using the PATH here!?!? */
+ execvp(spellargs[0], spellargs);
-#ifdef VDSUSP
- term.c_cc[VDSUSP] = _POSIX_VDISABLE;
-#endif /* VDSUSP */
+ /* Should not be reached, if alternate speller is found!!! */
+ exit(1);
+ }
-#endif /* _POSIX_VDISABLE */
+ /* Could not fork?? */
+ if (pid_spell < 0)
+ return FALSE;
+
+ /* Wait for alternate speller to complete */
+
+ wait(&alt_spell_status);
+ if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0)
+ return FALSE;
+
+ refresh();
+ free_filestruct(fileage);
+ global_init(1);
+ open_file(tempfile_name, 0, 1);
+
+#ifndef NANO_SMALL
+ if (mark_set) {
+ do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
+ mark_beginbuf = current;
+ mark_beginx = current_x;
+ /* In case the line got shorter, assign mark_beginx. */
+ SET(MARK_ISSET);
+ }
+#endif
- if (!ISSET(SUSPEND)) {
+ /* go back to the old position, mark the file as modified, and make
+ sure that the titlebar is refreshed */
+ do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
+ set_modified();
+ clearok(topwin, FALSE);
+ titlebar(NULL);
-/* Insane! */
-#ifdef _POSIX_VDISABLE
- term.c_cc[VSUSP] = _POSIX_VDISABLE;
-#else
- act.sa_handler = SIG_IGN;
- sigaction(SIGTSTP, &act, NULL);
+ return TRUE;
+}
#endif
- } else {
- /* If we don't do this, it seems other stuff interrupts the
- suspend handler! Try using nano with mutt without this
- line. */
- sigfillset(&act.sa_mask);
+int do_spell(void)
+{
+#ifdef DISABLE_SPELLER
+ nano_disabled_msg();
+ return (TRUE);
+#else
+ char *temp;
+ int spell_res;
- act.sa_handler = do_suspend;
- sigaction(SIGTSTP, &act, NULL);
+ if ((temp = safe_tempnam(0, "nano.")) == NULL) {
+ statusbar(_("Could not create a temporary filename: %s"),
+ strerror(errno));
+ return 0;
+ }
- act.sa_handler = do_cont;
- sigaction(SIGCONT, &act, NULL);
+ if (write_file(temp, 1, 0, 0) == -1) {
+ statusbar(_("Spell checking failed: unable to write temp file!"));
+ free(temp);
+ return 0;
}
-#ifdef _POSIX_VDISABLE
- tcsetattr(0, TCSANOW, &term);
+#ifdef ENABLE_MULTIBUFFER
+ /* update the current open_files entry before spell-checking, in case
+ any problems occur */
+ add_open_file(1);
#endif
-}
-
-void window_init(void)
-{
- if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
- die_too_small();
- /* Set up the main text window */
- edit = newwin(editwinrows, COLS, 2, 0);
+ if (alt_speller)
+ spell_res = do_alt_speller(temp);
+ else
+ spell_res = do_int_speller(temp);
- /* And the other windows */
- topwin = newwin(2, COLS, 0, 0);
- bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
+ remove(temp);
-#ifdef PDCURSES
- /* Oops, I guess we need this again.
- Moved here so the keypad still works after a Meta-X, for example */
- keypad(edit, TRUE);
- keypad(bottomwin, TRUE);
-#endif
-}
+ if (spell_res)
+ statusbar(_("Finished checking spelling"));
+ else
+ statusbar(_("Spell checking failed"));
-void mouse_init(void)
-{
-#ifndef DISABLE_MOUSE
-#ifdef NCURSES_MOUSE_VERSION
- if (ISSET(USE_MOUSE)) {
- keypad_on(edit, 1);
- keypad_on(bottomwin, 1);
+ free(temp);
+ return spell_res;
- mousemask(BUTTON1_RELEASED, NULL);
- mouseinterval(50);
- } else
- mousemask(0, NULL);
-#endif
#endif
}
-int do_tab(void)
-{
- do_char('\t');
- return 1;
-}
-
#if !defined(DISABLE_WRAPPING) && !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
/* The "indentation" of a line is the white-space between the quote part
* and the non-white-space of the line. */
-size_t indent_length(const char *line) {
+size_t indent_length(const char *line)
+{
size_t len = 0;
assert(line != NULL);
}
#endif /* !HAVE_REGEX_H */
-#ifdef HAVE_REGEX_H
-# define IFREG(a, b) a, b
-#else
-# define IFREG(a, b) a
-#endif
-
/* a_line and b_line are lines of text. The quotation part of a_line is
* the first a_quote characters. Check that the quotation part of
* b_line is the same. */
int quotes_match(const char *a_line, size_t a_quote,
- IFREG(const char *b_line, const regex_t *qreg))
+ IFREG(const char *b_line, const regex_t *qreg))
{
/* Here is the assumption about a_quote: */
assert(a_quote == quote_length(IFREG(a_line, qreg)));
/* We assume a_line and b_line have no quote part. Then, we return whether
* b_line could follow a_line in a paragraph. */
size_t indents_match(const char *a_line, size_t a_indent,
- const char *b_line, size_t b_indent)
+ const char *b_line, size_t b_indent)
{
assert(a_indent == indent_length(a_line));
assert(b_indent == indent_length(b_line));
* copies of the lines in place, too. We return the new copy of
* first_line. */
filestruct *backup_lines(filestruct *first_line, size_t par_len,
- size_t quote_len)
+ size_t quote_len)
{
/* We put the original lines, not copies, into the cut buffer, just
* out of a misguided sense of consistency, so if you un-cut, you
reset_cursor();
/* Now get a keystroke and see if it's unjustify; if not, unget the
- * keystroke and return */
+ * keystroke and return. */
#ifndef DISABLE_MOUSE
#ifdef NCURSES_MOUSE_VERSION
#endif
}
-#ifndef DISABLE_HELP
-/* This function allocates help_text, and stores the help string in it.
- * help_text should be NULL initially. */
-void help_init(void)
+int do_exit(void)
{
- size_t allocsize = 1; /* space needed for help_text */
- char *ptr = NULL;
-#ifndef NANO_SMALL
- const toggle *t;
+ int i;
+
+ if (!ISSET(MODIFIED)) {
+
+#ifdef ENABLE_MULTIBUFFER
+ if (!close_open_file()) {
+ display_main_list();
+ return 1;
+ }
+ else
#endif
- const shortcut *s;
+ finish(0);
+ }
- /* First set up the initial help text for the current function */
- if (currshortcut == whereis_list || currshortcut == replace_list
- || currshortcut == replace_list_2)
- ptr = _("Search Command Help Text\n\n "
- "Enter the words or characters you would like to search "
- "for, then hit enter. If there is a match for the text you "
- "entered, the screen will be updated to the location of the "
- "nearest match for the search string.\n\n "
- "If using Pico Mode via the -p or --pico flags, the "
- "Meta-P toggle, or a nanorc file, the previous search "
- "string will be shown in brackets after the Search: prompt. "
- "Hitting Enter without entering any text will perform the "
- "previous search. Otherwise, the previous string will be "
- "placed before the cursor, and can be edited or deleted "
- "before hitting enter.\n\n The following function keys are "
- "available in Search mode:\n\n");
- else if (currshortcut == goto_list)
- ptr = _("Go To Line Help Text\n\n "
- "Enter the line number that you wish to go to and hit "
- "Enter. If there are fewer lines of text than the "
- "number you entered, you will be brought to the last line "
- "of the file.\n\n The following function keys are "
- "available in Go To Line mode:\n\n");
- else if (currshortcut == insertfile_list)
- ptr = _("Insert File Help Text\n\n "
- "Type in the name of a file to be inserted into the current "
- "file buffer at the current cursor location.\n\n "
- "If you have compiled nano with multiple file buffer "
- "support, and enable multiple buffers with the -F "
- "or --multibuffer command line flags, the Meta-F toggle, or "
- "a nanorc file, inserting a file will cause it to be "
- "loaded into a separate buffer (use Meta-< and > to switch "
- "between file buffers).\n\n If you need another blank "
- "buffer, do not enter any filename, or type in a "
- "nonexistent filename at the prompt and press "
- "Enter.\n\n The following function keys are "
- "available in Insert File mode:\n\n");
- else if (currshortcut == writefile_list)
- ptr = _("Write File Help Text\n\n "
- "Type the name that you wish to save the current file "
- "as and hit Enter to save the file.\n\n If you have "
- "selected text with Ctrl-^, you will be prompted to "
- "save only the selected portion to a separate file. To "
- "reduce the chance of overwriting the current file with "
- "just a portion of it, the current filename is not the "
- "default in this mode.\n\n The following function keys "
- "are available in Write File mode:\n\n");
-#ifndef DISABLE_BROWSER
- else if (currshortcut == browser_list)
- ptr = _("File Browser Help Text\n\n "
- "The file browser is used to visually browse the "
- "directory structure to select a file for reading "
- "or writing. You may use the arrow keys or Page Up/"
- "Down to browse through the files, and S or Enter to "
- "choose the selected file or enter the selected "
- "directory. To move up one level, select the directory "
- "called \"..\" at the top of the file list.\n\n The "
- "following function keys are available in the file "
- "browser:\n\n");
- else if (currshortcut == gotodir_list)
- ptr = _("Browser Go To Directory Help Text\n\n "
- "Enter the name of the directory you would like to "
- "browse to.\n\n If tab completion has not been disabled, "
- "you can use the TAB key to (attempt to) automatically "
- "complete the directory name.\n\n The following function "
- "keys are available in Browser Go To Directory mode:\n\n");
+ if (ISSET(TEMP_OPT)) {
+ i = 1;
+ } else {
+ i = do_yesno(0, 0,
+ _
+ ("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
+ }
+
+#ifdef DEBUG
+ dump_buffer(fileage);
#endif
- else if (currshortcut == spell_list)
- ptr = _("Spell Check Help Text\n\n "
- "The spell checker checks the spelling of all text "
- "in the current file. When an unknown word is "
- "encountered, it is highlighted and a replacement can "
- "be edited. It will then prompt to replace every "
- "instance of the given misspelled word in the "
- "current file.\n\n The following other functions are "
- "available in Spell Check mode:\n\n");
+
+ if (i == 1) {
+ if (do_writeout(filename, 1, 0) > 0) {
+
+#ifdef ENABLE_MULTIBUFFER
+ if (!close_open_file()) {
+ display_main_list();
+ return 1;
+ }
+ else
+#endif
+ finish(0);
+ }
+ } else if (i == 0) {
+
+#ifdef ENABLE_MULTIBUFFER
+ if (!close_open_file()) {
+ display_main_list();
+ return 1;
+ }
+ else
+#endif
+ finish(0);
+ } else
+ statusbar(_("Cancelled"));
+
+ display_main_list();
+ return 1;
+}
+
+void signal_init(void)
+{
+#ifdef _POSIX_VDISABLE
+ struct termios term;
+#endif
+
+ /* Trap SIGINT and SIGQUIT cuz we want them to do useful things. */
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGINT, &act, NULL);
+
+ /* Trap SIGHUP cuz we want to write the file out. */
+ act.sa_handler = handle_hup;
+ sigaction(SIGHUP, &act, NULL);
+
#ifndef NANO_SMALL
- else if (currshortcut == extcmd_list)
- ptr = _("External Command Help Text\n\n "
- "This menu allows you to insert the output of a command "
- "run by the shell into the current buffer (or a new "
- "buffer in multibuffer mode).\n\n The following keys are "
- "available in this mode:\n\n");
+ act.sa_handler = handle_sigwinch;
+ sigaction(SIGWINCH, &act, NULL);
#endif
- else /* Default to the main help list */
- ptr = _(" nano help text\n\n "
- "The nano editor is designed to emulate the functionality and "
- "ease-of-use of the UW Pico text editor. There are four main "
- "sections of the editor: The top line shows the program "
- "version, the current filename being edited, and whether "
- "or not the file has been modified. Next is the main editor "
- "window showing the file being edited. The status line is "
- "the third line from the bottom and shows important messages. "
- "The bottom two lines show the most commonly used shortcuts "
- "in the editor.\n\n "
- "The notation for shortcuts is as follows: Control-key "
- "sequences are notated with a caret (^) symbol and are entered "
- "with the Control (Ctrl) key. Escape-key sequences are notated "
- "with the Meta (M) symbol and can be entered using either the "
- "Esc, Alt or Meta key depending on your keyboard setup. The "
- "following keystrokes are available in the main editor window. "
- "Alternative keys are shown in parentheses:\n\n");
- allocsize += strlen(ptr);
+#ifdef _POSIX_VDISABLE
+ tcgetattr(0, &term);
- /* The space needed for the shortcut lists, at most COLS characters,
- * plus '\n'. */
- allocsize += (COLS + 1) * length_of_list(currshortcut);
+#ifdef VDSUSP
+ term.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif /* VDSUSP */
-#ifndef NANO_SMALL
- /* If we're on the main list, we also count the toggle help text.
- * Each line has "M-%c\t\t\t", which fills 24 columns, plus at most
- * COLS - 24 characters, plus '\n'.*/
- if (currshortcut == main_list)
- for (t = toggles; t != NULL; t = t->next)
- allocsize += COLS - 17;
-#endif /* !NANO_SMALL */
+#endif /* _POSIX_VDISABLE */
- /* help_text has been freed and set to NULL unless the user resized
- * while in the help screen. */
- free(help_text);
+ if (!ISSET(SUSPEND)) {
- /* Allocate space for the help text */
- help_text = charalloc(allocsize);
+/* Insane! */
+#ifdef _POSIX_VDISABLE
+ term.c_cc[VSUSP] = _POSIX_VDISABLE;
+#else
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGTSTP, &act, NULL);
+#endif
- /* Now add the text we want */
- strcpy(help_text, ptr);
- ptr = help_text + strlen(help_text);
+ } else {
+ /* If we don't do this, it seems other stuff interrupts the
+ suspend handler! Try using nano with mutt without this
+ line. */
+ sigfillset(&act.sa_mask);
- /* Now add our shortcut info */
- for (s = currshortcut; s != NULL; s = s->next) {
- /* true if the character in s->altval is shown in first column */
- int meta_shortcut = 0;
+ act.sa_handler = do_suspend;
+ sigaction(SIGTSTP, &act, NULL);
- if (s->val > 0 && s->val < 32)
- ptr += sprintf(ptr, "^%c", s->val + 64);
-#ifndef NANO_SMALL
- else if (s->val == NANO_CONTROL_SPACE)
- ptr += sprintf(ptr, "^%.6s", _("Space"));
- else if (s->altval == NANO_ALT_SPACE) {
- meta_shortcut = 1;
- ptr += sprintf(ptr, "M-%.5s", _("Space"));
- }
+ act.sa_handler = do_cont;
+ sigaction(SIGCONT, &act, NULL);
+ }
+
+#ifdef _POSIX_VDISABLE
+ tcsetattr(0, TCSANOW, &term);
#endif
- else if (s->altval > 0) {
- meta_shortcut = 1;
- ptr += sprintf(ptr, "M-%c", s->altval -
- (('A' <= s->altval && s->altval <= 'Z') ||
- 'a' <= s->altval ? 32 : 0));
- }
- /* Hack */
- else if (s->val >= 'a') {
- meta_shortcut = 1;
- ptr += sprintf(ptr, "M-%c", s->val - 32);
- }
+}
- *(ptr++) = '\t';
+/* Handler for SIGHUP */
+RETSIGTYPE handle_hup(int signal)
+{
+ die(_("Received SIGHUP"));
+}
- if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
- ptr += sprintf(ptr, "(F%d)", s->misc1 - KEY_F0);
+/* What do we do when we catch the suspend signal */
+RETSIGTYPE do_suspend(int signal)
+{
+ endwin();
+ printf("\n\n\n\n\nUse \"fg\" to return to nano\n");
+ fflush(stdout);
- *(ptr++) = '\t';
+ /* Restore the terminal settings for the disabled keys */
+ tcsetattr(0, TCSANOW, &oldterm);
- if (!meta_shortcut && s->altval > 0)
- ptr += sprintf(ptr, "(M-%c)", s->altval -
- (('A' <= s->altval && s->altval <= 'Z') || 'a' <= s->altval
- ? 32 : 0));
+ /* We used to re-enable the default SIG_DFL and raise SIGTSTP, but
+ then we could be (and were) interrupted in the middle of the call.
+ So we do it the mutt way instead */
+ kill(0, SIGSTOP);
+}
- *(ptr++) = '\t';
+/* Restore the suspend handler when we come back into the prog */
+RETSIGTYPE do_cont(int signal)
+{
+ /* Now we just update the screen instead of having to reenable the
+ SIGTSTP handler. */
- assert(s->help != NULL);
- ptr += sprintf(ptr, "%.*s\n", COLS - 24, s->help);
- }
+ doupdate();
+ /* The Hurd seems to need this, otherwise a ^Y after a ^Z will
+ start suspending again. */
+ signal_init();
#ifndef NANO_SMALL
- /* And the toggles... */
- if (currshortcut == main_list)
- for (t = toggles; t != NULL; t = t->next) {
- ptr += sprintf(ptr, "M-%c\t\t\t", t->val - 32);
- assert(t->desc != NULL);
- ptr += sprintf(ptr, _("%.*s enable/disable\n"), COLS - 24, t->desc);
- }
+ /* Perhaps the user resized the window while we slept. */
+ handle_sigwinch(0);
+#endif
+}
+
+#ifndef NANO_SMALL
+void handle_sigwinch(int s)
+{
+ const char *tty = ttyname(0);
+ int fd;
+ int result = 0;
+ struct winsize win;
+
+ if (!tty)
+ return;
+ fd = open(tty, O_RDWR);
+ if (fd == -1)
+ return;
+ result = ioctl(fd, TIOCGWINSZ, &win);
+ close(fd);
+ if (result == -1)
+ return;
+
+ /* Could check whether the COLS or LINES changed, and return
+ * otherwise. EXCEPT, that COLS and LINES are ncurses global
+ * variables, and in some cases ncurses has already updated them.
+ * But not in all cases, argh. */
+ COLS = win.ws_col;
+ LINES = win.ws_row;
+ if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
+ die_too_small();
+
+#ifndef DISABLE_WRAPJUSTIFY
+ fill = wrap_at;
+ if (fill <= 0)
+ fill += COLS;
+ if (fill < MIN_FILL_LENGTH)
+ die_too_small();
+#endif
+
+ hblank = nrealloc(hblank, COLS + 1);
+ memset(hblank, ' ', COLS);
+ hblank[COLS] = '\0';
+
+#ifdef HAVE_RESIZETERM
+ resizeterm(LINES, COLS);
+#ifdef HAVE_WRESIZE
+ if (wresize(topwin, 2, COLS) == ERR)
+ die(_("Cannot resize top win"));
+ if (mvwin(topwin, 0, 0) == ERR)
+ die(_("Cannot move top win"));
+ if (wresize(edit, editwinrows, COLS) == ERR)
+ die(_("Cannot resize edit win"));
+ if (mvwin(edit, 2, 0) == ERR)
+ die(_("Cannot move edit win"));
+ if (wresize(bottomwin, 3 - no_help(), COLS) == ERR)
+ die(_("Cannot resize bottom win"));
+ if (mvwin(bottomwin, LINES - 3 + no_help(), 0) == ERR)
+ die(_("Cannot move bottom win"));
+#endif /* HAVE_WRESIZE */
+#endif /* HAVE_RESIZETERM */
+
+ fix_editbot();
+
+ if (current_y > editwinrows - 1)
+ edit_update(editbot, CENTER);
+ erase();
+
+ /* Do these b/c width may have changed... */
+ refresh();
+ titlebar(NULL);
+ edit_refresh();
+ display_main_list();
+ blank_statusbar();
+ total_refresh();
+
+ /* Turn cursor back on for sure */
+ curs_set(1);
+
+ /* Jump back to main loop */
+ siglongjmp(jmpbuf, 1);
+}
#endif /* !NANO_SMALL */
- /* If all went well, we didn't overwrite the allocated space for
- help_text. */
- assert(strlen(help_text) < allocsize);
+/* If the NumLock key has made the keypad go awry, print an error
+ message; hopefully we can address it later. */
+void print_numlock_warning(void)
+{
+ static int didmsg = 0;
+ if (!didmsg) {
+ statusbar(_
+ ("NumLock glitch detected. Keypad will malfunction with NumLock off"));
+ didmsg = 1;
+ }
}
-#endif
#ifndef NANO_SMALL
void do_toggle(const toggle *which)
}
#endif /* !NANO_SMALL */
-/* If the NumLock key has made the keypad go awry, print an error
- message; hopefully we can address it later. */
-void print_numlock_warning(void)
-{
- static int didmsg = 0;
- if (!didmsg) {
- statusbar(_
- ("NumLock glitch detected. Keypad will malfunction with NumLock off"));
- didmsg = 1;
- }
-}
-
/* This function returns the correct keystroke, given the A,B,C or D
input key. This is a common sequence of many terms which send
Esc-O-[A-D] or Esc-[-[A-D]. */
/* Look through the main shortcut list to see if we've hit a
shortcut key */
-
+
#if !defined(DISABLE_BROWSER) || !defined(DISABLE_MOUSE) || !defined (DISABLE_HELP)
for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
#else
else
s->func();
keyhandled = 1;
+ /* rarely, the value of s can change after s->func(),
+ leading to problems; get around this by breaking out
+ explicitly once we successfully handle a shortcut */
+ break;
}
}
/* If we're in raw mode or using Alt-Alt-x, we have to catch
keyhandled = 1;
}
-
#ifndef USE_SLANG
/* Hack, make insert key do something useful, like insert file */
if (kbinput == KEY_IC) {