summaryrefslogtreecommitdiff
path: root/src/nano.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nano.c')
-rw-r--r--src/nano.c2684
1 files changed, 2684 insertions, 0 deletions
diff --git a/src/nano.c b/src/nano.c
new file mode 100644
index 0000000..e4e2222
--- /dev/null
+++ b/src/nano.c
@@ -0,0 +1,2684 @@
+/* $Id: nano.c 4520 2010-11-12 06:23:14Z astyanax $ */
+/**************************************************************************
+ * nano.c *
+ * *
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
+ * 2008, 2009 Free Software Foundation, Inc. *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 3, or (at your option) *
+ * any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, but *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
+ * 02110-1301, USA. *
+ * *
+ **************************************************************************/
+
+#include "proto.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <locale.h>
+#include <time.h>
+#ifdef ENABLE_UTF8
+#include <langinfo.h>
+#endif
+#include <termios.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#ifndef NANO_TINY
+#include <sys/ioctl.h>
+#endif
+
+#ifndef DISABLE_MOUSE
+static int oldinterval = -1;
+ /* Used to store the user's original mouse click interval. */
+#endif
+#ifdef ENABLE_NANORC
+static bool no_rcfiles = FALSE;
+ /* Should we ignore all rcfiles? */
+#endif
+static struct termios oldterm;
+ /* The user's original terminal settings. */
+static struct sigaction act;
+ /* Used to set up all our fun signal handlers. */
+
+/* Create a new filestruct node. Note that we do not set prevnode->next
+ * 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;
+
+#ifdef ENABLE_COLOR
+ newnode->multidata = NULL;
+#endif
+
+ return newnode;
+}
+
+/* Make a copy of a filestruct node. */
+filestruct *copy_node(const filestruct *src)
+{
+ filestruct *dst;
+
+ assert(src != NULL);
+
+ dst = (filestruct *)nmalloc(sizeof(filestruct));
+
+ dst->data = mallocstrcpy(NULL, src->data);
+ dst->next = src->next;
+ dst->prev = src->prev;
+ dst->lineno = src->lineno;
+#ifdef ENABLE_COLOR
+ dst->multidata = NULL;
+#endif
+
+ return dst;
+}
+
+/* Splice a node into an existing filestruct. */
+void splice_node(filestruct *begin, filestruct *newnode, filestruct
+ *end)
+{
+ assert(newnode != NULL && begin != NULL);
+
+ newnode->next = end;
+ newnode->prev = begin;
+ begin->next = newnode;
+ if (end != NULL)
+ end->prev = newnode;
+}
+
+/* Unlink a node from the rest of the filestruct. */
+void unlink_node(const filestruct *fileptr)
+{
+ assert(fileptr != NULL);
+
+ if (fileptr->prev != NULL)
+ fileptr->prev->next = fileptr->next;
+ if (fileptr->next != NULL)
+ fileptr->next->prev = fileptr->prev;
+}
+
+/* Delete a node from the filestruct. */
+void delete_node(filestruct *fileptr)
+{
+ assert(fileptr != NULL && fileptr->data != NULL);
+
+ if (fileptr->data != NULL)
+ free(fileptr->data);
+
+#ifdef ENABLE_COLOR
+ if (fileptr->multidata)
+ free(fileptr->multidata);
+#endif
+
+ free(fileptr);
+}
+
+/* Duplicate a whole filestruct. */
+filestruct *copy_filestruct(const filestruct *src)
+{
+ filestruct *head, *copy;
+
+ assert(src != NULL);
+
+ copy = copy_node(src);
+ copy->prev = NULL;
+ head = copy;
+ src = src->next;
+
+ while (src != NULL) {
+ copy->next = copy_node(src);
+ copy->next->prev = copy;
+ copy = copy->next;
+
+ src = src->next;
+ }
+
+ copy->next = NULL;
+
+ return head;
+}
+
+/* Free a filestruct. */
+void free_filestruct(filestruct *src)
+{
+ assert(src != NULL);
+
+ while (src->next != NULL) {
+ src = src->next;
+ delete_node(src->prev);
+ }
+
+ delete_node(src);
+}
+
+/* Renumber all entries in a filestruct, starting with fileptr. */
+void renumber(filestruct *fileptr)
+{
+ ssize_t line;
+
+ assert(fileptr != NULL);
+
+ line = (fileptr->prev == NULL) ? 0 : fileptr->prev->lineno;
+
+ assert(fileptr != fileptr->next);
+
+ for (; fileptr != NULL; fileptr = fileptr->next)
+ fileptr->lineno = ++line;
+}
+
+/* Partition a filestruct so that it begins at (top, top_x) and ends at
+ * (bot, bot_x). */
+partition *partition_filestruct(filestruct *top, size_t top_x,
+ filestruct *bot, size_t bot_x)
+{
+ partition *p;
+
+ assert(top != NULL && bot != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
+
+ /* Initialize the partition. */
+ p = (partition *)nmalloc(sizeof(partition));
+
+ /* If the top and bottom of the partition are different from the top
+ * and bottom of the filestruct, save the latter and then set them
+ * to top and bot. */
+ if (top != openfile->fileage) {
+ p->fileage = openfile->fileage;
+ openfile->fileage = top;
+ } else
+ p->fileage = NULL;
+ if (bot != openfile->filebot) {
+ p->filebot = openfile->filebot;
+ openfile->filebot = bot;
+ } else
+ p->filebot = NULL;
+
+ /* Save the line above the top of the partition, detach the top of
+ * the partition from it, and save the text before top_x in
+ * top_data. */
+ p->top_prev = top->prev;
+ top->prev = NULL;
+ p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
+ p->top_data[top_x] = '\0';
+
+ /* Save the line below the bottom of the partition, detach the
+ * bottom of the partition from it, and save the text after bot_x in
+ * bot_data. */
+ p->bot_next = bot->next;
+ bot->next = NULL;
+ p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
+
+ /* Remove all text after bot_x at the bottom of the partition. */
+ null_at(&bot->data, bot_x);
+
+ /* Remove all text before top_x at the top of the partition. */
+ charmove(top->data, top->data + top_x, strlen(top->data) -
+ top_x + 1);
+ align(&top->data);
+
+ /* Return the partition. */
+ return p;
+}
+
+/* Unpartition a filestruct so that it begins at (fileage, 0) and ends
+ * at (filebot, strlen(filebot->data)) again. */
+void unpartition_filestruct(partition **p)
+{
+ char *tmp;
+
+ assert(p != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
+
+ /* Reattach the line above the top of the partition, and restore the
+ * text before top_x from top_data. Free top_data when we're done
+ * with it. */
+ tmp = mallocstrcpy(NULL, openfile->fileage->data);
+ openfile->fileage->prev = (*p)->top_prev;
+ if (openfile->fileage->prev != NULL)
+ openfile->fileage->prev->next = openfile->fileage;
+ openfile->fileage->data = charealloc(openfile->fileage->data,
+ strlen((*p)->top_data) + strlen(openfile->fileage->data) + 1);
+ strcpy(openfile->fileage->data, (*p)->top_data);
+ free((*p)->top_data);
+ strcat(openfile->fileage->data, tmp);
+ free(tmp);
+
+ /* Reattach the line below the bottom of the partition, and restore
+ * the text after bot_x from bot_data. Free bot_data when we're
+ * done with it. */
+ openfile->filebot->next = (*p)->bot_next;
+ if (openfile->filebot->next != NULL)
+ openfile->filebot->next->prev = openfile->filebot;
+ openfile->filebot->data = charealloc(openfile->filebot->data,
+ strlen(openfile->filebot->data) + strlen((*p)->bot_data) + 1);
+ strcat(openfile->filebot->data, (*p)->bot_data);
+ free((*p)->bot_data);
+
+ /* Restore the top and bottom of the filestruct, if they were
+ * different from the top and bottom of the partition. */
+ if ((*p)->fileage != NULL)
+ openfile->fileage = (*p)->fileage;
+ if ((*p)->filebot != NULL)
+ openfile->filebot = (*p)->filebot;
+
+ /* Uninitialize the partition. */
+ free(*p);
+ *p = NULL;
+}
+
+/* Move all the text between (top, top_x) and (bot, bot_x) in the
+ * current filestruct to a filestruct beginning with file_top and ending
+ * with file_bot. If no text is between (top, top_x) and (bot, bot_x),
+ * don't do anything. */
+void move_to_filestruct(filestruct **file_top, filestruct **file_bot,
+ filestruct *top, size_t top_x, filestruct *bot, size_t bot_x)
+{
+ filestruct *top_save;
+ bool edittop_inside;
+#ifndef NANO_TINY
+ bool mark_inside = FALSE;
+#endif
+
+ assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL);
+
+ /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */
+ if (top == bot && top_x == bot_x)
+ return;
+
+ /* Partition the filestruct so that it contains only the text from
+ * (top, top_x) to (bot, bot_x), keep track of whether the top of
+ * the edit window is inside the partition, and keep track of
+ * whether the mark begins inside the partition. */
+ filepart = partition_filestruct(top, top_x, bot, bot_x);
+ edittop_inside = (openfile->edittop->lineno >=
+ openfile->fileage->lineno && openfile->edittop->lineno <=
+ openfile->filebot->lineno);
+#ifndef NANO_TINY
+ if (openfile->mark_set)
+ mark_inside = (openfile->mark_begin->lineno >=
+ openfile->fileage->lineno &&
+ openfile->mark_begin->lineno <=
+ openfile->filebot->lineno &&
+ (openfile->mark_begin != openfile->fileage ||
+ openfile->mark_begin_x >= top_x) &&
+ (openfile->mark_begin != openfile->filebot ||
+ openfile->mark_begin_x <= bot_x));
+#endif
+
+ /* Get the number of characters in the text, and subtract it from
+ * totsize. */
+ openfile->totsize -= get_totsize(top, bot);
+
+ if (*file_top == NULL) {
+ /* If file_top is empty, just move all the text directly into
+ * it. This is equivalent to tacking the text in top onto the
+ * (lack of) text at the end of file_top. */
+ *file_top = openfile->fileage;
+ *file_bot = openfile->filebot;
+
+ /* Renumber starting with file_top. */
+ renumber(*file_top);
+ } else {
+ filestruct *file_bot_save = *file_bot;
+
+ /* Otherwise, tack the text in top onto the text at the end of
+ * file_bot. */
+ (*file_bot)->data = charealloc((*file_bot)->data,
+ strlen((*file_bot)->data) +
+ strlen(openfile->fileage->data) + 1);
+ strcat((*file_bot)->data, openfile->fileage->data);
+
+ /* Attach the line after top to the line after file_bot. Then,
+ * if there's more than one line after top, move file_bot down
+ * to bot. */
+ (*file_bot)->next = openfile->fileage->next;
+ if ((*file_bot)->next != NULL) {
+ (*file_bot)->next->prev = *file_bot;
+ *file_bot = openfile->filebot;
+ }
+
+ /* Renumber starting with the line after the original
+ * file_bot. */
+ if (file_bot_save->next != NULL)
+ renumber(file_bot_save->next);
+ }
+
+ /* Since the text has now been saved, remove it from the
+ * filestruct. */
+ openfile->fileage = (filestruct *)nmalloc(sizeof(filestruct));
+ openfile->fileage->data = mallocstrcpy(NULL, "");
+ openfile->filebot = openfile->fileage;
+
+#ifdef ENABLE_COLOR
+ openfile->fileage->multidata = NULL;
+#endif
+
+ /* Restore the current line and cursor position. If the mark begins
+ * inside the partition, set the beginning of the mark to where the
+ * saved text used to start. */
+ openfile->current = openfile->fileage;
+ openfile->current_x = top_x;
+#ifndef NANO_TINY
+ if (mark_inside) {
+ openfile->mark_begin = openfile->current;
+ openfile->mark_begin_x = openfile->current_x;
+ }
+#endif
+
+ top_save = openfile->fileage;
+
+ /* Unpartition the filestruct so that it contains all the text
+ * again, minus the saved text. */
+ unpartition_filestruct(&filepart);
+
+ /* If the top of the edit window was inside the old partition, put
+ * it in range of current. */
+ if (edittop_inside)
+ edit_update(NONE);
+
+ /* Renumber starting with the beginning line of the old
+ * partition. */
+ renumber(top_save);
+
+ /* If the NO_NEWLINES flag isn't set, and the text doesn't end with
+ * a magicline, add a new magicline. */
+ if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
+ new_magicline();
+}
+
+/* Copy all the text from the filestruct beginning with file_top and
+ * ending with file_bot to the current filestruct at the current cursor
+ * position. */
+void copy_from_filestruct(filestruct *file_top, filestruct *file_bot)
+{
+ filestruct *top_save;
+ size_t current_x_save = openfile->current_x;
+ bool edittop_inside;
+#ifndef NANO_TINY
+ bool right_side_up = FALSE, single_line = FALSE;
+#endif
+
+ assert(file_top != NULL && file_bot != NULL);
+
+#ifndef NANO_TINY
+ /* Keep track of whether the mark begins inside the partition and
+ * will need adjustment. */
+ if (openfile->mark_set) {
+ filestruct *top, *bot;
+ size_t top_x, bot_x;
+
+ mark_order((const filestruct **)&top, &top_x,
+ (const filestruct **)&bot, &bot_x, &right_side_up);
+
+ single_line = (top == bot);
+ }
+#endif
+
+ /* Partition the filestruct so that it contains no text, and keep
+ * track of whether the top of the edit window is inside the
+ * partition. */
+ filepart = partition_filestruct(openfile->current,
+ openfile->current_x, openfile->current, openfile->current_x);
+ edittop_inside = (openfile->edittop == openfile->fileage);
+
+ /* Put the top and bottom of the filestruct at copies of file_top
+ * and file_bot. */
+ openfile->fileage = copy_filestruct(file_top);
+ openfile->filebot = openfile->fileage;
+ while (openfile->filebot->next != NULL)
+ openfile->filebot = openfile->filebot->next;
+
+ /* Restore the current line and cursor position. If the mark begins
+ * inside the partition, adjust the mark coordinates to compensate
+ * for the change in the current line. */
+ openfile->current = openfile->filebot;
+ openfile->current_x = strlen(openfile->filebot->data);
+ if (openfile->fileage == openfile->filebot) {
+#ifndef NANO_TINY
+ if (openfile->mark_set) {
+ openfile->mark_begin = openfile->current;
+ if (!right_side_up)
+ openfile->mark_begin_x += openfile->current_x;
+ }
+#endif
+ openfile->current_x += current_x_save;
+ }
+#ifndef NANO_TINY
+ else if (openfile->mark_set) {
+ if (!right_side_up) {
+ if (single_line) {
+ openfile->mark_begin = openfile->current;
+ openfile->mark_begin_x -= current_x_save;
+ } else
+ openfile->mark_begin_x -= openfile->current_x;
+ }
+ }
+#endif
+
+ /* Get the number of characters in the copied text, and add it to
+ * totsize. */
+ openfile->totsize += get_totsize(openfile->fileage,
+ openfile->filebot);
+
+ /* Update the current y-coordinate to account for the number of
+ * lines the copied text has, less one since the first line will be
+ * tacked onto the current line. */
+ openfile->current_y += openfile->filebot->lineno - 1;
+
+ top_save = openfile->fileage;
+
+ /* If the top of the edit window is inside the partition, set it to
+ * where the copied text now starts. */
+ if (edittop_inside)
+ openfile->edittop = openfile->fileage;
+
+ /* Unpartition the filestruct so that it contains all the text
+ * again, plus the copied text. */
+ unpartition_filestruct(&filepart);
+
+ /* Renumber starting with the beginning line of the old
+ * partition. */
+ renumber(top_save);
+
+ /* If the NO_NEWLINES flag isn't set, and the text doesn't end with
+ * a magicline, add a new magicline. */
+ if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
+ new_magicline();
+}
+
+/* Create a new openfilestruct node. */
+openfilestruct *make_new_opennode(void)
+{
+ openfilestruct *newnode =
+ (openfilestruct *)nmalloc(sizeof(openfilestruct));
+
+ newnode->filename = NULL;
+ newnode->fileage = NULL;
+ newnode->filebot = NULL;
+ newnode->edittop = NULL;
+ newnode->current = NULL;
+#ifndef NANO_TINY
+ newnode->current_stat = NULL;
+ newnode->last_action = OTHER;
+#endif
+
+ return newnode;
+}
+
+/* Splice a node into an existing openfilestruct. */
+void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
+ openfilestruct *end)
+{
+ assert(newnode != NULL && begin != NULL);
+
+ newnode->next = end;
+ newnode->prev = begin;
+ begin->next = newnode;
+
+ if (end != NULL)
+ end->prev = newnode;
+}
+
+/* Unlink a node from the rest of the openfilestruct, and delete it. */
+void unlink_opennode(openfilestruct *fileptr)
+{
+ assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next);
+
+ fileptr->prev->next = fileptr->next;
+ fileptr->next->prev = fileptr->prev;
+
+ delete_opennode(fileptr);
+}
+
+/* Delete a node from the openfilestruct. */
+void delete_opennode(openfilestruct *fileptr)
+{
+ assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);
+
+ free(fileptr->filename);
+ free_filestruct(fileptr->fileage);
+#ifndef NANO_TINY
+ if (fileptr->current_stat != NULL)
+ free(fileptr->current_stat);
+#endif
+
+ free(fileptr);
+}
+
+#ifdef DEBUG
+/* Deallocate all memory associated with this and later files, including
+ * the lines of text. */
+void free_openfilestruct(openfilestruct *src)
+{
+ assert(src != NULL);
+
+ while (src != src->next) {
+ src = src->next;
+ delete_opennode(src->prev);
+ }
+
+ delete_opennode(src);
+}
+#endif
+
+/* Display a warning about a key disabled in view mode. */
+void print_view_warning(void)
+{
+ statusbar(_("Key invalid in view mode"));
+}
+
+/* Make nano exit gracefully. */
+void finish(void)
+{
+ /* Blank the statusbar (and shortcut list, if applicable), and move
+ * the cursor to the last line of the screen. */
+ if (!ISSET(NO_HELP))
+ blank_bottombars();
+ else
+ blank_statusbar();
+ wrefresh(bottomwin);
+ endwin();
+
+ /* Restore the old terminal settings. */
+ tcsetattr(0, TCSANOW, &oldterm);
+
+#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
+ if (!no_rcfiles && ISSET(HISTORYLOG))
+ save_history();
+#endif
+
+#ifdef DEBUG
+ thanks_for_all_the_fish();
+#endif
+
+ /* Get out. */
+ exit(0);
+}
+
+/* Make nano die gracefully. */
+void die(const char *msg, ...)
+{
+ va_list ap;
+
+ endwin();
+
+ /* Restore the old terminal settings. */
+ tcsetattr(0, TCSANOW, &oldterm);
+
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+
+ /* Save the current file buffer if it's been modified. */
+ if (openfile && openfile->modified) {
+ /* If we've partitioned the filestruct, unpartition it now. */
+ if (filepart != NULL)
+ unpartition_filestruct(&filepart);
+
+ die_save_file(openfile->filename
+#ifndef NANO_TINY
+ , openfile->current_stat
+#endif
+ );
+ }
+
+#ifdef ENABLE_MULTIBUFFER
+ /* Save all of the other modified file buffers, if any. */
+ if (openfile != NULL) {
+ openfilestruct *tmp = openfile;
+
+ while (tmp != openfile->next) {
+ openfile = openfile->next;
+
+ /* Save the current file buffer if it's been modified. */
+ if (openfile->modified)
+ die_save_file(openfile->filename
+#ifndef NANO_TINY
+ , openfile->current_stat
+#endif
+ );
+ }
+ }
+#endif
+
+ /* Get out. */
+ exit(1);
+}
+
+/* Save the current file under the name spacified in die_filename, which
+ * is modified to be unique if necessary. */
+void die_save_file(const char *die_filename
+#ifndef NANO_TINY
+ , struct stat *die_stat
+#endif
+ )
+{
+ char *retval;
+ bool failed = TRUE;
+
+ /* If we're using restricted mode, don't write any emergency backup
+ * files, since that would allow reading from or writing to files
+ * not specified on the command line. */
+ if (ISSET(RESTRICTED))
+ return;
+
+ /* If we can't save, we have really bad problems, but we might as
+ * well try. */
+ if (*die_filename == '\0')
+ die_filename = "nano";
+
+ retval = get_next_filename(die_filename, ".save");
+ if (retval[0] != '\0')
+ failed = !write_file(retval, NULL, TRUE, OVERWRITE, TRUE);
+
+ if (!failed)
+ fprintf(stderr, _("\nBuffer written to %s\n"), retval);
+ else if (retval[0] != '\0')
+ fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval,
+ strerror(errno));
+ else
+ fprintf(stderr, _("\nBuffer not written: %s\n"),
+ _("Too many backup files?"));
+
+#ifndef NANO_TINY
+ /* Try and chmod/chown the save file to the values of the original file, but
+ dont worry if it fails because we're supposed to be bailing as fast
+ as possible. */
+ if (die_stat) {
+ int shush;
+ shush = chmod(retval, die_stat->st_mode);
+ shush = chown(retval, die_stat->st_uid, die_stat->st_gid);
+ }
+#endif
+
+ free(retval);
+}
+
+/* Initialize the three window portions nano uses. */
+void window_init(void)
+{
+ /* If the screen height is too small, get out. */
+ editwinrows = LINES - 5 + no_more_space() + no_help();
+ if (COLS < MIN_EDITOR_COLS || editwinrows < MIN_EDITOR_ROWS)
+ die(_("Window size is too small for nano...\n"));
+
+#ifndef DISABLE_WRAPJUSTIFY
+ /* Set up fill, based on the screen width. */
+ fill = wrap_at;
+ if (fill <= 0)
+ fill += COLS;
+ if (fill < 0)
+ fill = 0;
+#endif
+
+ if (topwin != NULL)
+ delwin(topwin);
+ if (edit != NULL)
+ delwin(edit);
+ if (bottomwin != NULL)
+ delwin(bottomwin);
+
+ /* Set up the windows. */
+ topwin = newwin(2 - no_more_space(), COLS, 0, 0);
+ edit = newwin(editwinrows, COLS, 2 - no_more_space(), 0);
+ bottomwin = newwin(3 - no_help(), COLS, editwinrows + (2 -
+ no_more_space()), 0);
+
+ /* Turn the keypad on for the windows, if necessary. */
+ if (!ISSET(REBIND_KEYPAD)) {
+ keypad(topwin, TRUE);
+ keypad(edit, TRUE);
+ keypad(bottomwin, TRUE);
+ }
+}
+
+#ifndef DISABLE_MOUSE
+/* Disable mouse support. */
+void disable_mouse_support(void)
+{
+ mousemask(0, NULL);
+ mouseinterval(oldinterval);
+}
+
+/* Enable mouse support. */
+void enable_mouse_support(void)
+{
+ mousemask(ALL_MOUSE_EVENTS, NULL);
+ oldinterval = mouseinterval(50);
+}
+
+/* Initialize mouse support. Enable it if the USE_MOUSE flag is set,
+ * and disable it otherwise. */
+void mouse_init(void)
+{
+ if (ISSET(USE_MOUSE))
+ enable_mouse_support();
+ else
+ disable_mouse_support();
+}
+#endif /* !DISABLE_MOUSE */
+
+#ifdef HAVE_GETOPT_LONG
+#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, longflag, desc)
+#else
+#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, desc)
+#endif
+
+/* Print one usage string to the screen. This cuts down on duplicate
+ * strings to translate, and leaves out the parts that shouldn't be
+ * translatable (i.e. the flag names). */
+void print_opt_full(const char *shortflag
+#ifdef HAVE_GETOPT_LONG
+ , const char *longflag
+#endif
+ , const char *desc)
+{
+ printf(" %s\t", shortflag);
+ if (strlenpt(shortflag) < 8)
+ printf("\t");
+
+#ifdef HAVE_GETOPT_LONG
+ printf("%s\t", longflag);
+ if (strlenpt(longflag) < 8)
+ printf("\t\t");
+ else if (strlenpt(longflag) < 16)
+ printf("\t");
+#endif
+
+ if (desc != NULL)
+ printf("%s", _(desc));
+ printf("\n");
+}
+
+/* Explain how to properly use nano and its command line options. */
+void usage(void)
+{
+ printf(_("Usage: nano [OPTIONS] [[+LINE,COLUMN] FILE]...\n\n"));
+ printf(
+#ifdef HAVE_GETOPT_LONG
+ _("Option\t\tGNU long option\t\tMeaning\n")
+#else
+ _("Option\t\tMeaning\n")
+#endif
+ );
+ print_opt("-h, -?", "--help", N_("Show this message"));
+ print_opt(_("+LINE,COLUMN"), "",
+ N_("Start at line LINE, column COLUMN"));
+#ifndef NANO_TINY
+ print_opt("-A", "--smarthome", N_("Enable smart home key"));
+ print_opt("-B", "--backup", N_("Save backups of existing files"));
+ print_opt(_("-C <dir>"), _("--backupdir=<dir>"),
+ N_("Directory for saving unique backup files"));
+#endif
+ print_opt("-D", "--boldtext",
+ N_("Use bold instead of reverse video text"));
+#ifndef NANO_TINY
+ print_opt("-E", "--tabstospaces",
+ N_("Convert typed tabs to spaces"));
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ print_opt("-F", "--multibuffer", N_("Enable multiple file buffers"));
+#endif
+#ifdef ENABLE_NANORC
+#ifndef NANO_TINY
+ print_opt("-H", "--historylog",
+ N_("Log & read search/replace string history"));
+#endif
+ print_opt("-I", "--ignorercfiles",
+ N_("Don't look at nanorc files"));
+#endif
+ print_opt("-K", "--rebindkeypad",
+ N_("Fix numeric keypad key confusion problem"));
+ print_opt("-L", "--nonewlines",
+ N_("Don't add newlines to the ends of files"));
+#ifndef NANO_TINY
+ print_opt("-N", "--noconvert",
+ N_("Don't convert files from DOS/Mac format"));
+#endif
+ print_opt("-O", "--morespace", N_("Use one more line for editing"));
+#ifndef DISABLE_JUSTIFY
+ print_opt(_("-Q <str>"), _("--quotestr=<str>"),
+ N_("Quoting string"));
+#endif
+ print_opt("-R", "--restricted", N_("Restricted mode"));
+#ifndef NANO_TINY
+ print_opt("-S", "--smooth",
+ N_("Scroll by line instead of half-screen"));
+#endif
+ print_opt(_("-T <#cols>"), _("--tabsize=<#cols>"),
+ N_("Set width of a tab to #cols columns"));
+#ifndef NANO_TINY
+ print_opt("-U", "--quickblank", N_("Do quick statusbar blanking"));
+#endif
+ print_opt("-V", "--version",
+ N_("Print version information and exit"));
+#ifndef NANO_TINY
+ print_opt("-W", "--wordbounds",
+ N_("Detect word boundaries more accurately"));
+#endif
+#ifdef ENABLE_COLOR
+ print_opt(_("-Y <str>"), _("--syntax=<str>"),
+ N_("Syntax definition to use for coloring"));
+#endif
+ print_opt("-c", "--const", N_("Constantly show cursor position"));
+ print_opt("-d", "--rebinddelete",
+ N_("Fix Backspace/Delete confusion problem"));
+#ifndef NANO_TINY
+ print_opt("-i", "--autoindent",
+ N_("Automatically indent new lines"));
+ print_opt("-k", "--cut", N_("Cut from cursor to end of line"));
+#endif
+ print_opt("-l", "--nofollow",
+ N_("Don't follow symbolic links, overwrite"));
+#ifndef DISABLE_MOUSE
+ print_opt("-m", "--mouse", N_("Enable the use of the mouse"));
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ print_opt(_("-o <dir>"), _("--operatingdir=<dir>"),
+ N_("Set operating directory"));
+#endif
+ print_opt("-p", "--preserve",
+ N_("Preserve XON (^Q) and XOFF (^S) keys"));
+ print_opt("-q", "--quiet",
+ N_("Silently ignore startup issues like rc file errors"));
+#ifndef DISABLE_WRAPJUSTIFY
+ print_opt(_("-r <#cols>"), _("--fill=<#cols>"),
+ N_("Set wrapping point at column #cols"));
+#endif
+#ifndef DISABLE_SPELLER
+ print_opt(_("-s <prog>"), _("--speller=<prog>"),
+ N_("Enable alternate speller"));
+#endif
+ print_opt("-t", "--tempfile",
+ N_("Auto save on exit, don't prompt"));
+#ifndef NANO_TINY
+ print_opt("-u", "--undo", N_("Allow generic undo [EXPERIMENTAL]"));
+#endif
+
+ print_opt("-v", "--view", N_("View mode (read-only)"));
+#ifndef DISABLE_WRAPPING
+ print_opt("-w", "--nowrap", N_("Don't wrap long lines"));
+#endif
+ print_opt("-x", "--nohelp", N_("Don't show the two help lines"));
+ print_opt("-z", "--suspend", N_("Enable suspension"));
+ print_opt("-$", "--softwrap", N_("Enable soft line wrapping"));
+
+ /* This is a special case. */
+ print_opt("-a, -b, -e,", "", NULL);
+ print_opt("-f, -g, -j", "", N_("(ignored, for Pico compatibility)"));
+
+ exit(0);
+}
+
+/* Display the current version of nano, the date and time it was
+ * compiled, contact information for it, and the configuration options
+ * it was compiled with. */
+void version(void)
+{
+ printf(_(" GNU nano version %s (compiled %s, %s)\n"), VERSION,
+ __TIME__, __DATE__);
+ printf(" (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,\n");
+ printf(" 2008, 2009 Free Software Foundation, Inc.\n");
+ printf(
+ _(" Email: nano@nano-editor.org Web: http://www.nano-editor.org/"));
+ printf(_("\n Compiled options:"));
+
+#ifdef DISABLE_BROWSER
+ printf(" --disable-browser");
+#endif
+#ifdef DISABLE_HELP
+ printf(" --disable-help");
+#endif
+#ifdef DISABLE_JUSTIFY
+ printf(" --disable-justify");
+#endif
+#ifdef DISABLE_MOUSE
+ printf(" --disable-mouse");
+#endif
+#ifndef ENABLE_NLS
+ printf(" --disable-nls");
+#endif
+#ifdef DISABLE_OPERATINGDIR
+ printf(" --disable-operatingdir");
+#endif
+#ifdef DISABLE_SPELLER
+ printf(" --disable-speller");
+#endif
+#ifdef DISABLE_TABCOMP
+ printf(" --disable-tabcomp");
+#endif
+#ifdef DISABLE_WRAPPING
+ printf(" --disable-wrapping");
+#endif
+#ifdef DISABLE_ROOTWRAPPING
+ printf(" --disable-wrapping-as-root");
+#endif
+#ifdef ENABLE_COLOR
+ printf(" --enable-color");
+#endif
+#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 NANO_TINY
+ printf(" --enable-tiny");
+#endif
+#ifdef ENABLE_UTF8
+ printf(" --enable-utf8");
+#endif
+#ifdef USE_SLANG
+ printf(" --with-slang");
+#endif
+ printf("\n");
+}
+
+/* Return 1 if the MORE_SPACE flag is set, and 0 otherwise. This is
+ * used to calculate the relative screen position while taking this flag
+ * into account, since it adds one line to the edit window. */
+int no_more_space(void)
+{
+ return ISSET(MORE_SPACE) ? 1 : 0;
+}
+
+/* Return 2 if the NO_HELP flag is set, and 0 otherwise. This is used
+ * to calculate the relative screen position while taking this flag into
+ * account, since it removes two lines from the edit window. */
+int no_help(void)
+{
+ return ISSET(NO_HELP) ? 2 : 0;
+}
+
+/* Indicate a disabled function on the statusbar. */
+void nano_disabled_msg(void)
+{
+ statusbar(_("Sorry, support for this function has been disabled"));
+}
+
+/* If the current file buffer has been modified, and the TEMP_FILE flag
+ * isn't set, ask whether or not to save the file buffer. If the
+ * TEMP_FILE flag is set, save it unconditionally. Then, if more than
+ * one file buffer is open, close the current file buffer and switch to
+ * the next one. If only one file buffer is open, exit from nano. */
+void do_exit(void)
+{
+ int i;
+
+ /* If the file hasn't been modified, pretend the user chose not to
+ * save. */
+ if (!openfile->modified)
+ i = 0;
+ /* If the TEMP_FILE flag is set, pretend the user chose to save. */
+ else if (ISSET(TEMP_FILE))
+ i = 1;
+ /* Otherwise, ask the user whether or not to save. */
+ else
+ i = do_yesno_prompt(FALSE,
+ _("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
+
+#ifdef DEBUG
+ dump_filestruct(openfile->fileage);
+#endif
+
+ /* If the user chose not to save, or if the user chose to save and
+ * the save succeeded, we're ready to exit. */
+ if (i == 0 || (i == 1 && do_writeout(TRUE))) {
+#ifdef ENABLE_MULTIBUFFER
+ /* Exit only if there are no more open file buffers. */
+ if (!close_buffer())
+#endif
+ finish();
+ /* If the user canceled, we go on. */
+ } else if (i != 1)
+ statusbar(_("Cancelled"));
+
+ shortcut_init(FALSE);
+ display_main_list();
+}
+
+
+
+static struct sigaction pager_oldaction, pager_newaction; /* Original and temporary handlers for SIGINT. */
+static bool pager_sig_failed = FALSE; /* Did sigaction() fail without changing the signal handlers? */
+static bool pager_input_aborted = FALSE; /* Did someone invoke the pager and abort it via ^C? */
+
+
+/* Things which need to be run regardless of whether
+ we finished the stdin pipe correctly or not */
+void finish_stdin_pager(void)
+{
+ FILE *f;
+ int ttystdin;
+
+ /* Read whatever we did get from stdin */
+ f = fopen("/dev/stdin", "rb");
+ if (f == NULL)
+ nperror("fopen");
+
+ read_file(f, 0, "stdin", TRUE, FALSE);
+ ttystdin = open("/dev/tty", O_RDONLY);
+ if (!ttystdin)
+ die(_("Couldn't reopen stdin from keyboard, sorry\n"));
+
+ dup2(ttystdin,0);
+ close(ttystdin);
+ if (!pager_input_aborted)
+ tcgetattr(0, &oldterm);
+ if (!pager_sig_failed && sigaction(SIGINT, &pager_oldaction, NULL) == -1)
+ nperror("sigaction");
+ terminal_init();
+ doupdate();
+}
+
+
+/* Cancel reading from stdin like a pager */
+RETSIGTYPE cancel_stdin_pager(int signal)
+{
+ /* Currently do nothing, just handle the intr silently */
+ pager_input_aborted = TRUE;
+}
+
+/* Let nano read stdin for the first file at least */
+void stdin_pager(void)
+{
+ endwin();
+ if (!pager_input_aborted)
+ tcsetattr(0, TCSANOW, &oldterm);
+ fprintf(stderr, _("Reading from stdin, ^C to abort\n"));
+
+ /* Set things up so that Ctrl-C will cancel the new process. */
+ /* Enable interpretation of the special control keys so that we get
+ * SIGINT when Ctrl-C is pressed. */
+#ifndef NANO_TINY
+ enable_signals();
+#endif
+
+ if (sigaction(SIGINT, NULL, &pager_newaction) == -1) {
+ pager_sig_failed = TRUE;
+ nperror("sigaction");
+ } else {
+ pager_newaction.sa_handler = cancel_stdin_pager;
+ if (sigaction(SIGINT, &pager_newaction, &pager_oldaction) == -1) {
+ pager_sig_failed = TRUE;
+ nperror("sigaction");
+ }
+ }
+
+ open_buffer("", FALSE);
+ finish_stdin_pager();
+}
+
+
+
+/* Initialize the signal handlers. */
+void signal_init(void)
+{
+ /* Trap SIGINT and SIGQUIT because we want them to do useful
+ * things. */
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+
+ /* Trap SIGHUP and SIGTERM because we want to write the file out. */
+ act.sa_handler = handle_hupterm;
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+#ifndef NANO_TINY
+ /* Trap SIGWINCH because we want to handle window resizes. */
+ act.sa_handler = handle_sigwinch;
+ sigaction(SIGWINCH, &act, NULL);
+ allow_pending_sigwinch(FALSE);
+#endif
+
+ /* Trap normal suspend (^Z) so we can handle it ourselves. */
+ if (!ISSET(SUSPEND)) {
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGTSTP, &act, NULL);
+ } else {
+ /* Block all other signals in the suspend and continue handlers.
+ * If we don't do this, other stuff interrupts them! */
+ sigfillset(&act.sa_mask);
+
+ act.sa_handler = do_suspend;
+ sigaction(SIGTSTP, &act, NULL);
+
+ act.sa_handler = do_continue;
+ sigaction(SIGCONT, &act, NULL);
+ }
+}
+
+/* Handler for SIGHUP (hangup) and SIGTERM (terminate). */
+RETSIGTYPE handle_hupterm(int signal)
+{
+ die(_("Received SIGHUP or SIGTERM\n"));
+}
+
+/* Handler for SIGTSTP (suspend). */
+RETSIGTYPE do_suspend(int signal)
+{
+
+ if (ISSET(RESTRICTED)) {
+ nano_disabled_msg();
+ return;
+ }
+
+#ifndef DISABLE_MOUSE
+ /* Turn mouse support off. */
+ disable_mouse_support();
+#endif
+
+ /* Move the cursor to the last line of the screen. */
+ move(LINES - 1, 0);
+ endwin();
+
+ /* Display our helpful message. */
+ printf(_("Use \"fg\" to return to nano.\n"));
+ fflush(stdout);
+
+ /* Restore the old terminal settings. */
+ tcsetattr(0, TCSANOW, &oldterm);
+
+ /* Trap SIGHUP and SIGTERM so we can properly deal with them while
+ * suspended. */
+ act.sa_handler = handle_hupterm;
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+ /* Do what mutt does: send ourselves a SIGSTOP. */
+ kill(0, SIGSTOP);
+}
+
+/* the subnfunc version */
+void do_suspend_void(void)
+{
+ if (ISSET(SUSPEND))
+ do_suspend(0);
+}
+
+/* Handler for SIGCONT (continue after suspend). */
+RETSIGTYPE do_continue(int signal)
+{
+#ifndef DISABLE_MOUSE
+ /* Turn mouse support back on if it was on before. */
+ if (ISSET(USE_MOUSE))
+ enable_mouse_support();
+#endif
+
+#ifndef NANO_TINY
+ /* Perhaps the user resized the window while we slept. Handle it,
+ * and restore the terminal to its previous state and update the
+ * screen in the process. */
+ handle_sigwinch(0);
+#else
+ /* Restore the terminal to its previous state. */
+ terminal_init();
+
+ /* Turn the cursor back on for sure. */
+ curs_set(1);
+
+ /* Redraw the contents of the windows that need it. */
+ blank_statusbar();
+ wnoutrefresh(bottomwin);
+ total_refresh();
+#endif
+}
+
+#ifndef NANO_TINY
+/* Handler for SIGWINCH (window size change). */
+RETSIGTYPE handle_sigwinch(int signal)
+{
+ const char *tty = ttyname(0);
+ int fd, result = 0;
+ struct winsize win;
+
+ if (tty == NULL)
+ return;
+ fd = open(tty, O_RDWR);
+ if (fd == -1)
+ return;
+ result = ioctl(fd, TIOCGWINSZ, &win);
+ close(fd);
+ if (result == -1)
+ return;
+
+ /* We could check whether the COLS or LINES changed, and return
+ * otherwise. However, COLS and LINES are curses global variables,
+ * and in some cases curses has already updated them. But not in
+ * all cases. Argh. */
+#ifdef REDEFINIG_MACROS_OK
+ COLS = win.ws_col;
+ LINES = win.ws_row;
+#endif
+
+ /* If we've partitioned the filestruct, unpartition it now. */
+ if (filepart != NULL)
+ unpartition_filestruct(&filepart);
+
+#ifdef USE_SLANG
+ /* Slang curses emulation brain damage, part 1: If we just do what
+ * curses does here, it'll only work properly if the resize made the
+ * window smaller. Do what mutt does: Leave and immediately reenter
+ * Slang screen management mode. */
+ SLsmg_reset_smg();
+ SLsmg_init_smg();
+#else
+ /* Do the equivalent of what Minimum Profit does: Leave and
+ * immediately reenter curses mode. */
+ endwin();
+ doupdate();
+#endif
+
+ /* Restore the terminal to its previous state. */
+ terminal_init();
+
+ /* Turn the cursor back on for sure. */
+ curs_set(1);
+
+ /* Do the equivalent of what both mutt and Minimum Profit do:
+ * Reinitialize all the windows based on the new screen
+ * dimensions. */
+ window_init();
+
+ /* Redraw the contents of the windows that need it. */
+ blank_statusbar();
+ wnoutrefresh(bottomwin);
+ currmenu = MMAIN;
+ total_refresh();
+
+ /* Jump back to either main() or the unjustify routine in
+ * do_justify(). */
+ siglongjmp(jump_buf, 1);
+}
+
+/* If allow is TRUE, block any SIGWINCH signals that we get, so that we
+ * can deal with them later. If allow is FALSE, unblock any SIGWINCH
+ * signals that we have, so that we can deal with them now. */
+void allow_pending_sigwinch(bool allow)
+{
+ sigset_t winch;
+ sigemptyset(&winch);
+ sigaddset(&winch, SIGWINCH);
+ sigprocmask(allow ? SIG_UNBLOCK : SIG_BLOCK, &winch, NULL);
+}
+#endif /* !NANO_TINY */
+
+#ifndef NANO_TINY
+/* Handle the global toggle specified in which. */
+void do_toggle(int flag)
+{
+ bool enabled;
+ char *desc;
+
+ TOGGLE(flag);
+
+ switch (flag) {
+#ifndef DISABLE_MOUSE
+ case USE_MOUSE:
+ mouse_init();
+ break;
+#endif
+ case MORE_SPACE:
+ case NO_HELP:
+ window_init();
+ total_refresh();
+ break;
+ case SUSPEND:
+ signal_init();
+ break;
+#ifdef ENABLE_NANORC
+ case WHITESPACE_DISPLAY:
+ titlebar(NULL);
+ edit_refresh();
+ break;
+#endif
+#ifdef ENABLE_COLOR
+ case NO_COLOR_SYNTAX:
+ edit_refresh();
+ break;
+#endif
+ case SOFTWRAP:
+ total_refresh();
+ break;
+ }
+
+ enabled = ISSET(flag);
+
+ if (flag == NO_HELP
+#ifndef DISABLE_WRAPPING
+ || flag == NO_WRAP
+#endif
+#ifdef ENABLE_COLOR
+ || flag == NO_COLOR_SYNTAX
+#endif
+ )
+ enabled = !enabled;
+
+ desc = _(flagtostr(flag));
+ statusbar("%s %s", desc, enabled ? _("enabled") :
+ _("disabled"));
+}
+#endif /* !NANO_TINY */
+
+/* Disable extended input and output processing in our terminal
+ * settings. */
+void disable_extended_io(void)
+{
+ struct termios term;
+
+ tcgetattr(0, &term);
+ term.c_lflag &= ~IEXTEN;
+ term.c_oflag &= ~OPOST;
+ tcsetattr(0, TCSANOW, &term);
+}
+
+/* Disable interpretation of the special control keys in our terminal
+ * settings. */
+void disable_signals(void)
+{
+ struct termios term;
+
+ tcgetattr(0, &term);
+ term.c_lflag &= ~ISIG;
+ tcsetattr(0, TCSANOW, &term);
+}
+
+#ifndef NANO_TINY
+/* Enable interpretation of the special control keys in our terminal
+ * settings. */
+void enable_signals(void)
+{
+ struct termios term;
+
+ tcgetattr(0, &term);
+ term.c_lflag |= ISIG;
+ tcsetattr(0, TCSANOW, &term);
+}
+#endif
+
+/* Disable interpretation of the flow control characters in our terminal
+ * settings. */
+void disable_flow_control(void)
+{
+ struct termios term;
+
+ tcgetattr(0, &term);
+ term.c_iflag &= ~IXON;
+ tcsetattr(0, TCSANOW, &term);
+}
+
+/* Enable interpretation of the flow control characters in our terminal
+ * settings. */
+void enable_flow_control(void)
+{
+ struct termios term;
+
+ tcgetattr(0, &term);
+ term.c_iflag |= IXON;
+ tcsetattr(0, TCSANOW, &term);
+}
+
+/* Set up the terminal state. Put the terminal in raw mode (read one
+ * character at a time, disable the special control keys, and disable
+ * the flow control characters), disable translation of carriage return
+ * (^M) into newline (^J) so that we can tell the difference between the
+ * Enter key and Ctrl-J, and disable echoing of characters as they're
+ * typed. Finally, disable extended input and output processing, and,
+ * if we're not in preserve mode, reenable interpretation of the flow
+ * control characters. */
+void terminal_init(void)
+{
+#ifdef USE_SLANG
+ /* Slang curses emulation brain damage, part 2: Slang doesn't
+ * implement raw(), nonl(), or noecho() properly, so there's no way
+ * to properly reinitialize the terminal using them. We have to
+ * disable the special control keys and interpretation of the flow
+ * control characters using termios, save the terminal state after
+ * the first call, and restore it on subsequent calls. */
+ static struct termios newterm;
+ static bool newterm_set = FALSE;
+
+ if (!newterm_set) {
+#endif
+
+ raw();
+ nonl();
+ noecho();
+ disable_extended_io();
+ if (ISSET(PRESERVE))
+ enable_flow_control();
+
+ disable_signals();
+#ifdef USE_SLANG
+ if (!ISSET(PRESERVE))
+ disable_flow_control();
+
+ tcgetattr(0, &newterm);
+ newterm_set = TRUE;
+ } else
+ tcsetattr(0, TCSANOW, &newterm);
+#endif
+}
+
+/* Read in a character, interpret it as a shortcut or toggle if
+ * necessary, and return it. Set meta_key to TRUE if the character is a
+ * meta sequence, set func_key to TRUE if the character is a function
+ * key, set s_or_t to TRUE if the character is a shortcut or toggle
+ * key, set ran_func to TRUE if we ran a function associated with a
+ * shortcut key, and set finished to TRUE if we're done after running
+ * or trying to run a function associated with a shortcut key. If
+ * allow_funcs is FALSE, don't actually run any functions associated
+ * with shortcut keys. */
+int do_input(bool *meta_key, bool *func_key, bool *s_or_t, bool
+ *ran_func, bool *finished, bool allow_funcs)
+{
+ int input;
+ /* The character we read in. */
+ static int *kbinput = NULL;
+ /* The input buffer. */
+ static size_t kbinput_len = 0;
+ /* The length of the input buffer. */
+ bool cut_copy = FALSE;
+ /* Are we cutting or copying text? */
+ const sc *s;
+ bool have_shortcut;
+
+ *s_or_t = FALSE;
+ *ran_func = FALSE;
+ *finished = FALSE;
+
+ /* Read in a character. */
+ input = get_kbinput(edit, meta_key, func_key);
+
+#ifndef DISABLE_MOUSE
+ if (allow_funcs) {
+ /* If we got a mouse click and it was on a shortcut, read in the
+ * shortcut character. */
+ if (*func_key && input == KEY_MOUSE) {
+ if (do_mouse() == 1)
+ input = get_kbinput(edit, meta_key, func_key);
+ else {
+ *meta_key = FALSE;
+ *func_key = FALSE;
+ input = ERR;
+ }
+ }
+ }
+#endif
+
+ /* Check for a shortcut in the main list. */
+ s = get_shortcut(MMAIN, &input, meta_key, func_key);
+
+ /* If we got a shortcut from the main list, or a "universal"
+ * edit window shortcut, set have_shortcut to TRUE. */
+ have_shortcut = (s != NULL);
+
+ /* If we got a non-high-bit control key, a meta key sequence, or a
+ * function key, and it's not a shortcut or toggle, throw it out. */
+ if (!have_shortcut) {
+ if (is_ascii_cntrl_char(input) || *meta_key || *func_key) {
+ statusbar(_("Unknown Command"));
+ beep();
+ *meta_key = FALSE;
+ *func_key = FALSE;
+ input = ERR;
+ }
+ }
+
+ if (allow_funcs) {
+ /* If we got a character, and it isn't a shortcut or toggle,
+ * it's a normal text character. Display the warning if we're
+ * in view mode, or add the character to the input buffer if
+ * we're not. */
+ if (input != ERR && !have_shortcut) {
+ if (ISSET(VIEW_MODE))
+ print_view_warning();
+ else {
+ kbinput_len++;
+ kbinput = (int *)nrealloc(kbinput, kbinput_len *
+ sizeof(int));
+ kbinput[kbinput_len - 1] = input;
+
+ }
+ }
+
+ /* If we got a shortcut or toggle, or if there aren't any other
+ * characters waiting after the one we read in, we need to
+ * output all the characters in the input buffer if it isn't
+ * empty. Note that it should be empty if we're in view
+ * mode. */
+ if (have_shortcut || get_key_buffer_len() == 0) {
+#ifndef DISABLE_WRAPPING
+ /* If we got a shortcut or toggle, and it's not the shortcut
+ * for verbatim input, turn off prepending of wrapped
+ * text. */
+ if (have_shortcut && (!have_shortcut || s == NULL || s->scfunc !=
+ DO_VERBATIM_INPUT))
+ wrap_reset();
+#endif
+
+ if (kbinput != NULL) {
+ /* Display all the characters in the input buffer at
+ * once, filtering out control characters. */
+ char *output = charalloc(kbinput_len + 1);
+ size_t i;
+
+ for (i = 0; i < kbinput_len; i++)
+ output[i] = (char)kbinput[i];
+ output[i] = '\0';
+
+ do_output(output, kbinput_len, FALSE);
+
+ free(output);
+
+ /* Empty the input buffer. */
+ kbinput_len = 0;
+ free(kbinput);
+ kbinput = NULL;
+ }
+ }
+
+ if (have_shortcut) {
+ switch (input) {
+ /* Handle the normal edit window shortcuts, setting
+ * ran_func to TRUE if we try to run their associated
+ * functions and setting finished to TRUE to indicate
+ * that we're done after running or trying to run their
+ * associated functions. */
+ default:
+ /* If the function associated with this shortcut is
+ * cutting or copying text, indicate this. */
+ if (s->scfunc == DO_CUT_TEXT_VOID
+#ifndef NANO_TINY
+ || s->scfunc == DO_COPY_TEXT || s->scfunc ==
+ DO_CUT_TILL_END
+#endif
+ )
+ cut_copy = TRUE;
+
+ if (s->scfunc != 0) {
+ const subnfunc *f = sctofunc((sc *) s);
+ *ran_func = TRUE;
+ if (ISSET(VIEW_MODE) && f && !f->viewok)
+ print_view_warning();
+ else {
+#ifndef NANO_TINY
+ if (s->scfunc == DO_TOGGLE)
+ do_toggle(s->toggle);
+ else {
+#else
+ {
+#endif
+ iso_me_harder_funcmap(s->scfunc);
+#ifdef ENABLE_COLOR
+ if (f && !f->viewok && openfile->syntax != NULL
+ && openfile->syntax->nmultis > 0) {
+ reset_multis(openfile->current, FALSE);
+ }
+#endif
+ if (edit_refresh_needed) {
+#ifdef DEBUG
+ fprintf(stderr, "running edit_refresh() as edit_refresh_needed is true\n");
+#endif
+ edit_refresh();
+ edit_refresh_needed = FALSE;
+ }
+
+ }
+ }
+ }
+ *finished = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* If we aren't cutting or copying text, blow away the text in the
+ * cutbuffer. */
+ if (!cut_copy)
+ cutbuffer_reset();
+
+ return input;
+}
+
+void xon_complaint(void)
+{
+ statusbar(_("XON ignored, mumble mumble"));
+}
+
+void xoff_complaint(void)
+{
+ statusbar(_("XOFF ignored, mumble mumble"));
+}
+
+
+#ifndef DISABLE_MOUSE
+/* Handle a mouse click on the edit window or the shortcut list. */
+int do_mouse(void)
+{
+ int mouse_x, mouse_y;
+ int retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);
+
+ /* We can click on the edit window to move the cursor. */
+ if (retval == 0 && wmouse_trafo(edit, &mouse_y, &mouse_x, FALSE)) {
+ bool sameline;
+ /* Did they click on the line with the cursor? If they
+ * clicked on the cursor, we set the mark. */
+ filestruct *current_save = openfile->current;
+ size_t current_x_save = openfile->current_x;
+ size_t pww_save = openfile->placewewant;
+
+ sameline = (mouse_y == openfile->current_y);
+
+#ifdef DEBUG
+ fprintf(stderr, "mouse_y = %d, current_y = %d\n", mouse_y, openfile->current_y);
+#endif
+
+ if (ISSET(SOFTWRAP)) {
+ int i = 0;
+ for (openfile->current = openfile->edittop;
+ openfile->current->next && i < mouse_y;
+ openfile->current = openfile->current->next, i++) {
+ openfile->current_y = i;
+ i += strlenpt(openfile->current->data) / COLS;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "do_mouse(): moving to current_y = %d, i %d\n", openfile->current_y, i);
+ fprintf(stderr, " openfile->current->data = \"%s\"\n", openfile->current->data);
+#endif
+
+ if (i > mouse_y) {
+ openfile->current = openfile->current->prev;
+ openfile->current_x = actual_x(openfile->current->data, mouse_x + (mouse_y - openfile->current_y) * COLS);
+#ifdef DEBUG
+ fprintf(stderr, "do_mouse(): i > mouse_y, mouse_x = %d, current_x to = %d\n", mouse_x, openfile->current_x);
+#endif
+ } else {
+ openfile->current_x = actual_x(openfile->current->data, mouse_x);
+#ifdef DEBUG
+ fprintf(stderr, "do_mouse(): i <= mouse_y, mouse_x = %d, setting current_x to = %d\n", mouse_x, openfile->current_x);
+#endif
+ }
+
+ openfile->placewewant = xplustabs();
+
+ } else {
+ /* Move to where the click occurred. */
+ for (; openfile->current_y < mouse_y && openfile->current !=
+ openfile->filebot; openfile->current_y++)
+ openfile->current = openfile->current->next;
+ for (; openfile->current_y > mouse_y && openfile->current !=
+ openfile->fileage; openfile->current_y--)
+ openfile->current = openfile->current->prev;
+
+ openfile->current_x = actual_x(openfile->current->data,
+ get_page_start(xplustabs()) + mouse_x);
+
+ openfile->placewewant = xplustabs();
+ }
+
+#ifndef NANO_TINY
+ /* Clicking where the cursor is toggles the mark, as does
+ * clicking beyond the line length with the cursor at the end of
+ * the line. */
+ if (sameline && openfile->current_x == current_x_save)
+ do_mark();
+#endif
+
+ edit_redraw(current_save, pww_save);
+ }
+
+ return retval;
+}
+#endif /* !DISABLE_MOUSE */
+
+#ifdef ENABLE_COLOR
+void alloc_multidata_if_needed(filestruct *fileptr)
+{
+ if (!fileptr->multidata)
+ fileptr->multidata = (short *) nmalloc(openfile->syntax->nmultis * sizeof(short));
+}
+
+/* Precalculate the multi-line start and end regex info so we can speed up
+ rendering (with any hope at all...) */
+void precalc_multicolorinfo(void)
+{
+#ifdef DEBUG
+ fprintf(stderr, "entering precalc_multicolorinfo()\n");
+#endif
+ if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) {
+ const colortype *tmpcolor = openfile->colorstrings;
+ regmatch_t startmatch, endmatch;
+ filestruct *fileptr, *endptr;
+ time_t last_check = time(NULL), cur_check = 0;
+
+ /* Let us get keypresses to see if the user is trying to
+ start editing. We may want to throw up a statusbar
+ message before starting this later if it takes
+ too long to do this routine. For now silently
+ abort if they hit a key */
+ nodelay(edit, FALSE);
+
+ for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
+
+ /* If it's not a multi-line regex, amscray */
+ if (tmpcolor->end == NULL)
+ continue;
+#ifdef DEBUG
+ fprintf(stderr, "working on color id %d\n", tmpcolor->id);
+#endif
+
+
+ for (fileptr = openfile->fileage; fileptr != NULL; fileptr = fileptr->next) {
+ int startx = 0;
+ int nostart = 0;
+
+
+#ifdef DEBUG
+ fprintf(stderr, "working on lineno %lu\n", (unsigned long) fileptr->lineno);
+#endif
+
+ alloc_multidata_if_needed(fileptr);
+
+ if ((cur_check = time(NULL)) - last_check > 1) {
+ last_check = cur_check;
+ if (wgetch(edit) != ERR)
+ goto precalc_cleanup;
+ }
+
+ while ((nostart = regexec(tmpcolor->start, &fileptr->data[startx], 1, &startmatch, 0)) == 0) {
+ /* Look for end and start marking how many lines are encompassed
+ whcih should speed up rendering later */
+ startx += startmatch.rm_eo;
+#ifdef DEBUG
+ fprintf(stderr, "match found at pos %d...", startx);
+#endif
+
+ /* Look on this line first for end */
+ if (regexec(tmpcolor->end, &fileptr->data[startx], 1, &endmatch, 0) == 0) {
+ startx += endmatch.rm_eo;
+ fileptr->multidata[tmpcolor->id] |= CSTARTENDHERE;
+#ifdef DEBUG
+ fprintf(stderr, "end found on this line\n");
+#endif
+ continue;
+ }
+
+ /* Nice, we didn't find the end regex on this line. Let's start looking for it */
+ for (endptr = fileptr->next; endptr != NULL; endptr = endptr->next) {
+
+#ifdef DEBUG
+ fprintf(stderr, "advancing to line %lu to find end...\n", (unsigned long) endptr->lineno);
+#endif
+ /* Check for keyboard input again */
+ if ((cur_check = time(NULL)) - last_check > 1) {
+ last_check = cur_check;
+ if (wgetch(edit) != ERR)
+ goto precalc_cleanup;
+ }
+ if (regexec(tmpcolor->end, endptr->data, 1, &endmatch, 0) == 0)
+ break;
+ }
+
+ if (endptr == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "no end found, breaking out\n");
+#endif
+ break;
+ }
+
+
+#ifdef DEBUG
+ fprintf(stderr, "end found\n");
+#endif
+
+ /* We found it, we found it, la la la la la. Mark all the
+ lines in between and the ends properly */
+ fileptr->multidata[tmpcolor->id] |= CENDAFTER;
+#ifdef DEBUG
+ fprintf(stderr, "marking line %lu as CENDAFTER\n", (unsigned long) fileptr->lineno);
+#endif
+ for (fileptr = fileptr->next; fileptr != endptr; fileptr = fileptr->next) {
+ alloc_multidata_if_needed(fileptr);
+ fileptr->multidata[tmpcolor->id] = CWHOLELINE;
+#ifdef DEBUG
+ fprintf(stderr, "marking intermediary line %lu as CWHOLELINE\n", (unsigned long) fileptr->lineno);
+#endif
+ }
+ alloc_multidata_if_needed(endptr);
+#ifdef DEBUG
+ fprintf(stderr, "marking line %lu as BEGINBEFORE\n", (unsigned long) fileptr->lineno);
+#endif
+ endptr->multidata[tmpcolor->id] |= CBEGINBEFORE;
+ /* We should be able to skip all the way to the line of the match.
+ This may introduce more bugs but it's the Right Thing to do */
+ fileptr = endptr;
+ startx = endmatch.rm_eo;
+#ifdef DEBUG
+ fprintf(stderr, "jumping to line %lu pos %d to continue\n", (unsigned long) endptr->lineno, startx);
+#endif
+ }
+ if (nostart && startx == 0) {
+#ifdef DEBUG
+ fprintf(stderr, "no start found on line %lu, continuing\n", (unsigned long) fileptr->lineno);
+#endif
+ fileptr->multidata[tmpcolor->id] = CNONE;
+ continue;
+ }
+ }
+ }
+ }
+precalc_cleanup:
+ nodelay(edit, FALSE);
+}
+#endif /* ENABLE_COLOR */
+
+/* The user typed output_len multibyte characters. Add them to the edit
+ * buffer, filtering out all ASCII control characters if allow_cntrls is
+ * TRUE. */
+void do_output(char *output, size_t output_len, bool allow_cntrls)
+{
+ size_t current_len, orig_lenpt, i = 0;
+ char *char_buf = charalloc(mb_cur_max());
+ int char_buf_len;
+
+ assert(openfile->current != NULL && openfile->current->data != NULL);
+
+ current_len = strlen(openfile->current->data);
+ if (ISSET(SOFTWRAP))
+ orig_lenpt = strlenpt(openfile->current->data);
+
+ while (i < output_len) {
+ /* If allow_cntrls is TRUE, convert nulls and newlines
+ * properly. */
+ if (allow_cntrls) {
+ /* Null to newline, if needed. */
+ if (output[i] == '\0')
+ output[i] = '\n';
+ /* Newline to Enter, if needed. */
+ else if (output[i] == '\n') {
+ do_enter(FALSE);
+ i++;
+ continue;
+ }
+ }
+
+ /* Interpret the next multibyte character. */
+ char_buf_len = parse_mbchar(output + i, char_buf, NULL);
+
+ i += char_buf_len;
+
+ /* If allow_cntrls is FALSE, filter out an ASCII control
+ * character. */
+ if (!allow_cntrls && is_ascii_cntrl_char(*(output + i -
+ char_buf_len)))
+ continue;
+
+ /* If the NO_NEWLINES flag isn't set, when a character is
+ * added to the magicline, it means we need a new magicline. */
+ if (!ISSET(NO_NEWLINES) && openfile->filebot ==
+ openfile->current)
+ new_magicline();
+
+ /* More dangerousness fun =) */
+ openfile->current->data = charealloc(openfile->current->data,
+ current_len + (char_buf_len * 2));
+
+ assert(openfile->current_x <= current_len);
+
+ charmove(openfile->current->data + openfile->current_x +
+ char_buf_len, openfile->current->data +
+ openfile->current_x, current_len - openfile->current_x +
+ char_buf_len);
+ strncpy(openfile->current->data + openfile->current_x, char_buf,
+ char_buf_len);
+ current_len += char_buf_len;
+ openfile->totsize++;
+ set_modified();
+
+#ifndef NANO_TINY
+ update_undo(ADD);
+
+ /* Note that current_x has not yet been incremented. */
+ if (openfile->mark_set && openfile->current ==
+ openfile->mark_begin && openfile->current_x <
+ openfile->mark_begin_x)
+ openfile->mark_begin_x += char_buf_len;
+#endif
+
+ openfile->current_x += char_buf_len;
+
+#ifndef DISABLE_WRAPPING
+ /* If we're wrapping text, we need to call edit_refresh(). */
+ if (!ISSET(NO_WRAP))
+ if (do_wrap(openfile->current, FALSE))
+ edit_refresh_needed = TRUE;
+#endif
+
+#ifdef ENABLE_COLOR
+ /* If color syntaxes are available and turned on, we need to
+ * call edit_refresh(). */
+ if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX))
+ edit_refresh_needed = TRUE;
+#endif
+ }
+
+ /* Well we might also need a full refresh if we've changed the
+ line length to be a new multiple of COLS */
+ if (ISSET(SOFTWRAP) && edit_refresh_needed == FALSE)
+ if (strlenpt(openfile->current->data) / COLS != orig_lenpt / COLS)
+ edit_refresh_needed = TRUE;
+
+ free(char_buf);
+
+ openfile->placewewant = xplustabs();
+
+
+#ifdef ENABLE_COLOR
+ reset_multis(openfile->current, FALSE);
+#endif
+ if (edit_refresh_needed == TRUE) {
+ edit_refresh();
+ edit_refresh_needed = FALSE;
+ } else
+ update_line(openfile->current, openfile->current_x);
+}
+
+int main(int argc, char **argv)
+{
+ int optchr;
+ ssize_t startline = 1;
+ /* Line to try and start at. */
+ ssize_t startcol = 1;
+ /* Column to try and start at. */
+#ifndef DISABLE_WRAPJUSTIFY
+ bool fill_used = FALSE;
+ /* Was the fill option used? */
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ bool old_multibuffer;
+ /* The old value of the multibuffer option, restored after we
+ * load all files on the command line. */
+#endif
+#ifdef HAVE_GETOPT_LONG
+ const struct option long_options[] = {
+ {"help", 0, NULL, 'h'},
+ {"boldtext", 0, NULL, 'D'},
+#ifdef ENABLE_MULTIBUFFER
+ {"multibuffer", 0, NULL, 'F'},
+#endif
+#ifdef ENABLE_NANORC
+ {"ignorercfiles", 0, NULL, 'I'},
+#endif
+ {"rebindkeypad", 0, NULL, 'K'},
+ {"nonewlines", 0, NULL, 'L'},
+ {"morespace", 0, NULL, 'O'},
+#ifndef DISABLE_JUSTIFY
+ {"quotestr", 1, NULL, 'Q'},
+#endif
+ {"restricted", 0, NULL, 'R'},
+ {"tabsize", 1, NULL, 'T'},
+ {"version", 0, NULL, 'V'},
+#ifdef ENABLE_COLOR
+ {"syntax", 1, NULL, 'Y'},
+#endif
+ {"const", 0, NULL, 'c'},
+ {"rebinddelete", 0, NULL, 'd'},
+ {"nofollow", 0, NULL, 'l'},
+#ifndef DISABLE_MOUSE
+ {"mouse", 0, NULL, 'm'},
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ {"operatingdir", 1, NULL, 'o'},
+#endif
+ {"preserve", 0, NULL, 'p'},
+ {"quiet", 0, NULL, 'q'},
+#ifndef DISABLE_WRAPJUSTIFY
+ {"fill", 1, NULL, 'r'},
+#endif
+#ifndef DISABLE_SPELLER
+ {"speller", 1, NULL, 's'},
+#endif
+ {"tempfile", 0, NULL, 't'},
+ {"view", 0, NULL, 'v'},
+#ifndef DISABLE_WRAPPING
+ {"nowrap", 0, NULL, 'w'},
+#endif
+ {"nohelp", 0, NULL, 'x'},
+ {"suspend", 0, NULL, 'z'},
+#ifndef NANO_TINY
+ {"smarthome", 0, NULL, 'A'},
+ {"backup", 0, NULL, 'B'},
+ {"backupdir", 1, NULL, 'C'},
+ {"tabstospaces", 0, NULL, 'E'},
+ {"historylog", 0, NULL, 'H'},
+ {"noconvert", 0, NULL, 'N'},
+ {"smooth", 0, NULL, 'S'},
+ {"quickblank", 0, NULL, 'U'},
+ {"undo", 0, NULL, 'u'},
+ {"wordbounds", 0, NULL, 'W'},
+ {"autoindent", 0, NULL, 'i'},
+ {"cut", 0, NULL, 'k'},
+ {"softwrap", 0, NULL, '$'},
+#endif
+ {NULL, 0, NULL, 0}
+ };
+#endif
+
+#ifdef ENABLE_UTF8
+ {
+ /* If the locale set exists and uses UTF-8, we should use
+ * UTF-8. */
+ char *locale = setlocale(LC_ALL, "");
+
+ if (locale != NULL && (strcmp(nl_langinfo(CODESET),
+ "UTF-8") == 0)) {
+#ifdef USE_SLANG
+ SLutf8_enable(1);
+#endif
+ utf8_init();
+ }
+ }
+#else
+ setlocale(LC_ALL, "");
+#endif
+
+#ifdef ENABLE_NLS
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAPPING)
+ /* If we don't have rcfile support, --disable-wrapping-as-root is
+ * used, and we're root, turn wrapping off. */
+ if (geteuid() == NANO_ROOT_UID)
+ SET(NO_WRAP);
+#endif
+
+ while ((optchr =
+#ifdef HAVE_GETOPT_LONG
+ getopt_long(argc, argv,
+ "h?ABC:DEFHIKLNOQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$",
+ long_options, NULL)
+#else
+ getopt(argc, argv,
+ "h?ABC:DEFHIKLNOQ:RST:UVWY:abcdefgijklmo:pqr:s:tuvwxz$")
+#endif
+ ) != -1) {
+ switch (optchr) {
+ case 'a':
+ case 'b':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'j':
+ /* Pico compatibility flags. */
+ break;
+#ifndef NANO_TINY
+ case 'A':
+ SET(SMART_HOME);
+ break;
+ case 'B':
+ SET(BACKUP_FILE);
+ break;
+ case 'C':
+ backup_dir = mallocstrcpy(backup_dir, optarg);
+ break;
+#endif
+ case 'D':
+ SET(BOLD_TEXT);
+ break;
+#ifndef NANO_TINY
+ case 'E':
+ SET(TABS_TO_SPACES);
+ break;
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ case 'F':
+ SET(MULTIBUFFER);
+ break;
+#endif
+#ifdef ENABLE_NANORC
+#ifndef NANO_TINY
+ case 'H':
+ SET(HISTORYLOG);
+ break;
+#endif
+ case 'I':
+ no_rcfiles = TRUE;
+ break;
+#endif
+ case 'K':
+ SET(REBIND_KEYPAD);
+ break;
+ case 'L':
+ SET(NO_NEWLINES);
+ break;
+#ifndef NANO_TINY
+ case 'N':
+ SET(NO_CONVERT);
+ break;
+#endif
+ case 'O':
+ SET(MORE_SPACE);
+ break;
+#ifndef DISABLE_JUSTIFY
+ case 'Q':
+ quotestr = mallocstrcpy(quotestr, optarg);
+ break;
+#endif
+ case 'R':
+ SET(RESTRICTED);
+ break;
+#ifndef NANO_TINY
+ case 'S':
+ SET(SMOOTH_SCROLL);
+ break;
+#endif
+ case 'T':
+ if (!parse_num(optarg, &tabsize) || tabsize <= 0) {
+ fprintf(stderr, _("Requested tab size \"%s\" is invalid"), optarg);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ break;
+#ifndef NANO_TINY
+ case 'U':
+ SET(QUICK_BLANK);
+ break;
+#endif
+ case 'V':
+ version();
+ exit(0);
+#ifndef NANO_TINY
+ case 'W':
+ SET(WORD_BOUNDS);
+ break;
+#endif
+#ifdef ENABLE_COLOR
+ case 'Y':
+ syntaxstr = mallocstrcpy(syntaxstr, optarg);
+ break;
+#endif
+ case 'c':
+ SET(CONST_UPDATE);
+ break;
+ case 'd':
+ SET(REBIND_DELETE);
+ break;
+#ifndef NANO_TINY
+ case 'i':
+ SET(AUTOINDENT);
+ break;
+ case 'k':
+ SET(CUT_TO_END);
+ break;
+#endif
+ case 'l':
+ SET(NOFOLLOW_SYMLINKS);
+ break;
+#ifndef DISABLE_MOUSE
+ case 'm':
+ SET(USE_MOUSE);
+ break;
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ case 'o':
+ operating_dir = mallocstrcpy(operating_dir, optarg);
+ break;
+#endif
+ case 'p':
+ SET(PRESERVE);
+ break;
+ case 'q':
+ SET(QUIET);
+ break;
+#ifndef DISABLE_WRAPJUSTIFY
+ case 'r':
+ if (!parse_num(optarg, &wrap_at)) {
+ fprintf(stderr, _("Requested fill size \"%s\" is invalid"), optarg);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ fill_used = TRUE;
+ break;
+#endif
+#ifndef DISABLE_SPELLER
+ case 's':
+ alt_speller = mallocstrcpy(alt_speller, optarg);
+ break;
+#endif
+ case 't':
+ SET(TEMP_FILE);
+ break;
+#ifndef NANO_TINY
+ case 'u':
+ SET(UNDOABLE);
+ break;
+#endif
+ case 'v':
+ SET(VIEW_MODE);
+ break;
+#ifndef DISABLE_WRAPPING
+ case 'w':
+ SET(NO_WRAP);
+
+ /* If both --fill and --nowrap are given on the command line,
+ the last option wins, */
+ fill_used = FALSE;
+
+ break;
+#endif
+ case 'x':
+ SET(NO_HELP);
+ break;
+ case 'z':
+ SET(SUSPEND);
+ break;
+#ifndef NANO_TINY
+ case '$':
+ SET(SOFTWRAP);
+ break;
+#endif
+ default:
+ usage();
+ }
+ }
+
+ /* If the executable filename starts with 'r', enable restricted
+ * mode. */
+ if (*(tail(argv[0])) == 'r')
+ SET(RESTRICTED);
+
+ /* If we're using restricted mode, disable suspending, backups, and
+ * reading rcfiles, since they all would allow reading from or
+ * writing to files not specified on the command line. */
+ if (ISSET(RESTRICTED)) {
+ UNSET(SUSPEND);
+ UNSET(BACKUP_FILE);
+#ifdef ENABLE_NANORC
+ no_rcfiles = TRUE;
+#endif
+ }
+
+
+ /* Set up the shortcut lists.
+ Need to do this before the rcfile */
+ shortcut_init(FALSE);
+
+/* We've read through the command line options. Now back up the flags
+ * and values that are set, and read the rcfile(s). If the values
+ * haven't changed afterward, restore the backed-up values. */
+#ifdef ENABLE_NANORC
+ if (!no_rcfiles) {
+#ifndef DISABLE_OPERATINGDIR
+ char *operating_dir_cpy = operating_dir;
+#endif
+#ifndef DISABLE_WRAPJUSTIFY
+ ssize_t wrap_at_cpy = wrap_at;
+#endif
+#ifndef NANO_TINY
+ char *backup_dir_cpy = backup_dir;
+#endif
+#ifndef DISABLE_JUSTIFY
+ char *quotestr_cpy = quotestr;
+#endif
+#ifndef DISABLE_SPELLER
+ char *alt_speller_cpy = alt_speller;
+#endif
+ ssize_t tabsize_cpy = tabsize;
+ unsigned flags_cpy[sizeof(flags) / sizeof(flags[0])];
+ size_t i;
+
+ memcpy(flags_cpy, flags, sizeof(flags_cpy));
+
+#ifndef DISABLE_OPERATINGDIR
+ operating_dir = NULL;
+#endif
+#ifndef NANO_TINY
+ backup_dir = NULL;
+#endif
+#ifndef DISABLE_JUSTIFY
+ quotestr = NULL;
+#endif
+#ifndef DISABLE_SPELLER
+ alt_speller = NULL;
+#endif
+
+ do_rcfile();
+
+#ifdef DEBUG
+ fprintf(stderr, "After rebinding keys...\n");
+ print_sclist();
+#endif
+
+#ifndef DISABLE_OPERATINGDIR
+ if (operating_dir_cpy != NULL) {
+ free(operating_dir);
+ operating_dir = operating_dir_cpy;
+ }
+#endif
+#ifndef DISABLE_WRAPJUSTIFY
+ if (fill_used)
+ wrap_at = wrap_at_cpy;
+#endif
+#ifndef NANO_TINY
+ if (backup_dir_cpy != NULL) {
+ free(backup_dir);
+ backup_dir = backup_dir_cpy;
+ }
+#endif
+#ifndef DISABLE_JUSTIFY
+ if (quotestr_cpy != NULL) {
+ free(quotestr);
+ quotestr = quotestr_cpy;
+ }
+#endif
+#ifndef DISABLE_SPELLER
+ if (alt_speller_cpy != NULL) {
+ free(alt_speller);
+ alt_speller = alt_speller_cpy;
+ }
+#endif
+ if (tabsize_cpy != -1)
+ tabsize = tabsize_cpy;
+
+ for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++)
+ flags[i] |= flags_cpy[i];
+ }
+#ifdef DISABLE_ROOTWRAPPING
+ /* If we don't have any rcfiles, --disable-wrapping-as-root is used,
+ * and we're root, turn wrapping off. */
+ else if (geteuid() == NANO_ROOT_UID)
+ SET(NO_WRAP);
+#endif
+#endif /* ENABLE_NANORC */
+
+#ifndef DISABLE_WRAPPING
+ /* Overwrite an rcfile "set nowrap" or --disable-wrapping-as-root
+ if a --fill option was given on the command line. */
+ if (fill_used)
+ UNSET(NO_WRAP);
+#endif
+
+ /* If we're using bold text instead of reverse video text, set it up
+ * now. */
+ if (ISSET(BOLD_TEXT))
+ reverse_attr = A_BOLD;
+
+#ifndef NANO_TINY
+ /* Set up the search/replace history. */
+ history_init();
+#ifdef ENABLE_NANORC
+ if (!no_rcfiles && ISSET(HISTORYLOG))
+ load_history();
+#endif
+#endif
+
+#ifndef NANO_TINY
+ /* Set up the backup directory (unless we're using restricted mode,
+ * in which case backups are disabled, since they would allow
+ * reading from or writing to files not specified on the command
+ * line). This entails making sure it exists and is a directory, so
+ * that backup files will be saved there. */
+ if (!ISSET(RESTRICTED))
+ init_backup_dir();
+#endif
+
+#ifndef DISABLE_OPERATINGDIR
+ /* Set up the operating directory. This entails chdir()ing there,
+ * so that file reads and writes will be based there. */
+ init_operating_dir();
+#endif
+
+#ifndef DISABLE_JUSTIFY
+ /* If punct wasn't specified, set its default value. */
+ if (punct == NULL)
+ punct = mallocstrcpy(NULL, "!.?");
+
+ /* If brackets wasn't specified, set its default value. */
+ if (brackets == NULL)
+ brackets = mallocstrcpy(NULL, "\"')>]}");
+
+ /* If quotestr wasn't specified, set its default value. */
+ if (quotestr == NULL)
+ quotestr = mallocstrcpy(NULL,
+#ifdef HAVE_REGEX_H
+ "^([ \t]*[#:>|}])+"
+#else
+ "> "
+#endif
+ );
+#ifdef HAVE_REGEX_H
+ quoterc = regcomp(&quotereg, quotestr, REG_EXTENDED);
+
+ if (quoterc == 0) {
+ /* We no longer need quotestr, just quotereg. */
+ free(quotestr);
+ quotestr = NULL;
+ } else {
+ size_t size = regerror(quoterc, &quotereg, NULL, 0);
+
+ quoteerr = charalloc(size);
+ regerror(quoterc, &quotereg, quoteerr, size);
+ }
+#else
+ quotelen = strlen(quotestr);
+#endif /* !HAVE_REGEX_H */
+#endif /* !DISABLE_JUSTIFY */
+
+#ifndef DISABLE_SPELLER
+ /* If we don't have an alternative spell checker after reading the
+ * command line and/or rcfile(s), check $SPELL for one, as Pico
+ * does (unless we're using restricted mode, in which case spell
+ * checking is disabled, since it would allow reading from or
+ * writing to files not specified on the command line). */
+ if (!ISSET(RESTRICTED) && alt_speller == NULL) {
+ char *spellenv = getenv("SPELL");
+ if (spellenv != NULL)
+ alt_speller = mallocstrcpy(NULL, spellenv);
+ }
+#endif
+
+#ifndef NANO_TINY
+ /* If matchbrackets wasn't specified, set its default value. */
+ if (matchbrackets == NULL)
+ matchbrackets = mallocstrcpy(NULL, "(<[{)>]}");
+#endif
+
+#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
+ /* If whitespace wasn't specified, set its default value. */
+ if (whitespace == NULL) {
+ whitespace = mallocstrcpy(NULL, " ");
+ whitespace_len[0] = 1;
+ whitespace_len[1] = 1;
+ }
+#endif
+
+ /* If tabsize wasn't specified, set its default value. */
+ if (tabsize == -1)
+ tabsize = WIDTH_OF_TAB;
+
+ /* Back up the old terminal settings so that they can be restored. */
+ tcgetattr(0, &oldterm);
+
+ /* Initialize curses mode. If this fails, get out. */
+ if (initscr() == NULL)
+ exit(1);
+
+ /* Set up the terminal state. */
+ terminal_init();
+
+ /* Turn the cursor on for sure. */
+ curs_set(1);
+
+#ifdef DEBUG
+ fprintf(stderr, "Main: set up windows\n");
+#endif
+
+ /* Initialize all the windows based on the current screen
+ * dimensions. */
+ window_init();
+
+ /* Set up the signal handlers. */
+ signal_init();
+
+#ifndef DISABLE_MOUSE
+ /* Initialize mouse support. */
+ mouse_init();
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "Main: open file\n");
+#endif
+
+ /* If there's a +LINE or +LINE,COLUMN flag here, it is the first
+ * non-option argument, and it is followed by at least one other
+ * argument, the filename it applies to. */
+ if (0 < optind && optind < argc - 1 && argv[optind][0] == '+') {
+ parse_line_column(&argv[optind][1], &startline, &startcol);
+ optind++;
+ }
+
+ if (optind < argc && !strcmp(argv[optind], "-")) {
+ stdin_pager();
+ set_modified();
+ optind++;
+ }
+
+#ifdef ENABLE_MULTIBUFFER
+ old_multibuffer = ISSET(MULTIBUFFER);
+ SET(MULTIBUFFER);
+
+ /* Read all the files after the first one on the command line into
+ * new buffers. */
+ {
+ int i = optind + 1;
+ ssize_t iline = 1, icol = 1;
+
+ for (; i < argc; i++) {
+ /* If there's a +LINE or +LINE,COLUMN flag here, it is
+ * followed by at least one other argument, the filename it
+ * applies to. */
+ if (i < argc - 1 && argv[i][0] == '+' && iline == 1 &&
+ icol == 1)
+ parse_line_column(&argv[i][1], &iline, &icol);
+ else {
+ open_buffer(argv[i], FALSE);
+
+ if (iline > 1 || icol > 1) {
+ do_gotolinecolumn(iline, icol, FALSE, FALSE, FALSE,
+ FALSE);
+ iline = 1;
+ icol = 1;
+ }
+ }
+ }
+ }
+#endif
+
+ /* Read the first file on the command line into either the current
+ * buffer or a new buffer, depending on whether multibuffer mode is
+ * enabled. */
+ if (optind < argc)
+ open_buffer(argv[optind], FALSE);
+
+ /* We didn't open any files if all the command line arguments were
+ * invalid files like directories or if there were no command line
+ * arguments given. In this case, we have to load a blank buffer.
+ * Also, we unset view mode to allow editing. */
+ if (openfile == NULL) {
+ open_buffer("", FALSE);
+ UNSET(VIEW_MODE);
+ }
+
+#ifdef ENABLE_MULTIBUFFER
+ if (!old_multibuffer)
+ UNSET(MULTIBUFFER);
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "Main: top and bottom win\n");
+#endif
+
+#ifdef ENABLE_COLOR
+ if (openfile->syntax && openfile->syntax->nmultis > 0)
+ precalc_multicolorinfo();
+#endif
+
+ if (startline > 1 || startcol > 1)
+ do_gotolinecolumn(startline, startcol, FALSE, FALSE, FALSE,
+ FALSE);
+
+ display_main_list();
+
+ display_buffer();
+
+ while (TRUE) {
+ bool meta_key, func_key, s_or_t, ran_func, finished;
+
+ /* Make sure the cursor is in the edit window. */
+ reset_cursor();
+ wnoutrefresh(edit);
+
+#ifndef NANO_TINY
+ if (!jump_buf_main) {
+ /* If we haven't already, we're going to set jump_buf so
+ * that we return here after a SIGWINCH. Indicate this. */
+ jump_buf_main = TRUE;
+
+ /* Return here after a SIGWINCH. */
+ sigsetjmp(jump_buf, 1);
+ }
+#endif
+
+ /* Just in case we were at the statusbar prompt, make sure the
+ * statusbar cursor position is reset. */
+ do_prompt_abort();
+
+ /* If constant cursor position display is on, and there are no
+ * keys waiting in the input buffer, display the current cursor
+ * position on the statusbar. */
+ if (ISSET(CONST_UPDATE) && get_key_buffer_len() == 0)
+ do_cursorpos(TRUE);
+
+ currmenu = MMAIN;
+
+ /* Read in and interpret characters. */
+ do_input(&meta_key, &func_key, &s_or_t, &ran_func, &finished,
+ TRUE);
+ }
+
+ /* We should never get here. */
+ assert(FALSE);
+}
+