install-sh
missing
mkinstalldirs
-nano
-nano.info
nano.spec
stamp-h
stamp-h.in
stamp-h1
-texinfo.tex
disabled if justification is disabled. (DLR)
- Make sure the "historylog" option isn't included at all if
NANO_SMALL is defined. (DLR)
+ - Source reorganization: move code to src/, docs to doc/. (Jordi)
+ - Translation updates (see po/ChangeLog for details).
- files.c:
do_browser()
- Some of the Pico compatibility options in the file browser
interpreted as Ctrl-[character], and the support for Pico's
Esc Esc [three-digit decimal ASCII code] input method, if
applicable. (DLR)
+ - French translation by Jean-Philippe Guérard.
- nano.1.html, nanorc.5.html:
- Regenerated. (DLR)
- nanorc.sample:
## $Id$
AUTOMAKE_OPTIONS = gnu no-dependencies
-DEFS= -DSYSCONFDIR=\"$(sysconfdir)\"
+SUBDIRS = doc m4 po src
-bin_PROGRAMS = nano
-nano_SOURCES = color.c \
- cut.c \
- files.c \
- global.c \
- move.c \
- nano.c \
- nano.h \
- proto.h \
- rcfile.c \
- search.c \
- utils.c \
- winio.c
-
-man_MANS = nano.1 nanorc.5
-nano_LDADD = @GLIB_LIBS@ @LIBINTL@
-
-info_TEXINFOS = nano.texi
-MAKEINFO = makeinfo --no-split
EXTRA_DIST = ABOUT-NLS AUTHORS BUGS COPYING ChangeLog INSTALL NEWS \
README THANKS TODO UPGRADE config.rpath install-sh missing \
- mkinstalldirs nano.1.html nanorc.5.html faq.html \
- nanorc.sample nano.spec $(man_MANS)
-
-SUBDIRS = po m4
-
-localedir = $(datadir)/locale
-INCLUDES = -Iintl -DLOCALEDIR=\"$(localedir)\" -I@includedir@
+ mkinstalldirs nano.spec
ACLOCAL_AMFLAGS = -I m4
(^R^X^X)(requires figuring out when to keep cursor pos and when not
to).
- Fix resetstatuspos global which we shouldn't have.
+- Rewrite the nano FAQ in SGML.
Old requests:
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * color.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "proto.h"
-#include "nano.h"
-
-#ifdef ENABLE_COLOR
-
-/* For each syntax list entry, we go through the list of colors and
- * assign color pairs. */
-void set_colorpairs(void)
-{
- const syntaxtype *this_syntax = syntaxes;
-
- for(; this_syntax != NULL; this_syntax = this_syntax->next) {
- colortype *this_color = this_syntax->color;
- int color_pair = 1;
-
- for(; this_color != NULL; this_color = this_color->next) {
- const colortype *beforenow = this_syntax->color;
-
- for(; beforenow != NULL && beforenow != this_color &&
- (beforenow->fg != this_color->fg ||
- beforenow->bg != this_color->bg ||
- beforenow->bright != this_color->bright);
- beforenow = beforenow->next)
- ;
-
- if (beforenow != NULL && beforenow != this_color)
- this_color->pairnum = beforenow->pairnum;
- else {
- this_color->pairnum = color_pair;
- color_pair++;
- }
- }
- }
-}
-
-void do_colorinit(void)
-{
- if (has_colors()) {
- const colortype *tmpcolor = NULL;
-#ifdef HAVE_USE_DEFAULT_COLORS
- int defok;
-#endif
-
- start_color();
- /* Add in colors, if available */
-
-#ifdef HAVE_USE_DEFAULT_COLORS
- defok = use_default_colors() != ERR;
-#endif
-
- for (tmpcolor = colorstrings; tmpcolor != NULL;
- tmpcolor = tmpcolor->next) {
- short background = tmpcolor->bg;
-
- if (background == -1)
-#ifdef HAVE_USE_DEFAULT_COLORS
- if (!defok)
-#endif
- background = COLOR_BLACK;
-
- init_pair(tmpcolor->pairnum, tmpcolor->fg, background);
-
-#ifdef DEBUG
- fprintf(stderr, "Running %s with fg = %d and bg = %d\n",
- "init_pair()", tmpcolor->fg, tmpcolor->bg);
-#endif
- }
- }
-}
-
-/* Update the color information based on the current filename. */
-void update_color(void)
-{
- const syntaxtype *tmpsyntax;
-
- colorstrings = NULL;
- for (tmpsyntax = syntaxes; tmpsyntax != NULL; tmpsyntax = tmpsyntax->next) {
- const exttype *e;
-
- for (e = tmpsyntax->extensions; e != NULL; e = e->next) {
- /* Set colorstrings if we matched the extension regex */
- if (!regexec(&e->val, filename, 0, NULL, 0))
- colorstrings = tmpsyntax->color;
-
- if (colorstrings != NULL)
- break;
- }
- }
-
- /* if we haven't found a match, use the override string */
- if (colorstrings == NULL && syntaxstr != NULL) {
- for (tmpsyntax = syntaxes; tmpsyntax != NULL;
- tmpsyntax = tmpsyntax->next) {
- if (!strcasecmp(tmpsyntax->desc, syntaxstr))
- colorstrings = tmpsyntax->color;
- }
- }
- do_colorinit();
-}
-
-#endif /* ENABLE_COLOR */
# $Id$
AC_INIT([GNU Nano], [1.2.99-cvs], [nano-devel@gnu.org], [nano])
-AC_CONFIG_SRCDIR([nano.c])
+AC_CONFIG_SRCDIR([src/nano.c])
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER([config.h:config.h.in])
LDFLAGS="$LDFLAGS $glib_libs"
fi
-AC_CONFIG_FILES([Makefile m4/Makefile po/Makefile.in nano.spec])
+AC_CONFIG_FILES([
+Makefile
+doc/Makefile
+doc/man/Makefile
+doc/man/fr/Makefile
+doc/texinfo/Makefile
+m4/Makefile
+po/Makefile.in
+src/Makefile
+nano.spec
+])
+
AC_OUTPUT
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * cut.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-static int marked_cut; /* Is the cutbuffer from a mark? */
-
-#ifndef NANO_SMALL
-static int concatenate_cut; /* Should we add this cut string to the
- end of the last one? */
-#endif
-
-static filestruct *cutbottom = NULL;
- /* Pointer to end of cutbuffer */
-
-filestruct *get_cutbottom(void)
-{
- return cutbottom;
-}
-
-void add_to_cutbuffer(filestruct *inptr)
-{
-#ifdef DEBUG
- fprintf(stderr, "add_to_cutbuffer() called with inptr->data = %s\n",
- inptr->data);
-#endif
-
- if (cutbuffer == NULL) {
- cutbuffer = inptr;
- inptr->prev = NULL;
-#ifndef NANO_SMALL
- } else if (concatenate_cut && !ISSET(JUSTIFY_MODE)) {
- /* Just tack the text in inptr onto the text in cutbottom,
- unless we're backing up lines while justifying text. */
- cutbottom->data = charealloc(cutbottom->data,
- strlen(cutbottom->data) + strlen(inptr->data) + 1);
- strcat(cutbottom->data, inptr->data);
- return;
-#endif
- } else {
- cutbottom->next = inptr;
- inptr->prev = cutbottom;
- }
-
- inptr->next = NULL;
- cutbottom = inptr;
-}
-
-#ifndef NANO_SMALL
-/* Cut a marked segment instead of a whole line.
- *
- * The first cut character is top->data[top_x]. Unless top == bot, the
- * last cut line has length bot_x. That is, if bot_x > 0 then we cut to
- * bot->data[bot_x - 1].
- *
- * destructive is whether to actually modify the file structure, if not
- * then just copy the buffer into cutbuffer and don't pull it from the
- * file.
- *
- * If destructive, then we maintain totsize, totlines, filebot, the
- * magic line, and line numbers. Also, we set current and current_x so
- * the cursor will be on the first character after what was cut. We do
- * not do any screen updates. */
-void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot,
- size_t bot_x, int destructive)
-{
- filestruct *tmp, *next;
- size_t newsize;
-
- if (top == bot && top_x == bot_x)
- return;
- assert(top != NULL && bot != NULL);
-
- /* Make top be no later than bot. */
- if (top->lineno > bot->lineno) {
- filestruct *swap = top;
- int swap2 = top_x;
-
- top = bot;
- bot = swap;
-
- top_x = bot_x;
- bot_x = swap2;
- } else if (top == bot && top_x > bot_x) {
- /* And bot_x can't be an earlier character than top_x. */
- int swap = top_x;
-
- top_x = bot_x;
- bot_x = swap;
- }
-
- /* Make the first cut line manually. */
- tmp = copy_node(top);
- newsize = (top == bot ? bot_x - top_x : strlen(top->data + top_x));
- memmove(tmp->data, top->data + top_x, newsize);
- null_at(&tmp->data, newsize);
- add_to_cutbuffer(tmp);
-
- /* And make the remainder line manually too. */
- if (destructive) {
- current_x = top_x;
- totsize -= newsize;
- totlines -= bot->lineno - top->lineno;
-
- newsize = top_x + strlen(bot->data + bot_x) + 1;
- if (top == bot) {
- /* In this case, the remainder line is shorter, so we must
- move text from the end forward first. */
- memmove(top->data + top_x, bot->data + bot_x,
- newsize - top_x);
- top->data = charealloc(top->data, newsize);
- } else {
- totsize -= bot_x + 1;
-
- /* Here, the remainder line might get longer, so we
- realloc() it first. */
- top->data = charealloc(top->data, newsize);
- memmove(top->data + top_x, bot->data + bot_x,
- newsize - top_x);
- }
- }
-
- if (top == bot) {
-#ifdef DEBUG
- dump_buffer(cutbuffer);
-#endif
- return;
- }
-
- tmp = top->next;
- while (tmp != bot) {
- next = tmp->next;
- if (!destructive)
- tmp = copy_node(tmp);
- else
- totsize -= strlen(tmp->data) + 1;
- add_to_cutbuffer(tmp);
- tmp = next;
- }
-
- /* Make the last cut line manually. */
- tmp = copy_node(bot);
- null_at(&tmp->data, bot_x);
- add_to_cutbuffer(tmp);
-#ifdef DEBUG
- dump_buffer(cutbuffer);
-#endif
-
- if (destructive) {
- top->next = bot->next;
- if (top->next != NULL)
- top->next->prev = top;
- delete_node(bot);
- renumber(top);
- current = top;
- if (bot == filebot) {
- filebot = top;
- assert(bot_x == 0);
- if (top_x > 0)
- new_magicline();
- }
- }
-#ifdef DEBUG
- dump_buffer(cutbuffer);
-#endif
-}
-#endif
-
-int do_cut_text(void)
-{
- filestruct *fileptr;
-#ifndef NANO_SMALL
- int dontupdate = 0;
-#endif
-
- assert(current != NULL && current->data != NULL);
-
- check_statblank();
-
- if (!ISSET(KEEP_CUTBUFFER)) {
- free_filestruct(cutbuffer);
- cutbuffer = NULL;
- marked_cut = 0;
-#ifndef NANO_SMALL
- concatenate_cut = 0;
-#endif
-#ifdef DEBUG
- fprintf(stderr, "Blew away cutbuffer =)\n");
-#endif
- }
-
- /* You can't cut the magic line except with the mark. But
- trying does clear the cutbuffer if KEEP_CUTBUFFER is not set. */
- if (current == filebot
-#ifndef NANO_SMALL
- && !ISSET(MARK_ISSET)
-#endif
- )
- return 0;
-
- SET(KEEP_CUTBUFFER);
-
-#ifndef NANO_SMALL
- if (ISSET(CUT_TO_END) && !ISSET(MARK_ISSET)) {
- assert(current_x >= 0 && current_x <= strlen(current->data));
-
- if (current->data[current_x] == '\0') {
- /* If the line is empty and we didn't just cut a non-blank
- line, create a dummy line and add it to the cutbuffer */
- if (marked_cut != 1 && current->next != filebot) {
- filestruct *junk = make_new_node(current);
-
- junk->data = charalloc(1);
- junk->data[0] = '\0';
- add_to_cutbuffer(junk);
-#ifdef DEBUG
- dump_buffer(cutbuffer);
-#endif
- }
-
- do_delete();
- marked_cut = 2;
- return 1;
- } else {
- SET(MARK_ISSET);
-
- mark_beginx = strlen(current->data);
- mark_beginbuf = current;
- dontupdate = 1;
- }
- }
-
- if (ISSET(MARK_ISSET)) {
- /* Don't do_update() and move the screen position if the marked
- area lies entirely within the screen buffer */
- dontupdate |= current->lineno >= edittop->lineno &&
- current->lineno <= editbot->lineno &&
- mark_beginbuf->lineno >= edittop->lineno &&
- mark_beginbuf->lineno <= editbot->lineno;
- cut_marked_segment(current, current_x, mark_beginbuf,
- mark_beginx, 1);
-
- placewewant = xplustabs();
- UNSET(MARK_ISSET);
-
- /* If we just did a marked cut of part of a line, we should add
- the first line of any cut done immediately afterward to the
- end of this cut, as Pico does. */
- if (current == mark_beginbuf && current_x < strlen(current->data))
- concatenate_cut = 1;
- marked_cut = 1;
- if (dontupdate)
- edit_refresh();
- else
- edit_update(current, CENTER);
- set_modified();
-
- return 1;
- }
-#endif /* !NANO_SMALL */
-
- totlines--;
- totsize -= strlen(current->data) + 1;
- fileptr = current;
- current = current->next;
- current->prev = fileptr->prev;
- add_to_cutbuffer(fileptr);
-#ifdef DEBUG
- dump_buffer(cutbuffer);
-#endif
-
- if (fileptr == fileage)
- fileage = current;
- else
- current->prev->next = current;
-
- if (fileptr == edittop)
- edittop = current;
-
- renumber(current);
- current_x = 0;
- edit_refresh();
- set_modified();
- marked_cut = 0;
-#ifndef NANO_SMALL
- concatenate_cut = 0;
-#endif
- placewewant = 0;
- return 1;
-}
-
-int do_uncut_text(void)
-{
- filestruct *tmp = current, *fileptr = current;
- filestruct *newbuf = NULL, *newend = NULL;
- char *tmpstr, *tmpstr2;
- filestruct *hold = current;
- int i;
-
- wrap_reset();
- check_statblank();
- if (cutbuffer == NULL || fileptr == NULL)
- return 0; /* AIEEEEEEEEEEEE */
-
- /* If we're uncutting a previously non-marked block, uncut to end if
- we're not at the beginning of the line. If we are at the
- beginning of the line, set placewewant to 0. Pico does both of
- these. */
- if (marked_cut == 0) {
- if (current_x != 0)
- marked_cut = 2;
- else
- placewewant = 0;
- }
-
- /* If we're going to uncut on the magicline, always make a new
- magicline in advance. */
- if (current->next == NULL)
- new_magicline();
-
- if (marked_cut == 0 || cutbuffer->next != NULL)
- {
- newbuf = copy_filestruct(cutbuffer);
- for (newend = newbuf; newend->next != NULL && newend != NULL;
- newend = newend->next)
- totlines++;
- }
-
- /* Hook newbuf into fileptr */
- if (marked_cut != 0) {
- int recenter_me = 0;
- /* Should we eventually use edit_update(CENTER)? */
-
- /* If there's only one line in the cutbuffer */
- if (cutbuffer->next == NULL) {
- size_t buf_len = strlen(cutbuffer->data);
- size_t cur_len = strlen(current->data);
-
- current->data = charealloc(current->data, cur_len + buf_len + 1);
- memmove(current->data + current_x + buf_len,
- current->data + current_x, cur_len - current_x + 1);
- strncpy(current->data + current_x, cutbuffer->data, buf_len);
- /* Use strncpy() to not copy the terminal '\0'. */
-
- current_x += buf_len;
- totsize += buf_len;
-
- placewewant = xplustabs();
- update_cursor();
- } else { /* yuck -- no kidding! */
- tmp = current->next;
- /* New beginning */
- tmpstr = charalloc(current_x + strlen(newbuf->data) + 1);
- strncpy(tmpstr, current->data, current_x);
- strcpy(&tmpstr[current_x], newbuf->data);
- totsize += strlen(newbuf->data) + strlen(newend->data) + 1;
-
- /* New end */
- tmpstr2 = charalloc(strlen(newend->data) +
- strlen(¤t->data[current_x]) + 1);
- strcpy(tmpstr2, newend->data);
- strcat(tmpstr2, ¤t->data[current_x]);
-
- free(current->data);
- current->data = tmpstr;
- current->next = newbuf->next;
- newbuf->next->prev = current;
- delete_node(newbuf);
-
- current_x = strlen(newend->data);
- placewewant = xplustabs();
- free(newend->data);
- newend->data = tmpstr2;
-
- newend->next = tmp;
-
- /* If tmp isn't null, we're in the middle: update the
- prev pointer. If it IS null, we're at the end; update
- the filebot pointer */
-
- if (tmp != NULL)
- tmp->prev = newend;
- else {
- /* Fix the editbot pointer too */
- if (editbot == filebot)
- editbot = newend;
- filebot = newend;
- new_magicline();
- }
-
- /* Now why don't we update the totsize also */
- for (tmp = current->next; tmp != newend; tmp = tmp->next)
- totsize += strlen(tmp->data) + 1;
-
- current = newend;
- if (editbot->lineno < newend->lineno)
- recenter_me = 1;
- }
-
- /* If marked cut == 2, that means that we're doing a cut to end
- and we don't want anything else on the line, so we have to
- screw up all the work we just did and separate the line.
- There must be a better way to do this, but not at 1AM on a
- work night. */
-
- if (marked_cut == 2) {
- tmp = make_new_node(current);
- tmp->data = mallocstrcpy(NULL, current->data + current_x);
- splice_node(current, tmp, current->next);
- null_at(¤t->data, current_x);
- current = current->next;
- current_x = 0;
- placewewant = 0;
-
- /* Extra line added, update stuff */
- totlines++;
- totsize++;
- }
- /* Renumber from BEFORE where we pasted ;) */
- renumber(hold);
-
-#ifdef DEBUG
- dump_buffer(fileage);
- dump_buffer(cutbuffer);
-#endif
- set_modified();
- if (recenter_me)
- edit_update(current, CENTER);
- else
- edit_refresh();
- return 0;
- }
-
- if (fileptr != fileage) {
- tmp = fileptr->prev;
- tmp->next = newbuf;
- newbuf->prev = tmp;
- } else
- fileage = newbuf;
- totlines++; /* Unmarked uncuts don't split lines */
-
- /* This is so uncutting at the top of the buffer will work => */
- if (current_y == 0)
- edittop = newbuf;
-
- /* Connect the end of the buffer to the filestruct */
- newend->next = fileptr;
- fileptr->prev = newend;
-
- /* Recalculate size *sigh* */
- for (tmp = newbuf; tmp != fileptr; tmp = tmp->next)
- totsize += strlen(tmp->data) + 1;
-
- i = editbot->lineno;
- renumber(newbuf);
- /* Center the screen if we've moved beyond the line numbers of both
- the old and new editbots */
- if (i < newend->lineno && editbot->lineno < newend->lineno)
- edit_update(fileptr, CENTER);
- else
- edit_refresh();
-
-#ifdef DEBUG
- dump_buffer_reverse();
-#endif
-
- set_modified();
- return 1;
-}
--- /dev/null
+Makefile
+Makefile.in
--- /dev/null
+SUBDIRS = man texinfo
+
+EXTRA_DIST = faq.html nanorc.sample
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title>The GNU nano editor FAQ</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (X11; U; Linux 2.2.16 i586) [Netscape]">
+</head>
+<body text="#330000" bgcolor="#ffffff" link="#0000ef" vlink="#51188e" alink="#ff0000">
+<h1>The nano FAQ</h1>
+<h2>Table of Contents</h2>
+<h2><a href="#1">1. General</a></h2>
+<blockquote><p><a href="#1.1">1.1 About this FAQ</a><br>
+ <a href="#1.2">1.2. How do I contribute to it?</a><br>
+ <a href="#1.3">1.3. What is GNU nano?</a><br>
+ <a href="#1.4">1.4. What is the history behind nano?</a><br>
+ <a href="#1.5">1.5. Why the name change from TIP?</a><br>
+ <a href="#1.6">1.6. What is the current version of nano?</a><br>
+ <a href="#1.7">1.7. I want to read the manpage without having to download the program!</a></p></blockquote>
+<h2><a href="#2">2. Where to get GNU nano.</a></h2>
+<blockquote><p><a href="#2.1">2.1. FTP and WWW sites that carry nano.</a><br>
+ <a href="#2.2">2.2. RedHat and derivatives (.rpm) packages.</a><br>
+ <a href="#2.3">2.3. Debian (.deb) packages.</a><br>
+ <a href="#2.4">2.4. By CVS (for the brave).</a></p></blockquote>
+<h2><a href="#3">3. Installation and Configuration</a></h2>
+<blockquote><p><a href="#3.1">3.1. How do I install the RPM or DEB package?</a><br>
+ <a href="#3.2">3.2. Compiling from source: WHAT THE HECK DO I DO NOW?</a><br>
+ <a href="#3.3">3.3. Why does everything go into /usr/local?</a><br>
+ <a href="#3.4">3.4. I get errors about 'bindtextdomain','gettext' and/or 'gettextdomain'. What can I do about it?</a><br>
+ <a href="#3.5">3.5. Nano should automatically run strip on the binary when installing it!</a><br>
+ <a href="#3.6">3.6. How can I make the executable smaller? This is too bloated!</a><br>
+ <a href="#3.7">3.7. Tell me more about this multibuffer stuff!</a><br>
+ <a href="#3.8">3.8. How do I make a .nanorc file that nano will read when I start it?</a></p></blockquote>
+<h2><a href="#4">4. Running</a></h2>
+<blockquote><p><a href="#4.1">4.1. Ack! My backspace/delete/enter/double bucky/meta key doesn't seem to work! What can I do?</a><br>
+ <a href="#4.2">4.2. Nano crashes when I type <insert keystroke here>!</a><br>
+ <a href="#4.3">4.3. Nano crashes when I resize my window. How can I fix that?</a><br>
+ <a href="#4.4">4.4. [version 1.1.12 and earlier] Why does nano show ^\ in the shortcut list instead of ^J?</a><br>
+ <a href="#4.5a">4.5a. [version 1.1.12 and earlier] When I type in a search string, the string I last searched for is already in front of my cursor! What happened?!</a><br>
+ <a href="#4.5b">4.5b. [version 1.2.2 and later] Hey, the search string behavior has reverted, it's now like Pico, what happened to the consistency?</a><br>
+ <a href="#4.6">4.6. I get the message "NumLock glitch detected. Keypad will malfunction with NumLock off." What gives?</a><br>
+ <a href="#4.7">4.7. How do I make nano my default editor (in Pine, mutt, etc.)?</a><br>
+ <a href="#4.8">4.8. I've compiled nano with color support, but I don't see any color when I run it!</a></p></blockquote>
+<h2><a href="#5">5. Internationalization</a></h2>
+<blockquote><p><a href="#5.1">5.1. There's no translation for my language!</a><br>
+ <a href="#5.2">5.2. I don't like the translation for <x> in my language. How can I fix it?</a></p></blockquote>
+<h2><a href="#6">6. Advocacy and Licensing</a></h2>
+<blockquote><p><a href="#6.1">6.1. Why should I use nano instead of Pico?</a><br>
+ <a href="#6.2">6.2. Why should I use Pico instead of nano?</a><br>
+ <a href="#6.3">6.3. What is so bad about the Pine license?</a><br>
+ <a href="#6.4">6.4. Okay, well what mail program should I use then?</a><br>
+ <a href="#6.5">6.5. Why doesn't UW simply change their license?</a><br>
+ <a href="#6.6">6.6. What if tomorrow UW changes the license to be truly Free Software?</a></p></blockquote>
+<h2><a href="#7">7. Miscellaneous</a></h2>
+<blockquote><p><a href="#7.1">7.1. Nano related mailing lists.</a><br>
+ <a href="#7.2">7.2. I want to send the development team a big load of cash (or just a thank you).</a><br>
+ <a href="#7.3">7.3. How do I submit a patch?</a><br>
+ <a href="#7.4">7.4. How do I join the development team?</a><br>
+ <a href="#7.5">7.5. Can I have CVS write access?</a></p></blockquote>
+<h2><a href="#8">8. ChangeLog</a></h2>
+<hr width="100%">
+<h1><a name="1"></a>1. General</h1>
+<h2><a name="1.1"></a>1.1 About this FAQ</h2>
+<blockquote><p>This FAQ was written and is maintained by Chris Allegretta <<a href="mailto:chrisa@asty.org">chrisa@asty.org</a>>, who also happens to be the creator of nano. Maybe someone else will volunteer to maintain this FAQ someday, who knows...</p></blockquote>
+<h2><a name="1.2"></a>1.2. How do I contribute to it?</h2>
+<blockquote><p>Your best bet is to send it to the nano email address, <a href="mailto:nano@nano-editor.org">nano@nano-editor.org</a> and if it is useful enough it will be included in future versions.</p></blockquote>
+<h2><a name="1.3"></a>1.3. What is GNU nano?</h2>
+<blockquote><p>GNU nano is designed to be a free replacement for the Pico text editor, part of the Pine email suite from <a href="http://www.washington.edu/pine/">The University of Washington</a>. It aims to "emulate Pico as closely as possible and perhaps include extra functionality".</p></blockquote>
+<h2><a name="1.4"></a>1.4. What is the history behind nano?</h2>
+<blockquote><p>Funny you should ask!</p>
+ <p><b>In the beginning...</b></p>
+ <p>For years Pine was THE program used to read email on a Unix system. The Pico text editor is the portion of the program one would use to compose his or her mail messages. Many beginners to Unix flocked to Pico and Pine because of their well organized, easy to use interfaces. With the proliferation of GNU/Linux in the mid to late 90's, many University students became intimately familiar with the strengths (and weaknesses) of Pine and Pico.</p>
+ <p><b>Then came Debian...</b></p>
+ <p>The <a href="http://www.debian.org/">Debian GNU/Linux</a> distribution, known for its strict standards in distributing truly "free" software (i.e. software with no restrictions on redistribution), would not include a binary package for Pine or Pico. Many people had a serious dilemma: they loved these programs, but they were not truly free software in the <a href="http://www.gnu.org/philosophy/free-sw.html">GNU</a> sense of the word.</p>
+ <p><b>The event...</b></p>
+ <p>It was in late 1999 when Chris Allegretta (our hero) was yet again complaining to himself about the less-than-perfect license Pico was distributed under, the 1000 makefiles that came with it and how just a few small improvements could make it the Best Editor in the World (TM). Having been a convert from Slackware to Debian, he missed having a simple binary package that included Pine and Pico, and had grown tired of downloading them himself.</p>
+ <p>Finally something snapped inside and Chris coded and hacked like a madman for many hours straight one weekend to make a (barely usable) Pico clone, at the time called TIP (Tip Isn't Pico). The program could not be invoked without a filename, could not save files, had no help menu, spell checker, and so forth. But over time it improved, and with the help of a few great coders it matured to the (hopefully) stable state it is today.</p>
+ <p>In February 2001, nano was declared an official GNU program by Richard Stallman. Nano also reached its first production release on March 22, 2001.</p></blockquote>
+<h2><a name="1.5"></a>1.5. Why the name change from TIP?</h2>
+<blockquote><p>On January 10, 2000, TIP was officially renamed to nano because of a namespace conflict with another program called 'tip'. The original 'tip' program "establishes a full duplex terminal connection to a remote host", and was included with many older Unix systems (and newer ones like Solaris). The conflict was not noticed at first because there is no 'tip' utility included with most GNU/Linux distributions (where nano was developed).</p></blockquote>
+<h2><a name="1.6"></a>1.6. What is the current version of nano?</h2>
+<blockquote><p>The current version of nano *should* be 1.2.2. Of course you should always check the nano homepage to see what the latest and greatest version is.</p></blockquote>
+<h2><a name="1.7"></a>1.7. I want to read the man page without having to download the program!</h2>
+<blockquote><p>Jeez, demanding, aren't we? Okay, look <a href="http://www.nano-editor.org/dist/v1.2/nano.1.html">here</a>.</p></blockquote>
+<hr width="100%">
+<h1><a name="2"></a>2. Where to get GNU nano.</h1>
+<h2><a name="2.1"></a>2.1. FTP and WWW sites that carry nano.</h2>
+<blockquote><p>The nano distribution can be downloaded at the following fine web and ftp sites:</p>
+ <ul>
+ <li><a href="http://www.nano-editor.org/dist/">http://www.nano-editor.org/dist/</a></li>
+ <li><a href="http://www.ewtoo.org/~astyanax/nano/dist/">http://www.ewtoo.org/~astyanax/nano/dist/</a></li>
+ <li><a href="ftp://ftp.gnu.org/pub/gnu/nano/">ftp://ftp.gnu.org/pub/gnu/nano/</a></li>
+ </ul>
+</blockquote>
+<h2><a name="2.2"></a>2.2. RedHat and derivatives (.rpm) packages.</h2>
+<blockquote>
+ <ul>
+ <li><a href="http://www.nano-editor.org/dist/v1.2/RPMS/">http://www.nano-editor.org/dist/v1.2/RPMS/</a></li>
+ <li><a href="http://www.ewtoo.org/~astyanax/nano/dist/v1.2/RPMS/">http://www.ewtoo.org/~astyanax/nano/dist/v1.2/RPMS/</a></li>
+ </ul>
+ <p>Additionally, check out the RedHat contribs section at:</p>
+ <ul>
+ <li><a href="http://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/">http://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/</a></li>
+ <li><a href="ftp://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/">ftp://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/</a></li>
+ </ul>
+</blockquote>
+<h2><a name="2.3"></a>2.3. Debian (.deb) packages.</h2>
+<blockquote><p>Debian users can check out the current nano packages for:</p>
+ <ul>
+ <li><a href="http://packages.debian.org/stable/editors/nano.html">stable</a></li>
+ <li><a href="http://packages.debian.org/testing/editors/nano.html">testing</a></li>
+ <li><a href="http://packages.debian.org/unstable/editors/nano.html">unstable</a></li>
+ </ul>
+ <p>You can also have a look at the <a href="ftp://ftp.debian.org/debian/pool/main/n/nano/">Package Pool</a> to see all the available binary and source packages.</p>
+ <p>Note that versions < 0.9.10 are probably not for those wanting to get serious work done, so if you are using Debian 2.2, check that you have updated to 2.2r3, which comes with nano 0.9.23. If you're tracking unstable, you probably have the newest version already.</p></blockquote>
+<h2><a name="2.4"></a>2.4. By CVS (for the brave).</h2>
+<blockquote><p>For the 'bleeding edge' current version of nano, you can use CVS to download the current source code. <b>Note:</b> believe it or not, by downloading code that has not yet stabilized into an official release, there could quite possibly be bugs, in fact the code may not even compile! Anyway, see <a href="http://savannah.gnu.org/cvs/?group_id=1025">the nano CVS page</a> for info on anonymous CVS access to the nano source.</p></blockquote>
+<hr width="100%">
+<h1><a name="3"></a>3. Installation and Configuration</h1>
+<h2><a name="3.1"></a>3.1. How do install the RPM or DEB package?</h2>
+<blockquote><p>It's simple really! As root, type <b>rpm -Uvh nano-x.y.z-1.i386.rpm</b> if you have a RedHat-ish system or <b>dpkg -i nano_x.y.z-1.deb</b> if you have a Debian-ish system, where <b>x.y.z</b> is the release of nano. There are other programs to install packages, and if you wish to use those, knock yourself out.</p></blockquote>
+<h2><a name="3.2"></a>3.2. Compiling from source: WHAT THE HECK DO I DO NOW?</h2>
+<blockquote><p>Okay, take a deep breath, this really isn't hard. Unpack the nano source with a command like:</p>
+ <p><b>tar -zxvf nano-x.y.z.tar.gz</b></p>
+ <p>If you get error messages about the -z option, try this:</p>
+ <p><b>gzip -dc nano-x.y.z.tar.gz | tar xvf -</b></p>
+ <p>(again, where x.y.z is the version number in question). Then you need to run configure with any options you might want (if any).</p>
+ <p>The average case is this:</p>
+ <p><b>cd nano-x.y.z/</b><br>
+ <b>./configure</b><br>
+ <b>make</b><br>
+ <b>make install</b> (as root, of course)</p></blockquote>
+<h2><a name="3.3"></a>3.3. Why does everything go into /usr/local?</h2>
+<blockquote><p>Well, that's what the <b>configure</b> script defaults to. If you wish to change this, simply do this:</p>
+ <p><b>./configure --prefix=/usr</b></p>
+ <p>to put nano into /usr/bin when you run <b>make install</b>.</p></blockquote>
+<h2><a name="3.4"></a>3.4. I get errors about 'bindtextdomain', 'gettext' and/or 'gettextdomain'. What can I do about it?</h2>
+<blockquote><p>Try doing a <b>./configure --with-included-gettext</b> and see if that solves your problem. You may need to do a <b>make clean; make</b> to get it to work fully.</p></blockquote>
+<h2><a name="3.5"></a>3.5. Nano should automatically run strip on the binary when installing it!</h2>
+<blockquote><p>Actually, it does, but you have to use <b>make install-strip</b>. The default make install does not, and will not, run strip automatically.</p></blockquote>
+<h2><a name="3.6"></a>3.6. How can I make the executable smaller? This is too bloated!</h2>
+<blockquote><p>Actually, there are several parts of the editor that can be disabled. You can pass arguments to the <b>configure</b> script that disable certain features. Here's a brief list:</p>
+ <pre>
+ <b>--disable-tabcomp</b> Disables tab completion code for a smaller binary
+ <b>--disable-justify</b> Disable justify/unjustify function
+ <b>--disable-speller</b> Disables spell checker function
+ <b>--disable-help</b> Disables help function (^G)
+ <b>--disable-browser</b> Disables mini file browser
+ <b>--disable-wrapping</b> Disables all wrapping of text (and -w flag)
+ <b>--disable-mouse</b> Disables mouse support (and -m flag)
+ <b>--disable-operatingdir</b> Disable setting of operating directory</pre>
+ <p>There's also the <b>--enable-tiny</b> option which disables everything above, as well as some larger chunks of the program (like the marker code that you use Control-^ to select with). Also, if you know you aren't going to be using other languages you can use <b>--disable-nls</b> to disable internationalization and save a few K to a few dozen K depending on if you have locale support on your system. And finally there's always good old <b>strip</b> to strip all debugging code and code that exists in libraries on your system.</p>
+ <p>If, on the other hand, you can't live without bells and whistles, you could try:</p>
+ <pre>
+ <b>--enable-extra</b> Enable extra functions, including easter eggs
+ <b>--enable-nanorc</b> Enable use of .nanorc file
+ <b>--enable-color</b> Enables color and syntax highlighting
+ <b>--enable-multibuffer</b> Enables having multiple file buffers open
+ <b>--enable-all</b> Enables all of the above features</pre></blockquote>
+<h2><a name="3.7"></a>3.7. Tell me more about this multibuffer stuff!</h2>
+<blockquote><p>To use multiple file buffers, you must be using nano 1.1.12 or newer, and you must have configured nano with <b>--enable-multibuffer</b> or <b>--enable-extra</b> (use nano -V to check). Then when you want to enable inserting a file into its own buffer instead of into the current file, just hit <b>Meta-F</b>, then insert the file as normal with <b>^R</b>. If you always want files to be loaded into their own buffers, use the <b>--multibuffer</b> or <b>-F</b> flag when you invoke nano. </p>
+ <p>You can move between the buffers you have open with the <b>Meta-<</b> and <b>Meta-></b> keys, or more easily with <b>Meta-,</b> and <b>Meta-.</b> (clear as mud, right? =-). When you have more than one file buffer open, the ^X shortcut will say "Close", instead of the normal "Exit" when only one buffer is open.</p></blockquote>
+<h2><a name="3.8"></a>3.8. How do I make a .nanorc file that nano will read when I start it?</h2>
+<blockquote>It's not hard at all! But, your version of nano must have been compiled with <b>--enable-nanorc</b>, and again must be version 1.1.12 or newer (use nano -V to check your version and compiled features). Then simply copy the <b>nanorc.sample</b> that came with the nano source or your nano package (most likely in /usr/doc/nano) to .nanorc in your home directory. If you didn't get one, the syntax is simple. Flags are turned on and off by using the word <b>set</b> and the getopt_long flag for the feature, for example "set pico" or "set nowrap".</blockquote>
+<hr width="100%">
+<h1><a name="4"></a>4. Running</h1>
+<h2><a name="4.1"></a>4.1. Ack! My backspace/delete/enter/double bucky/meta key doesn't seem to work! What can I do?</h2>
+<blockquote><p>Try setting your $TERM variable to 'vt100'. Nano doesn't yet support every term entry under the sun.</p>
+ <p>Bourne shell users (like bash): <b>export TERM=vt100</b><br>
+ C Shell users (tcsh and csh): <b>setenv TERM vt100</b></p></blockquote>
+<h2><a name="4.2"></a>4.2. Nano crashes when I type <insert keystroke here>!</h2>
+<blockquote><p>If you aren't trying some bizarre keystroke combination with some bizarre $TERM entry, chances are you have found a bug. You are welcome to submit it to the <a href="mailto:nano-devel@gnu.org">nano-devel</a> list or to <a href="mailto:nano@nano-editor.org">nano@nano-editor.org</a>.</p></blockquote>
+<h2><a name="4.3"></a>4.3. Nano crashes when I resize my window. How can I fix that?</h2>
+<blockquote><p>Older versions of nano had this problem, please upgrade to a newer version (at least 0.9.9 would be great, 0.9.12 is recommended).</p></blockquote>
+<h2><a name="4.4"></a>4.4. [version 1.1.12 and earlier] Why does nano show ^\ in the shortcut list instead of ^J?</h2>
+<blockquote><p>The help (^G) and justify (^J) function were among the last to be written. To show the improvements that nano had over Pico (go to line # and replace), ^_ and ^\ were put on the shortcut list. Later, ^G came back in place of ^_ as it proved to be very valuable for new Unix users. If you use the <b>-p</b> option to nano (or hit Meta-P) you will get the same shortcuts at the bottom as Pico.</p></blockquote>
+<a name="4.5"></a>
+<h2><a name="4.5a"></a>4.5a. [version 1.1.12 and earlier] When I type in a search string, the string I last searched for is already in front of my cursor! What happened?!</h2>
+<blockquote><p>In nano version 0.9.20, the default is to have a completely consistent user interface across all user input functions. This means that regardless of whether you're being asked for a filename to insert or write, or a string to search for, the previous value is already inserted before the cursor. If you prefer the old behavior, use the Pico emulation mode (-p or --pico) or just hit Meta-P while in nano (see the ^G help text for more details).</p></blockquote>
+<h2><a name="4.5b"></a>4.5b. [version 1.2.2 and later] Hey, the search string behavior has reverted, it's now like Pico, what happened to the consistency?</h2>
+<blockquote><p>It was decided that consistency was nice, but people are used to Pico's inconsistent behavior. Also, in version 1.1.99pre1, search and replace history was introduced. If you wish to edit your previous search/replace entry (or any previous entry), you can do so by hitting the up arrow to cycle through your history. This method allows the best of both worlds: You don't need to erase the previous string if you want to enter a new one, but you can with one keystroke recall previous entries for editing. Therefore there is now no "Pico mode", nano is and has always been a Pico <b>clone</b>, and clones by default should be compatible.</p></blockquote>
+<h2><a name="4.6"></a>4.6. I get the message "NumLock glitch detected. Keypad will malfunction with NumLock off." What gives?</h2>
+<blockquote><p>Nano (and actually almost all console editors) has issues when cycling the NumLock key in certain X terminals (rxvt, aterm, wterm, etc...). When you switch NumLock from on to off, you put the terminal into an "application mode" that changes what sequences are sent by the keypad. These sequences vary sufficiently from terminal to terminal that it is nearly impossible to work around them from within nano.</p>
+ <p>In a nutshell, if you want to be able to use the keypad with the arrow and page up/down functionality, you have to exit nano and reset your terminal (presumably with "reset" or "stty sane" or similar) and then run nano again with NumLock off. If you know an easier way to restore "normal mode", please mail <a href="mailto:nano@nano-editor.org">nano@nano-editor.org</a>.</p></blockquote>
+<h2><a name="4.7"></a>4.7. How do I make nano my default editor (in Pine, mutt, etc.)?</h2>
+<blockquote><p>You need to make nano your $EDITOR. If you want this to be saved, you should put a line like this in your <b>.bashrc</b> if you use bash (or <b>.zshrc</b> if you believe in zsh):</p>
+ <p><b>export EDITOR=/usr/local/bin/nano</b></p>
+ <p>or if you use tcsh put this in your <b>.cshrc</b> file:</p>
+ <p><b>setenv EDITOR /usr/local/bin/nano</b></p>
+ <p>Change /usr/local/bin/nano to wherever nano is installed on your system. Type "which nano" to find out. This will not take effect until the next time you login. So log out and back in again.</p>
+ <p>Then on top that if you use Pine you must go into setup (type <b>S</b> at the main menu), then configure (type <b>C</b>). Hit enter on the lines that say:</p>
+ <p><b>[ ] enable-alternate-editor-cmd</b><br>
+ <b>[ ] enable-alternate-editor-implicitly</b></p>
+ <p>Then exit (<b>E</b>) and select Yes (<b>Y</b>).</p>
+ <p>Mutt users should see an effect immediately the next time you log in, no further configuration is needed. However, if you want to let people know you use nano to compose your email messages, you can put a line like this in your <b>.muttrc</b>:</p>
+ <p><b>my_hdr X-Composer: nano x.y.z</b></p>
+ <p>Again, replace x.y.z with the version of nano you use.</p></blockquote>
+<h2><a name="4.8"></a>4.8. I've compiled nano with color support, but I don't see any color when I run it!</h2>
+<blockquote><p>If you want nano to actually use color, you have to specify the color configurations you want it to use in your .nanorc. Some example configurations are in the <b>nanorc.sample</b> that comes with the nano source or your nano package. See Section <a href="#3.8">3.8</a>.</p></blockquote>
+<hr width="100%">
+<h1><a name="5"></a>5. Internationalization</h1>
+<h2><a name="5.1"></a>5.1. There's no translation for my language!</h2>
+<blockquote><p>On June of 2001, GNU nano entered the <a href="http://www.iro.umontreal.ca/contrib/po/HTML/">Free Translation Project</a> and since then, translations should be managed from there.</p>
+ <p>If there isn't a translation for your language, you could ask <a href="http://www.iro.umontreal.ca/contrib/po/HTML/teams.html">your language team</a> to translate nano, or better still, join your team and do it yourself. Joining a team is easy. You just need to ask the <a href="mailto:translation@iro.umontreal.ca">TP coordinator</a> to add you to your team, and send a <a href="http://www.iro.umontreal.ca/contrib/po/HTML/disclaim.html">translation disclaimer to the FSF</a> (this is necessary as nano is an official GNU package, but it does <b>not</b> mean that you transfer the rights of your work to the FSF, it's just so the FSF can legally manage them).</p>
+ <p>In any case, translating nano is very easy. Just grab the <b>nano.pot</b> file from the latest and greatest nano distribution (it's in the <b>po/</b> directory) and translate each line into your native language on the <b>msgstr</b> line. When you're done, you should send it to the TP's central po repository.</p></blockquote>
+<h2><a name="5.2"></a>5.2. I don't like the translation for <x> in my language. How can I fix it?</h2>
+<blockquote><p>The best way would probably be to e-mail the person listed in the <code>Last-Translator:</code> field in the <b><your_language>.po</b> file with your suggested corrections and they can make the changes reach the nano-devel list.</p></blockquote>
+<hr width="100%">
+<h1><a name="6"></a>6. Advocacy and Licensing</h1>
+<h2><a name="6.1"></a>6.1. Why should I use nano instead of Pico?</h2>
+<blockquote><p>There are many reasons to use nano instead of Pico, a more complete list can be found at the <a href="http://www.nano-editor.org/">nano homepage</a>.</p></blockquote>
+<h2><a name="6.2"></a>6.2. Why should I use Pico instead of nano?</h2>
+<blockquote><p>Again, check out the <a href="http://www.nano-editor.org/">nano homepage</a> for a good summary of reasons. It really is a matter of personal preference as to which editor you should use. If you're the type of person who likes using the original version of a program, then Pico is the editor for you. If you're looking for a few more features and a 'better' license as far as adding your own changes (sacrificing mailer integration with Pine), nano is the way to go.</p></blockquote>
+<h2><a name="6.3"></a>6.3. What is so bad about the Pine license?</h2>
+<blockquote><p>The U of W license for Pine and Pico is not considered truly Free Software according to both the Free Software Foundation and the <a href="http://www.debian.org/social_contract#guidelines">Debian Free Software Guidelines</a>. The main problem regards the limitations on distributing derived works: according to UW, you can distribute their software, and you can modify it, but you can not do both, i.e. distribute modified binaries.</p></blockquote>
+<h2><a name="6.4"></a>6.4. Okay, well what mail program should I use then?</h2>
+<blockquote><p>If you are looking to use a Free Software program similar to Pine and emacs is not your thing, you should definitely take a look at <a href="http://www.mutt.org/">mutt</a>. It is a full-screen, console based mail program that actually has a lot more flexibility than Pine, but has a keymap included in the distribution that allows you to use the same keystrokes as Pine would to send and receive mail. It's also licensed under the GPL.</p></blockquote>
+<h2><a name="6.5"></a>6.5. Why doesn't UW simply change their license?</h2>
+<blockquote><p>You're really not asking the right person here. I (Chris) waited a long time to see if UW would change their license because of the amount of high quality software being released and developed under the GPL without being taken advantage of by malicious corporate entities or other baddies, but no such luck so far.</p></blockquote>
+<h2><a name="6.6"></a>6.6. What if tomorrow UW changes the license to be truly Free Software?</h2>
+<blockquote><p>Honestly nothing would make me happier than to see that happen. Nano would continue to be developed independently until such time as Pico had all the features nano did or the projects merged. That just does not seem very likely given that there has been no sign of any changes in the past few years in a positive direction.</p></blockquote>
+<hr width="100%">
+<h1><a name="7"></a>7. Miscellaneous</h1>
+<h2><a name="7.1"></a>7.1. Nano related mailing lists.</h2>
+<blockquote><p>There are three mailing lists for nano hosted at <a href="http://savannah.gnu.org/">Savannah</a>, info-nano, help-nano and nano-devel. Info-nano is a very low traffic list where new versions of nano are announced (surprise!) Help-nano is for getting help with the editor without needing to hear all of the development issues surrounding it. Nano-devel is a normally low, sometimes high traffic list for discussing the present and future development of nano. Here are links to where you can sign up for a given list:</p>
+ <p>info-nano - <a href="http://mail.gnu.org/mailman/listinfo/info-nano/">http://mail.gnu.org/mailman/listinfo/info-nano/</a><br>
+ help-nano - <a href="http://mail.gnu.org/mailman/listinfo/help-nano/">http://mail.gnu.org/mailman/listinfo/help-nano/</a><br>
+ nano-devel - <a href="http://mail.gnu.org/mailman/listinfo/nano-devel/">http://mail.gnu.org/mailman/listinfo/nano-devel/</a></p></blockquote>
+<h2><a name="7.2"></a>7.2. I want to send the development team a big load of cash (or just a thank you).</h2>
+<blockquote><p>That's fine. Send it <a href="mailto:nano-devel@gnu.org">our way</a>! Better yet, fix a <a href="http://www.nano-editor.org/dist/v1.2/BUGS">bug</a> in the program or implement a <a href="http://www.nano-editor.org/dist/v1.2/TODO">cool feature</a> and send us that instead (though cash is fine too).</p></blockquote>
+<h2><a name="7.3"></a>7.3. How do I submit a patch?</h2>
+<blockquote><p>See Section <a href="#7.2">7.2</a>.</p></blockquote>
+<h2><a name="7.4"></a>7.4. How do I join the development team?</h2>
+<blockquote><p>The easiest way is to consistently send in good patches that add some needed functionality, fix a bug or two and/or make the program more optimized/efficient. Then ask nicely and you will probably be added to the Savannah development list and be given CVS write access after awhile. There is a lot of responsibility that goes along with being a team member, so don't think it's just something to add to your resume.</p></blockquote>
+<h2><a name="7.5"></a>7.5. Can I have CVS write access?</h2>
+<blockquote><p>Re-read Section <a href="#7.4">7.4</a> and you should know the answer.</p></blockquote>
+<h2><a name="8"></a>8. ChangeLog</h2>
+<blockquote>
+<p>
+2003/07/02 - Added question about nano's not showing color when it's compiled with color support (DLR; suggested by Jordi).<br>
+2003/02/23 - Updated RPM links for nano 1.2.x. (DLR).<br>
+2003/01/16 - Split section 4.5 into 4.5a and 4.5b for search string behavior. Added --enable-all docs.<br>
+2002/12/28 - More misc. fixes (David Benbennick, DLR).<br>
+2002/10/25 - Misc. fixes and link updates (DLR).<br>
+2002/09/10 - Another typo fix (DLR).<br>
+2002/05/15 - Typo fix (DLR).<br>
+2001/12/26 - Misc. fixes (Aaron S. Hawley, DLR).<br>
+2001/10/02 - Update for Free Translation Project.<br>
+2001/10/02 - Assorted fixes, Debian additions.<br>
+2001/06/30 - Silly typo fix.<br>
+2001/05/05 - Spelling fixes by David Lawrence Ramsey.<br>
+2001/05/02 - Misc fixes.<br>
+2001/03/26 - Typo fix in an URL.<br>
+2001/02/17 - Advocacy updates.<br>
+2001/02/15 - Added GNU notes for 0.9.99pre3.<br>
+2001/02/06 - Typo fixes.<br>
+2001/01/14 - Added note about NumLock glitch.<br>
+2001/01/10 - Linux -> GNU/Linux.<br>
+2001/01/09 - Added "making exe smaller" section.<br>
+2000/12/19 - Typo and assorted error fixes.<br>
+2000/11/28 - Added blurb about make install-strip.<br>
+2000/11/19 - Changed Debian frozen to stable.<br>
+2000/11/18 - Previous string display (4.5).<br>
+2000/09/27 - Moved addresses to nano-editor.org.<br>
+2000/06/31 - Initial framework.</p></blockquote>
+<p>$Id$</p>
+</body>
+</html>
--- /dev/null
+Makefile
+Makefile.in
--- /dev/null
+SUBDIRS = fr
+
+man_MANS = nano.1 nanorc.5
+
+EXTRA_DIST = nano.1.html nanorc.5.html $(man_MANS)
--- /dev/null
+Makefile
+Makefile.in
--- /dev/null
+
+mandir = @mandir@/fr
+man_MANS = nano.1 nanorc.5
+
+EXTRA_DIST = $(man_MANS)
--- /dev/null
+.\" Hey, EMACS: -*- nroff -*-
+.\" nano.1 is copyright (C) 1999, 2000, 2001, 2002, 2003 by
+.\" Chris Allegretta <chrisa@asty.org>
+.\"
+.\" (c) 2003 Jean-Philippe Guérard <jean-philippe.guerard@corbeaunoir.org>
+.\" pour l'adaptation française
+.\"
+.\" This is free documentation, see the latest version of the GNU General
+.\" Public License for copying conditions. There is NO warranty.
+.\"
+.\" Ce document est libre. Reportez-vous à la dernière version de la
+.\" licence publique générale GNU (GNU GPL) pour connaître ses
+.\" conditions d'utilisation. AUCUNE garantie n'est offerte.
+.\"
+.TH NANO 1 "19 juin 2003 (v.f. du 25 août 2003)"
+.\" Please adjust this date whenever revising the manpage.
+.\" Merci de modifier ces dates à chaque mise à jour de cette page.
+
+.SH NOM
+nano \- NAno un NOuvel éditeur, un clone libre et amélioré de Pico
+
+.SH SYNOPSIS
+.B nano
+.I [\+LIGNE]\ [options]\ [fichier]
+.br
+
+.SH DESCRIPTION
+Cette page de manuel décrit brièvement la commande \fBnano\fP.
+.PP
+.\" TeX users may be more comfortable with the \fB<whatever>\fP and
+.\" \fI<whatever>\fP escape sequences to invoke bold face and italics,
+.\" respectively.
+\fBnano\fP est un petit éditeur sympathique et libre, qui vise à remplacer
+Pico, l'éditeur par défaut du paquet non-libre Pine. \fBnano\fP ne se
+contentant pas de copier l'interface et l'ergonomie de Pico, il offre
+également certaines fonctionnalités manquantes (ou désactivées par
+défaut) de Pico. Ces fonctionnalités sont, par exemple, les fonctions de
+recherche et de remplacement, et la possibilité de sauter directement à
+un numéro de ligne.
+
+.SH OPTIONS
+.TP
+.B \+\fILIGNE\fP
+Démarre avec le curseur positionné à la ligne numéro \fILIGNE\fP.
+.TP
+.B \-B (\-\-backup)
+Lors de l'écriture d'un fichier, crée une copie de sécurité de la
+version précédente, en ajoutant un tilde (~) au nom du fichier.
+.TP
+.B \-D (\-\-dos)
+Écrit le fichier au format DOS.
+.TP
+.B \-F (\-\-multibuffer)
+Active le mode multifichiers, autorisant l'édition simultanée de
+plusieurs fichiers, si ce mode est disponible.
+.TP
+.B \-H (\-\-historylog)
+Enregistre les chaînes ayant fait l'objet d'une recherche ou d'un
+remplacement dans le fichier
+.I ~/.nano_history
+afin de permettre leur réutilisation. Ceci n'est possible que si
+.B nano
+est capable d'utiliser les fichiers
+nanorc.
+.TP
+.B \-I (\-\-ignorercfiles)
+N'utilise pas les fichiers
+.I $SYSCONFDIR/nanorc
+et
+.IR ~/.nanorc ,
+même si
+.B nano
+est capable de les utiliser.
+.TP
+.B \-K (\-\-keypad)
+Essaie au maximum de ne pas utiliser les appels keypad() de ncurses.
+Utilisez ce paramètre si vous vous apercevez que les flèches du pavé
+numérique ne fonctionnent pas sous nano.
+.TP
+.B \-M (\-\-mac)
+Écrit le fichier au format Mac.
+.TP
+.B \-N (\-\-noconvert)
+Désactive la conversion automatique des fichiers depuis les
+formats Mac et DOS.
+.TP
+.B \-Q \fIchaîne\fP (\-\-quotestr=\fIchaîne\fP)
+Définit le préfixe par défaut des citations. Nano utilise ce préfixe
+pour réaliser une justification correcte des citations.
+Si nano est capable d'utiliser des expressions rationnelles, le préfixe
+par défaut sera "^([\ \\t]*[|>:}#])+", sinon, ce sera ">\ ".
+.TP
+.B \-R (\-\-regexp)
+Active l'utilisation d'expressions rationnelles lors des recherches,
+ainsi que la possibilité d'utiliser \\\fIn\fP dans les chaînes de
+remplacement pour faire référence à la \fIn\fP-ième sous-expression du
+motif de recherche, si cette fonction est disponible.
+.TP
+.B \-S (\-\-smooth)
+Active le défilement progressif. Le texte défilera ligne-par-ligne au
+lieu de défiler morceau-par-morceau.
+.TP
+.B \-T \fInombre\fP (\-\-tabsize=\fInombre\fP)
+Définit la taille (largeur) des tabulations.
+.TP
+.B \-V (\-\-version)
+Affiche le numéro de version et l'auteur.
+.TP
+.B \-Y \fIchaîne\fP (\-\-syntax=\fIchaîne\fP)
+Indique quel mode de colorisation syntaxique adopter, parmi les modes
+définis dans le fichier
+.IR .nanorc ,
+si cette fonctionnalité est disponible.
+.TP
+.B \-c (\-\-const)
+Affiche en permanence la position du curseur.
+.TP
+.B \-h (\-\-help)
+Affiche de la liste des options disponibles en ligne de commande.
+.TP
+.B \-i (\-\-autoindent)
+Indentation automatique. Commence chaque nouvelle ligne au même niveau
+que la ligne précédente. Utile pour l'édition de code source.
+.TP
+.B \-k (\-\-cut)
+^K coupe le texte du curseur à la fin de la ligne, au lieu de couper la
+ligne entière.
+.TP
+.B \-l (\-\-nofollow)
+Si le fichier édité est un lien symbolique, remplace le lien par le
+nouveau fichier, sans le suivre. Ce mode ne serait-il pas utile pour
+éditer les fichiers de
+.IR /tmp \ ?
+.TP
+.B \-m (\-\-mouse)
+Active l'utilisation de la souris (si elle est disponible sur votre
+système).
+.TP
+.B \-o \fIrépertoire\fP (\-\-operatingdir=\fIrépertoire\fP)
+Définit le répertoire de travail. Demande à nano de mettre en place
+quelque-chose de similaire à une cage chroot.
+.TP
+.B \-p (\-\-preserve)
+Préserve les séquences XON et XOFF (^Q et ^S), afin qu'elle soient
+reçues par le terminal.
+.TP
+.B \-r \fIcolonne\fP (\-\-fill=\fIcolonne\fP)
+Passe automatiquement à la ligne à la colonne \fIcolonne\fP. Par défaut,
+le passage à la ligne se fait à la taille de l'écran moins huit. Si la
+valeur indiquée est négative, le point de passage à la ligne sera
+calculé relativement au bord droit de l'écran. Cela permet de faire
+varier le point de passage à la ligne lorsque l'on change la taille de
+l'écran.
+.TP
+.B \-s \fIprogramme\fP (\-\-speller=\fIprogramme\fP)
+Choix d'un correcteur orthographique de remplacement.
+.TP
+.B \-t (\-\-tempfile)
+S'il a été modifié, le fichier sera sauvegardé automatiquement sans
+demande de confirmation. Identique à l'option -t de Pico.
+.TP
+.B \-v (\-\-view)
+Mode visualisation (lecture seule).
+.TP
+.B \-w (\-\-nowrap)
+Désactive le passage automatique à la ligne.
+.TP
+.B \-x (\-\-nohelp)
+Désactive la barre d'aide affichée en bas de l'écran.
+.TP
+.B \-z (\-\-suspend)
+Autorise à suspendre l'éditeur.
+.TP
+.B \-a, \-b, \-e, \-f, \-g, \-j
+Ignoré, pour être compatible avec Pico.
+
+.SH FICHIERS D'INITIALISATION
+\fBnano\fP lit les fichiers d'initialisation dans l'ordre suivant\ :
+\fI$SYSCONFDIR/nanorc\fP, puis \fI~/.nanorc\fP. Reportez-vous à la page
+de manuel de \fBnanorc\fP(5) et au fichier-exemple \fInanorc.sample\fP
+qui devrait être livré avec \fBnano\fP.
+
+.SH NOTES
+Dans certains cas, \fBnano\fP essaiera de sauvegarder le fichier en
+cours d'édition dans un fichier de secours. Cela arrivera
+principalement si \fBnano\fP reçoit un signal SIGHUP ou SIGTERM, ou
+bien se trouve à court de mémoire. Le nom de ce fichier de secours sera
+\fInano.save\fP si le fichier en cours n'avait pas encore de nom, ou
+sera composé du nom du fichier en cours suivi du suffixe «\ .save\ ». Si
+un fichier de ce nom existe déjà dans le répertoire, un «\ .save\ » et
+un nombre seront ajoutée à la fin du nom du fichier en cours afin de le
+rendre unique (par exemple, «\ .save.1\ »). En mode multifichiers,
+\fBnano\fP réalisera une sauvegarde de tous les fichiers en cours dans
+leurs fichiers de secours respectifs.
+
+.SH BOGUES
+Merci de nous faire parvenir vos commentaires et de nous signaler les
+bogues, en écrivant, en anglais, à
+.BR nano@nano-editor.org .
+
+N'hésitez pas à faire parvenir vos suggestions et commentaires relatifs
+à la version française de \fBnano\fP à
+.BR jean-philippe.guerard@corbeaunoir.org .
+
+La liste de discussion anglophone \fBnano\fP est disponible via
+.BR nano-devel@gnu.org .
+Pour vous abonner, envoyez un courrier électronique à
+.BR nano-devel-request@gnu.org ,
+ayant pour objet «\ subscribe\ ».
+
+.SH SITE INTERNET
+http://www.nano-editor.org/
+
+.SH VOIR AUSSI
+.PD 0
+.TP
+\fBnanorc\fP(5)
+.PP
+\fI/usr/share/doc/nano/\fP (ou son équivalent sur votre système)
+
+.SH AUTEUR
+Chris Allegretta <chrisa@asty.org> et d'autres (voir les fichiers
+\fIAUTHORS\fP et \fITHANKS\fP pour plus d'information). Cette page de
+manuel a été initialement rédigée par Jordi Mallach
+<jordi@sindominio.net> pour le système GNU Debian (mais elle peut être
+utilisée par d'autres).
+
+.SH TRADUCTION
+Jean-Philippe Guérard <jean-philippe.guerard@corbeaunoir.org>, 2003.
--- /dev/null
+.\" Hey, EMACS: -*- nroff -*-
+.\" nanorc.5 is Copyright (C) 2003 Free Software Foundation, Inc.
+.\"
+.\" (c) 2003 Jean-Philippe Guérard <jean-philippe.guerard@corbeaunoir.org>
+.\" pour l'adaptation française
+.\"
+.\" This is free documentation, see the latest version of the GNU General
+.\" Public License for copying conditions. There is NO warranty.
+.\"
+.\" Ce document est libre. Reportez-vous à la dernière version de la
+.\" licence publique générale GNU (GNU GPL) pour connaître ses
+.\" conditions d'utilisation. AUCUNE garantie n'est offerte.
+.\"
+.TH NANORC 5 "19 juin 2003 (v.f. du 25 août 2003)"
+.\" Please adjust this date whenever revising the manpage.
+.\" Merci de modifier ces dates à chaque mise à jour de cette page.
+
+.SH NAME
+nanorc \- fichier de configuration de l'éditeur nano du projet GNU
+
+.SH DESCRIPTION
+Cette page de manuel décrit brièvement le fichier de configuration de
+l'éditeur \fBnano\fP du projet GNU.
+.PP
+\fBnano\fP est un petit éditeur sympathique et libre, qui vise à
+remplacer Pico, l'éditeur par défaut du paquet non-libre Pine.
+\fBnano\fP ne se contentant pas de copier l'interface et l'ergonomie de
+Pico, il offre également certaines fonctionnalités manquantes (ou
+désactivées par défaut) de Pico. Ces fonctionnalités sont, par exemple,
+les fonctions de recherche et de remplacement, et la possibilité de
+sauter directement à un numéro de ligne.
+.PP
+Le fichier \fInanorc\fP contient les paramètres par défaut de
+\fBnano\fP. Lors de son démarrage, nano commence par lire le fichier de
+configuration général \fI$SYSCONFDIR/nanorc\fP, puis lit le fichier de
+configuration personnel de l'utilisateur \fI~/.nanorc\fP.
+
+.SH OPTIONS
+Le fichier de configuration accepte une série de commandes «\ set\ »
+(activer) et «\ unset\ » (désactiver), qui permettent de définir la
+configuration de nano au démarrage sans avoir à utiliser d'options sur
+la ligne de commande.
+De plus, les mots clefs «\ syntax\ » (syntaxe) et «\ color\ » (couleur)
+sont utilisés pour définir les règles de colorisation pour différents
+motifs de texte. Nano lira une commande par ligne.
+
+Les options du fichier de configuration ont priorité sur les valeurs par
+défaut utilisées par nano. Les options données sur la ligne de commande
+permettent d'outrepasser les options du fichier de configuration.
+
+Les options sont désactivées (unset) par défaut, sauf les options
+prenant un argument.
+
+Les commandes et arguments reconnus sont\ :
+
+.TP 3
+\fBset/unset autoindent\fP
+Active l'indentation automatique.
+.TP
+\fBset/unset backup\fP
+Crée des copie de sécurité appelées
+.IR nom_du_fichier~ .
+.TP
+\fBset/unset const\fP
+Affiche en permanence la position du curseur dans la barre
+d'information.
+.TP
+\fBset/unset cut\fP
+Par défaut, utilise ^K pour couper le texte du curseur à la fin de la
+ligne, au lieu de couper la ligne entière.
+.TP
+\fBset fill \fIn\fP\fP
+Passe automatiquement à la ligne à la colonne \fIn\fP. Si la valeur
+indiquée est 0 ou moins, la longueur de la ligne sera égale à la largeur
+de l'écran moins \fIn\fP. La valeur par défaut est -8.
+.TP
+\fBset/unset historylog\fP
+Active l'utilisation de
+.I ~/.nano_history
+pour enregistrer et relire les chaînes ayant fait l'objet d'une
+recherche ou d'un remplacement.
+.TP
+\fBset/unset keypad\fP
+Utilise l'autre gestion du pavé numérique.
+.TP
+\fBset/unset multibuffer\fP
+Permet de charger simultanément plusieurs fichiers.
+.TP
+\fBset/unset noconvert\fP
+Pas de conversion depuis les formats DOS et Mac.
+.TP
+\fBset/unset nofollow\fP
+Ne suit pas les liens symboliques lors de l'écriture des fichiers.
+.TP
+\fBset/unset nohelp\fP
+Désactive la barre d'aide affichée en bas de l'écran.
+.TP
+\fBset/unset nowrap\fP
+Désactive le passage automatique à la ligne.
+.TP
+\fBset operatingdir "\fIrépertoire\fP"\fP
+\fBnano\fP ne lira et n'écrira des fichiers qu'à l'intérieur du
+\fIrépertoire\fP et de ses sous-répertoires. De plus, celui-ci devient
+le répertoire courant, afin que les fichiers soient écrits dans ce
+dossier. Par défaut, cette fonction est désactivée.
+.TP
+\fBset/unset preserve\fP
+Préserve les séquences XON et XOFF (^Q et ^S).
+.TP
+\fBset quotestr "\fIchaîne\fP"\fP
+Définit le préfixe par défaut utilisé pour les citations dans les
+courriers électroniques. Ce préfixe est utilisé pour réaliser une
+justification correcte de ces citations. Il s'agira, si votre système le
+permet, d'une «\ expression rationnelle étendue\ ». Dans le cas
+contraire, il s'agira d'une chaîne de texte brut. Si vous disposez des
+expressions rationnelles, la valeur par défaut sera\ :
+
+ set quotestr "^([\ \\t]*[|>:}#])+"
+
+Sinon, ce sera ">\ ". Notez que le «\ \\t\ » ci-dessus correspond à un
+caractère de tabulation.
+.TP
+\fBset/unset regexp\fP
+Utilise par défaut des expressions rationnelles pour les recherches.
+.TP
+\fBset/unset smooth\fP
+Active le défilement ligne-par-ligne du texte.
+.TP
+\fBset speller \fIprogramme\fP\fP
+Utilise le correcteur orthographique \fIprogramme\fP au lieu du
+correcteur intégré, qui s'appuie sur \fIspell\fP.
+.TP
+\fBset/unset suspend\fP
+Autorise à suspendre nano avec ^Z.
+.TP
+\fBset tabsize \fIn\fP\fP
+Utilise une largeur de tabulation de \fIn\fP au lieu de la valeur par
+défaut (8). Cette valeur doit être supérieur à 0.
+.TP
+\fBset/unset tempfile\fP
+S'il a été modifié, le fichier sera sauvegardé automatiquement
+sans demande de confirmation.
+.TP
+\fBset/unset view\fP
+Interdit la modification du fichier.
+.TP
+.B syntax "\fIchaîne\fP" ["\fImotif_de_fichier\fP" ... ]
+Définit une syntaxe nommée \fIchaîne\fP qui pourra être activé via
+l'option \fB-Y\fP, ou qui sera automatiquement activé si le nom du
+fichier en cours correspond au \fImotif_de_fichier\fP. Toutes les
+instructions de colorisation \fBcolor\fP suivantes s'appliqueront à cette
+syntaxe, jusqu'à la définition d'une nouvelle syntaxe.
+.TP
+.B color \fIcouleur_texte\fP[,\fIcouleur_fond\fP] "\fImotif\fP" ...
+Pour la syntaxe en cours, affiche toutes les expressions correspondant à
+l'expression rationnelle \fImotif\fP en utilisant la couleur de texte
+\fIcouleur_texte\fP et éventuellement la couleur de fond
+\fIcouleur_fond\fP. Les couleurs acceptées par \fBnano\fP pour le
+texte et le fond sont\ : \fIwhite\fP (blanc), \fIblack\fP (noir),
+\fIred\fP (rouge), \fIblue\fP (bleu), \fIgreen\fP (vert), \fIyellow\fP
+(jaune), \fImagenta\fP, et \fIcyan\fP. Vous pouvez ajouter à
+ces couleurs le préfixe \fIbright\fP pour obtenir une couleur plus
+lumineuse. Si votre terminal est capable de gérer la transparence, ne
+pas spécifier de \fIcouleur_fond\fP indique à \fBnano\fP d'essayer
+d'utiliser un fond transparent.
+.TP
+.B color \fIcouleur_texte\fP[,\fIcouleur_fond\fP] start="\fImotif_début\fP" end="\fImotif_fin\fP"
+Affiche les expressions commençant par \fImotif_début\fP et se terminant
+par \fImotif_fin\fP en utilisant la couleur de texte \fIcouleur_texte\fP
+et éventuellement la couleur de fond \fIcouleur_fond\fP. Cela permet à
+la colorisation syntaxique de s'étendre sur plusieurs lignes. Notez que
+toutes les occurrences successives de \fImotif_début\fP après le
+\fImotif_début\fP initial seront colorées jusqu'à la prochaine instance
+de \fImotif_fin\fP.
+
+.SH FICHIERS
+.TP
+.I $SYSCONFDIR/nanorc
+Fichier de configuration général
+.TP
+.I ~/.nanorc
+Fichier de configuration personnel
+
+.SH VOIR AUSSI
+.PD 0
+.TP
+\fBnano\fP(1)
+.PP
+\fI/usr/share/doc/nano/examples/nanorc.sample\fP (ou son équivalent sur
+votre système)
+
+.SH AUTEUR
+Chris Allegretta <chrisa@asty.org> et d'autres (voir les fichiers
+\fIAUTHORS\fP et \fITHANKS\fP pour plus d'information). Cette page de
+manuel a été rédigée par Jordi Mallach <jordi@gnu.org>.
+
+.SH TRADUCTION
+Jean-Philippe Guérard <jean-philippe.guerard@corbeaunoir.org>, 2003.
--- /dev/null
+.\" Hey, EMACS: -*- nroff -*-
+.\" nano.1 is copyright (C) 1999, 2000, 2001, 2002, 2003 by
+.\" Chris Allegretta <chrisa@asty.org>
+.\"
+.\" This is free documentation, see the latest version of the GNU General
+.\" Public License for copying conditions. There is NO warranty.
+.\"
+.\" $Id$
+.TH NANO 1 "August 24, 2003"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+
+.SH NAME
+nano \- Nano's ANOther editor, an enhanced free Pico clone
+
+.SH SYNOPSIS
+.B nano
+.I [\+LINE]\ [options]\ [file]
+.br
+
+.SH DESCRIPTION
+This manual page documents briefly the \fBnano\fP command.
+.PP
+.\" TeX users may be more comfortable with the \fB<whatever>\fP and
+.\" \fI<whatever>\fP escape sequences to invoke bold face and italics,
+.\" respectively.
+\fBnano\fP is a small, free and friendly editor which aims to replace
+Pico, the default editor included in the non-free Pine package. Rather
+than just copying Pico's look and feel, \fBnano\fP also implements some
+missing (or disabled by default) features in Pico, such as "search and
+replace" and "go to line number".
+
+.SH OPTIONS
+.TP
+.B \+\fILINE\fP
+Places cursor at \fILINE\fP on startup.
+.TP
+.B \-B (\-\-backup)
+When saving a file, back up the previous version of it to the current
+filename suffixed with a ~.
+.TP
+.B \-D (\-\-dos)
+Write file in DOS format.
+.TP
+.B \-F (\-\-multibuffer)
+Enable multiple file buffers, if available.
+.TP
+.B \-H (\-\-historylog)
+Log search and replace strings to
+.I ~/.nano_history
+so they may be stored for later editing, if nanorc support is
+configured.
+.TP
+.B \-I (\-\-ignorercfiles)
+Don't look at
+.I SYSCONFDIR/nanorc
+or
+.IR ~/.nanorc ,
+if nanorc support is available.
+.TP
+.B \-M (\-\-mac)
+Write file in Mac format.
+.TP
+.B \-N (\-\-noconvert)
+Disable automatic conversion of files from DOS/Mac format.
+.TP
+.B \-Q \fIstr\fP (\-\-quotestr=\fIstr\fP)
+Set the quoting string for justifying. The default is
+"^([\ \\t]*[|>:}#])+" if regular expression support is available, or
+">\ " otherwise.
+.TP
+.B \-R (\-\-regexp)
+Enable regular expression matching for search strings, as well as
+\\n subexpression replacement for replace strings, if available.
+.TP
+.B \-S (\-\-smooth)
+Enable smooth scrolling. Text will scroll line-by-line, instead of the
+usual chunk-by-chunk behavior.
+.TP
+.B \-T \fInum\fP (\-\-tabsize=\fInum\fP)
+Set the size (width) of a tab.
+.TP
+.B \-V (\-\-version)
+Show the current version number and author.
+.TP
+.B \-Y \fIstr\fP (\-\-syntax=\fIstr\fP)
+Specify a specific syntax highlighting from the
+.I .nanorc
+to use, if available.
+.TP
+.B \-c (\-\-const)
+Constantly show the cursor position.
+.TP
+.B \-d (\-\-rebinddelete)
+Interpret the Delete key differently so that both Backspace and Delete
+work properly. You should only need to use this option if Backspace
+acts like Delete on your system.
+.TP
+.B \-h (\-\-help)
+Display a summary of command line options.
+.TP
+.B \-i (\-\-autoindent)
+Indent new lines to the previous line's indentation. Useful when editing
+source code.
+.TP
+.B \-k (\-\-cut)
+Enable cut from cursor to end of line with ^K.
+.TP
+.B \-l (\-\-nofollow)
+If the file being edited is a symbolic link, replace the link with
+a new file, do not follow it. Good for editing files in
+.IR /tmp ,
+perhaps?
+.TP
+.B \-m (\-\-mouse)
+Enable mouse support (if available for your system).
+.TP
+.B \-o \fIdir\fP (\-\-operatingdir=\fIdir\fP)
+Set operating directory. Makes nano set up something similar to a
+chroot.
+.TP
+.B \-p (\-\-preserve)
+Preserve the XON and XOFF sequences (^Q and ^S) so they will be caught
+by the terminal.
+.TP
+.B \-r \fIcols\fP (\-\-fill=\fIcols\fP)
+Wrap lines at column \fIcols\fP. By default, this is the width of the
+screen, less eight. If this value is negative, wrapping will occur at
+\fIcols\fP columns from the right of the screen, allowing the wrap point
+to vary along with the screen width if resized.
+.TP
+.B \-s \fIprog\fP (\-\-speller=\fIprog\fP)
+Enable alternative spell checker command.
+.TP
+.B \-t (\-\-tempfile)
+Always save changed buffer without prompting. Same as Pico -t option.
+.TP
+.B \-v (\-\-view)
+View file (read only) mode.
+.TP
+.B \-w (\-\-nowrap)
+Disable wrapping of long lines.
+.TP
+.B \-x (\-\-nohelp)
+Disable help screen at bottom of editor.
+.TP
+.B \-z (\-\-suspend)
+Enable suspend ability.
+.TP
+.B \-a, \-b, \-e, \-f, \-g, \-j
+Ignored, for compatibility with Pico.
+
+.SH INITIALIZATION FILE
+\fBnano\fP will read initialization files in the following order:
+.IR SYSCONFDIR/nanorc ,
+then
+.IR ~/.nanorc .
+Please see
+.BR nanorc (5)
+and the example file \fBnanorc.sample\fP which should be provided with
+\fBnano\fP.
+
+.SH NOTES
+\fBnano\fP will try to dump the buffer into an emergency file in some
+cases. Mainly, this will happen if \fBnano\fP receives a SIGHUP or
+SIGTERM or runs out of memory, when it will write the buffer into a file
+named
+.I nano.save
+if the buffer didn't have a name already, or will add a ".save" suffix
+to the current filename. If an emergency file with that name already
+exists in the current directory, ".save" and a number (e.g. ".save.1")
+will be suffixed to the current filename in order to make it unique. In
+multibuffer mode, \fBnano\fP will write all the open buffers to the
+respective emergency files.
+
+.SH BUGS
+Please send any comments or bug reports to
+.BR nano@nano-editor.org .
+
+The \fBnano\fP mailing list is available from
+.BR nano-devel@gnu.org .
+
+To subscribe, email to
+.B nano-devel-request@gnu.org
+with a subject of "subscribe".
+
+.SH HOMEPAGE
+http://www.nano-editor.org/
+
+.SH SEE ALSO
+.PD 0
+.TP
+\fBnanorc\fP(5)
+.PP
+\fI/usr/share/doc/nano/\fP (or equivalent on your system)
+
+.SH AUTHOR
+Chris Allegretta <chrisa@asty.org>, et al (see
+.I AUTHORS
+and
+.I THANKS
+for details). This manual page was originally written by Jordi Mallach
+<jordi@sindominio.net>, for the Debian GNU system (but may be used by
+others).
--- /dev/null
+.\" Hey, EMACS: -*- nroff -*-
+.\" nanorc.5 is Copyright (C) 2003 Free Software Foundation, Inc.
+.\"
+.\" This is free documentation, see the latest version of the GNU General
+.\" Public License for copying conditions. There is NO warranty.
+.\"
+.\" $Id$
+.TH NANORC 5 "August 24, 2003"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.SH NAME
+nanorc \- GNU nano's rcfile
+.SH DESCRIPTION
+This manual page documents GNU \fBnano\fP's rcfile.
+.PP
+\fBnano\fP is a small, free and friendly editor which aims to replace
+Pico, the default editor included in the non-free Pine package. Rather
+than just copying Pico's look and feel, \fBnano\fP also implements some
+missing (or disabled by default) features in Pico, such as "search and
+replace" and "go to line number".
+.PP
+The \fInanorc\fP file contains the default settings for \fBnano\fP.
+During startup, \fBnano\fP will first read its system-wide settings from
+.IR SYSCONFDIR/nanorc ,
+and then user-specific settings from
+.IR ~/.nanorc .
+
+.SH OPTIONS
+The configuration file accepts a series of "set" and "unset" commands,
+which can be used to configure nano on startup without using the
+command-line options. Additionally, the "syntax" and "color" keywords
+are used to define syntax highlighting rules for different text
+patterns. GNU nano will read one command per line.
+
+Options in rcfiles take precedence over nano's defaults, and command
+line options override rcfile settings.
+
+Options are unset by default, except for options that take an argument.
+
+The supported commands and arguments are:
+
+.TP 3
+\fBset/unset autoindent\fP
+Use auto-indentation.
+.TP
+\fBset/unset backup\fP
+Create backup files in
+.IR filename~ .
+.TP
+\fBset/unset const\fP
+Constantly display the cursor position in the status bar.
+.TP
+\fBset/unset cut\fP
+Use cut to end of line with ^K by default.
+.TP
+\fBset fill \fIn\fP\fP
+Wrap lines at column number \fIn\fP. If \fIn\fP is 0 or less, the line
+length will be the screen width less \fIn\fP. The default value is -8.
+.TP
+\fBset/unset historylog\fP
+Enable
+.I ~/.nano_history
+for saving and reading search/replace strings.
+.TP
+\fBset/unset multibuffer\fP
+Allow inserting files into their own buffers.
+.TP
+\fBset/unset noconvert\fP
+Don't convert files from DOS/Mac format.
+.TP
+\fBset/unset nofollow\fP
+Don't follow symlinks when writing files.
+.TP
+\fBset/unset nohelp\fP
+Don't display the help lists at the bottom of the screen.
+.TP
+\fBset/unset nowrap\fP
+Don't wrap text at all.
+.TP
+\fBset operatingdir "\fIdirectory\fP"\fP
+\fBnano\fP will only read and write files inside \fIdirectory\fP and its
+subdirectories. Also, the current directory is changed to here, so
+files are inserted from this dir. By default the operating directory
+feature is turned off.
+.TP
+\fBset/unset preserve\fP
+Preserve the XON and XOFF keys (^Q and ^S).
+.TP
+\fBset quotestr "\fIstring\fP"\fP
+The email-quote string, used to justify email-quoted paragraphs. This
+is an "extended regular expression" if your system supports them,
+otherwise a literal string. The default value is
+
+ set quotestr "^([\ \\t]*[|>:}#])+"
+
+if you have regexps, otherwise set quotestr ">\ ". Note that '\\t'
+above stands for a literal Tab character.
+.TP
+\fBset/unset rebinddelete\fP
+Interpret the Delete key differently so that both Backspace and Delete
+work properly. You should only need to use this option if Backspace
+acts like Delete on your system.
+.TP
+\fBset/unset regexp\fP
+Do regular expression searches by default.
+.TP
+\fBset/unset smooth\fP
+Use smooth scrolling by default.
+.TP
+\fBset speller \fIspellprog\fP\fP
+Use spelling checker \fIspellprog\fP instead of the built-in one, which
+calls \fIspell\fP.
+.TP
+\fBset/unset suspend\fP
+Allow nano to be suspended with ^Z.
+.TP
+\fBset tabsize \fIn\fP\fP
+Use a tab size of \fIn\fP instead of the default (8); must be greater
+than 0.
+.TP
+\fBset/unset tempfile\fP
+Save automatically on exit, don't prompt.
+.TP
+\fBset/unset view\fP
+Disallow file modification.
+.TP
+.B syntax "\fIstr\fP" ["\fIfileregex\fP" ... ]
+Defines a syntax named \fIstr\fP which can be activated via the \fB-Y\fP
+flag, or will be automatically activated if the current filename matches
+\fIfileregex\fP. All following \fBcolor\fP statements will apply to
+\fIsyntax\fP until a new syntax is defined.
+.TP
+.B color \fIfgcolor\fP[,\fIbgcolor\fP] "\fIregex\fP" ...
+For the currently defined syntax, display all expressions matching
+\fIregex\fP with foreground color \fIfgcolor\fP and optional background
+color \fIbgcolor\fP. Legal colors for foreground and background color
+are: white, black, red, blue, green, yellow, magenta, and cyan. You may
+use the prefix "bright" to force a stronger color highlight. If your
+terminal supports transparency, not specifying a \fIbgcolor\fP tells
+\fBnano\fP to attempt to use a transparent background.
+.TP
+.B color \fIfgcolor\fP[,\fIbgcolor\fP] start="\fIsr\fP" end="\fIer\fP"
+Display expressions which start with \fIsr\fP and end with \fIer\fP
+with foreground color \fIfgcolor\fP and optional background color
+\fIbgcolor\fP. This allows syntax highlighting to span multiple lines.
+Note that all subsequent instances of \fIsr\fP after an initial \fIsr\fP
+is found will be highlighted until the first instance of \fIer\fP.
+\fI
+
+.SH FILES
+.TP
+.I SYSCONFDIR/nanorc
+System-wide configuration file
+.TP
+.I ~/.nanorc
+Per-user configuration file
+.SH SEE ALSO
+.PD 0
+.TP
+\fBnano\fP(1)
+.PP
+\fI/usr/share/doc/nano/examples/nanorc.sample\fP (or equivalent on your
+system)
+.SH AUTHOR
+Chris Allegretta <chrisa@asty.org>, et al (see
+.I AUTHORS
+and
+.I THANKS
+for details).
+This manual page was written by Jordi Mallach <jordi@gnu.org>.
--- /dev/null
+## Sample initialization file for GNU nano
+## Please note that you must have configured nano with --enable-nanorc
+## for this file to be read! Also note that characters specially
+## interpreted by the shell should not be escaped here.
+##
+## To make sure a value is not enabled, use "unset <option>"
+##
+## For the options that take parameters, the default value is given.
+## Other options are unset by default.
+
+## Use auto-indentation
+# set autoindent
+
+## Backup files to filename~
+# set backup
+
+## Constantly display the cursor position in the status bar.
+# set const
+
+## Use cut to end of line with ^K by default
+# set cut
+
+## Set the line length for wrapping text and justifying paragraphs.
+## If fill is negative, the line length will be the screen width less
+## this number.
+##
+# set fill -8
+
+## Enable ~/.nano_history for saving and reading search/replace strings.
+# set historylog
+
+## Allow multiple file buffers (using ^R inserts into separate buffer).
+## You must have configured with --enable-multibuffer or --enable-extra
+## for this to work.
+##
+## set multibuffer
+
+## Don't convert files from DOS/Mac format
+# set noconvert
+
+## Don't follow symlinks when writing files
+# set nofollow
+
+## Don't display the help lists at the bottom of the screen
+# set nohelp
+
+## Don't wrap text at all
+# set nowrap
+
+## Set operating directory. nano will not read or write files outside
+## this directory and its subdirectories. Also, the current directory
+## is changed to here, so files are inserted from this dir. A blank
+## string means the operating directory feature is turned off.
+##
+# set operatingdir ""
+
+## Preserve the XON and XOFF keys (^Q and ^S)
+# set preserve
+
+## The email-quote string, used to justify email-quoted paragraphs.
+## This is an extended regular expression if your system supports them,
+## otherwise a literal string. Default:
+# set quotestr "^([ ]*[\|>:}#])+"
+## if you have regexps, otherwise:
+# set quotestr "> "
+## You can get old nano quoted-justify behavior via:
+# set quotestr "(> )+"
+
+## Fix Backspace if it acts like Delete
+# set rebinddelete
+
+## Do extended regular expression searches by default
+# set regexp
+
+## Use smooth scrolling as the default
+# set smooth
+
+## Use this spelling checker instead of the internal one. This option
+## does not properly have a default value.
+##
+# set speller "aspell -c"
+
+## Allow nano to be suspended with ^Z
+# set suspend
+
+## Use this tab size instead of the default; it must be greater than 0
+# set tabsize 8
+
+## Save automatically on exit, don't prompt
+# set tempfile
+
+## Disallow file modification, why would you want this in an rc file? ;)
+# set view
+
+## Color setup
+## Format:
+## syntax "short description" ["filename regex" ...]
+## color foreground,background "regex" ["regex"...]
+##
+## Legal colors: white, black, red, blue, green, yellow, magenta, cyan.
+## You may use the prefix "bright" to mean a stronger color highlight.
+##
+## To use multi-line regexes use the start="regex" end="regex" format.
+##
+## If your system supports transparency, not specifying a background
+## color will use a transparent color. If you don't want this, be sure
+## to set the background color to black or white.
+##
+# syntax "c-file" "\.(c|h)$"
+# color red "\<[A-Z_]{2,}\>"
+# color green "\<(float|char|int|void|static|const|struct)\>"
+# color brightyellow "\<(if|while|do|else|case|switch)\>"
+# color brightcyan "^ *# *(define|include|ifn?def|endif|elif|else|if)"
+##
+## You will in general want your comments and strings to come last,
+## because syntax highlighting rules will be applied in the order they
+## are read in.
+##
+# color brightyellow "<[^= ]*>" ""(\\.|[^\"])*""
+##
+## This string is VERY resource intensive!!!
+# color brightyellow start=""(\\.|[^\"])*\\ *$" end="^(\\.|[^\"])*""
+##
+## And we want to have some nice comment highlighting too
+# color brightblue "//.*"
+# color brightblue start="/\*" end="\*/"
+
+## Here is a short example for HTML
+# syntax "HTML" "\.html$"
+# color blue start="<" end=">"
+# color red "&[^; ]*;"
+
+## Here is a short example for TeX files
+# syntax "TeX" "\.tex$"
+# color green "\\.|\\[A-Za-z]*"
+# color magenta "[{}]"
+# color blue "%.*"
+
+## Here is an example for quoted emails (under e.g. mutt)
+# syntax "mutt"
+# color green "^>.*"
+
+## Here is an example for groff
+##
+# syntax "groff" "\.ms$" "\.mm$" "\.me$" "\.tmac$" "^tmac." ".rof"
+## The argument of .nr or .ds
+# color cyan "^\.ds [^ ]*"
+# color cyan "^\.nr [^ ]*"
+## Single character escapes
+# color brightmagenta "\\."
+## Highlight the argument of \f or \s in the same color
+# color brightmagenta "\\f."
+# color brightmagenta "\\f\(.."
+# color brightmagenta "\\s(\+|\-)?[0-9]"
+## \n
+# color cyan "(\\|\\\\)n."
+# color cyan "(\\|\\\\)n\(.."
+# color cyan start="(\\|\\\\)n\[" end="]"
+## Requests
+# color brightgreen "^\. *[^ ]*"
+## Comments
+# color yellow "^\.\\\".*$"
+## Strings
+# color green "(\\|\\\\)\*."
+# color green "(\\|\\\\)\*\(.."
+# color green start="(\\|\\\\)\*\[" end="]"
+## Characters
+# color brightred "\\\(.."
+# color brightred start="\\\[" end="]"
+## Macro arguments
+# color brightcyan "\\\\\$[1-9]"
+
+## Here is an example for perl
+##
+# syntax "perl" "\.p[lm]$"
+# color red "\<(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork)|get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join|keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek|seekdir|se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr|y|truncate|umask|un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\>"
+# color magenta "\<(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\>"
+# color cyan start="[$@%]" end="( |\\W|-)"
+# color yellow "".*"|qq\|.*\|"
+# color white "[sm]/.*/"
+# color white start="(^use| = new)" end=";"
+# color green "#.*"
+# color yellow start="<< 'STOP'" end="STOP"
+
+## Here is an example for Java source
+##
+# syntax "Java source" "\.java$"
+# color green "\<(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\>"
+# color red "\<(break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\>"
+# color cyan "\<(abstract|class|extends|final|implements|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile)\>"
+# color red ""[^\"]*""
+# color yellow "\<(true|false|null)\>"
+# color blue "//.*"
+# color blue start="/\*" end="\*/"
+# color brightblue start="/\*\*" end="\*/"
+# color brightgreen,green " +$"
+
+## Here is an example for your .nanorc
+##
+# syntax "nanorc" "(\.)?nanorc$"
+# color brightwhite "^ *(set|unset|syntax|color).*$"
+# color cyan "^ *(set|unset) +(autoindent|backup|const|cut|fill|historylog|multibuffer|noconvert|nofollow|nohelp|nowrap|operatingdir|preserve|quotestr|rebinddelete|regexp|smooth|speller|suspend|tabsize|tempfile|view)"
+# color green "^ *(set|unset|syntax)\>"
+# color yellow "^ *color +(bright)?(white|black|red|blue|green|yellow|magenta|cyan)(,(white|black|red|blue|green|yellow|magenta|cyan))?\>"
+# color magenta "^ *color\>" "\<(start|end)="
+# color white "\"(\\.|[^\"])*\""
+# color blue "^ *#.*$"
--- /dev/null
+Makefile
+Makefile.in
+nano.info
+texinfo.tex
--- /dev/null
+
+info_TEXINFOS = nano.texi
+MAKEINFO = makeinfo --no-split
+
+EXTRA_DIST = nano.info
--- /dev/null
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename nano.info
+@settitle nano Command Manual
+@c %**end of header
+
+@c This file has the new style title page commands.
+@c Run `makeinfo' rather than `texinfo-format-buffer'.
+@smallbook
+@set EDITION 0.1
+@set VERSION 1.2.2
+@set UPDATED 24 Aug 2003
+
+@dircategory Editors
+@direntry
+* nano: (nano). Small and friendly text editor.
+@end direntry
+
+@c tex
+@c \overfullrule=0pt
+@c end tex
+
+@titlepage
+@title GNU @code{nano}
+@subtitle a small and friendly text editor.
+@subtitle version 1.2.2
+
+@author Chris Allegretta
+@page
+
+This manual documents GNU @code{nano}, a small and friendly text
+editor.
+
+This manual is part of the GNU @code{nano} distribution.@*
+@sp4
+Copyright (C) 1999, 2000, 2001, 2002 Chris Allegretta.
+
+Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+@iftex
+Permission is granted to process this file through @TeX{} and print the
+results, provided the printed document carries copying permission notice
+identical to this one except for the removal of this paragraph (this
+paragraph not being relevant to the printed manual).
+@end iftex
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation
+approved by the Foundation.
+
+You may contact the author by
+e-mail: @email{chrisa@@asty.org}@*
+@end titlepage
+
+@node Top, Introduction, (dir), (dir)
+
+This manual documents GNU @code{nano}, a small and friendly text
+editor.
+
+@menu
+* Introduction::
+* Editor Basics::
+* Online Help::
+* Feature Toggles::
+* The File Browser::
+* Pico Compatibility::
+* Building and Configure Options::
+@end menu
+
+@node Introduction, Editor Basics, Top, Top
+@chapter Introduction
+
+GNU @code{nano} is a small and friendly text editor. Besides basic text
+editing, @code{nano} offers many extra features like an interactive
+search and replace, goto line number, auto-indentation, feature toggles,
+internationalization support, and filename tab completion.
+
+@menu
+* Overview::
+* Command Line Options::
+@end menu
+
+@node Overview, Command Line Options, Introduction, Introduction
+@section Overview
+
+@code{nano} +LINE [GNU long option] [option] [ @var{file ...} ]
+
+The original goal for @code{nano} was a complete bug-for-bug compatible
+emulation of Pico, but nano's main goal is to be as compatible as
+possible while offering a superset of Pico's functionality. Also see
+@xref{Pico Compatibility}, for other differences.
+
+Email bug reports to @email{nano@@nano-editor.org}.
+
+@node Command Line Options, , Overview, Introduction
+@section Command Line Options
+
+@code{nano} takes the following options from the command line:
+
+@table @code
+
+@item -B, --backup
+When saving a file, back up the previous version of it to the current
+filename suffixed with a ~.
+
+@item -D, --dos
+Write file in DOS format.
+
+@item -F, --multibuffer
+Enable multiple file buffers, if available.
+
+@item -I, --ignorercfiles
+Don't look at SYSCONFDIR/nanorc or ~/.nanorc, if nanorc support is
+available.
+
+@item -M, --mac
+Write file in Mac format.
+
+@item -N, --noconvert
+Do not convert files from DOS/Mac format.
+
+@item -Q [str], --quotestr [str]
+Set the quoting string for justifying. The default is
+
+@quotation
+@code{^([ \t]*[|>:@}#])+}
+@end quotation
+
+if regular expression support is available, or ``> '' otherwise. Note
+that @code{\t} above stands for a literal Tab character.
+
+@item -R, --regexp
+Turn on regular expression search and search/replace.
+
+@item -S, --smooth
+Enable smooth scrolling.
+
+@item -T [num], --tabsize=[num]
+Set the displayed tab length to [num] columns.
+
+@item -V, --version
+Print the version number and copyright and quit.
+
+@item -Y, --syntax=[str]
+Specify a specific syntax highlighting from the .nanorc to use, if
+available.
+
+@item -c, --const
+Constantly display the cursor position and line number on the statusbar.
+
+@item -d, --rebinddelete
+Interpret the Delete key differently so that both Backspace and Delete
+work properly. You should only need to use this option if Backspace
+acts like Delete on your system.
+
+@item -h, --help
+Print the usage and exit.
+
+@item -i, --autoindent
+Automatically indent new lines to the same number of spaces and tabs as
+the previous line.
+
+@item -k, --cut
+Makes ^K cut from the current cursor position to the end of the current
+line.
+
+@item -l, --nofollow
+When writing files, if the given file is a symbolic link it is removed
+and a new file is created.
+
+@item -m, --mouse
+Enables the use of the mouse to select text (currently only useful for
+running under the X window system).
+
+@item -o [dir], --operatingdir=[dir]
+Set operating directory. Makes @code{nano} set up something similar to
+a chroot.
+
+@item -p, --preserve
+Preserve the ^Q (XON) and ^S (XOFF) sequences so data being sent to the
+editor can be can be stopped and started.
+
+@item -r [#cols], --fill=[#cols].
+Wrap lines at column #cols. By default this is the width of the screen,
+less eight. If this value is negative, wrapping will occur at #cols
+from the right of the screen, allowing it to vary along with the screen
+width if the screen is resized.
+
+@item -s [prog], --speller=[prog]
+Invoke [prog] as the spell checker. By default, @code{nano} uses its
+own interactive spell checker that requires the @code{spell} program be
+installed on your system.
+
+@item -t, --tempfile
+Do not ask whether or not to save the current contents of the file when
+exiting, assume yes. This is most useful when using @code{nano} as the
+composer of a mailer program.
+
+@anchor{Expert Mode}
+@item -x, --nohelp
+In Expert Mode, the Shortcut Lists will not appear at the bottom of the
+screen. This affects the location of the statusbar as well, as in
+Expert Mode it is located at the very bottom of the editor.
+
+Note: When accessing the help system, Expert Mode is temporarily
+disabled to display the help system navigation keys.
+
+@item -v, --view
+Do not allow the contents of the file to be altered. Note that this
+flag should NOT be used in place of correct file permissions to
+implement a read-only file.
+
+@item -w, --nowrap
+Do not wrap long lines at any length. This option overrides any value
+for -r.
+
+@item -z, --suspend
+Enable suspend ability of @code{nano} using the system's suspend
+keystroke (usually ^Z).
+
+@item -a, -b, -e, -f, -g, -j
+Ignored, for compatibility with Pico.
+
+@item +LINE
+Start at line number LINE instead of the default of line 1.
+@end table
+
+@node Editor Basics, Online Help, Introduction, Top
+@chapter Editor Basics
+@menu
+* Entering Text::
+* Special Functions::
+* The Titlebar::
+* The Statusbar::
+* Shortcut Lists::
+@end menu
+
+@node Entering Text, Special Functions, Editor Basics, Editor Basics
+@section Entering Text
+
+All key sequences in @code{nano} are entered using the keyboard.
+@code{nano} is a ``modeless'' editor. All keys with the exception of
+Control and Meta key sequences will enter text into the file being
+edited.
+
+@node Special Functions, The Titlebar, Entering Text, Editor Basics
+@section Special Functions
+
+Special functions use the Control key (displayed in the help and
+shortcut lists as ^), the Meta key (displayed as M), or the Esc key.
+
+@itemize @bullet
+@item
+Control key sequences are entered by holding down the Control key and
+pressing the desired key, or by pressing the Esc key twice and pressing
+the desired key.
+@item
+Pressing Esc twice and then typing a three-digit number from 000 to 255
+will enter the character with the corresponding ASCII code.
+@item
+Meta key sequences can be entered in a number of possible ways: Pressing
+the Escape key, then releasing it and pressing the desired key, or
+holding down the Alt key while pressing the desired key. This varies
+from keyboard to keyboard, and certain commercial operating systems
+``swallow'' the Alt key so that it never reaches the application. If
+your operating system does this, you should use the Escape key to
+generate Meta key sequences.
+@end itemize
+
+@node The Titlebar, The Statusbar, Special Functions, Editor Basics
+@section The Titlebar
+
+The titlebar is the line displayed at the top of the editor. There are
+three sections: left, center and right. The section on the left
+displays the version of @code{nano} being used. The center section
+displays the current file name, or ``New Buffer'' if the file has not
+yet been named. The section on the right will display ``Modified'' if
+the file has been modified since it was last saved or opened.
+
+Special modes: When @code{nano} is in ``File browser'' mode, the center
+section will display the current directory instead of the filename.
+@xref{The File Browser}.
+
+@node The Statusbar, Shortcut Lists, The Titlebar, Editor Basics
+@section The Statusbar
+
+The statusbar is located three lines from the bottom of the screen (or
+the bottom line in Expert Mode. @xref{Expert Mode}, for more info).
+
+The Statusbar shows important and informational messages. Any error
+messages that occur from using the editor will appear on the statusbar.
+Any questions that are asked of the user will be asked on the statusbar,
+and any user input (search strings, file names, etc) will be input on
+the statusbar.
+
+@node Shortcut Lists, , The Statusbar, Editor Basics
+@section Shortcut Lists
+
+The Shortcut Lists are the two lines at the bottom of the screen which
+show some of the more commonly used functions in the editor.
+
+@node Online Help, Feature Toggles, Editor Basics, Top
+@chapter Online Help
+
+The online help system in @code{nano} is available by pressing ^G.
+It is fairly self explanatory, documenting the various parts of the
+editor and available keystrokes. Navigation is via the ^Y (Page Up)
+and ^V (Page Down) keys. ^X exits the help system.
+
+
+@node Feature Toggles, The File Browser, Online Help, Top
+@chapter Feature Toggles
+
+Toggles allow you to change certain aspects of the editor that
+would normally be done via command line flags. They are invoked via
+certain Meta key sequences. @xref{Special Functions}, for more info.
+The following global toggles are available:
+
+@table @code
+
+@item Backup File Toggle (Meta-B)
+toggles the -B (@code{--backup}) command line flag.
+
+@item DOS Format Toggle (Meta-D)
+toggles the -D (@code{--dos}) command line flag.
+
+@item Multiple Files Toggle (Meta-F)
+toggles the -F (@code{--multibuffer}) command line flag.
+
+@item AutoIndent Toggle (Meta-I)
+toggles the -i (@code{--autoindent}) command line flag.
+
+@item Cut To End Toggle (Meta-K)
+toggles the -k (@code{--cut}) command line flag.
+
+@item Mouse Toggle (Meta-M)
+toggles the -m (@code{--mouse}) command line flag.
+
+@item Mac Format Toggle (Meta-O)
+toggles the -M (@code{--mac}) command line flag.
+
+@item Smooth Scrolling Toggle (Meta-S)
+toggles the -S (@code{--smooth}) command line flag.
+
+@item AutoWrap Toggle (Meta-W)
+toggles the -w (@code{--nowrap}) command line flag.
+
+@item Expert/Nohelp Toggle (Meta-X)
+toggles the -x (@code{--nohelp}) command line flag.
+
+@item Suspend Toggle (Meta-Z)
+toggles the -z (@code{--suspend}) command line flag.
+
+@item Open Previous File Toggle (Meta-<)
+changes buffer to previously loaded file.
+
+@item Open Next File Toggle (Meta->)
+changes buffer to next loaded file.
+
+@end table
+
+
+@node The File Browser, Pico Compatibility, Feature Toggles, Top
+@chapter The File Browser
+
+When reading or writing files, pressing ^T will invoke the file browser.
+Here, one can navigate directories in a graphical manner in order to
+find the desired file.
+
+Basic movement in the file browser is accomplished with the arrow keys
+and page up/down. The behavior of the enter (or `s') key varies by what
+is currently selected. If the currently selected object is a directory,
+the file browser will enter and display the contents of the directory.
+If the object is a file, this filename and path are copied to the
+statusbar and the file browser is exited.
+
+@node Pico Compatibility, Building and Configure Options, The File Browser, Top
+@chapter Pico Compatibility
+
+@code{nano} attempts to emulate Pico as closely as possible, but there
+are certain differences between the editors:
+
+@table @code
+@item Search and Replace History
+As of version 1.2.2 of @code{nano}, text entered as search or replace
+strings will be stored and can be accessed with the up/down arrow keys.
+Previously, @code{nano} offered a more consistent, but incompatible with
+Pico, method for entering search and replace strings. In the old
+method, previous entries would be displayed by default as editable text
+in front of the cursor, as opposed to being bracketed and uneditable as
+it is in Pico. The old behavior could be made compatible with Pico via
+the @code{-p} flag, but recent versions of Pico use the @code{-p} flag
+to preserve the XON and XOFF sequences within the editor. Since with
+the new method search and replace strings can still be edited by simply
+hitting the up arrow key once, the old method was removed completely.
+
+
+@item Writing or Appending Selected Text to Files
+Text selected using the Control-Caret (^^) key can be written out or
+appended to a new or existing file using the Writeout key (^O).
+
+@item Toggles
+Many options which alter the functionality of the program can be
+"toggled" on or off using Meta key sequences, meaning the program does
+not have to be restarted to turn a particular feature of the editor on
+or off. Please see the internal help function (^G) for a list of what
+functions can be toggled for a particular version of @code{nano}. Also
+see @xref{Feature Toggles}, though this may be out of date.
+
+@item Cursor Position Display
+The output of the "Display Cursor Position" in @code{nano} displays
+the given column position, as well as the row and total character
+position of the cursor.
+
+@item Interactive Replace and Spell Checker
+It is worth noting that the @code{nano} replace function is interactive,
+i.e, it does not stop after one search string is found and automatically
+replace it. The @code{nano} implementation will stop at each search
+string found and query whether to replace this instance or not. The
+internal spell checker operates similarly. Note that these is no way to
+force these functions to behave in the Pico fashion. As of version
+1.2.2, misspelled words are sorted and trimmed for uniqueness in the
+internal spell checker such that the words 'apple' and 'Apple' will be
+prompted for correction separately.
+@end table
+
+@node Building and Configure Options, , Pico Compatibility, Top
+@chapter Building and Configure Options
+
+Building @code{nano} from source is fairly straightforward if you are
+familiar with compiling programs with autoconf support:
+
+@itemize @bullet
+@item tar xvfz nano-x.y.z.tar.gz (where x.y.z is the version of nano)
+@item cd nano-x.y.z/
+@item ./configure
+@item make
+@item make install
+@end itemize
+
+if you are looking to optimize @code{nano} for size, you may want to
+consider the following command line options:
+
+
+@table @code
+
+@item --disable-tabcomp
+Disable the tab completion code when reading or writing files.
+
+@item --disable-justify
+Disable the justify (^J)/unjustify (^U) functions in the editor.
+
+@item --disable-speller
+Disable spell checker ability.
+
+@item --disable-help
+Disable the help function (^G). Disabling this option makes the binary
+much smaller, but makes it difficult for new users to learn more than
+very basic things about using the editor.
+
+@item --disable-browser
+Disable the mini file browser (^T) when reading or writing files.
+
+@item --disable-mouse
+Disable all mouse functionality. This also disables the -m command line
+flag, which enables the mouse functions.
+
+@item --disable-operatingdir
+Disable setting the operating directory. This also disables the -o
+command line flag.
+
+@item --enable-tiny
+This option disables all the above. It also disables some of the larger
+internals of the editor, like the marker code (^^) and the cut to line
+(-k) option, which depends on the marker code to work properly. It also
+disables the function toggles.
+
+@item --disable-wrapping
+Disable all word wrapping in the editor. This also eliminates the -w
+command line flag, as nonwrapping is then the default behavior.
+
+@item --disable-nls
+Disables Native Language support. This will make the available GNU
+@code{nano} translations unusable.
+
+@item --with-slang
+Compiling GNU @code{nano} with Slang is supported, and will make the
+binary notably smaller than if compiled with ncurses or other curses
+libraries.
+
+@end table
+
+@contents
+@bye
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
- <title>The GNU nano editor FAQ</title>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <meta name="GENERATOR" content="Mozilla/4.73 [en] (X11; U; Linux 2.2.16 i586) [Netscape]">
-</head>
-<body text="#330000" bgcolor="#ffffff" link="#0000ef" vlink="#51188e" alink="#ff0000">
-<h1>The nano FAQ</h1>
-<h2>Table of Contents</h2>
-<h2><a href="#1">1. General</a></h2>
-<blockquote><p><a href="#1.1">1.1 About this FAQ</a><br>
- <a href="#1.2">1.2. How do I contribute to it?</a><br>
- <a href="#1.3">1.3. What is GNU nano?</a><br>
- <a href="#1.4">1.4. What is the history behind nano?</a><br>
- <a href="#1.5">1.5. Why the name change from TIP?</a><br>
- <a href="#1.6">1.6. What is the current version of nano?</a><br>
- <a href="#1.7">1.7. I want to read the manpage without having to download the program!</a></p></blockquote>
-<h2><a href="#2">2. Where to get GNU nano.</a></h2>
-<blockquote><p><a href="#2.1">2.1. FTP and WWW sites that carry nano.</a><br>
- <a href="#2.2">2.2. RedHat and derivatives (.rpm) packages.</a><br>
- <a href="#2.3">2.3. Debian (.deb) packages.</a><br>
- <a href="#2.4">2.4. By CVS (for the brave).</a></p></blockquote>
-<h2><a href="#3">3. Installation and Configuration</a></h2>
-<blockquote><p><a href="#3.1">3.1. How do I install the RPM or DEB package?</a><br>
- <a href="#3.2">3.2. Compiling from source: WHAT THE HECK DO I DO NOW?</a><br>
- <a href="#3.3">3.3. Why does everything go into /usr/local?</a><br>
- <a href="#3.4">3.4. I get errors about 'bindtextdomain','gettext' and/or 'gettextdomain'. What can I do about it?</a><br>
- <a href="#3.5">3.5. Nano should automatically run strip on the binary when installing it!</a><br>
- <a href="#3.6">3.6. How can I make the executable smaller? This is too bloated!</a><br>
- <a href="#3.7">3.7. Tell me more about this multibuffer stuff!</a><br>
- <a href="#3.8">3.8. How do I make a .nanorc file that nano will read when I start it?</a></p></blockquote>
-<h2><a href="#4">4. Running</a></h2>
-<blockquote><p><a href="#4.1">4.1. Ack! My backspace/delete/enter/double bucky/meta key doesn't seem to work! What can I do?</a><br>
- <a href="#4.2">4.2. Nano crashes when I type <insert keystroke here>!</a><br>
- <a href="#4.3">4.3. Nano crashes when I resize my window. How can I fix that?</a><br>
- <a href="#4.4">4.4. [version 1.1.12 and earlier] Why does nano show ^\ in the shortcut list instead of ^J?</a><br>
- <a href="#4.5a">4.5a. [version 1.1.12 and earlier] When I type in a search string, the string I last searched for is already in front of my cursor! What happened?!</a><br>
- <a href="#4.5b">4.5b. [version 1.2.2 and later] Hey, the search string behavior has reverted, it's now like Pico, what happened to the consistency?</a><br>
- <a href="#4.6">4.6. I get the message "NumLock glitch detected. Keypad will malfunction with NumLock off." What gives?</a><br>
- <a href="#4.7">4.7. How do I make nano my default editor (in Pine, mutt, etc.)?</a><br>
- <a href="#4.8">4.8. I've compiled nano with color support, but I don't see any color when I run it!</a></p></blockquote>
-<h2><a href="#5">5. Internationalization</a></h2>
-<blockquote><p><a href="#5.1">5.1. There's no translation for my language!</a><br>
- <a href="#5.2">5.2. I don't like the translation for <x> in my language. How can I fix it?</a></p></blockquote>
-<h2><a href="#6">6. Advocacy and Licensing</a></h2>
-<blockquote><p><a href="#6.1">6.1. Why should I use nano instead of Pico?</a><br>
- <a href="#6.2">6.2. Why should I use Pico instead of nano?</a><br>
- <a href="#6.3">6.3. What is so bad about the Pine license?</a><br>
- <a href="#6.4">6.4. Okay, well what mail program should I use then?</a><br>
- <a href="#6.5">6.5. Why doesn't UW simply change their license?</a><br>
- <a href="#6.6">6.6. What if tomorrow UW changes the license to be truly Free Software?</a></p></blockquote>
-<h2><a href="#7">7. Miscellaneous</a></h2>
-<blockquote><p><a href="#7.1">7.1. Nano related mailing lists.</a><br>
- <a href="#7.2">7.2. I want to send the development team a big load of cash (or just a thank you).</a><br>
- <a href="#7.3">7.3. How do I submit a patch?</a><br>
- <a href="#7.4">7.4. How do I join the development team?</a><br>
- <a href="#7.5">7.5. Can I have CVS write access?</a></p></blockquote>
-<h2><a href="#8">8. ChangeLog</a></h2>
-<hr width="100%">
-<h1><a name="1"></a>1. General</h1>
-<h2><a name="1.1"></a>1.1 About this FAQ</h2>
-<blockquote><p>This FAQ was written and is maintained by Chris Allegretta <<a href="mailto:chrisa@asty.org">chrisa@asty.org</a>>, who also happens to be the creator of nano. Maybe someone else will volunteer to maintain this FAQ someday, who knows...</p></blockquote>
-<h2><a name="1.2"></a>1.2. How do I contribute to it?</h2>
-<blockquote><p>Your best bet is to send it to the nano email address, <a href="mailto:nano@nano-editor.org">nano@nano-editor.org</a> and if it is useful enough it will be included in future versions.</p></blockquote>
-<h2><a name="1.3"></a>1.3. What is GNU nano?</h2>
-<blockquote><p>GNU nano is designed to be a free replacement for the Pico text editor, part of the Pine email suite from <a href="http://www.washington.edu/pine/">The University of Washington</a>. It aims to "emulate Pico as closely as possible and perhaps include extra functionality".</p></blockquote>
-<h2><a name="1.4"></a>1.4. What is the history behind nano?</h2>
-<blockquote><p>Funny you should ask!</p>
- <p><b>In the beginning...</b></p>
- <p>For years Pine was THE program used to read email on a Unix system. The Pico text editor is the portion of the program one would use to compose his or her mail messages. Many beginners to Unix flocked to Pico and Pine because of their well organized, easy to use interfaces. With the proliferation of GNU/Linux in the mid to late 90's, many University students became intimately familiar with the strengths (and weaknesses) of Pine and Pico.</p>
- <p><b>Then came Debian...</b></p>
- <p>The <a href="http://www.debian.org/">Debian GNU/Linux</a> distribution, known for its strict standards in distributing truly "free" software (i.e. software with no restrictions on redistribution), would not include a binary package for Pine or Pico. Many people had a serious dilemma: they loved these programs, but they were not truly free software in the <a href="http://www.gnu.org/philosophy/free-sw.html">GNU</a> sense of the word.</p>
- <p><b>The event...</b></p>
- <p>It was in late 1999 when Chris Allegretta (our hero) was yet again complaining to himself about the less-than-perfect license Pico was distributed under, the 1000 makefiles that came with it and how just a few small improvements could make it the Best Editor in the World (TM). Having been a convert from Slackware to Debian, he missed having a simple binary package that included Pine and Pico, and had grown tired of downloading them himself.</p>
- <p>Finally something snapped inside and Chris coded and hacked like a madman for many hours straight one weekend to make a (barely usable) Pico clone, at the time called TIP (Tip Isn't Pico). The program could not be invoked without a filename, could not save files, had no help menu, spell checker, and so forth. But over time it improved, and with the help of a few great coders it matured to the (hopefully) stable state it is today.</p>
- <p>In February 2001, nano was declared an official GNU program by Richard Stallman. Nano also reached its first production release on March 22, 2001.</p></blockquote>
-<h2><a name="1.5"></a>1.5. Why the name change from TIP?</h2>
-<blockquote><p>On January 10, 2000, TIP was officially renamed to nano because of a namespace conflict with another program called 'tip'. The original 'tip' program "establishes a full duplex terminal connection to a remote host", and was included with many older Unix systems (and newer ones like Solaris). The conflict was not noticed at first because there is no 'tip' utility included with most GNU/Linux distributions (where nano was developed).</p></blockquote>
-<h2><a name="1.6"></a>1.6. What is the current version of nano?</h2>
-<blockquote><p>The current version of nano *should* be 1.2.2. Of course you should always check the nano homepage to see what the latest and greatest version is.</p></blockquote>
-<h2><a name="1.7"></a>1.7. I want to read the man page without having to download the program!</h2>
-<blockquote><p>Jeez, demanding, aren't we? Okay, look <a href="http://www.nano-editor.org/dist/v1.2/nano.1.html">here</a>.</p></blockquote>
-<hr width="100%">
-<h1><a name="2"></a>2. Where to get GNU nano.</h1>
-<h2><a name="2.1"></a>2.1. FTP and WWW sites that carry nano.</h2>
-<blockquote><p>The nano distribution can be downloaded at the following fine web and ftp sites:</p>
- <ul>
- <li><a href="http://www.nano-editor.org/dist/">http://www.nano-editor.org/dist/</a></li>
- <li><a href="http://www.ewtoo.org/~astyanax/nano/dist/">http://www.ewtoo.org/~astyanax/nano/dist/</a></li>
- <li><a href="ftp://ftp.gnu.org/pub/gnu/nano/">ftp://ftp.gnu.org/pub/gnu/nano/</a></li>
- </ul>
-</blockquote>
-<h2><a name="2.2"></a>2.2. RedHat and derivatives (.rpm) packages.</h2>
-<blockquote>
- <ul>
- <li><a href="http://www.nano-editor.org/dist/v1.2/RPMS/">http://www.nano-editor.org/dist/v1.2/RPMS/</a></li>
- <li><a href="http://www.ewtoo.org/~astyanax/nano/dist/v1.2/RPMS/">http://www.ewtoo.org/~astyanax/nano/dist/v1.2/RPMS/</a></li>
- </ul>
- <p>Additionally, check out the RedHat contribs section at:</p>
- <ul>
- <li><a href="http://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/">http://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/</a></li>
- <li><a href="ftp://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/">ftp://distro.ibiblio.org/pub/Linux/distributions/redhat/contrib/libc6/i386/</a></li>
- </ul>
-</blockquote>
-<h2><a name="2.3"></a>2.3. Debian (.deb) packages.</h2>
-<blockquote><p>Debian users can check out the current nano packages for:</p>
- <ul>
- <li><a href="http://packages.debian.org/stable/editors/nano.html">stable</a></li>
- <li><a href="http://packages.debian.org/testing/editors/nano.html">testing</a></li>
- <li><a href="http://packages.debian.org/unstable/editors/nano.html">unstable</a></li>
- </ul>
- <p>You can also have a look at the <a href="ftp://ftp.debian.org/debian/pool/main/n/nano/">Package Pool</a> to see all the available binary and source packages.</p>
- <p>Note that versions < 0.9.10 are probably not for those wanting to get serious work done, so if you are using Debian 2.2, check that you have updated to 2.2r3, which comes with nano 0.9.23. If you're tracking unstable, you probably have the newest version already.</p></blockquote>
-<h2><a name="2.4"></a>2.4. By CVS (for the brave).</h2>
-<blockquote><p>For the 'bleeding edge' current version of nano, you can use CVS to download the current source code. <b>Note:</b> believe it or not, by downloading code that has not yet stabilized into an official release, there could quite possibly be bugs, in fact the code may not even compile! Anyway, see <a href="http://savannah.gnu.org/cvs/?group_id=1025">the nano CVS page</a> for info on anonymous CVS access to the nano source.</p></blockquote>
-<hr width="100%">
-<h1><a name="3"></a>3. Installation and Configuration</h1>
-<h2><a name="3.1"></a>3.1. How do install the RPM or DEB package?</h2>
-<blockquote><p>It's simple really! As root, type <b>rpm -Uvh nano-x.y.z-1.i386.rpm</b> if you have a RedHat-ish system or <b>dpkg -i nano_x.y.z-1.deb</b> if you have a Debian-ish system, where <b>x.y.z</b> is the release of nano. There are other programs to install packages, and if you wish to use those, knock yourself out.</p></blockquote>
-<h2><a name="3.2"></a>3.2. Compiling from source: WHAT THE HECK DO I DO NOW?</h2>
-<blockquote><p>Okay, take a deep breath, this really isn't hard. Unpack the nano source with a command like:</p>
- <p><b>tar -zxvf nano-x.y.z.tar.gz</b></p>
- <p>If you get error messages about the -z option, try this:</p>
- <p><b>gzip -dc nano-x.y.z.tar.gz | tar xvf -</b></p>
- <p>(again, where x.y.z is the version number in question). Then you need to run configure with any options you might want (if any).</p>
- <p>The average case is this:</p>
- <p><b>cd nano-x.y.z/</b><br>
- <b>./configure</b><br>
- <b>make</b><br>
- <b>make install</b> (as root, of course)</p></blockquote>
-<h2><a name="3.3"></a>3.3. Why does everything go into /usr/local?</h2>
-<blockquote><p>Well, that's what the <b>configure</b> script defaults to. If you wish to change this, simply do this:</p>
- <p><b>./configure --prefix=/usr</b></p>
- <p>to put nano into /usr/bin when you run <b>make install</b>.</p></blockquote>
-<h2><a name="3.4"></a>3.4. I get errors about 'bindtextdomain', 'gettext' and/or 'gettextdomain'. What can I do about it?</h2>
-<blockquote><p>Try doing a <b>./configure --with-included-gettext</b> and see if that solves your problem. You may need to do a <b>make clean; make</b> to get it to work fully.</p></blockquote>
-<h2><a name="3.5"></a>3.5. Nano should automatically run strip on the binary when installing it!</h2>
-<blockquote><p>Actually, it does, but you have to use <b>make install-strip</b>. The default make install does not, and will not, run strip automatically.</p></blockquote>
-<h2><a name="3.6"></a>3.6. How can I make the executable smaller? This is too bloated!</h2>
-<blockquote><p>Actually, there are several parts of the editor that can be disabled. You can pass arguments to the <b>configure</b> script that disable certain features. Here's a brief list:</p>
- <pre>
- <b>--disable-tabcomp</b> Disables tab completion code for a smaller binary
- <b>--disable-justify</b> Disable justify/unjustify function
- <b>--disable-speller</b> Disables spell checker function
- <b>--disable-help</b> Disables help function (^G)
- <b>--disable-browser</b> Disables mini file browser
- <b>--disable-wrapping</b> Disables all wrapping of text (and -w flag)
- <b>--disable-mouse</b> Disables mouse support (and -m flag)
- <b>--disable-operatingdir</b> Disable setting of operating directory</pre>
- <p>There's also the <b>--enable-tiny</b> option which disables everything above, as well as some larger chunks of the program (like the marker code that you use Control-^ to select with). Also, if you know you aren't going to be using other languages you can use <b>--disable-nls</b> to disable internationalization and save a few K to a few dozen K depending on if you have locale support on your system. And finally there's always good old <b>strip</b> to strip all debugging code and code that exists in libraries on your system.</p>
- <p>If, on the other hand, you can't live without bells and whistles, you could try:</p>
- <pre>
- <b>--enable-extra</b> Enable extra functions, including easter eggs
- <b>--enable-nanorc</b> Enable use of .nanorc file
- <b>--enable-color</b> Enables color and syntax highlighting
- <b>--enable-multibuffer</b> Enables having multiple file buffers open
- <b>--enable-all</b> Enables all of the above features</pre></blockquote>
-<h2><a name="3.7"></a>3.7. Tell me more about this multibuffer stuff!</h2>
-<blockquote><p>To use multiple file buffers, you must be using nano 1.1.12 or newer, and you must have configured nano with <b>--enable-multibuffer</b> or <b>--enable-extra</b> (use nano -V to check). Then when you want to enable inserting a file into its own buffer instead of into the current file, just hit <b>Meta-F</b>, then insert the file as normal with <b>^R</b>. If you always want files to be loaded into their own buffers, use the <b>--multibuffer</b> or <b>-F</b> flag when you invoke nano. </p>
- <p>You can move between the buffers you have open with the <b>Meta-<</b> and <b>Meta-></b> keys, or more easily with <b>Meta-,</b> and <b>Meta-.</b> (clear as mud, right? =-). When you have more than one file buffer open, the ^X shortcut will say "Close", instead of the normal "Exit" when only one buffer is open.</p></blockquote>
-<h2><a name="3.8"></a>3.8. How do I make a .nanorc file that nano will read when I start it?</h2>
-<blockquote>It's not hard at all! But, your version of nano must have been compiled with <b>--enable-nanorc</b>, and again must be version 1.1.12 or newer (use nano -V to check your version and compiled features). Then simply copy the <b>nanorc.sample</b> that came with the nano source or your nano package (most likely in /usr/doc/nano) to .nanorc in your home directory. If you didn't get one, the syntax is simple. Flags are turned on and off by using the word <b>set</b> and the getopt_long flag for the feature, for example "set pico" or "set nowrap".</blockquote>
-<hr width="100%">
-<h1><a name="4"></a>4. Running</h1>
-<h2><a name="4.1"></a>4.1. Ack! My backspace/delete/enter/double bucky/meta key doesn't seem to work! What can I do?</h2>
-<blockquote><p>Try setting your $TERM variable to 'vt100'. Nano doesn't yet support every term entry under the sun.</p>
- <p>Bourne shell users (like bash): <b>export TERM=vt100</b><br>
- C Shell users (tcsh and csh): <b>setenv TERM vt100</b></p></blockquote>
-<h2><a name="4.2"></a>4.2. Nano crashes when I type <insert keystroke here>!</h2>
-<blockquote><p>If you aren't trying some bizarre keystroke combination with some bizarre $TERM entry, chances are you have found a bug. You are welcome to submit it to the <a href="mailto:nano-devel@gnu.org">nano-devel</a> list or to <a href="mailto:nano@nano-editor.org">nano@nano-editor.org</a>.</p></blockquote>
-<h2><a name="4.3"></a>4.3. Nano crashes when I resize my window. How can I fix that?</h2>
-<blockquote><p>Older versions of nano had this problem, please upgrade to a newer version (at least 0.9.9 would be great, 0.9.12 is recommended).</p></blockquote>
-<h2><a name="4.4"></a>4.4. [version 1.1.12 and earlier] Why does nano show ^\ in the shortcut list instead of ^J?</h2>
-<blockquote><p>The help (^G) and justify (^J) function were among the last to be written. To show the improvements that nano had over Pico (go to line # and replace), ^_ and ^\ were put on the shortcut list. Later, ^G came back in place of ^_ as it proved to be very valuable for new Unix users. If you use the <b>-p</b> option to nano (or hit Meta-P) you will get the same shortcuts at the bottom as Pico.</p></blockquote>
-<a name="4.5"></a>
-<h2><a name="4.5a"></a>4.5a. [version 1.1.12 and earlier] When I type in a search string, the string I last searched for is already in front of my cursor! What happened?!</h2>
-<blockquote><p>In nano version 0.9.20, the default is to have a completely consistent user interface across all user input functions. This means that regardless of whether you're being asked for a filename to insert or write, or a string to search for, the previous value is already inserted before the cursor. If you prefer the old behavior, use the Pico emulation mode (-p or --pico) or just hit Meta-P while in nano (see the ^G help text for more details).</p></blockquote>
-<h2><a name="4.5b"></a>4.5b. [version 1.2.2 and later] Hey, the search string behavior has reverted, it's now like Pico, what happened to the consistency?</h2>
-<blockquote><p>It was decided that consistency was nice, but people are used to Pico's inconsistent behavior. Also, in version 1.1.99pre1, search and replace history was introduced. If you wish to edit your previous search/replace entry (or any previous entry), you can do so by hitting the up arrow to cycle through your history. This method allows the best of both worlds: You don't need to erase the previous string if you want to enter a new one, but you can with one keystroke recall previous entries for editing. Therefore there is now no "Pico mode", nano is and has always been a Pico <b>clone</b>, and clones by default should be compatible.</p></blockquote>
-<h2><a name="4.6"></a>4.6. I get the message "NumLock glitch detected. Keypad will malfunction with NumLock off." What gives?</h2>
-<blockquote><p>Nano (and actually almost all console editors) has issues when cycling the NumLock key in certain X terminals (rxvt, aterm, wterm, etc...). When you switch NumLock from on to off, you put the terminal into an "application mode" that changes what sequences are sent by the keypad. These sequences vary sufficiently from terminal to terminal that it is nearly impossible to work around them from within nano.</p>
- <p>In a nutshell, if you want to be able to use the keypad with the arrow and page up/down functionality, you have to exit nano and reset your terminal (presumably with "reset" or "stty sane" or similar) and then run nano again with NumLock off. If you know an easier way to restore "normal mode", please mail <a href="mailto:nano@nano-editor.org">nano@nano-editor.org</a>.</p></blockquote>
-<h2><a name="4.7"></a>4.7. How do I make nano my default editor (in Pine, mutt, etc.)?</h2>
-<blockquote><p>You need to make nano your $EDITOR. If you want this to be saved, you should put a line like this in your <b>.bashrc</b> if you use bash (or <b>.zshrc</b> if you believe in zsh):</p>
- <p><b>export EDITOR=/usr/local/bin/nano</b></p>
- <p>or if you use tcsh put this in your <b>.cshrc</b> file:</p>
- <p><b>setenv EDITOR /usr/local/bin/nano</b></p>
- <p>Change /usr/local/bin/nano to wherever nano is installed on your system. Type "which nano" to find out. This will not take effect until the next time you login. So log out and back in again.</p>
- <p>Then on top that if you use Pine you must go into setup (type <b>S</b> at the main menu), then configure (type <b>C</b>). Hit enter on the lines that say:</p>
- <p><b>[ ] enable-alternate-editor-cmd</b><br>
- <b>[ ] enable-alternate-editor-implicitly</b></p>
- <p>Then exit (<b>E</b>) and select Yes (<b>Y</b>).</p>
- <p>Mutt users should see an effect immediately the next time you log in, no further configuration is needed. However, if you want to let people know you use nano to compose your email messages, you can put a line like this in your <b>.muttrc</b>:</p>
- <p><b>my_hdr X-Composer: nano x.y.z</b></p>
- <p>Again, replace x.y.z with the version of nano you use.</p></blockquote>
-<h2><a name="4.8"></a>4.8. I've compiled nano with color support, but I don't see any color when I run it!</h2>
-<blockquote><p>If you want nano to actually use color, you have to specify the color configurations you want it to use in your .nanorc. Some example configurations are in the <b>nanorc.sample</b> that comes with the nano source or your nano package. See Section <a href="#3.8">3.8</a>.</p></blockquote>
-<hr width="100%">
-<h1><a name="5"></a>5. Internationalization</h1>
-<h2><a name="5.1"></a>5.1. There's no translation for my language!</h2>
-<blockquote><p>On June of 2001, GNU nano entered the <a href="http://www.iro.umontreal.ca/contrib/po/HTML/">Free Translation Project</a> and since then, translations should be managed from there.</p>
- <p>If there isn't a translation for your language, you could ask <a href="http://www.iro.umontreal.ca/contrib/po/HTML/teams.html">your language team</a> to translate nano, or better still, join your team and do it yourself. Joining a team is easy. You just need to ask the <a href="mailto:translation@iro.umontreal.ca">TP coordinator</a> to add you to your team, and send a <a href="http://www.iro.umontreal.ca/contrib/po/HTML/disclaim.html">translation disclaimer to the FSF</a> (this is necessary as nano is an official GNU package, but it does <b>not</b> mean that you transfer the rights of your work to the FSF, it's just so the FSF can legally manage them).</p>
- <p>In any case, translating nano is very easy. Just grab the <b>nano.pot</b> file from the latest and greatest nano distribution (it's in the <b>po/</b> directory) and translate each line into your native language on the <b>msgstr</b> line. When you're done, you should send it to the TP's central po repository.</p></blockquote>
-<h2><a name="5.2"></a>5.2. I don't like the translation for <x> in my language. How can I fix it?</h2>
-<blockquote><p>The best way would probably be to e-mail the person listed in the <code>Last-Translator:</code> field in the <b><your_language>.po</b> file with your suggested corrections and they can make the changes reach the nano-devel list.</p></blockquote>
-<hr width="100%">
-<h1><a name="6"></a>6. Advocacy and Licensing</h1>
-<h2><a name="6.1"></a>6.1. Why should I use nano instead of Pico?</h2>
-<blockquote><p>There are many reasons to use nano instead of Pico, a more complete list can be found at the <a href="http://www.nano-editor.org/">nano homepage</a>.</p></blockquote>
-<h2><a name="6.2"></a>6.2. Why should I use Pico instead of nano?</h2>
-<blockquote><p>Again, check out the <a href="http://www.nano-editor.org/">nano homepage</a> for a good summary of reasons. It really is a matter of personal preference as to which editor you should use. If you're the type of person who likes using the original version of a program, then Pico is the editor for you. If you're looking for a few more features and a 'better' license as far as adding your own changes (sacrificing mailer integration with Pine), nano is the way to go.</p></blockquote>
-<h2><a name="6.3"></a>6.3. What is so bad about the Pine license?</h2>
-<blockquote><p>The U of W license for Pine and Pico is not considered truly Free Software according to both the Free Software Foundation and the <a href="http://www.debian.org/social_contract#guidelines">Debian Free Software Guidelines</a>. The main problem regards the limitations on distributing derived works: according to UW, you can distribute their software, and you can modify it, but you can not do both, i.e. distribute modified binaries.</p></blockquote>
-<h2><a name="6.4"></a>6.4. Okay, well what mail program should I use then?</h2>
-<blockquote><p>If you are looking to use a Free Software program similar to Pine and emacs is not your thing, you should definitely take a look at <a href="http://www.mutt.org/">mutt</a>. It is a full-screen, console based mail program that actually has a lot more flexibility than Pine, but has a keymap included in the distribution that allows you to use the same keystrokes as Pine would to send and receive mail. It's also licensed under the GPL.</p></blockquote>
-<h2><a name="6.5"></a>6.5. Why doesn't UW simply change their license?</h2>
-<blockquote><p>You're really not asking the right person here. I (Chris) waited a long time to see if UW would change their license because of the amount of high quality software being released and developed under the GPL without being taken advantage of by malicious corporate entities or other baddies, but no such luck so far.</p></blockquote>
-<h2><a name="6.6"></a>6.6. What if tomorrow UW changes the license to be truly Free Software?</h2>
-<blockquote><p>Honestly nothing would make me happier than to see that happen. Nano would continue to be developed independently until such time as Pico had all the features nano did or the projects merged. That just does not seem very likely given that there has been no sign of any changes in the past few years in a positive direction.</p></blockquote>
-<hr width="100%">
-<h1><a name="7"></a>7. Miscellaneous</h1>
-<h2><a name="7.1"></a>7.1. Nano related mailing lists.</h2>
-<blockquote><p>There are three mailing lists for nano hosted at <a href="http://savannah.gnu.org/">Savannah</a>, info-nano, help-nano and nano-devel. Info-nano is a very low traffic list where new versions of nano are announced (surprise!) Help-nano is for getting help with the editor without needing to hear all of the development issues surrounding it. Nano-devel is a normally low, sometimes high traffic list for discussing the present and future development of nano. Here are links to where you can sign up for a given list:</p>
- <p>info-nano - <a href="http://mail.gnu.org/mailman/listinfo/info-nano/">http://mail.gnu.org/mailman/listinfo/info-nano/</a><br>
- help-nano - <a href="http://mail.gnu.org/mailman/listinfo/help-nano/">http://mail.gnu.org/mailman/listinfo/help-nano/</a><br>
- nano-devel - <a href="http://mail.gnu.org/mailman/listinfo/nano-devel/">http://mail.gnu.org/mailman/listinfo/nano-devel/</a></p></blockquote>
-<h2><a name="7.2"></a>7.2. I want to send the development team a big load of cash (or just a thank you).</h2>
-<blockquote><p>That's fine. Send it <a href="mailto:nano-devel@gnu.org">our way</a>! Better yet, fix a <a href="http://www.nano-editor.org/dist/v1.2/BUGS">bug</a> in the program or implement a <a href="http://www.nano-editor.org/dist/v1.2/TODO">cool feature</a> and send us that instead (though cash is fine too).</p></blockquote>
-<h2><a name="7.3"></a>7.3. How do I submit a patch?</h2>
-<blockquote><p>See Section <a href="#7.2">7.2</a>.</p></blockquote>
-<h2><a name="7.4"></a>7.4. How do I join the development team?</h2>
-<blockquote><p>The easiest way is to consistently send in good patches that add some needed functionality, fix a bug or two and/or make the program more optimized/efficient. Then ask nicely and you will probably be added to the Savannah development list and be given CVS write access after awhile. There is a lot of responsibility that goes along with being a team member, so don't think it's just something to add to your resume.</p></blockquote>
-<h2><a name="7.5"></a>7.5. Can I have CVS write access?</h2>
-<blockquote><p>Re-read Section <a href="#7.4">7.4</a> and you should know the answer.</p></blockquote>
-<h2><a name="8"></a>8. ChangeLog</h2>
-<blockquote>
-<p>
-2003/07/02 - Added question about nano's not showing color when it's compiled with color support (DLR; suggested by Jordi).<br>
-2003/02/23 - Updated RPM links for nano 1.2.x. (DLR).<br>
-2003/01/16 - Split section 4.5 into 4.5a and 4.5b for search string behavior. Added --enable-all docs.<br>
-2002/12/28 - More misc. fixes (David Benbennick, DLR).<br>
-2002/10/25 - Misc. fixes and link updates (DLR).<br>
-2002/09/10 - Another typo fix (DLR).<br>
-2002/05/15 - Typo fix (DLR).<br>
-2001/12/26 - Misc. fixes (Aaron S. Hawley, DLR).<br>
-2001/10/02 - Update for Free Translation Project.<br>
-2001/10/02 - Assorted fixes, Debian additions.<br>
-2001/06/30 - Silly typo fix.<br>
-2001/05/05 - Spelling fixes by David Lawrence Ramsey.<br>
-2001/05/02 - Misc fixes.<br>
-2001/03/26 - Typo fix in an URL.<br>
-2001/02/17 - Advocacy updates.<br>
-2001/02/15 - Added GNU notes for 0.9.99pre3.<br>
-2001/02/06 - Typo fixes.<br>
-2001/01/14 - Added note about NumLock glitch.<br>
-2001/01/10 - Linux -> GNU/Linux.<br>
-2001/01/09 - Added "making exe smaller" section.<br>
-2000/12/19 - Typo and assorted error fixes.<br>
-2000/11/28 - Added blurb about make install-strip.<br>
-2000/11/19 - Changed Debian frozen to stable.<br>
-2000/11/18 - Previous string display (4.5).<br>
-2000/09/27 - Moved addresses to nano-editor.org.<br>
-2000/06/31 - Initial framework.</p></blockquote>
-<p>$Id$</p>
-</body>
-</html>
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * files.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <utime.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <ctype.h>
-#include <dirent.h>
-#include <pwd.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-/* Set a default value for PATH_MAX, so we can use it below in lines like
- path = getcwd(NULL, PATH_MAX + 1); */
-#ifndef PATH_MAX
-#define PATH_MAX -1
-#endif
-
-#ifndef NANO_SMALL
-static int fileformat = 0; /* 0 = *nix, 1 = DOS, 2 = Mac */
-#endif
-
-/* Load file into edit buffer - takes data from file struct. */
-void load_file(int update)
-{
- current = fileage;
-
-#ifdef ENABLE_MULTIBUFFER
- /* if update is zero, add a new entry to the open_files structure;
- otherwise, update the current entry (the latter is needed in the
- case of the alternate spell checker) */
- add_open_file(update);
-#endif
-
-#ifdef ENABLE_COLOR
- update_color();
- edit_refresh();
-#endif
-}
-
-/* What happens when there is no file to open? aiee! */
-void new_file(void)
-{
- fileage = make_new_node(NULL);
- fileage->data = charalloc(1);
- fileage->data[0] = '\0';
- filebot = fileage;
- edittop = fileage;
- editbot = fileage;
- current = fileage;
- current_x = 0;
- totlines = 1;
- totsize = 0;
-
-#ifdef ENABLE_MULTIBUFFER
- /* if there aren't any entries in open_files, create the entry for
- this new file; without this, if nano is started without a filename
- on the command line, a new file will be created, but it will be
- given no open_files entry */
- if (open_files == NULL) {
- add_open_file(0);
- /* turn off view mode in this case; this is for consistency
- whether multibuffers are compiled in or not */
- UNSET(VIEW_MODE);
- }
-#else
- /* if multibuffers haven't been compiled in, turn off view mode
- unconditionally; otherwise, don't turn them off (except in the
- above case), so that we can view multiple files properly */
- UNSET(VIEW_MODE);
-#endif
-
-#ifdef ENABLE_COLOR
- update_color();
- edit_refresh();
-#endif
-}
-
-filestruct *read_line(char *buf, filestruct *prev, int *line1ins, int len)
-{
- filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct));
-
- /* nulls to newlines; len is the string's real length here */
- unsunder(buf, len);
-
- assert(strlen(buf) == len);
-
- fileptr->data = mallocstrcpy(NULL, buf);
-
-#ifndef NANO_SMALL
- /* If it's a DOS file (CRLF), and file conversion isn't disabled,
- strip out the CR part */
- if (!ISSET(NO_CONVERT) && len > 0 && buf[len - 1] == '\r') {
- fileptr->data[len - 1] = '\0';
- totsize--;
-
- if (fileformat == 0)
- fileformat = 1;
- }
-#endif
-
- if (*line1ins != 0 || fileage == NULL) {
- /* Special case, insert with cursor on 1st line. */
- fileptr->prev = NULL;
- fileptr->next = fileage;
- fileptr->lineno = 1;
- if (*line1ins != 0) {
- *line1ins = 0;
- /* If we're inserting into the first line of the file, then
- we want to make sure that our edit buffer stays on the
- first line (and that fileage stays up to date!) */
- edittop = fileptr;
- } else
- filebot = fileptr;
- fileage = fileptr;
- } else {
- assert(prev != NULL);
- fileptr->prev = prev;
- fileptr->next = NULL;
- fileptr->lineno = prev->lineno + 1;
- prev->next = fileptr;
- }
-
- return fileptr;
-}
-
-int read_file(FILE *f, const char *filename, int quiet)
-{
- int num_lines = 0, len = 0;
- char input = '\0'; /* current input character */
- char *buf;
- long i = 0, bufx = 128;
- filestruct *fileptr = current, *tmp = NULL;
-#ifndef NANO_SMALL
- int old_no_convert = ISSET(NO_CONVERT);
-#endif
- int line1ins = 0;
- int input_int;
-
- buf = charalloc(bufx);
- buf[0] = '\0';
-
- if (current != NULL) {
- if (current == fileage)
- line1ins = 1;
- else
- fileptr = current->prev;
- tmp = fileptr;
- }
-
- /* For the assertion in read_line(), it must be true that if current is
- * NULL then so is fileage. */
- assert(current != NULL || fileage == NULL);
-
- /* Read the entire file into file struct. */
- while ((input_int = getc(f)) != EOF) {
- input = (char)input_int;
-#ifndef NANO_SMALL
- /* If the file has binary chars in it, don't stupidly
- assume it's a DOS or Mac formatted file if it hasn't been
- detected as one already! */
- if (fileformat == 0 && !ISSET(NO_CONVERT)
- && is_cntrl_char((int)input) != 0 && input != '\t'
- && input != '\r' && input != '\n')
- SET(NO_CONVERT);
-#endif
-
- if (input == '\n') {
-
- /* read in the line properly */
- fileptr = read_line(buf, fileptr, &line1ins, len);
-
- /* reset the line length, in preparation for the next line */
- len = 0;
-
- num_lines++;
- buf[0] = '\0';
- i = 0;
-#ifndef NANO_SMALL
- /* If it's a Mac file (no LF just a CR), and file conversion
- isn't disabled, handle it! */
- } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') {
- fileformat = 2;
-
- /* read in the line properly */
- fileptr = read_line(buf, fileptr, &line1ins, len);
-
- /* reset the line length, in preparation for the next line;
- since we've already read in the next character, reset it
- to 1 instead of 0 */
- len = 1;
-
- num_lines++;
- totsize++;
- buf[0] = input;
- buf[1] = '\0';
- i = 1;
-#endif
- } else {
-
- /* Calculate the total length of the line; it might have
- nulls in it, so we can't just use strlen(). */
- len++;
-
- /* Now we allocate a bigger buffer 128 characters at a time.
- If we allocate a lot of space for one line, we may indeed
- have to use a buffer this big later on, so we don't
- decrease it at all. We do free it at the end, though. */
- if (i >= bufx - 1) {
- bufx += 128;
- buf = charealloc(buf, bufx);
- }
- buf[i] = input;
- buf[i + 1] = '\0';
- i++;
- }
- totsize++;
- }
-
- /* This conditional duplicates previous read_byte() behavior;
- perhaps this could use some better handling. */
- if (ferror(f))
- nperror(filename);
- fclose(f);
-
- /* Did we not get a newline but still have stuff to do? */
- if (len > 0) {
-#ifndef NANO_SMALL
- /* If file conversion isn't disabled, the last character in
- this file is a CR and fileformat isn't set yet, make sure
- it's set to Mac format */
- if (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' && fileformat == 0)
- fileformat = 2;
-#endif
-
- /* read in the LAST line properly */
- fileptr = read_line(buf, fileptr, &line1ins, len);
-
- num_lines++;
- totsize++;
- buf[0] = '\0';
- }
-#ifndef NANO_SMALL
- else if (!ISSET(NO_CONVERT) && input == '\r') {
- /* If file conversion isn't disabled and the last character in
- this file is a CR, read it in properly as a (Mac format)
- line */
- buf[0] = input;
- buf[1] = '\0';
- len = 1;
- fileptr = read_line(buf, fileptr, &line1ins, len);
- num_lines++;
- totsize++;
- buf[0] = '\0';
- }
-#endif
-
- free(buf);
-
-#ifndef NANO_SMALL
- /* If NO_CONVERT wasn't set before we read the file, but it is now,
- unset it again. */
- if (!old_no_convert && ISSET(NO_CONVERT))
- UNSET(NO_CONVERT);
-#endif
-
- /* Did we even GET a file if we don't already have one? */
- if (totsize == 0 || fileptr == NULL)
- new_file();
-
- /* Did we try to insert a file of 0 bytes? */
- if (num_lines != 0) {
- if (current != NULL) {
- fileptr->next = current;
- current->prev = fileptr;
- renumber(current);
- current_x = 0;
- placewewant = 0;
- } else if (fileptr->next == NULL) {
- filebot = fileptr;
- new_magicline();
- totsize--;
- load_file(quiet);
- }
- }
-
-#ifndef NANO_SMALL
- if (fileformat == 2)
- statusbar(P_("Read %d line (Converted from Mac format)",
- "Read %d lines (Converted from Mac format)",
- num_lines), num_lines);
- else if (fileformat == 1)
- statusbar(P_("Read %d line (Converted from DOS format)",
- "Read %d lines (Converted from DOS format)",
- num_lines), num_lines);
- else
-#endif
- statusbar(P_("Read %d line", "Read %d lines", num_lines),
- num_lines);
-
- totlines += num_lines;
-
- return 1;
-}
-
-/* Open the file (and decide if it exists). */
-int open_file(const char *filename, int insert, int quiet)
-{
- int fd;
- FILE *f;
- struct stat fileinfo;
-
- if (filename[0] == '\0' || stat(filename, &fileinfo) == -1) {
- if (insert && !quiet) {
- statusbar(_("\"%s\" not found"), filename);
- return -1;
- } else {
- /* We have a new file */
- statusbar(_("New File"));
- new_file();
- }
- } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
- S_ISBLK(fileinfo.st_mode)) {
- /* Don't open character or block files. Sorry, /dev/sndstat! */
- statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory") :
- _("File \"%s\" is a device file"), filename);
- if (!insert)
- new_file();
- return -1;
- } else if ((fd = open(filename, O_RDONLY)) == -1) {
- /* If we're in multibuffer mode, don't be quiet when an error
- occurs while opening a file */
- if (!quiet
-#ifdef ENABLE_MULTIBUFFER
- || ISSET(MULTIBUFFER)
-#endif
- )
- statusbar("%s: %s", strerror(errno), filename);
- if (!insert)
- new_file();
- return -1;
- } else { /* File is A-OK */
- if (!quiet)
- statusbar(_("Reading File"));
- f = fdopen(fd, "rb"); /* Binary for our own line-end munging */
- if (f == NULL) {
- nperror("fdopen");
- close(fd);
- return -1;
- }
- read_file(f, filename, quiet);
-#ifndef NANO_SMALL
- stat(filename, &originalfilestat);
-#endif
- }
-
- return 1;
-}
-
-/* This function will return the name of the first available extension
- * of a filename (starting with the filename, then filename.1, etc).
- * Memory is allocated for the return value. If no writable extension
- * exists, we return "". */
-char *get_next_filename(const char *name)
-{
- int i = 0;
- char *buf = NULL;
- struct stat fs;
-
- buf = charalloc(strlen(name) + num_of_digits(INT_MAX) + 2);
- strcpy(buf, name);
-
- while (1) {
-
- if (stat(buf, &fs) == -1)
- return buf;
- if (i == INT_MAX)
- break;
-
- i++;
- strcpy(buf, name);
- sprintf(&buf[strlen(name)], ".%d", i);
- }
-
- /* We get here only if there is no possible save file. */
- buf[0] = '\0';
-
- return buf;
-}
-
-int do_insertfile(int loading_file)
-{
- int i, old_current_x = current_x;
- char *realname = NULL;
- static char *inspath = NULL;
-
- if (inspath == NULL) {
- inspath = charalloc(1);
- inspath[0] = '\0';
- }
-
- wrap_reset();
-
-#if !defined(DISABLE_BROWSER) || !defined(NANO_SMALL) && defined(ENABLE_MULTIBUFFER)
- start_again: /* Goto here when the user cancels the file browser. */
-#endif
-
-#if !defined(DISABLE_BROWSER) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
- currshortcut = insertfile_list;
-#endif
-
-#ifndef DISABLE_OPERATINGDIR
- if (operating_dir != NULL && strcmp(operating_dir, ".") != 0)
-#ifdef ENABLE_MULTIBUFFER
- if (ISSET(MULTIBUFFER))
- i = statusq(1, insertfile_list, inspath,
-#ifndef NANO_SMALL
- 0,
-#endif
- _("File to insert into new buffer [from %s] "),
- operating_dir);
- else
-#endif
- i = statusq(1, insertfile_list, inspath,
-#ifndef NANO_SMALL
- 0,
-#endif
- _("File to insert [from %s] "),
- operating_dir);
-
- else
-#endif
-#ifdef ENABLE_MULTIBUFFER
- if (ISSET(MULTIBUFFER))
- i = statusq(1, insertfile_list, inspath,
-#ifndef NANO_SMALL
- 0,
-#endif
- _("File to insert into new buffer [from ./] "));
- else
-#endif /* ENABLE_MULTIBUFFER */
- i = statusq(1, insertfile_list, inspath,
-#ifndef NANO_SMALL
- 0,
-#endif
- _("File to insert [from ./] "));
-
- if (i != -1) {
- inspath = mallocstrcpy(inspath, answer);
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", answer);
-#endif
-
-#ifndef NANO_SMALL
-#ifdef ENABLE_MULTIBUFFER
- if (i == TOGGLE_LOAD_KEY) {
- /* Don't allow toggling if we're in view mode. */
- if (!ISSET(VIEW_MODE))
- TOGGLE(MULTIBUFFER);
- loading_file = ISSET(MULTIBUFFER);
- goto start_again;
- }
-#endif /* ENABLE_MULTIBUFFER */
-
- if (i == NANO_EXTCMD_KEY) {
- int ts = statusq(TRUE, extcmd_list, "", NULL,
- _("Command to execute"));
- if (ts == -1 || answer == NULL || answer[0] == '\0') {
- statusbar(_("Cancelled"));
- display_main_list();
- return 0;
- }
- }
-#endif /* !NANO_SMALL */
-#ifndef DISABLE_BROWSER
- if (i == NANO_TOFILES_KEY) {
- char *tmp = do_browse_from(answer);
-
- if (tmp != NULL) {
- free(answer);
- answer = tmp;
- resetstatuspos = 1;
- } else
- goto start_again;
- }
-#endif
-
-#ifndef DISABLE_OPERATINGDIR
- if (i != NANO_EXTCMD_KEY && check_operating_dir(answer, FALSE)) {
- statusbar(_("Can't insert file from outside of %s"),
- operating_dir);
- return 0;
- }
-#endif
-
-#ifdef ENABLE_MULTIBUFFER
- if (loading_file) {
- /* update the current entry in the open_files structure */
- add_open_file(1);
- new_file();
- UNSET(MODIFIED);
-#ifndef NANO_SMALL
- UNSET(MARK_ISSET);
-#endif
- }
-#endif
-
-#ifndef NANO_SMALL
- if (i == NANO_EXTCMD_KEY) {
- realname = mallocstrcpy(realname, "");
- i = open_pipe(answer);
- } else
-#endif /* NANO_SMALL */
- {
- realname = real_dir_from_tilde(answer);
- i = open_file(realname, 1, loading_file);
- }
-
-#ifdef ENABLE_MULTIBUFFER
- if (loading_file) {
- /* if there was an error opening the file, free() realname,
- free() fileage (which now points to the new buffer we
- created to hold the file), reload the buffer we had open
- before, and skip the insertion; otherwise, save realname
- in filename and continue the insertion */
- if (i == -1) {
- free(realname);
- free(fileage);
- load_open_file();
- goto skip_insert;
- } else
- filename = mallocstrcpy(filename, realname);
- }
-#endif
-
- free(realname);
-
-#ifdef DEBUG
- dump_buffer(fileage);
-#endif
-
-#ifdef ENABLE_MULTIBUFFER
- if (loading_file)
- load_file(0);
- else
-#endif
- set_modified();
-
- /* Here we want to rebuild the edit window */
- fix_editbot();
-
-#ifdef ENABLE_MULTIBUFFER
- /* If we've loaded another file, update the titlebar's contents */
- if (loading_file) {
- clearok(topwin, FALSE);
- titlebar(NULL);
-
- /* And re-init the shortcut list */
- shortcut_init(0);
- }
-#endif
-
-#ifdef ENABLE_MULTIBUFFER
- if (!loading_file) {
-#endif
-
- /* Restore the old x-coordinate position */
- current_x = old_current_x;
-
-#ifdef ENABLE_MULTIBUFFER
- }
-#endif
-
- /* If we've gone off the bottom, recenter; otherwise, just redraw */
- if (current->lineno > editbot->lineno)
- edit_update(current, CENTER);
- else
- edit_refresh();
-
- } else {
- statusbar(_("Cancelled"));
- i = 0;
- }
-
-#ifdef ENABLE_MULTIBUFFER
- skip_insert:
-#endif
-
- free(inspath);
- inspath = NULL;
-
- display_main_list();
- return i;
-}
-
-int do_insertfile_void(void)
-{
- int result = 0;
-#ifdef ENABLE_MULTIBUFFER
- if (ISSET(VIEW_MODE)) {
- if (ISSET(MULTIBUFFER))
- result = do_insertfile(1);
- else
- statusbar(_("Key illegal in non-multibuffer mode"));
- }
- else
- result = do_insertfile(ISSET(MULTIBUFFER));
-#else
- result = do_insertfile(0);
-#endif
-
- display_main_list();
- return result;
-}
-
-#ifdef ENABLE_MULTIBUFFER
-/* Create a new openfilestruct node. */
-openfilestruct *make_new_opennode(openfilestruct *prevnode)
-{
- openfilestruct *newnode = (openfilestruct *)nmalloc(sizeof(openfilestruct));
-
- newnode->filename = NULL;
- newnode->fileage = NULL;
- newnode->filebot = NULL;
-
- newnode->prev = prevnode;
- newnode->next = NULL;
-
- return newnode;
-}
-
-/* Splice a node into an existing openfilestruct. */
-void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
- openfilestruct *end)
-{
- newnode->next = end;
- newnode->prev = begin;
- begin->next = newnode;
- if (end != NULL)
- end->prev = newnode;
-}
-
-/* Unlink a node from the rest of the openfilestruct. */
-void unlink_opennode(const openfilestruct *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 openfilestruct. */
-void delete_opennode(openfilestruct *fileptr)
-{
- if (fileptr != NULL) {
- if (fileptr->filename != NULL)
- free(fileptr->filename);
- if (fileptr->fileage != NULL)
- free_filestruct(fileptr->fileage);
- free(fileptr);
- }
-}
-
-/* Deallocate all memory associated with this and later files,
- * including the lines of text. */
-void free_openfilestruct(openfilestruct *src)
-{
- if (src != NULL) {
- while (src->next != NULL) {
- src = src->next;
- delete_opennode(src->prev);
-#ifdef DEBUG
- fprintf(stderr, "%s: free'd a node, YAY!\n", "delete_opennode()");
-#endif
- }
- delete_opennode(src);
-#ifdef DEBUG
- fprintf(stderr, "%s: free'd last node.\n", "delete_opennode()");
-#endif
- }
-}
-
-/*
- * Add/update an entry to the open_files openfilestruct. If update is
- * zero, a new entry is created; otherwise, the current entry is updated.
- * Return 0 on success or 1 on error.
- */
-int add_open_file(int update)
-{
- openfilestruct *tmp;
-
- if (fileage == NULL || current == NULL || filename == NULL)
- return 1;
-
- /* if no entries, make the first one */
- if (open_files == NULL)
- open_files = make_new_opennode(NULL);
-
- else if (!update) {
-
- /* otherwise, if we're not updating, make a new entry for
- open_files and splice it in after the current one */
-
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", open_files->filename);
-#endif
-
- tmp = make_new_opennode(NULL);
- splice_opennode(open_files, tmp, open_files->next);
- open_files = open_files->next;
- }
-
- /* save current filename */
- open_files->filename = mallocstrcpy(open_files->filename, filename);
-
-#ifndef NANO_SMALL
- /* save the file's stat */
- open_files->originalfilestat = originalfilestat;
-#endif
-
- /* save current total number of lines */
- open_files->file_totlines = totlines;
-
- /* save current total size */
- open_files->file_totsize = totsize;
-
- /* save current x-coordinate position */
- open_files->file_current_x = current_x;
-
- /* save current y-coordinate position */
- open_files->file_current_y = current_y;
-
- /* save current place we want */
- open_files->file_placewewant = placewewant;
-
- /* save current line number */
- open_files->file_lineno = current->lineno;
-
- /* if we're updating, save current modification status (and marking
- status, if available) */
- if (update) {
-#ifndef NANO_SMALL
- open_files->file_flags = (MODIFIED & ISSET(MODIFIED)) | (MARK_ISSET & ISSET(MARK_ISSET));
- if (ISSET(MARK_ISSET)) {
- open_files->file_mark_beginbuf = mark_beginbuf;
- open_files->file_mark_beginx = mark_beginx;
- }
-#else
- open_files->file_flags = (MODIFIED & ISSET(MODIFIED));
-#endif
- }
-
- /* if we're not in view mode and not updating, the file contents
- might have changed, so save the filestruct; otherwise, don't */
- if (!(ISSET(VIEW_MODE) && !update)) {
- /* save current file buffer */
- open_files->fileage = fileage;
- open_files->filebot = filebot;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", open_files->filename);
-#endif
-
- return 0;
-}
-
-/*
- * Read the current entry in the open_files structure and set up the
- * currently open file using that entry's information. Return 0 on
- * success or 1 on error.
- */
-int load_open_file(void)
-{
- if (open_files == NULL)
- return 1;
-
- /* set up the filename, the file buffer, the total number of lines in
- the file, and the total file size */
- filename = mallocstrcpy(filename, open_files->filename);
-#ifndef NANO_SMALL
- originalfilestat = open_files->originalfilestat;
-#endif
- fileage = open_files->fileage;
- current = fileage;
- filebot = open_files->filebot;
- totlines = open_files->file_totlines;
- totsize = open_files->file_totsize;
-
- /* restore modification status */
- if (open_files->file_flags & MODIFIED)
- SET(MODIFIED);
- else
- UNSET(MODIFIED);
-
-#ifndef NANO_SMALL
- /* restore marking status */
- if (open_files->file_flags & MARK_ISSET) {
- mark_beginbuf = open_files->file_mark_beginbuf;
- mark_beginx = open_files->file_mark_beginx;
- SET(MARK_ISSET);
- } else
- UNSET(MARK_ISSET);
-#endif
-
-#ifdef ENABLE_COLOR
- update_color();
-#endif
-
- /* restore full file position: line number, x-coordinate, y-
- coordinate, place we want */
- do_gotopos(open_files->file_lineno, open_files->file_current_x, open_files->file_current_y, open_files->file_placewewant);
-
- /* update the titlebar */
- clearok(topwin, FALSE);
- titlebar(NULL);
-
- /* now we're done */
- return 0;
-}
-
-/*
- * Open the previous entry in the open_files structure. If closing_file
- * is zero, update the current entry before switching from it.
- * Otherwise, we are about to close that entry, so don't bother doing so.
- * Return 0 on success and 1 on error.
- */
-int open_prevfile(int closing_file)
-{
- if (open_files == NULL)
- return 1;
-
- /* if we're not about to close the current entry, update it before
- doing anything */
- if (!closing_file)
- add_open_file(1);
-
- if (open_files->prev == NULL && open_files->next == NULL) {
-
- /* only one file open */
- if (!closing_file)
- statusbar(_("No more open files"));
- return 1;
- }
-
- if (open_files->prev != NULL) {
- open_files = open_files->prev;
-
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", open_files->filename);
-#endif
-
- }
-
- else if (open_files->next != NULL) {
-
- /* if we're at the beginning, wrap around to the end */
- while (open_files->next != NULL)
- open_files = open_files->next;
-
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", open_files->filename);
-#endif
-
- }
-
- load_open_file();
-
- statusbar(_("Switched to %s"),
- ((open_files->filename[0] == '\0') ? "New Buffer" : open_files->filename));
-
-#ifdef DEBUG
- dump_buffer(current);
-#endif
-
- return 0;
-}
-
-/* This function is used by the shortcut list. */
-int open_prevfile_void(void)
-{
- return open_prevfile(0);
-}
-
-/*
- * Open the next entry in the open_files structure. If closing_file is
- * zero, update the current entry before switching from it. Otherwise, we
- * are about to close that entry, so don't bother doing so. Return 0 on
- * success and 1 on error.
- */
-int open_nextfile(int closing_file)
-{
- if (open_files == NULL)
- return 1;
-
- /* if we're not about to close the current entry, update it before
- doing anything */
- if (!closing_file)
- add_open_file(1);
-
- if (open_files->prev == NULL && open_files->next == NULL) {
-
- /* only one file open */
- if (!closing_file)
- statusbar(_("No more open files"));
- return 1;
- }
-
- if (open_files->next != NULL) {
- open_files = open_files->next;
-
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", open_files->filename);
-#endif
-
- }
- else if (open_files->prev != NULL) {
-
- /* if we're at the end, wrap around to the beginning */
- while (open_files->prev != NULL) {
- open_files = open_files->prev;
-
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", open_files->filename);
-#endif
-
- }
- }
-
- load_open_file();
-
- statusbar(_("Switched to %s"),
- ((open_files->filename[0] == '\0') ? "New Buffer" : open_files->filename));
-
-#ifdef DEBUG
- dump_buffer(current);
-#endif
-
- return 0;
-}
-
-/* This function is used by the shortcut list. */
-int open_nextfile_void(void)
-{
- return open_nextfile(0);
-}
-
-/*
- * Delete an entry from the open_files filestruct. After deletion of an
- * entry, the next or previous entry is opened, whichever is found first.
- * Return 0 on success or 1 on error.
- */
-int close_open_file(void)
-{
- openfilestruct *tmp;
-
- if (open_files == NULL)
- return 1;
-
- /* make sure open_files->fileage and fileage, and open_files->filebot
- and filebot, are in sync; they might not be if lines have been cut
- from the top or bottom of the file */
- open_files->fileage = fileage;
- open_files->filebot = filebot;
-
- tmp = open_files;
- if (open_nextfile(1)) {
- if (open_prevfile(1))
- return 1;
- }
-
- unlink_opennode(tmp);
- delete_opennode(tmp);
-
- shortcut_init(0);
- display_main_list();
- return 0;
-}
-#endif /* MULTIBUFFER */
-
-#if !defined(DISABLE_SPELLER) || !defined(DISABLE_OPERATINGDIR)
-/*
- * When passed "[relative path]" or "[relative path][filename]" in
- * origpath, return "[full path]" or "[full path][filename]" on success,
- * or NULL on error. This is still done if the file doesn't exist but
- * the relative path does (since the file could exist in memory but not
- * yet on disk); it is not done if the relative path doesn't exist (since
- * the first call to chdir() will fail then).
- */
-char *get_full_path(const char *origpath)
-{
- char *newpath = NULL, *last_slash, *d_here, *d_there, *d_there_file, tmp;
- int path_only, last_slash_index;
- struct stat fileinfo;
- char *expanded_origpath;
-
- /* first, get the current directory, and tack a slash onto the end of
- it, unless it turns out to be "/", in which case leave it alone */
-
-#ifdef PATH_MAX
- d_here = getcwd(NULL, PATH_MAX + 1);
-#else
- d_here = getcwd(NULL, 0);
-#endif
-
- if (d_here != NULL) {
-
- align(&d_here);
- if (strcmp(d_here, "/")) {
- d_here = charealloc(d_here, strlen(d_here) + 2);
- strcat(d_here, "/");
- }
-
- /* stat origpath; if stat() fails, assume that origpath refers to
- a new file that hasn't been saved to disk yet (i. e. set
- path_only to 0); if stat() succeeds, set path_only to 0 if
- origpath doesn't refer to a directory, or to 1 if it does */
- path_only = !stat(origpath, &fileinfo) && S_ISDIR(fileinfo.st_mode);
-
- expanded_origpath = real_dir_from_tilde(origpath);
- /* save the value of origpath in both d_there and d_there_file */
- d_there = mallocstrcpy(NULL, expanded_origpath);
- d_there_file = mallocstrcpy(NULL, expanded_origpath);
- free(expanded_origpath);
-
- /* if we have a path but no filename, tack slashes onto the ends
- of both d_there and d_there_file, if they don't end in slashes
- already */
- if (path_only) {
- tmp = d_there[strlen(d_there) - 1];
- if (tmp != '/') {
- d_there = charealloc(d_there, strlen(d_there) + 2);
- strcat(d_there, "/");
- d_there_file = charealloc(d_there_file, strlen(d_there_file) + 2);
- strcat(d_there_file, "/");
- }
- }
-
- /* search for the last slash in d_there */
- last_slash = strrchr(d_there, '/');
-
- /* if we didn't find one, copy d_here into d_there; all data is
- then set up */
- if (last_slash == NULL)
- d_there = mallocstrcpy(d_there, d_here);
- else {
- /* otherwise, remove all non-path elements from d_there
- (i. e. everything after the last slash) */
- last_slash_index = strlen(d_there) - strlen(last_slash);
- null_at(&d_there, last_slash_index + 1);
-
- /* and remove all non-file elements from d_there_file (i. e.
- everything before and including the last slash); if we
- have a path but no filename, don't do anything */
- if (!path_only) {
- last_slash = strrchr(d_there_file, '/');
- last_slash++;
- strcpy(d_there_file, last_slash);
- align(&d_there_file);
- }
-
- /* now go to the path specified in d_there */
- if (chdir(d_there) != -1) {
- /* get the full pathname, and save it back in d_there,
- tacking a slash on the end if we have a path but no
- filename; if the saving fails, get out */
-
- free(d_there);
-
-#ifdef PATH_MAX
- d_there = getcwd(NULL, PATH_MAX + 1);
-#else
- d_there = getcwd(NULL, 0);
-#endif
-
- align(&d_there);
- if (d_there != NULL) {
-
- /* add a slash to d_there, unless it's "/", in which
- case we don't need it */
- if (strcmp(d_there, "/")) {
- d_there = charealloc(d_there, strlen(d_there) + 2);
- strcat(d_there, "/");
- }
- }
- else
- return NULL;
- }
-
- /* finally, go back to where we were before, d_here (no error
- checking is done on this chdir(), because we can do
- nothing if it fails) */
- chdir(d_here);
- }
-
- /* all data is set up; fill in newpath */
-
- /* if we have a path and a filename, newpath = d_there +
- d_there_file; otherwise, newpath = d_there */
- if (!path_only) {
- newpath = charalloc(strlen(d_there) + strlen(d_there_file) + 1);
- strcpy(newpath, d_there);
- strcat(newpath, d_there_file);
- }
- else {
- newpath = charalloc(strlen(d_there) + 1);
- strcpy(newpath, d_there);
- }
-
- /* finally, clean up */
- free(d_there_file);
- free(d_there);
- free(d_here);
- }
-
- return newpath;
-}
-#endif /* !DISABLE_SPELLER || !DISABLE_OPERATINGDIR */
-
-#ifndef DISABLE_SPELLER
-/*
- * This function accepts a path and returns the full path (via
- * get_full_path()). On error, if the path doesn't reference a
- * directory, or if the directory isn't writable, it returns NULL.
- */
-char *check_writable_directory(const char *path)
-{
- char *full_path = get_full_path(path);
- int writable;
- struct stat fileinfo;
-
- /* if get_full_path() failed, return NULL */
- if (full_path == NULL)
- return NULL;
-
- /* otherwise, stat() the full path to see if it's writable by the
- user; set writable to 1 if it is, or 0 if it isn't */
- writable = !stat(full_path, &fileinfo) && (fileinfo.st_mode & S_IWUSR);
-
- /* if the full path doesn't end in a slash (meaning get_full_path()
- found that it isn't a directory) or isn't writable, free full_path
- and return NULL */
- if (full_path[strlen(full_path) - 1] != '/' || writable == 0) {
- free(full_path);
- return NULL;
- }
-
- /* otherwise, return the full path */
- return full_path;
-}
-
-/*
- * This function accepts a directory name and filename prefix the same
- * way that tempnam() does, determines the location for its temporary
- * file the same way that tempnam() does, safely creates the temporary
- * file there via mkstemp(), and returns the name of the temporary file
- * the same way that tempnam() does. It does not reference the value of
- * TMP_MAX because the total number of random filenames that it can
- * generate using one prefix is equal to 256**6, which is a sufficiently
- * large number to handle most cases. Since the behavior after tempnam()
- * generates TMP_MAX random filenames is implementation-defined, my
- * implementation is to go on generating random filenames regardless of
- * it.
- */
-char *safe_tempnam(const char *dirname, const char *filename_prefix)
-{
- char *full_tempdir = NULL;
- const char *TMPDIR_env;
- int filedesc;
-
- /* if $TMPDIR is set and non-empty, set tempdir to it, run it through
- get_full_path(), and save the result in full_tempdir; otherwise,
- leave full_tempdir set to NULL */
- TMPDIR_env = getenv("TMPDIR");
- if (TMPDIR_env != NULL && TMPDIR_env[0] != '\0')
- full_tempdir = check_writable_directory(TMPDIR_env);
-
- /* if $TMPDIR is blank or isn't set, or isn't a writable
- directory, and dirname isn't NULL, try it; otherwise, leave
- full_tempdir set to NULL */
- if (full_tempdir == NULL && dirname != NULL)
- full_tempdir = check_writable_directory(dirname);
-
- /* if $TMPDIR is blank or isn't set, or if it isn't a writable
- directory, and dirname is NULL, try P_tmpdir instead */
- if (full_tempdir == NULL)
- full_tempdir = check_writable_directory(P_tmpdir);
-
- /* if P_tmpdir didn't work, use /tmp instead */
- if (full_tempdir == NULL) {
- full_tempdir = charalloc(6);
- strcpy(full_tempdir, "/tmp/");
- }
-
- full_tempdir = charealloc(full_tempdir, strlen(full_tempdir) + 12);
-
- /* like tempnam(), use only the first 5 characters of the prefix */
- strncat(full_tempdir, filename_prefix, 5);
- strcat(full_tempdir, "XXXXXX");
- filedesc = mkstemp(full_tempdir);
-
- /* if mkstemp succeeded, close the resulting file; afterwards, it'll be
- 0 bytes long, so delete it; finally, return the filename (all that's
- left of it) */
- if (filedesc != -1) {
- close(filedesc);
- unlink(full_tempdir);
- return full_tempdir;
- }
-
- free(full_tempdir);
- return NULL;
-}
-#endif /* !DISABLE_SPELLER */
-
-#ifndef DISABLE_OPERATINGDIR
-/* Initialize full_operating_dir based on operating_dir. */
-void init_operating_dir(void)
-{
- assert(full_operating_dir == NULL);
-
- if (operating_dir == NULL)
- return;
- full_operating_dir = get_full_path(operating_dir);
-
- /* If get_full_path() failed or the operating directory is
- inaccessible, unset operating_dir. */
- if (full_operating_dir == NULL || chdir(full_operating_dir) == -1) {
- free(full_operating_dir);
- full_operating_dir = NULL;
- free(operating_dir);
- operating_dir = NULL;
- }
-}
-
-/*
- * Check to see if we're inside the operating directory. Return 0 if we
- * are, or 1 otherwise. If allow_tabcomp is nonzero, allow incomplete
- * names that would be matches for the operating directory, so that tab
- * completion will work.
- */
-int check_operating_dir(const char *currpath, int allow_tabcomp)
-{
- /* The char *full_operating_dir is global for mem cleanup, and
- therefore we only need to get it the first time this function
- is called; also, a relative operating directory path will
- only be handled properly if this is done */
-
- char *fullpath;
- int retval = 0;
- const char *whereami1, *whereami2 = NULL;
-
- /* if no operating directory is set, don't bother doing anything */
- if (operating_dir == NULL)
- return 0;
-
- fullpath = get_full_path(currpath);
- if (fullpath == NULL)
- return 1;
-
- whereami1 = strstr(fullpath, full_operating_dir);
- if (allow_tabcomp)
- whereami2 = strstr(full_operating_dir, fullpath);
-
- /* if both searches failed, we're outside the operating directory */
- /* otherwise */
- /* check the search results; if the full operating directory path is
- not at the beginning of the full current path (for normal usage)
- and vice versa (for tab completion, if we're allowing it), we're
- outside the operating directory */
- if (whereami1 != fullpath && whereami2 != full_operating_dir)
- retval = 1;
- free(fullpath);
- /* otherwise, we're still inside it */
- return retval;
-}
-#endif
-
-/*
- * Write a file out. If tmp is nonzero, we set the umask to 0600,
- * we don't set the global variable filename to its name, and don't
- * print out how many lines we wrote on the statusbar.
- *
- * tmp means we are writing a tmp file in a secure fashion. We use
- * it when spell checking or dumping the file on an error.
- *
- * append == 1 means we are appending instead of overwriting.
- * append == 2 means we are prepending instead of overwriting.
- *
- * nonamechange means don't change the current filename, it is ignored
- * if tmp is nonzero or if we're appending/prepending.
- */
-int write_file(const char *name, int tmp, int append, int nonamechange)
-{
- int retval = -1;
- /* Instead of returning in this function, you should always
- * merely set retval then goto cleanup_and_exit. */
- long size;
- int lineswritten = 0;
- char *buf = NULL;
- const filestruct *fileptr;
- FILE *f;
- int fd;
- int mask = 0, realexists, anyexists;
- struct stat st, lst;
- char *realname = NULL;
-
- if (name[0] == '\0') {
- statusbar(_("Cancelled"));
- return -1;
- }
- if (!tmp)
- titlebar(NULL);
- fileptr = fileage;
-
- realname = real_dir_from_tilde(name);
-
-#ifndef DISABLE_OPERATINGDIR
- /* If we're writing a temporary file, we're probably going outside
- the operating directory, so skip the operating directory test. */
- if (!tmp && operating_dir != NULL && check_operating_dir(realname, 0)) {
- statusbar(_("Can't write outside of %s"), operating_dir);
- goto cleanup_and_exit;
- }
-#endif
-
- /* Save the state of file at the end of the symlink (if there is
- one). */
- realexists = stat(realname, &st);
-
-#ifndef NANO_SMALL
- /* We backup only if the backup toggle is set, the file isn't
- temporary, and the file already exists. Furthermore, if we aren't
- appending, prepending, or writing a selection, we backup only if
- the file has not been modified by someone else since nano opened
- it. */
- if (ISSET(BACKUP_FILE) && !tmp && realexists == 0 &&
- (append != 0 || ISSET(MARK_ISSET) ||
- originalfilestat.st_mtime == st.st_mtime)) {
- FILE *backup_file;
- char *backupname = NULL;
- char backupbuf[COPYFILEBLOCKSIZE];
- size_t bytesread;
- struct utimbuf filetime;
-
- /* save the original file's access and modification times */
- filetime.actime = originalfilestat.st_atime;
- filetime.modtime = originalfilestat.st_mtime;
-
- /* open the original file to copy to the backup */
- f = fopen(realname, "rb");
- if (f == NULL) {
- statusbar(_("Could not read %s for backup: %s"), realname,
- strerror(errno));
- return -1;
- }
-
- backupname = charalloc(strlen(realname) + 2);
- sprintf(backupname, "%s~", realname);
-
- /* get a file descriptor for the destination backup file */
- backup_file = fopen(backupname, "wb");
- if (backup_file == NULL) {
- statusbar(_("Couldn't write backup: %s"), strerror(errno));
- free(backupname);
- return -1;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "Backing up %s to %s\n", realname, backupname);
-#endif
-
- /* copy the file */
- while ((bytesread = fread(backupbuf, sizeof(char),
- COPYFILEBLOCKSIZE, f)) > 0)
- if (fwrite(backupbuf, sizeof(char), bytesread, backup_file) <= 0)
- break;
- fclose(backup_file);
- fclose(f);
-
- if (chmod(backupname, originalfilestat.st_mode) == -1)
- statusbar(_("Could not set permissions %o on backup %s: %s"),
- originalfilestat.st_mode, backupname,
- strerror(errno));
-
- if (chown(backupname, originalfilestat.st_uid,
- originalfilestat.st_gid) == -1)
- statusbar(_("Could not set owner %d/group %d on backup %s: %s"),
- originalfilestat.st_uid, originalfilestat.st_gid,
- backupname, strerror(errno));
-
- if (utime(backupname, &filetime) == -1)
- statusbar(_("Could not set access/modification time on backup %s: %s"),
- backupname, strerror(errno));
-
- free(backupname);
- }
-#endif
-
- /* Stat the link itself for the check... */
- anyexists = lstat(realname, &lst);
-
- /* New case: if the file exists, just give up */
- if (tmp && anyexists != -1)
- goto cleanup_and_exit;
- /* NOTE: If you change this statement, you MUST CHANGE the if
- statement below (that says:
- if (realexists == -1 || tmp || (ISSET(NOFOLLOW_SYMLINKS) &&
- S_ISLNK(lst.st_mode))) {
- to reflect whether or not to link/unlink/rename the file */
- else if (append != 2 && (!ISSET(NOFOLLOW_SYMLINKS) || !S_ISLNK(lst.st_mode)
- || tmp)) {
- /* Use O_EXCL if tmp is nonzero. This is now copied from joe,
- because wiggy says so *shrug*. */
- if (append != 0)
- fd = open(realname, O_WRONLY | O_CREAT | O_APPEND, (S_IRUSR | S_IWUSR));
- else if (tmp)
- fd = open(realname, O_WRONLY | O_CREAT | O_EXCL, (S_IRUSR | S_IWUSR));
- else
- fd = open(realname, O_WRONLY | O_CREAT | O_TRUNC, (S_IRUSR | S_IWUSR));
-
- /* First, just give up if we couldn't even open the file */
- if (fd == -1) {
- if (!tmp && ISSET(TEMP_OPT)) {
- UNSET(TEMP_OPT);
- retval = do_writeout(filename, 1, 0);
- } else
- statusbar(_("Could not open file for writing: %s"),
- strerror(errno));
- goto cleanup_and_exit;
- }
-
- }
- /* Don't follow symlink. Create new file. */
- else {
- buf = charalloc(strlen(realname) + 8);
- strcpy(buf, realname);
- strcat(buf, ".XXXXXX");
- if ((fd = mkstemp(buf)) == -1) {
- if (ISSET(TEMP_OPT)) {
- UNSET(TEMP_OPT);
- retval = do_writeout(filename, 1, 0);
- } else
- statusbar(_("Could not open file for writing: %s"),
- strerror(errno));
- goto cleanup_and_exit;
- }
- }
-
-#ifdef DEBUG
- dump_buffer(fileage);
-#endif
-
- f = fdopen(fd, append == 1 ? "ab" : "wb");
- if (f == NULL) {
- statusbar(_("Could not open file for writing: %s"), strerror(errno));
- goto cleanup_and_exit;
- }
-
- while (fileptr != NULL && fileptr->next != NULL) {
- int data_len;
-
- /* Next line is so we discount the "magic line" */
- if (filebot == fileptr && fileptr->data[0] == '\0')
- break;
-
- data_len = strlen(fileptr->data);
-
- /* newlines to nulls, just before we write to disk */
- sunder(fileptr->data);
-
- size = fwrite(fileptr->data, 1, data_len, f);
-
- /* nulls to newlines; data_len is the string's real length here */
- unsunder(fileptr->data, data_len);
-
- if (size < data_len) {
- statusbar(_("Could not open file for writing: %s"),
- strerror(errno));
- fclose(f);
- goto cleanup_and_exit;
- }
-#ifdef DEBUG
- else
- fprintf(stderr, "Wrote >%s\n", fileptr->data);
-#endif
-#ifndef NANO_SMALL
- if (ISSET(DOS_FILE) || ISSET(MAC_FILE))
- putc('\r', f);
-
- if (!ISSET(MAC_FILE))
-#endif
- putc('\n', f);
-
- fileptr = fileptr->next;
- lineswritten++;
- }
-
- if (fileptr != NULL) {
- int data_len = strlen(fileptr->data);
-
- /* newlines to nulls, just before we write to disk */
- sunder(fileptr->data);
-
- size = fwrite(fileptr->data, 1, data_len, f);
-
- /* nulls to newlines; data_len is the string's real length here */
- unsunder(fileptr->data, data_len);
-
- if (size < data_len) {
- statusbar(_("Could not open file for writing: %s"),
- strerror(errno));
- goto cleanup_and_exit;
- } else if (data_len > 0) {
-#ifndef NANO_SMALL
- if (ISSET(DOS_FILE) || ISSET(MAC_FILE)) {
- if (putc('\r', f) == EOF) {
- statusbar(_("Could not open file for writing: %s"),
- strerror(errno));
- fclose(f);
- goto cleanup_and_exit;
- }
- lineswritten++;
- }
-
- if (!ISSET(MAC_FILE))
-#endif
- {
- if (putc('\n', f) == EOF) {
- statusbar(_("Could not open file for writing: %s"),
- strerror(errno));
- fclose(f);
- goto cleanup_and_exit;
- }
- lineswritten++;
- }
- }
- }
-
- if (fclose(f) != 0) {
- statusbar(_("Could not close %s: %s"), realname, strerror(errno));
- unlink(buf);
- goto cleanup_and_exit;
- }
-
- /* if we're prepending, open the real file, and append it here */
- if (append == 2) {
- int fd_source, fd_dest;
- FILE *f_source, *f_dest;
- int prechar;
-
- if ((fd_dest = open(buf, O_WRONLY | O_APPEND, (S_IRUSR | S_IWUSR))) == -1) {
- statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
- goto cleanup_and_exit;
- }
- f_dest = fdopen(fd_dest, "wb");
- if (f_dest == NULL) {
- statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
- close(fd_dest);
- goto cleanup_and_exit;
- }
- if ((fd_source = open(realname, O_RDONLY | O_CREAT)) == -1) {
- statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
- fclose(f_dest);
- goto cleanup_and_exit;
- }
- f_source = fdopen(fd_source, "rb");
- if (f_source == NULL) {
- statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
- fclose(f_dest);
- close(fd_source);
- goto cleanup_and_exit;
- }
-
- /* Doing this in blocks is an exercise left to some other reader. */
- while ((prechar = getc(f_source)) != EOF) {
- if (putc(prechar, f_dest) == EOF) {
- statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
- fclose(f_source);
- fclose(f_dest);
- goto cleanup_and_exit;
- }
- }
-
- if (ferror(f_source)) {
- statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
- fclose(f_source);
- fclose(f_dest);
- goto cleanup_and_exit;
- }
-
- fclose(f_source);
- fclose(f_dest);
- }
-
- if (realexists == -1 || tmp ||
- (ISSET(NOFOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
-
- /* Use default umask as file permissions if file is a new file. */
- mask = umask(0);
- umask(mask);
-
- if (tmp) /* We don't want anyone reading our temporary file! */
- mask = S_IRUSR | S_IWUSR;
- else
- mask = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
- S_IWOTH) & ~mask;
- } else
- /* Use permissions from file we are overwriting. */
- mask = st.st_mode;
-
- if (append == 2 ||
- (!tmp && (ISSET(NOFOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode)))) {
- if (unlink(realname) == -1) {
- if (errno != ENOENT) {
- statusbar(_("Could not open %s for writing: %s"),
- realname, strerror(errno));
- unlink(buf);
- goto cleanup_and_exit;
- }
- }
- if (link(buf, realname) != -1)
- unlink(buf);
- else if (errno != EPERM) {
- statusbar(_("Could not open %s for writing: %s"),
- name, strerror(errno));
- unlink(buf);
- goto cleanup_and_exit;
- } else if (rename(buf, realname) == -1) { /* Try a rename?? */
- statusbar(_("Could not open %s for writing: %s"),
- realname, strerror(errno));
- unlink(buf);
- goto cleanup_and_exit;
- }
- }
- if (chmod(realname, mask) == -1)
- statusbar(_("Could not set permissions %o on %s: %s"),
- mask, realname, strerror(errno));
-
- if (!tmp && append == 0) {
- if (!nonamechange) {
- filename = mallocstrcpy(filename, realname);
-#ifdef ENABLE_COLOR
- update_color();
- edit_refresh();
-#endif
- }
-
-#ifndef NANO_SMALL
- /* Update originalfilestat to reference the file as it is now. */
- stat(filename, &originalfilestat);
-#endif
- statusbar(P_("Wrote %d line", "Wrote %d lines", lineswritten),
- lineswritten);
- UNSET(MODIFIED);
- titlebar(NULL);
- }
-
- retval = 1;
-
- cleanup_and_exit:
- free(realname);
- free(buf);
- return retval;
-}
-
-int do_writeout(const char *path, int exiting, int append)
-{
- int i = 0;
-#ifdef NANO_EXTRA
- static int did_cred = 0;
-#endif
-
-#if !defined(DISABLE_BROWSER) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
- currshortcut = writefile_list;
-#endif
-
- answer = mallocstrcpy(answer, path);
-
- if (exiting && ISSET(TEMP_OPT)) {
- if (filename[0] != '\0') {
- i = write_file(answer, 0, 0, 0);
- display_main_list();
- return i;
- } else {
- UNSET(TEMP_OPT);
- do_exit();
-
- /* They cancelled, abort quit */
- return -1;
- }
- }
-
- while (1) {
-#ifndef NANO_SMALL
- const char *formatstr, *backupstr;
-
- if (ISSET(MAC_FILE))
- formatstr = _(" [Mac Format]");
- else if (ISSET(DOS_FILE))
- formatstr = _(" [DOS Format]");
- else
- formatstr = "";
-
- if (ISSET(BACKUP_FILE))
- backupstr = _(" [Backup]");
- else
- backupstr = "";
-
- /* Be nice to the translation folks */
- if (ISSET(MARK_ISSET) && !exiting) {
- if (append == 2)
- i = statusq(1, writefile_list, "", 0,
- "%s%s%s", _("Prepend Selection to File"), formatstr, backupstr);
- else if (append == 1)
- i = statusq(1, writefile_list, "", 0,
- "%s%s%s", _("Append Selection to File"), formatstr, backupstr);
- else
- i = statusq(1, writefile_list, "", 0,
- "%s%s%s", _("Write Selection to File"), formatstr, backupstr);
- } else {
- if (append == 2)
- i = statusq(1, writefile_list, answer, 0,
- "%s%s%s", _("File Name to Prepend to"), formatstr, backupstr);
- else if (append == 1)
- i = statusq(1, writefile_list, answer, 0,
- "%s%s%s", _("File Name to Append to"), formatstr, backupstr);
- else
- i = statusq(1, writefile_list, answer, 0,
- "%s%s%s", _("File Name to Write"), formatstr, backupstr);
- }
-#else
- if (append == 2)
- i = statusq(1, writefile_list, answer,
- "%s", _("File Name to Prepend to"));
- else if (append == 1)
- i = statusq(1, writefile_list, answer,
- "%s", _("File Name to Append to"));
- else
- i = statusq(1, writefile_list, answer,
- "%s", _("File Name to Write"));
-#endif /* !NANO_SMALL */
-
- if (i == -1) {
- statusbar(_("Cancelled"));
- display_main_list();
- return 0;
- }
-
-#ifndef DISABLE_BROWSER
- if (i == NANO_TOFILES_KEY) {
- char *tmp = do_browse_from(answer);
-
- currshortcut = writefile_list;
- if (tmp == NULL)
- continue;
- free(answer);
- answer = tmp;
- } else
-#endif /* !DISABLE_BROWSER */
-#ifndef NANO_SMALL
- if (i == TOGGLE_DOS_KEY) {
- UNSET(MAC_FILE);
- TOGGLE(DOS_FILE);
- continue;
- } else if (i == TOGGLE_MAC_KEY) {
- UNSET(DOS_FILE);
- TOGGLE(MAC_FILE);
- continue;
- } else if (i == TOGGLE_BACKUP_KEY) {
- TOGGLE(BACKUP_FILE);
- continue;
- } else
-#endif /* !NANO_SMALL */
- if (i == NANO_PREPEND_KEY) {
- append = append == 2 ? 0 : 2;
- continue;
- } else if (i == NANO_APPEND_KEY) {
- append = append == 1 ? 0 : 1;
- continue;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "filename is %s\n", answer);
-#endif
-
-#ifdef NANO_EXTRA
- if (exiting && !ISSET(TEMP_OPT) && !strcasecmp(answer, "zzy")
- && !did_cred) {
- do_credits();
- did_cred = 1;
- return -1;
- }
-#endif
- if (append == 0 && strcmp(answer, filename)) {
- struct stat st;
-
- if (!stat(answer, &st)) {
- i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));
-
- if (i == 0 || i == -1)
- continue;
- }
- }
-
-#ifndef NANO_SMALL
- /* Here's where we allow the selected text to be written to
- a separate file. */
- if (ISSET(MARK_ISSET) && !exiting) {
- filestruct *fileagebak = fileage;
- filestruct *filebotbak = filebot;
- filestruct *cutback = cutbuffer;
- int oldmod = ISSET(MODIFIED);
- /* write_file() unsets the MODIFIED flag. */
-
- cutbuffer = NULL;
-
- /* Put the marked text in the cutbuffer without changing
- the open file. */
- cut_marked_segment(current, current_x, mark_beginbuf,
- mark_beginx, 0);
-
- fileage = cutbuffer;
- filebot = get_cutbottom();
- i = write_file(answer, 0, append, 1);
-
- /* Now restore everything */
- free_filestruct(cutbuffer);
- fileage = fileagebak;
- filebot = filebotbak;
- cutbuffer = cutback;
- if (oldmod)
- set_modified();
- } else
-#endif /* !NANO_SMALL */
- i = write_file(answer, 0, append, 0);
-
-#ifdef ENABLE_MULTIBUFFER
- /* If we're not about to exit, update the current entry in
- the open_files structure. */
- if (!exiting)
- add_open_file(1);
-#endif
- display_main_list();
- return i;
- } /* while (1) */
-}
-
-int do_writeout_void(void)
-{
- return do_writeout(filename, 0, 0);
-}
-
-/* Return a malloc()ed string containing the actual directory, used
- * to convert ~user and ~/ notation... */
-char *real_dir_from_tilde(const char *buf)
-{
- char *dirtmp = NULL;
-
- if (buf[0] == '~') {
- size_t i;
- const struct passwd *userdata;
-
- /* Figure how how much of the str we need to compare */
- for (i = 1; buf[i] != '/' && buf[i] != '\0'; i++)
- ;
-
- /* Determine home directory using getpwuid() or getpwent(),
- don't rely on $HOME */
- if (i == 1)
- userdata = getpwuid(geteuid());
- else {
- do {
- userdata = getpwent();
- } while (userdata != NULL &&
- strncmp(userdata->pw_name, buf + 1, i - 1));
- }
- endpwent();
-
- if (userdata != NULL) { /* User found */
- dirtmp = charalloc(strlen(userdata->pw_dir) + strlen(buf + i) + 1);
- sprintf(dirtmp, "%s%s", userdata->pw_dir, &buf[i]);
- }
- }
-
- if (dirtmp == NULL)
- dirtmp = mallocstrcpy(dirtmp, buf);
-
- return dirtmp;
-}
-
-#ifndef DISABLE_TABCOMP
-/* Tack a slash onto the string we're completing if it's a directory. We
- * assume there is room for one more character on the end of buf. The
- * return value says whether buf is a directory. */
-int append_slash_if_dir(char *buf, int *lastwastab, int *place)
-{
- char *dirptr = real_dir_from_tilde(buf);
- struct stat fileinfo;
- int ret = 0;
-
- assert(dirptr != buf);
-
- if (stat(dirptr, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode)) {
- strncat(buf, "/", 1);
- (*place)++;
- /* now we start over again with # of tabs so far */
- *lastwastab = 0;
- ret = 1;
- }
-
- free(dirptr);
- return ret;
-}
-
-/*
- * These functions (username_tab_completion(), cwd_tab_completion(), and
- * input_tab()) were taken from busybox 0.46 (cmdedit.c). Here is the
- * notice from that file:
- *
- * Termios command line History and Editting, originally
- * intended for NetBSD sh (ash)
- * Copyright (c) 1999
- * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
- * Etc: Dave Cinege <dcinege@psychosis.com>
- * Majorly adjusted/re-written for busybox:
- * Erik Andersen <andersee@debian.org>
- *
- * You may use this code as you wish, so long as the original author(s)
- * are attributed in any redistributions of the source code.
- * This code is 'as is' with no warranty.
- * This code may safely be consumed by a BSD or GPL license.
- */
-
-char **username_tab_completion(char *buf, int *num_matches)
-{
- char **matches = (char **)NULL;
- char *matchline = NULL;
- struct passwd *userdata;
-
- *num_matches = 0;
- matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
-
- strcat(buf, "*");
-
- while ((userdata = getpwent()) != NULL) {
-
- if (check_wildcard_match(userdata->pw_name, &buf[1]) == TRUE) {
-
- /* Cool, found a match. Add it to the list
- * This makes a lot more sense to me (Chris) this way...
- */
-
-#ifndef DISABLE_OPERATINGDIR
- /* ...unless the match exists outside the operating
- directory, in which case just go to the next match */
-
- if (operating_dir != NULL) {
- if (check_operating_dir(userdata->pw_dir, 1) != 0)
- continue;
- }
-#endif
-
- matchline = charalloc(strlen(userdata->pw_name) + 2);
- sprintf(matchline, "~%s", userdata->pw_name);
- matches[*num_matches] = matchline;
- ++*num_matches;
-
- /* If there's no more room, bail out */
- if (*num_matches == BUFSIZ)
- break;
- }
- }
- endpwent();
-
- return matches;
-}
-
-/* This was originally called exe_n_cwd_tab_completion, but we're not
- worried about executables, only filenames :> */
-
-char **cwd_tab_completion(char *buf, int *num_matches)
-{
- char *dirname, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
- char **matches = (char **)NULL;
- DIR *dir;
- struct dirent *next;
-
- matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
-
- /* Stick a wildcard onto the buf, for later use */
- strcat(buf, "*");
-
- /* Okie, if there's a / in the buffer, strip out the directory part */
- if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
- dirname = charalloc(strlen(buf) + 1);
- tmp = buf + strlen(buf);
- while (*tmp != '/' && tmp != buf)
- tmp--;
-
- tmp++;
-
- strncpy(dirname, buf, tmp - buf + 1);
- dirname[tmp - buf] = '\0';
-
- } else {
-
-#ifdef PATH_MAX
- if ((dirname = getcwd(NULL, PATH_MAX + 1)) == NULL)
-#else
- /* The better, but apparently segfault-causing way */
- if ((dirname = getcwd(NULL, 0)) == NULL)
-#endif /* PATH_MAX */
- return matches;
- else
- tmp = buf;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "\nDir = %s\n", dirname);
- fprintf(stderr, "\nbuf = %s\n", buf);
- fprintf(stderr, "\ntmp = %s\n", tmp);
-#endif
-
- dirtmp = real_dir_from_tilde(dirname);
- free(dirname);
- dirname = dirtmp;
-
-#ifdef DEBUG
- fprintf(stderr, "\nDir = %s\n", dirname);
- fprintf(stderr, "\nbuf = %s\n", buf);
- fprintf(stderr, "\ntmp = %s\n", tmp);
-#endif
-
-
- dir = opendir(dirname);
- if (dir == NULL) {
- /* Don't print an error, just shut up and return */
- *num_matches = 0;
- beep();
- return matches;
- }
- while ((next = readdir(dir)) != NULL) {
-
-#ifdef DEBUG
- fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
-#endif
- /* See if this matches */
- if (check_wildcard_match(next->d_name, tmp) == TRUE) {
-
- /* Cool, found a match. Add it to the list
- * This makes a lot more sense to me (Chris) this way...
- */
-
-#ifndef DISABLE_OPERATINGDIR
- /* ...unless the match exists outside the operating
- directory, in which case just go to the next match; to
- properly do operating directory checking, we have to add the
- directory name to the beginning of the proposed match
- before we check it */
-
- if (operating_dir != NULL) {
- tmp2 = charalloc(strlen(dirname) + strlen(next->d_name) + 2);
- strcpy(tmp2, dirname);
- strcat(tmp2, "/");
- strcat(tmp2, next->d_name);
- if (check_operating_dir(tmp2, 1)) {
- free(tmp2);
- continue;
- }
- free(tmp2);
- }
-#endif
-
- tmp2 = NULL;
- tmp2 = charalloc(strlen(next->d_name) + 1);
- strcpy(tmp2, next->d_name);
- matches[*num_matches] = tmp2;
- ++*num_matches;
-
- /* If there's no more room, bail out */
- if (*num_matches == BUFSIZ)
- break;
- }
- }
- closedir(dir);
- free(dirname);
-
- return matches;
-}
-
-/* This function now has an arg which refers to how much the statusbar
- * (place) should be advanced, i.e. the new cursor pos. */
-char *input_tab(char *buf, int place, int *lastwastab, int *newplace, int *list)
-{
- /* Do TAB completion */
- static int num_matches = 0, match_matches = 0;
- static char **matches = (char **)NULL;
- int pos = place, i = 0, col = 0, editline = 0;
- int longestname = 0, is_dir = 0;
- char *foo;
-
- *list = 0;
-
- if (*lastwastab == FALSE) {
- char *tmp, *copyto, *matchbuf;
-
- *lastwastab = 1;
-
- /* Make a local copy of the string -- up to the position of the
- cursor */
- matchbuf = charalloc(strlen(buf) + 2);
- memset(matchbuf, '\0', strlen(buf) + 2);
-
- strncpy(matchbuf, buf, place);
- tmp = matchbuf;
-
- /* skip any leading white space */
- while (*tmp && isspace((int)*tmp))
- ++tmp;
-
- /* Free up any memory already allocated */
- if (matches != NULL) {
- for (i = i; i < num_matches; i++)
- free(matches[i]);
- free(matches);
- matches = (char **)NULL;
- num_matches = 0;
- }
-
- /* If the word starts with `~' and there is no slash in the word,
- * then try completing this word as a username. */
-
- /* If the original string begins with a tilde, and the part
- we're trying to tab-complete doesn't contain a slash, copy
- the part we're tab-completing into buf, so tab completion
- will result in buf's containing only the tab-completed
- username. */
- if (buf[0] == '~' && strchr(tmp, '/') == NULL) {
- buf = mallocstrcpy(buf, tmp);
- matches = username_tab_completion(tmp, &num_matches);
- }
- /* If we're in the middle of the original line, copy the string
- only up to the cursor position into buf, so tab completion
- will result in buf's containing only the tab-completed
- path/filename. */
- else if (strlen(buf) > strlen(tmp))
- buf = mallocstrcpy(buf, tmp);
-
- /* Try to match everything in the current working directory that
- * matches. */
- if (matches == NULL)
- matches = cwd_tab_completion(tmp, &num_matches);
-
- /* Don't leak memory */
- free(matchbuf);
-
-#ifdef DEBUG
- fprintf(stderr, "%d matches found...\n", num_matches);
-#endif
- /* Did we find exactly one match? */
- switch (num_matches) {
- case 0:
- blank_edit();
- wrefresh(edit);
- break;
- case 1:
-
- buf = charealloc(buf, strlen(buf) + strlen(matches[0]) + 1);
-
- if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
- for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
- tmp--);
- tmp++;
- } else
- tmp = buf;
-
- if (!strcmp(tmp, matches[0]))
- is_dir = append_slash_if_dir(buf, lastwastab, newplace);
-
- if (is_dir != 0)
- break;
-
- copyto = tmp;
- for (pos = 0; *tmp == matches[0][pos] &&
- pos <= strlen(matches[0]); pos++)
- tmp++;
-
- /* write out the matched name */
- strncpy(copyto, matches[0], strlen(matches[0]) + 1);
- *newplace += strlen(matches[0]) - pos;
-
- /* if an exact match is typed in and Tab is pressed,
- *newplace will now be negative; in that case, make it
- zero, so that the cursor will stay where it is instead of
- moving backward */
- if (*newplace < 0)
- *newplace = 0;
-
- /* Is it a directory? */
- append_slash_if_dir(buf, lastwastab, newplace);
-
- break;
- default:
- /* Check to see if all matches share a beginning, and, if so,
- tack it onto buf and then beep */
-
- if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
- for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
- tmp--);
- tmp++;
- } else
- tmp = buf;
-
- for (pos = 0; *tmp == matches[0][pos] && *tmp != '\0' &&
- pos <= strlen(matches[0]); pos++)
- tmp++;
-
- while (1) {
- match_matches = 0;
-
- for (i = 0; i < num_matches; i++) {
- if (matches[i][pos] == 0)
- break;
- else if (matches[i][pos] == matches[0][pos])
- match_matches++;
- }
- if (match_matches == num_matches &&
- (i == num_matches || matches[i] != 0)) {
- /* All the matches have the same character at pos+1,
- so paste it into buf... */
- buf = charealloc(buf, strlen(buf) + 2);
- strncat(buf, matches[0] + pos, 1);
- *newplace += 1;
- pos++;
- } else {
- beep();
- break;
- }
- }
- }
- } else {
- /* Ok -- the last char was a TAB. Since they
- * just hit TAB again, print a list of all the
- * available choices... */
- if (matches != NULL && num_matches > 1) {
-
- /* Blank the edit window, and print the matches out there */
- blank_edit();
- wmove(edit, 0, 0);
-
- editline = 0;
-
- /* Figure out the length of the longest filename */
- for (i = 0; i < num_matches; i++)
- if (strlen(matches[i]) > longestname)
- longestname = strlen(matches[i]);
-
- if (longestname > COLS - 1)
- longestname = COLS - 1;
-
- foo = charalloc(longestname + 5);
-
- /* Print the list of matches */
- for (i = 0, col = 0; i < num_matches; i++) {
-
- /* make each filename shown be the same length as the longest
- filename, with two spaces at the end */
- snprintf(foo, longestname + 1, matches[i]);
- while (strlen(foo) < longestname)
- strcat(foo, " ");
-
- strcat(foo, " ");
-
- /* Disable el cursor */
- curs_set(0);
- /* now, put the match on the screen */
- waddnstr(edit, foo, strlen(foo));
- col += strlen(foo);
-
- /* And if the next match isn't going to fit on the
- line, move to the next one */
- if (col > COLS - longestname && i + 1 < num_matches) {
- editline++;
- wmove(edit, editline, 0);
- if (editline == editwinrows - 1) {
- waddstr(edit, _("(more)"));
- break;
- }
- col = 0;
- }
- }
- free(foo);
- wrefresh(edit);
- *list = 1;
- } else
- beep();
- }
-
- /* Only refresh the edit window if we don't have a list of filename
- matches on it */
- if (*list == 0)
- edit_refresh();
- curs_set(1);
- return buf;
-}
-#endif /* !DISABLE_TABCOMP */
-
-#ifndef DISABLE_BROWSER
-/* Return the stat of the file pointed to by path */
-struct stat filestat(const char *path)
-{
- struct stat st;
-
- stat(path, &st);
- return st;
-}
-
-/* Our sort routine for file listings - sort directories before
- * files, and then alphabetically. */
-int diralphasort(const void *va, const void *vb)
-{
- struct stat fileinfo;
- const char *a = *(char *const *)va, *b = *(char *const *)vb;
- int aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
- int bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
-
- if (aisdir != 0 && bisdir == 0)
- return -1;
- if (aisdir == 0 && bisdir != 0)
- return 1;
-
- return strcasecmp(a, b);
-}
-
-/* Free our malloc()ed memory */
-void free_charptrarray(char **array, int len)
-{
- for (; len > 0; len--)
- free(array[len - 1]);
- free(array);
-}
-
-/* Only print the last part of a path; isn't there a shell
- * command for this? */
-const char *tail(const char *foo)
-{
- const char *tmp = foo + strlen(foo);
-
- while (*tmp != '/' && tmp != foo)
- tmp--;
-
- if (*tmp == '/')
- tmp++;
-
- return tmp;
-}
-
-/* Strip one dir from the end of a string. */
-void striponedir(char *foo)
-{
- char *tmp;
-
- assert(foo != NULL);
- /* Don't strip the root dir */
- if (*foo == '\0' || strcmp(foo, "/") == 0)
- return;
-
- tmp = foo + strlen(foo) - 1;
- assert(tmp >= foo);
- if (*tmp == '/')
- *tmp = '\0';
-
- while (*tmp != '/' && tmp != foo)
- tmp--;
-
- if (tmp != foo)
- *tmp = '\0';
- else { /* SPK may need to make a 'default' path here */
- if (*tmp != '/')
- *tmp = '.';
- *(tmp + 1) = '\0';
- }
-}
-
-int readable_dir(const char *path)
-{
- DIR *dir = opendir(path);
-
- /* If dir is NULL, don't do closedir(), since that changes errno. */
- if (dir != NULL)
- closedir(dir);
- return dir != NULL;
-}
-
-/* Initialize the browser code, including the list of files in *path */
-char **browser_init(const char *path, int *longest, int *numents)
-{
- DIR *dir;
- struct dirent *next;
- char **filelist;
- int i = 0;
- size_t path_len;
-
- dir = opendir(path);
- if (dir == NULL)
- return NULL;
-
- *numents = 0;
- while ((next = readdir(dir)) != NULL) {
- if (!strcmp(next->d_name, "."))
- continue;
- (*numents)++;
- if (strlen(next->d_name) > *longest)
- *longest = strlen(next->d_name);
- }
- rewinddir(dir);
- *longest += 10;
-
- filelist = (char **)nmalloc(*numents * sizeof (char *));
-
- if (!strcmp(path, "/"))
- path = "";
- path_len = strlen(path);
-
- while ((next = readdir(dir)) != NULL) {
- if (!strcmp(next->d_name, "."))
- continue;
-
- filelist[i] = charalloc(strlen(next->d_name) + path_len + 2);
- sprintf(filelist[i], "%s/%s", path, next->d_name);
- i++;
- }
- closedir(dir);
-
- if (*longest > COLS - 1)
- *longest = COLS - 1;
-
- return filelist;
-}
-
-/* Our browser function. inpath is the path to start browsing from */
-char *do_browser(const char *inpath)
-{
- struct stat st;
- char *foo, *retval = NULL;
- static char *path = NULL;
- int numents = 0, i = 0, j = 0, kbinput = -1, meta, longest = 0;
- int abort = 0, col = 0, selected = 0, editline = 0, width = 0;
- int filecols = 0, lineno = 0;
- char **filelist = (char **)NULL;
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- MEVENT mevent;
-#endif
-
- assert(inpath != NULL);
-
- /* If path isn't the same as inpath, we are being passed a new
- dir as an arg. We free it here so it will be copied from
- inpath below */
- if (path != NULL && strcmp(path, inpath)) {
- free(path);
- path = NULL;
- }
-
- /* if path doesn't exist, make it so */
- if (path == NULL)
- path = mallocstrcpy(NULL, inpath);
-
- filelist = browser_init(path, &longest, &numents);
- foo = charalloc(longest + 8);
-
- /* Sort the list by directory first, then alphabetically */
- qsort(filelist, numents, sizeof(char *), diralphasort);
-
- titlebar(path);
- bottombars(browser_list);
- curs_set(0);
- wmove(edit, 0, 0);
- i = 0;
- width = 0;
- filecols = 0;
-
- /* Loop invariant: Microsoft sucks. */
- do {
- char *new_path;
- /* Used by the Go To Directory prompt. */
-
- blank_statusbar_refresh();
-
-#if !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
- currshortcut = browser_list;
-#endif
-
- editline = 0;
- col = 0;
-
- /* Compute line number we're on now, so we don't divide by zero later */
- lineno = selected;
- if (width != 0)
- lineno /= width;
-
- switch (kbinput) {
-
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- case KEY_MOUSE:
- if (getmouse(&mevent) == ERR)
- return retval;
-
- /* If they clicked in the edit window, they probably clicked
- on a file */
- if (wenclose(edit, mevent.y, mevent.x)) {
- int selectedbackup = selected;
-
- mevent.y -= 2;
-
- /* Longest is the width of each column. There are two
- * spaces between each column. */
- selected = (lineno / editwinrows) * editwinrows * width
- + mevent.y * width + mevent.x / (longest + 2);
-
- /* If they clicked beyond the end of a row, select the
- * end of that row. */
- if (mevent.x > width * (longest + 2))
- selected--;
-
- /* If we're off the screen, reset to the last item.
- If we clicked where we did last time, select this name! */
- if (selected > numents - 1)
- selected = numents - 1;
- else if (selectedbackup == selected)
- ungetch('s'); /* Unget the 'select' key */
- } else /* Must be clicking a shortcut */
- do_mouse();
-
- break;
-#endif
- case NANO_UP_KEY:
- if (selected - width >= 0)
- selected -= width;
- break;
- case NANO_BACK_KEY:
- if (selected > 0)
- selected--;
- break;
- case NANO_DOWN_KEY:
- if (selected + width <= numents - 1)
- selected += width;
- break;
- case NANO_FORWARD_KEY:
- if (selected < numents - 1)
- selected++;
- break;
- case NANO_PREVPAGE_KEY:
- case NANO_PREVPAGE_FKEY:
- case '-':
- if (selected >= (editwinrows + lineno % editwinrows) * width)
- selected -= (editwinrows + lineno % editwinrows) * width;
- else
- selected = 0;
- break;
- case NANO_NEXTPAGE_KEY:
- case NANO_NEXTPAGE_FKEY:
- case ' ':
- selected += (editwinrows - lineno % editwinrows) * width;
- if (selected >= numents)
- selected = numents - 1;
- break;
- case NANO_HELP_KEY:
- case NANO_HELP_FKEY:
- do_help();
- break;
- case NANO_ENTER_KEY:
- case 's': /* More Pico compatibility */
- case 'S':
- /* You can't cd up from / */
- if (!strcmp(filelist[selected], "/..") && !strcmp(path, "/")) {
- statusbar(_("Can't move up a directory"));
- beep();
- break;
- }
-
-#ifndef DISABLE_OPERATINGDIR
- /*
- * Note: the selected file can be outside the operating
- * directory if it is .. or if it is a symlink to a directory
- * outside the opdir. */
- if (check_operating_dir(filelist[selected], FALSE)) {
- statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
- beep();
- break;
- }
-#endif
-
- if (stat(filelist[selected], &st) == -1) {
- statusbar(_("Can't open \"%s\": %s"), filelist[selected], strerror(errno));
- beep();
- break;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- retval = mallocstrcpy(retval, filelist[selected]);
- abort = 1;
- break;
- }
-
- new_path = mallocstrcpy(NULL, filelist[selected]);
-
- if (strcmp("..", tail(new_path)) == 0) {
- /* They want to go up a level, so strip off .. and the
- current dir */
- striponedir(new_path);
- /* SPK for '.' path, get the current path via getcwd */
- if (strcmp(new_path, ".") == 0) {
- free(new_path);
- new_path = getcwd(NULL, PATH_MAX + 1);
- }
- striponedir(new_path);
- }
-
- if (!readable_dir(new_path)) {
- /* We can't open this dir for some reason. Complain */
- statusbar(_("Can't open \"%s\": %s"), new_path, strerror(errno));
- free(new_path);
- break;
- }
-
- free_charptrarray(filelist, numents);
- free(foo);
- free(path);
- path = new_path;
- return do_browser(path);
-
- /* Goto a specific directory */
- case NANO_GOTO_KEY:
- case NANO_GOTO_FKEY:
- curs_set(1);
- j = statusq(0, gotodir_list, "",
-#ifndef NANO_SMALL
- 0,
-#endif
- _("Goto Directory"));
- bottombars(browser_list);
- curs_set(0);
-
- if (j < 0) {
- statusbar(_("Goto Cancelled"));
- break;
- }
-
- new_path = real_dir_from_tilde(answer);
-
- if (new_path[0] != '/') {
- new_path = charealloc(new_path, strlen(path) + strlen(answer) + 2);
- sprintf(new_path, "%s/%s", path, answer);
- }
-
-#ifndef DISABLE_OPERATINGDIR
- if (check_operating_dir(new_path, FALSE)) {
- statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
- free(new_path);
- break;
- }
-#endif
-
- if (!readable_dir(new_path)) {
- /* We can't open this dir for some reason. Complain */
- statusbar(_("Can't open \"%s\": %s"), answer, strerror(errno));
- free(new_path);
- break;
- }
-
- /* Start over again with the new path value */
- free_charptrarray(filelist, numents);
- free(foo);
- free(path);
- path = new_path;
- return do_browser(path);
-
- /* Stuff we want to abort the browser */
- case 'e': /* Pico compatibility, yeech */
- case 'E':
- case NANO_CANCEL_KEY:
- case NANO_EXIT_KEY:
- case NANO_EXIT_FKEY:
- abort = 1;
- break;
- }
- if (abort)
- break;
-
- blank_edit();
-
- if (width != 0)
- i = width * editwinrows * ((selected / width) / editwinrows);
- else
- i = 0;
-
- wmove(edit, 0, 0);
- for (j = i; j < numents && editline <= editwinrows - 1; j++) {
- filecols++;
-
- strncpy(foo, tail(filelist[j]), strlen(tail(filelist[j])) + 1);
- while (strlen(foo) < longest)
- strcat(foo, " ");
- col += strlen(foo);
-
- /* Put file info in the string also */
- /* We use lstat here to detect links; then, if we find a
- symlink, we examine it via stat() to see if it is a
- directory or just a file symlink */
- lstat(filelist[j], &st);
- if (S_ISDIR(st.st_mode))
- strcpy(foo + longest - 5, "(dir)");
- else {
- if (S_ISLNK(st.st_mode)) {
- /* Aha! It's a symlink! Now, is it a dir? If so,
- mark it as such */
- st = filestat(filelist[j]);
- if (S_ISDIR(st.st_mode))
- strcpy(foo + longest - 5, "(dir)");
- else
- strcpy(foo + longest - 2, "--");
- } else if (st.st_size < (1 << 10)) /* less than 1 K */
- sprintf(foo + longest - 7, "%4d B",
- (int) st.st_size);
- else if (st.st_size >= (1 << 30)) /* at least 1 gig */
- sprintf(foo + longest - 7, "%4d GB",
- (int) st.st_size >> 30);
- else if (st.st_size >= (1 << 20)) /* at least 1 meg */
- sprintf(foo + longest - 7, "%4d MB",
- (int) st.st_size >> 20);
- else /* It's more than 1 k and less than a meg */
- sprintf(foo + longest - 7, "%4d KB",
- (int) st.st_size >> 10);
- }
-
- /* Highlight the currently selected file/dir */
- if (j == selected)
- wattron(edit, A_REVERSE);
- waddstr(edit, foo);
- if (j == selected)
- wattroff(edit, A_REVERSE);
-
- /* And add some space between the cols */
- waddstr(edit, " ");
- col += 2;
-
- /* And if the next entry isn't going to fit on the
- line, move to the next one */
- if (col > COLS - longest) {
- editline++;
- wmove(edit, editline, 0);
- col = 0;
- if (width == 0)
- width = filecols;
- }
- }
- wrefresh(edit);
- } while ((kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE))) != NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
- curs_set(1);
- blank_edit();
- titlebar(NULL);
- edit_refresh();
-
- /* cleanup */
- free_charptrarray(filelist, numents);
- free(foo);
- return retval;
-}
-
-/* Browser front end, checks to see if inpath has a dir in it and, if so,
- starts do_browser from there, else from the current dir */
-char *do_browse_from(const char *inpath)
-{
- struct stat st;
- char *bob;
- /* The result of do_browser; the selected file name. */
- char *path;
- /* inpath, tilde expanded. */
-
- assert(inpath != NULL);
-
- path = real_dir_from_tilde(inpath);
-
- /*
- * Perhaps path is a directory. If so, we will pass that to
- * do_browser. Otherwise, perhaps path is a directory / a file. So
- * we try stripping off the last path element. If it still isn't a
- * directory, just use the current directory. */
-
- if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
- striponedir(path);
- if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
- free(path);
- path = getcwd(NULL, PATH_MAX + 1);
- }
- }
-
-#ifndef DISABLE_OPERATINGDIR
- /* If the resulting path isn't in the operating directory, use that. */
- if (check_operating_dir(path, FALSE))
- path = mallocstrcpy(path, operating_dir);
-#endif
-
- if (!readable_dir(path)) {
- beep();
- bob = NULL;
- } else
- bob = do_browser(path);
- free(path);
- return bob;
-}
-#endif /* !DISABLE_BROWSER */
-
-#ifndef NANO_SMALL
-#ifdef ENABLE_NANORC
-void load_history(void)
-{
- FILE *hist;
- const struct passwd *userage = NULL;
- static char *nanohist;
- char *buf, *ptr;
- char *homenv = getenv("HOME");
- historyheadtype *history = &search_history;
-
-
- if (homenv != NULL) {
- nanohist = charealloc(nanohist, strlen(homenv) + 15);
- sprintf(nanohist, "%s/.nano_history", homenv);
- } else {
- userage = getpwuid(geteuid());
- endpwent();
- nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
- sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
- }
-
- /* assume do_rcfile has reported missing home dir */
-
- if (homenv != NULL || userage != NULL) {
- hist = fopen(nanohist, "r");
- if (hist == NULL) {
- if (errno != ENOENT) {
- /* Don't save history when we quit. */
- UNSET(HISTORYLOG);
- rcfile_error(_("Unable to open ~/.nano_history file, %s"), strerror(errno));
- }
- free(nanohist);
- } else {
- buf = charalloc(1024);
- while (fgets(buf, 1023, hist) != 0) {
- ptr = buf;
- while (*ptr != '\n' && *ptr != '\0' && ptr < buf + 1023)
- ptr++;
- *ptr = '\0';
- if (strlen(buf))
- update_history(history, buf);
- else
- history = &replace_history;
- }
- fclose(hist);
- free(buf);
- free(nanohist);
- UNSET(HISTORY_CHANGED);
- }
- }
-}
-
-/* save histories to ~/.nano_history */
-void save_history(void)
-{
- FILE *hist;
- const struct passwd *userage = NULL;
- char *nanohist = NULL;
- char *homenv = getenv("HOME");
- historytype *h;
-
- /* don't save unchanged or empty histories */
- if (!((search_history.count || replace_history.count) &&
- ISSET(HISTORY_CHANGED) && !ISSET(VIEW_MODE)))
- return;
-
- if (homenv != NULL) {
- nanohist = charealloc(nanohist, strlen(homenv) + 15);
- sprintf(nanohist, "%s/.nano_history", homenv);
- } else {
- userage = getpwuid(geteuid());
- endpwent();
- nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
- sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
- }
-
- if (homenv != NULL || userage != NULL) {
- hist = fopen(nanohist, "wb");
- if (hist == NULL) {
- rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
- } else {
- /* set rw only by owner for security ?? */
- chmod(nanohist, S_IRUSR | S_IWUSR);
- /* write oldest first */
- for (h = search_history.tail ; h->prev ; h = h->prev) {
- h->data = charealloc(h->data, strlen(h->data) + 2);
- strcat(h->data, "\n");
- if (fputs(h->data, hist) == EOF) {
- rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
- goto come_from;
- }
- }
- if (fputs("\n", hist) == EOF) {
- rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
- goto come_from;
- }
- for (h = replace_history.tail ; h->prev ; h = h->prev) {
- h->data = charealloc(h->data, strlen(h->data) + 2);
- strcat(h->data, "\n");
- if (fputs(h->data, hist) == EOF) {
- rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
- goto come_from;
- }
- }
- come_from:
- fclose(hist);
- }
- free(nanohist);
- }
-}
-#endif /* ENABLE_NANORC */
-#endif /* !NANO_SMALL */
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * global.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <assert.h>
-#include <sys/stat.h>
-#include "proto.h"
-#include "nano.h"
-
-/* Global variables */
-
-/* wrap_at might be set in rcfile.c or nano.c */
-int wrap_at = -CHARS_FROM_EOL;/* Right justified fill value, allows resize */
-char *last_search = NULL; /* Last string we searched for */
-char *last_replace = NULL; /* Last replacement string */
-int search_last_line; /* Is this the last search line? */
-int search_offscreen; /* Search lines not displayed */
-
-int flags = 0; /* Our new flag containing many options */
-WINDOW *edit; /* The file portion of the editor */
-WINDOW *topwin; /* Top line of screen */
-WINDOW *bottomwin; /* Bottom buffer */
-char *filename = NULL; /* Name of the file */
-
-#ifndef NANO_SMALL
-struct stat originalfilestat; /* Stat for the file as we loaded it */
-#endif
-
-int editwinrows = 0; /* How many rows long is the edit
- window? */
-filestruct *current; /* Current buffer pointer */
-int current_x = 0, current_y = 0; /* Current position of X and Y in
- the editor - relative to edit
- window (0,0) */
-filestruct *fileage = NULL; /* Our file buffer */
-filestruct *edittop = NULL; /* Pointer to the top of the edit
- buffer with respect to the
- file struct */
-filestruct *editbot = NULL; /* Same for the bottom */
-filestruct *filebot = NULL; /* Last node in the file struct */
-filestruct *cutbuffer = NULL; /* A place to store cut text */
-
-#ifdef ENABLE_MULTIBUFFER
-openfilestruct *open_files = NULL; /* The list of open files */
-#endif
-
-#ifndef DISABLE_JUSTIFY
-char *quotestr = NULL; /* Quote string. The default value is
- set in main(). */
-#endif
-
-int resetstatuspos; /* Hack for resetting the status bar
- cursor position */
-char *answer = NULL; /* Answer str to many questions */
-int totlines = 0; /* Total number of lines in the file */
-long totsize = 0; /* Total number of bytes in the file */
-int placewewant = 0; /* The column we'd like the cursor
- to jump to when we go to the
- next or previous line */
-
-int tabsize = -1; /* Our internal tabsize variable. The
- default value 8 is set in main(). */
-
-char *hblank = NULL; /* A horizontal blank line */
-#ifndef DISABLE_HELP
-char *help_text; /* The text in the help window */
-#endif
-
-/* More stuff for the marker select */
-
-#ifndef NANO_SMALL
-filestruct *mark_beginbuf; /* the begin marker buffer */
-int mark_beginx; /* X value in the string to start */
-#endif
-
-#ifndef DISABLE_OPERATINGDIR
-char *operating_dir = NULL; /* Operating directory, which we can't */
-char *full_operating_dir = NULL;/* go higher than */
-#endif
-
-#ifndef DISABLE_SPELLER
-char *alt_speller = NULL; /* Alternative spell command */
-#endif
-
-shortcut *main_list = NULL;
-shortcut *whereis_list = NULL;
-shortcut *replace_list = NULL;
-shortcut *replace_list_2 = NULL; /* 2nd half of replace dialog */
-shortcut *goto_list = NULL;
-shortcut *writefile_list = NULL;
-shortcut *insertfile_list = NULL;
-#ifndef DISABLE_HELP
-shortcut *help_list = NULL;
-#endif
-#ifndef DISABLE_SPELLER
-shortcut *spell_list = NULL;
-#endif
-#ifndef NANO_SMALL
-shortcut *extcmd_list = NULL;
-#endif
-#ifndef DISABLE_BROWSER
-shortcut *browser_list = NULL;
-shortcut *gotodir_list = NULL;
-#endif
-
-#ifdef ENABLE_COLOR
-const colortype *colorstrings = NULL;
-syntaxtype *syntaxes = NULL;
-char *syntaxstr = NULL;
-#endif
-
-#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
-const shortcut *currshortcut; /* Current shortcut list we're using */
-#endif
-
-#ifndef NANO_SMALL
-toggle *toggles = NULL;
-#endif
-
-#ifndef NANO_SMALL
-historyheadtype search_history;
-historyheadtype replace_history;
-#endif
-
-/* Regular expressions */
-
-#ifdef HAVE_REGEX_H
-regex_t search_regexp; /* Global to store compiled search regexp */
-regmatch_t regmatches[10]; /* Match positions for parenthetical
- subexpressions, max of 10 */
-#endif
-
-int curses_ended = FALSE; /* Indicates to statusbar() to simply
- * write to stderr, since endwin() has
- * ended curses mode. */
-
-
-int length_of_list(const shortcut *s)
-{
- int i = 0;
-
- for (; s != NULL; s = s->next)
- i++;
- return i;
-}
-
-/* Initialize a struct *without* our lovely braces =( */
-void sc_init_one(shortcut **shortcutage, int key, const char *desc,
-#ifndef DISABLE_HELP
- const char *help,
-#endif
- int alt, int misc1, int misc2, int view, int (*func) (void))
-{
- shortcut *s;
-
- if (*shortcutage == NULL) {
- *shortcutage = (shortcut *)nmalloc(sizeof(shortcut));
- s = *shortcutage;
- } else {
- for (s = *shortcutage; s->next != NULL; s = s->next)
- ;
- s->next = (shortcut *)nmalloc(sizeof(shortcut));
- s = s->next;
- }
-
- s->val = key;
- s->desc = desc;
-#ifndef DISABLE_HELP
- s->help = help;
-#endif
- s->altval = alt;
- s->misc1 = misc1;
- s->misc2 = misc2;
- s->viewok = view;
- s->func = func;
- s->next = NULL;
-}
-
-#ifndef NANO_SMALL
-/* Create one new toggle structure, at the end of the toggles
- * linked list. */
-void toggle_init_one(int val, const char *desc, int flag)
-{
- toggle *u;
-
- if (toggles == NULL) {
- toggles = (toggle *)nmalloc(sizeof(toggle));
- u = toggles;
- } else {
- for (u = toggles; u->next != NULL; u = u->next)
- ;
- u->next = (toggle *)nmalloc(sizeof(toggle));
- u = u->next;
- }
-
- u->val = val;
- u->desc = desc;
- u->flag = flag;
- u->next = NULL;
-}
-
-void toggle_init(void)
-{
- char *toggle_const_msg, *toggle_autoindent_msg, *toggle_suspend_msg,
- *toggle_nohelp_msg, *toggle_cuttoend_msg,
- *toggle_noconvert_msg, *toggle_dos_msg, *toggle_mac_msg,
- *toggle_backup_msg, *toggle_smooth_msg;
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- char *toggle_mouse_msg;
-#endif
-#ifndef DISABLE_WRAPPING
- char *toggle_wrap_msg;
-#endif
-#ifdef ENABLE_MULTIBUFFER
- char *toggle_load_msg;
-#endif
-#ifdef ENABLE_COLOR
- char *toggle_syntax_msg;
-#endif
-
- /* There is no need to reinitialize the toggles. They can't
- change. */
- if (toggles != NULL)
- return;
-
- toggle_const_msg = _("Constant cursor position");
- toggle_autoindent_msg = _("Auto indent");
- toggle_suspend_msg = _("Suspend");
- toggle_nohelp_msg = _("Help mode");
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- toggle_mouse_msg = _("Mouse support");
-#endif
- toggle_cuttoend_msg = _("Cut to end");
- toggle_noconvert_msg = _("No conversion from DOS/Mac format");
- toggle_dos_msg = _("Writing file in DOS format");
- toggle_mac_msg = _("Writing file in Mac format");
- toggle_backup_msg = _("Backing up file");
- toggle_smooth_msg = _("Smooth scrolling");
-#ifdef ENABLE_COLOR
- toggle_syntax_msg = _("Color syntax highlighting");
-#endif
-#ifndef DISABLE_WRAPPING
- toggle_wrap_msg = _("Auto line wrap");
-#endif
-#ifdef ENABLE_MULTIBUFFER
- toggle_load_msg = _("Multiple file buffers");
-#endif
-
- toggle_init_one(TOGGLE_CONST_KEY, toggle_const_msg, CONSTUPDATE);
- toggle_init_one(TOGGLE_AUTOINDENT_KEY, toggle_autoindent_msg, AUTOINDENT);
- toggle_init_one(TOGGLE_SUSPEND_KEY, toggle_suspend_msg, SUSPEND);
- toggle_init_one(TOGGLE_NOHELP_KEY, toggle_nohelp_msg, NO_HELP);
-#ifndef DISABLE_WRAPPING
- toggle_init_one(TOGGLE_WRAP_KEY, toggle_wrap_msg, NO_WRAP);
-#endif
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- toggle_init_one(TOGGLE_MOUSE_KEY, toggle_mouse_msg, USE_MOUSE);
-#endif
- toggle_init_one(TOGGLE_CUTTOEND_KEY, toggle_cuttoend_msg, CUT_TO_END);
-#ifdef ENABLE_MULTIBUFFER
- toggle_init_one(TOGGLE_LOAD_KEY, toggle_load_msg, MULTIBUFFER);
-#endif
- toggle_init_one(TOGGLE_NOCONVERT_KEY, toggle_noconvert_msg, NO_CONVERT);
- toggle_init_one(TOGGLE_DOS_KEY, toggle_dos_msg, DOS_FILE);
- toggle_init_one(TOGGLE_MAC_KEY, toggle_mac_msg, MAC_FILE);
- toggle_init_one(TOGGLE_BACKUP_KEY, toggle_backup_msg, BACKUP_FILE);
- toggle_init_one(TOGGLE_SMOOTH_KEY, toggle_smooth_msg, SMOOTHSCROLL);
-#ifdef ENABLE_COLOR
- toggle_init_one(TOGGLE_SYNTAX_KEY, toggle_syntax_msg, COLOR_SYNTAX);
-#endif
-}
-
-#ifdef DEBUG
-/* Deallocate all of the toggles. */
-void free_toggles(void)
-{
- while (toggles != NULL) {
- toggle *pt = toggles; /* Think "previous toggle" */
-
- toggles = toggles->next;
- free(pt);
- }
-}
-#endif
-#endif /* !NANO_SMALL */
-
-/* Deallocate the given shortcut. */
-void free_shortcutage(shortcut **shortcutage)
-{
- assert(shortcutage != NULL);
- while (*shortcutage != NULL) {
- shortcut *ps = *shortcutage;
- *shortcutage = (*shortcutage)->next;
- free(ps);
- }
-}
-
-void shortcut_init(int unjustify)
-{
-#ifndef DISABLE_HELP
- const char *nano_help_msg = "", *nano_writeout_msg = "", *nano_exit_msg =
- "", *nano_goto_msg = "", *nano_justify_msg =
- "", *nano_replace_msg = "", *nano_insert_msg =
- "", *nano_whereis_msg = "", *nano_whereis_next_msg =
- "", *nano_prevpage_msg = "", *nano_nextpage_msg =
- "", *nano_cut_msg = "", *nano_uncut_msg =
- "", *nano_cursorpos_msg = "", *nano_spell_msg =
- "", *nano_up_msg = "", *nano_down_msg =
- "", *nano_forward_msg = "", *nano_back_msg = "", *nano_home_msg =
- "", *nano_end_msg = "", *nano_firstline_msg =
- "", *nano_lastline_msg = "", *nano_refresh_msg =
- "", *nano_mark_msg = "", *nano_delete_msg =
- "", *nano_backspace_msg = "", *nano_tab_msg =
- "", *nano_enter_msg = "", *nano_cancel_msg =
- "", *nano_unjustify_msg = "", *nano_append_msg =
- "", *nano_prepend_msg = "", *nano_tofiles_msg =
- "", *nano_gotodir_msg = "", *nano_case_msg =
- "", *nano_reverse_msg = "", *nano_execute_msg =
- "", *nano_dos_msg = "", *nano_mac_msg =
- "", *nano_backup_msg = "", *nano_editstr_msg =
- "", *nano_parabegin_msg = "", *nano_paraend_msg = "";
-
-#ifdef ENABLE_MULTIBUFFER
- const char *nano_openprev_msg = "", *nano_opennext_msg =
- "", *nano_multibuffer_msg = "";
-#endif
-#ifdef HAVE_REGEX_H
- const char *nano_regexp_msg = "", *nano_bracket_msg = "";
-#endif
-
- nano_help_msg = _("Invoke the help menu");
- nano_writeout_msg = _("Write the current file to disk");
-#ifdef ENABLE_MULTIBUFFER
- nano_exit_msg = _("Close current file buffer/Exit from nano");
-#else
- nano_exit_msg = _("Exit from nano");
-#endif
- nano_goto_msg = _("Go to a specific line number");
- nano_justify_msg = _("Justify the current paragraph");
- nano_unjustify_msg = _("Unjustify after a justify");
- nano_replace_msg = _("Replace text within the editor");
- nano_insert_msg = _("Insert another file into the current one");
- nano_whereis_msg = _("Search for text within the editor");
- nano_whereis_next_msg = _("Repeat last search");
- nano_prevpage_msg = _("Move to the previous screen");
- nano_nextpage_msg = _("Move to the next screen");
- nano_cut_msg = _("Cut the current line and store it in the cutbuffer");
- nano_uncut_msg = _("Uncut from the cutbuffer into the current line");
- nano_cursorpos_msg = _("Show the position of the cursor");
- nano_spell_msg = _("Invoke the spell checker, if available");
- nano_up_msg = _("Move up one line");
- nano_down_msg = _("Move down one line");
- nano_forward_msg = _("Move forward one character");
- nano_back_msg = _("Move back one character");
- nano_home_msg = _("Move to the beginning of the current line");
- nano_end_msg = _("Move to the end of the current line");
- nano_firstline_msg = _("Go to the first line of the file");
- nano_lastline_msg = _("Go to the last line of the file");
- nano_refresh_msg = _("Refresh (redraw) the current screen");
- nano_mark_msg = _("Mark text at the current cursor location");
- nano_delete_msg = _("Delete the character under the cursor");
- nano_backspace_msg =
- _("Delete the character to the left of the cursor");
- nano_tab_msg = _("Insert a tab character");
- nano_enter_msg = _("Insert a carriage return at the cursor position");
- nano_case_msg =
- _("Make the current search or replace case (in)sensitive");
- nano_tofiles_msg = _("Go to file browser");
- nano_execute_msg = _("Execute external command");
- nano_gotodir_msg = _("Go to directory");
- nano_cancel_msg = _("Cancel the current function");
- nano_append_msg = _("Append to the current file");
- nano_prepend_msg = _("Prepend to the current file");
- nano_reverse_msg = _("Search backwards");
- nano_dos_msg = _("Write file out in DOS format");
- nano_mac_msg = _("Write file out in Mac format");
- nano_backup_msg = _("Back up original file when saving");
- nano_editstr_msg = _("Edit the previous search/replace strings");
- nano_parabegin_msg = _("Go to the beginning of the current paragraph");
- nano_paraend_msg = _("Go to the end of the current paragraph");
-#ifdef HAVE_REGEX_H
- nano_regexp_msg = _("Use regular expressions");
- nano_bracket_msg = _("Find other bracket");
-#endif
-#ifdef ENABLE_MULTIBUFFER
- nano_openprev_msg = _("Switch to previous file buffer");
- nano_opennext_msg = _("Switch to next file buffer");
- nano_multibuffer_msg = _("Toggle insert into new file buffer");
-#endif
-#endif /* !DISABLE_HELP */
-
- free_shortcutage(&main_list);
-
-/* The following macro is to be used in calling sc_init_one. The point is
- * that sc_init_one takes 9 arguments, unless DISABLE_HELP is defined,
- * when the fourth one should not be there. */
-#ifdef DISABLE_HELP
-# define IFHELP(help, nextvar) nextvar
-#else
-# define IFHELP(help, nextvar) help, nextvar
-#endif
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), NANO_HELP_FKEY, 0, VIEW,
- do_help);
-
-#ifdef ENABLE_MULTIBUFFER
- if (open_files != NULL && (open_files->prev != NULL || open_files->next != NULL))
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_EXIT_KEY, _("Close"),
- IFHELP(nano_exit_msg, 0), NANO_EXIT_FKEY, 0, VIEW,
- do_exit);
- else
-#endif
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_EXIT_KEY, _("Exit"),
- IFHELP(nano_exit_msg, 0), NANO_EXIT_FKEY, 0, VIEW,
- do_exit);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_WRITEOUT_KEY, _("WriteOut"),
- IFHELP(nano_writeout_msg, 0),
- NANO_WRITEOUT_FKEY, 0, NOVIEW, do_writeout_void);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_JUSTIFY_KEY, _("Justify"),
- IFHELP(nano_justify_msg, 0), NANO_JUSTIFY_FKEY, 0,
- NOVIEW, do_justify);
-
- /* this is so we can view multiple files */
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_INSERTFILE_KEY, _("Read File"),
- IFHELP(nano_insert_msg, 0), NANO_INSERTFILE_FKEY, 0,
-#ifdef ENABLE_MULTIBUFFER
- VIEW
-#else
- NOVIEW
-#endif
- , do_insertfile_void);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_WHEREIS_KEY, _("Where Is"),
- IFHELP(nano_whereis_msg, 0),
- NANO_WHEREIS_FKEY, 0, VIEW, do_search);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_PREVPAGE_KEY, _("Prev Page"),
- IFHELP(nano_prevpage_msg, 0),
- NANO_PREVPAGE_FKEY, KEY_PPAGE, VIEW, do_page_up);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_NEXTPAGE_KEY, _("Next Page"),
- IFHELP(nano_nextpage_msg, 0),
- NANO_NEXTPAGE_FKEY, KEY_NPAGE, VIEW, do_page_down);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_CUT_KEY, _("Cut Text"),
- IFHELP(nano_cut_msg, 0),
- NANO_CUT_FKEY, 0, NOVIEW, do_cut_text);
-
- if (unjustify)
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_UNJUSTIFY_KEY, _("UnJustify"),
- IFHELP(nano_unjustify_msg, 0),
- 0, 0, NOVIEW, do_uncut_text);
- else
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_UNCUT_KEY, _("UnCut Txt"),
- IFHELP(nano_uncut_msg, 0),
- NANO_UNCUT_FKEY, 0, NOVIEW, do_uncut_text);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_CURSORPOS_KEY, _("Cur Pos"),
- IFHELP(nano_cursorpos_msg, 0),
- NANO_CURSORPOS_FKEY, 0, VIEW, do_cursorpos_void);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&main_list, NANO_SPELL_KEY, _("To Spell"),
- IFHELP(nano_spell_msg, 0),
- NANO_SPELL_FKEY, 0, NOVIEW, do_spell);
-
- sc_init_one(&main_list, NANO_UP_KEY, _("Up"),
- IFHELP(nano_up_msg, 0),
- KEY_UP, 0, VIEW, do_up);
-
- sc_init_one(&main_list, NANO_DOWN_KEY, _("Down"),
- IFHELP(nano_down_msg, 0),
- KEY_DOWN, 0, VIEW, do_down);
-
- sc_init_one(&main_list, NANO_FORWARD_KEY, _("Forward"),
- IFHELP(nano_forward_msg, 0),
- KEY_RIGHT, 0, VIEW, do_right);
-
- sc_init_one(&main_list, NANO_BACK_KEY, _("Back"),
- IFHELP(nano_back_msg, 0),
- KEY_LEFT, 0, VIEW, do_left);
-
- sc_init_one(&main_list, NANO_HOME_KEY, _("Home"),
- IFHELP(nano_home_msg, 0),
- KEY_HOME, 362, VIEW, do_home);
-
- sc_init_one(&main_list, NANO_END_KEY, _("End"),
- IFHELP(nano_end_msg, 0),
- KEY_END, 385, VIEW, do_end);
-
- sc_init_one(&main_list, NANO_REFRESH_KEY, _("Refresh"),
- IFHELP(nano_refresh_msg, 0),
- 0, 0, VIEW, total_refresh);
-
- sc_init_one(&main_list, NANO_MARK_KEY, _("Mark Text"),
- IFHELP(nano_mark_msg, NANO_ALT_MARK_KEY),
- 0, 0, NOVIEW, do_mark);
-
- sc_init_one(&main_list, NANO_DELETE_KEY, _("Delete"),
- IFHELP(nano_delete_msg, 0), KEY_DC,
- NANO_CONTROL_D, NOVIEW, do_delete);
-
- sc_init_one(&main_list, NANO_BACKSPACE_KEY, _("Backspace"),
- IFHELP(nano_backspace_msg, 0),
- KEY_BACKSPACE, 127, NOVIEW, do_backspace);
-
- sc_init_one(&main_list, NANO_TAB_KEY, _("Tab"),
- IFHELP(nano_tab_msg, 0), 0, 0, NOVIEW, do_tab);
-
- sc_init_one(&main_list, NANO_REPLACE_KEY, _("Replace"),
- IFHELP(nano_replace_msg, NANO_ALT_REPLACE_KEY),
- NANO_REPLACE_FKEY, 0, NOVIEW, do_replace);
-
- sc_init_one(&main_list, NANO_ENTER_KEY, _("Enter"),
- IFHELP(nano_enter_msg, 0),
- KEY_ENTER, NANO_CONTROL_M, NOVIEW, do_enter);
-
- sc_init_one(&main_list, NANO_GOTO_KEY, _("Go To Line"),
- IFHELP(nano_goto_msg, NANO_ALT_GOTO_KEY),
- NANO_GOTO_FKEY, 0, VIEW, do_gotoline_void);
-
-#ifndef NANO_SMALL
- sc_init_one(&main_list, NANO_NEXTWORD_KEY, _("Next Word"),
- IFHELP(_("Move forward one word"), 0),
- 0, 0, VIEW, do_next_word);
-
- sc_init_one(&main_list, -9, _("Prev Word"),
- IFHELP(_("Move backward one word"), NANO_PREVWORD_KEY), 0, 0,
- VIEW, do_prev_word);
-#endif
-#if !defined(NANO_SMALL) && defined(HAVE_REGEX_H)
- sc_init_one(&main_list, -9, _("Find Other Bracket"),
- IFHELP(nano_bracket_msg, NANO_BRACKET_KEY),
- 0, 0, VIEW, do_find_bracket);
-#endif
-
- sc_init_one(&main_list, -9, _("Where Is Next"),
- IFHELP(nano_whereis_next_msg, NANO_WHEREIS_NEXT_KEY), 0, 0,
- NOVIEW, do_research);
-
-#ifdef ENABLE_MULTIBUFFER
- sc_init_one(&main_list, -9, _("Previous File"),
- IFHELP(nano_openprev_msg, NANO_OPENPREV_KEY),
- 0, 0, VIEW, open_prevfile_void);
- sc_init_one(&main_list, -9, _("Next File"),
- IFHELP(nano_opennext_msg, NANO_OPENNEXT_KEY),
- 0, 0, VIEW, open_nextfile_void);
-#endif
-
- free_shortcutage(&whereis_list);
-
- sc_init_one(&whereis_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, NANO_FIRSTLINE_KEY, _("First Line"),
- IFHELP(nano_firstline_msg, 0),
- 0, 0, VIEW, do_first_line);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, NANO_LASTLINE_KEY, _("Last Line"),
- IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, NANO_OTHERSEARCH_KEY, _("Replace"),
- IFHELP(nano_replace_msg, 0), 0, 0, VIEW, do_replace);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, NANO_FROMSEARCHTOGOTO_KEY, _("Go To Line"),
- IFHELP(nano_goto_msg, 0), 0, 0, VIEW, do_gotoline_void);
-
-#ifndef DISABLE_JUSTIFY
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, NANO_PARABEGIN_KEY, _("Beg of Par"),
- IFHELP(nano_parabegin_msg, 0), 0, 0, VIEW, do_para_begin);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, NANO_PARAEND_KEY, _("End of Par"),
- IFHELP(nano_paraend_msg, 0), 0, 0, VIEW, do_para_end);
-#endif
-
-#ifndef NANO_SMALL
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, TOGGLE_CASE_KEY, _("Case Sens"),
- IFHELP(nano_case_msg, 0), 0, 0, VIEW, 0);
-
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, TOGGLE_BACKWARDS_KEY, _("Direction"),
- IFHELP(nano_reverse_msg, 0), 0, 0, VIEW, 0);
-
-#ifdef HAVE_REGEX_H
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, TOGGLE_REGEXP_KEY, _("Regexp"),
- IFHELP(nano_regexp_msg, 0), 0, 0, VIEW, 0);
-#endif
-
-#ifndef NANO_SMALL
- /* Translators: try to keep this string under 10 characters long */
- sc_init_one(&whereis_list, KEY_UP, _("History"),
- IFHELP(nano_editstr_msg, 0), NANO_UP_KEY, 0, VIEW, 0);
-#endif
-
-#endif /* !NANO_SMALL */
-
- free_shortcutage(&replace_list);
-
- sc_init_one(&replace_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&replace_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-
- sc_init_one(&replace_list, NANO_FIRSTLINE_KEY, _("First Line"),
- IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, do_first_line);
-
- sc_init_one(&replace_list, NANO_LASTLINE_KEY, _("Last Line"),
- IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
-
- /* Translators: try to keep this string under 12 characters long */
- sc_init_one(&replace_list, NANO_OTHERSEARCH_KEY, _("No Replace"),
- IFHELP(nano_whereis_msg, 0), 0, 0, VIEW, do_search);
-
- sc_init_one(&replace_list, NANO_FROMSEARCHTOGOTO_KEY, _("Go To Line"),
- IFHELP(nano_goto_msg, 0), 0, 0, VIEW, do_gotoline_void);
-
-#ifndef NANO_SMALL
- sc_init_one(&replace_list, TOGGLE_CASE_KEY, _("Case Sens"),
- IFHELP(nano_case_msg, 0), 0, 0, VIEW, 0);
-
- sc_init_one(&replace_list, TOGGLE_BACKWARDS_KEY, _("Direction"),
- IFHELP(nano_reverse_msg, 0), 0, 0, VIEW, 0);
-
-#ifdef HAVE_REGEX_H
- sc_init_one(&replace_list, TOGGLE_REGEXP_KEY, _("Regexp"),
- IFHELP(nano_regexp_msg, 0), 0, 0, VIEW, 0);
-#endif
-
- sc_init_one(&replace_list, KEY_UP, _("History"),
- IFHELP(nano_editstr_msg, 0), NANO_UP_KEY, 0, VIEW, 0);
-#endif /* !NANO_SMALL */
-
- free_shortcutage(&replace_list_2);
-
- sc_init_one(&replace_list_2, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&replace_list_2, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-
- sc_init_one(&replace_list_2, NANO_FIRSTLINE_KEY, _("First Line"),
- IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, do_first_line);
-
- sc_init_one(&replace_list_2, NANO_LASTLINE_KEY, _("Last Line"),
- IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
-
-#ifndef NANO_SMALL
- sc_init_one(&replace_list_2, KEY_UP, _("History"),
- IFHELP(nano_editstr_msg, 0), NANO_UP_KEY, 0, VIEW, 0);
-#endif
-
- free_shortcutage(&goto_list);
-
- sc_init_one(&goto_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&goto_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-
- sc_init_one(&goto_list, NANO_FIRSTLINE_KEY, _("First Line"),
- IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, do_first_line);
-
- sc_init_one(&goto_list, NANO_LASTLINE_KEY, _("Last Line"),
- IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
-
-#ifndef DISABLE_HELP
- free_shortcutage(&help_list);
-
- sc_init_one(&help_list, NANO_PREVPAGE_KEY, _("Prev Page"),
- IFHELP(nano_prevpage_msg, 0), NANO_PREVPAGE_FKEY,
- KEY_PPAGE, VIEW, do_page_up);
-
- sc_init_one(&help_list, NANO_NEXTPAGE_KEY, _("Next Page"),
- IFHELP(nano_nextpage_msg, 0),
- NANO_NEXTPAGE_FKEY, KEY_NPAGE, VIEW, do_page_down);
-
- sc_init_one(&help_list, NANO_EXIT_KEY, _("Exit"),
- IFHELP(nano_exit_msg, 0), NANO_EXIT_FKEY, 0, VIEW,
- do_exit);
-#endif
-
- free_shortcutage(&writefile_list);
-
- sc_init_one(&writefile_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
-#ifndef DISABLE_BROWSER
- /* Translators: try to keep this string under 16 characters long */
- sc_init_one(&writefile_list, NANO_TOFILES_KEY, _("To Files"),
- IFHELP(nano_tofiles_msg, 0), 0, 0, NOVIEW, 0);
-#endif
-
-#ifndef NANO_SMALL
- /* Translators: try to keep this string under 16 characters long */
- sc_init_one(&writefile_list, TOGGLE_DOS_KEY, _("DOS Format"),
- IFHELP(nano_dos_msg, 0), 0, 0, NOVIEW, 0);
-
- /* Translators: try to keep this string under 16 characters long */
- sc_init_one(&writefile_list, TOGGLE_MAC_KEY, _("Mac Format"),
- IFHELP(nano_mac_msg, 0), 0, 0, NOVIEW, 0);
-#endif
-
- /* Translators: try to keep this string under 16 characters long */
- sc_init_one(&writefile_list, NANO_APPEND_KEY, _("Append"),
- IFHELP(nano_append_msg, 0), 0, 0, NOVIEW, 0);
-
- /* Translators: try to keep this string under 16 characters long */
- sc_init_one(&writefile_list, NANO_PREPEND_KEY, _("Prepend"),
- IFHELP(nano_prepend_msg, 0), 0, 0, NOVIEW, 0);
-
-#ifndef NANO_SMALL
- /* Translators: try to keep this string under 16 characters long */
- sc_init_one(&writefile_list, TOGGLE_BACKUP_KEY, _("Backup File"),
- IFHELP(nano_backup_msg, 0), 0, 0, NOVIEW, 0);
-#endif
-
- sc_init_one(&writefile_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-
- free_shortcutage(&insertfile_list);
-
- sc_init_one(&insertfile_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&insertfile_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-
-#ifndef DISABLE_BROWSER
- sc_init_one(&insertfile_list, NANO_TOFILES_KEY, _("To Files"),
- IFHELP(nano_tofiles_msg, 0), 0, 0, NOVIEW, 0);
-#endif
-#ifndef NANO_SMALL
- /* Translators: try to keep this string under 22 characters long */
- sc_init_one(&insertfile_list, NANO_EXTCMD_KEY, _("Execute Command"),
- IFHELP(nano_execute_msg, 0), 0, 0, NOVIEW, 0);
-#ifdef ENABLE_MULTIBUFFER
- /* Translators: try to keep this string under 22 characters long */
- sc_init_one(&insertfile_list, TOGGLE_LOAD_KEY, _("New Buffer"),
- IFHELP(nano_multibuffer_msg, 0), 0, 0, NOVIEW, 0);
-#endif
-#endif
-
-#ifndef DISABLE_SPELLER
- free_shortcutage(&spell_list);
-
- sc_init_one(&spell_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&spell_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-#endif
-
-#ifndef NANO_SMALL
- free_shortcutage(&extcmd_list);
-
- sc_init_one(&extcmd_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&extcmd_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-#endif
-
-#ifndef DISABLE_BROWSER
- free_shortcutage(&browser_list);
-
- sc_init_one(&browser_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&browser_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), NANO_EXIT_FKEY, 0, VIEW, 0);
-
- sc_init_one(&browser_list, NANO_PREVPAGE_KEY, _("Prev Page"),
- IFHELP(nano_prevpage_msg, 0), NANO_PREVPAGE_FKEY,
- KEY_PPAGE, VIEW, 0);
-
- sc_init_one(&browser_list, NANO_NEXTPAGE_KEY, _("Next Page"),
- IFHELP(nano_nextpage_msg, 0), NANO_NEXTPAGE_FKEY,
- KEY_NPAGE, VIEW, 0);
-
- /* Translators: try to keep this string under 22 characters long */
- sc_init_one(&browser_list, NANO_GOTO_KEY, _("Go To Dir"),
- IFHELP(nano_gotodir_msg, NANO_ALT_GOTO_KEY),
- NANO_GOTO_FKEY, 0, VIEW, 0);
-
- free_shortcutage(&gotodir_list);
-
- sc_init_one(&gotodir_list, NANO_HELP_KEY, _("Get Help"),
- IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
-
- sc_init_one(&gotodir_list, NANO_CANCEL_KEY, _("Cancel"),
- IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
-#endif
-
-#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
- currshortcut = main_list;
-#endif
-#ifndef NANO_SMALL
- toggle_init();
-#endif
-}
-
-/* This function is called just before calling exit(). Practically, the
- * only effect is to cause a segmentation fault if the various data
- * structures got bolloxed earlier. Thus, we don't bother having this
- * function unless debugging is turned on. */
-#ifdef DEBUG
-/* added by SPK for memory cleanup, gracefully return our malloc()s */
-void thanks_for_all_the_fish(void)
-{
-#ifndef DISABLE_JUSTIFY
- if (quotestr != NULL)
- free(quotestr);
-#endif
-#ifndef DISABLE_OPERATINGDIR
- if (operating_dir != NULL)
- free(operating_dir);
- if (full_operating_dir != NULL)
- free(full_operating_dir);
-#endif
- if (last_search != NULL)
- free(last_search);
- if (last_replace != NULL)
- free(last_replace);
- if (hblank != NULL)
- free(hblank);
-#ifndef DISABLE_SPELLER
- if (alt_speller != NULL)
- free(alt_speller);
-#endif
-#ifndef DISABLE_HELP
- if (help_text != NULL)
- free(help_text);
-#endif
- if (filename != NULL)
- free(filename);
- if (answer != NULL)
- free(answer);
- if (cutbuffer != NULL)
- free_filestruct(cutbuffer);
-
- free_shortcutage(&main_list);
- free_shortcutage(&whereis_list);
- free_shortcutage(&replace_list);
- free_shortcutage(&replace_list_2);
- free_shortcutage(&goto_list);
- free_shortcutage(&writefile_list);
- free_shortcutage(&insertfile_list);
-#ifndef DISABLE_HELP
- free_shortcutage(&help_list);
-#endif
-#ifndef DISABLE_SPELLER
- free_shortcutage(&spell_list);
-#endif
-#ifndef NANO_SMALL
- free_shortcutage(&extcmd_list);
-#endif
-#ifndef DISABLE_BROWSER
- free_shortcutage(&browser_list);
- free_shortcutage(&gotodir_list);
-#endif
-
-#ifndef NANO_SMALL
- free_toggles();
-#endif
-
-#ifdef ENABLE_MULTIBUFFER
- if (open_files != NULL) {
- /* We free the memory associated with each open file. */
- while (open_files->prev != NULL)
- open_files = open_files->prev;
- free_openfilestruct(open_files);
- }
-#else
- free_filestruct(fileage);
-#endif
-
-#ifdef ENABLE_COLOR
- free(syntaxstr);
- while (syntaxes != NULL) {
- syntaxtype *bill = syntaxes;
-
- free(syntaxes->desc);
- while (syntaxes->extensions != NULL) {
- exttype *bob = syntaxes->extensions;
-
- syntaxes->extensions = bob->next;
- regfree(&bob->val);
- free(bob);
- }
- while (syntaxes->color != NULL) {
- colortype *bob = syntaxes->color;
-
- syntaxes->color = bob->next;
- regfree(&bob->start);
- if (bob->end != NULL)
- regfree(bob->end);
- free(bob->end);
- free(bob);
- }
- syntaxes = syntaxes->next;
- free(bill);
- }
-#endif /* ENABLE_COLOR */
-#ifndef NANO_SMALL
- /* free history lists */
- free_history(&search_history);
- free_history(&replace_history);
-#endif
-}
-#endif /* DEBUG */
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * move.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-int do_home(void)
-{
- current_x = 0;
- placewewant = 0;
- update_line(current, current_x);
- return 1;
-}
-
-int do_end(void)
-{
- current_x = strlen(current->data);
- placewewant = xplustabs();
- update_line(current, current_x);
- return 1;
-}
-
-void page_up(void)
-{
- if (edittop != fileage) {
-#ifndef NANO_SMALL
- if (ISSET(SMOOTHSCROLL))
- edit_update(edittop->prev, TOP);
- else
-#endif
- {
- edit_update(edittop, CENTER);
- /* Now that we've updated the edit window, edittop might be
- at the top of the file; if so, just move the cursor up one
- line and don't center it. */
- if (edittop != fileage)
- center_cursor();
- else
- reset_cursor();
- }
- } else
- current_y = 0;
-
- update_cursor();
-}
-
-int do_page_up(void)
-{
- int i;
-
- wrap_reset();
- current_x = 0;
- placewewant = 0;
-
- if (current == fileage)
- return 0;
-
- current_y = 0;
- current = edittop;
- for (i = 0; i <= editwinrows - 3 && current->prev != NULL; i++)
- current = current->prev;
-
- edit_update(current, TOP);
- update_cursor();
-
- check_statblank();
- return 1;
-}
-
-int do_page_down(void)
-{
- wrap_reset();
- current_x = 0;
- placewewant = 0;
-
- if (current == filebot)
- return 0;
-
- /* AHEM, if we only have a screen or less of text, DON'T do an
- edit_update(), just move the cursor to editbot! */
- if (edittop == fileage && editbot == filebot && totlines < editwinrows) {
- current = editbot;
- reset_cursor();
-#ifndef NANO_SMALL
- /* ...unless marking is on, in which case we need it to update
- the highlight. */
- if (ISSET(MARK_ISSET))
- edit_update(current, NONE);
-#endif
- } else if (editbot != filebot || edittop == fileage) {
- current_y = 0;
- current = editbot;
-
- if (current->prev != NULL)
- current = current->prev;
- if (current->prev != NULL)
- current = current->prev;
- edit_update(current, TOP);
- } else {
- while (current != filebot) {
- current = current->next;
- current_y++;
- }
- edit_update(edittop, TOP);
- }
-
- update_cursor();
- check_statblank();
- return 1;
-}
-
-int do_up(void)
-{
- wrap_reset();
- if (current->prev != NULL) {
- current_x = actual_x(current->prev, placewewant);
- current = current->prev;
- if (current_y > 0) {
- update_line(current->next, 0);
- /* It is necessary to change current first, so the mark
- display will change! */
- current_y--;
- update_line(current, current_x);
- } else
- page_up();
- check_statblank();
- }
- return 1;
-}
-
-/* Return value 1 means we moved down, 0 means we were already at the
- * bottom. */
-int do_down(void)
-{
- wrap_reset();
- check_statblank();
-
- if (current->next == NULL)
- return 0;
-
- current = current->next;
- current_x = actual_x(current, placewewant);
-
- /* Note current_y is zero-based. This test checks for the cursor's
- * being on the last row of the edit window. */
- if (current_y == editwinrows - 1) {
-#ifndef NANO_SMALL
- if (ISSET(SMOOTHSCROLL)) {
- /* In this case current_y does not change. The cursor
- * remains at the bottom of the edit window. */
- edittop = edittop->next;
- editbot = editbot->next;
- edit_refresh();
- } else
-#endif
- {
- /* Set edittop so editbot->next (or else editbot) is
- * centered, and set current_y = editwinrows / 2. */
- edit_update(editbot->next != NULL ? editbot->next : editbot, CENTER);
- center_cursor();
- }
- } else {
- update_line(current->prev, 0);
- update_line(current, current_x);
- current_y++;
- }
- return 1;
-}
-
-int do_left(void)
-{
- if (current_x > 0)
- current_x--;
- else if (current != fileage) {
- do_up();
- current_x = strlen(current->data);
- }
- placewewant = xplustabs();
- update_line(current, current_x);
- check_statblank();
- return 1;
-}
-
-int do_right(void)
-{
- assert(current_x <= strlen(current->data));
-
- if (current->data[current_x] != '\0')
- current_x++;
- else if (current->next != NULL) {
- do_down();
- current_x = 0;
- }
- placewewant = xplustabs();
- update_line(current, current_x);
- check_statblank();
- return 1;
-}
+++ /dev/null
-.\" Hey, EMACS: -*- nroff -*-
-.\" nano.1 is copyright (C) 1999, 2000, 2001, 2002, 2003 by
-.\" Chris Allegretta <chrisa@asty.org>
-.\"
-.\" This is free documentation, see the latest version of the GNU General
-.\" Public License for copying conditions. There is NO warranty.
-.\"
-.\" $Id$
-.TH NANO 1 "August 24, 2003"
-.\" Please adjust this date whenever revising the manpage.
-.\"
-
-.SH NAME
-nano \- Nano's ANOther editor, an enhanced free Pico clone
-
-.SH SYNOPSIS
-.B nano
-.I [\+LINE]\ [options]\ [file]
-.br
-
-.SH DESCRIPTION
-This manual page documents briefly the \fBnano\fP command.
-.PP
-.\" TeX users may be more comfortable with the \fB<whatever>\fP and
-.\" \fI<whatever>\fP escape sequences to invoke bold face and italics,
-.\" respectively.
-\fBnano\fP is a small, free and friendly editor which aims to replace
-Pico, the default editor included in the non-free Pine package. Rather
-than just copying Pico's look and feel, \fBnano\fP also implements some
-missing (or disabled by default) features in Pico, such as "search and
-replace" and "go to line number".
-
-.SH OPTIONS
-.TP
-.B \+\fILINE\fP
-Places cursor at \fILINE\fP on startup.
-.TP
-.B \-B (\-\-backup)
-When saving a file, back up the previous version of it to the current
-filename suffixed with a ~.
-.TP
-.B \-D (\-\-dos)
-Write file in DOS format.
-.TP
-.B \-F (\-\-multibuffer)
-Enable multiple file buffers, if available.
-.TP
-.B \-H (\-\-historylog)
-Log search and replace strings to
-.I ~/.nano_history
-so they may be stored for later editing, if nanorc support is
-configured.
-.TP
-.B \-I (\-\-ignorercfiles)
-Don't look at
-.I SYSCONFDIR/nanorc
-or
-.IR ~/.nanorc ,
-if nanorc support is available.
-.TP
-.B \-M (\-\-mac)
-Write file in Mac format.
-.TP
-.B \-N (\-\-noconvert)
-Disable automatic conversion of files from DOS/Mac format.
-.TP
-.B \-Q \fIstr\fP (\-\-quotestr=\fIstr\fP)
-Set the quoting string for justifying. The default is
-"^([\ \\t]*[|>:}#])+" if regular expression support is available, or
-">\ " otherwise.
-.TP
-.B \-R (\-\-regexp)
-Enable regular expression matching for search strings, as well as
-\\n subexpression replacement for replace strings, if available.
-.TP
-.B \-S (\-\-smooth)
-Enable smooth scrolling. Text will scroll line-by-line, instead of the
-usual chunk-by-chunk behavior.
-.TP
-.B \-T \fInum\fP (\-\-tabsize=\fInum\fP)
-Set the size (width) of a tab.
-.TP
-.B \-V (\-\-version)
-Show the current version number and author.
-.TP
-.B \-Y \fIstr\fP (\-\-syntax=\fIstr\fP)
-Specify a specific syntax highlighting from the
-.I .nanorc
-to use, if available.
-.TP
-.B \-c (\-\-const)
-Constantly show the cursor position.
-.TP
-.B \-d (\-\-rebinddelete)
-Interpret the Delete key differently so that both Backspace and Delete
-work properly. You should only need to use this option if Backspace
-acts like Delete on your system.
-.TP
-.B \-h (\-\-help)
-Display a summary of command line options.
-.TP
-.B \-i (\-\-autoindent)
-Indent new lines to the previous line's indentation. Useful when editing
-source code.
-.TP
-.B \-k (\-\-cut)
-Enable cut from cursor to end of line with ^K.
-.TP
-.B \-l (\-\-nofollow)
-If the file being edited is a symbolic link, replace the link with
-a new file, do not follow it. Good for editing files in
-.IR /tmp ,
-perhaps?
-.TP
-.B \-m (\-\-mouse)
-Enable mouse support (if available for your system).
-.TP
-.B \-o \fIdir\fP (\-\-operatingdir=\fIdir\fP)
-Set operating directory. Makes nano set up something similar to a
-chroot.
-.TP
-.B \-p (\-\-preserve)
-Preserve the XON and XOFF sequences (^Q and ^S) so they will be caught
-by the terminal.
-.TP
-.B \-r \fIcols\fP (\-\-fill=\fIcols\fP)
-Wrap lines at column \fIcols\fP. By default, this is the width of the
-screen, less eight. If this value is negative, wrapping will occur at
-\fIcols\fP columns from the right of the screen, allowing the wrap point
-to vary along with the screen width if resized.
-.TP
-.B \-s \fIprog\fP (\-\-speller=\fIprog\fP)
-Enable alternative spell checker command.
-.TP
-.B \-t (\-\-tempfile)
-Always save changed buffer without prompting. Same as Pico -t option.
-.TP
-.B \-v (\-\-view)
-View file (read only) mode.
-.TP
-.B \-w (\-\-nowrap)
-Disable wrapping of long lines.
-.TP
-.B \-x (\-\-nohelp)
-Disable help screen at bottom of editor.
-.TP
-.B \-z (\-\-suspend)
-Enable suspend ability.
-.TP
-.B \-a, \-b, \-e, \-f, \-g, \-j
-Ignored, for compatibility with Pico.
-
-.SH INITIALIZATION FILE
-\fBnano\fP will read initialization files in the following order:
-.IR SYSCONFDIR/nanorc ,
-then
-.IR ~/.nanorc .
-Please see
-.BR nanorc (5)
-and the example file \fBnanorc.sample\fP which should be provided with
-\fBnano\fP.
-
-.SH NOTES
-\fBnano\fP will try to dump the buffer into an emergency file in some
-cases. Mainly, this will happen if \fBnano\fP receives a SIGHUP or
-SIGTERM or runs out of memory, when it will write the buffer into a file
-named
-.I nano.save
-if the buffer didn't have a name already, or will add a ".save" suffix
-to the current filename. If an emergency file with that name already
-exists in the current directory, ".save" and a number (e.g. ".save.1")
-will be suffixed to the current filename in order to make it unique. In
-multibuffer mode, \fBnano\fP will write all the open buffers to the
-respective emergency files.
-
-.SH BUGS
-Please send any comments or bug reports to
-.BR nano@nano-editor.org .
-
-The \fBnano\fP mailing list is available from
-.BR nano-devel@gnu.org .
-
-To subscribe, email to
-.B nano-devel-request@gnu.org
-with a subject of "subscribe".
-
-.SH HOMEPAGE
-http://www.nano-editor.org/
-
-.SH SEE ALSO
-.PD 0
-.TP
-\fBnanorc\fP(5)
-.PP
-\fI/usr/share/doc/nano/\fP (or equivalent on your system)
-
-.SH AUTHOR
-Chris Allegretta <chrisa@asty.org>, et al (see
-.I AUTHORS
-and
-.I THANKS
-for details). This manual page was originally written by Jordi Mallach
-<jordi@sindominio.net>, for the Debian GNU system (but may be used by
-others).
+++ /dev/null
-<HTML><HEAD><TITLE>Manpage of NANO</TITLE>
-</HEAD><BODY>
-<H1>NANO</H1>
-Section: User Commands (1)<BR>Updated: August 24, 2003<BR><A HREF="#index">Index</A>
-<A HREF="http://localhost/cgi-bin/man/man2html">Return to Main Contents</A><HR>
-
-
-
-<P>
-<A NAME="lbAB"> </A>
-<H2>NAME</H2>
-
-nano - Nano's ANOther editor, an enhanced free Pico clone
-<P>
-<A NAME="lbAC"> </A>
-<H2>SYNOPSIS</H2>
-
-<B>nano</B>
-
-<I>[+LINE] [options] [file]</I>
-
-<BR>
-
-<P>
-<A NAME="lbAD"> </A>
-<H2>DESCRIPTION</H2>
-
-This manual page documents briefly the <B>nano</B> command.
-<P>
-
-
-
-
-<B>nano</B> is a small, free and friendly editor which aims to replace
-Pico, the default editor included in the non-free Pine package. Rather
-than just copying Pico's look and feel, <B>nano</B> also implements some
-missing (or disabled by default) features in Pico, such as "search and
-replace" and "go to line number".
-<P>
-<A NAME="lbAE"> </A>
-<H2>OPTIONS</H2>
-
-<DL COMPACT>
-<DT><B>+</B><I>LINE</I>
-
-<DD>
-Places cursor at <I>LINE</I> on startup.
-<DT><B>-B (--backup)</B>
-
-<DD>
-When saving a file, back up the previous version of it to the current
-filename suffixed with a ~.
-<DT><B>-D (--dos)</B>
-
-<DD>
-Write file in DOS format.
-<DT><B>-F (--multibuffer)</B>
-
-<DD>
-Enable multiple file buffers, if available.
-<DT><B>-H (--historylog)</B>
-
-<DD>
-Log search and replace strings to
-<I>~/.nano_history</I>
-
-so they may be stored for later editing, if nanorc support is
-configured.
-<DT><B>-I (--ignorercfiles)</B>
-
-<DD>
-Don't look at
-<I>SYSCONFDIR/nanorc</I>
-
-or
-<I>~/.nanorc</I>,
-
-if nanorc support is available.
-<DT><B>-M (--mac)</B>
-
-<DD>
-Write file in Mac format.
-<DT><B>-N (--noconvert)</B>
-
-<DD>
-Disable automatic conversion of files from DOS/Mac format.
-<DT><B>-Q </B><I>str</I> (--quotestr=<I>str</I>)
-
-<DD>
-Set the quoting string for justifying. The default is
-"^([ \t]*[|>:}#])+" if regular expression support is available, or
-"> " otherwise.
-<DT><B>-R (--regexp)</B>
-
-<DD>
-Enable regular expression matching for search strings, as well as
-\n subexpression replacement for replace strings, if available.
-<DT><B>-S (--smooth)</B>
-
-<DD>
-Enable smooth scrolling. Text will scroll line-by-line, instead of the
-usual chunk-by-chunk behavior.
-<DT><B>-T </B><I>num</I> (--tabsize=<I>num</I>)
-
-<DD>
-Set the size (width) of a tab.
-<DT><B>-V (--version)</B>
-
-<DD>
-Show the current version number and author.
-<DT><B>-Y </B><I>str</I> (--syntax=<I>str</I>)
-
-<DD>
-Specify a specific syntax highlighting from the
-<I>.nanorc</I>
-
-to use, if available.
-<DT><B>-c (--const)</B>
-
-<DD>
-Constantly show the cursor position.
-<DT><B>-d (--rebinddelete)</B>
-
-<DD>
-Interpret the Delete key differently so that both Backspace and Delete
-work properly. You should only need to use this option if Backspace
-acts like Delete on your system.
-<DT><B>-h (--help)</B>
-
-<DD>
-Display a summary of command line options.
-<DT><B>-i (--autoindent)</B>
-
-<DD>
-Indent new lines to the previous line's indentation. Useful when editing
-source code.
-<DT><B>-k (--cut)</B>
-
-<DD>
-Enable cut from cursor to end of line with ^K.
-<DT><B>-l (--nofollow)</B>
-
-<DD>
-If the file being edited is a symbolic link, replace the link with
-a new file, do not follow it. Good for editing files in
-<I>/tmp</I>,
-
-perhaps?
-<DT><B>-m (--mouse)</B>
-
-<DD>
-Enable mouse support (if available for your system).
-<DT><B>-o </B><I>dir</I> (--operatingdir=<I>dir</I>)
-
-<DD>
-Set operating directory. Makes nano set up something similar to a
-chroot.
-<DT><B>-p (--preserve)</B>
-
-<DD>
-Preserve the XON and XOFF sequences (^Q and ^S) so they will be caught
-by the terminal.
-<DT><B>-r </B><I>cols</I> (--fill=<I>cols</I>)
-
-<DD>
-Wrap lines at column <I>cols</I>. By default, this is the width of the
-screen, less eight. If this value is negative, wrapping will occur at
-<I>cols</I> columns from the right of the screen, allowing the wrap point
-to vary along with the screen width if resized.
-<DT><B>-s </B><I>prog</I> (--speller=<I>prog</I>)
-
-<DD>
-Enable alternative spell checker command.
-<DT><B>-t (--tempfile)</B>
-
-<DD>
-Always save changed buffer without prompting. Same as Pico -t option.
-<DT><B>-v (--view)</B>
-
-<DD>
-View file (read only) mode.
-<DT><B>-w (--nowrap)</B>
-
-<DD>
-Disable wrapping of long lines.
-<DT><B>-x (--nohelp)</B>
-
-<DD>
-Disable help screen at bottom of editor.
-<DT><B>-z (--suspend)</B>
-
-<DD>
-Enable suspend ability.
-<DT><B>-a, -b, -e, -f, -g, -j</B>
-
-<DD>
-Ignored, for compatibility with Pico.
-<P>
-</DL>
-<A NAME="lbAF"> </A>
-<H2>INITIALIZATION FILE</H2>
-
-<B>nano</B> will read initialization files in the following order:
-<I>SYSCONFDIR/nanorc</I>,
-
-then
-<I>~/.nanorc</I>.
-
-Please see
-<B><A HREF="http://localhost/cgi-bin/man/man2html?5+nanorc">nanorc</A></B>(5)
-
-and the example file <B>nanorc.sample</B> which should be provided with
-<B>nano</B>.
-<P>
-<A NAME="lbAG"> </A>
-<H2>NOTES</H2>
-
-<B>nano</B> will try to dump the buffer into an emergency file in some
-cases. Mainly, this will happen if <B>nano</B> receives a SIGHUP or
-SIGTERM or runs out of memory, when it will write the buffer into a file
-named
-<I>nano.save</I>
-
-if the buffer didn't have a name already, or will add a ".save" suffix
-to the current filename. If an emergency file with that name already
-exists in the current directory, ".save" and a number (e.g. ".save.1")
-will be suffixed to the current filename in order to make it unique. In
-multibuffer mode, <B>nano</B> will write all the open buffers to the
-respective emergency files.
-<P>
-<A NAME="lbAH"> </A>
-<H2>BUGS</H2>
-
-Please send any comments or bug reports to
-<B><A HREF="mailto:nano@nano-editor.org">nano@nano-editor.org</A></B>.
-
-<P>
-The <B>nano</B> mailing list is available from
-<B><A HREF="mailto:nano-devel@gnu.org">nano-devel@gnu.org</A></B>.
-
-<P>
-To subscribe, email to
-<B><A HREF="mailto:nano-devel-request@gnu.org">nano-devel-request@gnu.org</A></B>
-
-with a subject of "subscribe".
-<P>
-<A NAME="lbAI"> </A>
-<H2>HOMEPAGE</H2>
-
-<A HREF="http://www.nano-editor.org/">http://www.nano-editor.org/</A>
-<P>
-<A NAME="lbAJ"> </A>
-<H2>SEE ALSO</H2>
-
-
-<DL COMPACT>
-<DT><B><A HREF="http://localhost/cgi-bin/man/man2html?5+nanorc">nanorc</A></B>(5)<DD>
-</DL>
-<P>
-
-<I>/usr/share/doc/nano/</I> (or equivalent on your system)
-<P>
-<A NAME="lbAK"> </A>
-<H2>AUTHOR</H2>
-
-Chris Allegretta <<A HREF="mailto:chrisa@asty.org">chrisa@asty.org</A>>, et al (see
-<I>AUTHORS</I>
-
-and
-<I>THANKS</I>
-
-for details). This manual page was originally written by Jordi Mallach
-<<A HREF="mailto:jordi@sindominio.net">jordi@sindominio.net</A>>, for the Debian GNU system (but may be used by
-others).
-<P>
-
-<HR>
-<A NAME="index"> </A><H2>Index</H2>
-<DL>
-<DT><A HREF="#lbAB">NAME</A><DD>
-<DT><A HREF="#lbAC">SYNOPSIS</A><DD>
-<DT><A HREF="#lbAD">DESCRIPTION</A><DD>
-<DT><A HREF="#lbAE">OPTIONS</A><DD>
-<DT><A HREF="#lbAF">INITIALIZATION FILE</A><DD>
-<DT><A HREF="#lbAG">NOTES</A><DD>
-<DT><A HREF="#lbAH">BUGS</A><DD>
-<DT><A HREF="#lbAI">HOMEPAGE</A><DD>
-<DT><A HREF="#lbAJ">SEE ALSO</A><DD>
-<DT><A HREF="#lbAK">AUTHOR</A><DD>
-</DL>
-<HR>
-This document was created by
-<A HREF="http://localhost/cgi-bin/man/man2html">man2html</A>,
-using the manual pages.<BR>
-Time: 21:16:54 GMT, August 24, 2003
-</BODY>
-</HTML>
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * nano.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <signal.h>
-#include <setjmp.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <ctype.h>
-#include <locale.h>
-#include <limits.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#endif
-
-#ifdef HAVE_TERMIO_H
-#include <termio.h>
-#endif
-
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-
-#ifndef DISABLE_WRAPJUSTIFY
-static int fill = 0; /* Fill - where to wrap lines, basically */
-#endif
-
-static struct termios oldterm; /* The user's original term settings */
-static struct sigaction act; /* For all our fun signal handlers */
-
-static sigjmp_buf jmpbuf; /* Used to return to mainloop after SIGWINCH */
-
-/* What we do when we're all set to exit */
-RETSIGTYPE finish(int sigage)
-{
- if (!ISSET(NO_HELP)) {
- mvwaddstr(bottomwin, 1, 0, hblank);
- mvwaddstr(bottomwin, 2, 0, hblank);
- } else
- mvwaddstr(bottomwin, 0, 0, hblank);
-
- wrefresh(bottomwin);
- endwin();
-
- /* Restore the old term settings */
- tcsetattr(0, TCSANOW, &oldterm);
-
-#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
- if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
- save_history();
-#endif
-
-#ifdef DEBUG
- thanks_for_all_the_fish();
-#endif
-
- exit(sigage);
-}
-
-/* Die (gracefully?) */
-void die(const char *msg, ...)
-{
- va_list ap;
-
- endwin();
- curses_ended = TRUE;
-
- /* Restore the old term settings */
- tcsetattr(0, TCSANOW, &oldterm);
-
- va_start(ap, msg);
- vfprintf(stderr, msg, ap);
- va_end(ap);
-
- /* save the currently loaded file if it's been modified */
- if (ISSET(MODIFIED))
- die_save_file(filename);
-
-#ifdef ENABLE_MULTIBUFFER
- /* then save all of the other modified loaded files, if any */
- if (open_files != NULL) {
- openfilestruct *tmp;
-
- tmp = open_files;
-
- while (open_files->prev != NULL)
- open_files = open_files->prev;
-
- while (open_files->next != NULL) {
-
- /* if we already saved the file above (i. e. if it was the
- currently loaded file), don't save it again */
- if (tmp != open_files) {
- /* make sure open_files->fileage and fileage, and
- open_files->filebot and filebot, are in sync; they
- might not be if lines have been cut from the top or
- bottom of the file */
- fileage = open_files->fileage;
- filebot = open_files->filebot;
- /* save the file if it's been modified */
- if (open_files->file_flags & MODIFIED)
- die_save_file(open_files->filename);
- }
- open_files = open_files->next;
- }
- }
-#endif
-
- exit(1); /* We have a problem: exit w/ errorlevel(1) */
-}
-
-void die_save_file(const char *die_filename)
-{
- char *ret;
- int i = -1;
-
- /* If we can't save, we have REAL bad problems, but we might as well
- TRY. */
- if (die_filename[0] == '\0')
- ret = get_next_filename("nano.save");
- else {
- char *buf = charalloc(strlen(die_filename) + 6);
-
- strcpy(buf, die_filename);
- strcat(buf, ".save");
- ret = get_next_filename(buf);
- free(buf);
- }
- if (ret[0] != '\0')
- i = write_file(ret, 1, 0, 0);
-
- if (i != -1)
- fprintf(stderr, _("\nBuffer written to %s\n"), ret);
- else
- fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), ret);
-
- free(ret);
-}
-
-/* Die with an error message that the screen was too small if, well, the
- * screen is too small. */
-void die_too_small(void)
-{
- die(_("Window size is too small for nano...\n"));
-}
-
-void print_view_warning(void)
-{
- statusbar(_("Key illegal in VIEW mode"));
-}
-
-/* Initialize global variables - no better way for now. If
- * save_cutbuffer is nonzero, don't set cutbuffer to NULL. */
-void global_init(int save_cutbuffer)
-{
- current_x = 0;
- current_y = 0;
-
- editwinrows = LINES - 5 + no_help();
- if (editwinrows < MIN_EDITOR_ROWS || COLS < MIN_EDITOR_COLS)
- die_too_small();
-
- fileage = NULL;
- if (!save_cutbuffer)
- cutbuffer = NULL;
- current = NULL;
- edittop = NULL;
- editbot = NULL;
- totlines = 0;
- totsize = 0;
- placewewant = 0;
-
-#ifndef DISABLE_WRAPJUSTIFY
- fill = wrap_at;
- if (fill <= 0)
- fill += COLS;
- if (fill < 0)
- fill = 0;
-#endif
-
- hblank = charalloc(COLS + 1);
- memset(hblank, ' ', COLS);
- hblank[COLS] = '\0';
-}
-
-void window_init(void)
-{
- editwinrows = LINES - 5 + no_help();
- if (editwinrows < MIN_EDITOR_ROWS)
- die_too_small();
-
- if (edit != NULL)
- delwin(edit);
- if (topwin != NULL)
- delwin(topwin);
- if (bottomwin != NULL)
- delwin(bottomwin);
-
- /* 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);
-
- /* This is so the keypad still works after a Meta-X, for example. */
- keypad(edit, TRUE);
- keypad(bottomwin, TRUE);
-}
-
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
-void mouse_init(void)
-{
- if (ISSET(USE_MOUSE)) {
- mousemask(BUTTON1_RELEASED, NULL);
- mouseinterval(50);
- } else
- mousemask(0, NULL);
-}
-#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 "
- "The previous search string will be shown in brackets after "
- "the Search: prompt. Hitting Enter without entering any text "
- "will perform the previous search.\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
-#ifndef DISABLE_SPELLER
- 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");
-#endif
-#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 can be "
- "entered either by using the Control (Ctrl) key or pressing the "
- "Esc key twice. 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. Also, pressing Esc "
- "twice and then typing a three-digit number from 000 to 255 "
- "will enter the character with the corresponding ASCII code. "
- "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) {
- size_t endislen = strlen(_("enable/disable"));
-
- for (t = toggles; t != NULL; t = t->next)
- allocsize += 8 + strlen(t->desc) + endislen;
- }
-#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"));
- } else if (s->val == KEY_UP)
- ptr += sprintf(ptr, "%.2s", _("Up"));
-#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 ? COLS - 24 : 0, s->help);
- }
-
-#ifndef NANO_SMALL
- /* And the toggles... */
- if (currshortcut == main_list)
- for (t = toggles; t != NULL; t = t->next) {
- assert(t->desc != NULL);
- ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", t->val - 32, t->desc,
- _("enable/disable"));
- }
-#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)
-{
- filestruct *dst = (filestruct *)nmalloc(sizeof(filestruct));
-
- assert(src != NULL);
-
- dst->data = charalloc(strlen(src->data) + 1);
- dst->next = src->next;
- dst->prev = src->prev;
- strcpy(dst->data, src->data);
- dst->lineno = src->lineno;
-
- 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)
-{
- 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)
-{
- if (fileptr != NULL) {
- if (fileptr->data != NULL)
- free(fileptr->data);
- free(fileptr);
- }
-}
-
-/* Okay, now let's duplicate a whole struct! */
-filestruct *copy_filestruct(const filestruct *src)
-{
- filestruct *head; /* copy of src, top of the copied list */
- filestruct *prev; /* temp that traverses the list */
-
- assert(src != NULL);
-
- prev = copy_node(src);
- prev->prev = NULL;
- head = prev;
- src = src->next;
- while (src != NULL) {
- prev->next = copy_node(src);
- prev->next->prev = prev;
- prev = prev->next;
-
- src = src->next;
- }
-
- prev->next = NULL;
- return head;
-}
-
-/* Frees a filestruct. */
-void free_filestruct(filestruct *src)
-{
- if (src != NULL) {
- while (src->next != NULL) {
- src = src->next;
- delete_node(src->prev);
-#ifdef DEBUG
- fprintf(stderr, "%s: free'd a node, YAY!\n", "delete_node()");
-#endif
- }
- delete_node(src);
-#ifdef DEBUG
- fprintf(stderr, "%s: free'd last node.\n", "delete_node()");
-#endif
- }
-}
-
-void renumber_all(void)
-{
- filestruct *temp;
- int i = 1;
-
- assert(fileage == NULL || fileage != fileage->next);
- for (temp = fileage; temp != NULL; temp = temp->next)
- temp->lineno = i++;
-}
-
-void renumber(filestruct *fileptr)
-{
- if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
- renumber_all();
- else {
- int lineno = fileptr->prev->lineno;
-
- assert(fileptr != fileptr->next);
- for (; fileptr != NULL; fileptr = fileptr->next)
- fileptr->lineno = ++lineno;
- }
-}
-
-/* Print one usage string to the screen, removes lots of duplicate
- * 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)
-{
- printf(" %s\t", shortflag);
- if (strlen(shortflag) < 8)
- printf("\t");
-
-#ifdef HAVE_GETOPT_LONG
- printf("%s\t", longflag);
- if (strlen(longflag) < 8)
- printf("\t\t");
- else if (strlen(longflag) < 16)
- printf("\t");
-#endif
-
- printf("%s\n", desc);
-}
-
-void usage(void)
-{
-#ifdef HAVE_GETOPT_LONG
- printf(_("Usage: nano [+LINE] [GNU long option] [option] [file]\n\n"));
- printf(_("Option\t\tLong option\t\tMeaning\n"));
-#else
- printf(_("Usage: nano [+LINE] [option] [file]\n\n"));
- printf(_("Option\t\tMeaning\n"));
-#endif /* HAVE_GETOPT_LONG */
-
- print1opt("-h, -?", "--help", _("Show this message"));
- print1opt(_("+LINE"), "", _("Start at line number LINE"));
-#ifndef NANO_SMALL
- print1opt("-B", "--backup", _("Backup existing files on save"));
- print1opt("-D", "--dos", _("Write file in DOS format"));
-#endif
-#ifdef ENABLE_MULTIBUFFER
- print1opt("-F", "--multibuffer", _("Enable multiple file buffers"));
-#endif
-#ifdef ENABLE_NANORC
-#ifndef NANO_SMALL
- print1opt("-H", "--historylog", _("Log & read search/replace string history"));
-#endif
- print1opt("-I", "--ignorercfiles", _("Don't look at nanorc files"));
-#endif
-#ifndef NANO_SMALL
- print1opt("-M", "--mac", _("Write file in Mac format"));
- print1opt("-N", "--noconvert", _("Don't convert files from DOS/Mac format"));
-#endif
-#ifndef DISABLE_JUSTIFY
- print1opt(_("-Q [str]"), _("--quotestr=[str]"), _("Quoting string, default \"> \""));
-#endif
-#ifdef HAVE_REGEX_H
- print1opt("-R", "--regexp", _("Do regular expression searches"));
-#endif
-#ifndef NANO_SMALL
- print1opt("-S", "--smooth", _("Smooth scrolling"));
-#endif
- print1opt(_("-T [num]"), _("--tabsize=[num]"), _("Set width of a tab to num"));
- print1opt("-V", "--version", _("Print version information and exit"));
-#ifdef ENABLE_COLOR
- print1opt(_("-Y [str]"), _("--syntax [str]"), _("Syntax definition to use"));
-#endif
- print1opt("-c", "--const", _("Constantly show cursor position"));
-#ifndef NANO_SMALL
- print1opt("-d", "--rebinddelete", _("Fix Backspace if it acts like Delete"));
- print1opt("-i", "--autoindent", _("Automatically indent new lines"));
- print1opt("-k", "--cut", _("Let ^K cut from cursor to end of line"));
-#endif
- print1opt("-l", "--nofollow", _("Don't follow symbolic links, overwrite"));
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- print1opt("-m", "--mouse", _("Enable mouse"));
-#endif
-#ifndef DISABLE_OPERATINGDIR
- print1opt(_("-o [dir]"), _("--operatingdir=[dir]"), _("Set operating directory"));
-#endif
- print1opt("-p", "--preserve", _("Preserve XON (^Q) and XOFF (^S) keys"));
-#ifndef DISABLE_WRAPJUSTIFY
- print1opt(_("-r [#cols]"), _("--fill=[#cols]"), _("Set fill cols to (wrap lines at) #cols"));
-#endif
-#ifndef DISABLE_SPELLER
- print1opt(_("-s [prog]"), _("--speller=[prog]"), _("Enable alternate speller"));
-#endif
- print1opt("-t", "--tempfile", _("Auto save on exit, don't prompt"));
- print1opt("-v", "--view", _("View (read only) mode"));
-#ifndef DISABLE_WRAPPING
- print1opt("-w", "--nowrap", _("Don't wrap long lines"));
-#endif
- print1opt("-x", "--nohelp", _("Don't show help window"));
- print1opt("-z", "--suspend", _("Enable suspend"));
-
- /* this is a special case */
- printf(" %s\t\t\t%s\n","-a, -b, -e, -f, -g, -j", _("(ignored, for Pico compatibility)"));
-
- exit(0);
-}
-
-void version(void)
-{
- printf(_(" GNU nano version %s (compiled %s, %s)\n"),
- VERSION, __TIME__, __DATE__);
- printf(_
- (" Email: nano@nano-editor.org Web: http://www.nano-editor.org/"));
- printf(_("\n Compiled options:"));
-
-#ifndef ENABLE_NLS
- printf(" --disable-nls");
-#endif
-#ifdef DEBUG
- printf(" --enable-debug");
-#endif
-#ifdef NANO_EXTRA
- printf(" --enable-extra");
-#endif
-#ifdef NANO_SMALL
- printf(" --enable-tiny");
-#else
-#ifdef DISABLE_BROWSER
- printf(" --disable-browser");
-#endif
-#ifdef DISABLE_HELP
- printf(" --disable-help");
-#endif
-#ifdef DISABLE_JUSTIFY
- printf(" --disable-justify");
-#endif
-#if defined(DISABLE_MOUSE) || !defined(NCURSES_MOUSE_VERSION)
- printf(" --disable-mouse");
-#endif
-#ifdef DISABLE_OPERATINGDIR
- printf(" --disable-operatingdir");
-#endif
-#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 DISABLE_ROOTWRAP
- printf(" --disable-wrapping-as-root");
-#endif
-#ifdef ENABLE_COLOR
- printf(" --enable-color");
-#endif
-#ifdef ENABLE_MULTIBUFFER
- printf(" --enable-multibuffer");
-#endif
-#ifdef ENABLE_NANORC
- printf(" --enable-nanorc");
-#endif
-#ifdef USE_SLANG
- printf(" --with-slang");
-#endif
- printf("\n");
-}
-
-/* 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();
-}
-
-int no_help(void)
-{
- return ISSET(NO_HELP) ? 2 : 0;
-}
-
-#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP) || defined(NANO_SMALL)
-void nano_disabled_msg(void)
-{
- statusbar(_("Sorry, support for this function has been disabled"));
-}
-#endif
-
-#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. */
-RETSIGTYPE cancel_fork(int signal)
-{
- if (kill(pid, SIGKILL) == -1)
- nperror("kill");
-}
-
-int open_pipe(const char *command)
-{
- 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. */
-
- if (pipe(fd) == -1) {
- statusbar(_("Could not pipe"));
- return 1;
- }
-
- /* Fork a child. */
-
- 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);
- }
-
- /* Else continue as parent. */
-
- close(fd[1]);
-
- if (pid == -1) {
- close(fd[0]);
- statusbar(_("Could not fork"));
- return 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. */
-
- /* See if the platform supports disabling individual control
- * characters. */
-#ifdef _POSIX_VDISABLE
- if (cancel_sigs == 0 && tcgetattr(0, &term) == -1) {
- cancel_sigs = 2;
- nperror("tcgetattr");
- }
- if (cancel_sigs == 0) {
- 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 */
-
- f = fdopen(fd[0], "rb");
- if (f == NULL)
- 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 == 0 && 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 */
-
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
-void do_mouse(void)
-{
- MEVENT mevent;
- int currslen;
- const shortcut *s = currshortcut;
-
- if (getmouse(&mevent) == ERR)
- return;
-
- /* If mouse not in edit or bottom window, return */
- if (wenclose(edit, mevent.y, mevent.x) && currshortcut == main_list) {
- int sameline;
- /* Did they click on the line with the cursor? If they
- clicked on the cursor, we set the mark. */
- size_t xcur;
- /* The character they clicked on. */
-
- /* Subtract out size of topwin. Perhaps we need a constant
- * somewhere? */
- mevent.y -= 2;
-
- sameline = mevent.y == current_y;
-
- /* Move to where the click occurred. */
- for(; current_y < mevent.y && current->next != NULL; current_y++)
- current = current->next;
- for(; current_y > mevent.y && current->prev != NULL; current_y--)
- current = current->prev;
-
- xcur = actual_x(current, get_page_start(xplustabs()) + mevent.x);
-
- /* Selecting where the cursor is toggles the mark. As does
- selecting beyond the line length with the cursor at the end of
- the line. */
- if (sameline && xcur == current_x) {
- if (ISSET(VIEW_MODE)) {
- print_view_warning();
- return;
- }
- do_mark();
- }
-
- current_x = xcur;
- placewewant = xplustabs();
- edit_refresh();
- } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
- int i, k;
-
- if (currshortcut == main_list)
- currslen = MAIN_VISIBLE;
- else
- currslen = length_of_list(currshortcut);
-
- if (currslen < 2)
- k = COLS / 6;
- else
- k = COLS / ((currslen + (currslen %2)) / 2);
-
- /* Determine what shortcut list was clicked */
- mevent.y -= (editwinrows + 3);
-
- if (mevent.y < 0) /* They clicked on the statusbar */
- return;
-
- /* Don't select stuff beyond list length */
- if (mevent.x / k >= currslen)
- return;
-
- for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
- s = s->next;
-
- /* 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
-
-/* 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 = charealloc(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
-}
-
-int do_backspace(void)
-{
- int refresh = 0;
- if (current_x > 0) {
- assert(current_x <= strlen(current->data));
- /* Let's get dangerous */
- memmove(¤t->data[current_x - 1], ¤t->data[current_x],
- strlen(current->data) - current_x + 1);
-#ifdef DEBUG
- fprintf(stderr, "current->data now = \"%s\"\n", current->data);
-#endif
- align(¤t->data);
-#ifndef NANO_SMALL
- if (current_x <= mark_beginx && mark_beginbuf == current)
- mark_beginx--;
-#endif
- do_left();
-#ifdef ENABLE_COLOR
- refresh = 1;
-#endif
- } else {
- filestruct *previous;
- const filestruct *tmp;
-
- if (current == fileage)
- return 0; /* Can't delete past top of file */
-
- previous = current->prev;
- current_x = strlen(previous->data);
- placewewant = strlenpt(previous->data);
-#ifndef NANO_SMALL
- if (current == mark_beginbuf) {
- mark_beginx += current_x;
- mark_beginbuf = previous;
- }
-#endif
- previous->data = charealloc(previous->data,
- current_x + strlen(current->data) + 1);
- strcpy(previous->data + current_x, current->data);
-
- unlink_node(current);
- delete_node(current);
- tmp = current;
- current = (previous->next ? previous->next : previous);
- renumber(current);
- /* We had to renumber before doing update_line. */
- if (tmp == edittop)
- page_up();
-
- /* Ooops, sanity check */
- if (tmp == filebot) {
- filebot = current;
- editbot = current;
-
- /* Recreate the magic line if we're deleting it AND if the
- line we're on now is NOT blank. if it is blank we
- can just use IT for the magic line. This is how Pico
- appears to do it, in any case. */
- if (current->data[0] != '\0') {
- new_magicline();
- fix_editbot();
- }
- }
-
- current = previous;
- if (current_y > 0)
- current_y--;
- totlines--;
-#ifdef DEBUG
- fprintf(stderr, "After, data = \"%s\"\n", current->data);
-#endif
- refresh = 1;
- }
-
- totsize--;
- set_modified();
- if (refresh)
- edit_refresh();
- return 1;
-}
-
-int do_delete(void)
-{
- int refresh = 0;
-
- /* blbf -> blank line before filebot (see below) */
- int blbf = 0;
-
- if (current->next == filebot && current->data[0] == '\0')
- blbf = 1;
-
- placewewant = xplustabs();
-
- if (current_x != strlen(current->data)) {
- /* Let's get dangerous */
- memmove(¤t->data[current_x], ¤t->data[current_x + 1],
- strlen(current->data) - current_x);
-
- align(¤t->data);
-#ifdef ENABLE_COLOR
- refresh = 1;
-#endif
- } else if (current->next != NULL && (current->next != filebot || blbf)) {
- /* We can delete the line before filebot only if it is blank: it
- becomes the new magic line then. */
-
- filestruct *foo;
-
- current->data = charealloc(current->data,
- strlen(current->data) +
- strlen(current->next->data) + 1);
- strcat(current->data, current->next->data);
-
- foo = current->next;
- if (filebot == foo) {
- filebot = current;
- editbot = current;
- }
-
- unlink_node(foo);
- delete_node(foo);
- renumber(current);
- totlines--;
- refresh = 1;
- } else
- return 0;
-
- totsize--;
- set_modified();
- update_line(current, current_x);
- if (refresh)
- edit_refresh();
- return 1;
-}
-
-int do_tab(void)
-{
- do_char('\t');
- return 1;
-}
-
-/* Someone hits return *gasp!* */
-int do_enter(void)
-{
- 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) {
-#ifndef NANO_SMALL
- if (ISSET(SMOOTHSCROLL))
- edit_update(current, NONE);
- else
-#endif
- edit_update(current, CENTER);
- reset_cursor();
- } else {
- current_y++;
- edit_refresh();
- update_cursor();
- }
-
- totlines++;
- set_modified();
-
- placewewant = xplustabs();
- return 1;
-}
-
-#ifndef NANO_SMALL
-int do_next_word(void)
-{
- 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;
-
- placewewant = xplustabs();
-
- 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);
- }
- return 0;
-}
-
-/* The same thing for backwards. */
-int do_prev_word(void)
-{
- filestruct *old = current;
-
- assert(current != NULL && current->data != 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--;
-
- if (current_x >= 0)
- break;
-
- if (current->prev != NULL)
- current_x = strlen(current->prev->data);
- }
-
- if (current != NULL) {
- while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
- current_x--;
- } else {
- current = fileage;
- current_x = 0;
- }
-
- 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);
- }
- 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);
- }
- }
- }
- update_line(current, current_x);
- }
- return 0;
-}
-#endif /* !NANO_SMALL */
-
-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;
-}
-
-void wrap_reset(void)
-{
- UNSET(SAMELINEWRAP);
-}
-
-#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)
-{
- 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 */
-
-/* There are three steps. First, we decide where to wrap. Then, we
- * create the new wrap line. Finally, we clean up. */
-
-/* Step 1, finding where to wrap. We are going to add a new-line
- * after a white-space character. 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 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. */
-
-#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 (word_back != i && wrap_line[1] != ' ' && wrap_line[1] != '\t')
- wrap_loc = i;
- }
- if (wrap_loc < 0 || i == len)
- return 0;
-
-/* 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). */
-
- /* 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));
-
- /* new_line_len will later be increased by the lengths of indentation
- * and wrap_line. */
- new_line_len = after_break_len;
-
- /* 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);
-
- /* +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 (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
-
- /* Now we allocate the new line and copy into it. */
- newline = charalloc(new_line_len + 1); /* +1 for \0 */
- *newline = '\0';
-
-#ifndef NANO_SMALL
- if (ISSET(AUTOINDENT)) {
- strncpy(newline, indentation, indent_len);
- newline[indent_len] = '\0';
- }
-#endif
- strcat(newline, after_break);
- /* We end the old line after wrap_loc. Note this does not eat the
- * space. */
- null_at(&inptr->data, wrap_loc + 1);
- totsize++;
- if (wrapping) {
- /* In this case, totsize increases by 1 since we add a space
- * between after_break and wrap_line. If the line already ends
- * in a tab or a space, we don't add a space and decrement
- * totsize to account for that. */
- if (!isspace((int) newline[strlen(newline) - 1]))
- strcat(newline, " ");
- else
- totsize--;
- 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 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 is NULL, then temp is the last line of the
- * file, so we must set filebot. */
- if (temp->next != NULL)
- temp->next->prev = temp;
- else
- filebot = temp;
- }
-
-/* Step 3, clean up. Here we reposition the cursor and mark, and do some
- * other sundry things. */
-
- /* later wraps of this line will be prepended to the next line. */
- SET(SAMELINEWRAP);
-
- /* 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
- /* 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 */
-
- /* Place the cursor. */
- reset_cursor();
-
- return 1;
-}
-#endif /* !DISABLE_WRAPPING */
-
-#ifndef DISABLE_SPELLER
-/* A word is misspelled in the file. Let the user replace it. We
- * return False if the user cancels. */
-int do_int_spell_fix(const char *word)
-{
- char *save_search;
- char *save_replace;
- filestruct *current_save = current;
- int current_x_save = current_x;
- filestruct *edittop_save = edittop;
- /* Save where we are. */
- int i = 0;
- /* The return value. */
- int reverse_search_set = ISSET(REVERSE_SEARCH);
-#ifndef NANO_SMALL
- int case_sens_set = ISSET(CASE_SENSITIVE);
- int mark_set = ISSET(MARK_ISSET);
-
- SET(CASE_SENSITIVE);
- /* Make sure the marking highlight is off during Spell Check */
- UNSET(MARK_ISSET);
-#endif
- /* Make sure Spell Check goes forward only */
- UNSET(REVERSE_SEARCH);
-
- /* 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 = -1;
-
- search_last_line = FALSE;
-
- /* We find the first whole-word occurrence of word. */
- while (findnextstr(TRUE, TRUE, fileage, -1, word))
- if (is_whole_word(current_x, current->data, word)) {
- edit_refresh();
-
- do_replace_highlight(TRUE, word);
-
- /* allow replace word to be corrected */
- i = statusq(0, spell_list, word,
-#ifndef NANO_SMALL
- NULL,
-#endif
- _("Edit a replacement"));
-
- do_replace_highlight(FALSE, word);
-
- if (i != -1 && strcmp(word, answer)) {
- int j = 0;
-
- search_last_line = FALSE;
- current_x--;
- do_replace_loop(word, current_save, ¤t_x_save, TRUE, &j);
- }
-
- break;
- }
-
- /* restore the search/replace strings */
- free(last_search); last_search=save_search;
- free(last_replace); last_replace=save_replace;
-
- /* restore where we were */
- current = current_save;
- current_x = current_x_save;
- edittop = edittop_save;
-
- /* restore Search/Replace direction */
- if (reverse_search_set)
- SET(REVERSE_SEARCH);
-
-#ifndef NANO_SMALL
- if (!case_sens_set)
- UNSET(CASE_SENSITIVE);
-
- /* restore marking highlight */
- if (mark_set)
- SET(MARK_ISSET);
-#endif
-
- return i != -1;
-}
-
-/* Integrated spell checking using 'spell' program. Return value: NULL
- * for normal termination, otherwise the error string. */
-char *do_int_speller(char *tempfile_name)
-{
- char *read_buff, *read_buff_ptr, *read_buff_word;
- size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
- int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
- pid_t pid_spell, pid_sort, pid_uniq;
- int spell_status, sort_status, uniq_status;
-
- /* Create all three pipes up front */
-
- if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 || pipe(uniq_fd) == -1)
- return _("Could not create pipe");
-
- statusbar(_("Creating misspelled word list, please wait..."));
- /* A new process to run spell in */
-
- if ((pid_spell = fork()) == 0) {
-
- /* Child continues, (i.e. future spell process) */
-
- close(spell_fd[0]);
-
- /* replace the standard in with the tempfile */
- if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
- goto close_pipes_and_exit;
-
- if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
- goto close_pipes_and_exit;
-
- close(tempfile_fd);
-
- /* send spell's standard out to the pipe */
- if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
- goto close_pipes_and_exit;
-
- close(spell_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);
- }
-
- /* Parent continues here */
- close(spell_fd[1]);
-
- /* A new process to run sort in */
- if ((pid_sort = fork()) == 0) {
-
- /* Child continues, (i.e. future spell process) */
- /* replace the standard in with output of the old pipe */
- if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
- goto close_pipes_and_exit;
-
- close(spell_fd[0]);
-
- /* send sort's standard out to the new pipe */
- if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
- goto close_pipes_and_exit;
-
- close(sort_fd[1]);
-
- /* Start sort program. Use -f to remove mixed case without having
- to have ANOTHER pipe for tr. If this isn't portable, let me know. */
- execlp("sort", "sort", "-f", NULL);
-
- /* Should not be reached, if sort is found */
- exit(1);
- }
-
- close(spell_fd[0]);
- close(sort_fd[1]);
-
- /* A new process to run uniq in */
- if ((pid_uniq = fork()) == 0) {
-
- /* Child continues, (i.e. future uniq process) */
- /* replace the standard in with output of the old pipe */
- if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
- goto close_pipes_and_exit;
-
- close(sort_fd[0]);
-
- /* send uniq's standard out to the new pipe */
- if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
- goto close_pipes_and_exit;
-
- close(uniq_fd[1]);
-
- /* Start uniq program, we are using PATH */
- execlp("uniq", "uniq", NULL);
-
- /* Should not be reached, if uniq is found */
- exit(1);
- }
-
- close(sort_fd[0]);
- close(uniq_fd[1]);
-
- /* Child process was not forked successfully */
- if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
- close(uniq_fd[0]);
- return _("Could not fork");
- }
-
- /* Get system pipe buffer size */
- if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
- close(uniq_fd[0]);
- return _("Could not get size of pipe buffer");
- }
-
- /* 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(uniq_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
- read_buff_read += bytesread;
- read_buff_size += pipe_buff_size;
- read_buff = read_buff_ptr = charealloc(read_buff, read_buff_size);
- read_buff_ptr += read_buff_read;
-
- }
-
- *read_buff_ptr = (char)NULL;
- close(uniq_fd[0]);
-
- /* Process the spelling errors */
- read_buff_word = read_buff_ptr = read_buff;
-
- while (*read_buff_ptr != '\0') {
-
- 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++;
- }
-
- /* 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();
- edit_refresh();
-
- /* Process end of spell process */
-
- waitpid(pid_spell, &spell_status, 0);
- waitpid(pid_sort, &sort_status, 0);
- waitpid(pid_uniq, &uniq_status, 0);
-
- if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
- return _("Error invoking \"spell\"");
-
- if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status))
- return _("Error invoking \"sort -f\"");
-
- if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
- return _("Error invoking \"uniq\"");
-
- /* Otherwise... */
- return NULL;
-
-close_pipes_and_exit:
-
- /* Don't leak any handles */
- close(tempfile_fd);
- close(spell_fd[0]);
- close(spell_fd[1]);
- close(sort_fd[0]);
- close(sort_fd[1]);
- close(uniq_fd[0]);
- close(uniq_fd[1]);
- exit(1);
-}
-
-/* External spell checking. Return value: NULL for normal termination,
- * otherwise the error string. */
-char *do_alt_speller(char *tempfile_name)
-{
- 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
-
- endwin();
-
- /* Set up an argument list to pass the execvp function */
- if (spellargs == NULL) {
- spellargs = (char **)nmalloc(arglen * sizeof(char *));
-
- spellargs[0] = strtok(alt_speller, " ");
- while ((ptr = strtok(NULL, " ")) != NULL) {
- arglen++;
- spellargs = (char **)nrealloc(spellargs, arglen * sizeof(char *));
- spellargs[arglen - 3] = ptr;
- }
- spellargs[arglen - 1] = NULL;
- }
- spellargs[arglen - 2] = tempfile_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);
-
- /* Should not be reached, if alternate speller is found!!! */
- exit(1);
- }
-
- /* Could not fork?? */
- if (pid_spell < 0)
- return _("Could not fork");
-
- /* Wait for alternate speller to complete */
-
- wait(&alt_spell_status);
- if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0) {
- char *altspell_error = NULL;
- char *invoke_error = _("Could not invoke \"%s\"");
- int msglen = strlen(invoke_error) + strlen(alt_speller) + 2;
-
- altspell_error = charalloc(msglen);
- snprintf(altspell_error, msglen, invoke_error, alt_speller);
- return altspell_error;
- }
-
- 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
-
- /* 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 NULL;
-}
-#endif
-
-int do_spell(void)
-{
-#ifdef DISABLE_SPELLER
- nano_disabled_msg();
- return TRUE;
-#else
- char *temp, *spell_msg;
-
- if ((temp = safe_tempnam(0, "nano.")) == NULL) {
- statusbar(_("Could not create a temporary filename: %s"),
- strerror(errno));
- return 0;
- }
-
- if (write_file(temp, 1, 0, 0) == -1) {
- statusbar(_("Spell checking failed: unable to write temp file!"));
- free(temp);
- return 0;
- }
-
-#ifdef ENABLE_MULTIBUFFER
- /* update the current open_files entry before spell-checking, in case
- any problems occur */
- add_open_file(1);
-#endif
-
- if (alt_speller != NULL)
- spell_msg = do_alt_speller(temp);
- else
- spell_msg = do_int_speller(temp);
- remove(temp);
- free(temp);
-
- if (spell_msg != NULL) {
- statusbar(_("Spell checking failed: %s"), spell_msg);
- return 0;
- }
-
- statusbar(_("Finished checking spelling"));
- return 1;
-#endif
-}
-
-#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 len = 0;
-
- assert(line != NULL);
- while (*line == ' ' || *line == '\t') {
- line++;
- len++;
- }
- return len;
-}
-#endif /* !DISABLE_WRAPPING && !NANO_SMALL || !DISABLE_JUSTIFY */
-
-#ifndef DISABLE_JUSTIFY
-/* justify_format() replaces Tab by Space and multiple spaces by 1 (except
- * it maintains 2 after a . ! or ?). Note the terminating \0
- * counts as a space.
- *
- * If !changes_allowed and justify_format() needs to make a change, it
- * returns 1, otherwise returns 0.
- *
- * If changes_allowed, justify_format() might make line->data
- * shorter, and change the actual pointer with null_at().
- *
- * justify_format() will not look at the first skip characters of line.
- * skip should be at most strlen(line->data). The skip+1st character must
- * not be whitespace. */
-int justify_format(int changes_allowed, filestruct *line, size_t skip)
-{
- const char *punct = ".?!";
- const char *brackets = "'\")}]>";
- char *back, *front;
-
- /* These four asserts are assumptions about the input data. */
- assert(line != NULL);
- assert(line->data != NULL);
- assert(skip < strlen(line->data));
- assert(line->data[skip] != ' ' && line->data[skip] != '\t');
-
- back = line->data + skip;
- front = back;
- for (front = back; ; front++) {
- int remove_space = 0;
- /* Do we want to remove this space? */
-
- if (*front == '\t') {
- if (!changes_allowed)
- return 1;
- *front = ' ';
- }
- /* these tests are safe since line->data + skip is not a space */
- if ((*front == '\0' || *front == ' ') && *(front - 1) == ' ') {
- const char *bob = front - 2;
-
- remove_space = 1;
- for (bob = back - 2; bob >= line->data + skip; bob--) {
- if (strchr(punct, *bob) != NULL) {
- remove_space = 0;
- break;
- }
- if (strchr(brackets, *bob) == NULL)
- break;
- }
- }
-
- if (remove_space) {
- /* Now *front is a space we want to remove. We do that by
- * simply failing to assign it to *back. */
- if (!changes_allowed)
- return 1;
-#ifndef NANO_SMALL
- if (mark_beginbuf == line && back - line->data < mark_beginx)
- mark_beginx--;
-#endif
- if (*front == '\0')
- *(back - 1) = '\0';
- } else {
- *back = *front;
- back++;
- }
- if (*front == '\0')
- break;
- }
-
- back--;
- assert(*back == '\0' && *front == '\0');
-
- /* This assert merely documents a fact about the loop above. */
- assert(changes_allowed != 0 || back == front);
-
- /* Now back is the new end of line->data. */
- if (back != front) {
- totsize -= front - back;
- null_at(&line->data, back - line->data);
-#ifndef NANO_SMALL
- if (mark_beginbuf == line && back - line->data < mark_beginx)
- mark_beginx = back - line->data;
-#endif
- }
- return 0;
-}
-
-/* The "quote part" of a line is the largest initial substring matching
- * the quote string. This function returns the length of the quote part
- * of the given line.
- *
- * Note that if !HAVE_REGEX_H then we match concatenated copies of
- * quotestr. */
-#ifdef HAVE_REGEX_H
-size_t quote_length(const char *line, const regex_t *qreg)
-{
- regmatch_t matches;
- int rc = regexec(qreg, line, 1, &matches, 0);
-
- if (rc == REG_NOMATCH || matches.rm_so == (regoff_t) -1)
- return 0;
- /* matches.rm_so should be 0, since the quote string should start with
- * the caret ^. */
- return matches.rm_eo;
-}
-#else /* !HAVE_REGEX_H */
-size_t quote_length(const char *line)
-{
- size_t qdepth = 0;
- size_t qlen = strlen(quotestr);
-
- /* Compute quote depth level */
- while (!strcmp(line + qdepth, quotestr))
- qdepth += qlen;
- return qdepth;
-}
-#endif /* !HAVE_REGEX_H */
-
-/* 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))
-{
- /* Here is the assumption about a_quote: */
- assert(a_quote == quote_length(IFREG(a_line, qreg)));
- return a_quote == quote_length(IFREG(b_line, qreg)) &&
- !strncmp(a_line, b_line, a_quote);
-}
-
-/* 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)
-{
- assert(a_indent == indent_length(a_line));
- assert(b_indent == indent_length(b_line));
-
- return b_indent <= a_indent && !strncmp(a_line, b_line, b_indent);
-}
-
-/* Put the next par_len lines, starting with first_line, in the cut
- * buffer. We assume there are enough lines after first_line. We leave
- * 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)
-{
- /* 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
- * get the actual same paragraph back, not a copy. */
- filestruct *alice = first_line;
-
- set_modified();
- cutbuffer = NULL;
- for(; par_len > 0; par_len--) {
- filestruct *bob = copy_node(alice);
-
- if (alice == first_line)
- first_line = bob;
- if (alice == current)
- current = bob;
- if (alice == edittop)
- edittop = bob;
-#ifndef NANO_SMALL
- if (alice == mark_beginbuf)
- mark_beginbuf = bob;
-#endif
- justify_format(1, bob,
- quote_len + indent_length(bob->data + quote_len));
-
- assert(alice != NULL && bob != NULL);
- add_to_cutbuffer(alice);
- splice_node(bob->prev, bob, bob->next);
- alice = bob->next;
- }
- return first_line;
-}
-
-/* Is it possible to break line at or before goal? */
-int breakable(const char *line, int goal)
-{
- for(; *line != '\0' && goal >= 0; line++) {
- if (*line == ' ' || *line == '\t')
- return TRUE;
-
- if (is_cntrl_char(*line) != 0)
- goal -= 2;
- else
- goal -= 1;
- }
- /* If goal is not negative, the whole line (one word) was short
- * enough. */
- return goal >= 0;
-}
-
-/* We are trying to break a chunk off line. We find the last space such
- * that the display length to there is at most goal + 1. If there is
- * no such space, and force is not 0, then we find the first space.
- * Anyway, we then take the last space in that group of spaces. The
- * terminating '\0' counts as a space. */
-int break_line(const char *line, int goal, int force)
-{
- /* Note that we use int instead of size_t, since goal is at most COLS,
- * the screen width, which will always be reasonably small. */
- int space_loc = -1;
- /* Current tentative return value. Index of the last space we
- * found with short enough display width. */
- int cur_loc = 0;
- /* Current index in line */
-
- assert(line != NULL);
- for(; *line != '\0' && goal >= 0; line++, cur_loc++) {
- if (*line == ' ')
- space_loc = cur_loc;
- assert(*line != '\t');
-
- if (is_cntrl_char(*line))
- goal -= 2;
- else
- goal--;
- }
- if (goal >= 0)
- /* In fact, the whole line displays shorter than goal. */
- return cur_loc;
- if (space_loc == -1) {
- /* No space found short enough. */
- if (force)
- for(; *line != '\0'; line++, cur_loc++)
- if (*line == ' ' && *(line + 1) != ' ' && *(line + 1) != '\0')
- return cur_loc;
- return -1;
- }
- /* Perhaps the character after space_loc is a space. But because
- * of justify_format(), there can be only two adjacent. */
- if (*(line - cur_loc + space_loc + 1) == ' ' ||
- *(line - cur_loc + space_loc + 1) == '\0')
- space_loc++;
- return space_loc;
-}
-
-/* This function performs operations on paragraphs: justify, go to
- * beginning, and go to end. */
-int do_para_operation(int operation)
-{
-/* operation == 0 means we're justifying the paragraph, operation == 1
- * means we're moving to the beginning line of the paragraph, and
- * operation == 2 means we're moving to the ending line of the
- * paragraph.
- *
- * To explain the justifying algorithm, I first need to define some
- * phrases about paragraphs and quotation:
- * A line of text consists of a "quote part", followed by an
- * "indentation part", followed by text. The functions quote_length()
- * and indent_length() calculate these parts.
- *
- * A line is "part of a paragraph" if it has a part not in the quote
- * part or the indentation.
- *
- * A line is "the beginning of a paragraph" if it is part of a paragraph
- * and
- * 1) it is the top line of the file, or
- * 2) the line above it is not part of a paragraph, or
- * 3) the line above it does not have precisely the same quote
- * part, or
- * 4) the indentation of this line is not an initial substring of the
- * indentation of the previous line, or
- * 5) this line has no quote part and some indentation, and
- * AUTOINDENT is not set.
- * The reason for number 5) is that if AUTOINDENT is not set, then an
- * indented line is expected to start a paragraph, like in books. Thus,
- * nano can justify an indented paragraph only if AUTOINDENT is turned
- * on.
- *
- * A contiguous set of lines is a "paragraph" if each line is part of
- * a paragraph and only the first line is the beginning of a paragraph.
- */
-
- size_t quote_len;
- /* Length of the initial quotation of the paragraph we justify. */
- size_t par_len;
- /* Number of lines in that paragraph. */
- filestruct *first_mod_line = NULL;
- /* Will be the first line of the resulting justified paragraph
- * that differs from the original. For restoring after uncut. */
- filestruct *last_par_line = current;
- /* Will be the last line of the result, also for uncut. */
- filestruct *cutbuffer_save = cutbuffer;
- /* When the paragraph gets modified, all lines from the changed
- * one down are stored in the cut buffer. We back up the original
- * to restore it later. */
-
- /* We save these global variables to be restored if the user
- * unjustifies. Note we don't need to save totlines. */
- int current_x_save = current_x;
- int current_y_save = current_y;
- filestruct *current_save = current;
- int flags_save = flags;
- long totsize_save = totsize;
- filestruct *edittop_save = edittop;
- filestruct *editbot_save = editbot;
-#ifndef NANO_SMALL
- filestruct *mark_beginbuf_save = mark_beginbuf;
- int mark_beginx_save = mark_beginx;
-#endif
-
- size_t indent_len; /* generic indentation length */
- filestruct *line; /* generic line of text */
- size_t i; /* generic loop variable */
-
- static int no_restart = 0;
- /* whether we're blocking restarting when searching for the
- * beginning line of the paragraph */
-
-#ifdef HAVE_REGEX_H
- regex_t qreg; /* qreg is the compiled quotation regexp.
- * We no longer care about quotestr. */
- int rc = regcomp(&qreg, quotestr, REG_EXTENDED);
-
- if (rc) {
- size_t size = regerror(rc, &qreg, NULL, 0);
- char *strerror = charalloc(size);
-
- regerror(rc, &qreg, strerror, size);
- statusbar(_("Bad quote string %s: %s"), quotestr, strerror);
- free(strerror);
- return -1;
- }
-#endif
-
- /* Here is an assumption that is always true anyway. */
- assert(current != NULL);
-
- current_x = 0;
-
- restart_bps:
-/* Here we find the first line of the paragraph to justify. If the
- * current line is in a paragraph, then we move back to the first line.
- * Otherwise we move down to the first line that is in a paragraph. */
- quote_len = quote_length(IFREG(current->data, &qreg));
- indent_len = indent_length(current->data + quote_len);
-
- if (current->data[quote_len + indent_len] != '\0') {
- /* This line is part of a paragraph. So we must search back to
- * the first line of this paragraph. First we check items 1) and
- * 3) above. */
- while (current->prev != NULL && quotes_match(current->data,
- quote_len, IFREG(current->prev->data, &qreg))) {
- size_t temp_id_len =
- indent_length(current->prev->data + quote_len);
- /* The indentation length of the previous line. */
-
- /* Is this line the beginning of a paragraph, according to
- items 2), 5), or 4) above? If so, stop. */
- if (current->prev->data[quote_len + temp_id_len] == '\0' ||
- (quote_len == 0 && indent_len > 0
-#ifndef NANO_SMALL
- && !ISSET(AUTOINDENT)
-#endif
- ) ||
- !indents_match(current->prev->data + quote_len,
- temp_id_len, current->data + quote_len, indent_len))
- break;
- indent_len = temp_id_len;
- current = current->prev;
- current_y--;
- }
- } else if (operation == 1) {
- /* This line is not part of a paragraph. Move up until we get
- * to a non "blank" line, and then move down once. */
- do {
- /* There is no previous paragraph, so nothing to move to. */
- if (current->prev == NULL) {
- placewewant = 0;
- if (current_y < 0)
- edit_update(current, CENTER);
- else
- edit_refresh();
- return 0;
- }
- current = current->prev;
- current_y--;
- quote_len = quote_length(IFREG(current->data, &qreg));
- indent_len = indent_length(current->data + quote_len);
- } while (current->data[quote_len + indent_len] == '\0');
- current = current->next;
- } else {
- /* This line is not part of a paragraph. Move down until we get
- * to a non "blank" line. */
- do {
- /* There is no next paragraph, so nothing to justify. */
- if (current->next == NULL) {
- placewewant = 0;
- edit_refresh();
-#ifdef HAVE_REGEX_H
- regfree(&qreg);
-#endif
- return 0;
- }
- current = current->next;
- current_y++;
- quote_len = quote_length(IFREG(current->data, &qreg));
- indent_len = indent_length(current->data + quote_len);
- } while (current->data[quote_len + indent_len] == '\0');
- }
-/* Now current is the first line of the paragraph, and quote_len
- * is the quotation length of that line. */
-
-/* Next step, compute par_len, the number of lines in this paragraph. */
- line = current;
- par_len = 1;
- indent_len = indent_length(line->data + quote_len);
-
- while (line->next != NULL && quotes_match(current->data, quote_len,
- IFREG(line->next->data, &qreg))) {
- size_t temp_id_len = indent_length(line->next->data + quote_len);
-
- if (!indents_match(line->data + quote_len, indent_len,
- line->next->data + quote_len, temp_id_len) ||
- line->next->data[quote_len + temp_id_len] == '\0' ||
- (quote_len == 0 && temp_id_len > 0
-#ifndef NANO_SMALL
- && !ISSET(AUTOINDENT)
-#endif
- ))
- break;
- indent_len = temp_id_len;
- line = line->next;
- par_len++;
- }
-#ifdef HAVE_REGEX_H
- /* We no longer need to check quotation, unless we're searching for
- * the beginning of the paragraph. */
- if (operation != 1)
- regfree(&qreg);
-#endif
-/* Now par_len is the number of lines in this paragraph. Should never
- * call quotes_match() or quote_length() again. */
-
- /* If we're searching for the beginning of the paragraph, skip the
- * justification. If we're searching for the end of the paragraph,
- * move down the number of lines in the paragraph and skip the
- * justification. */
- if (operation == 1)
- goto skip_justify;
- else if (operation == 2) {
- while (par_len > 0) {
- current = current->next;
- current_y++;
- par_len--;
- }
- goto skip_justify;
- }
-
-/* Next step, we loop through the lines of this paragraph, justifying
- * each one individually. */
- SET(JUSTIFY_MODE);
- for(; par_len > 0; current_y++, par_len--) {
- size_t line_len;
- size_t display_len;
- /* The width of current in screen columns. */
- int break_pos;
- /* Where we will break the line. */
-
- indent_len = indent_length(current->data + quote_len) +
- quote_len;
- /* justify_format() removes excess spaces from the line, and
- * changes tabs to spaces. The first argument, 0, means don't
- * change the line, just say whether there are changes to be
- * made. If there are, we do backup_lines(), which copies the
- * original paragraph to the cutbuffer for unjustification, and
- * then calls justify_format() on the remaining lines. */
- if (first_mod_line == NULL && justify_format(0, current, indent_len))
- first_mod_line = backup_lines(current, par_len, quote_len);
-
- line_len = strlen(current->data);
- display_len = strlenpt(current->data);
-
- if (display_len > fill) {
- /* The line is too long. Try to wrap it to the next. */
- break_pos = break_line(current->data + indent_len,
- fill - strnlenpt(current->data, indent_len),
- 1);
- if (break_pos == -1 || break_pos + indent_len == line_len)
- /* We can't break the line, or don't need to, so just go
- * on to the next. */
- goto continue_loc;
- break_pos += indent_len;
- assert(break_pos < line_len);
- /* If we haven't backed up the paragraph, do it now. */
- if (first_mod_line == NULL)
- first_mod_line = backup_lines(current, par_len, quote_len);
- if (par_len == 1) {
- /* There is no next line in this paragraph. We make a new
- * line and copy text after break_pos into it. */
- splice_node(current, make_new_node(current),
- current->next);
- /* In a non-quoted paragraph, we copy the indent only if
- AUTOINDENT is turned on. */
- if (quote_len == 0)
-#ifndef NANO_SMALL
- if (!ISSET(AUTOINDENT))
-#endif
- indent_len = 0;
- current->next->data = charalloc(indent_len + line_len -
- break_pos);
- strncpy(current->next->data, current->data,
- indent_len);
- strcpy(current->next->data + indent_len,
- current->data + break_pos + 1);
- assert(strlen(current->next->data) ==
- indent_len + line_len - break_pos - 1);
- totlines++;
- totsize += indent_len;
- par_len++;
- } else {
- size_t next_line_len = strlen(current->next->data);
-
- indent_len = quote_len +
- indent_length(current->next->data + quote_len);
- current->next->data = charealloc(current->next->data,
- next_line_len + line_len - break_pos + 1);
-
- memmove(current->next->data + indent_len + line_len - break_pos,
- current->next->data + indent_len,
- next_line_len - indent_len + 1);
- strcpy(current->next->data + indent_len,
- current->data + break_pos + 1);
- current->next->data[indent_len + line_len - break_pos - 1]
- = ' ';
-#ifndef NANO_SMALL
- if (mark_beginbuf == current->next) {
- if (mark_beginx < indent_len)
- mark_beginx = indent_len;
- mark_beginx += line_len - break_pos;
- }
-#endif
- }
-#ifndef NANO_SMALL
- if (mark_beginbuf == current && mark_beginx > break_pos) {
- mark_beginbuf = current->next;
- mark_beginx -= break_pos + 1 - indent_len;
- }
-#endif
- null_at(¤t->data, break_pos);
- current = current->next;
- } else if (display_len < fill && par_len > 1) {
- size_t next_line_len;
-
- indent_len = quote_len +
- indent_length(current->next->data + quote_len);
- /* If we can't pull a word from the next line up to this one,
- * just go on. */
- if (!breakable(current->next->data + indent_len,
- fill - display_len - 1))
- goto continue_loc;
-
- /* If we haven't backed up the paragraph, do it now. */
- if (first_mod_line == NULL)
- first_mod_line = backup_lines(current, par_len, quote_len);
-
- break_pos = break_line(current->next->data + indent_len,
- fill - display_len - 1, FALSE);
- assert(break_pos != -1);
-
- current->data = charealloc(current->data,
- line_len + break_pos + 2);
- current->data[line_len] = ' ';
- strncpy(current->data + line_len + 1,
- current->next->data + indent_len, break_pos);
- current->data[line_len + break_pos + 1] = '\0';
-#ifndef NANO_SMALL
- if (mark_beginbuf == current->next) {
- if (mark_beginx < indent_len + break_pos) {
- mark_beginbuf = current;
- if (mark_beginx <= indent_len)
- mark_beginx = line_len + 1;
- else
- mark_beginx = line_len + 1 + mark_beginx - indent_len;
- } else
- mark_beginx -= break_pos + 1;
- }
-#endif
- next_line_len = strlen(current->next->data);
- if (indent_len + break_pos == next_line_len) {
- line = current->next;
-
- /* Don't destroy edittop! */
- if (line == edittop)
- edittop = current;
-
- unlink_node(line);
- delete_node(line);
- totlines--;
- totsize -= indent_len;
- current_y--;
- } else {
- memmove(current->next->data + indent_len,
- current->next->data + indent_len + break_pos + 1,
- next_line_len - break_pos - indent_len);
- null_at(¤t->next->data,
- next_line_len - break_pos);
- current = current->next;
- }
- } else
- continue_loc:
- current = current->next;
- }
- UNSET(JUSTIFY_MODE);
-
-/* We are now done justifying the paragraph. There are cleanup things
- * to do, and we check for unjustify. */
-
- /* totlines, totsize, and current_y have been maintained above. We
- * now set last_par_line to the new end of the paragraph, update
- * fileage, set current_x. Also, edit_refresh() needs the line
- * numbers to be right, so we renumber(). */
- last_par_line = current->prev;
- if (first_mod_line != NULL) {
- if (first_mod_line->prev == NULL)
- fileage = first_mod_line;
- renumber(first_mod_line);
- }
-
- skip_justify:
- if (operation == 1) {
- /* We're on the same line we started on. Search for the first
- * non-"blank" line before the line we're on (if there is one),
- * continually restart that search from the current position
- * until we find a line that's part of a paragraph, and then
- * search once more from there, so that we end up on the first
- * line of that paragraph. In the process, skip over lines
- * consisting only of spacing characters, as searching for the
- * end of the paragraph does. Then update the screen. */
- if (current != fileage && current == current_save && !no_restart) {
- while (current->prev != NULL) {
- int j, j_space = 0;
- current = current->prev;
- current_y--;
- for (j = 0; j < strlen(current->data); j++) {
- if (isspace(current->data[j]))
- j_space++;
- else {
- j = -1;
- break;
- }
- }
- if (j != j_space && strlen(current->data) >=
- (quote_len + indent_len) &&
- current->data[quote_len + indent_len] != '\0') {
- no_restart = 1;
- break;
- }
- }
- goto restart_bps;
- } else
- no_restart = 0;
-#ifdef HAVE_REGEX_H
- /* We no longer need to check quotation, if we were
- * searching for the beginning of the paragraph. */
- regfree(&qreg);
-#endif
- if (current_y < 0)
- edit_update(current, CENTER);
- else
- edit_refresh();
- return 0;
- } else if (operation == 2) {
- /* We've already moved to the end of the paragraph. Update the
- * screen. */
- if (current_y > editwinrows - 1)
- edit_update(current, CENTER);
- else
- edit_refresh();
- return 0;
- }
-
- if (current_y > editwinrows - 1)
- edit_update(current, CENTER);
- else
- edit_refresh();
-
- statusbar(_("Can now UnJustify!"));
- /* Change the shortcut list to display the unjustify code */
- shortcut_init(1);
- display_main_list();
- reset_cursor();
-
- /* Now get a keystroke and see if it's unjustify; if not, unget the
- * keystroke and return. */
-
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- /* If it was a mouse click, parse it with do_mouse() and it might
- * become the unjustify key. Else give it back to the input stream. */
- if ((i = wgetch(edit)) == KEY_MOUSE)
- do_mouse();
- else
- ungetch(i);
-#endif
-
- if ((i = wgetch(edit)) != NANO_UNJUSTIFY_KEY) {
- ungetch(i);
- /* Did we back up anything at all? */
- if (cutbuffer != cutbuffer_save)
- free_filestruct(cutbuffer);
- placewewant = 0;
- } else {
- /* Else restore the justify we just did (ungrateful user!) */
- current = current_save;
- current_x = current_x_save;
- current_y = current_y_save;
- edittop = edittop_save;
- editbot = editbot_save;
- if (first_mod_line != NULL) {
- filestruct *cutbottom = get_cutbottom();
-
- /* Splice the cutbuffer back into the file. */
- cutbottom->next = last_par_line->next;
- cutbottom->next->prev = cutbottom;
- /* The line numbers after the end of the paragraph have
- * been changed, so we change them back. */
- renumber(cutbottom->next);
- if (first_mod_line->prev != NULL) {
- cutbuffer->prev = first_mod_line->prev;
- cutbuffer->prev->next = cutbuffer;
- } else
- fileage = cutbuffer;
- cutbuffer = NULL;
-
- last_par_line->next = NULL;
- free_filestruct(first_mod_line);
-
- /* Restore global variables from before justify */
- totsize = totsize_save;
- totlines = filebot->lineno;
-#ifndef NANO_SMALL
- mark_beginbuf = mark_beginbuf_save;
- mark_beginx = mark_beginx_save;
-#endif
- flags = flags_save;
- if (!ISSET(MODIFIED)) {
- titlebar(NULL);
- wrefresh(topwin);
- }
- }
- edit_refresh();
- }
- cutbuffer = cutbuffer_save;
- blank_statusbar_refresh();
- /* display shortcut list with UnCut */
- shortcut_init(0);
- display_main_list();
-
- return 0;
-}
-#endif /* !DISABLE_JUSTIFY */
-
-int do_justify(void)
-{
-#ifdef DISABLE_JUSTIFY
- nano_disabled_msg();
- return 1;
-#else
- return do_para_operation(0);
-#endif
-}
-
-#ifndef DISABLE_JUSTIFY
-int do_para_begin(void)
-{
- return do_para_operation(1);
-}
-
-int do_para_end(void)
-{
- return do_para_operation(2);
-}
-#endif
-
-int do_exit(void)
-{
- int i;
-
- if (!ISSET(MODIFIED)) {
-
-#ifdef ENABLE_MULTIBUFFER
- if (!close_open_file()) {
- display_main_list();
- return 1;
- }
- else
-#endif
- finish(0);
- }
-
- 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
-
- 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 and SIGTERM cuz we want to write the file out. */
- act.sa_handler = handle_hupterm;
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
-
-#ifndef NANO_SMALL
- act.sa_handler = handle_sigwinch;
- sigaction(SIGWINCH, &act, NULL);
-#endif
-
-#ifdef _POSIX_VDISABLE
- tcgetattr(0, &term);
-
- if (!ISSET(PRESERVE)) {
- /* Ignore ^S and ^Q, much to Chris' chagrin */
- term.c_cc[VSTOP] = _POSIX_VDISABLE;
- term.c_cc[VSTART] = _POSIX_VDISABLE;
- }
-#ifdef VDSUSP
- term.c_cc[VDSUSP] = _POSIX_VDISABLE;
-#endif /* VDSUSP */
-
-#endif /* _POSIX_VDISABLE */
-
- if (!ISSET(SUSPEND)) {
- /* Insane! */
-#ifdef _POSIX_VDISABLE
- term.c_cc[VSUSP] = _POSIX_VDISABLE;
-#else
- act.sa_handler = SIG_IGN;
- sigaction(SIGTSTP, &act, NULL);
-#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);
-
- act.sa_handler = do_suspend;
- sigaction(SIGTSTP, &act, NULL);
-
- act.sa_handler = do_cont;
- sigaction(SIGCONT, &act, NULL);
- }
-
-#ifdef _POSIX_VDISABLE
- tcsetattr(0, TCSANOW, &term);
-#endif
-}
-
-/* Handler for SIGHUP and SIGTERM */
-RETSIGTYPE handle_hupterm(int signal)
-{
- die(_("Received SIGHUP or SIGTERM\n"));
-}
-
-/* What do we do when we catch the suspend signal */
-RETSIGTYPE do_suspend(int signal)
-{
- endwin();
- printf("\n\n\n\n\n%s\n", _("Use \"fg\" to return to nano"));
- fflush(stdout);
-
- /* Restore the terminal settings for the disabled keys */
- tcsetattr(0, TCSANOW, &oldterm);
-
- /* 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);
-}
-
-/* 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. */
-
- doupdate();
- /* The Hurd seems to need this, otherwise a ^Y after a ^Z will
- start suspending again. */
- signal_init();
-
-#ifndef NANO_SMALL
- /* 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 == NULL)
- 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;
- editwinrows = LINES - 5 + no_help();
- if (editwinrows < MIN_EDITOR_ROWS || COLS < MIN_EDITOR_COLS)
- die_too_small();
-
-#ifndef DISABLE_WRAPJUSTIFY
- fill = wrap_at;
- if (fill <= 0)
- fill += COLS;
- if (fill < 0)
- fill = 0;
-#endif
-
- hblank = charealloc(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 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;
- }
-}
-
-#ifndef NANO_SMALL
-void do_toggle(const toggle *which)
-{
- int enabled;
-
- /* Even easier! */
- TOGGLE(which->flag);
-
- switch (which->val) {
- case TOGGLE_SUSPEND_KEY:
- signal_init();
- break;
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- case TOGGLE_MOUSE_KEY:
- mouse_init();
- break;
-#endif
- case TOGGLE_NOHELP_KEY:
- wclear(bottomwin);
- wrefresh(bottomwin);
- window_init();
- fix_editbot();
- edit_refresh();
- display_main_list();
- break;
- case TOGGLE_DOS_KEY:
- UNSET(MAC_FILE);
- break;
- case TOGGLE_MAC_KEY:
- UNSET(DOS_FILE);
- break;
-#ifdef ENABLE_COLOR
- case TOGGLE_SYNTAX_KEY:
- edit_refresh();
- break;
-#endif
- }
-
- /* We are assuming here that shortcut_init() above didn't free and
- * reallocate the toggles. */
- enabled = ISSET(which->flag);
- if (which->val == TOGGLE_NOHELP_KEY || which->val == TOGGLE_WRAP_KEY)
- enabled = !enabled;
- statusbar("%s %s", which->desc,
- enabled ? _("enabled") : _("disabled"));
-}
-#endif /* !NANO_SMALL */
-
-/* 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]. */
-int abcd(int input)
-{
- switch (input) {
- case 'A':
- case 'a':
- return KEY_UP;
- case 'B':
- case 'b':
- return KEY_DOWN;
- case 'C':
- case 'c':
- return KEY_RIGHT;
- case 'D':
- case 'd':
- return KEY_LEFT;
- default:
- return 0;
- }
-}
-
-int main(int argc, char *argv[])
-{
- int optchr;
- int startline = 0; /* Line to try and start at */
- int modify_control_seq = 0;
- int fill_flag_used = 0; /* Was the fill option used? */
- const shortcut *s;
- int keyhandled = 0; /* Have we handled the keystroke yet? */
- int kbinput = -1; /* Input from keyboard */
- int meta;
-
-#ifndef NANO_SMALL
- const toggle *t;
-#endif
-#ifdef _POSIX_VDISABLE
- struct termios term;
-#endif
-#ifdef HAVE_GETOPT_LONG
- int option_index = 0;
- const struct option long_options[] = {
- {"help", 0, 0, 'h'},
-#ifdef ENABLE_MULTIBUFFER
- {"multibuffer", 0, 0, 'F'},
-#endif
-#ifdef ENABLE_NANORC
-#ifndef NANO_SMALL
- {"historylog", 0, 0, 'H'},
-#endif
- {"ignorercfiles", 0, 0, 'I'},
-#endif
-#ifndef DISABLE_JUSTIFY
- {"quotestr", 1, 0, 'Q'},
-#endif
-#ifdef HAVE_REGEX_H
- {"regexp", 0, 0, 'R'},
-#endif
- {"tabsize", 1, 0, 'T'},
- {"version", 0, 0, 'V'},
-#ifdef ENABLE_COLOR
- {"syntax", 1, 0, 'Y'},
-#endif
- {"const", 0, 0, 'c'},
- {"rebinddelete", 0, 0, 'd'},
- {"nofollow", 0, 0, 'l'},
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- {"mouse", 0, 0, 'm'},
-#endif
-#ifndef DISABLE_OPERATINGDIR
- {"operatingdir", 1, 0, 'o'},
-#endif
- {"preserve", 0, 0, 'p'},
-#ifndef DISABLE_WRAPJUSTIFY
- {"fill", 1, 0, 'r'},
-#endif
-#ifndef DISABLE_SPELLER
- {"speller", 1, 0, 's'},
-#endif
- {"tempfile", 0, 0, 't'},
- {"view", 0, 0, 'v'},
-#ifndef DISABLE_WRAPPING
- {"nowrap", 0, 0, 'w'},
-#endif
- {"nohelp", 0, 0, 'x'},
- {"suspend", 0, 0, 'z'},
-#ifndef NANO_SMALL
- {"backup", 0, 0, 'B'},
- {"dos", 0, 0, 'D'},
- {"mac", 0, 0, 'M'},
- {"noconvert", 0, 0, 'N'},
- {"smooth", 0, 0, 'S'},
- {"autoindent", 0, 0, 'i'},
- {"cut", 0, 0, 'k'},
-#endif
- {0, 0, 0, 0}
- };
-#endif
-
-#ifdef ENABLE_NLS
- setlocale(LC_ALL, "");
- bindtextdomain(PACKAGE, LOCALEDIR);
- textdomain(PACKAGE);
-#endif
-
-#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
- /* if we don't have rcfile support, we're root, and
- --disable-wrapping-as-root is used, turn wrapping off */
- if (geteuid() == 0)
- SET(NO_WRAP);
-#endif
-
-#ifdef HAVE_GETOPT_LONG
- while ((optchr = getopt_long(argc, argv, "h?BDFHIMNQ:RST:VY:abcdefgijklmo:pr:s:tvwxz",
- long_options, &option_index)) != -1) {
-#else
- while ((optchr =
- getopt(argc, argv, "h?BDFHIMNQ:RST:VY:abcdefgijklmo:pr:s:tvwxz")) != -1) {
-#endif
-
- switch (optchr) {
-
- case 'a':
- case 'b':
- case 'e':
- case 'f':
- case 'g':
- case 'j':
- /* Pico compatibility flags */
- break;
-#ifndef NANO_SMALL
- case 'B':
- SET(BACKUP_FILE);
- break;
- case 'D':
- SET(DOS_FILE);
- break;
-#endif
-#ifdef ENABLE_MULTIBUFFER
- case 'F':
- SET(MULTIBUFFER);
- break;
-#endif
-#ifdef ENABLE_NANORC
-#ifndef NANO_SMALL
- case 'H':
- SET(HISTORYLOG);
- break;
-#endif
- case 'I':
- SET(NO_RCFILE);
- break;
-#endif
-#ifndef NANO_SMALL
- case 'M':
- SET(MAC_FILE);
- break;
- case 'N':
- SET(NO_CONVERT);
- break;
-#endif
-#ifndef DISABLE_JUSTIFY
- case 'Q':
- quotestr = mallocstrcpy(quotestr, optarg);
- break;
-#endif
-#ifdef HAVE_REGEX_H
- case 'R':
- SET(USE_REGEXP);
- break;
-#endif
-#ifndef NANO_SMALL
- case 'S':
- SET(SMOOTHSCROLL);
- break;
-#endif
- case 'T':
- {
- int i;
- char *first_error;
-
- /* Using strtol() instead of atoi() lets us accept 0
- * while checking other errors. */
- i = (int)strtol(optarg, &first_error, 10);
- if (errno == ERANGE || *optarg == '\0' || *first_error != '\0')
- usage();
- else
- tabsize = i;
- if (tabsize <= 0) {
- fprintf(stderr, _("Tab size is too small for nano...\n"));
- exit(1);
- }
- }
- break;
- case 'V':
- version();
- exit(0);
-#ifdef ENABLE_COLOR
- case 'Y':
- syntaxstr = mallocstrcpy(syntaxstr, optarg);
- break;
-#endif
- case 'c':
- SET(CONSTUPDATE);
- break;
- case 'd':
- SET(REBIND_DELETE);
- break;
-#ifndef NANO_SMALL
- case 'i':
- SET(AUTOINDENT);
- break;
- case 'k':
- SET(CUT_TO_END);
- break;
-#endif
- case 'l':
- SET(NOFOLLOW_SYMLINKS);
- break;
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- case 'm':
- SET(USE_MOUSE);
- break;
-#endif
-#ifndef DISABLE_OPERATINGDIR
- case 'o':
- operating_dir = mallocstrcpy(operating_dir, optarg);
- break;
-#endif
- case 'p':
- SET(PRESERVE);
-#ifdef HAVE_GETOPT_LONG
-#endif
- break;
-#ifndef DISABLE_WRAPJUSTIFY
- case 'r':
- {
- int i;
- char *first_error;
-
- /* Using strtol() instead of atoi() lets us accept 0
- * while checking other errors. */
- i = (int)strtol(optarg, &first_error, 10);
- if (errno == ERANGE || *optarg == '\0' || *first_error != '\0')
- usage();
- else
- wrap_at = i;
- }
- fill_flag_used = 1;
- break;
-#endif
-#ifndef DISABLE_SPELLER
- case 's':
- alt_speller = mallocstrcpy(alt_speller, optarg);
- break;
-#endif
- case 't':
- SET(TEMP_OPT);
- break;
- case 'v':
- SET(VIEW_MODE);
- break;
-#ifndef DISABLE_WRAPPING
- case 'w':
- SET(NO_WRAP);
- break;
-#endif
- case 'x':
- SET(NO_HELP);
- break;
- case 'z':
- SET(SUSPEND);
- break;
- default:
- usage();
- }
- }
-
-/* 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 (!ISSET(NO_RCFILE)) {
-#ifndef DISABLE_OPERATINGDIR
- char *operating_dir_cpy = operating_dir;
-#endif
-#ifndef DISABLE_WRAPPING
- int wrap_at_cpy = wrap_at;
-#endif
-#ifndef DISABLE_JUSTIFY
- char *quotestr_cpy = quotestr;
-#endif
-#ifndef DISABLE_SPELLER
- char *alt_speller_cpy = alt_speller;
-#endif
- int tabsize_cpy = tabsize;
- long flags_cpy = flags;
-
-#ifndef DISABLE_OPERATINGDIR
- operating_dir = NULL;
-#endif
-#ifndef DISABLE_JUSTIFY
- quotestr = NULL;
-#endif
-#ifndef DISABLE_SPELLER
- alt_speller = NULL;
-#endif
-
- do_rcfile();
-
-#ifndef DISABLE_OPERATINGDIR
- if (operating_dir_cpy != NULL) {
- free(operating_dir);
- operating_dir = operating_dir_cpy;
- }
-#endif
-#ifndef DISABLE_WRAPPING
- if (fill_flag_used)
- wrap_at = wrap_at_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 > 0)
- tabsize = tabsize_cpy;
- flags |= flags_cpy;
- }
-#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
- else if (geteuid() == 0)
- SET(NO_WRAP);
-#endif
-#endif /* ENABLE_NANORC */
-
-#ifndef NANO_SMALL
- history_init();
-#ifdef ENABLE_NANORC
- if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
- load_history();
-#endif
-#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 (quotestr == NULL)
-#ifdef HAVE_REGEX_H
- quotestr = mallocstrcpy(NULL, "^([ \t]*[|>:}#])+");
-#else
- quotestr = mallocstrcpy(NULL, "> ");
-#endif
-#endif /* !DISABLE_JUSTIFY */
- if (tabsize == -1)
- tabsize = 8;
-
- /* Clear the filename we'll be using */
- filename = charalloc(1);
- filename[0] = '\0';
-
- /* If there's a +LINE flag, it is the first non-option argument. */
- if (0 < optind && optind < argc && argv[optind][0] == '+') {
- startline = atoi(&argv[optind][1]);
- optind++;
- }
- if (0 < optind && optind < argc)
- filename = mallocstrcpy(filename, argv[optind]);
-
- /* See if there's a non-option in argv (first non-option is the
- filename, if +LINE is not given) */
- if (argc > 1 && argc > optind) {
- /* Look for the +line flag... */
- if (argv[optind][0] == '+') {
- startline = atoi(&argv[optind][1]);
- optind++;
- if (argc > optind)
- filename = mallocstrcpy(filename, argv[optind]);
- } else
- filename = mallocstrcpy(filename, argv[optind]);
- }
-
- /* First back up the old settings so they can be restored, duh */
- tcgetattr(0, &oldterm);
-
-#ifdef _POSIX_VDISABLE
- term = oldterm;
- term.c_cc[VINTR] = _POSIX_VDISABLE;
- term.c_cc[VQUIT] = _POSIX_VDISABLE;
- term.c_lflag &= ~IEXTEN;
- tcsetattr(0, TCSANOW, &term);
-#endif
-
- /* now ncurses init stuff... */
- initscr();
- savetty();
- nonl();
- cbreak();
- noecho();
-
- /* Set up some global variables */
- global_init(0);
- shortcut_init(0);
- signal_init();
-
-#ifdef DEBUG
- fprintf(stderr, "Main: set up windows\n");
-#endif
-
- window_init();
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- mouse_init();
-#endif
-
- keypad(edit, TRUE);
- keypad(bottomwin, TRUE);
-
-#ifdef DEBUG
- fprintf(stderr, "Main: bottom win\n");
-#endif
- /* Set up bottom of window */
- display_main_list();
-
-#ifdef DEBUG
- fprintf(stderr, "Main: open file\n");
-#endif
-
- open_file(filename, 0, 1);
-#ifdef ENABLE_MULTIBUFFER
- /* If we're using multibuffers and more than one file is specified
- on the command line, load them all and switch to the first one
- afterward */
- if (ISSET(MULTIBUFFER) && optind + 1 < argc) {
- for (optind++; optind < argc; optind++) {
- add_open_file(1);
- new_file();
- filename = mallocstrcpy(filename, argv[optind]);
- open_file(filename, 0, 0);
- load_file(0);
- }
- open_nextfile_void();
- }
-#endif
-
- titlebar(NULL);
-
- if (startline > 0)
- do_gotoline(startline, 0);
-
- /* Return here after a sigwinch */
- sigsetjmp(jmpbuf, 1);
-
- /* SHUT UP GCC! */
- startline = 0;
- fill_flag_used = 0;
- keyhandled = 0;
-
- /* This variable should be initialized after the sigsetjmp(), so we
- can't do Esc-Esc then quickly resize and muck things up. */
- modify_control_seq = 0;
-
- edit_refresh();
- reset_cursor();
-
- while (1) {
- keyhandled = 0;
-
- if (ISSET(CONSTUPDATE))
- do_cursorpos(1);
-
-#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
- currshortcut = main_list;
-#endif
-
-#ifndef _POSIX_VDISABLE
- /* We're going to have to do it the old way, i.e. on cygwin */
- raw();
-#endif
-
- kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE));
-#ifdef DEBUG
- fprintf(stderr, "AHA! %c (%d)\n", kbinput, kbinput);
-#endif
- if (meta == 1) {
- switch (kbinput) {
-#ifdef ENABLE_MULTIBUFFER
- case NANO_OPENPREV_KEY:
- case NANO_OPENPREV_ALTKEY:
- open_prevfile_void();
- keyhandled = 1;
- break;
- case NANO_OPENNEXT_KEY:
- case NANO_OPENNEXT_ALTKEY:
- open_nextfile_void();
- keyhandled = 1;
- break;
-#endif
- default:
- /* Check for the altkey defs.... */
- for (s = main_list; s != NULL; s = s->next)
- if (kbinput == s->altval || (kbinput >= 'A' &&
- kbinput <= 'Z' && kbinput == s->altval - 32)) {
- if (ISSET(VIEW_MODE) && !s->viewok)
- print_view_warning();
- else {
- if (s->func != do_cut_text)
- UNSET(KEEP_CUTBUFFER);
- s->func();
- }
- keyhandled = 1;
- break;
- }
-#ifndef NANO_SMALL
- if (!keyhandled)
- /* And for toggle switches */
- for (t = toggles; t != NULL; t = t->next)
- if (kbinput == t->val || (t->val >= 'a' &&
- t->val <= 'z' && kbinput == t->val - 32)) {
- UNSET(KEEP_CUTBUFFER);
- do_toggle(t);
- keyhandled = 1;
- break;
- }
-#endif
-#ifdef DEBUG
- fprintf(stderr, "I got Alt-%c! (%d)\n", kbinput,
- kbinput);
-#endif
- }
- }
-
- /* Look through the main shortcut list to see if we've hit a
- shortcut key */
-
- if (!keyhandled)
-#if !defined(DISABLE_BROWSER) || !defined (DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
- for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
-#else
- for (s = main_list; s != NULL && !keyhandled; s = s->next) {
-#endif
- if (kbinput == s->val ||
- (s->misc1 && kbinput == s->misc1) ||
- (s->misc2 && kbinput == s->misc2)) {
- if (ISSET(VIEW_MODE) && !s->viewok)
- print_view_warning();
- else {
- if (s->func != do_cut_text)
- UNSET(KEEP_CUTBUFFER);
- 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 (!keyhandled)
- UNSET(KEEP_CUTBUFFER);
-
-#ifdef _POSIX_VDISABLE
- /* Don't even think about changing this string */
- if (kbinput == NANO_CONTROL_Q)
- statusbar(_("XON ignored, mumble mumble."));
- if (kbinput == NANO_CONTROL_S)
- statusbar(_("XOFF ignored, mumble mumble."));
-#endif
- /* If we're in raw mode or using Alt-Alt-x, we have to catch
- Control-S and Control-Q */
- if (kbinput == NANO_CONTROL_Q || kbinput == NANO_CONTROL_S)
- keyhandled = 1;
-
- /* Catch ^Z by hand when triggered also */
- if (kbinput == NANO_SUSPEND_KEY) {
- if (ISSET(SUSPEND))
- do_suspend(0);
- keyhandled = 1;
- }
-
- /* Last gasp, stuff that's not in the main lists */
- if (!keyhandled)
- switch (kbinput) {
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- case KEY_MOUSE:
- do_mouse();
- break;
-#endif
-
- case NANO_CONTROL_3: /* Ctrl-[ (Esc), which should
- * have been handled before we
- * got here */
- case NANO_CONTROL_5: /* Ctrl-] */
- break;
- default:
-#ifdef DEBUG
- fprintf(stderr, "I got %c (%d)!\n", kbinput, kbinput);
-#endif
- /* We no longer stop unhandled sequences so that people with
- odd character sets can type... */
-
- if (ISSET(VIEW_MODE))
- print_view_warning();
- else
- do_char(kbinput);
- }
-
- reset_cursor();
- wrefresh(edit);
- }
- assert(0);
-}
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * nano.h *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
-#ifndef NANO_H
-#define NANO_H 1
-
-/* Macros for the flags int... */
-#define SET(bit) flags |= bit
-#define UNSET(bit) flags &= ~bit
-#define ISSET(bit) (flags & bit)
-#define TOGGLE(bit) flags ^= bit
-
-/* Define charalloc as a macro rather than duplicating code */
-#define charalloc(howmuch) (char *)nmalloc((howmuch) * sizeof(char))
-#define charealloc(ptr, howmuch) (char *)nrealloc(ptr, (howmuch) * sizeof(char))
-#ifdef BROKEN_REGEXEC
-#define regexec(preg, string, nmatch, pmatch, eflags) regexec_safe(preg, string, nmatch, pmatch, eflags)
-#endif
-
-#ifndef NANO_SMALL
- /* For the backup file copy ... */
-# define COPYFILEBLOCKSIZE 1024
-#endif
-
-#ifdef USE_SLANG /* Slang support enabled */
-#include <slcurses.h>
-#define KEY_IC SL_KEY_IC
-#define KEY_DC SL_KEY_DELETE
-#define KEY_SUSPEND -1
-#elif defined(HAVE_NCURSES_H)
-#include <ncurses.h>
-#else /* Uh oh */
-#include <curses.h>
-#endif /* CURSES_H */
-
-#ifdef ENABLE_NLS
-# ifdef HAVE_LIBINTL_H
-# include <libintl.h>
-# endif
-# define _(string) gettext(string)
-# define P_(singular, plural, number) ngettext(singular, plural, number)
-#else
-# define _(string) (string)
-# define P_(singular, plural, number) (number == 1 ? singular : plural)
-#endif
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "config.h"
-
-#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
-#include <glib.h>
-# ifndef HAVE_SNPRINTF
-# define snprintf g_snprintf
-# endif
-# ifndef HAVE_VSNPRINTF
-# define vsnprintf g_vsnprintf
-# endif
-#endif
-
-#ifndef HAVE_STRCASECMP
-#define strcasecmp nstricmp
-#endif
-
-#ifndef HAVE_STRNCASECMP
-#define strncasecmp nstrnicmp
-#endif
-
-/* HP-UX 10 & 11 do not seem to support KEY_HOME and KEY_END */
-#ifndef KEY_HOME
-#define KEY_HOME -1
-#endif /* KEY_HOME */
-
-#ifndef KEY_END
-#define KEY_END -1
-#endif /* KEY_END */
-
-/* Snatch these out of the ncurses defs, so we can use them in search
- history regardless of whether we're using ncurses or not */
-#if !defined(KEY_UP) || !defined(KEY_DOWN)
-#define KEY_UP 0403
-#define KEY_DOWN 0402
-#endif /* !KEY_UP || !KEY_DOWN */
-
-#define VERMSG "GNU nano " VERSION
-
-#if defined(DISABLE_WRAPPING) && defined(DISABLE_JUSTIFY)
-#define DISABLE_WRAPJUSTIFY 1
-#endif
-
-/* Structure types */
-typedef struct filestruct {
- char *data;
- struct filestruct *next; /* Next node */
- struct filestruct *prev; /* Previous node */
- int lineno; /* The line number */
-} filestruct;
-
-#ifdef ENABLE_MULTIBUFFER
-typedef struct openfilestruct {
- char *filename;
-#ifndef NANO_SMALL
- struct stat originalfilestat;
-#endif
- struct openfilestruct *next; /* Next node */
- struct openfilestruct *prev; /* Previous node */
- struct filestruct *fileage; /* Current file */
- struct filestruct *filebot; /* Current file's last line */
-#ifndef NANO_SMALL
- struct filestruct *file_mark_beginbuf;
- /* Current file's beginning marked line */
- int file_mark_beginx; /* Current file's beginning marked line's
- x-coordinate position */
-#endif
- int file_current_x; /* Current file's x-coordinate position */
- int file_current_y; /* Current file's y-coordinate position */
- int file_flags; /* Current file's flags: modification
- status (and marking status, if
- available) */
- int file_placewewant; /* Current file's place we want */
- int file_totlines; /* Current file's total number of lines */
- long file_totsize; /* Current file's total size */
- int file_lineno; /* Current file's line number */
-} openfilestruct;
-#endif
-
-typedef struct shortcut {
- int val; /* Actual sequence that generates the keystroke */
- int altval; /* Alt key # for this function, or 0 for none */
- int misc1; /* Other int functions we want bound */
- int misc2;
- int viewok; /* is this function legal in view mode? */
- int (*func) (void); /* Function to call when we catch this key */
- const char *desc; /* Description, e.g. "Page Up" */
-#ifndef DISABLE_HELP
- const char *help; /* Help file entry text */
-#endif
- struct shortcut *next;
-} shortcut;
-
-#ifndef NANO_SMALL
-typedef struct toggle {
- int val; /* Sequence to toggle the key. Should only need 1 */
- const char *desc; /* Description for when toggle is, uh, toggled,
- e.g. "Pico Messages"; we'll append Enabled or
- Disabled */
- int flag; /* What flag actually gets toggled */
- struct toggle *next;
-} toggle;
-#endif /* !NANO_SMALL */
-
-#ifdef ENABLE_NANORC
-typedef struct rcoption {
- const char *name;
- int flag;
-} rcoption;
-#endif /* ENABLE_NANORC */
-
-#ifdef ENABLE_COLOR
-
-typedef struct colortype {
- int fg; /* fg color */
- int bg; /* bg color */
- int bright; /* Is this color A_BOLD? */
- int pairnum; /* Color pair number used for this fg/bg */
- regex_t start; /* Start (or all) of the regex string */
- regex_t *end; /* End of the regex string */
- struct colortype *next;
-} colortype;
-
-typedef struct exttype {
- regex_t val; /* The extensions that match this syntax. */
- struct exttype *next;
-} exttype;
-
-typedef struct syntaxtype {
- char *desc; /* Name of this syntax type */
- exttype *extensions; /* List of extensions that this applies to */
- colortype *color; /* color struct for this syntax */
- struct syntaxtype *next;
-} syntaxtype;
-
-#endif /* ENABLE_COLOR */
-
-#ifndef NANO_SMALL
-typedef struct historytype {
- struct historytype *next;
- struct historytype *prev;
- char *data;
-} historytype;
-typedef struct historyheadtype {
- struct historytype *next; /* keep *next and *prev members together */
- struct historytype *prev; /* and in same order as in historytype */
- struct historytype *tail;
- struct historytype *current;
- int count;
- int len;
-} historyheadtype;
-#endif /* !NANO_SMALL */
-
-/* Bitwise flags so we can save space (or more correctly, not waste it) */
-
-#define MODIFIED (1<<0)
-#define KEEP_CUTBUFFER (1<<1)
-#define CASE_SENSITIVE (1<<2)
-#define MARK_ISSET (1<<3)
-#define CONSTUPDATE (1<<4)
-#define NO_HELP (1<<5)
-#define NOFOLLOW_SYMLINKS (1<<6)
-#define SUSPEND (1<<7)
-#define NO_WRAP (1<<8)
-#define AUTOINDENT (1<<9)
-#define SAMELINEWRAP (1<<10)
-#define VIEW_MODE (1<<11)
-#define USE_MOUSE (1<<12)
-#define USE_REGEXP (1<<13)
-#define REGEXP_COMPILED (1<<14)
-#define TEMP_OPT (1<<15)
-#define CUT_TO_END (1<<16)
-#define REVERSE_SEARCH (1<<17)
-#define MULTIBUFFER (1<<18)
-#define DOS_FILE (1<<19)
-#define MAC_FILE (1<<20)
-#define SMOOTHSCROLL (1<<21)
-#define DISABLE_CURPOS (1<<22) /* Damn, we still need it */
-#define REBIND_DELETE (1<<23)
-#define NO_CONVERT (1<<24)
-#define BACKUP_FILE (1<<25)
-#define NO_RCFILE (1<<26)
-#define COLOR_SYNTAX (1<<27)
-#define PRESERVE (1<<28)
-#define HISTORY_CHANGED (1<<29)
-#define HISTORYLOG (1<<30)
-#define JUSTIFY_MODE (1<<31)
-
-/* Control key sequences, changing these would be very very bad */
-
-#define NANO_CONTROL_SPACE 0
-#define NANO_CONTROL_A 1
-#define NANO_CONTROL_B 2
-#define NANO_CONTROL_C 3
-#define NANO_CONTROL_D 4
-#define NANO_CONTROL_E 5
-#define NANO_CONTROL_F 6
-#define NANO_CONTROL_G 7
-#define NANO_CONTROL_H 8
-#define NANO_CONTROL_I 9
-#define NANO_CONTROL_J 10
-#define NANO_CONTROL_K 11
-#define NANO_CONTROL_L 12
-#define NANO_CONTROL_M 13
-#define NANO_CONTROL_N 14
-#define NANO_CONTROL_O 15
-#define NANO_CONTROL_P 16
-#define NANO_CONTROL_Q 17
-#define NANO_CONTROL_R 18
-#define NANO_CONTROL_S 19
-#define NANO_CONTROL_T 20
-#define NANO_CONTROL_U 21
-#define NANO_CONTROL_V 22
-#define NANO_CONTROL_W 23
-#define NANO_CONTROL_X 24
-#define NANO_CONTROL_Y 25
-#define NANO_CONTROL_Z 26
-#define NANO_CONTROL_3 27
-#define NANO_CONTROL_4 28
-#define NANO_CONTROL_5 29
-#define NANO_CONTROL_6 30
-#define NANO_CONTROL_7 31
-#define NANO_CONTROL_8 127
-
-#define NANO_ALT_A 'a'
-#define NANO_ALT_B 'b'
-#define NANO_ALT_C 'c'
-#define NANO_ALT_D 'd'
-#define NANO_ALT_E 'e'
-#define NANO_ALT_F 'f'
-#define NANO_ALT_G 'g'
-#define NANO_ALT_H 'h'
-#define NANO_ALT_I 'i'
-#define NANO_ALT_J 'j'
-#define NANO_ALT_K 'k'
-#define NANO_ALT_L 'l'
-#define NANO_ALT_M 'm'
-#define NANO_ALT_N 'n'
-#define NANO_ALT_O 'o'
-#define NANO_ALT_P 'p'
-#define NANO_ALT_Q 'q'
-#define NANO_ALT_R 'r'
-#define NANO_ALT_S 's'
-#define NANO_ALT_T 't'
-#define NANO_ALT_U 'u'
-#define NANO_ALT_V 'v'
-#define NANO_ALT_W 'w'
-#define NANO_ALT_X 'x'
-#define NANO_ALT_Y 'y'
-#define NANO_ALT_Z 'z'
-#define NANO_ALT_PERIOD '.'
-#define NANO_ALT_COMMA ','
-#define NANO_ALT_LCARAT '<'
-#define NANO_ALT_RCARAT '>'
-#define NANO_ALT_BRACKET ']'
-#define NANO_ALT_SPACE ' '
-
-/* Some semi-changeable keybindings; don't play with unless you're sure you
-know what you're doing */
-
-#define NANO_INSERTFILE_KEY NANO_CONTROL_R
-#define NANO_INSERTFILE_FKEY KEY_F(5)
-#define NANO_EXIT_KEY NANO_CONTROL_X
-#define NANO_EXIT_FKEY KEY_F(2)
-#define NANO_WRITEOUT_KEY NANO_CONTROL_O
-#define NANO_WRITEOUT_FKEY KEY_F(3)
-#define NANO_GOTO_KEY NANO_CONTROL_7
-#define NANO_GOTO_FKEY KEY_F(13)
-#define NANO_ALT_GOTO_KEY NANO_ALT_G
-#define NANO_HELP_KEY NANO_CONTROL_G
-#define NANO_HELP_FKEY KEY_F(1)
-#define NANO_WHEREIS_KEY NANO_CONTROL_W
-#define NANO_WHEREIS_FKEY KEY_F(6)
-#define NANO_WHEREIS_NEXT_KEY NANO_ALT_W
-#define NANO_REPLACE_KEY NANO_CONTROL_4
-#define NANO_REPLACE_FKEY KEY_F(14)
-#define NANO_ALT_REPLACE_KEY NANO_ALT_R
-#define NANO_OTHERSEARCH_KEY NANO_CONTROL_R
-#define NANO_PREVPAGE_KEY NANO_CONTROL_Y
-#define NANO_PREVPAGE_FKEY KEY_F(7)
-#define NANO_NEXTPAGE_KEY NANO_CONTROL_V
-#define NANO_NEXTPAGE_FKEY KEY_F(8)
-#define NANO_CUT_KEY NANO_CONTROL_K
-#define NANO_CUT_FKEY KEY_F(9)
-#define NANO_UNCUT_KEY NANO_CONTROL_U
-#define NANO_UNCUT_FKEY KEY_F(10)
-#define NANO_CURSORPOS_KEY NANO_CONTROL_C
-#define NANO_CURSORPOS_FKEY KEY_F(11)
-#define NANO_SPELL_KEY NANO_CONTROL_T
-#define NANO_SPELL_FKEY KEY_F(12)
-#define NANO_FIRSTLINE_KEY NANO_PREVPAGE_KEY
-#define NANO_LASTLINE_KEY NANO_NEXTPAGE_KEY
-#define NANO_CANCEL_KEY NANO_CONTROL_C
-#define NANO_REFRESH_KEY NANO_CONTROL_L
-#define NANO_JUSTIFY_KEY NANO_CONTROL_J
-#define NANO_JUSTIFY_FKEY KEY_F(4)
-#define NANO_UNJUSTIFY_KEY NANO_CONTROL_U
-#define NANO_UP_KEY NANO_CONTROL_P
-#define NANO_DOWN_KEY NANO_CONTROL_N
-#define NANO_FORWARD_KEY NANO_CONTROL_F
-#define NANO_BACK_KEY NANO_CONTROL_B
-#define NANO_MARK_KEY NANO_CONTROL_6
-#define NANO_ALT_MARK_KEY NANO_ALT_A
-#define NANO_HOME_KEY NANO_CONTROL_A
-#define NANO_END_KEY NANO_CONTROL_E
-#define NANO_DELETE_KEY NANO_CONTROL_D
-#define NANO_BACKSPACE_KEY NANO_CONTROL_H
-#define NANO_TAB_KEY NANO_CONTROL_I
-#define NANO_SUSPEND_KEY NANO_CONTROL_Z
-#define NANO_ENTER_KEY NANO_CONTROL_M
-#define NANO_FROMSEARCHTOGOTO_KEY NANO_CONTROL_T
-#define NANO_TOFILES_KEY NANO_CONTROL_T
-#define NANO_APPEND_KEY NANO_ALT_A
-#define NANO_PREPEND_KEY NANO_ALT_P
-#define NANO_OPENPREV_KEY NANO_ALT_LCARAT
-#define NANO_OPENNEXT_KEY NANO_ALT_RCARAT
-#define NANO_OPENPREV_ALTKEY NANO_ALT_COMMA
-#define NANO_OPENNEXT_ALTKEY NANO_ALT_PERIOD
-#define NANO_BRACKET_KEY NANO_ALT_BRACKET
-#define NANO_EXTCMD_KEY NANO_CONTROL_X
-#define NANO_NEXTWORD_KEY NANO_CONTROL_SPACE
-#define NANO_PREVWORD_KEY NANO_ALT_SPACE
-#define NANO_PARABEGIN_KEY NANO_CONTROL_W
-#define NANO_PARAEND_KEY NANO_CONTROL_O
-
-#ifndef NANO_SMALL
-/* Toggles do not exist with NANO_SMALL. */
-#define TOGGLE_CONST_KEY NANO_ALT_C
-#define TOGGLE_AUTOINDENT_KEY NANO_ALT_I
-#define TOGGLE_SUSPEND_KEY NANO_ALT_Z
-#define TOGGLE_NOHELP_KEY NANO_ALT_X
-#define TOGGLE_MOUSE_KEY NANO_ALT_M
-#define TOGGLE_CUTTOEND_KEY NANO_ALT_K
-#define TOGGLE_REGEXP_KEY NANO_ALT_R
-#define TOGGLE_WRAP_KEY NANO_ALT_L
-#define TOGGLE_BACKWARDS_KEY NANO_ALT_B
-#define TOGGLE_CASE_KEY NANO_ALT_C
-#define TOGGLE_LOAD_KEY NANO_ALT_F
-#define TOGGLE_DOS_KEY NANO_ALT_D
-#define TOGGLE_MAC_KEY NANO_ALT_O
-#define TOGGLE_SMOOTH_KEY NANO_ALT_S
-#define TOGGLE_NOCONVERT_KEY NANO_ALT_N
-#define TOGGLE_BACKUP_KEY NANO_ALT_B
-#define TOGGLE_SYNTAX_KEY NANO_ALT_Y
-#endif /* !NANO_SMALL */
-
-#define MAIN_VISIBLE 12
-
-#define VIEW 1
-#define NOVIEW 0
-
-typedef enum {
- CENTER, TOP, NONE
-} topmidbotnone;
-
-/* Minimum editor window rows required for nano to work correctly */
-#define MIN_EDITOR_ROWS 3
-
-/* Minimum editor window cols required for nano to work correctly */
-#define MIN_EDITOR_COLS 10
-
-/* Default number of characters from end-of-line where text wrapping
- occurs */
-#define CHARS_FROM_EOL 8
-
-/* Maximum number of search history strings saved, same value used for
- replace history */
-#define MAX_SEARCH_HISTORY 100
-
-#endif /* !NANO_H */
+++ /dev/null
-\input texinfo @c -*-texinfo-*-
-@c %**start of header
-@setfilename nano.info
-@settitle nano Command Manual
-@c %**end of header
-
-@c This file has the new style title page commands.
-@c Run `makeinfo' rather than `texinfo-format-buffer'.
-@smallbook
-@set EDITION 0.1
-@set VERSION 1.2.2
-@set UPDATED 24 Aug 2003
-
-@dircategory Editors
-@direntry
-* nano: (nano). Small and friendly text editor.
-@end direntry
-
-@c tex
-@c \overfullrule=0pt
-@c end tex
-
-@titlepage
-@title GNU @code{nano}
-@subtitle a small and friendly text editor.
-@subtitle version 1.2.2
-
-@author Chris Allegretta
-@page
-
-This manual documents GNU @code{nano}, a small and friendly text
-editor.
-
-This manual is part of the GNU @code{nano} distribution.@*
-@sp4
-Copyright (C) 1999, 2000, 2001, 2002 Chris Allegretta.
-
-Permission is granted to make and distribute verbatim copies of this
-manual provided the copyright notice and this permission notice are
-preserved on all copies.
-
-@iftex
-Permission is granted to process this file through @TeX{} and print the
-results, provided the printed document carries copying permission notice
-identical to this one except for the removal of this paragraph (this
-paragraph not being relevant to the printed manual).
-@end iftex
-
-Permission is granted to copy and distribute modified versions of this
-manual under the conditions for verbatim copying, provided that the
-entire resulting derived work is distributed under the terms of a
-permission notice identical to this one.
-
-Permission is granted to copy and distribute translations of this manual
-into another language, under the above conditions for modified versions,
-except that this permission notice may be stated in a translation
-approved by the Foundation.
-
-You may contact the author by
-e-mail: @email{chrisa@@asty.org}@*
-@end titlepage
-
-@node Top, Introduction, (dir), (dir)
-
-This manual documents GNU @code{nano}, a small and friendly text
-editor.
-
-@menu
-* Introduction::
-* Editor Basics::
-* Online Help::
-* Feature Toggles::
-* The File Browser::
-* Pico Compatibility::
-* Building and Configure Options::
-@end menu
-
-@node Introduction, Editor Basics, Top, Top
-@chapter Introduction
-
-GNU @code{nano} is a small and friendly text editor. Besides basic text
-editing, @code{nano} offers many extra features like an interactive
-search and replace, goto line number, auto-indentation, feature toggles,
-internationalization support, and filename tab completion.
-
-@menu
-* Overview::
-* Command Line Options::
-@end menu
-
-@node Overview, Command Line Options, Introduction, Introduction
-@section Overview
-
-@code{nano} +LINE [GNU long option] [option] [ @var{file ...} ]
-
-The original goal for @code{nano} was a complete bug-for-bug compatible
-emulation of Pico, but nano's main goal is to be as compatible as
-possible while offering a superset of Pico's functionality. Also see
-@xref{Pico Compatibility}, for other differences.
-
-Email bug reports to @email{nano@@nano-editor.org}.
-
-@node Command Line Options, , Overview, Introduction
-@section Command Line Options
-
-@code{nano} takes the following options from the command line:
-
-@table @code
-
-@item -B, --backup
-When saving a file, back up the previous version of it to the current
-filename suffixed with a ~.
-
-@item -D, --dos
-Write file in DOS format.
-
-@item -F, --multibuffer
-Enable multiple file buffers, if available.
-
-@item -I, --ignorercfiles
-Don't look at SYSCONFDIR/nanorc or ~/.nanorc, if nanorc support is
-available.
-
-@item -M, --mac
-Write file in Mac format.
-
-@item -N, --noconvert
-Do not convert files from DOS/Mac format.
-
-@item -Q [str], --quotestr [str]
-Set the quoting string for justifying. The default is
-
-@quotation
-@code{^([ \t]*[|>:@}#])+}
-@end quotation
-
-if regular expression support is available, or ``> '' otherwise. Note
-that @code{\t} above stands for a literal Tab character.
-
-@item -R, --regexp
-Turn on regular expression search and search/replace.
-
-@item -S, --smooth
-Enable smooth scrolling.
-
-@item -T [num], --tabsize=[num]
-Set the displayed tab length to [num] columns.
-
-@item -V, --version
-Print the version number and copyright and quit.
-
-@item -Y, --syntax=[str]
-Specify a specific syntax highlighting from the .nanorc to use, if
-available.
-
-@item -c, --const
-Constantly display the cursor position and line number on the statusbar.
-
-@item -d, --rebinddelete
-Interpret the Delete key differently so that both Backspace and Delete
-work properly. You should only need to use this option if Backspace
-acts like Delete on your system.
-
-@item -h, --help
-Print the usage and exit.
-
-@item -i, --autoindent
-Automatically indent new lines to the same number of spaces and tabs as
-the previous line.
-
-@item -k, --cut
-Makes ^K cut from the current cursor position to the end of the current
-line.
-
-@item -l, --nofollow
-When writing files, if the given file is a symbolic link it is removed
-and a new file is created.
-
-@item -m, --mouse
-Enables the use of the mouse to select text (currently only useful for
-running under the X window system).
-
-@item -o [dir], --operatingdir=[dir]
-Set operating directory. Makes @code{nano} set up something similar to
-a chroot.
-
-@item -p, --preserve
-Preserve the ^Q (XON) and ^S (XOFF) sequences so data being sent to the
-editor can be can be stopped and started.
-
-@item -r [#cols], --fill=[#cols].
-Wrap lines at column #cols. By default this is the width of the screen,
-less eight. If this value is negative, wrapping will occur at #cols
-from the right of the screen, allowing it to vary along with the screen
-width if the screen is resized.
-
-@item -s [prog], --speller=[prog]
-Invoke [prog] as the spell checker. By default, @code{nano} uses its
-own interactive spell checker that requires the @code{spell} program be
-installed on your system.
-
-@item -t, --tempfile
-Do not ask whether or not to save the current contents of the file when
-exiting, assume yes. This is most useful when using @code{nano} as the
-composer of a mailer program.
-
-@anchor{Expert Mode}
-@item -x, --nohelp
-In Expert Mode, the Shortcut Lists will not appear at the bottom of the
-screen. This affects the location of the statusbar as well, as in
-Expert Mode it is located at the very bottom of the editor.
-
-Note: When accessing the help system, Expert Mode is temporarily
-disabled to display the help system navigation keys.
-
-@item -v, --view
-Do not allow the contents of the file to be altered. Note that this
-flag should NOT be used in place of correct file permissions to
-implement a read-only file.
-
-@item -w, --nowrap
-Do not wrap long lines at any length. This option overrides any value
-for -r.
-
-@item -z, --suspend
-Enable suspend ability of @code{nano} using the system's suspend
-keystroke (usually ^Z).
-
-@item -a, -b, -e, -f, -g, -j
-Ignored, for compatibility with Pico.
-
-@item +LINE
-Start at line number LINE instead of the default of line 1.
-@end table
-
-@node Editor Basics, Online Help, Introduction, Top
-@chapter Editor Basics
-@menu
-* Entering Text::
-* Special Functions::
-* The Titlebar::
-* The Statusbar::
-* Shortcut Lists::
-@end menu
-
-@node Entering Text, Special Functions, Editor Basics, Editor Basics
-@section Entering Text
-
-All key sequences in @code{nano} are entered using the keyboard.
-@code{nano} is a ``modeless'' editor. All keys with the exception of
-Control and Meta key sequences will enter text into the file being
-edited.
-
-@node Special Functions, The Titlebar, Entering Text, Editor Basics
-@section Special Functions
-
-Special functions use the Control key (displayed in the help and
-shortcut lists as ^), the Meta key (displayed as M), or the Esc key.
-
-@itemize @bullet
-@item
-Control key sequences are entered by holding down the Control key and
-pressing the desired key, or by pressing the Esc key twice and pressing
-the desired key.
-@item
-Pressing Esc twice and then typing a three-digit number from 000 to 255
-will enter the character with the corresponding ASCII code.
-@item
-Meta key sequences can be entered in a number of possible ways: Pressing
-the Escape key, then releasing it and pressing the desired key, or
-holding down the Alt key while pressing the desired key. This varies
-from keyboard to keyboard, and certain commercial operating systems
-``swallow'' the Alt key so that it never reaches the application. If
-your operating system does this, you should use the Escape key to
-generate Meta key sequences.
-@end itemize
-
-@node The Titlebar, The Statusbar, Special Functions, Editor Basics
-@section The Titlebar
-
-The titlebar is the line displayed at the top of the editor. There are
-three sections: left, center and right. The section on the left
-displays the version of @code{nano} being used. The center section
-displays the current file name, or ``New Buffer'' if the file has not
-yet been named. The section on the right will display ``Modified'' if
-the file has been modified since it was last saved or opened.
-
-Special modes: When @code{nano} is in ``File browser'' mode, the center
-section will display the current directory instead of the filename.
-@xref{The File Browser}.
-
-@node The Statusbar, Shortcut Lists, The Titlebar, Editor Basics
-@section The Statusbar
-
-The statusbar is located three lines from the bottom of the screen (or
-the bottom line in Expert Mode. @xref{Expert Mode}, for more info).
-
-The Statusbar shows important and informational messages. Any error
-messages that occur from using the editor will appear on the statusbar.
-Any questions that are asked of the user will be asked on the statusbar,
-and any user input (search strings, file names, etc) will be input on
-the statusbar.
-
-@node Shortcut Lists, , The Statusbar, Editor Basics
-@section Shortcut Lists
-
-The Shortcut Lists are the two lines at the bottom of the screen which
-show some of the more commonly used functions in the editor.
-
-@node Online Help, Feature Toggles, Editor Basics, Top
-@chapter Online Help
-
-The online help system in @code{nano} is available by pressing ^G.
-It is fairly self explanatory, documenting the various parts of the
-editor and available keystrokes. Navigation is via the ^Y (Page Up)
-and ^V (Page Down) keys. ^X exits the help system.
-
-
-@node Feature Toggles, The File Browser, Online Help, Top
-@chapter Feature Toggles
-
-Toggles allow you to change certain aspects of the editor that
-would normally be done via command line flags. They are invoked via
-certain Meta key sequences. @xref{Special Functions}, for more info.
-The following global toggles are available:
-
-@table @code
-
-@item Backup File Toggle (Meta-B)
-toggles the -B (@code{--backup}) command line flag.
-
-@item DOS Format Toggle (Meta-D)
-toggles the -D (@code{--dos}) command line flag.
-
-@item Multiple Files Toggle (Meta-F)
-toggles the -F (@code{--multibuffer}) command line flag.
-
-@item AutoIndent Toggle (Meta-I)
-toggles the -i (@code{--autoindent}) command line flag.
-
-@item Cut To End Toggle (Meta-K)
-toggles the -k (@code{--cut}) command line flag.
-
-@item Mouse Toggle (Meta-M)
-toggles the -m (@code{--mouse}) command line flag.
-
-@item Mac Format Toggle (Meta-O)
-toggles the -M (@code{--mac}) command line flag.
-
-@item Smooth Scrolling Toggle (Meta-S)
-toggles the -S (@code{--smooth}) command line flag.
-
-@item AutoWrap Toggle (Meta-W)
-toggles the -w (@code{--nowrap}) command line flag.
-
-@item Expert/Nohelp Toggle (Meta-X)
-toggles the -x (@code{--nohelp}) command line flag.
-
-@item Suspend Toggle (Meta-Z)
-toggles the -z (@code{--suspend}) command line flag.
-
-@item Open Previous File Toggle (Meta-<)
-changes buffer to previously loaded file.
-
-@item Open Next File Toggle (Meta->)
-changes buffer to next loaded file.
-
-@end table
-
-
-@node The File Browser, Pico Compatibility, Feature Toggles, Top
-@chapter The File Browser
-
-When reading or writing files, pressing ^T will invoke the file browser.
-Here, one can navigate directories in a graphical manner in order to
-find the desired file.
-
-Basic movement in the file browser is accomplished with the arrow keys
-and page up/down. The behavior of the enter (or `s') key varies by what
-is currently selected. If the currently selected object is a directory,
-the file browser will enter and display the contents of the directory.
-If the object is a file, this filename and path are copied to the
-statusbar and the file browser is exited.
-
-@node Pico Compatibility, Building and Configure Options, The File Browser, Top
-@chapter Pico Compatibility
-
-@code{nano} attempts to emulate Pico as closely as possible, but there
-are certain differences between the editors:
-
-@table @code
-@item Search and Replace History
-As of version 1.2.2 of @code{nano}, text entered as search or replace
-strings will be stored and can be accessed with the up/down arrow keys.
-Previously, @code{nano} offered a more consistent, but incompatible with
-Pico, method for entering search and replace strings. In the old
-method, previous entries would be displayed by default as editable text
-in front of the cursor, as opposed to being bracketed and uneditable as
-it is in Pico. The old behavior could be made compatible with Pico via
-the @code{-p} flag, but recent versions of Pico use the @code{-p} flag
-to preserve the XON and XOFF sequences within the editor. Since with
-the new method search and replace strings can still be edited by simply
-hitting the up arrow key once, the old method was removed completely.
-
-
-@item Writing or Appending Selected Text to Files
-Text selected using the Control-Caret (^^) key can be written out or
-appended to a new or existing file using the Writeout key (^O).
-
-@item Toggles
-Many options which alter the functionality of the program can be
-"toggled" on or off using Meta key sequences, meaning the program does
-not have to be restarted to turn a particular feature of the editor on
-or off. Please see the internal help function (^G) for a list of what
-functions can be toggled for a particular version of @code{nano}. Also
-see @xref{Feature Toggles}, though this may be out of date.
-
-@item Cursor Position Display
-The output of the "Display Cursor Position" in @code{nano} displays
-the given column position, as well as the row and total character
-position of the cursor.
-
-@item Interactive Replace and Spell Checker
-It is worth noting that the @code{nano} replace function is interactive,
-i.e, it does not stop after one search string is found and automatically
-replace it. The @code{nano} implementation will stop at each search
-string found and query whether to replace this instance or not. The
-internal spell checker operates similarly. Note that these is no way to
-force these functions to behave in the Pico fashion. As of version
-1.2.2, misspelled words are sorted and trimmed for uniqueness in the
-internal spell checker such that the words 'apple' and 'Apple' will be
-prompted for correction separately.
-@end table
-
-@node Building and Configure Options, , Pico Compatibility, Top
-@chapter Building and Configure Options
-
-Building @code{nano} from source is fairly straightforward if you are
-familiar with compiling programs with autoconf support:
-
-@itemize @bullet
-@item tar xvfz nano-x.y.z.tar.gz (where x.y.z is the version of nano)
-@item cd nano-x.y.z/
-@item ./configure
-@item make
-@item make install
-@end itemize
-
-if you are looking to optimize @code{nano} for size, you may want to
-consider the following command line options:
-
-
-@table @code
-
-@item --disable-tabcomp
-Disable the tab completion code when reading or writing files.
-
-@item --disable-justify
-Disable the justify (^J)/unjustify (^U) functions in the editor.
-
-@item --disable-speller
-Disable spell checker ability.
-
-@item --disable-help
-Disable the help function (^G). Disabling this option makes the binary
-much smaller, but makes it difficult for new users to learn more than
-very basic things about using the editor.
-
-@item --disable-browser
-Disable the mini file browser (^T) when reading or writing files.
-
-@item --disable-mouse
-Disable all mouse functionality. This also disables the -m command line
-flag, which enables the mouse functions.
-
-@item --disable-operatingdir
-Disable setting the operating directory. This also disables the -o
-command line flag.
-
-@item --enable-tiny
-This option disables all the above. It also disables some of the larger
-internals of the editor, like the marker code (^^) and the cut to line
-(-k) option, which depends on the marker code to work properly. It also
-disables the function toggles.
-
-@item --disable-wrapping
-Disable all word wrapping in the editor. This also eliminates the -w
-command line flag, as nonwrapping is then the default behavior.
-
-@item --disable-nls
-Disables Native Language support. This will make the available GNU
-@code{nano} translations unusable.
-
-@item --with-slang
-Compiling GNU @code{nano} with Slang is supported, and will make the
-binary notably smaller than if compiled with ncurses or other curses
-libraries.
-
-@end table
-
-@contents
-@bye
+++ /dev/null
-.\" Hey, EMACS: -*- nroff -*-
-.\" nanorc.5 is Copyright (C) 2003 Free Software Foundation, Inc.
-.\"
-.\" This is free documentation, see the latest version of the GNU General
-.\" Public License for copying conditions. There is NO warranty.
-.\"
-.\" $Id$
-.TH NANORC 5 "August 24, 2003"
-.\" Please adjust this date whenever revising the manpage.
-.\"
-.SH NAME
-nanorc \- GNU nano's rcfile
-.SH DESCRIPTION
-This manual page documents GNU \fBnano\fP's rcfile.
-.PP
-\fBnano\fP is a small, free and friendly editor which aims to replace
-Pico, the default editor included in the non-free Pine package. Rather
-than just copying Pico's look and feel, \fBnano\fP also implements some
-missing (or disabled by default) features in Pico, such as "search and
-replace" and "go to line number".
-.PP
-The \fInanorc\fP file contains the default settings for \fBnano\fP.
-During startup, \fBnano\fP will first read its system-wide settings from
-.IR SYSCONFDIR/nanorc ,
-and then user-specific settings from
-.IR ~/.nanorc .
-
-.SH OPTIONS
-The configuration file accepts a series of "set" and "unset" commands,
-which can be used to configure nano on startup without using the
-command-line options. Additionally, the "syntax" and "color" keywords
-are used to define syntax highlighting rules for different text
-patterns. GNU nano will read one command per line.
-
-Options in rcfiles take precedence over nano's defaults, and command
-line options override rcfile settings.
-
-Options are unset by default, except for options that take an argument.
-
-The supported commands and arguments are:
-
-.TP 3
-\fBset/unset autoindent\fP
-Use auto-indentation.
-.TP
-\fBset/unset backup\fP
-Create backup files in
-.IR filename~ .
-.TP
-\fBset/unset const\fP
-Constantly display the cursor position in the status bar.
-.TP
-\fBset/unset cut\fP
-Use cut to end of line with ^K by default.
-.TP
-\fBset fill \fIn\fP\fP
-Wrap lines at column number \fIn\fP. If \fIn\fP is 0 or less, the line
-length will be the screen width less \fIn\fP. The default value is -8.
-.TP
-\fBset/unset historylog\fP
-Enable
-.I ~/.nano_history
-for saving and reading search/replace strings.
-.TP
-\fBset/unset multibuffer\fP
-Allow inserting files into their own buffers.
-.TP
-\fBset/unset noconvert\fP
-Don't convert files from DOS/Mac format.
-.TP
-\fBset/unset nofollow\fP
-Don't follow symlinks when writing files.
-.TP
-\fBset/unset nohelp\fP
-Don't display the help lists at the bottom of the screen.
-.TP
-\fBset/unset nowrap\fP
-Don't wrap text at all.
-.TP
-\fBset operatingdir "\fIdirectory\fP"\fP
-\fBnano\fP will only read and write files inside \fIdirectory\fP and its
-subdirectories. Also, the current directory is changed to here, so
-files are inserted from this dir. By default the operating directory
-feature is turned off.
-.TP
-\fBset/unset preserve\fP
-Preserve the XON and XOFF keys (^Q and ^S).
-.TP
-\fBset quotestr "\fIstring\fP"\fP
-The email-quote string, used to justify email-quoted paragraphs. This
-is an "extended regular expression" if your system supports them,
-otherwise a literal string. The default value is
-
- set quotestr "^([\ \\t]*[|>:}#])+"
-
-if you have regexps, otherwise set quotestr ">\ ". Note that '\\t'
-above stands for a literal Tab character.
-.TP
-\fBset/unset rebinddelete\fP
-Interpret the Delete key differently so that both Backspace and Delete
-work properly. You should only need to use this option if Backspace
-acts like Delete on your system.
-.TP
-\fBset/unset regexp\fP
-Do regular expression searches by default.
-.TP
-\fBset/unset smooth\fP
-Use smooth scrolling by default.
-.TP
-\fBset speller \fIspellprog\fP\fP
-Use spelling checker \fIspellprog\fP instead of the built-in one, which
-calls \fIspell\fP.
-.TP
-\fBset/unset suspend\fP
-Allow nano to be suspended with ^Z.
-.TP
-\fBset tabsize \fIn\fP\fP
-Use a tab size of \fIn\fP instead of the default (8); must be greater
-than 0.
-.TP
-\fBset/unset tempfile\fP
-Save automatically on exit, don't prompt.
-.TP
-\fBset/unset view\fP
-Disallow file modification.
-.TP
-.B syntax "\fIstr\fP" ["\fIfileregex\fP" ... ]
-Defines a syntax named \fIstr\fP which can be activated via the \fB-Y\fP
-flag, or will be automatically activated if the current filename matches
-\fIfileregex\fP. All following \fBcolor\fP statements will apply to
-\fIsyntax\fP until a new syntax is defined.
-.TP
-.B color \fIfgcolor\fP[,\fIbgcolor\fP] "\fIregex\fP" ...
-For the currently defined syntax, display all expressions matching
-\fIregex\fP with foreground color \fIfgcolor\fP and optional background
-color \fIbgcolor\fP. Legal colors for foreground and background color
-are: white, black, red, blue, green, yellow, magenta, and cyan. You may
-use the prefix "bright" to force a stronger color highlight. If your
-terminal supports transparency, not specifying a \fIbgcolor\fP tells
-\fBnano\fP to attempt to use a transparent background.
-.TP
-.B color \fIfgcolor\fP[,\fIbgcolor\fP] start="\fIsr\fP" end="\fIer\fP"
-Display expressions which start with \fIsr\fP and end with \fIer\fP
-with foreground color \fIfgcolor\fP and optional background color
-\fIbgcolor\fP. This allows syntax highlighting to span multiple lines.
-Note that all subsequent instances of \fIsr\fP after an initial \fIsr\fP
-is found will be highlighted until the first instance of \fIer\fP.
-\fI
-
-.SH FILES
-.TP
-.I SYSCONFDIR/nanorc
-System-wide configuration file
-.TP
-.I ~/.nanorc
-Per-user configuration file
-.SH SEE ALSO
-.PD 0
-.TP
-\fBnano\fP(1)
-.PP
-\fI/usr/share/doc/nano/examples/nanorc.sample\fP (or equivalent on your
-system)
-.SH AUTHOR
-Chris Allegretta <chrisa@asty.org>, et al (see
-.I AUTHORS
-and
-.I THANKS
-for details).
-This manual page was written by Jordi Mallach <jordi@gnu.org>.
+++ /dev/null
-<HTML><HEAD><TITLE>Manpage of NANORC</TITLE>
-</HEAD><BODY>
-<H1>NANORC</H1>
-Section: File Formats (5)<BR>Updated: August 24, 2003<BR><A HREF="#index">Index</A>
-<A HREF="http://localhost/cgi-bin/man/man2html">Return to Main Contents</A><HR>
-
-
-
-<A NAME="lbAB"> </A>
-<H2>NAME</H2>
-
-nanorc - GNU nano's rcfile
-<A NAME="lbAC"> </A>
-<H2>DESCRIPTION</H2>
-
-This manual page documents GNU <B>nano</B>'s rcfile.
-<P>
-
-<B>nano</B> is a small, free and friendly editor which aims to replace
-Pico, the default editor included in the non-free Pine package. Rather
-than just copying Pico's look and feel, <B>nano</B> also implements some
-missing (or disabled by default) features in Pico, such as "search and
-replace" and "go to line number".
-<P>
-
-The <I>nanorc</I> file contains the default settings for <B>nano</B>.
-During startup, <B>nano</B> will first read its system-wide settings from
-<I>SYSCONFDIR/nanorc</I>,
-
-and then user-specific settings from
-<I>~/.nanorc</I>.
-
-<P>
-<A NAME="lbAD"> </A>
-<H2>OPTIONS</H2>
-
-The configuration file accepts a series of "set" and "unset" commands,
-which can be used to configure nano on startup without using the
-command-line options. Additionally, the "syntax" and "color" keywords
-are used to define syntax highlighting rules for different text
-patterns. GNU nano will read one command per line.
-<P>
-Options in rcfiles take precedence over nano's defaults, and command
-line options override rcfile settings.
-<P>
-Options are unset by default, except for options that take an argument.
-<P>
-The supported commands and arguments are:
-<P>
-<DL COMPACT>
-<DT><B>set/unset autoindent</B><DD>
-Use auto-indentation.
-<DT><B>set/unset backup</B><DD>
-Create backup files in
-<I>filename~</I>.
-
-<DT><B>set/unset const</B><DD>
-Constantly display the cursor position in the status bar.
-<DT><B>set/unset cut</B><DD>
-Use cut to end of line with ^K by default.
-<DT><B>set fill </B><I>n</I><DD>
-Wrap lines at column number <I>n</I>. If <I>n</I> is 0 or less, the line
-length will be the screen width less <I>n</I>. The default value is -8.
-<DT><B>set/unset historylog</B><DD>
-Enable
-<I>~/.nano_history</I>
-
-for saving and reading search/replace strings.
-<DT><B>set/unset multibuffer</B><DD>
-Allow inserting files into their own buffers.
-<DT><B>set/unset noconvert</B><DD>
-Don't convert files from DOS/Mac format.
-<DT><B>set/unset nofollow</B><DD>
-Don't follow symlinks when writing files.
-<DT><B>set/unset nohelp</B><DD>
-Don't display the help lists at the bottom of the screen.
-<DT><B>set/unset nowrap</B><DD>
-Don't wrap text at all.
-<DT><B>set operatingdir "</B><I>directory</I>"<DD>
-<B>nano</B> will only read and write files inside <I>directory</I> and its
-subdirectories. Also, the current directory is changed to here, so
-files are inserted from this dir. By default the operating directory
-feature is turned off.
-<DT><B>set/unset preserve</B><DD>
-Preserve the XON and XOFF keys (^Q and ^S).
-<DT><B>set quotestr "</B><I>string</I>"<DD>
-The email-quote string, used to justify email-quoted paragraphs. This
-is an "extended regular expression" if your system supports them,
-otherwise a literal string. The default value is
-<P>
-<TT> </TT>set quotestr "^([ \t]*[|>:}#])+"<BR>
-<P>
-if you have regexps, otherwise set quotestr "> ". Note that '\t'
-above stands for a literal Tab character.
-<DT><B>set/unset rebinddelete</B><DD>
-Interpret the Delete key differently so that both Backspace and Delete
-work properly. You should only need to use this option if Backspace
-acts like Delete on your system.
-<DT><B>set/unset regexp</B><DD>
-Do regular expression searches by default.
-<DT><B>set/unset smooth</B><DD>
-Use smooth scrolling by default.
-<DT><B>set speller </B><I>spellprog</I><DD>
-Use spelling checker <I>spellprog</I> instead of the built-in one, which
-calls <I>spell</I>.
-<DT><B>set/unset suspend</B><DD>
-Allow nano to be suspended with ^Z.
-<DT><B>set tabsize </B><I>n</I><DD>
-Use a tab size of <I>n</I> instead of the default (8); must be greater
-than 0.
-<DT><B>set/unset tempfile</B><DD>
-Save automatically on exit, don't prompt.
-<DT><B>set/unset view</B><DD>
-Disallow file modification.
-<DT><B>syntax </B><I>str</I> [<I>fileregex</I> ... ]
-
-<DD>
-Defines a syntax named <I>str</I> which can be activated via the <B>-Y</B>
-flag, or will be automatically activated if the current filename matches
-<I>fileregex</I>. All following <B>color</B> statements will apply to
-<I>syntax</I> until a new syntax is defined.
-<DT><B>color </B><I>fgcolor</I>[,<I>bgcolor</I>] <I>regex</I> ...
-
-<DD>
-For the currently defined syntax, display all expressions matching
-<I>regex</I> with foreground color <I>fgcolor</I> and optional background
-color <I>bgcolor</I>. Legal colors for foreground and background color
-are: white, black, red, blue, green, yellow, magenta, and cyan. You may
-use the prefix "bright" to force a stronger color highlight. If your
-terminal supports transparency, not specifying a <I>bgcolor</I> tells
-<B>nano</B> to attempt to use a transparent background.
-<DT><B>color </B><I>fgcolor</I>[,<I>bgcolor</I>] start=<I>sr</I> end=<I>er</I>
-
-<DD>
-Display expressions which start with <I>sr</I> and end with <I>er</I>
-with foreground color <I>fgcolor</I> and optional background color
-<I>bgcolor</I>. This allows syntax highlighting to span multiple lines.
-Note that all subsequent instances of <I>sr</I> after an initial <I>sr</I>
-is found will be highlighted until the first instance of <I>er</I>.
-<I>
-<P>
-</DL>
-</I><A NAME="lbAE"> </A>
-<H2>FILES</H2>
-
-<DL COMPACT>
-<DT><I>SYSCONFDIR/nanorc</I>
-
-<DD>
-System-wide configuration file
-<DT><I>~/.nanorc</I>
-
-<DD>
-Per-user configuration file
-</DL>
-<A NAME="lbAF"> </A>
-<H2>SEE ALSO</H2>
-
-
-<DL COMPACT>
-<DT><B><A HREF="http://localhost/cgi-bin/man/man2html?1+nano">nano</A></B>(1)<DD>
-</DL>
-<P>
-
-<I>/usr/share/doc/nano/examples/nanorc.sample</I> (or equivalent on your
-system)
-<A NAME="lbAG"> </A>
-<H2>AUTHOR</H2>
-
-Chris Allegretta <<A HREF="mailto:chrisa@asty.org">chrisa@asty.org</A>>, et al (see
-<I>AUTHORS</I>
-
-and
-<I>THANKS</I>
-
-for details).
-This manual page was written by Jordi Mallach <<A HREF="mailto:jordi@gnu.org">jordi@gnu.org</A>>.
-<P>
-
-<HR>
-<A NAME="index"> </A><H2>Index</H2>
-<DL>
-<DT><A HREF="#lbAB">NAME</A><DD>
-<DT><A HREF="#lbAC">DESCRIPTION</A><DD>
-<DT><A HREF="#lbAD">OPTIONS</A><DD>
-<DT><A HREF="#lbAE">FILES</A><DD>
-<DT><A HREF="#lbAF">SEE ALSO</A><DD>
-<DT><A HREF="#lbAG">AUTHOR</A><DD>
-</DL>
-<HR>
-This document was created by
-<A HREF="http://localhost/cgi-bin/man/man2html">man2html</A>,
-using the manual pages.<BR>
-Time: 21:17:00 GMT, August 24, 2003
-</BODY>
-</HTML>
+++ /dev/null
-## Sample initialization file for GNU nano
-## Please note that you must have configured nano with --enable-nanorc
-## for this file to be read! Also note that characters specially
-## interpreted by the shell should not be escaped here.
-##
-## To make sure a value is not enabled, use "unset <option>"
-##
-## For the options that take parameters, the default value is given.
-## Other options are unset by default.
-
-## Use auto-indentation
-# set autoindent
-
-## Backup files to filename~
-# set backup
-
-## Constantly display the cursor position in the status bar.
-# set const
-
-## Use cut to end of line with ^K by default
-# set cut
-
-## Set the line length for wrapping text and justifying paragraphs.
-## If fill is negative, the line length will be the screen width less
-## this number.
-##
-# set fill -8
-
-## Enable ~/.nano_history for saving and reading search/replace strings.
-# set historylog
-
-## Allow multiple file buffers (using ^R inserts into separate buffer).
-## You must have configured with --enable-multibuffer or --enable-extra
-## for this to work.
-##
-## set multibuffer
-
-## Don't convert files from DOS/Mac format
-# set noconvert
-
-## Don't follow symlinks when writing files
-# set nofollow
-
-## Don't display the help lists at the bottom of the screen
-# set nohelp
-
-## Don't wrap text at all
-# set nowrap
-
-## Set operating directory. nano will not read or write files outside
-## this directory and its subdirectories. Also, the current directory
-## is changed to here, so files are inserted from this dir. A blank
-## string means the operating directory feature is turned off.
-##
-# set operatingdir ""
-
-## Preserve the XON and XOFF keys (^Q and ^S)
-# set preserve
-
-## The email-quote string, used to justify email-quoted paragraphs.
-## This is an extended regular expression if your system supports them,
-## otherwise a literal string. Default:
-# set quotestr "^([ ]*[\|>:}#])+"
-## if you have regexps, otherwise:
-# set quotestr "> "
-## You can get old nano quoted-justify behavior via:
-# set quotestr "(> )+"
-
-## Fix Backspace if it acts like Delete
-# set rebinddelete
-
-## Do extended regular expression searches by default
-# set regexp
-
-## Use smooth scrolling as the default
-# set smooth
-
-## Use this spelling checker instead of the internal one. This option
-## does not properly have a default value.
-##
-# set speller "aspell -c"
-
-## Allow nano to be suspended with ^Z
-# set suspend
-
-## Use this tab size instead of the default; it must be greater than 0
-# set tabsize 8
-
-## Save automatically on exit, don't prompt
-# set tempfile
-
-## Disallow file modification, why would you want this in an rc file? ;)
-# set view
-
-## Color setup
-## Format:
-## syntax "short description" ["filename regex" ...]
-## color foreground,background "regex" ["regex"...]
-##
-## Legal colors: white, black, red, blue, green, yellow, magenta, cyan.
-## You may use the prefix "bright" to mean a stronger color highlight.
-##
-## To use multi-line regexes use the start="regex" end="regex" format.
-##
-## If your system supports transparency, not specifying a background
-## color will use a transparent color. If you don't want this, be sure
-## to set the background color to black or white.
-##
-# syntax "c-file" "\.(c|h)$"
-# color red "\<[A-Z_]{2,}\>"
-# color green "\<(float|char|int|void|static|const|struct)\>"
-# color brightyellow "\<(if|while|do|else|case|switch)\>"
-# color brightcyan "^ *# *(define|include|ifn?def|endif|elif|else|if)"
-##
-## You will in general want your comments and strings to come last,
-## because syntax highlighting rules will be applied in the order they
-## are read in.
-##
-# color brightyellow "<[^= ]*>" ""(\\.|[^\"])*""
-##
-## This string is VERY resource intensive!!!
-# color brightyellow start=""(\\.|[^\"])*\\ *$" end="^(\\.|[^\"])*""
-##
-## And we want to have some nice comment highlighting too
-# color brightblue "//.*"
-# color brightblue start="/\*" end="\*/"
-
-## Here is a short example for HTML
-# syntax "HTML" "\.html$"
-# color blue start="<" end=">"
-# color red "&[^; ]*;"
-
-## Here is a short example for TeX files
-# syntax "TeX" "\.tex$"
-# color green "\\.|\\[A-Za-z]*"
-# color magenta "[{}]"
-# color blue "%.*"
-
-## Here is an example for quoted emails (under e.g. mutt)
-# syntax "mutt"
-# color green "^>.*"
-
-## Here is an example for groff
-##
-# syntax "groff" "\.ms$" "\.mm$" "\.me$" "\.tmac$" "^tmac." ".rof"
-## The argument of .nr or .ds
-# color cyan "^\.ds [^ ]*"
-# color cyan "^\.nr [^ ]*"
-## Single character escapes
-# color brightmagenta "\\."
-## Highlight the argument of \f or \s in the same color
-# color brightmagenta "\\f."
-# color brightmagenta "\\f\(.."
-# color brightmagenta "\\s(\+|\-)?[0-9]"
-## \n
-# color cyan "(\\|\\\\)n."
-# color cyan "(\\|\\\\)n\(.."
-# color cyan start="(\\|\\\\)n\[" end="]"
-## Requests
-# color brightgreen "^\. *[^ ]*"
-## Comments
-# color yellow "^\.\\\".*$"
-## Strings
-# color green "(\\|\\\\)\*."
-# color green "(\\|\\\\)\*\(.."
-# color green start="(\\|\\\\)\*\[" end="]"
-## Characters
-# color brightred "\\\(.."
-# color brightred start="\\\[" end="]"
-## Macro arguments
-# color brightcyan "\\\\\$[1-9]"
-
-## Here is an example for perl
-##
-# syntax "perl" "\.p[lm]$"
-# color red "\<(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork)|get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join|keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek|seekdir|se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr|y|truncate|umask|un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\>"
-# color magenta "\<(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\>"
-# color cyan start="[$@%]" end="( |\\W|-)"
-# color yellow "".*"|qq\|.*\|"
-# color white "[sm]/.*/"
-# color white start="(^use| = new)" end=";"
-# color green "#.*"
-# color yellow start="<< 'STOP'" end="STOP"
-
-## Here is an example for Java source
-##
-# syntax "Java source" "\.java$"
-# color green "\<(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\>"
-# color red "\<(break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\>"
-# color cyan "\<(abstract|class|extends|final|implements|import|instanceof|interface|native|package|private|protected|public|static|strictfp|super|synchronized|throws|volatile)\>"
-# color red ""[^\"]*""
-# color yellow "\<(true|false|null)\>"
-# color blue "//.*"
-# color blue start="/\*" end="\*/"
-# color brightblue start="/\*\*" end="\*/"
-# color brightgreen,green " +$"
-
-## Here is an example for your .nanorc
-##
-# syntax "nanorc" "(\.)?nanorc$"
-# color brightwhite "^ *(set|unset|syntax|color).*$"
-# color cyan "^ *(set|unset) +(autoindent|backup|const|cut|fill|historylog|multibuffer|noconvert|nofollow|nohelp|nowrap|operatingdir|preserve|quotestr|rebinddelete|regexp|smooth|speller|suspend|tabsize|tempfile|view)"
-# color green "^ *(set|unset|syntax)\>"
-# color yellow "^ *color +(bright)?(white|black|red|blue|green|yellow|magenta|cyan)(,(white|black|red|blue|green|yellow|magenta|cyan))?\>"
-# color magenta "^ *color\>" "\<(start|end)="
-# color white "\"(\\.|[^\"])*\""
-# color blue "^ *#.*$"
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * proto.h *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-/* Externs */
-
-#include <sys/stat.h>
-
-#ifdef HAVE_REGEX_H
-#include <regex.h>
-#endif
-
-#include "nano.h"
-
-extern int wrap_at;
-extern int editwinrows;
-extern int current_x, current_y, totlines;
-extern int placewewant;
-#ifndef NANO_SMALL
-extern int mark_beginx;
-#endif
-extern long totsize;
-extern int temp_opt;
-extern int wrap_at, flags, tabsize;
-extern int search_last_line;
-extern int search_offscreen;
-extern int currslen;
-
-#ifndef DISABLE_JUSTIFY
-extern char *quotestr;
-#endif
-
-extern WINDOW *edit, *topwin, *bottomwin;
-extern char *filename;
-extern struct stat originalfilestat;
-extern char *answer;
-extern char *hblank;
-#ifndef DISABLE_HELP
-extern char *help_text;
-#endif
-extern char *last_search;
-extern char *last_replace;
-#ifndef DISABLE_OPERATINGDIR
-extern char *operating_dir;
-extern char *full_operating_dir;
-#endif
-#ifndef DISABLE_SPELLER
-extern char *alt_speller;
-#endif
-
-extern int resetstatuspos;
-extern struct stat fileinfo;
-extern filestruct *current, *fileage, *edittop, *editbot, *filebot;
-extern filestruct *cutbuffer;
-#ifndef NANO_SMALL
-extern filestruct *mark_beginbuf;
-#endif
-
-#ifdef ENABLE_MULTIBUFFER
-extern openfilestruct *open_files;
-#endif
-
-#ifdef ENABLE_COLOR
-extern const colortype *colorstrings;
-extern syntaxtype *syntaxes;
-extern char *syntaxstr;
-#endif
-
-extern shortcut *shortcut_list;
-extern shortcut *main_list, *whereis_list;
-extern shortcut *replace_list, *goto_list;
-extern shortcut *writefile_list, *insertfile_list;
-extern shortcut *replace_list_2;
-#ifndef NANO_SMALL
-extern shortcut *extcmd_list;
-#endif
-#ifndef DISABLE_HELP
-extern shortcut *help_list;
-#endif
-#ifndef DISABLE_SPELLER
-extern shortcut *spell_list;
-#endif
-#ifndef DISABLE_BROWSER
-extern shortcut *browser_list, *gotodir_list;
-#endif
-
-#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
-extern const shortcut *currshortcut;
-#endif
-
-#ifdef HAVE_REGEX_H
-extern regex_t search_regexp;
-extern regmatch_t regmatches[10];
-#ifdef ENABLE_COLOR
-extern regex_t syntaxfile_regexp;
-extern regmatch_t synfilematches[1];
-#endif /* ENABLE_COLOR */
-#endif /* HAVE_REGEX_H */
-
-#ifndef NANO_SMALL
-extern toggle *toggles;
-#endif
-
-#ifndef NANO_SMALL
-extern historyheadtype search_history;
-extern historyheadtype replace_history;
-#endif
-
-extern int curses_ended;
-
-/* Functions we want available */
-
-/* Public functions in color.c */
-#ifdef ENABLE_COLOR
-void set_colorpairs(void);
-void do_colorinit(void);
-void update_color(void);
-#endif /* ENABLE_COLOR */
-
-/* Public functions in cut.c */
-filestruct *get_cutbottom(void);
-void add_to_cutbuffer(filestruct *inptr);
-void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot,
- size_t bot_x, int destructive);
-int do_cut_text(void);
-int do_uncut_text(void);
-
-/* Public functions in files.c */
-void load_file(int update);
-void new_file(void);
-filestruct *read_line(char *buf, filestruct *prev, int *line1ins, int len);
-int read_file(FILE *f, const char *filename, int quiet);
-int open_file(const char *filename, int insert, int quiet);
-char *get_next_filename(const char *name);
-int do_insertfile(int loading_file);
-int do_insertfile_void(void);
-#ifdef ENABLE_MULTIBUFFER
-openfilestruct *make_new_opennode(openfilestruct *prevnode);
-void splice_opennode(openfilestruct *begin, openfilestruct *newnode, openfilestruct *end);
-void unlink_opennode(const openfilestruct *fileptr);
-void delete_opennode(openfilestruct *fileptr);
-void free_openfilestruct(openfilestruct *src);
-int add_open_file(int update);
-int load_open_file(void);
-int open_prevfile(int closing_file);
-int open_prevfile_void(void);
-int open_nextfile(int closing_file);
-int open_nextfile_void(void);
-int close_open_file(void);
-#endif
-#if !defined(DISABLE_SPELLER) || !defined(DISABLE_OPERATINGDIR)
-char *get_full_path(const char *origpath);
-#endif
-#ifndef DISABLE_SPELLER
-char *check_writable_directory(const char *path);
-char *safe_tempnam(const char *dirname, const char *filename_prefix);
-#endif
-#ifndef DISABLE_OPERATINGDIR
-void init_operating_dir(void);
-int check_operating_dir(const char *currpath, int allow_tabcomp);
-#endif
-int write_file(const char *name, int tmp, int append, int nonamechange);
-int do_writeout(const char *path, int exiting, int append);
-int do_writeout_void(void);
-char *real_dir_from_tilde(const char *buf);
-#ifndef DISABLE_TABCOMP
-int append_slash_if_dir(char *buf, int *lastwastab, int *place);
-char **username_tab_completion(char *buf, int *num_matches);
-char **cwd_tab_completion(char *buf, int *num_matches);
-char *input_tab(char *buf, int place, int *lastwastab, int *newplace, int *list);
-#endif
-#ifndef DISABLE_BROWSER
-struct stat filestat(const char *path);
-int diralphasort(const void *va, const void *vb);
-void free_charptrarray(char **array, int len);
-const char *tail(const char *foo);
-void striponedir(char *foo);
-int readable_dir(const char *path);
-char **browser_init(const char *path, int *longest, int *numents);
-char *do_browser(const char *inpath);
-char *do_browse_from(const char *inpath);
-#endif
-
-/* Public functions in global.c */
-int length_of_list(const shortcut *s);
-void sc_init_one(shortcut **shortcutage, int key, const char *desc,
-#ifndef DISABLE_HELP
- const char *help,
-#endif
- int alt, int misc1, int misc2, int view, int (*func) (void));
-#ifndef NANO_SMALL
-void toggle_init_one(int val, const char *desc, int flag);
-void toggle_init(void);
-#ifdef DEBUG
-void free_toggles(void);
-#endif
-#endif
-void free_shortcutage(shortcut **shortcutage);
-void shortcut_init(int unjustify);
-#ifdef DEBUG
-void thanks_for_all_the_fish(void);
-#endif
-
-/* Public functions in move.c */
-int do_home(void);
-int do_end(void);
-void page_up(void);
-int do_page_up(void);
-int do_page_down(void);
-int do_up(void);
-int do_down(void);
-int do_left(void);
-int do_right(void);
-
-/* Public functions in nano.c */
-RETSIGTYPE finish(int sigage);
-void die(const char *msg, ...);
-void die_save_file(const char *die_filename);
-void die_too_small(void);
-void print_view_warning(void);
-void global_init(int save_cutbuffer);
-void window_init(void);
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
-void mouse_init(void);
-#endif
-#ifndef DISABLE_HELP
-void help_init(void);
-#endif
-filestruct *make_new_node(filestruct *prevnode);
-filestruct *copy_node(const filestruct *src);
-void splice_node(filestruct *begin, filestruct *newnode, filestruct *end);
-void unlink_node(const filestruct *fileptr);
-void delete_node(filestruct *fileptr);
-filestruct *copy_filestruct(const filestruct *src);
-void free_filestruct(filestruct *src);
-void renumber_all(void);
-void renumber(filestruct *fileptr);
-void print1opt(const char *shortflag, const char *longflag,
- const char *desc);
-void usage(void);
-void version(void);
-void do_early_abort(void);
-int no_help(void);
-#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP) || defined(NANO_SMALL)
-void nano_disabled_msg(void);
-#endif
-#ifndef NANO_SMALL
-RETSIGTYPE cancel_fork(int signal);
-int open_pipe(const char *command);
-#endif
-#ifndef DISABLE_MOUSE
-#ifdef NCURSES_MOUSE_VERSION
-void do_mouse(void);
-#endif
-#endif
-void do_char(char ch);
-int do_backspace(void);
-int do_delete(void);
-int do_tab(void);
-int do_enter(void);
-int do_next_word(void);
-int do_prev_word(void);
-int do_mark(void);
-void wrap_reset(void);
-#ifndef DISABLE_WRAPPING
-int do_wrap(filestruct *inptr);
-#endif
-#ifndef DISABLE_SPELLER
-int do_int_spell_fix(const char *word);
-char *do_int_speller(char *tempfile_name);
-char *do_alt_speller(char *tempfile_name);
-#endif
-int do_spell(void);
-#if !defined(DISABLE_WRAPPING) && !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
-size_t indent_length(const char *line);
-#endif
-#ifndef DISABLE_JUSTIFY
-int justify_format(int changes_allowed, filestruct *line, size_t skip);
-#ifdef HAVE_REGEX_H
-size_t quote_length(const char *line, const regex_t *qreg);
-#else
-size_t quote_length(const char *line);
-#endif
-#ifdef HAVE_REGEX_H
-# define IFREG(a, b) a, b
-#else
-# define IFREG(a, b) a
-#endif
-int quotes_match(const char *a_line, size_t a_quote,
- IFREG(const char *b_line, const regex_t *qreg));
-size_t indents_match(const char *a_line, size_t a_indent,
- const char *b_line, size_t b_indent);
-filestruct *backup_lines(filestruct *first_line, size_t par_len,
- size_t quote_len);
-int breakable(const char *line, int goal);
-int break_line(const char *line, int goal, int force);
-int do_para_operation(int operation);
-#endif /* !DISABLE_JUSTIFY */
-int do_justify(void);
-#ifndef DISABLE_JUSTIFY
-int do_para_begin(void);
-int do_para_end(void);
-#endif
-int do_exit(void);
-void signal_init(void);
-RETSIGTYPE handle_hupterm(int signal);
-RETSIGTYPE do_suspend(int signal);
-RETSIGTYPE do_cont(int signal);
-#ifndef NANO_SMALL
-void handle_sigwinch(int s);
-#endif
-void print_numlock_warning(void);
-#ifndef NANO_SMALL
-void do_toggle(const toggle *which);
-#endif
-int abcd(int input);
-
-/* Public functions in rcfile.c */
-#ifdef ENABLE_NANORC
-void rcfile_error(const char *msg, ...);
-void rcfile_msg(const char *msg, ...);
-char *parse_next_word(char *ptr);
-char *parse_argument(char *ptr);
-#ifdef ENABLE_COLOR
-int colortoint(const char *colorname, int *bright);
-char *parse_next_regex(char *ptr);
-int nregcomp(regex_t *preg, const char *regex, int flags);
-void parse_syntax(char *ptr);
-void parse_colors(char *ptr);
-#endif /* ENABLE_COLOR */
-void parse_rcfile(FILE *rcstream);
-void do_rcfile(void);
-#endif /* ENABLE_NANORC */
-
-/* Public functions in search.c */
-#ifdef HAVE_REGEX_H
-int regexp_init(const char *regexp);
-void regexp_cleanup(void);
-#endif
-void not_found_msg(const char *str);
-void search_abort(void);
-void search_init_globals(void);
-int search_init(int replacing);
-int is_whole_word(int curr_pos, const char *datastr, const char *searchword);
-filestruct *findnextstr(int quiet, int bracket_mode,
- const filestruct *begin, int beginx,
- const char *needle);
-int do_search(void);
-int do_research(void);
-void replace_abort(void);
-#ifdef HAVE_REGEX_H
-int replace_regexp(char *string, int create_flag);
-#endif
-char *replace_line(void);
-int do_replace_loop(const char *prevanswer, const filestruct *begin,
- int *beginx, int wholewords, int *i);
-int do_replace(void);
-int do_gotoline(int line, int save_pos);
-int do_gotoline_void(void);
-#if defined (ENABLE_MULTIBUFFER) || !defined (DISABLE_SPELLER)
-void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant);
-#endif
-int do_find_bracket(void);
-#ifndef NANO_SMALL
-void history_init(void);
-historytype *find_node(historytype *h, char *s);
-void remove_node(historytype *r);
-void insert_node(historytype *h, const char *s);
-void update_history(historyheadtype *h, char *s);
-char *get_history_older(historyheadtype *h);
-char *get_history_newer(historyheadtype *h);
-char *get_history_completion(historyheadtype *h, char *s);
-void free_history(historyheadtype *h);
-#ifdef ENABLE_NANORC
-void load_history(void);
-void save_history(void);
-#endif
-#endif
-
-/* Public functions in utils.c */
-#ifdef BROKEN_REGEXEC
-int regexec_safe(const regex_t *preg, const char *string, size_t nmatch,
- regmatch_t pmatch[], int eflags);
-#endif
-int is_cntrl_char(int c);
-int num_of_digits(int n);
-void align(char **strp);
-void null_at(char **data, size_t index);
-void unsunder(char *str, size_t true_len);
-void sunder(char *str);
-#ifndef HAVE_STRCASECMP
-int nstricmp(const char *s1, const char *s2);
-#endif
-#ifndef HAVE_STRNCASECMP
-int nstrnicmp(const char *s1, const char *s2, size_t n);
-#endif
-#ifndef NANO_SMALL
-const char *revstrstr(const char *haystack, const char *needle,
- const char *rev_start);
-const char *revstristr(const char *haystack, const char *needle,
- const char *rev_start);
-#endif
-const char *stristr(const char *haystack, const char *needle);
-const char *strstrwrapper(const char *haystack, const char *needle,
- const char *rev_start, int line_pos);
-void nperror(const char *s);
-void *nmalloc(size_t howmuch);
-void *nrealloc(void *ptr, size_t howmuch);
-char *mallocstrcpy(char *dest, const char *src);
-void new_magicline(void);
-#ifndef DISABLE_TABCOMP
-int check_wildcard_match(const char *text, const char *pattern);
-#endif
-
-/* Public functions in winio.c */
-int get_kbinput(WINDOW *win, int *meta, int rebind_delete);
-char *get_verbatim_kbinput(WINDOW *win, int *kbinput_len);
-int get_ignored_kbinput(WINDOW *win);
-int get_accepted_kbinput(WINDOW *win, int kbinput, int *meta,
- int rebind_delete);
-int get_ascii_kbinput(WINDOW *win, int kbinput);
-int get_escape_seq_kbinput(WINDOW *win, int kbinput);
-int get_skip_tilde_kbinput(WINDOW *win, int errval, int retval);
-int do_first_line(void);
-int do_last_line(void);
-int xpt(const filestruct *fileptr, int index);
-size_t xplustabs(void);
-size_t actual_x(const filestruct *fileptr, size_t xplus);
-size_t strnlenpt(const char *buf, size_t size);
-size_t strlenpt(const char *buf);
-void blank_bottombars(void);
-void blank_bottomwin(void);
-void blank_edit(void);
-void blank_statusbar(void);
-void blank_statusbar_refresh(void);
-void check_statblank(void);
-void nanoget_repaint(const char *buf, const char *inputbuf, int x);
-int nanogetstr(int allowtabs, const char *buf, const char *def,
-#ifndef NANO_SMALL
- historyheadtype *history_list,
-#endif
- const shortcut *s
-#ifndef DISABLE_TABCOMP
- , int *list
-#endif
- );
-void set_modified(void);
-void titlebar(const char *path);
-void bottombars(const shortcut *s);
-void onekey(const char *keystroke, const char *desc, int len);
-#ifndef NDEBUG
-int check_linenumbers(const filestruct *fileptr);
-#endif
-int get_page_start(int column);
-void reset_cursor(void);
-void add_marked_sameline(int begin, int end, filestruct *fileptr, int y,
- int virt_cur_x, int this_page);
-void edit_add(const filestruct *fileptr, int yval, int start
-#ifndef NANO_SMALL
- , int virt_mark_beginx, int virt_cur_x
-#endif
- );
-void update_line(filestruct *fileptr, int index);
-void update_cursor(void);
-void center_cursor(void);
-void edit_refresh(void);
-void edit_refresh_clearok(void);
-void edit_update(filestruct *fileptr, topmidbotnone location);
-int statusq(int tabs, const shortcut *s, const char *def,
-#ifndef NANO_SMALL
- historyheadtype *history_list,
-#endif
- const char *msg, ...);
-int do_yesno(int all, int leavecursor, const char *msg, ...);
-int total_refresh(void);
-void display_main_list(void);
-void statusbar(const char *msg, ...);
-int do_cursorpos(int constant);
-int do_cursorpos_void(void);
-int line_len(const char *ptr);
-int do_help(void);
-void do_replace_highlight(int highlight_flag, const char *word);
-void fix_editbot(void);
-#ifdef DEBUG
-void dump_buffer(const filestruct *inptr);
-void dump_buffer_reverse(void);
-#endif
-#ifdef NANO_EXTRA
-void do_credits(void);
-#endif
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * rcfile.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-#ifdef ENABLE_NANORC
-
-const static rcoption rcopts[] = {
-#ifndef NANO_SMALL
- {"autoindent", AUTOINDENT},
- {"backup", BACKUP_FILE},
-#endif
- {"const", CONSTUPDATE},
-#ifndef NANO_SMALL
- {"cut", CUT_TO_END},
-#endif
-#ifndef DISABLE_WRAPJUSTIFY
- {"fill", 0},
-#endif
-#ifndef NANO_SMALL
- {"historylog", HISTORYLOG},
-#endif
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- {"mouse", USE_MOUSE},
-#endif
-#ifdef ENABLE_MULTIBUFFER
- {"multibuffer", MULTIBUFFER},
-#endif
-#ifndef NANO_SMALL
- {"noconvert", NO_CONVERT},
-#endif
- {"nofollow", NOFOLLOW_SYMLINKS},
- {"nohelp", NO_HELP},
-#ifndef DISABLE_WRAPPING
- {"nowrap", NO_WRAP},
-#endif
-#ifndef DISABLE_OPERATINGDIR
- {"operatingdir", 0},
-#endif
- {"preserve", PRESERVE},
- {"rebinddelete", REBIND_DELETE},
-#ifndef DISABLE_JUSTIFY
- {"quotestr", 0},
-#endif
-#ifdef HAVE_REGEX_H
- {"regexp", USE_REGEXP},
-#endif
-#ifndef NANO_SMALL
- {"smooth", SMOOTHSCROLL},
-#endif
-#ifndef DISABLE_SPELLER
- {"speller", 0},
-#endif
- {"suspend", SUSPEND},
- {"tabsize", 0},
- {"tempfile", TEMP_OPT},
- {"view", VIEW_MODE},
- {NULL, 0}
-};
-
-static int errors = 0;
-static int lineno = 0;
-static char *nanorc;
-
-/* We have an error in some part of the rcfile; put it on stderr and
- make the user hit return to continue starting up nano. */
-void rcfile_error(const char *msg, ...)
-{
- va_list ap;
-
- fprintf(stderr, "\n");
- if (lineno > 0)
- fprintf(stderr, _("Error in %s on line %d: "), nanorc, lineno);
-
- va_start(ap, msg);
- vfprintf(stderr, msg, ap);
- va_end(ap);
- fprintf(stderr, _("\nPress return to continue starting nano\n"));
-
- while (getchar() != '\n');
-}
-
-/* Just print the error (one of many, perhaps) but don't abort, yet. */
-void rcfile_msg(const char *msg, ...)
-{
- va_list ap;
-
- if (!errors) {
- errors = 1;
- fprintf(stderr, "\n");
- }
- va_start(ap, msg);
- vfprintf(stderr, msg, ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-
-/* Parse the next word from the string. Returns NULL if we hit EOL. */
-char *parse_next_word(char *ptr)
-{
- while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n' && *ptr != '\0')
- ptr++;
-
- if (*ptr == '\0')
- return NULL;
-
- /* Null terminate and advance ptr */
- *ptr++ = 0;
-
- while (*ptr == ' ' || *ptr == '\t')
- ptr++;
-
- return ptr;
-}
-
-/* The keywords operatingdir, fill, tabsize, speller, and quotestr take
- * an argument when set. Among these, operatingdir, speller, and
- * quotestr have to allow tabs and spaces in the argument. Thus, if the
- * next word starts with a ", we say it ends with the last " of the line.
- * Otherwise, the word is interpreted as usual. That is so the arguments
- * can contain "s too. */
-char *parse_argument(char *ptr)
-{
- const char *ptr_bak = ptr;
- char *last_quote = NULL;
-
- assert(ptr != NULL);
-
- if (*ptr != '"')
- return parse_next_word(ptr);
-
- do {
- ptr++;
- if (*ptr == '"')
- last_quote = ptr;
- } while (*ptr != '\n' && *ptr != '\0');
-
- if (last_quote == NULL) {
- if (*ptr == '\0')
- ptr = NULL;
- else
- *ptr++ = '\0';
- rcfile_error(_("Argument %s has unterminated \""), ptr_bak);
- } else {
- *last_quote = '\0';
- ptr = last_quote + 1;
- }
- if (ptr != NULL)
- while (*ptr == ' ' || *ptr == '\t')
- ptr++;
- return ptr;
-}
-
-#ifdef ENABLE_COLOR
-
-int colortoint(const char *colorname, int *bright)
-{
- int mcolor = 0;
-
- if (colorname == NULL)
- return -1;
-
- if (!strncasecmp(colorname, "bright", 6)) {
- *bright = 1;
- colorname += 6;
- }
-
- if (!strcasecmp(colorname, "green"))
- mcolor = COLOR_GREEN;
- else if (!strcasecmp(colorname, "red"))
- mcolor = COLOR_RED;
- else if (!strcasecmp(colorname, "blue"))
- mcolor = COLOR_BLUE;
- else if (!strcasecmp(colorname, "white"))
- mcolor = COLOR_WHITE;
- else if (!strcasecmp(colorname, "yellow"))
- mcolor = COLOR_YELLOW;
- else if (!strcasecmp(colorname, "cyan"))
- mcolor = COLOR_CYAN;
- else if (!strcasecmp(colorname, "magenta"))
- mcolor = COLOR_MAGENTA;
- else if (!strcasecmp(colorname, "black"))
- mcolor = COLOR_BLACK;
- else {
- rcfile_error(_("Color %s not understood.\n"
- "Valid colors are \"green\", \"red\", \"blue\", \n"
- "\"white\", \"yellow\", \"cyan\", \"magenta\" and \n"
- "\"black\", with the optional prefix \"bright\" \n"
- "for foreground colors.\n"), colorname);
- mcolor = -1;
- }
- return mcolor;
-}
-
-char *parse_next_regex(char *ptr)
-{
- while ((*ptr != '"' || (*(ptr + 1) != ' ' && *(ptr + 1) != '\n'))
- && *ptr != '\n' && *ptr != '\0')
- ptr++;
-
- if (*ptr == '\0')
- return NULL;
-
- /* Null terminate and advance ptr */
- *ptr++ = '\0';
-
- while (*ptr == ' ' || *ptr == '\t')
- ptr++;
-
- return ptr;
-}
-
-/* Compile the regular expression regex to preg. Returns FALSE on success,
- TRUE if the expression is invalid. */
-int nregcomp(regex_t *preg, const char *regex, int flags)
-{
- int rc = regcomp(preg, regex, REG_EXTENDED | flags);
-
- if (rc != 0) {
- size_t len = regerror(rc, preg, NULL, 0);
- char *str = charalloc(len);
-
- regerror(rc, preg, str, len);
- rcfile_error(_("Bad regex \"%s\": %s"), regex, str);
- free(str);
- }
- return rc != 0;
-}
-
-void parse_syntax(char *ptr)
-{
- syntaxtype *tmpsyntax = NULL;
- const char *fileregptr = NULL, *nameptr = NULL;
- exttype *endext = NULL;
- /* The end of the extensions list for this syntax. */
-
- while (*ptr == ' ')
- ptr++;
-
- if (*ptr == '\n' || *ptr == '\0')
- return;
-
- if (*ptr != '"') {
- rcfile_error(_("Regex strings must begin and end with a \" character\n"));
- return;
- }
- ptr++;
-
- nameptr = ptr;
- ptr = parse_next_regex(ptr);
-
- if (ptr == NULL) {
- rcfile_error(_("Missing syntax name"));
- return;
- }
-
- if (syntaxes == NULL) {
- syntaxes = (syntaxtype *)nmalloc(sizeof(syntaxtype));
- tmpsyntax = syntaxes;
- SET(COLOR_SYNTAX);
- } else {
- for (tmpsyntax = syntaxes; tmpsyntax->next != NULL;
- tmpsyntax = tmpsyntax->next)
- ;
- tmpsyntax->next = (syntaxtype *)nmalloc(sizeof(syntaxtype));
- tmpsyntax = tmpsyntax->next;
-#ifdef DEBUG
- fprintf(stderr, "Adding new syntax after 1st\n");
-#endif
- }
- tmpsyntax->desc = mallocstrcpy(NULL, nameptr);
- tmpsyntax->color = NULL;
- tmpsyntax->extensions = NULL;
- tmpsyntax->next = NULL;
-#ifdef DEBUG
- fprintf(stderr, "Starting a new syntax type\n");
- fprintf(stderr, "string val=%s\n", nameptr);
-#endif
-
- /* Now load in the extensions to their part of the struct */
- while (*ptr != '\n' && *ptr != '\0') {
- exttype *newext;
- /* The new extension structure. */
-
- while (*ptr != '"' && *ptr != '\n' && *ptr != '\0')
- ptr++;
-
- if (*ptr == '\n' || *ptr == '\0')
- return;
- ptr++;
-
- fileregptr = ptr;
- ptr = parse_next_regex(ptr);
-
- newext = (exttype *)nmalloc(sizeof(exttype));
- if (nregcomp(&newext->val, fileregptr, REG_NOSUB))
- free(newext);
- else {
- if (endext == NULL)
- tmpsyntax->extensions = newext;
- else
- endext->next = newext;
- endext = newext;
- endext->next = NULL;
- }
- }
-}
-
-/* Parse the color stuff into the colorstrings array */
-void parse_colors(char *ptr)
-{
- int fg, bg, bright = 0;
- int expectend = 0; /* Do we expect an end= line? */
- char *fgstr;
- colortype *tmpcolor = NULL;
- syntaxtype *tmpsyntax = NULL;
-
- fgstr = ptr;
- ptr = parse_next_word(ptr);
-
- if (ptr == NULL) {
- rcfile_error(_("Missing color name"));
- return;
- }
-
- if (strstr(fgstr, ",")) {
- char *bgcolorname;
- strtok(fgstr, ",");
- bgcolorname = strtok(NULL, ",");
- if (!strncasecmp(bgcolorname, "bright", 6)) {
- rcfile_error(_("Background color %s cannot be bright"), bgcolorname);
- return;
- }
- bg = colortoint(bgcolorname, &bright);
- } else
- bg = -1;
-
- fg = colortoint(fgstr, &bright);
-
- /* Don't try and parse screwed up fg colors */
- if (fg == -1)
- return;
-
- if (syntaxes == NULL) {
- rcfile_error(_("Cannot add a color directive without a syntax line"));
- return;
- }
-
- for (tmpsyntax = syntaxes; tmpsyntax->next != NULL;
- tmpsyntax = tmpsyntax->next)
- ;
-
- /* Now the fun part, start adding regexps to individual strings
- in the colorstrings array, woo! */
-
- while (*ptr != '\0') {
- colortype *newcolor;
- /* The new color structure. */
- int cancelled = 0;
- /* The start expression was bad. */
-
- while (*ptr == ' ')
- ptr++;
-
- if (*ptr == '\n' || *ptr == '\0')
- break;
-
- if (!strncasecmp(ptr, "start=", 6)) {
- ptr += 6;
- expectend = 1;
- }
-
- if (*ptr != '"') {
- rcfile_error(_("Regex strings must begin and end with a \" character\n"));
- ptr = parse_next_regex(ptr);
- continue;
- }
- ptr++;
-
- newcolor = (colortype *)nmalloc(sizeof(colortype));
- fgstr = ptr;
- ptr = parse_next_regex(ptr);
- if (nregcomp(&newcolor->start, fgstr, 0)) {
- free(newcolor);
- cancelled = 1;
- } else {
- newcolor->fg = fg;
- newcolor->bg = bg;
- newcolor->bright = bright;
- newcolor->next = NULL;
- newcolor->end = NULL;
-
- if (tmpsyntax->color == NULL) {
- tmpsyntax->color = newcolor;
-#ifdef DEBUG
- fprintf(stderr, "Starting a new colorstring for fg %d bg %d\n",
- fg, bg);
-#endif
- } else {
- for (tmpcolor = tmpsyntax->color; tmpcolor->next != NULL;
- tmpcolor = tmpcolor->next)
- ;
-#ifdef DEBUG
- fprintf(stderr, "Adding new entry for fg %d bg %d\n", fg, bg);
-#endif
- tmpcolor->next = newcolor;
- }
- }
-
- if (expectend) {
- if (ptr == NULL || strncasecmp(ptr, "end=", 4)) {
- rcfile_error(_
- ("\"start=\" requires a corresponding \"end=\""));
- return;
- }
-
- ptr += 4;
-
- if (*ptr != '"') {
- rcfile_error(_
- ("Regex strings must begin and end with a \" character\n"));
- continue;
- }
- ptr++;
-
- fgstr = ptr;
- ptr = parse_next_regex(ptr);
-
- /* If the start regex was invalid, skip past the end regex to
- * stay in sync. */
- if (cancelled)
- continue;
- newcolor->end = (regex_t *)nmalloc(sizeof(regex_t));
- if (nregcomp(newcolor->end, fgstr, 0)) {
- free(newcolor->end);
- newcolor->end = NULL;
- }
- }
- }
-}
-
-#endif /* ENABLE_COLOR */
-
-/* Parse the RC file, once it has been opened successfully */
-void parse_rcfile(FILE *rcstream)
-{
- char *buf, *ptr, *keyword, *option;
- int set = 0, i, j;
-
- buf = charalloc(1024);
- while (fgets(buf, 1023, rcstream) != 0) {
- lineno++;
- ptr = buf;
- while (*ptr == ' ' || *ptr == '\t')
- ptr++;
-
- if (*ptr == '\n' || *ptr == '\0')
- continue;
-
- if (*ptr == '#') {
-#ifdef DEBUG
- fprintf(stderr, "%s: Read a comment\n", "parse_rcfile()");
-#endif
- continue; /* Skip past commented lines */
- }
-
- /* Else skip to the next space */
- keyword = ptr;
- ptr = parse_next_word(ptr);
- if (ptr == NULL)
- continue;
-
- /* Else try to parse the keyword */
- if (!strcasecmp(keyword, "set"))
- set = 1;
- else if (!strcasecmp(keyword, "unset"))
- set = -1;
-#ifdef ENABLE_COLOR
- else if (!strcasecmp(keyword, "syntax"))
- parse_syntax(ptr);
- else if (!strcasecmp(keyword, "color"))
- parse_colors(ptr);
-#endif /* ENABLE_COLOR */
- else {
- rcfile_msg(_("command %s not understood"), keyword);
- continue;
- }
-
- option = ptr;
- ptr = parse_next_word(ptr);
- /* We don't care if ptr == NULL, as it should if using proper syntax */
-
- if (set != 0) {
- for (i = 0; rcopts[i].name != NULL; i++) {
- if (!strcasecmp(option, rcopts[i].name)) {
-#ifdef DEBUG
- fprintf(stderr, "%s: Parsing option %s\n",
- "parse_rcfile()", rcopts[i].name);
-#endif
- if (set == 1) {
- if (!strcasecmp(rcopts[i].name, "tabsize")
-#ifndef DISABLE_OPERATINGDIR
- || !strcasecmp(rcopts[i].name, "operatingdir")
-#endif
-#ifndef DISABLE_WRAPJUSTIFY
- || !strcasecmp(rcopts[i].name, "fill")
-#endif
-#ifndef DISABLE_JUSTIFY
- || !strcasecmp(rcopts[i].name, "quotestr")
-#endif
-#ifndef DISABLE_SPELLER
- || !strcasecmp(rcopts[i].name, "speller")
-#endif
- ) {
- if (*ptr == '\n' || *ptr == '\0') {
- rcfile_error(_
- ("Option %s requires an argument"),
- rcopts[i].name);
- continue;
- }
- option = ptr;
- if (*option == '"')
- option++;
- ptr = parse_argument(ptr);
-#ifdef DEBUG
- fprintf(stderr, "option = %s\n", option);
-#endif
-#ifndef DISABLE_OPERATINGDIR
- if (!strcasecmp(rcopts[i].name, "operatingdir"))
- operating_dir = mallocstrcpy(NULL, option);
- else
-#endif
-#ifndef DISABLE_WRAPJUSTIFY
- if (!strcasecmp(rcopts[i].name, "fill")) {
- char *first_error;
-
- /* Using strtol() instead of atoi() lets
- * us accept 0 while checking other
- * errors. */
- j = (int)strtol(option, &first_error, 10);
- if (errno == ERANGE || *option == '\0' || *first_error != '\0')
- rcfile_error(_("Requested fill size %d invalid"),
- j);
- else
- wrap_at = j;
- } else
-#endif
-#ifndef DISABLE_JUSTIFY
- if (!strcasecmp(rcopts[i].name, "quotestr"))
- quotestr = mallocstrcpy(NULL, option);
- else
-#endif
-#ifndef DISABLE_SPELLER
- if (!strcasecmp(rcopts[i].name, "speller"))
- alt_speller = mallocstrcpy(NULL, option);
- else
-#endif
- {
- char *first_error;
-
- /* Using strtol instead of atoi lets us
- * accept 0 while checking other
- * errors. */
- j = (int)strtol(option, &first_error, 10);
- if (errno == ERANGE || *option == '\0' || *first_error != '\0')
- rcfile_error(_("Requested tab size %d invalid"),
- j);
- else
- tabsize = j;
- }
- } else
- SET(rcopts[i].flag);
-#ifdef DEBUG
- fprintf(stderr, "set flag %d!\n",
- rcopts[i].flag);
-#endif
- } else {
- UNSET(rcopts[i].flag);
-#ifdef DEBUG
- fprintf(stderr, "unset flag %d!\n",
- rcopts[i].flag);
-#endif
- }
- }
- }
- }
- }
- free(buf);
- if (errors)
- rcfile_error(_("Errors found in .nanorc file"));
-
- return;
-}
-
-/* The main rc file function, tries to open the rc file */
-void do_rcfile(void)
-{
- FILE *rcstream;
- const struct passwd *userage;
- uid_t euid = geteuid();
- char *homenv = getenv("HOME");
-
-#ifdef SYSCONFDIR
- assert(sizeof(SYSCONFDIR) == strlen(SYSCONFDIR) + 1);
- nanorc = charalloc(sizeof(SYSCONFDIR) + 7);
- sprintf(nanorc, "%s/nanorc", SYSCONFDIR);
- /* Try to open system nanorc */
- if ((rcstream = fopen(nanorc, "r")) != NULL) {
- /* Parse it! */
- parse_rcfile(rcstream);
- fclose(rcstream);
- }
-#endif
-
- lineno = 0;
-
- /* Rely on $HOME, fall back on getpwuid() */
- if (homenv != NULL) {
- nanorc = charealloc(nanorc, strlen(homenv) + 10);
- sprintf(nanorc, "%s/.nanorc", homenv);
- } else {
- userage = getpwuid(euid);
- endpwent();
-
- if (userage == NULL) {
- rcfile_error(_("I can't find my home directory! Wah!"));
- SET(NO_RCFILE);
- } else {
- nanorc = charealloc(nanorc, strlen(userage->pw_dir) + 9);
- sprintf(nanorc, "%s/.nanorc", userage->pw_dir);
-
- }
- }
-
- if (!ISSET(NO_RCFILE)) {
-
-#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
- /* If we've already read $SYSCONFDIR/nanorc (if it's there), we're
- root, and --disable-wrapping-as-root is used, turn wrapping off */
- if (euid == 0)
- SET(NO_WRAP);
-#endif
- if ((rcstream = fopen(nanorc, "r")) == NULL) {
- /* Don't complain about the file not existing */
- if (errno != ENOENT) {
- rcfile_error(_("Unable to open ~/.nanorc file, %s"),
- strerror(errno));
- SET(NO_RCFILE);
- }
- } else {
- parse_rcfile(rcstream);
- fclose(rcstream);
- }
- }
- lineno = 0;
-
- free(nanorc);
-#ifdef ENABLE_COLOR
- set_colorpairs();
-#endif
-}
-
-#endif /* ENABLE_NANORC */
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * search.c *
- * *
- * Copyright (C) 2000-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <errno.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-/* Regular expression helper functions */
-
-#ifdef HAVE_REGEX_H
-int regexp_init(const char *regexp)
-{
- /* Hmm, perhaps we should check for whether regcomp returns successfully */
- if (regcomp(&search_regexp, regexp, (ISSET(CASE_SENSITIVE) ? 0 : REG_ICASE)
- | REG_EXTENDED) != 0)
- return 0;
-
- SET(REGEXP_COMPILED);
- return 1;
-}
-
-void regexp_cleanup(void)
-{
- UNSET(REGEXP_COMPILED);
- regfree(&search_regexp);
-}
-#endif
-
-void not_found_msg(const char *str)
-{
- if (strlen(str) <= COLS / 2)
- statusbar(_("\"%s\" not found"), str);
- else {
- char *foo = mallocstrcpy(NULL, str);
-
- foo[COLS / 2] = '\0';
- statusbar(_("\"%s...\" not found"), foo);
- free(foo);
- }
-}
-
-void search_abort(void)
-{
- display_main_list();
- wrefresh(bottomwin);
- if (ISSET(MARK_ISSET))
- edit_refresh_clearok();
-
-#ifdef HAVE_REGEX_H
- if (ISSET(REGEXP_COMPILED))
- regexp_cleanup();
-#endif
-}
-
-void search_init_globals(void)
-{
- if (last_search == NULL) {
- last_search = charalloc(1);
- last_search[0] = '\0';
- }
- if (last_replace == NULL) {
- last_replace = charalloc(1);
- last_replace[0] = '\0';
- }
-}
-
-/* Set up the system variables for a search or replace. Return -1 on
- * abort, 0 on success, and 1 on rerun calling program. Return -2 to
- * run opposite program (search -> replace, replace -> search).
- *
- * replacing = 1 if we call from do_replace(), 0 if called from
- * do_search(). */
-int search_init(int replacing)
-{
- int i = 0;
- char *buf;
- static char *backupstring = NULL;
-#ifdef HAVE_REGEX_H
- const char *regex_error = _("Invalid regex \"%s\"");
-#endif /* HAVE_REGEX_H */
-
- search_init_globals();
-
- if (backupstring == NULL)
- backupstring = mallocstrcpy(backupstring, "");
-
-#ifndef NANO_SMALL
- search_history.current = (historytype *)&search_history.next;
-#endif
-
- if (last_search[0] != '\0') {
- buf = charalloc(COLS / 3 + 7);
- /* We use COLS / 3 here because we need to see more on the line */
- sprintf(buf, " [%.*s%s]", COLS / 3, last_search,
- strlen(last_search) > COLS / 3 ? "..." : "");
- } else {
- buf = charalloc(1);
- buf[0] = '\0';
- }
-
- /* This is now one simple call. It just does a lot */
- i = statusq(0, replacing ? replace_list : whereis_list, backupstring,
-#ifndef NANO_SMALL
- &search_history,
-#endif
- "%s%s%s%s%s%s",
- _("Search"),
-
- /* This string is just a modifier for the search prompt,
- no grammar is implied */
- ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : "",
-
- /* This string is just a modifier for the search prompt,
- no grammar is implied */
- ISSET(USE_REGEXP) ? _(" [Regexp]") : "",
-
- /* This string is just a modifier for the search prompt,
- no grammar is implied */
- ISSET(REVERSE_SEARCH) ? _(" [Backwards]") : "",
-
- replacing ? _(" (to replace)") : "",
- buf);
-
- /* Release buf now that we don't need it anymore */
- free(buf);
-
- /* Cancel any search, or just return with no previous search */
- if (i == -1 || (i < 0 && last_search[0] == '\0')) {
- statusbar(_("Search Cancelled"));
- reset_cursor();
- free(backupstring);
- backupstring = NULL;
-#ifndef NANO_SMALL
- search_history.current = search_history.next;
-#endif
- return -1;
- } else {
- switch (i) {
- case -2: /* Same string */
-#ifdef HAVE_REGEX_H
- if (ISSET(USE_REGEXP))
- /* If answer is "", use last_search! */
- if (regexp_init(last_search) == 0) {
- statusbar(regex_error, last_search);
- reset_cursor();
- free(backupstring);
- backupstring = NULL;
- return -3;
- }
-#endif
- break;
- case 0: /* They entered something new */
-#ifdef HAVE_REGEX_H
- if (ISSET(USE_REGEXP))
- if (regexp_init(answer) == 0) {
- statusbar(regex_error, answer);
- reset_cursor();
- free(backupstring);
- backupstring = NULL;
-#ifndef NANO_SMALL
- search_history.current = search_history.next;
-#endif
- return -3;
- }
-#endif
- free(backupstring);
- backupstring = NULL;
- last_replace[0] = '\0';
- break;
-#ifndef NANO_SMALL
- case TOGGLE_CASE_KEY:
- TOGGLE(CASE_SENSITIVE);
- backupstring = mallocstrcpy(backupstring, answer);
- return 1;
- case TOGGLE_BACKWARDS_KEY:
- TOGGLE(REVERSE_SEARCH);
- backupstring = mallocstrcpy(backupstring, answer);
- return 1;
-#ifdef HAVE_REGEX_H
- case TOGGLE_REGEXP_KEY:
- TOGGLE(USE_REGEXP);
- backupstring = mallocstrcpy(backupstring, answer);
- return 1;
-#endif
-#endif /* !NANO_SMALL */
- case NANO_OTHERSEARCH_KEY:
- backupstring = mallocstrcpy(backupstring, answer);
- return -2; /* Call the opposite search function */
- case NANO_FROMSEARCHTOGOTO_KEY:
- free(backupstring);
- backupstring = NULL;
-#ifndef NANO_SMALL
- search_history.current = search_history.next;
-#endif
- i = (int)strtol(answer, &buf, 10); /* Just testing answer here */
- if (!(errno == ERANGE || *answer == '\0' || *buf != '\0'))
- do_gotoline(-1, 0);
- else
- do_gotoline_void();
- return -3;
- default:
- do_early_abort();
- free(backupstring);
- backupstring = NULL;
- return -3;
- }
- }
- return 0;
-}
-
-int is_whole_word(int curr_pos, const char *datastr, const char *searchword)
-{
- size_t sln = curr_pos + strlen(searchword);
-
- /* start of line or previous character not a letter and end of line
- or next character not a letter */
- return (curr_pos < 1 || !isalpha((int)datastr[curr_pos - 1])) &&
- (sln == strlen(datastr) || !isalpha((int)datastr[sln]));
-}
-
-filestruct *findnextstr(int quiet, int bracket_mode,
- const filestruct *begin, int beginx,
- const char *needle)
-{
- filestruct *fileptr = current;
- const char *searchstr, *rev_start = NULL, *found = NULL;
- int current_x_find = 0;
-
- search_offscreen = 0;
-
- if (!ISSET(REVERSE_SEARCH)) { /* forward search */
- /* Argh, current_x is set to -1 by nano.c:do_int_spell_fix(), and
- * strlen returns size_t, which is unsigned. */
- assert(current_x < 0 || current_x <= strlen(fileptr->data));
- current_x_find = current_x;
- if (current_x_find < 0 || fileptr->data[current_x_find] != '\0')
- current_x_find++;
-
- searchstr = &fileptr->data[current_x_find];
-
- /* Look for needle in searchstr */
- while ((found = strstrwrapper(searchstr, needle, rev_start, current_x_find)) == NULL) {
-
- /* finished processing file, get out */
- if (search_last_line) {
- if (!quiet)
- not_found_msg(needle);
- update_line(fileptr, current_x);
- return NULL;
- }
-
- update_line(fileptr, 0);
-
- /* reset current_x_find between lines */
- current_x_find = 0;
-
- fileptr = fileptr->next;
-
- if (fileptr == editbot)
- search_offscreen = 1;
-
- /* EOF reached?, wrap around once */
- if (fileptr == NULL) {
- /* don't wrap if looking for bracket match */
- if (bracket_mode)
- return NULL;
- fileptr = fileage;
- search_offscreen = 1;
- if (!quiet)
- statusbar(_("Search Wrapped"));
- }
-
- /* Original start line reached */
- if (fileptr == begin)
- search_last_line = 1;
-
- searchstr = fileptr->data;
- }
-
- /* We found an instance */
- current_x_find = found - fileptr->data;
- /* Ensure we haven't wrapped around again! */
- if (search_last_line && current_x_find > beginx) {
- if (!quiet)
- not_found_msg(needle);
- return NULL;
- }
- }
-#ifndef NANO_SMALL
- else { /* reverse search */
- current_x_find = current_x - 1;
- /* Make sure we haven't passed the beginning of the string */
- rev_start = &fileptr->data[current_x_find];
- searchstr = fileptr->data;
-
- /* Look for needle in searchstr */
- while ((found = strstrwrapper(searchstr, needle, rev_start, current_x_find)) == NULL) {
- /* finished processing file, get out */
- if (search_last_line) {
- if (!quiet)
- not_found_msg(needle);
- return NULL;
- }
-
- update_line(fileptr, 0);
-
- /* reset current_x_find between lines */
- current_x_find = 0;
-
- fileptr = fileptr->prev;
-
- if (fileptr == edittop->prev)
- search_offscreen = 1;
-
- /* SOF reached?, wrap around once */
-/* ? */ if (fileptr == NULL) {
- if (bracket_mode)
- return NULL;
- fileptr = filebot;
- search_offscreen = 1;
- if (!quiet)
- statusbar(_("Search Wrapped"));
- }
- /* Original start line reached */
- if (fileptr == begin)
- search_last_line = 1;
-
- searchstr = fileptr->data;
- rev_start = fileptr->data + strlen(fileptr->data);
- }
-
- /* We found an instance */
- current_x_find = found - fileptr->data;
- /* Ensure we haven't wrapped around again! */
- if ((search_last_line) && (current_x_find < beginx)) {
- if (!quiet)
- not_found_msg(needle);
- return NULL;
- }
- }
-#endif /* !NANO_SMALL */
-
- /* Set globals now that we are sure we found something */
- current = fileptr;
- current_x = current_x_find;
-
- if (!bracket_mode) {
- update_line(current, current_x);
- placewewant = xplustabs();
- reset_cursor();
- }
- return fileptr;
-}
-
-/* Search for a string. */
-int do_search(void)
-{
- int i;
- filestruct *fileptr = current, *didfind;
- int fileptr_x = current_x;
-
- wrap_reset();
- i = search_init(0);
- switch (i) {
- case -1:
- current = fileptr;
- search_abort();
- return 0;
- case -3:
- search_abort();
- return 0;
- case -2:
- do_replace();
- return 0;
- case 1:
- do_search();
- search_abort();
- return 1;
- }
-
- /* If answer is now "", copy last_search into answer... */
- if (answer[0] == '\0')
- answer = mallocstrcpy(answer, last_search);
- else
- last_search = mallocstrcpy(last_search, answer);
-
-#ifndef NANO_SMALL
- /* add this search string to the search history list */
- if (answer[0] != '\0')
- update_history(&search_history, answer);
-#endif /* !NANO_SMALL */
-
- search_last_line = 0;
- didfind = findnextstr(FALSE, FALSE, current, current_x, answer);
-
- if (fileptr == current && fileptr_x == current_x && didfind != NULL)
- statusbar(_("This is the only occurrence"));
- else if (current->lineno <= edittop->lineno
- || current->lineno >= editbot->lineno)
- edit_update(current, CENTER);
-
- search_abort();
-
- return 1;
-}
-
-/* Search for the next string without prompting. */
-int do_research(void)
-{
- filestruct *fileptr = current, *didfind;
- int fileptr_x = current_x;
-#ifdef HAVE_REGEX_H
- const char *regex_error = _("Invalid regex \"%s\"");
-#endif /* HAVE_REGEX_H */
-
- wrap_reset();
- search_init_globals();
-
- if (last_search[0] != '\0') {
-
-#ifdef HAVE_REGEX_H
- if (ISSET(USE_REGEXP))
- if (regexp_init(last_search) == 0) {
- statusbar(regex_error, last_search);
- reset_cursor();
- return -3;
- }
-#endif
-
- search_last_line = 0;
- didfind = findnextstr(FALSE, FALSE, current, current_x, last_search);
-
- if (fileptr == current && fileptr_x == current_x && didfind != NULL)
- statusbar(_("This is the only occurrence"));
- else if (current->lineno <= edittop->lineno
- || current->lineno >= editbot->lineno)
- edit_update(current, CENTER);
-
- } else
- statusbar(_("No current search pattern"));
-
- search_abort();
-
- return 1;
-}
-
-void replace_abort(void)
-{
- /* Identical to search_abort, so we'll call it here. If it
- does something different later, we can change it back. For now
- it's just a waste to duplicate code */
- search_abort();
- placewewant = xplustabs();
-}
-
-#ifdef HAVE_REGEX_H
-int replace_regexp(char *string, int create_flag)
-{
- /* Split personality here - if create_flag is NULL, just calculate
- * the size of the replacement line (necessary because of
- * subexpressions like \1 \2 \3 in the replaced text). */
-
- char *c;
- int new_size = strlen(current->data) + 1;
- int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
-
- new_size -= search_match_count;
-
- /* Iterate through the replacement text to handle subexpression
- * replacement using \1, \2, \3, etc. */
-
- c = last_replace;
- while (*c != '\0') {
- if (*c != '\\') {
- if (create_flag)
- *string++ = *c;
- c++;
- new_size++;
- } else {
- int num = (int) *(c + 1) - (int) '0';
- if (num >= 1 && num <= 9) {
-
- int i = regmatches[num].rm_eo - regmatches[num].rm_so;
-
- if (num > search_regexp.re_nsub) {
- /* Ugh, they specified a subexpression that doesn't
- * exist. */
- return -1;
- }
-
- /* Skip over the replacement expression */
- c += 2;
-
- /* But add the length of the subexpression to new_size */
- new_size += i;
-
- /* And if create_flag is set, append the result of the
- * subexpression match to the new line */
- if (create_flag) {
- strncpy(string, current->data + current_x +
- regmatches[num].rm_so, i);
- string += i;
- }
-
- } else {
- if (create_flag)
- *string++ = *c;
- c++;
- new_size++;
- }
- }
- }
-
- if (create_flag)
- *string = '\0';
-
- return new_size;
-}
-#endif
-
-char *replace_line(void)
-{
- char *copy, *tmp;
- int new_line_size;
- int search_match_count;
-
- /* Calculate size of new line */
-#ifdef HAVE_REGEX_H
- if (ISSET(USE_REGEXP)) {
- search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
- new_line_size = replace_regexp(NULL, 0);
- /* If they specified an invalid subexpression in the replace
- * text, return NULL, indicating an error */
- if (new_line_size < 0)
- return NULL;
- } else {
-#else
- {
-#endif
- search_match_count = strlen(last_search);
- new_line_size = strlen(current->data) - strlen(last_search) +
- strlen(last_replace) + 1;
- }
-
- /* Create buffer */
- copy = charalloc(new_line_size);
-
- /* Head of Original Line */
- strncpy(copy, current->data, current_x);
- copy[current_x] = '\0';
-
- /* Replacement Text */
- if (!ISSET(USE_REGEXP))
- strcat(copy, last_replace);
-#ifdef HAVE_REGEX_H
- else
- replace_regexp(copy + current_x, 1);
-#endif
-
- /* The tail of the original line */
-
- /* This may expose other bugs, because it no longer goes through
- * each character in the string and tests for string goodness. But
- * because we can assume the invariant that current->data is less
- * than current_x + strlen(last_search) long, this should be safe.
- * Or it will expose bugs ;-) */
- tmp = current->data + current_x + search_match_count;
- strcat(copy, tmp);
-
- return copy;
-}
-
-/* Step through each replace word and prompt user before replacing
- * word. Return -1 if the string to replace isn't found at all.
- * Otherwise, return the number of replacements made. */
-int do_replace_loop(const char *prevanswer, const filestruct *begin,
- int *beginx, int wholewords, int *i)
-{
- int replaceall = 0, numreplaced = -1;
-
- filestruct *fileptr = NULL;
- char *copy;
-
- switch (*i) {
- case -1: /* Aborted enter */
- if (last_replace[0] != '\0')
- answer = mallocstrcpy(answer, last_replace);
- statusbar(_("Replace Cancelled"));
- replace_abort();
- return 0;
- case 0: /* They actually entered something */
- break;
- default:
- if (*i != -2) { /* First page, last page, for example, could
- get here */
- do_early_abort();
- replace_abort();
- return 0;
- }
- }
-
- last_replace = mallocstrcpy(last_replace, answer);
- while (1) {
- /* Sweet optimization by Rocco here */
- fileptr = findnextstr(fileptr || replaceall || search_last_line,
- FALSE, begin, *beginx, prevanswer);
-
- if (current->lineno <= edittop->lineno
- || current->lineno >= editbot->lineno)
- edit_update(current, CENTER);
-
- /* No more matches. Done! */
- if (fileptr == NULL)
- break;
-
- /* Make sure only whole words are found */
- if (wholewords && !is_whole_word(current_x, fileptr->data, prevanswer))
- continue;
-
- /* If we're here, we've found the search string */
- if (numreplaced == -1)
- numreplaced = 0;
-
- if (!replaceall) {
- curs_set(0);
- do_replace_highlight(TRUE, prevanswer);
-
- *i = do_yesno(1, 1, _("Replace this instance?"));
-
- do_replace_highlight(FALSE, prevanswer);
- curs_set(1);
- }
-
- if (*i > 0 || replaceall) { /* Yes, replace it!!!! */
- long length_change;
- size_t match_len;
-
- if (*i == 2)
- replaceall = 1;
-
- copy = replace_line();
- if (copy == NULL) {
- statusbar(_("Replace failed: unknown subexpression!"));
- replace_abort();
- return 0;
- }
-
- length_change = strlen(copy) - strlen(current->data);
-
-#ifdef HAVE_REGEX_H
- if (ISSET(USE_REGEXP))
- match_len = regmatches[0].rm_eo - regmatches[0].rm_so;
- else
-#endif
- match_len = strlen(prevanswer);
-
-#ifndef NANO_SMALL
- if (current == mark_beginbuf && mark_beginx > current_x) {
- if (mark_beginx < current_x + match_len)
- mark_beginx = current_x;
- else
- mark_beginx += length_change;
- }
-#endif
-
- assert(0 <= match_len + length_change);
- if (current == begin && current_x <= *beginx) {
- if (*beginx < current_x + match_len)
- *beginx = current_x + match_len;
- *beginx += length_change;
- }
-
- /* Set the cursor at the last character of the replacement
- * text, so searching will resume after the replacement text.
- * Note that current_x might be set to -1 here. */
-#ifndef NANO_SMALL
- if (!ISSET(REVERSE_SEARCH))
-#endif
- current_x += match_len + length_change - 1;
-
- /* Cleanup */
- totsize += length_change;
- free(current->data);
- current->data = copy;
-
- edit_refresh();
- set_modified();
- numreplaced++;
- } else if (*i == -1) /* Abort, else do nothing and continue
- loop */
- break;
- }
-
- /* If text has been added to the magicline, make a new magicline. */
- if (filebot->data[0] != '\0')
- new_magicline();
-
- return numreplaced;
-}
-
-/* Replace a string. */
-int do_replace(void)
-{
- int i, numreplaced, beginx;
- filestruct *begin;
- char *prevanswer = NULL;
-
- if (ISSET(VIEW_MODE)) {
- print_view_warning();
- replace_abort();
- return 0;
- }
-
- i = search_init(1);
- switch (i) {
- case -1:
- statusbar(_("Replace Cancelled"));
- replace_abort();
- return 0;
- case 1:
- do_replace();
- return 1;
- case -2:
- do_search();
- return 0;
- case -3:
- replace_abort();
- return 0;
- }
-
-#ifndef NANO_SMALL
- if (answer[0] != '\0')
- update_history(&search_history, answer);
-#endif /* !NANO_SMALL */
-
- /* If answer is now == "", copy last_search into answer
- (and prevanswer)... */
- if (answer[0] == '\0')
- answer = mallocstrcpy(answer, last_search);
- else
- last_search = mallocstrcpy(last_search, answer);
-
- prevanswer = mallocstrcpy(prevanswer, last_search);
-
-#ifndef NANO_SMALL
- replace_history.current = (historytype *)&replace_history.next;
- last_replace = mallocstrcpy(last_replace, "");
-#endif
-
- i = statusq(0, replace_list_2, last_replace,
-#ifndef NANO_SMALL
- &replace_history,
-#endif
- _("Replace with"));
-
-#ifndef NANO_SMALL
- if (i == 0 && answer[0] != '\0')
- update_history(&replace_history, answer);
-#endif /* !NANO_SMALL */
-
- begin = current;
- beginx = current_x;
- search_last_line = 0;
-
- numreplaced = do_replace_loop(prevanswer, begin, &beginx, FALSE, &i);
-
- /* restore where we were */
- current = begin;
- current_x = beginx;
- renumber_all();
- edit_update(current, CENTER);
-
- if (numreplaced >= 0)
- statusbar(P_("Replaced %d occurrence", "Replaced %d occurrences",
- numreplaced), numreplaced);
- else
- not_found_msg(prevanswer);
-
- free(prevanswer);
- replace_abort();
- return 1;
-}
-
-int do_gotoline(int line, int save_pos)
-{
- if (line <= 0) { /* Ask for it */
- int st = statusq(FALSE, goto_list, line != 0 ? answer : "",
-#ifndef NANO_SMALL
- NULL,
-#endif
- _("Enter line number"));
-
- /* Cancel, or Enter with blank string. */
- if (st == -1 || st == -2)
- statusbar(_("Aborted"));
- if (st != 0) {
- display_main_list();
- return 0;
- }
-
- line = atoi(answer);
-
- /* Bounds check */
- if (line <= 0) {
- statusbar(_("Come on, be reasonable"));
- display_main_list();
- return 0;
- }
- }
-
- for (current = fileage; current->next != NULL && line > 1; line--)
- current = current->next;
-
- current_x = 0;
-
- /* if save_pos is nonzero, don't change the cursor position when
- updating the edit window */
- if (save_pos)
- edit_update(current, NONE);
- else
- edit_update(current, CENTER);
- placewewant = 0;
- display_main_list();
- return 1;
-}
-
-int do_gotoline_void(void)
-{
- return do_gotoline(0, 0);
-}
-
-#if defined(ENABLE_MULTIBUFFER) || !defined(DISABLE_SPELLER)
-void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant)
-{
- /* since do_gotoline() resets the x-coordinate but not the
- y-coordinate, set the coordinates up this way */
- current_y = pos_y;
- do_gotoline(line, 1);
-
- /* make sure that the x-coordinate is sane here */
- if (pos_x > strlen(current->data))
- pos_x = strlen(current->data);
-
- /* set the rest of the coordinates up */
- current_x = pos_x;
- placewewant = pos_placewewant;
- update_line(current, pos_x);
-}
-#endif
-
-#if !defined(NANO_SMALL) && defined(HAVE_REGEX_H)
-int do_find_bracket(void)
-{
- char ch_under_cursor, wanted_ch;
- const char *pos, *brackets = "([{<>}])";
- char regexp_pat[] = "[ ]";
- int offset, have_search_offscreen = 0, flagsave, current_x_save, count = 1;
- filestruct *current_save;
-
- ch_under_cursor = current->data[current_x];
-
-/* if ((!(pos = strchr(brackets, ch_under_cursor))) || (!((offset = pos - brackets) < 8))) { */
-
- if (((pos = strchr(brackets, ch_under_cursor)) == NULL) || (((offset = pos - brackets) < 8) == 0)) {
- statusbar(_("Not a bracket"));
- return 1;
- }
-
- blank_statusbar_refresh();
-
- wanted_ch = *(brackets + ((strlen(brackets) - (offset + 1))));
-
- current_x_save = current_x;
- current_save = current;
- flagsave = flags;
- SET(USE_REGEXP);
-
-/* apparent near redundancy with regexp_pat[] here is needed, [][] works, [[]] doesn't */
-
- if (offset < (strlen(brackets) / 2)) { /* on a left bracket */
- regexp_pat[1] = wanted_ch;
- regexp_pat[2] = ch_under_cursor;
- UNSET(REVERSE_SEARCH);
- } else { /* on a right bracket */
- regexp_pat[1] = ch_under_cursor;
- regexp_pat[2] = wanted_ch;
- SET(REVERSE_SEARCH);
- }
-
- regexp_init(regexp_pat);
-
- while (1) {
- search_last_line = 0;
- if (findnextstr(1, 1, current, current_x, regexp_pat) != NULL) {
- have_search_offscreen |= search_offscreen;
-
- /* found identical bracket */
- if (current->data[current_x] == ch_under_cursor)
- count++;
- else {
-
- /* found complementary bracket */
- if (!(--count)) {
- if (have_search_offscreen)
- edit_update(current, CENTER);
- else
- update_line(current, current_x);
- placewewant = xplustabs();
- reset_cursor();
- break;
- }
- }
- } else {
-
- /* didn't find either left or right bracket */
- statusbar(_("No matching bracket"));
- current_x = current_x_save;
- current = current_save;
- update_line(current, current_x);
- break;
- }
- }
-
- if (ISSET(REGEXP_COMPILED))
- regexp_cleanup();
- flags = flagsave;
- return 0;
-}
-#endif
-
-#ifndef NANO_SMALL
-/*
- * search and replace history list support functions
- */
-
-/* initialize search and replace history lists */
-void history_init(void)
-{
- search_history.next = (historytype *)&search_history.prev;
- search_history.prev = NULL;
- search_history.tail = (historytype *)&search_history.next;
- search_history.current = search_history.next;
- search_history.count = 0;
- search_history.len = 0;
-
- replace_history.next = (historytype *)&replace_history.prev;
- replace_history.prev = NULL;
- replace_history.tail = (historytype *)&replace_history.next;
- replace_history.current = replace_history.next;
- replace_history.count = 0;
- replace_history.len = 0;
-}
-
-/* find first node containing string *s in history list *h */
-historytype *find_node(historytype *h, char *s)
-{
- for (; h->next != NULL; h = h->next)
- if (strcmp(s, h->data) == 0)
- return h;
- return NULL;
-}
-
-/* remove node *r */
-void remove_node(historytype *r)
-{
- r->prev->next = r->next;
- r->next->prev = r->prev;
- free(r->data);
- free(r);
-}
-
-/* add a node after node *h */
-void insert_node(historytype *h, const char *s)
-{
- historytype *a;
-
- a = (historytype *)nmalloc(sizeof(historytype));
- a->next = h->next;
- a->prev = h->next->prev;
- h->next->prev = a;
- h->next = a;
- a->data = mallocstrcpy(NULL, s);
-}
-
-/* update history list */
-void update_history(historyheadtype *h, char *s)
-{
- historytype *p;
-
- if ((p = find_node(h->next, s)) != NULL) {
- if (p == h->next) /* catch delete and re-insert of
- same string in 1st node */
- goto up_hs;
- remove_node(p); /* delete identical older string */
- h->count--;
- }
- if (h->count == MAX_SEARCH_HISTORY) { /* list 'full', delete oldest */
- remove_node(h->tail);
- h->count--;
- }
- insert_node((historytype *)h, s);
- h->count++;
- SET(HISTORY_CHANGED);
- up_hs:
- h->current = h->next;
-}
-
-/* return a pointer to either the next older history or NULL if no more */
-char *get_history_older(historyheadtype *h)
-{
- if (h->current->next != NULL) { /* any older entries? */
- h->current = h->current->next; /* yes */
- return h->current->data; /* return it */
- }
- return NULL; /* end of list */
-}
-
-char *get_history_newer(historyheadtype *h)
-{
- if (h->current->prev != NULL) {
- h->current = h->current->prev;
- if (h->current->prev != NULL)
- return h->current->data;
- }
- return NULL;
-}
-
-/* get a completion */
-char *get_history_completion(historyheadtype *h, char *s)
-{
- historytype *p;
-
- for (p = h->current->next; p->next != NULL; p = p->next) {
- if (strncmp(s, p->data, h->len) == 0 && strlen(p->data) != h->len) {
- h->current = p;
- return p->data;
- }
- }
- h->current = (historytype*)h;
- null_at(&s, h->len);
- return s;
-}
-
-/* free a history list */
-void free_history(historyheadtype *h)
-{
- historytype *p, *n;
-
- for (p = h->next; (n = p->next); p = n)
- remove_node(p);
-}
-
-/* end of history support functions */
-#endif /* !NANO_SMALL */
--- /dev/null
+Makefile
+Makefile.in
+nano
+.deps
--- /dev/null
+
+DEFS= -DSYSCONFDIR=\"$(sysconfdir)\"
+localedir = $(datadir)/locale
+INCLUDES = -Iintl -DLOCALEDIR=\"$(localedir)\" -I@includedir@
+
+ACLOCAL_AMFLAGS = -I m4
+
+bin_PROGRAMS = nano
+nano_SOURCES = color.c \
+ cut.c \
+ files.c \
+ global.c \
+ move.c \
+ nano.c \
+ nano.h \
+ proto.h \
+ rcfile.c \
+ search.c \
+ utils.c \
+ winio.c
+
+nano_LDADD = @GLIB_LIBS@ @LIBINTL@
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * color.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "proto.h"
+#include "nano.h"
+
+#ifdef ENABLE_COLOR
+
+/* For each syntax list entry, we go through the list of colors and
+ * assign color pairs. */
+void set_colorpairs(void)
+{
+ const syntaxtype *this_syntax = syntaxes;
+
+ for(; this_syntax != NULL; this_syntax = this_syntax->next) {
+ colortype *this_color = this_syntax->color;
+ int color_pair = 1;
+
+ for(; this_color != NULL; this_color = this_color->next) {
+ const colortype *beforenow = this_syntax->color;
+
+ for(; beforenow != NULL && beforenow != this_color &&
+ (beforenow->fg != this_color->fg ||
+ beforenow->bg != this_color->bg ||
+ beforenow->bright != this_color->bright);
+ beforenow = beforenow->next)
+ ;
+
+ if (beforenow != NULL && beforenow != this_color)
+ this_color->pairnum = beforenow->pairnum;
+ else {
+ this_color->pairnum = color_pair;
+ color_pair++;
+ }
+ }
+ }
+}
+
+void do_colorinit(void)
+{
+ if (has_colors()) {
+ const colortype *tmpcolor = NULL;
+#ifdef HAVE_USE_DEFAULT_COLORS
+ int defok;
+#endif
+
+ start_color();
+ /* Add in colors, if available */
+
+#ifdef HAVE_USE_DEFAULT_COLORS
+ defok = use_default_colors() != ERR;
+#endif
+
+ for (tmpcolor = colorstrings; tmpcolor != NULL;
+ tmpcolor = tmpcolor->next) {
+ short background = tmpcolor->bg;
+
+ if (background == -1)
+#ifdef HAVE_USE_DEFAULT_COLORS
+ if (!defok)
+#endif
+ background = COLOR_BLACK;
+
+ init_pair(tmpcolor->pairnum, tmpcolor->fg, background);
+
+#ifdef DEBUG
+ fprintf(stderr, "Running %s with fg = %d and bg = %d\n",
+ "init_pair()", tmpcolor->fg, tmpcolor->bg);
+#endif
+ }
+ }
+}
+
+/* Update the color information based on the current filename. */
+void update_color(void)
+{
+ const syntaxtype *tmpsyntax;
+
+ colorstrings = NULL;
+ for (tmpsyntax = syntaxes; tmpsyntax != NULL; tmpsyntax = tmpsyntax->next) {
+ const exttype *e;
+
+ for (e = tmpsyntax->extensions; e != NULL; e = e->next) {
+ /* Set colorstrings if we matched the extension regex */
+ if (!regexec(&e->val, filename, 0, NULL, 0))
+ colorstrings = tmpsyntax->color;
+
+ if (colorstrings != NULL)
+ break;
+ }
+ }
+
+ /* if we haven't found a match, use the override string */
+ if (colorstrings == NULL && syntaxstr != NULL) {
+ for (tmpsyntax = syntaxes; tmpsyntax != NULL;
+ tmpsyntax = tmpsyntax->next) {
+ if (!strcasecmp(tmpsyntax->desc, syntaxstr))
+ colorstrings = tmpsyntax->color;
+ }
+ }
+ do_colorinit();
+}
+
+#endif /* ENABLE_COLOR */
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * cut.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+static int marked_cut; /* Is the cutbuffer from a mark? */
+
+#ifndef NANO_SMALL
+static int concatenate_cut; /* Should we add this cut string to the
+ end of the last one? */
+#endif
+
+static filestruct *cutbottom = NULL;
+ /* Pointer to end of cutbuffer */
+
+filestruct *get_cutbottom(void)
+{
+ return cutbottom;
+}
+
+void add_to_cutbuffer(filestruct *inptr)
+{
+#ifdef DEBUG
+ fprintf(stderr, "add_to_cutbuffer() called with inptr->data = %s\n",
+ inptr->data);
+#endif
+
+ if (cutbuffer == NULL) {
+ cutbuffer = inptr;
+ inptr->prev = NULL;
+#ifndef NANO_SMALL
+ } else if (concatenate_cut && !ISSET(JUSTIFY_MODE)) {
+ /* Just tack the text in inptr onto the text in cutbottom,
+ unless we're backing up lines while justifying text. */
+ cutbottom->data = charealloc(cutbottom->data,
+ strlen(cutbottom->data) + strlen(inptr->data) + 1);
+ strcat(cutbottom->data, inptr->data);
+ return;
+#endif
+ } else {
+ cutbottom->next = inptr;
+ inptr->prev = cutbottom;
+ }
+
+ inptr->next = NULL;
+ cutbottom = inptr;
+}
+
+#ifndef NANO_SMALL
+/* Cut a marked segment instead of a whole line.
+ *
+ * The first cut character is top->data[top_x]. Unless top == bot, the
+ * last cut line has length bot_x. That is, if bot_x > 0 then we cut to
+ * bot->data[bot_x - 1].
+ *
+ * destructive is whether to actually modify the file structure, if not
+ * then just copy the buffer into cutbuffer and don't pull it from the
+ * file.
+ *
+ * If destructive, then we maintain totsize, totlines, filebot, the
+ * magic line, and line numbers. Also, we set current and current_x so
+ * the cursor will be on the first character after what was cut. We do
+ * not do any screen updates. */
+void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot,
+ size_t bot_x, int destructive)
+{
+ filestruct *tmp, *next;
+ size_t newsize;
+
+ if (top == bot && top_x == bot_x)
+ return;
+ assert(top != NULL && bot != NULL);
+
+ /* Make top be no later than bot. */
+ if (top->lineno > bot->lineno) {
+ filestruct *swap = top;
+ int swap2 = top_x;
+
+ top = bot;
+ bot = swap;
+
+ top_x = bot_x;
+ bot_x = swap2;
+ } else if (top == bot && top_x > bot_x) {
+ /* And bot_x can't be an earlier character than top_x. */
+ int swap = top_x;
+
+ top_x = bot_x;
+ bot_x = swap;
+ }
+
+ /* Make the first cut line manually. */
+ tmp = copy_node(top);
+ newsize = (top == bot ? bot_x - top_x : strlen(top->data + top_x));
+ memmove(tmp->data, top->data + top_x, newsize);
+ null_at(&tmp->data, newsize);
+ add_to_cutbuffer(tmp);
+
+ /* And make the remainder line manually too. */
+ if (destructive) {
+ current_x = top_x;
+ totsize -= newsize;
+ totlines -= bot->lineno - top->lineno;
+
+ newsize = top_x + strlen(bot->data + bot_x) + 1;
+ if (top == bot) {
+ /* In this case, the remainder line is shorter, so we must
+ move text from the end forward first. */
+ memmove(top->data + top_x, bot->data + bot_x,
+ newsize - top_x);
+ top->data = charealloc(top->data, newsize);
+ } else {
+ totsize -= bot_x + 1;
+
+ /* Here, the remainder line might get longer, so we
+ realloc() it first. */
+ top->data = charealloc(top->data, newsize);
+ memmove(top->data + top_x, bot->data + bot_x,
+ newsize - top_x);
+ }
+ }
+
+ if (top == bot) {
+#ifdef DEBUG
+ dump_buffer(cutbuffer);
+#endif
+ return;
+ }
+
+ tmp = top->next;
+ while (tmp != bot) {
+ next = tmp->next;
+ if (!destructive)
+ tmp = copy_node(tmp);
+ else
+ totsize -= strlen(tmp->data) + 1;
+ add_to_cutbuffer(tmp);
+ tmp = next;
+ }
+
+ /* Make the last cut line manually. */
+ tmp = copy_node(bot);
+ null_at(&tmp->data, bot_x);
+ add_to_cutbuffer(tmp);
+#ifdef DEBUG
+ dump_buffer(cutbuffer);
+#endif
+
+ if (destructive) {
+ top->next = bot->next;
+ if (top->next != NULL)
+ top->next->prev = top;
+ delete_node(bot);
+ renumber(top);
+ current = top;
+ if (bot == filebot) {
+ filebot = top;
+ assert(bot_x == 0);
+ if (top_x > 0)
+ new_magicline();
+ }
+ }
+#ifdef DEBUG
+ dump_buffer(cutbuffer);
+#endif
+}
+#endif
+
+int do_cut_text(void)
+{
+ filestruct *fileptr;
+#ifndef NANO_SMALL
+ int dontupdate = 0;
+#endif
+
+ assert(current != NULL && current->data != NULL);
+
+ check_statblank();
+
+ if (!ISSET(KEEP_CUTBUFFER)) {
+ free_filestruct(cutbuffer);
+ cutbuffer = NULL;
+ marked_cut = 0;
+#ifndef NANO_SMALL
+ concatenate_cut = 0;
+#endif
+#ifdef DEBUG
+ fprintf(stderr, "Blew away cutbuffer =)\n");
+#endif
+ }
+
+ /* You can't cut the magic line except with the mark. But
+ trying does clear the cutbuffer if KEEP_CUTBUFFER is not set. */
+ if (current == filebot
+#ifndef NANO_SMALL
+ && !ISSET(MARK_ISSET)
+#endif
+ )
+ return 0;
+
+ SET(KEEP_CUTBUFFER);
+
+#ifndef NANO_SMALL
+ if (ISSET(CUT_TO_END) && !ISSET(MARK_ISSET)) {
+ assert(current_x >= 0 && current_x <= strlen(current->data));
+
+ if (current->data[current_x] == '\0') {
+ /* If the line is empty and we didn't just cut a non-blank
+ line, create a dummy line and add it to the cutbuffer */
+ if (marked_cut != 1 && current->next != filebot) {
+ filestruct *junk = make_new_node(current);
+
+ junk->data = charalloc(1);
+ junk->data[0] = '\0';
+ add_to_cutbuffer(junk);
+#ifdef DEBUG
+ dump_buffer(cutbuffer);
+#endif
+ }
+
+ do_delete();
+ marked_cut = 2;
+ return 1;
+ } else {
+ SET(MARK_ISSET);
+
+ mark_beginx = strlen(current->data);
+ mark_beginbuf = current;
+ dontupdate = 1;
+ }
+ }
+
+ if (ISSET(MARK_ISSET)) {
+ /* Don't do_update() and move the screen position if the marked
+ area lies entirely within the screen buffer */
+ dontupdate |= current->lineno >= edittop->lineno &&
+ current->lineno <= editbot->lineno &&
+ mark_beginbuf->lineno >= edittop->lineno &&
+ mark_beginbuf->lineno <= editbot->lineno;
+ cut_marked_segment(current, current_x, mark_beginbuf,
+ mark_beginx, 1);
+
+ placewewant = xplustabs();
+ UNSET(MARK_ISSET);
+
+ /* If we just did a marked cut of part of a line, we should add
+ the first line of any cut done immediately afterward to the
+ end of this cut, as Pico does. */
+ if (current == mark_beginbuf && current_x < strlen(current->data))
+ concatenate_cut = 1;
+ marked_cut = 1;
+ if (dontupdate)
+ edit_refresh();
+ else
+ edit_update(current, CENTER);
+ set_modified();
+
+ return 1;
+ }
+#endif /* !NANO_SMALL */
+
+ totlines--;
+ totsize -= strlen(current->data) + 1;
+ fileptr = current;
+ current = current->next;
+ current->prev = fileptr->prev;
+ add_to_cutbuffer(fileptr);
+#ifdef DEBUG
+ dump_buffer(cutbuffer);
+#endif
+
+ if (fileptr == fileage)
+ fileage = current;
+ else
+ current->prev->next = current;
+
+ if (fileptr == edittop)
+ edittop = current;
+
+ renumber(current);
+ current_x = 0;
+ edit_refresh();
+ set_modified();
+ marked_cut = 0;
+#ifndef NANO_SMALL
+ concatenate_cut = 0;
+#endif
+ placewewant = 0;
+ return 1;
+}
+
+int do_uncut_text(void)
+{
+ filestruct *tmp = current, *fileptr = current;
+ filestruct *newbuf = NULL, *newend = NULL;
+ char *tmpstr, *tmpstr2;
+ filestruct *hold = current;
+ int i;
+
+ wrap_reset();
+ check_statblank();
+ if (cutbuffer == NULL || fileptr == NULL)
+ return 0; /* AIEEEEEEEEEEEE */
+
+ /* If we're uncutting a previously non-marked block, uncut to end if
+ we're not at the beginning of the line. If we are at the
+ beginning of the line, set placewewant to 0. Pico does both of
+ these. */
+ if (marked_cut == 0) {
+ if (current_x != 0)
+ marked_cut = 2;
+ else
+ placewewant = 0;
+ }
+
+ /* If we're going to uncut on the magicline, always make a new
+ magicline in advance. */
+ if (current->next == NULL)
+ new_magicline();
+
+ if (marked_cut == 0 || cutbuffer->next != NULL)
+ {
+ newbuf = copy_filestruct(cutbuffer);
+ for (newend = newbuf; newend->next != NULL && newend != NULL;
+ newend = newend->next)
+ totlines++;
+ }
+
+ /* Hook newbuf into fileptr */
+ if (marked_cut != 0) {
+ int recenter_me = 0;
+ /* Should we eventually use edit_update(CENTER)? */
+
+ /* If there's only one line in the cutbuffer */
+ if (cutbuffer->next == NULL) {
+ size_t buf_len = strlen(cutbuffer->data);
+ size_t cur_len = strlen(current->data);
+
+ current->data = charealloc(current->data, cur_len + buf_len + 1);
+ memmove(current->data + current_x + buf_len,
+ current->data + current_x, cur_len - current_x + 1);
+ strncpy(current->data + current_x, cutbuffer->data, buf_len);
+ /* Use strncpy() to not copy the terminal '\0'. */
+
+ current_x += buf_len;
+ totsize += buf_len;
+
+ placewewant = xplustabs();
+ update_cursor();
+ } else { /* yuck -- no kidding! */
+ tmp = current->next;
+ /* New beginning */
+ tmpstr = charalloc(current_x + strlen(newbuf->data) + 1);
+ strncpy(tmpstr, current->data, current_x);
+ strcpy(&tmpstr[current_x], newbuf->data);
+ totsize += strlen(newbuf->data) + strlen(newend->data) + 1;
+
+ /* New end */
+ tmpstr2 = charalloc(strlen(newend->data) +
+ strlen(¤t->data[current_x]) + 1);
+ strcpy(tmpstr2, newend->data);
+ strcat(tmpstr2, ¤t->data[current_x]);
+
+ free(current->data);
+ current->data = tmpstr;
+ current->next = newbuf->next;
+ newbuf->next->prev = current;
+ delete_node(newbuf);
+
+ current_x = strlen(newend->data);
+ placewewant = xplustabs();
+ free(newend->data);
+ newend->data = tmpstr2;
+
+ newend->next = tmp;
+
+ /* If tmp isn't null, we're in the middle: update the
+ prev pointer. If it IS null, we're at the end; update
+ the filebot pointer */
+
+ if (tmp != NULL)
+ tmp->prev = newend;
+ else {
+ /* Fix the editbot pointer too */
+ if (editbot == filebot)
+ editbot = newend;
+ filebot = newend;
+ new_magicline();
+ }
+
+ /* Now why don't we update the totsize also */
+ for (tmp = current->next; tmp != newend; tmp = tmp->next)
+ totsize += strlen(tmp->data) + 1;
+
+ current = newend;
+ if (editbot->lineno < newend->lineno)
+ recenter_me = 1;
+ }
+
+ /* If marked cut == 2, that means that we're doing a cut to end
+ and we don't want anything else on the line, so we have to
+ screw up all the work we just did and separate the line.
+ There must be a better way to do this, but not at 1AM on a
+ work night. */
+
+ if (marked_cut == 2) {
+ tmp = make_new_node(current);
+ tmp->data = mallocstrcpy(NULL, current->data + current_x);
+ splice_node(current, tmp, current->next);
+ null_at(¤t->data, current_x);
+ current = current->next;
+ current_x = 0;
+ placewewant = 0;
+
+ /* Extra line added, update stuff */
+ totlines++;
+ totsize++;
+ }
+ /* Renumber from BEFORE where we pasted ;) */
+ renumber(hold);
+
+#ifdef DEBUG
+ dump_buffer(fileage);
+ dump_buffer(cutbuffer);
+#endif
+ set_modified();
+ if (recenter_me)
+ edit_update(current, CENTER);
+ else
+ edit_refresh();
+ return 0;
+ }
+
+ if (fileptr != fileage) {
+ tmp = fileptr->prev;
+ tmp->next = newbuf;
+ newbuf->prev = tmp;
+ } else
+ fileage = newbuf;
+ totlines++; /* Unmarked uncuts don't split lines */
+
+ /* This is so uncutting at the top of the buffer will work => */
+ if (current_y == 0)
+ edittop = newbuf;
+
+ /* Connect the end of the buffer to the filestruct */
+ newend->next = fileptr;
+ fileptr->prev = newend;
+
+ /* Recalculate size *sigh* */
+ for (tmp = newbuf; tmp != fileptr; tmp = tmp->next)
+ totsize += strlen(tmp->data) + 1;
+
+ i = editbot->lineno;
+ renumber(newbuf);
+ /* Center the screen if we've moved beyond the line numbers of both
+ the old and new editbots */
+ if (i < newend->lineno && editbot->lineno < newend->lineno)
+ edit_update(fileptr, CENTER);
+ else
+ edit_refresh();
+
+#ifdef DEBUG
+ dump_buffer_reverse();
+#endif
+
+ set_modified();
+ return 1;
+}
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * files.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+/* Set a default value for PATH_MAX, so we can use it below in lines like
+ path = getcwd(NULL, PATH_MAX + 1); */
+#ifndef PATH_MAX
+#define PATH_MAX -1
+#endif
+
+#ifndef NANO_SMALL
+static int fileformat = 0; /* 0 = *nix, 1 = DOS, 2 = Mac */
+#endif
+
+/* Load file into edit buffer - takes data from file struct. */
+void load_file(int update)
+{
+ current = fileage;
+
+#ifdef ENABLE_MULTIBUFFER
+ /* if update is zero, add a new entry to the open_files structure;
+ otherwise, update the current entry (the latter is needed in the
+ case of the alternate spell checker) */
+ add_open_file(update);
+#endif
+
+#ifdef ENABLE_COLOR
+ update_color();
+ edit_refresh();
+#endif
+}
+
+/* What happens when there is no file to open? aiee! */
+void new_file(void)
+{
+ fileage = make_new_node(NULL);
+ fileage->data = charalloc(1);
+ fileage->data[0] = '\0';
+ filebot = fileage;
+ edittop = fileage;
+ editbot = fileage;
+ current = fileage;
+ current_x = 0;
+ totlines = 1;
+ totsize = 0;
+
+#ifdef ENABLE_MULTIBUFFER
+ /* if there aren't any entries in open_files, create the entry for
+ this new file; without this, if nano is started without a filename
+ on the command line, a new file will be created, but it will be
+ given no open_files entry */
+ if (open_files == NULL) {
+ add_open_file(0);
+ /* turn off view mode in this case; this is for consistency
+ whether multibuffers are compiled in or not */
+ UNSET(VIEW_MODE);
+ }
+#else
+ /* if multibuffers haven't been compiled in, turn off view mode
+ unconditionally; otherwise, don't turn them off (except in the
+ above case), so that we can view multiple files properly */
+ UNSET(VIEW_MODE);
+#endif
+
+#ifdef ENABLE_COLOR
+ update_color();
+ edit_refresh();
+#endif
+}
+
+filestruct *read_line(char *buf, filestruct *prev, int *line1ins, int len)
+{
+ filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct));
+
+ /* nulls to newlines; len is the string's real length here */
+ unsunder(buf, len);
+
+ assert(strlen(buf) == len);
+
+ fileptr->data = mallocstrcpy(NULL, buf);
+
+#ifndef NANO_SMALL
+ /* If it's a DOS file (CRLF), and file conversion isn't disabled,
+ strip out the CR part */
+ if (!ISSET(NO_CONVERT) && len > 0 && buf[len - 1] == '\r') {
+ fileptr->data[len - 1] = '\0';
+ totsize--;
+
+ if (fileformat == 0)
+ fileformat = 1;
+ }
+#endif
+
+ if (*line1ins != 0 || fileage == NULL) {
+ /* Special case, insert with cursor on 1st line. */
+ fileptr->prev = NULL;
+ fileptr->next = fileage;
+ fileptr->lineno = 1;
+ if (*line1ins != 0) {
+ *line1ins = 0;
+ /* If we're inserting into the first line of the file, then
+ we want to make sure that our edit buffer stays on the
+ first line (and that fileage stays up to date!) */
+ edittop = fileptr;
+ } else
+ filebot = fileptr;
+ fileage = fileptr;
+ } else {
+ assert(prev != NULL);
+ fileptr->prev = prev;
+ fileptr->next = NULL;
+ fileptr->lineno = prev->lineno + 1;
+ prev->next = fileptr;
+ }
+
+ return fileptr;
+}
+
+int read_file(FILE *f, const char *filename, int quiet)
+{
+ int num_lines = 0, len = 0;
+ char input = '\0'; /* current input character */
+ char *buf;
+ long i = 0, bufx = 128;
+ filestruct *fileptr = current, *tmp = NULL;
+#ifndef NANO_SMALL
+ int old_no_convert = ISSET(NO_CONVERT);
+#endif
+ int line1ins = 0;
+ int input_int;
+
+ buf = charalloc(bufx);
+ buf[0] = '\0';
+
+ if (current != NULL) {
+ if (current == fileage)
+ line1ins = 1;
+ else
+ fileptr = current->prev;
+ tmp = fileptr;
+ }
+
+ /* For the assertion in read_line(), it must be true that if current is
+ * NULL then so is fileage. */
+ assert(current != NULL || fileage == NULL);
+
+ /* Read the entire file into file struct. */
+ while ((input_int = getc(f)) != EOF) {
+ input = (char)input_int;
+#ifndef NANO_SMALL
+ /* If the file has binary chars in it, don't stupidly
+ assume it's a DOS or Mac formatted file if it hasn't been
+ detected as one already! */
+ if (fileformat == 0 && !ISSET(NO_CONVERT)
+ && is_cntrl_char((int)input) != 0 && input != '\t'
+ && input != '\r' && input != '\n')
+ SET(NO_CONVERT);
+#endif
+
+ if (input == '\n') {
+
+ /* read in the line properly */
+ fileptr = read_line(buf, fileptr, &line1ins, len);
+
+ /* reset the line length, in preparation for the next line */
+ len = 0;
+
+ num_lines++;
+ buf[0] = '\0';
+ i = 0;
+#ifndef NANO_SMALL
+ /* If it's a Mac file (no LF just a CR), and file conversion
+ isn't disabled, handle it! */
+ } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') {
+ fileformat = 2;
+
+ /* read in the line properly */
+ fileptr = read_line(buf, fileptr, &line1ins, len);
+
+ /* reset the line length, in preparation for the next line;
+ since we've already read in the next character, reset it
+ to 1 instead of 0 */
+ len = 1;
+
+ num_lines++;
+ totsize++;
+ buf[0] = input;
+ buf[1] = '\0';
+ i = 1;
+#endif
+ } else {
+
+ /* Calculate the total length of the line; it might have
+ nulls in it, so we can't just use strlen(). */
+ len++;
+
+ /* Now we allocate a bigger buffer 128 characters at a time.
+ If we allocate a lot of space for one line, we may indeed
+ have to use a buffer this big later on, so we don't
+ decrease it at all. We do free it at the end, though. */
+ if (i >= bufx - 1) {
+ bufx += 128;
+ buf = charealloc(buf, bufx);
+ }
+ buf[i] = input;
+ buf[i + 1] = '\0';
+ i++;
+ }
+ totsize++;
+ }
+
+ /* This conditional duplicates previous read_byte() behavior;
+ perhaps this could use some better handling. */
+ if (ferror(f))
+ nperror(filename);
+ fclose(f);
+
+ /* Did we not get a newline but still have stuff to do? */
+ if (len > 0) {
+#ifndef NANO_SMALL
+ /* If file conversion isn't disabled, the last character in
+ this file is a CR and fileformat isn't set yet, make sure
+ it's set to Mac format */
+ if (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' && fileformat == 0)
+ fileformat = 2;
+#endif
+
+ /* read in the LAST line properly */
+ fileptr = read_line(buf, fileptr, &line1ins, len);
+
+ num_lines++;
+ totsize++;
+ buf[0] = '\0';
+ }
+#ifndef NANO_SMALL
+ else if (!ISSET(NO_CONVERT) && input == '\r') {
+ /* If file conversion isn't disabled and the last character in
+ this file is a CR, read it in properly as a (Mac format)
+ line */
+ buf[0] = input;
+ buf[1] = '\0';
+ len = 1;
+ fileptr = read_line(buf, fileptr, &line1ins, len);
+ num_lines++;
+ totsize++;
+ buf[0] = '\0';
+ }
+#endif
+
+ free(buf);
+
+#ifndef NANO_SMALL
+ /* If NO_CONVERT wasn't set before we read the file, but it is now,
+ unset it again. */
+ if (!old_no_convert && ISSET(NO_CONVERT))
+ UNSET(NO_CONVERT);
+#endif
+
+ /* Did we even GET a file if we don't already have one? */
+ if (totsize == 0 || fileptr == NULL)
+ new_file();
+
+ /* Did we try to insert a file of 0 bytes? */
+ if (num_lines != 0) {
+ if (current != NULL) {
+ fileptr->next = current;
+ current->prev = fileptr;
+ renumber(current);
+ current_x = 0;
+ placewewant = 0;
+ } else if (fileptr->next == NULL) {
+ filebot = fileptr;
+ new_magicline();
+ totsize--;
+ load_file(quiet);
+ }
+ }
+
+#ifndef NANO_SMALL
+ if (fileformat == 2)
+ statusbar(P_("Read %d line (Converted from Mac format)",
+ "Read %d lines (Converted from Mac format)",
+ num_lines), num_lines);
+ else if (fileformat == 1)
+ statusbar(P_("Read %d line (Converted from DOS format)",
+ "Read %d lines (Converted from DOS format)",
+ num_lines), num_lines);
+ else
+#endif
+ statusbar(P_("Read %d line", "Read %d lines", num_lines),
+ num_lines);
+
+ totlines += num_lines;
+
+ return 1;
+}
+
+/* Open the file (and decide if it exists). */
+int open_file(const char *filename, int insert, int quiet)
+{
+ int fd;
+ FILE *f;
+ struct stat fileinfo;
+
+ if (filename[0] == '\0' || stat(filename, &fileinfo) == -1) {
+ if (insert && !quiet) {
+ statusbar(_("\"%s\" not found"), filename);
+ return -1;
+ } else {
+ /* We have a new file */
+ statusbar(_("New File"));
+ new_file();
+ }
+ } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
+ S_ISBLK(fileinfo.st_mode)) {
+ /* Don't open character or block files. Sorry, /dev/sndstat! */
+ statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory") :
+ _("File \"%s\" is a device file"), filename);
+ if (!insert)
+ new_file();
+ return -1;
+ } else if ((fd = open(filename, O_RDONLY)) == -1) {
+ /* If we're in multibuffer mode, don't be quiet when an error
+ occurs while opening a file */
+ if (!quiet
+#ifdef ENABLE_MULTIBUFFER
+ || ISSET(MULTIBUFFER)
+#endif
+ )
+ statusbar("%s: %s", strerror(errno), filename);
+ if (!insert)
+ new_file();
+ return -1;
+ } else { /* File is A-OK */
+ if (!quiet)
+ statusbar(_("Reading File"));
+ f = fdopen(fd, "rb"); /* Binary for our own line-end munging */
+ if (f == NULL) {
+ nperror("fdopen");
+ close(fd);
+ return -1;
+ }
+ read_file(f, filename, quiet);
+#ifndef NANO_SMALL
+ stat(filename, &originalfilestat);
+#endif
+ }
+
+ return 1;
+}
+
+/* This function will return the name of the first available extension
+ * of a filename (starting with the filename, then filename.1, etc).
+ * Memory is allocated for the return value. If no writable extension
+ * exists, we return "". */
+char *get_next_filename(const char *name)
+{
+ int i = 0;
+ char *buf = NULL;
+ struct stat fs;
+
+ buf = charalloc(strlen(name) + num_of_digits(INT_MAX) + 2);
+ strcpy(buf, name);
+
+ while (1) {
+
+ if (stat(buf, &fs) == -1)
+ return buf;
+ if (i == INT_MAX)
+ break;
+
+ i++;
+ strcpy(buf, name);
+ sprintf(&buf[strlen(name)], ".%d", i);
+ }
+
+ /* We get here only if there is no possible save file. */
+ buf[0] = '\0';
+
+ return buf;
+}
+
+int do_insertfile(int loading_file)
+{
+ int i, old_current_x = current_x;
+ char *realname = NULL;
+ static char *inspath = NULL;
+
+ if (inspath == NULL) {
+ inspath = charalloc(1);
+ inspath[0] = '\0';
+ }
+
+ wrap_reset();
+
+#if !defined(DISABLE_BROWSER) || !defined(NANO_SMALL) && defined(ENABLE_MULTIBUFFER)
+ start_again: /* Goto here when the user cancels the file browser. */
+#endif
+
+#if !defined(DISABLE_BROWSER) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+ currshortcut = insertfile_list;
+#endif
+
+#ifndef DISABLE_OPERATINGDIR
+ if (operating_dir != NULL && strcmp(operating_dir, ".") != 0)
+#ifdef ENABLE_MULTIBUFFER
+ if (ISSET(MULTIBUFFER))
+ i = statusq(1, insertfile_list, inspath,
+#ifndef NANO_SMALL
+ 0,
+#endif
+ _("File to insert into new buffer [from %s] "),
+ operating_dir);
+ else
+#endif
+ i = statusq(1, insertfile_list, inspath,
+#ifndef NANO_SMALL
+ 0,
+#endif
+ _("File to insert [from %s] "),
+ operating_dir);
+
+ else
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ if (ISSET(MULTIBUFFER))
+ i = statusq(1, insertfile_list, inspath,
+#ifndef NANO_SMALL
+ 0,
+#endif
+ _("File to insert into new buffer [from ./] "));
+ else
+#endif /* ENABLE_MULTIBUFFER */
+ i = statusq(1, insertfile_list, inspath,
+#ifndef NANO_SMALL
+ 0,
+#endif
+ _("File to insert [from ./] "));
+
+ if (i != -1) {
+ inspath = mallocstrcpy(inspath, answer);
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", answer);
+#endif
+
+#ifndef NANO_SMALL
+#ifdef ENABLE_MULTIBUFFER
+ if (i == TOGGLE_LOAD_KEY) {
+ /* Don't allow toggling if we're in view mode. */
+ if (!ISSET(VIEW_MODE))
+ TOGGLE(MULTIBUFFER);
+ loading_file = ISSET(MULTIBUFFER);
+ goto start_again;
+ }
+#endif /* ENABLE_MULTIBUFFER */
+
+ if (i == NANO_EXTCMD_KEY) {
+ int ts = statusq(TRUE, extcmd_list, "", NULL,
+ _("Command to execute"));
+ if (ts == -1 || answer == NULL || answer[0] == '\0') {
+ statusbar(_("Cancelled"));
+ display_main_list();
+ return 0;
+ }
+ }
+#endif /* !NANO_SMALL */
+#ifndef DISABLE_BROWSER
+ if (i == NANO_TOFILES_KEY) {
+ char *tmp = do_browse_from(answer);
+
+ if (tmp != NULL) {
+ free(answer);
+ answer = tmp;
+ resetstatuspos = 1;
+ } else
+ goto start_again;
+ }
+#endif
+
+#ifndef DISABLE_OPERATINGDIR
+ if (i != NANO_EXTCMD_KEY && check_operating_dir(answer, FALSE)) {
+ statusbar(_("Can't insert file from outside of %s"),
+ operating_dir);
+ return 0;
+ }
+#endif
+
+#ifdef ENABLE_MULTIBUFFER
+ if (loading_file) {
+ /* update the current entry in the open_files structure */
+ add_open_file(1);
+ new_file();
+ UNSET(MODIFIED);
+#ifndef NANO_SMALL
+ UNSET(MARK_ISSET);
+#endif
+ }
+#endif
+
+#ifndef NANO_SMALL
+ if (i == NANO_EXTCMD_KEY) {
+ realname = mallocstrcpy(realname, "");
+ i = open_pipe(answer);
+ } else
+#endif /* NANO_SMALL */
+ {
+ realname = real_dir_from_tilde(answer);
+ i = open_file(realname, 1, loading_file);
+ }
+
+#ifdef ENABLE_MULTIBUFFER
+ if (loading_file) {
+ /* if there was an error opening the file, free() realname,
+ free() fileage (which now points to the new buffer we
+ created to hold the file), reload the buffer we had open
+ before, and skip the insertion; otherwise, save realname
+ in filename and continue the insertion */
+ if (i == -1) {
+ free(realname);
+ free(fileage);
+ load_open_file();
+ goto skip_insert;
+ } else
+ filename = mallocstrcpy(filename, realname);
+ }
+#endif
+
+ free(realname);
+
+#ifdef DEBUG
+ dump_buffer(fileage);
+#endif
+
+#ifdef ENABLE_MULTIBUFFER
+ if (loading_file)
+ load_file(0);
+ else
+#endif
+ set_modified();
+
+ /* Here we want to rebuild the edit window */
+ fix_editbot();
+
+#ifdef ENABLE_MULTIBUFFER
+ /* If we've loaded another file, update the titlebar's contents */
+ if (loading_file) {
+ clearok(topwin, FALSE);
+ titlebar(NULL);
+
+ /* And re-init the shortcut list */
+ shortcut_init(0);
+ }
+#endif
+
+#ifdef ENABLE_MULTIBUFFER
+ if (!loading_file) {
+#endif
+
+ /* Restore the old x-coordinate position */
+ current_x = old_current_x;
+
+#ifdef ENABLE_MULTIBUFFER
+ }
+#endif
+
+ /* If we've gone off the bottom, recenter; otherwise, just redraw */
+ if (current->lineno > editbot->lineno)
+ edit_update(current, CENTER);
+ else
+ edit_refresh();
+
+ } else {
+ statusbar(_("Cancelled"));
+ i = 0;
+ }
+
+#ifdef ENABLE_MULTIBUFFER
+ skip_insert:
+#endif
+
+ free(inspath);
+ inspath = NULL;
+
+ display_main_list();
+ return i;
+}
+
+int do_insertfile_void(void)
+{
+ int result = 0;
+#ifdef ENABLE_MULTIBUFFER
+ if (ISSET(VIEW_MODE)) {
+ if (ISSET(MULTIBUFFER))
+ result = do_insertfile(1);
+ else
+ statusbar(_("Key illegal in non-multibuffer mode"));
+ }
+ else
+ result = do_insertfile(ISSET(MULTIBUFFER));
+#else
+ result = do_insertfile(0);
+#endif
+
+ display_main_list();
+ return result;
+}
+
+#ifdef ENABLE_MULTIBUFFER
+/* Create a new openfilestruct node. */
+openfilestruct *make_new_opennode(openfilestruct *prevnode)
+{
+ openfilestruct *newnode = (openfilestruct *)nmalloc(sizeof(openfilestruct));
+
+ newnode->filename = NULL;
+ newnode->fileage = NULL;
+ newnode->filebot = NULL;
+
+ newnode->prev = prevnode;
+ newnode->next = NULL;
+
+ return newnode;
+}
+
+/* Splice a node into an existing openfilestruct. */
+void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
+ openfilestruct *end)
+{
+ newnode->next = end;
+ newnode->prev = begin;
+ begin->next = newnode;
+ if (end != NULL)
+ end->prev = newnode;
+}
+
+/* Unlink a node from the rest of the openfilestruct. */
+void unlink_opennode(const openfilestruct *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 openfilestruct. */
+void delete_opennode(openfilestruct *fileptr)
+{
+ if (fileptr != NULL) {
+ if (fileptr->filename != NULL)
+ free(fileptr->filename);
+ if (fileptr->fileage != NULL)
+ free_filestruct(fileptr->fileage);
+ free(fileptr);
+ }
+}
+
+/* Deallocate all memory associated with this and later files,
+ * including the lines of text. */
+void free_openfilestruct(openfilestruct *src)
+{
+ if (src != NULL) {
+ while (src->next != NULL) {
+ src = src->next;
+ delete_opennode(src->prev);
+#ifdef DEBUG
+ fprintf(stderr, "%s: free'd a node, YAY!\n", "delete_opennode()");
+#endif
+ }
+ delete_opennode(src);
+#ifdef DEBUG
+ fprintf(stderr, "%s: free'd last node.\n", "delete_opennode()");
+#endif
+ }
+}
+
+/*
+ * Add/update an entry to the open_files openfilestruct. If update is
+ * zero, a new entry is created; otherwise, the current entry is updated.
+ * Return 0 on success or 1 on error.
+ */
+int add_open_file(int update)
+{
+ openfilestruct *tmp;
+
+ if (fileage == NULL || current == NULL || filename == NULL)
+ return 1;
+
+ /* if no entries, make the first one */
+ if (open_files == NULL)
+ open_files = make_new_opennode(NULL);
+
+ else if (!update) {
+
+ /* otherwise, if we're not updating, make a new entry for
+ open_files and splice it in after the current one */
+
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+
+ tmp = make_new_opennode(NULL);
+ splice_opennode(open_files, tmp, open_files->next);
+ open_files = open_files->next;
+ }
+
+ /* save current filename */
+ open_files->filename = mallocstrcpy(open_files->filename, filename);
+
+#ifndef NANO_SMALL
+ /* save the file's stat */
+ open_files->originalfilestat = originalfilestat;
+#endif
+
+ /* save current total number of lines */
+ open_files->file_totlines = totlines;
+
+ /* save current total size */
+ open_files->file_totsize = totsize;
+
+ /* save current x-coordinate position */
+ open_files->file_current_x = current_x;
+
+ /* save current y-coordinate position */
+ open_files->file_current_y = current_y;
+
+ /* save current place we want */
+ open_files->file_placewewant = placewewant;
+
+ /* save current line number */
+ open_files->file_lineno = current->lineno;
+
+ /* if we're updating, save current modification status (and marking
+ status, if available) */
+ if (update) {
+#ifndef NANO_SMALL
+ open_files->file_flags = (MODIFIED & ISSET(MODIFIED)) | (MARK_ISSET & ISSET(MARK_ISSET));
+ if (ISSET(MARK_ISSET)) {
+ open_files->file_mark_beginbuf = mark_beginbuf;
+ open_files->file_mark_beginx = mark_beginx;
+ }
+#else
+ open_files->file_flags = (MODIFIED & ISSET(MODIFIED));
+#endif
+ }
+
+ /* if we're not in view mode and not updating, the file contents
+ might have changed, so save the filestruct; otherwise, don't */
+ if (!(ISSET(VIEW_MODE) && !update)) {
+ /* save current file buffer */
+ open_files->fileage = fileage;
+ open_files->filebot = filebot;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+
+ return 0;
+}
+
+/*
+ * Read the current entry in the open_files structure and set up the
+ * currently open file using that entry's information. Return 0 on
+ * success or 1 on error.
+ */
+int load_open_file(void)
+{
+ if (open_files == NULL)
+ return 1;
+
+ /* set up the filename, the file buffer, the total number of lines in
+ the file, and the total file size */
+ filename = mallocstrcpy(filename, open_files->filename);
+#ifndef NANO_SMALL
+ originalfilestat = open_files->originalfilestat;
+#endif
+ fileage = open_files->fileage;
+ current = fileage;
+ filebot = open_files->filebot;
+ totlines = open_files->file_totlines;
+ totsize = open_files->file_totsize;
+
+ /* restore modification status */
+ if (open_files->file_flags & MODIFIED)
+ SET(MODIFIED);
+ else
+ UNSET(MODIFIED);
+
+#ifndef NANO_SMALL
+ /* restore marking status */
+ if (open_files->file_flags & MARK_ISSET) {
+ mark_beginbuf = open_files->file_mark_beginbuf;
+ mark_beginx = open_files->file_mark_beginx;
+ SET(MARK_ISSET);
+ } else
+ UNSET(MARK_ISSET);
+#endif
+
+#ifdef ENABLE_COLOR
+ update_color();
+#endif
+
+ /* restore full file position: line number, x-coordinate, y-
+ coordinate, place we want */
+ do_gotopos(open_files->file_lineno, open_files->file_current_x, open_files->file_current_y, open_files->file_placewewant);
+
+ /* update the titlebar */
+ clearok(topwin, FALSE);
+ titlebar(NULL);
+
+ /* now we're done */
+ return 0;
+}
+
+/*
+ * Open the previous entry in the open_files structure. If closing_file
+ * is zero, update the current entry before switching from it.
+ * Otherwise, we are about to close that entry, so don't bother doing so.
+ * Return 0 on success and 1 on error.
+ */
+int open_prevfile(int closing_file)
+{
+ if (open_files == NULL)
+ return 1;
+
+ /* if we're not about to close the current entry, update it before
+ doing anything */
+ if (!closing_file)
+ add_open_file(1);
+
+ if (open_files->prev == NULL && open_files->next == NULL) {
+
+ /* only one file open */
+ if (!closing_file)
+ statusbar(_("No more open files"));
+ return 1;
+ }
+
+ if (open_files->prev != NULL) {
+ open_files = open_files->prev;
+
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+
+ }
+
+ else if (open_files->next != NULL) {
+
+ /* if we're at the beginning, wrap around to the end */
+ while (open_files->next != NULL)
+ open_files = open_files->next;
+
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+
+ }
+
+ load_open_file();
+
+ statusbar(_("Switched to %s"),
+ ((open_files->filename[0] == '\0') ? "New Buffer" : open_files->filename));
+
+#ifdef DEBUG
+ dump_buffer(current);
+#endif
+
+ return 0;
+}
+
+/* This function is used by the shortcut list. */
+int open_prevfile_void(void)
+{
+ return open_prevfile(0);
+}
+
+/*
+ * Open the next entry in the open_files structure. If closing_file is
+ * zero, update the current entry before switching from it. Otherwise, we
+ * are about to close that entry, so don't bother doing so. Return 0 on
+ * success and 1 on error.
+ */
+int open_nextfile(int closing_file)
+{
+ if (open_files == NULL)
+ return 1;
+
+ /* if we're not about to close the current entry, update it before
+ doing anything */
+ if (!closing_file)
+ add_open_file(1);
+
+ if (open_files->prev == NULL && open_files->next == NULL) {
+
+ /* only one file open */
+ if (!closing_file)
+ statusbar(_("No more open files"));
+ return 1;
+ }
+
+ if (open_files->next != NULL) {
+ open_files = open_files->next;
+
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+
+ }
+ else if (open_files->prev != NULL) {
+
+ /* if we're at the end, wrap around to the beginning */
+ while (open_files->prev != NULL) {
+ open_files = open_files->prev;
+
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+
+ }
+ }
+
+ load_open_file();
+
+ statusbar(_("Switched to %s"),
+ ((open_files->filename[0] == '\0') ? "New Buffer" : open_files->filename));
+
+#ifdef DEBUG
+ dump_buffer(current);
+#endif
+
+ return 0;
+}
+
+/* This function is used by the shortcut list. */
+int open_nextfile_void(void)
+{
+ return open_nextfile(0);
+}
+
+/*
+ * Delete an entry from the open_files filestruct. After deletion of an
+ * entry, the next or previous entry is opened, whichever is found first.
+ * Return 0 on success or 1 on error.
+ */
+int close_open_file(void)
+{
+ openfilestruct *tmp;
+
+ if (open_files == NULL)
+ return 1;
+
+ /* make sure open_files->fileage and fileage, and open_files->filebot
+ and filebot, are in sync; they might not be if lines have been cut
+ from the top or bottom of the file */
+ open_files->fileage = fileage;
+ open_files->filebot = filebot;
+
+ tmp = open_files;
+ if (open_nextfile(1)) {
+ if (open_prevfile(1))
+ return 1;
+ }
+
+ unlink_opennode(tmp);
+ delete_opennode(tmp);
+
+ shortcut_init(0);
+ display_main_list();
+ return 0;
+}
+#endif /* MULTIBUFFER */
+
+#if !defined(DISABLE_SPELLER) || !defined(DISABLE_OPERATINGDIR)
+/*
+ * When passed "[relative path]" or "[relative path][filename]" in
+ * origpath, return "[full path]" or "[full path][filename]" on success,
+ * or NULL on error. This is still done if the file doesn't exist but
+ * the relative path does (since the file could exist in memory but not
+ * yet on disk); it is not done if the relative path doesn't exist (since
+ * the first call to chdir() will fail then).
+ */
+char *get_full_path(const char *origpath)
+{
+ char *newpath = NULL, *last_slash, *d_here, *d_there, *d_there_file, tmp;
+ int path_only, last_slash_index;
+ struct stat fileinfo;
+ char *expanded_origpath;
+
+ /* first, get the current directory, and tack a slash onto the end of
+ it, unless it turns out to be "/", in which case leave it alone */
+
+#ifdef PATH_MAX
+ d_here = getcwd(NULL, PATH_MAX + 1);
+#else
+ d_here = getcwd(NULL, 0);
+#endif
+
+ if (d_here != NULL) {
+
+ align(&d_here);
+ if (strcmp(d_here, "/")) {
+ d_here = charealloc(d_here, strlen(d_here) + 2);
+ strcat(d_here, "/");
+ }
+
+ /* stat origpath; if stat() fails, assume that origpath refers to
+ a new file that hasn't been saved to disk yet (i. e. set
+ path_only to 0); if stat() succeeds, set path_only to 0 if
+ origpath doesn't refer to a directory, or to 1 if it does */
+ path_only = !stat(origpath, &fileinfo) && S_ISDIR(fileinfo.st_mode);
+
+ expanded_origpath = real_dir_from_tilde(origpath);
+ /* save the value of origpath in both d_there and d_there_file */
+ d_there = mallocstrcpy(NULL, expanded_origpath);
+ d_there_file = mallocstrcpy(NULL, expanded_origpath);
+ free(expanded_origpath);
+
+ /* if we have a path but no filename, tack slashes onto the ends
+ of both d_there and d_there_file, if they don't end in slashes
+ already */
+ if (path_only) {
+ tmp = d_there[strlen(d_there) - 1];
+ if (tmp != '/') {
+ d_there = charealloc(d_there, strlen(d_there) + 2);
+ strcat(d_there, "/");
+ d_there_file = charealloc(d_there_file, strlen(d_there_file) + 2);
+ strcat(d_there_file, "/");
+ }
+ }
+
+ /* search for the last slash in d_there */
+ last_slash = strrchr(d_there, '/');
+
+ /* if we didn't find one, copy d_here into d_there; all data is
+ then set up */
+ if (last_slash == NULL)
+ d_there = mallocstrcpy(d_there, d_here);
+ else {
+ /* otherwise, remove all non-path elements from d_there
+ (i. e. everything after the last slash) */
+ last_slash_index = strlen(d_there) - strlen(last_slash);
+ null_at(&d_there, last_slash_index + 1);
+
+ /* and remove all non-file elements from d_there_file (i. e.
+ everything before and including the last slash); if we
+ have a path but no filename, don't do anything */
+ if (!path_only) {
+ last_slash = strrchr(d_there_file, '/');
+ last_slash++;
+ strcpy(d_there_file, last_slash);
+ align(&d_there_file);
+ }
+
+ /* now go to the path specified in d_there */
+ if (chdir(d_there) != -1) {
+ /* get the full pathname, and save it back in d_there,
+ tacking a slash on the end if we have a path but no
+ filename; if the saving fails, get out */
+
+ free(d_there);
+
+#ifdef PATH_MAX
+ d_there = getcwd(NULL, PATH_MAX + 1);
+#else
+ d_there = getcwd(NULL, 0);
+#endif
+
+ align(&d_there);
+ if (d_there != NULL) {
+
+ /* add a slash to d_there, unless it's "/", in which
+ case we don't need it */
+ if (strcmp(d_there, "/")) {
+ d_there = charealloc(d_there, strlen(d_there) + 2);
+ strcat(d_there, "/");
+ }
+ }
+ else
+ return NULL;
+ }
+
+ /* finally, go back to where we were before, d_here (no error
+ checking is done on this chdir(), because we can do
+ nothing if it fails) */
+ chdir(d_here);
+ }
+
+ /* all data is set up; fill in newpath */
+
+ /* if we have a path and a filename, newpath = d_there +
+ d_there_file; otherwise, newpath = d_there */
+ if (!path_only) {
+ newpath = charalloc(strlen(d_there) + strlen(d_there_file) + 1);
+ strcpy(newpath, d_there);
+ strcat(newpath, d_there_file);
+ }
+ else {
+ newpath = charalloc(strlen(d_there) + 1);
+ strcpy(newpath, d_there);
+ }
+
+ /* finally, clean up */
+ free(d_there_file);
+ free(d_there);
+ free(d_here);
+ }
+
+ return newpath;
+}
+#endif /* !DISABLE_SPELLER || !DISABLE_OPERATINGDIR */
+
+#ifndef DISABLE_SPELLER
+/*
+ * This function accepts a path and returns the full path (via
+ * get_full_path()). On error, if the path doesn't reference a
+ * directory, or if the directory isn't writable, it returns NULL.
+ */
+char *check_writable_directory(const char *path)
+{
+ char *full_path = get_full_path(path);
+ int writable;
+ struct stat fileinfo;
+
+ /* if get_full_path() failed, return NULL */
+ if (full_path == NULL)
+ return NULL;
+
+ /* otherwise, stat() the full path to see if it's writable by the
+ user; set writable to 1 if it is, or 0 if it isn't */
+ writable = !stat(full_path, &fileinfo) && (fileinfo.st_mode & S_IWUSR);
+
+ /* if the full path doesn't end in a slash (meaning get_full_path()
+ found that it isn't a directory) or isn't writable, free full_path
+ and return NULL */
+ if (full_path[strlen(full_path) - 1] != '/' || writable == 0) {
+ free(full_path);
+ return NULL;
+ }
+
+ /* otherwise, return the full path */
+ return full_path;
+}
+
+/*
+ * This function accepts a directory name and filename prefix the same
+ * way that tempnam() does, determines the location for its temporary
+ * file the same way that tempnam() does, safely creates the temporary
+ * file there via mkstemp(), and returns the name of the temporary file
+ * the same way that tempnam() does. It does not reference the value of
+ * TMP_MAX because the total number of random filenames that it can
+ * generate using one prefix is equal to 256**6, which is a sufficiently
+ * large number to handle most cases. Since the behavior after tempnam()
+ * generates TMP_MAX random filenames is implementation-defined, my
+ * implementation is to go on generating random filenames regardless of
+ * it.
+ */
+char *safe_tempnam(const char *dirname, const char *filename_prefix)
+{
+ char *full_tempdir = NULL;
+ const char *TMPDIR_env;
+ int filedesc;
+
+ /* if $TMPDIR is set and non-empty, set tempdir to it, run it through
+ get_full_path(), and save the result in full_tempdir; otherwise,
+ leave full_tempdir set to NULL */
+ TMPDIR_env = getenv("TMPDIR");
+ if (TMPDIR_env != NULL && TMPDIR_env[0] != '\0')
+ full_tempdir = check_writable_directory(TMPDIR_env);
+
+ /* if $TMPDIR is blank or isn't set, or isn't a writable
+ directory, and dirname isn't NULL, try it; otherwise, leave
+ full_tempdir set to NULL */
+ if (full_tempdir == NULL && dirname != NULL)
+ full_tempdir = check_writable_directory(dirname);
+
+ /* if $TMPDIR is blank or isn't set, or if it isn't a writable
+ directory, and dirname is NULL, try P_tmpdir instead */
+ if (full_tempdir == NULL)
+ full_tempdir = check_writable_directory(P_tmpdir);
+
+ /* if P_tmpdir didn't work, use /tmp instead */
+ if (full_tempdir == NULL) {
+ full_tempdir = charalloc(6);
+ strcpy(full_tempdir, "/tmp/");
+ }
+
+ full_tempdir = charealloc(full_tempdir, strlen(full_tempdir) + 12);
+
+ /* like tempnam(), use only the first 5 characters of the prefix */
+ strncat(full_tempdir, filename_prefix, 5);
+ strcat(full_tempdir, "XXXXXX");
+ filedesc = mkstemp(full_tempdir);
+
+ /* if mkstemp succeeded, close the resulting file; afterwards, it'll be
+ 0 bytes long, so delete it; finally, return the filename (all that's
+ left of it) */
+ if (filedesc != -1) {
+ close(filedesc);
+ unlink(full_tempdir);
+ return full_tempdir;
+ }
+
+ free(full_tempdir);
+ return NULL;
+}
+#endif /* !DISABLE_SPELLER */
+
+#ifndef DISABLE_OPERATINGDIR
+/* Initialize full_operating_dir based on operating_dir. */
+void init_operating_dir(void)
+{
+ assert(full_operating_dir == NULL);
+
+ if (operating_dir == NULL)
+ return;
+ full_operating_dir = get_full_path(operating_dir);
+
+ /* If get_full_path() failed or the operating directory is
+ inaccessible, unset operating_dir. */
+ if (full_operating_dir == NULL || chdir(full_operating_dir) == -1) {
+ free(full_operating_dir);
+ full_operating_dir = NULL;
+ free(operating_dir);
+ operating_dir = NULL;
+ }
+}
+
+/*
+ * Check to see if we're inside the operating directory. Return 0 if we
+ * are, or 1 otherwise. If allow_tabcomp is nonzero, allow incomplete
+ * names that would be matches for the operating directory, so that tab
+ * completion will work.
+ */
+int check_operating_dir(const char *currpath, int allow_tabcomp)
+{
+ /* The char *full_operating_dir is global for mem cleanup, and
+ therefore we only need to get it the first time this function
+ is called; also, a relative operating directory path will
+ only be handled properly if this is done */
+
+ char *fullpath;
+ int retval = 0;
+ const char *whereami1, *whereami2 = NULL;
+
+ /* if no operating directory is set, don't bother doing anything */
+ if (operating_dir == NULL)
+ return 0;
+
+ fullpath = get_full_path(currpath);
+ if (fullpath == NULL)
+ return 1;
+
+ whereami1 = strstr(fullpath, full_operating_dir);
+ if (allow_tabcomp)
+ whereami2 = strstr(full_operating_dir, fullpath);
+
+ /* if both searches failed, we're outside the operating directory */
+ /* otherwise */
+ /* check the search results; if the full operating directory path is
+ not at the beginning of the full current path (for normal usage)
+ and vice versa (for tab completion, if we're allowing it), we're
+ outside the operating directory */
+ if (whereami1 != fullpath && whereami2 != full_operating_dir)
+ retval = 1;
+ free(fullpath);
+ /* otherwise, we're still inside it */
+ return retval;
+}
+#endif
+
+/*
+ * Write a file out. If tmp is nonzero, we set the umask to 0600,
+ * we don't set the global variable filename to its name, and don't
+ * print out how many lines we wrote on the statusbar.
+ *
+ * tmp means we are writing a tmp file in a secure fashion. We use
+ * it when spell checking or dumping the file on an error.
+ *
+ * append == 1 means we are appending instead of overwriting.
+ * append == 2 means we are prepending instead of overwriting.
+ *
+ * nonamechange means don't change the current filename, it is ignored
+ * if tmp is nonzero or if we're appending/prepending.
+ */
+int write_file(const char *name, int tmp, int append, int nonamechange)
+{
+ int retval = -1;
+ /* Instead of returning in this function, you should always
+ * merely set retval then goto cleanup_and_exit. */
+ long size;
+ int lineswritten = 0;
+ char *buf = NULL;
+ const filestruct *fileptr;
+ FILE *f;
+ int fd;
+ int mask = 0, realexists, anyexists;
+ struct stat st, lst;
+ char *realname = NULL;
+
+ if (name[0] == '\0') {
+ statusbar(_("Cancelled"));
+ return -1;
+ }
+ if (!tmp)
+ titlebar(NULL);
+ fileptr = fileage;
+
+ realname = real_dir_from_tilde(name);
+
+#ifndef DISABLE_OPERATINGDIR
+ /* If we're writing a temporary file, we're probably going outside
+ the operating directory, so skip the operating directory test. */
+ if (!tmp && operating_dir != NULL && check_operating_dir(realname, 0)) {
+ statusbar(_("Can't write outside of %s"), operating_dir);
+ goto cleanup_and_exit;
+ }
+#endif
+
+ /* Save the state of file at the end of the symlink (if there is
+ one). */
+ realexists = stat(realname, &st);
+
+#ifndef NANO_SMALL
+ /* We backup only if the backup toggle is set, the file isn't
+ temporary, and the file already exists. Furthermore, if we aren't
+ appending, prepending, or writing a selection, we backup only if
+ the file has not been modified by someone else since nano opened
+ it. */
+ if (ISSET(BACKUP_FILE) && !tmp && realexists == 0 &&
+ (append != 0 || ISSET(MARK_ISSET) ||
+ originalfilestat.st_mtime == st.st_mtime)) {
+ FILE *backup_file;
+ char *backupname = NULL;
+ char backupbuf[COPYFILEBLOCKSIZE];
+ size_t bytesread;
+ struct utimbuf filetime;
+
+ /* save the original file's access and modification times */
+ filetime.actime = originalfilestat.st_atime;
+ filetime.modtime = originalfilestat.st_mtime;
+
+ /* open the original file to copy to the backup */
+ f = fopen(realname, "rb");
+ if (f == NULL) {
+ statusbar(_("Could not read %s for backup: %s"), realname,
+ strerror(errno));
+ return -1;
+ }
+
+ backupname = charalloc(strlen(realname) + 2);
+ sprintf(backupname, "%s~", realname);
+
+ /* get a file descriptor for the destination backup file */
+ backup_file = fopen(backupname, "wb");
+ if (backup_file == NULL) {
+ statusbar(_("Couldn't write backup: %s"), strerror(errno));
+ free(backupname);
+ return -1;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "Backing up %s to %s\n", realname, backupname);
+#endif
+
+ /* copy the file */
+ while ((bytesread = fread(backupbuf, sizeof(char),
+ COPYFILEBLOCKSIZE, f)) > 0)
+ if (fwrite(backupbuf, sizeof(char), bytesread, backup_file) <= 0)
+ break;
+ fclose(backup_file);
+ fclose(f);
+
+ if (chmod(backupname, originalfilestat.st_mode) == -1)
+ statusbar(_("Could not set permissions %o on backup %s: %s"),
+ originalfilestat.st_mode, backupname,
+ strerror(errno));
+
+ if (chown(backupname, originalfilestat.st_uid,
+ originalfilestat.st_gid) == -1)
+ statusbar(_("Could not set owner %d/group %d on backup %s: %s"),
+ originalfilestat.st_uid, originalfilestat.st_gid,
+ backupname, strerror(errno));
+
+ if (utime(backupname, &filetime) == -1)
+ statusbar(_("Could not set access/modification time on backup %s: %s"),
+ backupname, strerror(errno));
+
+ free(backupname);
+ }
+#endif
+
+ /* Stat the link itself for the check... */
+ anyexists = lstat(realname, &lst);
+
+ /* New case: if the file exists, just give up */
+ if (tmp && anyexists != -1)
+ goto cleanup_and_exit;
+ /* NOTE: If you change this statement, you MUST CHANGE the if
+ statement below (that says:
+ if (realexists == -1 || tmp || (ISSET(NOFOLLOW_SYMLINKS) &&
+ S_ISLNK(lst.st_mode))) {
+ to reflect whether or not to link/unlink/rename the file */
+ else if (append != 2 && (!ISSET(NOFOLLOW_SYMLINKS) || !S_ISLNK(lst.st_mode)
+ || tmp)) {
+ /* Use O_EXCL if tmp is nonzero. This is now copied from joe,
+ because wiggy says so *shrug*. */
+ if (append != 0)
+ fd = open(realname, O_WRONLY | O_CREAT | O_APPEND, (S_IRUSR | S_IWUSR));
+ else if (tmp)
+ fd = open(realname, O_WRONLY | O_CREAT | O_EXCL, (S_IRUSR | S_IWUSR));
+ else
+ fd = open(realname, O_WRONLY | O_CREAT | O_TRUNC, (S_IRUSR | S_IWUSR));
+
+ /* First, just give up if we couldn't even open the file */
+ if (fd == -1) {
+ if (!tmp && ISSET(TEMP_OPT)) {
+ UNSET(TEMP_OPT);
+ retval = do_writeout(filename, 1, 0);
+ } else
+ statusbar(_("Could not open file for writing: %s"),
+ strerror(errno));
+ goto cleanup_and_exit;
+ }
+
+ }
+ /* Don't follow symlink. Create new file. */
+ else {
+ buf = charalloc(strlen(realname) + 8);
+ strcpy(buf, realname);
+ strcat(buf, ".XXXXXX");
+ if ((fd = mkstemp(buf)) == -1) {
+ if (ISSET(TEMP_OPT)) {
+ UNSET(TEMP_OPT);
+ retval = do_writeout(filename, 1, 0);
+ } else
+ statusbar(_("Could not open file for writing: %s"),
+ strerror(errno));
+ goto cleanup_and_exit;
+ }
+ }
+
+#ifdef DEBUG
+ dump_buffer(fileage);
+#endif
+
+ f = fdopen(fd, append == 1 ? "ab" : "wb");
+ if (f == NULL) {
+ statusbar(_("Could not open file for writing: %s"), strerror(errno));
+ goto cleanup_and_exit;
+ }
+
+ while (fileptr != NULL && fileptr->next != NULL) {
+ int data_len;
+
+ /* Next line is so we discount the "magic line" */
+ if (filebot == fileptr && fileptr->data[0] == '\0')
+ break;
+
+ data_len = strlen(fileptr->data);
+
+ /* newlines to nulls, just before we write to disk */
+ sunder(fileptr->data);
+
+ size = fwrite(fileptr->data, 1, data_len, f);
+
+ /* nulls to newlines; data_len is the string's real length here */
+ unsunder(fileptr->data, data_len);
+
+ if (size < data_len) {
+ statusbar(_("Could not open file for writing: %s"),
+ strerror(errno));
+ fclose(f);
+ goto cleanup_and_exit;
+ }
+#ifdef DEBUG
+ else
+ fprintf(stderr, "Wrote >%s\n", fileptr->data);
+#endif
+#ifndef NANO_SMALL
+ if (ISSET(DOS_FILE) || ISSET(MAC_FILE))
+ putc('\r', f);
+
+ if (!ISSET(MAC_FILE))
+#endif
+ putc('\n', f);
+
+ fileptr = fileptr->next;
+ lineswritten++;
+ }
+
+ if (fileptr != NULL) {
+ int data_len = strlen(fileptr->data);
+
+ /* newlines to nulls, just before we write to disk */
+ sunder(fileptr->data);
+
+ size = fwrite(fileptr->data, 1, data_len, f);
+
+ /* nulls to newlines; data_len is the string's real length here */
+ unsunder(fileptr->data, data_len);
+
+ if (size < data_len) {
+ statusbar(_("Could not open file for writing: %s"),
+ strerror(errno));
+ goto cleanup_and_exit;
+ } else if (data_len > 0) {
+#ifndef NANO_SMALL
+ if (ISSET(DOS_FILE) || ISSET(MAC_FILE)) {
+ if (putc('\r', f) == EOF) {
+ statusbar(_("Could not open file for writing: %s"),
+ strerror(errno));
+ fclose(f);
+ goto cleanup_and_exit;
+ }
+ lineswritten++;
+ }
+
+ if (!ISSET(MAC_FILE))
+#endif
+ {
+ if (putc('\n', f) == EOF) {
+ statusbar(_("Could not open file for writing: %s"),
+ strerror(errno));
+ fclose(f);
+ goto cleanup_and_exit;
+ }
+ lineswritten++;
+ }
+ }
+ }
+
+ if (fclose(f) != 0) {
+ statusbar(_("Could not close %s: %s"), realname, strerror(errno));
+ unlink(buf);
+ goto cleanup_and_exit;
+ }
+
+ /* if we're prepending, open the real file, and append it here */
+ if (append == 2) {
+ int fd_source, fd_dest;
+ FILE *f_source, *f_dest;
+ int prechar;
+
+ if ((fd_dest = open(buf, O_WRONLY | O_APPEND, (S_IRUSR | S_IWUSR))) == -1) {
+ statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
+ goto cleanup_and_exit;
+ }
+ f_dest = fdopen(fd_dest, "wb");
+ if (f_dest == NULL) {
+ statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
+ close(fd_dest);
+ goto cleanup_and_exit;
+ }
+ if ((fd_source = open(realname, O_RDONLY | O_CREAT)) == -1) {
+ statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
+ fclose(f_dest);
+ goto cleanup_and_exit;
+ }
+ f_source = fdopen(fd_source, "rb");
+ if (f_source == NULL) {
+ statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
+ fclose(f_dest);
+ close(fd_source);
+ goto cleanup_and_exit;
+ }
+
+ /* Doing this in blocks is an exercise left to some other reader. */
+ while ((prechar = getc(f_source)) != EOF) {
+ if (putc(prechar, f_dest) == EOF) {
+ statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
+ fclose(f_source);
+ fclose(f_dest);
+ goto cleanup_and_exit;
+ }
+ }
+
+ if (ferror(f_source)) {
+ statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
+ fclose(f_source);
+ fclose(f_dest);
+ goto cleanup_and_exit;
+ }
+
+ fclose(f_source);
+ fclose(f_dest);
+ }
+
+ if (realexists == -1 || tmp ||
+ (ISSET(NOFOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
+
+ /* Use default umask as file permissions if file is a new file. */
+ mask = umask(0);
+ umask(mask);
+
+ if (tmp) /* We don't want anyone reading our temporary file! */
+ mask = S_IRUSR | S_IWUSR;
+ else
+ mask = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
+ S_IWOTH) & ~mask;
+ } else
+ /* Use permissions from file we are overwriting. */
+ mask = st.st_mode;
+
+ if (append == 2 ||
+ (!tmp && (ISSET(NOFOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode)))) {
+ if (unlink(realname) == -1) {
+ if (errno != ENOENT) {
+ statusbar(_("Could not open %s for writing: %s"),
+ realname, strerror(errno));
+ unlink(buf);
+ goto cleanup_and_exit;
+ }
+ }
+ if (link(buf, realname) != -1)
+ unlink(buf);
+ else if (errno != EPERM) {
+ statusbar(_("Could not open %s for writing: %s"),
+ name, strerror(errno));
+ unlink(buf);
+ goto cleanup_and_exit;
+ } else if (rename(buf, realname) == -1) { /* Try a rename?? */
+ statusbar(_("Could not open %s for writing: %s"),
+ realname, strerror(errno));
+ unlink(buf);
+ goto cleanup_and_exit;
+ }
+ }
+ if (chmod(realname, mask) == -1)
+ statusbar(_("Could not set permissions %o on %s: %s"),
+ mask, realname, strerror(errno));
+
+ if (!tmp && append == 0) {
+ if (!nonamechange) {
+ filename = mallocstrcpy(filename, realname);
+#ifdef ENABLE_COLOR
+ update_color();
+ edit_refresh();
+#endif
+ }
+
+#ifndef NANO_SMALL
+ /* Update originalfilestat to reference the file as it is now. */
+ stat(filename, &originalfilestat);
+#endif
+ statusbar(P_("Wrote %d line", "Wrote %d lines", lineswritten),
+ lineswritten);
+ UNSET(MODIFIED);
+ titlebar(NULL);
+ }
+
+ retval = 1;
+
+ cleanup_and_exit:
+ free(realname);
+ free(buf);
+ return retval;
+}
+
+int do_writeout(const char *path, int exiting, int append)
+{
+ int i = 0;
+#ifdef NANO_EXTRA
+ static int did_cred = 0;
+#endif
+
+#if !defined(DISABLE_BROWSER) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+ currshortcut = writefile_list;
+#endif
+
+ answer = mallocstrcpy(answer, path);
+
+ if (exiting && ISSET(TEMP_OPT)) {
+ if (filename[0] != '\0') {
+ i = write_file(answer, 0, 0, 0);
+ display_main_list();
+ return i;
+ } else {
+ UNSET(TEMP_OPT);
+ do_exit();
+
+ /* They cancelled, abort quit */
+ return -1;
+ }
+ }
+
+ while (1) {
+#ifndef NANO_SMALL
+ const char *formatstr, *backupstr;
+
+ if (ISSET(MAC_FILE))
+ formatstr = _(" [Mac Format]");
+ else if (ISSET(DOS_FILE))
+ formatstr = _(" [DOS Format]");
+ else
+ formatstr = "";
+
+ if (ISSET(BACKUP_FILE))
+ backupstr = _(" [Backup]");
+ else
+ backupstr = "";
+
+ /* Be nice to the translation folks */
+ if (ISSET(MARK_ISSET) && !exiting) {
+ if (append == 2)
+ i = statusq(1, writefile_list, "", 0,
+ "%s%s%s", _("Prepend Selection to File"), formatstr, backupstr);
+ else if (append == 1)
+ i = statusq(1, writefile_list, "", 0,
+ "%s%s%s", _("Append Selection to File"), formatstr, backupstr);
+ else
+ i = statusq(1, writefile_list, "", 0,
+ "%s%s%s", _("Write Selection to File"), formatstr, backupstr);
+ } else {
+ if (append == 2)
+ i = statusq(1, writefile_list, answer, 0,
+ "%s%s%s", _("File Name to Prepend to"), formatstr, backupstr);
+ else if (append == 1)
+ i = statusq(1, writefile_list, answer, 0,
+ "%s%s%s", _("File Name to Append to"), formatstr, backupstr);
+ else
+ i = statusq(1, writefile_list, answer, 0,
+ "%s%s%s", _("File Name to Write"), formatstr, backupstr);
+ }
+#else
+ if (append == 2)
+ i = statusq(1, writefile_list, answer,
+ "%s", _("File Name to Prepend to"));
+ else if (append == 1)
+ i = statusq(1, writefile_list, answer,
+ "%s", _("File Name to Append to"));
+ else
+ i = statusq(1, writefile_list, answer,
+ "%s", _("File Name to Write"));
+#endif /* !NANO_SMALL */
+
+ if (i == -1) {
+ statusbar(_("Cancelled"));
+ display_main_list();
+ return 0;
+ }
+
+#ifndef DISABLE_BROWSER
+ if (i == NANO_TOFILES_KEY) {
+ char *tmp = do_browse_from(answer);
+
+ currshortcut = writefile_list;
+ if (tmp == NULL)
+ continue;
+ free(answer);
+ answer = tmp;
+ } else
+#endif /* !DISABLE_BROWSER */
+#ifndef NANO_SMALL
+ if (i == TOGGLE_DOS_KEY) {
+ UNSET(MAC_FILE);
+ TOGGLE(DOS_FILE);
+ continue;
+ } else if (i == TOGGLE_MAC_KEY) {
+ UNSET(DOS_FILE);
+ TOGGLE(MAC_FILE);
+ continue;
+ } else if (i == TOGGLE_BACKUP_KEY) {
+ TOGGLE(BACKUP_FILE);
+ continue;
+ } else
+#endif /* !NANO_SMALL */
+ if (i == NANO_PREPEND_KEY) {
+ append = append == 2 ? 0 : 2;
+ continue;
+ } else if (i == NANO_APPEND_KEY) {
+ append = append == 1 ? 0 : 1;
+ continue;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "filename is %s\n", answer);
+#endif
+
+#ifdef NANO_EXTRA
+ if (exiting && !ISSET(TEMP_OPT) && !strcasecmp(answer, "zzy")
+ && !did_cred) {
+ do_credits();
+ did_cred = 1;
+ return -1;
+ }
+#endif
+ if (append == 0 && strcmp(answer, filename)) {
+ struct stat st;
+
+ if (!stat(answer, &st)) {
+ i = do_yesno(0, 0, _("File exists, OVERWRITE ?"));
+
+ if (i == 0 || i == -1)
+ continue;
+ }
+ }
+
+#ifndef NANO_SMALL
+ /* Here's where we allow the selected text to be written to
+ a separate file. */
+ if (ISSET(MARK_ISSET) && !exiting) {
+ filestruct *fileagebak = fileage;
+ filestruct *filebotbak = filebot;
+ filestruct *cutback = cutbuffer;
+ int oldmod = ISSET(MODIFIED);
+ /* write_file() unsets the MODIFIED flag. */
+
+ cutbuffer = NULL;
+
+ /* Put the marked text in the cutbuffer without changing
+ the open file. */
+ cut_marked_segment(current, current_x, mark_beginbuf,
+ mark_beginx, 0);
+
+ fileage = cutbuffer;
+ filebot = get_cutbottom();
+ i = write_file(answer, 0, append, 1);
+
+ /* Now restore everything */
+ free_filestruct(cutbuffer);
+ fileage = fileagebak;
+ filebot = filebotbak;
+ cutbuffer = cutback;
+ if (oldmod)
+ set_modified();
+ } else
+#endif /* !NANO_SMALL */
+ i = write_file(answer, 0, append, 0);
+
+#ifdef ENABLE_MULTIBUFFER
+ /* If we're not about to exit, update the current entry in
+ the open_files structure. */
+ if (!exiting)
+ add_open_file(1);
+#endif
+ display_main_list();
+ return i;
+ } /* while (1) */
+}
+
+int do_writeout_void(void)
+{
+ return do_writeout(filename, 0, 0);
+}
+
+/* Return a malloc()ed string containing the actual directory, used
+ * to convert ~user and ~/ notation... */
+char *real_dir_from_tilde(const char *buf)
+{
+ char *dirtmp = NULL;
+
+ if (buf[0] == '~') {
+ size_t i;
+ const struct passwd *userdata;
+
+ /* Figure how how much of the str we need to compare */
+ for (i = 1; buf[i] != '/' && buf[i] != '\0'; i++)
+ ;
+
+ /* Determine home directory using getpwuid() or getpwent(),
+ don't rely on $HOME */
+ if (i == 1)
+ userdata = getpwuid(geteuid());
+ else {
+ do {
+ userdata = getpwent();
+ } while (userdata != NULL &&
+ strncmp(userdata->pw_name, buf + 1, i - 1));
+ }
+ endpwent();
+
+ if (userdata != NULL) { /* User found */
+ dirtmp = charalloc(strlen(userdata->pw_dir) + strlen(buf + i) + 1);
+ sprintf(dirtmp, "%s%s", userdata->pw_dir, &buf[i]);
+ }
+ }
+
+ if (dirtmp == NULL)
+ dirtmp = mallocstrcpy(dirtmp, buf);
+
+ return dirtmp;
+}
+
+#ifndef DISABLE_TABCOMP
+/* Tack a slash onto the string we're completing if it's a directory. We
+ * assume there is room for one more character on the end of buf. The
+ * return value says whether buf is a directory. */
+int append_slash_if_dir(char *buf, int *lastwastab, int *place)
+{
+ char *dirptr = real_dir_from_tilde(buf);
+ struct stat fileinfo;
+ int ret = 0;
+
+ assert(dirptr != buf);
+
+ if (stat(dirptr, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode)) {
+ strncat(buf, "/", 1);
+ (*place)++;
+ /* now we start over again with # of tabs so far */
+ *lastwastab = 0;
+ ret = 1;
+ }
+
+ free(dirptr);
+ return ret;
+}
+
+/*
+ * These functions (username_tab_completion(), cwd_tab_completion(), and
+ * input_tab()) were taken from busybox 0.46 (cmdedit.c). Here is the
+ * notice from that file:
+ *
+ * Termios command line History and Editting, originally
+ * intended for NetBSD sh (ash)
+ * Copyright (c) 1999
+ * Main code: Adam Rogoyski <rogoyski@cs.utexas.edu>
+ * Etc: Dave Cinege <dcinege@psychosis.com>
+ * Majorly adjusted/re-written for busybox:
+ * Erik Andersen <andersee@debian.org>
+ *
+ * You may use this code as you wish, so long as the original author(s)
+ * are attributed in any redistributions of the source code.
+ * This code is 'as is' with no warranty.
+ * This code may safely be consumed by a BSD or GPL license.
+ */
+
+char **username_tab_completion(char *buf, int *num_matches)
+{
+ char **matches = (char **)NULL;
+ char *matchline = NULL;
+ struct passwd *userdata;
+
+ *num_matches = 0;
+ matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
+
+ strcat(buf, "*");
+
+ while ((userdata = getpwent()) != NULL) {
+
+ if (check_wildcard_match(userdata->pw_name, &buf[1]) == TRUE) {
+
+ /* Cool, found a match. Add it to the list
+ * This makes a lot more sense to me (Chris) this way...
+ */
+
+#ifndef DISABLE_OPERATINGDIR
+ /* ...unless the match exists outside the operating
+ directory, in which case just go to the next match */
+
+ if (operating_dir != NULL) {
+ if (check_operating_dir(userdata->pw_dir, 1) != 0)
+ continue;
+ }
+#endif
+
+ matchline = charalloc(strlen(userdata->pw_name) + 2);
+ sprintf(matchline, "~%s", userdata->pw_name);
+ matches[*num_matches] = matchline;
+ ++*num_matches;
+
+ /* If there's no more room, bail out */
+ if (*num_matches == BUFSIZ)
+ break;
+ }
+ }
+ endpwent();
+
+ return matches;
+}
+
+/* This was originally called exe_n_cwd_tab_completion, but we're not
+ worried about executables, only filenames :> */
+
+char **cwd_tab_completion(char *buf, int *num_matches)
+{
+ char *dirname, *dirtmp = NULL, *tmp = NULL, *tmp2 = NULL;
+ char **matches = (char **)NULL;
+ DIR *dir;
+ struct dirent *next;
+
+ matches = (char **)nmalloc(BUFSIZ * sizeof(char *));
+
+ /* Stick a wildcard onto the buf, for later use */
+ strcat(buf, "*");
+
+ /* Okie, if there's a / in the buffer, strip out the directory part */
+ if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
+ dirname = charalloc(strlen(buf) + 1);
+ tmp = buf + strlen(buf);
+ while (*tmp != '/' && tmp != buf)
+ tmp--;
+
+ tmp++;
+
+ strncpy(dirname, buf, tmp - buf + 1);
+ dirname[tmp - buf] = '\0';
+
+ } else {
+
+#ifdef PATH_MAX
+ if ((dirname = getcwd(NULL, PATH_MAX + 1)) == NULL)
+#else
+ /* The better, but apparently segfault-causing way */
+ if ((dirname = getcwd(NULL, 0)) == NULL)
+#endif /* PATH_MAX */
+ return matches;
+ else
+ tmp = buf;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "\nDir = %s\n", dirname);
+ fprintf(stderr, "\nbuf = %s\n", buf);
+ fprintf(stderr, "\ntmp = %s\n", tmp);
+#endif
+
+ dirtmp = real_dir_from_tilde(dirname);
+ free(dirname);
+ dirname = dirtmp;
+
+#ifdef DEBUG
+ fprintf(stderr, "\nDir = %s\n", dirname);
+ fprintf(stderr, "\nbuf = %s\n", buf);
+ fprintf(stderr, "\ntmp = %s\n", tmp);
+#endif
+
+
+ dir = opendir(dirname);
+ if (dir == NULL) {
+ /* Don't print an error, just shut up and return */
+ *num_matches = 0;
+ beep();
+ return matches;
+ }
+ while ((next = readdir(dir)) != NULL) {
+
+#ifdef DEBUG
+ fprintf(stderr, "Comparing \'%s\'\n", next->d_name);
+#endif
+ /* See if this matches */
+ if (check_wildcard_match(next->d_name, tmp) == TRUE) {
+
+ /* Cool, found a match. Add it to the list
+ * This makes a lot more sense to me (Chris) this way...
+ */
+
+#ifndef DISABLE_OPERATINGDIR
+ /* ...unless the match exists outside the operating
+ directory, in which case just go to the next match; to
+ properly do operating directory checking, we have to add the
+ directory name to the beginning of the proposed match
+ before we check it */
+
+ if (operating_dir != NULL) {
+ tmp2 = charalloc(strlen(dirname) + strlen(next->d_name) + 2);
+ strcpy(tmp2, dirname);
+ strcat(tmp2, "/");
+ strcat(tmp2, next->d_name);
+ if (check_operating_dir(tmp2, 1)) {
+ free(tmp2);
+ continue;
+ }
+ free(tmp2);
+ }
+#endif
+
+ tmp2 = NULL;
+ tmp2 = charalloc(strlen(next->d_name) + 1);
+ strcpy(tmp2, next->d_name);
+ matches[*num_matches] = tmp2;
+ ++*num_matches;
+
+ /* If there's no more room, bail out */
+ if (*num_matches == BUFSIZ)
+ break;
+ }
+ }
+ closedir(dir);
+ free(dirname);
+
+ return matches;
+}
+
+/* This function now has an arg which refers to how much the statusbar
+ * (place) should be advanced, i.e. the new cursor pos. */
+char *input_tab(char *buf, int place, int *lastwastab, int *newplace, int *list)
+{
+ /* Do TAB completion */
+ static int num_matches = 0, match_matches = 0;
+ static char **matches = (char **)NULL;
+ int pos = place, i = 0, col = 0, editline = 0;
+ int longestname = 0, is_dir = 0;
+ char *foo;
+
+ *list = 0;
+
+ if (*lastwastab == FALSE) {
+ char *tmp, *copyto, *matchbuf;
+
+ *lastwastab = 1;
+
+ /* Make a local copy of the string -- up to the position of the
+ cursor */
+ matchbuf = charalloc(strlen(buf) + 2);
+ memset(matchbuf, '\0', strlen(buf) + 2);
+
+ strncpy(matchbuf, buf, place);
+ tmp = matchbuf;
+
+ /* skip any leading white space */
+ while (*tmp && isspace((int)*tmp))
+ ++tmp;
+
+ /* Free up any memory already allocated */
+ if (matches != NULL) {
+ for (i = i; i < num_matches; i++)
+ free(matches[i]);
+ free(matches);
+ matches = (char **)NULL;
+ num_matches = 0;
+ }
+
+ /* If the word starts with `~' and there is no slash in the word,
+ * then try completing this word as a username. */
+
+ /* If the original string begins with a tilde, and the part
+ we're trying to tab-complete doesn't contain a slash, copy
+ the part we're tab-completing into buf, so tab completion
+ will result in buf's containing only the tab-completed
+ username. */
+ if (buf[0] == '~' && strchr(tmp, '/') == NULL) {
+ buf = mallocstrcpy(buf, tmp);
+ matches = username_tab_completion(tmp, &num_matches);
+ }
+ /* If we're in the middle of the original line, copy the string
+ only up to the cursor position into buf, so tab completion
+ will result in buf's containing only the tab-completed
+ path/filename. */
+ else if (strlen(buf) > strlen(tmp))
+ buf = mallocstrcpy(buf, tmp);
+
+ /* Try to match everything in the current working directory that
+ * matches. */
+ if (matches == NULL)
+ matches = cwd_tab_completion(tmp, &num_matches);
+
+ /* Don't leak memory */
+ free(matchbuf);
+
+#ifdef DEBUG
+ fprintf(stderr, "%d matches found...\n", num_matches);
+#endif
+ /* Did we find exactly one match? */
+ switch (num_matches) {
+ case 0:
+ blank_edit();
+ wrefresh(edit);
+ break;
+ case 1:
+
+ buf = charealloc(buf, strlen(buf) + strlen(matches[0]) + 1);
+
+ if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
+ for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
+ tmp--);
+ tmp++;
+ } else
+ tmp = buf;
+
+ if (!strcmp(tmp, matches[0]))
+ is_dir = append_slash_if_dir(buf, lastwastab, newplace);
+
+ if (is_dir != 0)
+ break;
+
+ copyto = tmp;
+ for (pos = 0; *tmp == matches[0][pos] &&
+ pos <= strlen(matches[0]); pos++)
+ tmp++;
+
+ /* write out the matched name */
+ strncpy(copyto, matches[0], strlen(matches[0]) + 1);
+ *newplace += strlen(matches[0]) - pos;
+
+ /* if an exact match is typed in and Tab is pressed,
+ *newplace will now be negative; in that case, make it
+ zero, so that the cursor will stay where it is instead of
+ moving backward */
+ if (*newplace < 0)
+ *newplace = 0;
+
+ /* Is it a directory? */
+ append_slash_if_dir(buf, lastwastab, newplace);
+
+ break;
+ default:
+ /* Check to see if all matches share a beginning, and, if so,
+ tack it onto buf and then beep */
+
+ if (buf[0] != '\0' && strstr(buf, "/") != NULL) {
+ for (tmp = buf + strlen(buf); *tmp != '/' && tmp != buf;
+ tmp--);
+ tmp++;
+ } else
+ tmp = buf;
+
+ for (pos = 0; *tmp == matches[0][pos] && *tmp != '\0' &&
+ pos <= strlen(matches[0]); pos++)
+ tmp++;
+
+ while (1) {
+ match_matches = 0;
+
+ for (i = 0; i < num_matches; i++) {
+ if (matches[i][pos] == 0)
+ break;
+ else if (matches[i][pos] == matches[0][pos])
+ match_matches++;
+ }
+ if (match_matches == num_matches &&
+ (i == num_matches || matches[i] != 0)) {
+ /* All the matches have the same character at pos+1,
+ so paste it into buf... */
+ buf = charealloc(buf, strlen(buf) + 2);
+ strncat(buf, matches[0] + pos, 1);
+ *newplace += 1;
+ pos++;
+ } else {
+ beep();
+ break;
+ }
+ }
+ }
+ } else {
+ /* Ok -- the last char was a TAB. Since they
+ * just hit TAB again, print a list of all the
+ * available choices... */
+ if (matches != NULL && num_matches > 1) {
+
+ /* Blank the edit window, and print the matches out there */
+ blank_edit();
+ wmove(edit, 0, 0);
+
+ editline = 0;
+
+ /* Figure out the length of the longest filename */
+ for (i = 0; i < num_matches; i++)
+ if (strlen(matches[i]) > longestname)
+ longestname = strlen(matches[i]);
+
+ if (longestname > COLS - 1)
+ longestname = COLS - 1;
+
+ foo = charalloc(longestname + 5);
+
+ /* Print the list of matches */
+ for (i = 0, col = 0; i < num_matches; i++) {
+
+ /* make each filename shown be the same length as the longest
+ filename, with two spaces at the end */
+ snprintf(foo, longestname + 1, matches[i]);
+ while (strlen(foo) < longestname)
+ strcat(foo, " ");
+
+ strcat(foo, " ");
+
+ /* Disable el cursor */
+ curs_set(0);
+ /* now, put the match on the screen */
+ waddnstr(edit, foo, strlen(foo));
+ col += strlen(foo);
+
+ /* And if the next match isn't going to fit on the
+ line, move to the next one */
+ if (col > COLS - longestname && i + 1 < num_matches) {
+ editline++;
+ wmove(edit, editline, 0);
+ if (editline == editwinrows - 1) {
+ waddstr(edit, _("(more)"));
+ break;
+ }
+ col = 0;
+ }
+ }
+ free(foo);
+ wrefresh(edit);
+ *list = 1;
+ } else
+ beep();
+ }
+
+ /* Only refresh the edit window if we don't have a list of filename
+ matches on it */
+ if (*list == 0)
+ edit_refresh();
+ curs_set(1);
+ return buf;
+}
+#endif /* !DISABLE_TABCOMP */
+
+#ifndef DISABLE_BROWSER
+/* Return the stat of the file pointed to by path */
+struct stat filestat(const char *path)
+{
+ struct stat st;
+
+ stat(path, &st);
+ return st;
+}
+
+/* Our sort routine for file listings - sort directories before
+ * files, and then alphabetically. */
+int diralphasort(const void *va, const void *vb)
+{
+ struct stat fileinfo;
+ const char *a = *(char *const *)va, *b = *(char *const *)vb;
+ int aisdir = stat(a, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
+ int bisdir = stat(b, &fileinfo) != -1 && S_ISDIR(fileinfo.st_mode);
+
+ if (aisdir != 0 && bisdir == 0)
+ return -1;
+ if (aisdir == 0 && bisdir != 0)
+ return 1;
+
+ return strcasecmp(a, b);
+}
+
+/* Free our malloc()ed memory */
+void free_charptrarray(char **array, int len)
+{
+ for (; len > 0; len--)
+ free(array[len - 1]);
+ free(array);
+}
+
+/* Only print the last part of a path; isn't there a shell
+ * command for this? */
+const char *tail(const char *foo)
+{
+ const char *tmp = foo + strlen(foo);
+
+ while (*tmp != '/' && tmp != foo)
+ tmp--;
+
+ if (*tmp == '/')
+ tmp++;
+
+ return tmp;
+}
+
+/* Strip one dir from the end of a string. */
+void striponedir(char *foo)
+{
+ char *tmp;
+
+ assert(foo != NULL);
+ /* Don't strip the root dir */
+ if (*foo == '\0' || strcmp(foo, "/") == 0)
+ return;
+
+ tmp = foo + strlen(foo) - 1;
+ assert(tmp >= foo);
+ if (*tmp == '/')
+ *tmp = '\0';
+
+ while (*tmp != '/' && tmp != foo)
+ tmp--;
+
+ if (tmp != foo)
+ *tmp = '\0';
+ else { /* SPK may need to make a 'default' path here */
+ if (*tmp != '/')
+ *tmp = '.';
+ *(tmp + 1) = '\0';
+ }
+}
+
+int readable_dir(const char *path)
+{
+ DIR *dir = opendir(path);
+
+ /* If dir is NULL, don't do closedir(), since that changes errno. */
+ if (dir != NULL)
+ closedir(dir);
+ return dir != NULL;
+}
+
+/* Initialize the browser code, including the list of files in *path */
+char **browser_init(const char *path, int *longest, int *numents)
+{
+ DIR *dir;
+ struct dirent *next;
+ char **filelist;
+ int i = 0;
+ size_t path_len;
+
+ dir = opendir(path);
+ if (dir == NULL)
+ return NULL;
+
+ *numents = 0;
+ while ((next = readdir(dir)) != NULL) {
+ if (!strcmp(next->d_name, "."))
+ continue;
+ (*numents)++;
+ if (strlen(next->d_name) > *longest)
+ *longest = strlen(next->d_name);
+ }
+ rewinddir(dir);
+ *longest += 10;
+
+ filelist = (char **)nmalloc(*numents * sizeof (char *));
+
+ if (!strcmp(path, "/"))
+ path = "";
+ path_len = strlen(path);
+
+ while ((next = readdir(dir)) != NULL) {
+ if (!strcmp(next->d_name, "."))
+ continue;
+
+ filelist[i] = charalloc(strlen(next->d_name) + path_len + 2);
+ sprintf(filelist[i], "%s/%s", path, next->d_name);
+ i++;
+ }
+ closedir(dir);
+
+ if (*longest > COLS - 1)
+ *longest = COLS - 1;
+
+ return filelist;
+}
+
+/* Our browser function. inpath is the path to start browsing from */
+char *do_browser(const char *inpath)
+{
+ struct stat st;
+ char *foo, *retval = NULL;
+ static char *path = NULL;
+ int numents = 0, i = 0, j = 0, kbinput = -1, meta, longest = 0;
+ int abort = 0, col = 0, selected = 0, editline = 0, width = 0;
+ int filecols = 0, lineno = 0;
+ char **filelist = (char **)NULL;
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ MEVENT mevent;
+#endif
+
+ assert(inpath != NULL);
+
+ /* If path isn't the same as inpath, we are being passed a new
+ dir as an arg. We free it here so it will be copied from
+ inpath below */
+ if (path != NULL && strcmp(path, inpath)) {
+ free(path);
+ path = NULL;
+ }
+
+ /* if path doesn't exist, make it so */
+ if (path == NULL)
+ path = mallocstrcpy(NULL, inpath);
+
+ filelist = browser_init(path, &longest, &numents);
+ foo = charalloc(longest + 8);
+
+ /* Sort the list by directory first, then alphabetically */
+ qsort(filelist, numents, sizeof(char *), diralphasort);
+
+ titlebar(path);
+ bottombars(browser_list);
+ curs_set(0);
+ wmove(edit, 0, 0);
+ i = 0;
+ width = 0;
+ filecols = 0;
+
+ /* Loop invariant: Microsoft sucks. */
+ do {
+ char *new_path;
+ /* Used by the Go To Directory prompt. */
+
+ blank_statusbar_refresh();
+
+#if !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+ currshortcut = browser_list;
+#endif
+
+ editline = 0;
+ col = 0;
+
+ /* Compute line number we're on now, so we don't divide by zero later */
+ lineno = selected;
+ if (width != 0)
+ lineno /= width;
+
+ switch (kbinput) {
+
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ case KEY_MOUSE:
+ if (getmouse(&mevent) == ERR)
+ return retval;
+
+ /* If they clicked in the edit window, they probably clicked
+ on a file */
+ if (wenclose(edit, mevent.y, mevent.x)) {
+ int selectedbackup = selected;
+
+ mevent.y -= 2;
+
+ /* Longest is the width of each column. There are two
+ * spaces between each column. */
+ selected = (lineno / editwinrows) * editwinrows * width
+ + mevent.y * width + mevent.x / (longest + 2);
+
+ /* If they clicked beyond the end of a row, select the
+ * end of that row. */
+ if (mevent.x > width * (longest + 2))
+ selected--;
+
+ /* If we're off the screen, reset to the last item.
+ If we clicked where we did last time, select this name! */
+ if (selected > numents - 1)
+ selected = numents - 1;
+ else if (selectedbackup == selected)
+ ungetch('s'); /* Unget the 'select' key */
+ } else /* Must be clicking a shortcut */
+ do_mouse();
+
+ break;
+#endif
+ case NANO_UP_KEY:
+ if (selected - width >= 0)
+ selected -= width;
+ break;
+ case NANO_BACK_KEY:
+ if (selected > 0)
+ selected--;
+ break;
+ case NANO_DOWN_KEY:
+ if (selected + width <= numents - 1)
+ selected += width;
+ break;
+ case NANO_FORWARD_KEY:
+ if (selected < numents - 1)
+ selected++;
+ break;
+ case NANO_PREVPAGE_KEY:
+ case NANO_PREVPAGE_FKEY:
+ case '-':
+ if (selected >= (editwinrows + lineno % editwinrows) * width)
+ selected -= (editwinrows + lineno % editwinrows) * width;
+ else
+ selected = 0;
+ break;
+ case NANO_NEXTPAGE_KEY:
+ case NANO_NEXTPAGE_FKEY:
+ case ' ':
+ selected += (editwinrows - lineno % editwinrows) * width;
+ if (selected >= numents)
+ selected = numents - 1;
+ break;
+ case NANO_HELP_KEY:
+ case NANO_HELP_FKEY:
+ do_help();
+ break;
+ case NANO_ENTER_KEY:
+ case 's': /* More Pico compatibility */
+ case 'S':
+ /* You can't cd up from / */
+ if (!strcmp(filelist[selected], "/..") && !strcmp(path, "/")) {
+ statusbar(_("Can't move up a directory"));
+ beep();
+ break;
+ }
+
+#ifndef DISABLE_OPERATINGDIR
+ /*
+ * Note: the selected file can be outside the operating
+ * directory if it is .. or if it is a symlink to a directory
+ * outside the opdir. */
+ if (check_operating_dir(filelist[selected], FALSE)) {
+ statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
+ beep();
+ break;
+ }
+#endif
+
+ if (stat(filelist[selected], &st) == -1) {
+ statusbar(_("Can't open \"%s\": %s"), filelist[selected], strerror(errno));
+ beep();
+ break;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ retval = mallocstrcpy(retval, filelist[selected]);
+ abort = 1;
+ break;
+ }
+
+ new_path = mallocstrcpy(NULL, filelist[selected]);
+
+ if (strcmp("..", tail(new_path)) == 0) {
+ /* They want to go up a level, so strip off .. and the
+ current dir */
+ striponedir(new_path);
+ /* SPK for '.' path, get the current path via getcwd */
+ if (strcmp(new_path, ".") == 0) {
+ free(new_path);
+ new_path = getcwd(NULL, PATH_MAX + 1);
+ }
+ striponedir(new_path);
+ }
+
+ if (!readable_dir(new_path)) {
+ /* We can't open this dir for some reason. Complain */
+ statusbar(_("Can't open \"%s\": %s"), new_path, strerror(errno));
+ free(new_path);
+ break;
+ }
+
+ free_charptrarray(filelist, numents);
+ free(foo);
+ free(path);
+ path = new_path;
+ return do_browser(path);
+
+ /* Goto a specific directory */
+ case NANO_GOTO_KEY:
+ case NANO_GOTO_FKEY:
+ curs_set(1);
+ j = statusq(0, gotodir_list, "",
+#ifndef NANO_SMALL
+ 0,
+#endif
+ _("Goto Directory"));
+ bottombars(browser_list);
+ curs_set(0);
+
+ if (j < 0) {
+ statusbar(_("Goto Cancelled"));
+ break;
+ }
+
+ new_path = real_dir_from_tilde(answer);
+
+ if (new_path[0] != '/') {
+ new_path = charealloc(new_path, strlen(path) + strlen(answer) + 2);
+ sprintf(new_path, "%s/%s", path, answer);
+ }
+
+#ifndef DISABLE_OPERATINGDIR
+ if (check_operating_dir(new_path, FALSE)) {
+ statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
+ free(new_path);
+ break;
+ }
+#endif
+
+ if (!readable_dir(new_path)) {
+ /* We can't open this dir for some reason. Complain */
+ statusbar(_("Can't open \"%s\": %s"), answer, strerror(errno));
+ free(new_path);
+ break;
+ }
+
+ /* Start over again with the new path value */
+ free_charptrarray(filelist, numents);
+ free(foo);
+ free(path);
+ path = new_path;
+ return do_browser(path);
+
+ /* Stuff we want to abort the browser */
+ case 'e': /* Pico compatibility, yeech */
+ case 'E':
+ case NANO_CANCEL_KEY:
+ case NANO_EXIT_KEY:
+ case NANO_EXIT_FKEY:
+ abort = 1;
+ break;
+ }
+ if (abort)
+ break;
+
+ blank_edit();
+
+ if (width != 0)
+ i = width * editwinrows * ((selected / width) / editwinrows);
+ else
+ i = 0;
+
+ wmove(edit, 0, 0);
+ for (j = i; j < numents && editline <= editwinrows - 1; j++) {
+ filecols++;
+
+ strncpy(foo, tail(filelist[j]), strlen(tail(filelist[j])) + 1);
+ while (strlen(foo) < longest)
+ strcat(foo, " ");
+ col += strlen(foo);
+
+ /* Put file info in the string also */
+ /* We use lstat here to detect links; then, if we find a
+ symlink, we examine it via stat() to see if it is a
+ directory or just a file symlink */
+ lstat(filelist[j], &st);
+ if (S_ISDIR(st.st_mode))
+ strcpy(foo + longest - 5, "(dir)");
+ else {
+ if (S_ISLNK(st.st_mode)) {
+ /* Aha! It's a symlink! Now, is it a dir? If so,
+ mark it as such */
+ st = filestat(filelist[j]);
+ if (S_ISDIR(st.st_mode))
+ strcpy(foo + longest - 5, "(dir)");
+ else
+ strcpy(foo + longest - 2, "--");
+ } else if (st.st_size < (1 << 10)) /* less than 1 K */
+ sprintf(foo + longest - 7, "%4d B",
+ (int) st.st_size);
+ else if (st.st_size >= (1 << 30)) /* at least 1 gig */
+ sprintf(foo + longest - 7, "%4d GB",
+ (int) st.st_size >> 30);
+ else if (st.st_size >= (1 << 20)) /* at least 1 meg */
+ sprintf(foo + longest - 7, "%4d MB",
+ (int) st.st_size >> 20);
+ else /* It's more than 1 k and less than a meg */
+ sprintf(foo + longest - 7, "%4d KB",
+ (int) st.st_size >> 10);
+ }
+
+ /* Highlight the currently selected file/dir */
+ if (j == selected)
+ wattron(edit, A_REVERSE);
+ waddstr(edit, foo);
+ if (j == selected)
+ wattroff(edit, A_REVERSE);
+
+ /* And add some space between the cols */
+ waddstr(edit, " ");
+ col += 2;
+
+ /* And if the next entry isn't going to fit on the
+ line, move to the next one */
+ if (col > COLS - longest) {
+ editline++;
+ wmove(edit, editline, 0);
+ col = 0;
+ if (width == 0)
+ width = filecols;
+ }
+ }
+ wrefresh(edit);
+ } while ((kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE))) != NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
+ curs_set(1);
+ blank_edit();
+ titlebar(NULL);
+ edit_refresh();
+
+ /* cleanup */
+ free_charptrarray(filelist, numents);
+ free(foo);
+ return retval;
+}
+
+/* Browser front end, checks to see if inpath has a dir in it and, if so,
+ starts do_browser from there, else from the current dir */
+char *do_browse_from(const char *inpath)
+{
+ struct stat st;
+ char *bob;
+ /* The result of do_browser; the selected file name. */
+ char *path;
+ /* inpath, tilde expanded. */
+
+ assert(inpath != NULL);
+
+ path = real_dir_from_tilde(inpath);
+
+ /*
+ * Perhaps path is a directory. If so, we will pass that to
+ * do_browser. Otherwise, perhaps path is a directory / a file. So
+ * we try stripping off the last path element. If it still isn't a
+ * directory, just use the current directory. */
+
+ if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
+ striponedir(path);
+ if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
+ free(path);
+ path = getcwd(NULL, PATH_MAX + 1);
+ }
+ }
+
+#ifndef DISABLE_OPERATINGDIR
+ /* If the resulting path isn't in the operating directory, use that. */
+ if (check_operating_dir(path, FALSE))
+ path = mallocstrcpy(path, operating_dir);
+#endif
+
+ if (!readable_dir(path)) {
+ beep();
+ bob = NULL;
+ } else
+ bob = do_browser(path);
+ free(path);
+ return bob;
+}
+#endif /* !DISABLE_BROWSER */
+
+#ifndef NANO_SMALL
+#ifdef ENABLE_NANORC
+void load_history(void)
+{
+ FILE *hist;
+ const struct passwd *userage = NULL;
+ static char *nanohist;
+ char *buf, *ptr;
+ char *homenv = getenv("HOME");
+ historyheadtype *history = &search_history;
+
+
+ if (homenv != NULL) {
+ nanohist = charealloc(nanohist, strlen(homenv) + 15);
+ sprintf(nanohist, "%s/.nano_history", homenv);
+ } else {
+ userage = getpwuid(geteuid());
+ endpwent();
+ nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
+ sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
+ }
+
+ /* assume do_rcfile has reported missing home dir */
+
+ if (homenv != NULL || userage != NULL) {
+ hist = fopen(nanohist, "r");
+ if (hist == NULL) {
+ if (errno != ENOENT) {
+ /* Don't save history when we quit. */
+ UNSET(HISTORYLOG);
+ rcfile_error(_("Unable to open ~/.nano_history file, %s"), strerror(errno));
+ }
+ free(nanohist);
+ } else {
+ buf = charalloc(1024);
+ while (fgets(buf, 1023, hist) != 0) {
+ ptr = buf;
+ while (*ptr != '\n' && *ptr != '\0' && ptr < buf + 1023)
+ ptr++;
+ *ptr = '\0';
+ if (strlen(buf))
+ update_history(history, buf);
+ else
+ history = &replace_history;
+ }
+ fclose(hist);
+ free(buf);
+ free(nanohist);
+ UNSET(HISTORY_CHANGED);
+ }
+ }
+}
+
+/* save histories to ~/.nano_history */
+void save_history(void)
+{
+ FILE *hist;
+ const struct passwd *userage = NULL;
+ char *nanohist = NULL;
+ char *homenv = getenv("HOME");
+ historytype *h;
+
+ /* don't save unchanged or empty histories */
+ if (!((search_history.count || replace_history.count) &&
+ ISSET(HISTORY_CHANGED) && !ISSET(VIEW_MODE)))
+ return;
+
+ if (homenv != NULL) {
+ nanohist = charealloc(nanohist, strlen(homenv) + 15);
+ sprintf(nanohist, "%s/.nano_history", homenv);
+ } else {
+ userage = getpwuid(geteuid());
+ endpwent();
+ nanohist = charealloc(nanohist, strlen(userage->pw_dir) + 15);
+ sprintf(nanohist, "%s/.nano_history", userage->pw_dir);
+ }
+
+ if (homenv != NULL || userage != NULL) {
+ hist = fopen(nanohist, "wb");
+ if (hist == NULL) {
+ rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
+ } else {
+ /* set rw only by owner for security ?? */
+ chmod(nanohist, S_IRUSR | S_IWUSR);
+ /* write oldest first */
+ for (h = search_history.tail ; h->prev ; h = h->prev) {
+ h->data = charealloc(h->data, strlen(h->data) + 2);
+ strcat(h->data, "\n");
+ if (fputs(h->data, hist) == EOF) {
+ rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
+ goto come_from;
+ }
+ }
+ if (fputs("\n", hist) == EOF) {
+ rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
+ goto come_from;
+ }
+ for (h = replace_history.tail ; h->prev ; h = h->prev) {
+ h->data = charealloc(h->data, strlen(h->data) + 2);
+ strcat(h->data, "\n");
+ if (fputs(h->data, hist) == EOF) {
+ rcfile_msg(_("Unable to write ~/.nano_history file, %s"), strerror(errno));
+ goto come_from;
+ }
+ }
+ come_from:
+ fclose(hist);
+ }
+ free(nanohist);
+ }
+}
+#endif /* ENABLE_NANORC */
+#endif /* !NANO_SMALL */
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * global.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include "proto.h"
+#include "nano.h"
+
+/* Global variables */
+
+/* wrap_at might be set in rcfile.c or nano.c */
+int wrap_at = -CHARS_FROM_EOL;/* Right justified fill value, allows resize */
+char *last_search = NULL; /* Last string we searched for */
+char *last_replace = NULL; /* Last replacement string */
+int search_last_line; /* Is this the last search line? */
+int search_offscreen; /* Search lines not displayed */
+
+int flags = 0; /* Our new flag containing many options */
+WINDOW *edit; /* The file portion of the editor */
+WINDOW *topwin; /* Top line of screen */
+WINDOW *bottomwin; /* Bottom buffer */
+char *filename = NULL; /* Name of the file */
+
+#ifndef NANO_SMALL
+struct stat originalfilestat; /* Stat for the file as we loaded it */
+#endif
+
+int editwinrows = 0; /* How many rows long is the edit
+ window? */
+filestruct *current; /* Current buffer pointer */
+int current_x = 0, current_y = 0; /* Current position of X and Y in
+ the editor - relative to edit
+ window (0,0) */
+filestruct *fileage = NULL; /* Our file buffer */
+filestruct *edittop = NULL; /* Pointer to the top of the edit
+ buffer with respect to the
+ file struct */
+filestruct *editbot = NULL; /* Same for the bottom */
+filestruct *filebot = NULL; /* Last node in the file struct */
+filestruct *cutbuffer = NULL; /* A place to store cut text */
+
+#ifdef ENABLE_MULTIBUFFER
+openfilestruct *open_files = NULL; /* The list of open files */
+#endif
+
+#ifndef DISABLE_JUSTIFY
+char *quotestr = NULL; /* Quote string. The default value is
+ set in main(). */
+#endif
+
+int resetstatuspos; /* Hack for resetting the status bar
+ cursor position */
+char *answer = NULL; /* Answer str to many questions */
+int totlines = 0; /* Total number of lines in the file */
+long totsize = 0; /* Total number of bytes in the file */
+int placewewant = 0; /* The column we'd like the cursor
+ to jump to when we go to the
+ next or previous line */
+
+int tabsize = -1; /* Our internal tabsize variable. The
+ default value 8 is set in main(). */
+
+char *hblank = NULL; /* A horizontal blank line */
+#ifndef DISABLE_HELP
+char *help_text; /* The text in the help window */
+#endif
+
+/* More stuff for the marker select */
+
+#ifndef NANO_SMALL
+filestruct *mark_beginbuf; /* the begin marker buffer */
+int mark_beginx; /* X value in the string to start */
+#endif
+
+#ifndef DISABLE_OPERATINGDIR
+char *operating_dir = NULL; /* Operating directory, which we can't */
+char *full_operating_dir = NULL;/* go higher than */
+#endif
+
+#ifndef DISABLE_SPELLER
+char *alt_speller = NULL; /* Alternative spell command */
+#endif
+
+shortcut *main_list = NULL;
+shortcut *whereis_list = NULL;
+shortcut *replace_list = NULL;
+shortcut *replace_list_2 = NULL; /* 2nd half of replace dialog */
+shortcut *goto_list = NULL;
+shortcut *writefile_list = NULL;
+shortcut *insertfile_list = NULL;
+#ifndef DISABLE_HELP
+shortcut *help_list = NULL;
+#endif
+#ifndef DISABLE_SPELLER
+shortcut *spell_list = NULL;
+#endif
+#ifndef NANO_SMALL
+shortcut *extcmd_list = NULL;
+#endif
+#ifndef DISABLE_BROWSER
+shortcut *browser_list = NULL;
+shortcut *gotodir_list = NULL;
+#endif
+
+#ifdef ENABLE_COLOR
+const colortype *colorstrings = NULL;
+syntaxtype *syntaxes = NULL;
+char *syntaxstr = NULL;
+#endif
+
+#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+const shortcut *currshortcut; /* Current shortcut list we're using */
+#endif
+
+#ifndef NANO_SMALL
+toggle *toggles = NULL;
+#endif
+
+#ifndef NANO_SMALL
+historyheadtype search_history;
+historyheadtype replace_history;
+#endif
+
+/* Regular expressions */
+
+#ifdef HAVE_REGEX_H
+regex_t search_regexp; /* Global to store compiled search regexp */
+regmatch_t regmatches[10]; /* Match positions for parenthetical
+ subexpressions, max of 10 */
+#endif
+
+int curses_ended = FALSE; /* Indicates to statusbar() to simply
+ * write to stderr, since endwin() has
+ * ended curses mode. */
+
+
+int length_of_list(const shortcut *s)
+{
+ int i = 0;
+
+ for (; s != NULL; s = s->next)
+ i++;
+ return i;
+}
+
+/* Initialize a struct *without* our lovely braces =( */
+void sc_init_one(shortcut **shortcutage, int key, const char *desc,
+#ifndef DISABLE_HELP
+ const char *help,
+#endif
+ int alt, int misc1, int misc2, int view, int (*func) (void))
+{
+ shortcut *s;
+
+ if (*shortcutage == NULL) {
+ *shortcutage = (shortcut *)nmalloc(sizeof(shortcut));
+ s = *shortcutage;
+ } else {
+ for (s = *shortcutage; s->next != NULL; s = s->next)
+ ;
+ s->next = (shortcut *)nmalloc(sizeof(shortcut));
+ s = s->next;
+ }
+
+ s->val = key;
+ s->desc = desc;
+#ifndef DISABLE_HELP
+ s->help = help;
+#endif
+ s->altval = alt;
+ s->misc1 = misc1;
+ s->misc2 = misc2;
+ s->viewok = view;
+ s->func = func;
+ s->next = NULL;
+}
+
+#ifndef NANO_SMALL
+/* Create one new toggle structure, at the end of the toggles
+ * linked list. */
+void toggle_init_one(int val, const char *desc, int flag)
+{
+ toggle *u;
+
+ if (toggles == NULL) {
+ toggles = (toggle *)nmalloc(sizeof(toggle));
+ u = toggles;
+ } else {
+ for (u = toggles; u->next != NULL; u = u->next)
+ ;
+ u->next = (toggle *)nmalloc(sizeof(toggle));
+ u = u->next;
+ }
+
+ u->val = val;
+ u->desc = desc;
+ u->flag = flag;
+ u->next = NULL;
+}
+
+void toggle_init(void)
+{
+ char *toggle_const_msg, *toggle_autoindent_msg, *toggle_suspend_msg,
+ *toggle_nohelp_msg, *toggle_cuttoend_msg,
+ *toggle_noconvert_msg, *toggle_dos_msg, *toggle_mac_msg,
+ *toggle_backup_msg, *toggle_smooth_msg;
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ char *toggle_mouse_msg;
+#endif
+#ifndef DISABLE_WRAPPING
+ char *toggle_wrap_msg;
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ char *toggle_load_msg;
+#endif
+#ifdef ENABLE_COLOR
+ char *toggle_syntax_msg;
+#endif
+
+ /* There is no need to reinitialize the toggles. They can't
+ change. */
+ if (toggles != NULL)
+ return;
+
+ toggle_const_msg = _("Constant cursor position");
+ toggle_autoindent_msg = _("Auto indent");
+ toggle_suspend_msg = _("Suspend");
+ toggle_nohelp_msg = _("Help mode");
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ toggle_mouse_msg = _("Mouse support");
+#endif
+ toggle_cuttoend_msg = _("Cut to end");
+ toggle_noconvert_msg = _("No conversion from DOS/Mac format");
+ toggle_dos_msg = _("Writing file in DOS format");
+ toggle_mac_msg = _("Writing file in Mac format");
+ toggle_backup_msg = _("Backing up file");
+ toggle_smooth_msg = _("Smooth scrolling");
+#ifdef ENABLE_COLOR
+ toggle_syntax_msg = _("Color syntax highlighting");
+#endif
+#ifndef DISABLE_WRAPPING
+ toggle_wrap_msg = _("Auto line wrap");
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ toggle_load_msg = _("Multiple file buffers");
+#endif
+
+ toggle_init_one(TOGGLE_CONST_KEY, toggle_const_msg, CONSTUPDATE);
+ toggle_init_one(TOGGLE_AUTOINDENT_KEY, toggle_autoindent_msg, AUTOINDENT);
+ toggle_init_one(TOGGLE_SUSPEND_KEY, toggle_suspend_msg, SUSPEND);
+ toggle_init_one(TOGGLE_NOHELP_KEY, toggle_nohelp_msg, NO_HELP);
+#ifndef DISABLE_WRAPPING
+ toggle_init_one(TOGGLE_WRAP_KEY, toggle_wrap_msg, NO_WRAP);
+#endif
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ toggle_init_one(TOGGLE_MOUSE_KEY, toggle_mouse_msg, USE_MOUSE);
+#endif
+ toggle_init_one(TOGGLE_CUTTOEND_KEY, toggle_cuttoend_msg, CUT_TO_END);
+#ifdef ENABLE_MULTIBUFFER
+ toggle_init_one(TOGGLE_LOAD_KEY, toggle_load_msg, MULTIBUFFER);
+#endif
+ toggle_init_one(TOGGLE_NOCONVERT_KEY, toggle_noconvert_msg, NO_CONVERT);
+ toggle_init_one(TOGGLE_DOS_KEY, toggle_dos_msg, DOS_FILE);
+ toggle_init_one(TOGGLE_MAC_KEY, toggle_mac_msg, MAC_FILE);
+ toggle_init_one(TOGGLE_BACKUP_KEY, toggle_backup_msg, BACKUP_FILE);
+ toggle_init_one(TOGGLE_SMOOTH_KEY, toggle_smooth_msg, SMOOTHSCROLL);
+#ifdef ENABLE_COLOR
+ toggle_init_one(TOGGLE_SYNTAX_KEY, toggle_syntax_msg, COLOR_SYNTAX);
+#endif
+}
+
+#ifdef DEBUG
+/* Deallocate all of the toggles. */
+void free_toggles(void)
+{
+ while (toggles != NULL) {
+ toggle *pt = toggles; /* Think "previous toggle" */
+
+ toggles = toggles->next;
+ free(pt);
+ }
+}
+#endif
+#endif /* !NANO_SMALL */
+
+/* Deallocate the given shortcut. */
+void free_shortcutage(shortcut **shortcutage)
+{
+ assert(shortcutage != NULL);
+ while (*shortcutage != NULL) {
+ shortcut *ps = *shortcutage;
+ *shortcutage = (*shortcutage)->next;
+ free(ps);
+ }
+}
+
+void shortcut_init(int unjustify)
+{
+#ifndef DISABLE_HELP
+ const char *nano_help_msg = "", *nano_writeout_msg = "", *nano_exit_msg =
+ "", *nano_goto_msg = "", *nano_justify_msg =
+ "", *nano_replace_msg = "", *nano_insert_msg =
+ "", *nano_whereis_msg = "", *nano_whereis_next_msg =
+ "", *nano_prevpage_msg = "", *nano_nextpage_msg =
+ "", *nano_cut_msg = "", *nano_uncut_msg =
+ "", *nano_cursorpos_msg = "", *nano_spell_msg =
+ "", *nano_up_msg = "", *nano_down_msg =
+ "", *nano_forward_msg = "", *nano_back_msg = "", *nano_home_msg =
+ "", *nano_end_msg = "", *nano_firstline_msg =
+ "", *nano_lastline_msg = "", *nano_refresh_msg =
+ "", *nano_mark_msg = "", *nano_delete_msg =
+ "", *nano_backspace_msg = "", *nano_tab_msg =
+ "", *nano_enter_msg = "", *nano_cancel_msg =
+ "", *nano_unjustify_msg = "", *nano_append_msg =
+ "", *nano_prepend_msg = "", *nano_tofiles_msg =
+ "", *nano_gotodir_msg = "", *nano_case_msg =
+ "", *nano_reverse_msg = "", *nano_execute_msg =
+ "", *nano_dos_msg = "", *nano_mac_msg =
+ "", *nano_backup_msg = "", *nano_editstr_msg =
+ "", *nano_parabegin_msg = "", *nano_paraend_msg = "";
+
+#ifdef ENABLE_MULTIBUFFER
+ const char *nano_openprev_msg = "", *nano_opennext_msg =
+ "", *nano_multibuffer_msg = "";
+#endif
+#ifdef HAVE_REGEX_H
+ const char *nano_regexp_msg = "", *nano_bracket_msg = "";
+#endif
+
+ nano_help_msg = _("Invoke the help menu");
+ nano_writeout_msg = _("Write the current file to disk");
+#ifdef ENABLE_MULTIBUFFER
+ nano_exit_msg = _("Close current file buffer/Exit from nano");
+#else
+ nano_exit_msg = _("Exit from nano");
+#endif
+ nano_goto_msg = _("Go to a specific line number");
+ nano_justify_msg = _("Justify the current paragraph");
+ nano_unjustify_msg = _("Unjustify after a justify");
+ nano_replace_msg = _("Replace text within the editor");
+ nano_insert_msg = _("Insert another file into the current one");
+ nano_whereis_msg = _("Search for text within the editor");
+ nano_whereis_next_msg = _("Repeat last search");
+ nano_prevpage_msg = _("Move to the previous screen");
+ nano_nextpage_msg = _("Move to the next screen");
+ nano_cut_msg = _("Cut the current line and store it in the cutbuffer");
+ nano_uncut_msg = _("Uncut from the cutbuffer into the current line");
+ nano_cursorpos_msg = _("Show the position of the cursor");
+ nano_spell_msg = _("Invoke the spell checker, if available");
+ nano_up_msg = _("Move up one line");
+ nano_down_msg = _("Move down one line");
+ nano_forward_msg = _("Move forward one character");
+ nano_back_msg = _("Move back one character");
+ nano_home_msg = _("Move to the beginning of the current line");
+ nano_end_msg = _("Move to the end of the current line");
+ nano_firstline_msg = _("Go to the first line of the file");
+ nano_lastline_msg = _("Go to the last line of the file");
+ nano_refresh_msg = _("Refresh (redraw) the current screen");
+ nano_mark_msg = _("Mark text at the current cursor location");
+ nano_delete_msg = _("Delete the character under the cursor");
+ nano_backspace_msg =
+ _("Delete the character to the left of the cursor");
+ nano_tab_msg = _("Insert a tab character");
+ nano_enter_msg = _("Insert a carriage return at the cursor position");
+ nano_case_msg =
+ _("Make the current search or replace case (in)sensitive");
+ nano_tofiles_msg = _("Go to file browser");
+ nano_execute_msg = _("Execute external command");
+ nano_gotodir_msg = _("Go to directory");
+ nano_cancel_msg = _("Cancel the current function");
+ nano_append_msg = _("Append to the current file");
+ nano_prepend_msg = _("Prepend to the current file");
+ nano_reverse_msg = _("Search backwards");
+ nano_dos_msg = _("Write file out in DOS format");
+ nano_mac_msg = _("Write file out in Mac format");
+ nano_backup_msg = _("Back up original file when saving");
+ nano_editstr_msg = _("Edit the previous search/replace strings");
+ nano_parabegin_msg = _("Go to the beginning of the current paragraph");
+ nano_paraend_msg = _("Go to the end of the current paragraph");
+#ifdef HAVE_REGEX_H
+ nano_regexp_msg = _("Use regular expressions");
+ nano_bracket_msg = _("Find other bracket");
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ nano_openprev_msg = _("Switch to previous file buffer");
+ nano_opennext_msg = _("Switch to next file buffer");
+ nano_multibuffer_msg = _("Toggle insert into new file buffer");
+#endif
+#endif /* !DISABLE_HELP */
+
+ free_shortcutage(&main_list);
+
+/* The following macro is to be used in calling sc_init_one. The point is
+ * that sc_init_one takes 9 arguments, unless DISABLE_HELP is defined,
+ * when the fourth one should not be there. */
+#ifdef DISABLE_HELP
+# define IFHELP(help, nextvar) nextvar
+#else
+# define IFHELP(help, nextvar) help, nextvar
+#endif
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), NANO_HELP_FKEY, 0, VIEW,
+ do_help);
+
+#ifdef ENABLE_MULTIBUFFER
+ if (open_files != NULL && (open_files->prev != NULL || open_files->next != NULL))
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_EXIT_KEY, _("Close"),
+ IFHELP(nano_exit_msg, 0), NANO_EXIT_FKEY, 0, VIEW,
+ do_exit);
+ else
+#endif
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_EXIT_KEY, _("Exit"),
+ IFHELP(nano_exit_msg, 0), NANO_EXIT_FKEY, 0, VIEW,
+ do_exit);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_WRITEOUT_KEY, _("WriteOut"),
+ IFHELP(nano_writeout_msg, 0),
+ NANO_WRITEOUT_FKEY, 0, NOVIEW, do_writeout_void);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_JUSTIFY_KEY, _("Justify"),
+ IFHELP(nano_justify_msg, 0), NANO_JUSTIFY_FKEY, 0,
+ NOVIEW, do_justify);
+
+ /* this is so we can view multiple files */
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_INSERTFILE_KEY, _("Read File"),
+ IFHELP(nano_insert_msg, 0), NANO_INSERTFILE_FKEY, 0,
+#ifdef ENABLE_MULTIBUFFER
+ VIEW
+#else
+ NOVIEW
+#endif
+ , do_insertfile_void);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_WHEREIS_KEY, _("Where Is"),
+ IFHELP(nano_whereis_msg, 0),
+ NANO_WHEREIS_FKEY, 0, VIEW, do_search);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_PREVPAGE_KEY, _("Prev Page"),
+ IFHELP(nano_prevpage_msg, 0),
+ NANO_PREVPAGE_FKEY, KEY_PPAGE, VIEW, do_page_up);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_NEXTPAGE_KEY, _("Next Page"),
+ IFHELP(nano_nextpage_msg, 0),
+ NANO_NEXTPAGE_FKEY, KEY_NPAGE, VIEW, do_page_down);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_CUT_KEY, _("Cut Text"),
+ IFHELP(nano_cut_msg, 0),
+ NANO_CUT_FKEY, 0, NOVIEW, do_cut_text);
+
+ if (unjustify)
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_UNJUSTIFY_KEY, _("UnJustify"),
+ IFHELP(nano_unjustify_msg, 0),
+ 0, 0, NOVIEW, do_uncut_text);
+ else
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_UNCUT_KEY, _("UnCut Txt"),
+ IFHELP(nano_uncut_msg, 0),
+ NANO_UNCUT_FKEY, 0, NOVIEW, do_uncut_text);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_CURSORPOS_KEY, _("Cur Pos"),
+ IFHELP(nano_cursorpos_msg, 0),
+ NANO_CURSORPOS_FKEY, 0, VIEW, do_cursorpos_void);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&main_list, NANO_SPELL_KEY, _("To Spell"),
+ IFHELP(nano_spell_msg, 0),
+ NANO_SPELL_FKEY, 0, NOVIEW, do_spell);
+
+ sc_init_one(&main_list, NANO_UP_KEY, _("Up"),
+ IFHELP(nano_up_msg, 0),
+ KEY_UP, 0, VIEW, do_up);
+
+ sc_init_one(&main_list, NANO_DOWN_KEY, _("Down"),
+ IFHELP(nano_down_msg, 0),
+ KEY_DOWN, 0, VIEW, do_down);
+
+ sc_init_one(&main_list, NANO_FORWARD_KEY, _("Forward"),
+ IFHELP(nano_forward_msg, 0),
+ KEY_RIGHT, 0, VIEW, do_right);
+
+ sc_init_one(&main_list, NANO_BACK_KEY, _("Back"),
+ IFHELP(nano_back_msg, 0),
+ KEY_LEFT, 0, VIEW, do_left);
+
+ sc_init_one(&main_list, NANO_HOME_KEY, _("Home"),
+ IFHELP(nano_home_msg, 0),
+ KEY_HOME, 362, VIEW, do_home);
+
+ sc_init_one(&main_list, NANO_END_KEY, _("End"),
+ IFHELP(nano_end_msg, 0),
+ KEY_END, 385, VIEW, do_end);
+
+ sc_init_one(&main_list, NANO_REFRESH_KEY, _("Refresh"),
+ IFHELP(nano_refresh_msg, 0),
+ 0, 0, VIEW, total_refresh);
+
+ sc_init_one(&main_list, NANO_MARK_KEY, _("Mark Text"),
+ IFHELP(nano_mark_msg, NANO_ALT_MARK_KEY),
+ 0, 0, NOVIEW, do_mark);
+
+ sc_init_one(&main_list, NANO_DELETE_KEY, _("Delete"),
+ IFHELP(nano_delete_msg, 0), KEY_DC,
+ NANO_CONTROL_D, NOVIEW, do_delete);
+
+ sc_init_one(&main_list, NANO_BACKSPACE_KEY, _("Backspace"),
+ IFHELP(nano_backspace_msg, 0),
+ KEY_BACKSPACE, 127, NOVIEW, do_backspace);
+
+ sc_init_one(&main_list, NANO_TAB_KEY, _("Tab"),
+ IFHELP(nano_tab_msg, 0), 0, 0, NOVIEW, do_tab);
+
+ sc_init_one(&main_list, NANO_REPLACE_KEY, _("Replace"),
+ IFHELP(nano_replace_msg, NANO_ALT_REPLACE_KEY),
+ NANO_REPLACE_FKEY, 0, NOVIEW, do_replace);
+
+ sc_init_one(&main_list, NANO_ENTER_KEY, _("Enter"),
+ IFHELP(nano_enter_msg, 0),
+ KEY_ENTER, NANO_CONTROL_M, NOVIEW, do_enter);
+
+ sc_init_one(&main_list, NANO_GOTO_KEY, _("Go To Line"),
+ IFHELP(nano_goto_msg, NANO_ALT_GOTO_KEY),
+ NANO_GOTO_FKEY, 0, VIEW, do_gotoline_void);
+
+#ifndef NANO_SMALL
+ sc_init_one(&main_list, NANO_NEXTWORD_KEY, _("Next Word"),
+ IFHELP(_("Move forward one word"), 0),
+ 0, 0, VIEW, do_next_word);
+
+ sc_init_one(&main_list, -9, _("Prev Word"),
+ IFHELP(_("Move backward one word"), NANO_PREVWORD_KEY), 0, 0,
+ VIEW, do_prev_word);
+#endif
+#if !defined(NANO_SMALL) && defined(HAVE_REGEX_H)
+ sc_init_one(&main_list, -9, _("Find Other Bracket"),
+ IFHELP(nano_bracket_msg, NANO_BRACKET_KEY),
+ 0, 0, VIEW, do_find_bracket);
+#endif
+
+ sc_init_one(&main_list, -9, _("Where Is Next"),
+ IFHELP(nano_whereis_next_msg, NANO_WHEREIS_NEXT_KEY), 0, 0,
+ NOVIEW, do_research);
+
+#ifdef ENABLE_MULTIBUFFER
+ sc_init_one(&main_list, -9, _("Previous File"),
+ IFHELP(nano_openprev_msg, NANO_OPENPREV_KEY),
+ 0, 0, VIEW, open_prevfile_void);
+ sc_init_one(&main_list, -9, _("Next File"),
+ IFHELP(nano_opennext_msg, NANO_OPENNEXT_KEY),
+ 0, 0, VIEW, open_nextfile_void);
+#endif
+
+ free_shortcutage(&whereis_list);
+
+ sc_init_one(&whereis_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, NANO_FIRSTLINE_KEY, _("First Line"),
+ IFHELP(nano_firstline_msg, 0),
+ 0, 0, VIEW, do_first_line);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, NANO_LASTLINE_KEY, _("Last Line"),
+ IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, NANO_OTHERSEARCH_KEY, _("Replace"),
+ IFHELP(nano_replace_msg, 0), 0, 0, VIEW, do_replace);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, NANO_FROMSEARCHTOGOTO_KEY, _("Go To Line"),
+ IFHELP(nano_goto_msg, 0), 0, 0, VIEW, do_gotoline_void);
+
+#ifndef DISABLE_JUSTIFY
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, NANO_PARABEGIN_KEY, _("Beg of Par"),
+ IFHELP(nano_parabegin_msg, 0), 0, 0, VIEW, do_para_begin);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, NANO_PARAEND_KEY, _("End of Par"),
+ IFHELP(nano_paraend_msg, 0), 0, 0, VIEW, do_para_end);
+#endif
+
+#ifndef NANO_SMALL
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, TOGGLE_CASE_KEY, _("Case Sens"),
+ IFHELP(nano_case_msg, 0), 0, 0, VIEW, 0);
+
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, TOGGLE_BACKWARDS_KEY, _("Direction"),
+ IFHELP(nano_reverse_msg, 0), 0, 0, VIEW, 0);
+
+#ifdef HAVE_REGEX_H
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, TOGGLE_REGEXP_KEY, _("Regexp"),
+ IFHELP(nano_regexp_msg, 0), 0, 0, VIEW, 0);
+#endif
+
+#ifndef NANO_SMALL
+ /* Translators: try to keep this string under 10 characters long */
+ sc_init_one(&whereis_list, KEY_UP, _("History"),
+ IFHELP(nano_editstr_msg, 0), NANO_UP_KEY, 0, VIEW, 0);
+#endif
+
+#endif /* !NANO_SMALL */
+
+ free_shortcutage(&replace_list);
+
+ sc_init_one(&replace_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&replace_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+
+ sc_init_one(&replace_list, NANO_FIRSTLINE_KEY, _("First Line"),
+ IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, do_first_line);
+
+ sc_init_one(&replace_list, NANO_LASTLINE_KEY, _("Last Line"),
+ IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
+
+ /* Translators: try to keep this string under 12 characters long */
+ sc_init_one(&replace_list, NANO_OTHERSEARCH_KEY, _("No Replace"),
+ IFHELP(nano_whereis_msg, 0), 0, 0, VIEW, do_search);
+
+ sc_init_one(&replace_list, NANO_FROMSEARCHTOGOTO_KEY, _("Go To Line"),
+ IFHELP(nano_goto_msg, 0), 0, 0, VIEW, do_gotoline_void);
+
+#ifndef NANO_SMALL
+ sc_init_one(&replace_list, TOGGLE_CASE_KEY, _("Case Sens"),
+ IFHELP(nano_case_msg, 0), 0, 0, VIEW, 0);
+
+ sc_init_one(&replace_list, TOGGLE_BACKWARDS_KEY, _("Direction"),
+ IFHELP(nano_reverse_msg, 0), 0, 0, VIEW, 0);
+
+#ifdef HAVE_REGEX_H
+ sc_init_one(&replace_list, TOGGLE_REGEXP_KEY, _("Regexp"),
+ IFHELP(nano_regexp_msg, 0), 0, 0, VIEW, 0);
+#endif
+
+ sc_init_one(&replace_list, KEY_UP, _("History"),
+ IFHELP(nano_editstr_msg, 0), NANO_UP_KEY, 0, VIEW, 0);
+#endif /* !NANO_SMALL */
+
+ free_shortcutage(&replace_list_2);
+
+ sc_init_one(&replace_list_2, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&replace_list_2, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+
+ sc_init_one(&replace_list_2, NANO_FIRSTLINE_KEY, _("First Line"),
+ IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, do_first_line);
+
+ sc_init_one(&replace_list_2, NANO_LASTLINE_KEY, _("Last Line"),
+ IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
+
+#ifndef NANO_SMALL
+ sc_init_one(&replace_list_2, KEY_UP, _("History"),
+ IFHELP(nano_editstr_msg, 0), NANO_UP_KEY, 0, VIEW, 0);
+#endif
+
+ free_shortcutage(&goto_list);
+
+ sc_init_one(&goto_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&goto_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+
+ sc_init_one(&goto_list, NANO_FIRSTLINE_KEY, _("First Line"),
+ IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, do_first_line);
+
+ sc_init_one(&goto_list, NANO_LASTLINE_KEY, _("Last Line"),
+ IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line);
+
+#ifndef DISABLE_HELP
+ free_shortcutage(&help_list);
+
+ sc_init_one(&help_list, NANO_PREVPAGE_KEY, _("Prev Page"),
+ IFHELP(nano_prevpage_msg, 0), NANO_PREVPAGE_FKEY,
+ KEY_PPAGE, VIEW, do_page_up);
+
+ sc_init_one(&help_list, NANO_NEXTPAGE_KEY, _("Next Page"),
+ IFHELP(nano_nextpage_msg, 0),
+ NANO_NEXTPAGE_FKEY, KEY_NPAGE, VIEW, do_page_down);
+
+ sc_init_one(&help_list, NANO_EXIT_KEY, _("Exit"),
+ IFHELP(nano_exit_msg, 0), NANO_EXIT_FKEY, 0, VIEW,
+ do_exit);
+#endif
+
+ free_shortcutage(&writefile_list);
+
+ sc_init_one(&writefile_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+#ifndef DISABLE_BROWSER
+ /* Translators: try to keep this string under 16 characters long */
+ sc_init_one(&writefile_list, NANO_TOFILES_KEY, _("To Files"),
+ IFHELP(nano_tofiles_msg, 0), 0, 0, NOVIEW, 0);
+#endif
+
+#ifndef NANO_SMALL
+ /* Translators: try to keep this string under 16 characters long */
+ sc_init_one(&writefile_list, TOGGLE_DOS_KEY, _("DOS Format"),
+ IFHELP(nano_dos_msg, 0), 0, 0, NOVIEW, 0);
+
+ /* Translators: try to keep this string under 16 characters long */
+ sc_init_one(&writefile_list, TOGGLE_MAC_KEY, _("Mac Format"),
+ IFHELP(nano_mac_msg, 0), 0, 0, NOVIEW, 0);
+#endif
+
+ /* Translators: try to keep this string under 16 characters long */
+ sc_init_one(&writefile_list, NANO_APPEND_KEY, _("Append"),
+ IFHELP(nano_append_msg, 0), 0, 0, NOVIEW, 0);
+
+ /* Translators: try to keep this string under 16 characters long */
+ sc_init_one(&writefile_list, NANO_PREPEND_KEY, _("Prepend"),
+ IFHELP(nano_prepend_msg, 0), 0, 0, NOVIEW, 0);
+
+#ifndef NANO_SMALL
+ /* Translators: try to keep this string under 16 characters long */
+ sc_init_one(&writefile_list, TOGGLE_BACKUP_KEY, _("Backup File"),
+ IFHELP(nano_backup_msg, 0), 0, 0, NOVIEW, 0);
+#endif
+
+ sc_init_one(&writefile_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+
+ free_shortcutage(&insertfile_list);
+
+ sc_init_one(&insertfile_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&insertfile_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+
+#ifndef DISABLE_BROWSER
+ sc_init_one(&insertfile_list, NANO_TOFILES_KEY, _("To Files"),
+ IFHELP(nano_tofiles_msg, 0), 0, 0, NOVIEW, 0);
+#endif
+#ifndef NANO_SMALL
+ /* Translators: try to keep this string under 22 characters long */
+ sc_init_one(&insertfile_list, NANO_EXTCMD_KEY, _("Execute Command"),
+ IFHELP(nano_execute_msg, 0), 0, 0, NOVIEW, 0);
+#ifdef ENABLE_MULTIBUFFER
+ /* Translators: try to keep this string under 22 characters long */
+ sc_init_one(&insertfile_list, TOGGLE_LOAD_KEY, _("New Buffer"),
+ IFHELP(nano_multibuffer_msg, 0), 0, 0, NOVIEW, 0);
+#endif
+#endif
+
+#ifndef DISABLE_SPELLER
+ free_shortcutage(&spell_list);
+
+ sc_init_one(&spell_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&spell_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+#endif
+
+#ifndef NANO_SMALL
+ free_shortcutage(&extcmd_list);
+
+ sc_init_one(&extcmd_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&extcmd_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+#endif
+
+#ifndef DISABLE_BROWSER
+ free_shortcutage(&browser_list);
+
+ sc_init_one(&browser_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&browser_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), NANO_EXIT_FKEY, 0, VIEW, 0);
+
+ sc_init_one(&browser_list, NANO_PREVPAGE_KEY, _("Prev Page"),
+ IFHELP(nano_prevpage_msg, 0), NANO_PREVPAGE_FKEY,
+ KEY_PPAGE, VIEW, 0);
+
+ sc_init_one(&browser_list, NANO_NEXTPAGE_KEY, _("Next Page"),
+ IFHELP(nano_nextpage_msg, 0), NANO_NEXTPAGE_FKEY,
+ KEY_NPAGE, VIEW, 0);
+
+ /* Translators: try to keep this string under 22 characters long */
+ sc_init_one(&browser_list, NANO_GOTO_KEY, _("Go To Dir"),
+ IFHELP(nano_gotodir_msg, NANO_ALT_GOTO_KEY),
+ NANO_GOTO_FKEY, 0, VIEW, 0);
+
+ free_shortcutage(&gotodir_list);
+
+ sc_init_one(&gotodir_list, NANO_HELP_KEY, _("Get Help"),
+ IFHELP(nano_help_msg, 0), 0, 0, VIEW, do_help);
+
+ sc_init_one(&gotodir_list, NANO_CANCEL_KEY, _("Cancel"),
+ IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0);
+#endif
+
+#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+ currshortcut = main_list;
+#endif
+#ifndef NANO_SMALL
+ toggle_init();
+#endif
+}
+
+/* This function is called just before calling exit(). Practically, the
+ * only effect is to cause a segmentation fault if the various data
+ * structures got bolloxed earlier. Thus, we don't bother having this
+ * function unless debugging is turned on. */
+#ifdef DEBUG
+/* added by SPK for memory cleanup, gracefully return our malloc()s */
+void thanks_for_all_the_fish(void)
+{
+#ifndef DISABLE_JUSTIFY
+ if (quotestr != NULL)
+ free(quotestr);
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ if (operating_dir != NULL)
+ free(operating_dir);
+ if (full_operating_dir != NULL)
+ free(full_operating_dir);
+#endif
+ if (last_search != NULL)
+ free(last_search);
+ if (last_replace != NULL)
+ free(last_replace);
+ if (hblank != NULL)
+ free(hblank);
+#ifndef DISABLE_SPELLER
+ if (alt_speller != NULL)
+ free(alt_speller);
+#endif
+#ifndef DISABLE_HELP
+ if (help_text != NULL)
+ free(help_text);
+#endif
+ if (filename != NULL)
+ free(filename);
+ if (answer != NULL)
+ free(answer);
+ if (cutbuffer != NULL)
+ free_filestruct(cutbuffer);
+
+ free_shortcutage(&main_list);
+ free_shortcutage(&whereis_list);
+ free_shortcutage(&replace_list);
+ free_shortcutage(&replace_list_2);
+ free_shortcutage(&goto_list);
+ free_shortcutage(&writefile_list);
+ free_shortcutage(&insertfile_list);
+#ifndef DISABLE_HELP
+ free_shortcutage(&help_list);
+#endif
+#ifndef DISABLE_SPELLER
+ free_shortcutage(&spell_list);
+#endif
+#ifndef NANO_SMALL
+ free_shortcutage(&extcmd_list);
+#endif
+#ifndef DISABLE_BROWSER
+ free_shortcutage(&browser_list);
+ free_shortcutage(&gotodir_list);
+#endif
+
+#ifndef NANO_SMALL
+ free_toggles();
+#endif
+
+#ifdef ENABLE_MULTIBUFFER
+ if (open_files != NULL) {
+ /* We free the memory associated with each open file. */
+ while (open_files->prev != NULL)
+ open_files = open_files->prev;
+ free_openfilestruct(open_files);
+ }
+#else
+ free_filestruct(fileage);
+#endif
+
+#ifdef ENABLE_COLOR
+ free(syntaxstr);
+ while (syntaxes != NULL) {
+ syntaxtype *bill = syntaxes;
+
+ free(syntaxes->desc);
+ while (syntaxes->extensions != NULL) {
+ exttype *bob = syntaxes->extensions;
+
+ syntaxes->extensions = bob->next;
+ regfree(&bob->val);
+ free(bob);
+ }
+ while (syntaxes->color != NULL) {
+ colortype *bob = syntaxes->color;
+
+ syntaxes->color = bob->next;
+ regfree(&bob->start);
+ if (bob->end != NULL)
+ regfree(bob->end);
+ free(bob->end);
+ free(bob);
+ }
+ syntaxes = syntaxes->next;
+ free(bill);
+ }
+#endif /* ENABLE_COLOR */
+#ifndef NANO_SMALL
+ /* free history lists */
+ free_history(&search_history);
+ free_history(&replace_history);
+#endif
+}
+#endif /* DEBUG */
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * move.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+int do_home(void)
+{
+ current_x = 0;
+ placewewant = 0;
+ update_line(current, current_x);
+ return 1;
+}
+
+int do_end(void)
+{
+ current_x = strlen(current->data);
+ placewewant = xplustabs();
+ update_line(current, current_x);
+ return 1;
+}
+
+void page_up(void)
+{
+ if (edittop != fileage) {
+#ifndef NANO_SMALL
+ if (ISSET(SMOOTHSCROLL))
+ edit_update(edittop->prev, TOP);
+ else
+#endif
+ {
+ edit_update(edittop, CENTER);
+ /* Now that we've updated the edit window, edittop might be
+ at the top of the file; if so, just move the cursor up one
+ line and don't center it. */
+ if (edittop != fileage)
+ center_cursor();
+ else
+ reset_cursor();
+ }
+ } else
+ current_y = 0;
+
+ update_cursor();
+}
+
+int do_page_up(void)
+{
+ int i;
+
+ wrap_reset();
+ current_x = 0;
+ placewewant = 0;
+
+ if (current == fileage)
+ return 0;
+
+ current_y = 0;
+ current = edittop;
+ for (i = 0; i <= editwinrows - 3 && current->prev != NULL; i++)
+ current = current->prev;
+
+ edit_update(current, TOP);
+ update_cursor();
+
+ check_statblank();
+ return 1;
+}
+
+int do_page_down(void)
+{
+ wrap_reset();
+ current_x = 0;
+ placewewant = 0;
+
+ if (current == filebot)
+ return 0;
+
+ /* AHEM, if we only have a screen or less of text, DON'T do an
+ edit_update(), just move the cursor to editbot! */
+ if (edittop == fileage && editbot == filebot && totlines < editwinrows) {
+ current = editbot;
+ reset_cursor();
+#ifndef NANO_SMALL
+ /* ...unless marking is on, in which case we need it to update
+ the highlight. */
+ if (ISSET(MARK_ISSET))
+ edit_update(current, NONE);
+#endif
+ } else if (editbot != filebot || edittop == fileage) {
+ current_y = 0;
+ current = editbot;
+
+ if (current->prev != NULL)
+ current = current->prev;
+ if (current->prev != NULL)
+ current = current->prev;
+ edit_update(current, TOP);
+ } else {
+ while (current != filebot) {
+ current = current->next;
+ current_y++;
+ }
+ edit_update(edittop, TOP);
+ }
+
+ update_cursor();
+ check_statblank();
+ return 1;
+}
+
+int do_up(void)
+{
+ wrap_reset();
+ if (current->prev != NULL) {
+ current_x = actual_x(current->prev, placewewant);
+ current = current->prev;
+ if (current_y > 0) {
+ update_line(current->next, 0);
+ /* It is necessary to change current first, so the mark
+ display will change! */
+ current_y--;
+ update_line(current, current_x);
+ } else
+ page_up();
+ check_statblank();
+ }
+ return 1;
+}
+
+/* Return value 1 means we moved down, 0 means we were already at the
+ * bottom. */
+int do_down(void)
+{
+ wrap_reset();
+ check_statblank();
+
+ if (current->next == NULL)
+ return 0;
+
+ current = current->next;
+ current_x = actual_x(current, placewewant);
+
+ /* Note current_y is zero-based. This test checks for the cursor's
+ * being on the last row of the edit window. */
+ if (current_y == editwinrows - 1) {
+#ifndef NANO_SMALL
+ if (ISSET(SMOOTHSCROLL)) {
+ /* In this case current_y does not change. The cursor
+ * remains at the bottom of the edit window. */
+ edittop = edittop->next;
+ editbot = editbot->next;
+ edit_refresh();
+ } else
+#endif
+ {
+ /* Set edittop so editbot->next (or else editbot) is
+ * centered, and set current_y = editwinrows / 2. */
+ edit_update(editbot->next != NULL ? editbot->next : editbot, CENTER);
+ center_cursor();
+ }
+ } else {
+ update_line(current->prev, 0);
+ update_line(current, current_x);
+ current_y++;
+ }
+ return 1;
+}
+
+int do_left(void)
+{
+ if (current_x > 0)
+ current_x--;
+ else if (current != fileage) {
+ do_up();
+ current_x = strlen(current->data);
+ }
+ placewewant = xplustabs();
+ update_line(current, current_x);
+ check_statblank();
+ return 1;
+}
+
+int do_right(void)
+{
+ assert(current_x <= strlen(current->data));
+
+ if (current->data[current_x] != '\0')
+ current_x++;
+ else if (current->next != NULL) {
+ do_down();
+ current_x = 0;
+ }
+ placewewant = xplustabs();
+ update_line(current, current_x);
+ check_statblank();
+ return 1;
+}
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * nano.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <ctype.h>
+#include <locale.h>
+#include <limits.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef HAVE_TERMIO_H
+#include <termio.h>
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#ifndef DISABLE_WRAPJUSTIFY
+static int fill = 0; /* Fill - where to wrap lines, basically */
+#endif
+
+static struct termios oldterm; /* The user's original term settings */
+static struct sigaction act; /* For all our fun signal handlers */
+
+static sigjmp_buf jmpbuf; /* Used to return to mainloop after SIGWINCH */
+
+/* What we do when we're all set to exit */
+RETSIGTYPE finish(int sigage)
+{
+ if (!ISSET(NO_HELP)) {
+ mvwaddstr(bottomwin, 1, 0, hblank);
+ mvwaddstr(bottomwin, 2, 0, hblank);
+ } else
+ mvwaddstr(bottomwin, 0, 0, hblank);
+
+ wrefresh(bottomwin);
+ endwin();
+
+ /* Restore the old term settings */
+ tcsetattr(0, TCSANOW, &oldterm);
+
+#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
+ if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
+ save_history();
+#endif
+
+#ifdef DEBUG
+ thanks_for_all_the_fish();
+#endif
+
+ exit(sigage);
+}
+
+/* Die (gracefully?) */
+void die(const char *msg, ...)
+{
+ va_list ap;
+
+ endwin();
+ curses_ended = TRUE;
+
+ /* Restore the old term settings */
+ tcsetattr(0, TCSANOW, &oldterm);
+
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+
+ /* save the currently loaded file if it's been modified */
+ if (ISSET(MODIFIED))
+ die_save_file(filename);
+
+#ifdef ENABLE_MULTIBUFFER
+ /* then save all of the other modified loaded files, if any */
+ if (open_files != NULL) {
+ openfilestruct *tmp;
+
+ tmp = open_files;
+
+ while (open_files->prev != NULL)
+ open_files = open_files->prev;
+
+ while (open_files->next != NULL) {
+
+ /* if we already saved the file above (i. e. if it was the
+ currently loaded file), don't save it again */
+ if (tmp != open_files) {
+ /* make sure open_files->fileage and fileage, and
+ open_files->filebot and filebot, are in sync; they
+ might not be if lines have been cut from the top or
+ bottom of the file */
+ fileage = open_files->fileage;
+ filebot = open_files->filebot;
+ /* save the file if it's been modified */
+ if (open_files->file_flags & MODIFIED)
+ die_save_file(open_files->filename);
+ }
+ open_files = open_files->next;
+ }
+ }
+#endif
+
+ exit(1); /* We have a problem: exit w/ errorlevel(1) */
+}
+
+void die_save_file(const char *die_filename)
+{
+ char *ret;
+ int i = -1;
+
+ /* If we can't save, we have REAL bad problems, but we might as well
+ TRY. */
+ if (die_filename[0] == '\0')
+ ret = get_next_filename("nano.save");
+ else {
+ char *buf = charalloc(strlen(die_filename) + 6);
+
+ strcpy(buf, die_filename);
+ strcat(buf, ".save");
+ ret = get_next_filename(buf);
+ free(buf);
+ }
+ if (ret[0] != '\0')
+ i = write_file(ret, 1, 0, 0);
+
+ if (i != -1)
+ fprintf(stderr, _("\nBuffer written to %s\n"), ret);
+ else
+ fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), ret);
+
+ free(ret);
+}
+
+/* Die with an error message that the screen was too small if, well, the
+ * screen is too small. */
+void die_too_small(void)
+{
+ die(_("Window size is too small for nano...\n"));
+}
+
+void print_view_warning(void)
+{
+ statusbar(_("Key illegal in VIEW mode"));
+}
+
+/* Initialize global variables - no better way for now. If
+ * save_cutbuffer is nonzero, don't set cutbuffer to NULL. */
+void global_init(int save_cutbuffer)
+{
+ current_x = 0;
+ current_y = 0;
+
+ editwinrows = LINES - 5 + no_help();
+ if (editwinrows < MIN_EDITOR_ROWS || COLS < MIN_EDITOR_COLS)
+ die_too_small();
+
+ fileage = NULL;
+ if (!save_cutbuffer)
+ cutbuffer = NULL;
+ current = NULL;
+ edittop = NULL;
+ editbot = NULL;
+ totlines = 0;
+ totsize = 0;
+ placewewant = 0;
+
+#ifndef DISABLE_WRAPJUSTIFY
+ fill = wrap_at;
+ if (fill <= 0)
+ fill += COLS;
+ if (fill < 0)
+ fill = 0;
+#endif
+
+ hblank = charalloc(COLS + 1);
+ memset(hblank, ' ', COLS);
+ hblank[COLS] = '\0';
+}
+
+void window_init(void)
+{
+ editwinrows = LINES - 5 + no_help();
+ if (editwinrows < MIN_EDITOR_ROWS)
+ die_too_small();
+
+ if (edit != NULL)
+ delwin(edit);
+ if (topwin != NULL)
+ delwin(topwin);
+ if (bottomwin != NULL)
+ delwin(bottomwin);
+
+ /* 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);
+
+ /* This is so the keypad still works after a Meta-X, for example. */
+ keypad(edit, TRUE);
+ keypad(bottomwin, TRUE);
+}
+
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+void mouse_init(void)
+{
+ if (ISSET(USE_MOUSE)) {
+ mousemask(BUTTON1_RELEASED, NULL);
+ mouseinterval(50);
+ } else
+ mousemask(0, NULL);
+}
+#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 "
+ "The previous search string will be shown in brackets after "
+ "the Search: prompt. Hitting Enter without entering any text "
+ "will perform the previous search.\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
+#ifndef DISABLE_SPELLER
+ 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");
+#endif
+#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 can be "
+ "entered either by using the Control (Ctrl) key or pressing the "
+ "Esc key twice. 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. Also, pressing Esc "
+ "twice and then typing a three-digit number from 000 to 255 "
+ "will enter the character with the corresponding ASCII code. "
+ "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) {
+ size_t endislen = strlen(_("enable/disable"));
+
+ for (t = toggles; t != NULL; t = t->next)
+ allocsize += 8 + strlen(t->desc) + endislen;
+ }
+#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"));
+ } else if (s->val == KEY_UP)
+ ptr += sprintf(ptr, "%.2s", _("Up"));
+#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 ? COLS - 24 : 0, s->help);
+ }
+
+#ifndef NANO_SMALL
+ /* And the toggles... */
+ if (currshortcut == main_list)
+ for (t = toggles; t != NULL; t = t->next) {
+ assert(t->desc != NULL);
+ ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", t->val - 32, t->desc,
+ _("enable/disable"));
+ }
+#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)
+{
+ filestruct *dst = (filestruct *)nmalloc(sizeof(filestruct));
+
+ assert(src != NULL);
+
+ dst->data = charalloc(strlen(src->data) + 1);
+ dst->next = src->next;
+ dst->prev = src->prev;
+ strcpy(dst->data, src->data);
+ dst->lineno = src->lineno;
+
+ 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)
+{
+ 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)
+{
+ if (fileptr != NULL) {
+ if (fileptr->data != NULL)
+ free(fileptr->data);
+ free(fileptr);
+ }
+}
+
+/* Okay, now let's duplicate a whole struct! */
+filestruct *copy_filestruct(const filestruct *src)
+{
+ filestruct *head; /* copy of src, top of the copied list */
+ filestruct *prev; /* temp that traverses the list */
+
+ assert(src != NULL);
+
+ prev = copy_node(src);
+ prev->prev = NULL;
+ head = prev;
+ src = src->next;
+ while (src != NULL) {
+ prev->next = copy_node(src);
+ prev->next->prev = prev;
+ prev = prev->next;
+
+ src = src->next;
+ }
+
+ prev->next = NULL;
+ return head;
+}
+
+/* Frees a filestruct. */
+void free_filestruct(filestruct *src)
+{
+ if (src != NULL) {
+ while (src->next != NULL) {
+ src = src->next;
+ delete_node(src->prev);
+#ifdef DEBUG
+ fprintf(stderr, "%s: free'd a node, YAY!\n", "delete_node()");
+#endif
+ }
+ delete_node(src);
+#ifdef DEBUG
+ fprintf(stderr, "%s: free'd last node.\n", "delete_node()");
+#endif
+ }
+}
+
+void renumber_all(void)
+{
+ filestruct *temp;
+ int i = 1;
+
+ assert(fileage == NULL || fileage != fileage->next);
+ for (temp = fileage; temp != NULL; temp = temp->next)
+ temp->lineno = i++;
+}
+
+void renumber(filestruct *fileptr)
+{
+ if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
+ renumber_all();
+ else {
+ int lineno = fileptr->prev->lineno;
+
+ assert(fileptr != fileptr->next);
+ for (; fileptr != NULL; fileptr = fileptr->next)
+ fileptr->lineno = ++lineno;
+ }
+}
+
+/* Print one usage string to the screen, removes lots of duplicate
+ * 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)
+{
+ printf(" %s\t", shortflag);
+ if (strlen(shortflag) < 8)
+ printf("\t");
+
+#ifdef HAVE_GETOPT_LONG
+ printf("%s\t", longflag);
+ if (strlen(longflag) < 8)
+ printf("\t\t");
+ else if (strlen(longflag) < 16)
+ printf("\t");
+#endif
+
+ printf("%s\n", desc);
+}
+
+void usage(void)
+{
+#ifdef HAVE_GETOPT_LONG
+ printf(_("Usage: nano [+LINE] [GNU long option] [option] [file]\n\n"));
+ printf(_("Option\t\tLong option\t\tMeaning\n"));
+#else
+ printf(_("Usage: nano [+LINE] [option] [file]\n\n"));
+ printf(_("Option\t\tMeaning\n"));
+#endif /* HAVE_GETOPT_LONG */
+
+ print1opt("-h, -?", "--help", _("Show this message"));
+ print1opt(_("+LINE"), "", _("Start at line number LINE"));
+#ifndef NANO_SMALL
+ print1opt("-B", "--backup", _("Backup existing files on save"));
+ print1opt("-D", "--dos", _("Write file in DOS format"));
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ print1opt("-F", "--multibuffer", _("Enable multiple file buffers"));
+#endif
+#ifdef ENABLE_NANORC
+#ifndef NANO_SMALL
+ print1opt("-H", "--historylog", _("Log & read search/replace string history"));
+#endif
+ print1opt("-I", "--ignorercfiles", _("Don't look at nanorc files"));
+#endif
+#ifndef NANO_SMALL
+ print1opt("-M", "--mac", _("Write file in Mac format"));
+ print1opt("-N", "--noconvert", _("Don't convert files from DOS/Mac format"));
+#endif
+#ifndef DISABLE_JUSTIFY
+ print1opt(_("-Q [str]"), _("--quotestr=[str]"), _("Quoting string, default \"> \""));
+#endif
+#ifdef HAVE_REGEX_H
+ print1opt("-R", "--regexp", _("Do regular expression searches"));
+#endif
+#ifndef NANO_SMALL
+ print1opt("-S", "--smooth", _("Smooth scrolling"));
+#endif
+ print1opt(_("-T [num]"), _("--tabsize=[num]"), _("Set width of a tab to num"));
+ print1opt("-V", "--version", _("Print version information and exit"));
+#ifdef ENABLE_COLOR
+ print1opt(_("-Y [str]"), _("--syntax [str]"), _("Syntax definition to use"));
+#endif
+ print1opt("-c", "--const", _("Constantly show cursor position"));
+#ifndef NANO_SMALL
+ print1opt("-d", "--rebinddelete", _("Fix Backspace if it acts like Delete"));
+ print1opt("-i", "--autoindent", _("Automatically indent new lines"));
+ print1opt("-k", "--cut", _("Let ^K cut from cursor to end of line"));
+#endif
+ print1opt("-l", "--nofollow", _("Don't follow symbolic links, overwrite"));
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ print1opt("-m", "--mouse", _("Enable mouse"));
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ print1opt(_("-o [dir]"), _("--operatingdir=[dir]"), _("Set operating directory"));
+#endif
+ print1opt("-p", "--preserve", _("Preserve XON (^Q) and XOFF (^S) keys"));
+#ifndef DISABLE_WRAPJUSTIFY
+ print1opt(_("-r [#cols]"), _("--fill=[#cols]"), _("Set fill cols to (wrap lines at) #cols"));
+#endif
+#ifndef DISABLE_SPELLER
+ print1opt(_("-s [prog]"), _("--speller=[prog]"), _("Enable alternate speller"));
+#endif
+ print1opt("-t", "--tempfile", _("Auto save on exit, don't prompt"));
+ print1opt("-v", "--view", _("View (read only) mode"));
+#ifndef DISABLE_WRAPPING
+ print1opt("-w", "--nowrap", _("Don't wrap long lines"));
+#endif
+ print1opt("-x", "--nohelp", _("Don't show help window"));
+ print1opt("-z", "--suspend", _("Enable suspend"));
+
+ /* this is a special case */
+ printf(" %s\t\t\t%s\n","-a, -b, -e, -f, -g, -j", _("(ignored, for Pico compatibility)"));
+
+ exit(0);
+}
+
+void version(void)
+{
+ printf(_(" GNU nano version %s (compiled %s, %s)\n"),
+ VERSION, __TIME__, __DATE__);
+ printf(_
+ (" Email: nano@nano-editor.org Web: http://www.nano-editor.org/"));
+ printf(_("\n Compiled options:"));
+
+#ifndef ENABLE_NLS
+ printf(" --disable-nls");
+#endif
+#ifdef DEBUG
+ printf(" --enable-debug");
+#endif
+#ifdef NANO_EXTRA
+ printf(" --enable-extra");
+#endif
+#ifdef NANO_SMALL
+ printf(" --enable-tiny");
+#else
+#ifdef DISABLE_BROWSER
+ printf(" --disable-browser");
+#endif
+#ifdef DISABLE_HELP
+ printf(" --disable-help");
+#endif
+#ifdef DISABLE_JUSTIFY
+ printf(" --disable-justify");
+#endif
+#if defined(DISABLE_MOUSE) || !defined(NCURSES_MOUSE_VERSION)
+ printf(" --disable-mouse");
+#endif
+#ifdef DISABLE_OPERATINGDIR
+ printf(" --disable-operatingdir");
+#endif
+#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 DISABLE_ROOTWRAP
+ printf(" --disable-wrapping-as-root");
+#endif
+#ifdef ENABLE_COLOR
+ printf(" --enable-color");
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ printf(" --enable-multibuffer");
+#endif
+#ifdef ENABLE_NANORC
+ printf(" --enable-nanorc");
+#endif
+#ifdef USE_SLANG
+ printf(" --with-slang");
+#endif
+ printf("\n");
+}
+
+/* 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();
+}
+
+int no_help(void)
+{
+ return ISSET(NO_HELP) ? 2 : 0;
+}
+
+#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP) || defined(NANO_SMALL)
+void nano_disabled_msg(void)
+{
+ statusbar(_("Sorry, support for this function has been disabled"));
+}
+#endif
+
+#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. */
+RETSIGTYPE cancel_fork(int signal)
+{
+ if (kill(pid, SIGKILL) == -1)
+ nperror("kill");
+}
+
+int open_pipe(const char *command)
+{
+ 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. */
+
+ if (pipe(fd) == -1) {
+ statusbar(_("Could not pipe"));
+ return 1;
+ }
+
+ /* Fork a child. */
+
+ 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);
+ }
+
+ /* Else continue as parent. */
+
+ close(fd[1]);
+
+ if (pid == -1) {
+ close(fd[0]);
+ statusbar(_("Could not fork"));
+ return 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. */
+
+ /* See if the platform supports disabling individual control
+ * characters. */
+#ifdef _POSIX_VDISABLE
+ if (cancel_sigs == 0 && tcgetattr(0, &term) == -1) {
+ cancel_sigs = 2;
+ nperror("tcgetattr");
+ }
+ if (cancel_sigs == 0) {
+ 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 */
+
+ f = fdopen(fd[0], "rb");
+ if (f == NULL)
+ 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 == 0 && 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 */
+
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+void do_mouse(void)
+{
+ MEVENT mevent;
+ int currslen;
+ const shortcut *s = currshortcut;
+
+ if (getmouse(&mevent) == ERR)
+ return;
+
+ /* If mouse not in edit or bottom window, return */
+ if (wenclose(edit, mevent.y, mevent.x) && currshortcut == main_list) {
+ int sameline;
+ /* Did they click on the line with the cursor? If they
+ clicked on the cursor, we set the mark. */
+ size_t xcur;
+ /* The character they clicked on. */
+
+ /* Subtract out size of topwin. Perhaps we need a constant
+ * somewhere? */
+ mevent.y -= 2;
+
+ sameline = mevent.y == current_y;
+
+ /* Move to where the click occurred. */
+ for(; current_y < mevent.y && current->next != NULL; current_y++)
+ current = current->next;
+ for(; current_y > mevent.y && current->prev != NULL; current_y--)
+ current = current->prev;
+
+ xcur = actual_x(current, get_page_start(xplustabs()) + mevent.x);
+
+ /* Selecting where the cursor is toggles the mark. As does
+ selecting beyond the line length with the cursor at the end of
+ the line. */
+ if (sameline && xcur == current_x) {
+ if (ISSET(VIEW_MODE)) {
+ print_view_warning();
+ return;
+ }
+ do_mark();
+ }
+
+ current_x = xcur;
+ placewewant = xplustabs();
+ edit_refresh();
+ } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
+ int i, k;
+
+ if (currshortcut == main_list)
+ currslen = MAIN_VISIBLE;
+ else
+ currslen = length_of_list(currshortcut);
+
+ if (currslen < 2)
+ k = COLS / 6;
+ else
+ k = COLS / ((currslen + (currslen %2)) / 2);
+
+ /* Determine what shortcut list was clicked */
+ mevent.y -= (editwinrows + 3);
+
+ if (mevent.y < 0) /* They clicked on the statusbar */
+ return;
+
+ /* Don't select stuff beyond list length */
+ if (mevent.x / k >= currslen)
+ return;
+
+ for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
+ s = s->next;
+
+ /* 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
+
+/* 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 = charealloc(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
+}
+
+int do_backspace(void)
+{
+ int refresh = 0;
+ if (current_x > 0) {
+ assert(current_x <= strlen(current->data));
+ /* Let's get dangerous */
+ memmove(¤t->data[current_x - 1], ¤t->data[current_x],
+ strlen(current->data) - current_x + 1);
+#ifdef DEBUG
+ fprintf(stderr, "current->data now = \"%s\"\n", current->data);
+#endif
+ align(¤t->data);
+#ifndef NANO_SMALL
+ if (current_x <= mark_beginx && mark_beginbuf == current)
+ mark_beginx--;
+#endif
+ do_left();
+#ifdef ENABLE_COLOR
+ refresh = 1;
+#endif
+ } else {
+ filestruct *previous;
+ const filestruct *tmp;
+
+ if (current == fileage)
+ return 0; /* Can't delete past top of file */
+
+ previous = current->prev;
+ current_x = strlen(previous->data);
+ placewewant = strlenpt(previous->data);
+#ifndef NANO_SMALL
+ if (current == mark_beginbuf) {
+ mark_beginx += current_x;
+ mark_beginbuf = previous;
+ }
+#endif
+ previous->data = charealloc(previous->data,
+ current_x + strlen(current->data) + 1);
+ strcpy(previous->data + current_x, current->data);
+
+ unlink_node(current);
+ delete_node(current);
+ tmp = current;
+ current = (previous->next ? previous->next : previous);
+ renumber(current);
+ /* We had to renumber before doing update_line. */
+ if (tmp == edittop)
+ page_up();
+
+ /* Ooops, sanity check */
+ if (tmp == filebot) {
+ filebot = current;
+ editbot = current;
+
+ /* Recreate the magic line if we're deleting it AND if the
+ line we're on now is NOT blank. if it is blank we
+ can just use IT for the magic line. This is how Pico
+ appears to do it, in any case. */
+ if (current->data[0] != '\0') {
+ new_magicline();
+ fix_editbot();
+ }
+ }
+
+ current = previous;
+ if (current_y > 0)
+ current_y--;
+ totlines--;
+#ifdef DEBUG
+ fprintf(stderr, "After, data = \"%s\"\n", current->data);
+#endif
+ refresh = 1;
+ }
+
+ totsize--;
+ set_modified();
+ if (refresh)
+ edit_refresh();
+ return 1;
+}
+
+int do_delete(void)
+{
+ int refresh = 0;
+
+ /* blbf -> blank line before filebot (see below) */
+ int blbf = 0;
+
+ if (current->next == filebot && current->data[0] == '\0')
+ blbf = 1;
+
+ placewewant = xplustabs();
+
+ if (current_x != strlen(current->data)) {
+ /* Let's get dangerous */
+ memmove(¤t->data[current_x], ¤t->data[current_x + 1],
+ strlen(current->data) - current_x);
+
+ align(¤t->data);
+#ifdef ENABLE_COLOR
+ refresh = 1;
+#endif
+ } else if (current->next != NULL && (current->next != filebot || blbf)) {
+ /* We can delete the line before filebot only if it is blank: it
+ becomes the new magic line then. */
+
+ filestruct *foo;
+
+ current->data = charealloc(current->data,
+ strlen(current->data) +
+ strlen(current->next->data) + 1);
+ strcat(current->data, current->next->data);
+
+ foo = current->next;
+ if (filebot == foo) {
+ filebot = current;
+ editbot = current;
+ }
+
+ unlink_node(foo);
+ delete_node(foo);
+ renumber(current);
+ totlines--;
+ refresh = 1;
+ } else
+ return 0;
+
+ totsize--;
+ set_modified();
+ update_line(current, current_x);
+ if (refresh)
+ edit_refresh();
+ return 1;
+}
+
+int do_tab(void)
+{
+ do_char('\t');
+ return 1;
+}
+
+/* Someone hits return *gasp!* */
+int do_enter(void)
+{
+ 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) {
+#ifndef NANO_SMALL
+ if (ISSET(SMOOTHSCROLL))
+ edit_update(current, NONE);
+ else
+#endif
+ edit_update(current, CENTER);
+ reset_cursor();
+ } else {
+ current_y++;
+ edit_refresh();
+ update_cursor();
+ }
+
+ totlines++;
+ set_modified();
+
+ placewewant = xplustabs();
+ return 1;
+}
+
+#ifndef NANO_SMALL
+int do_next_word(void)
+{
+ 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;
+
+ placewewant = xplustabs();
+
+ 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);
+ }
+ return 0;
+}
+
+/* The same thing for backwards. */
+int do_prev_word(void)
+{
+ filestruct *old = current;
+
+ assert(current != NULL && current->data != 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--;
+
+ if (current_x >= 0)
+ break;
+
+ if (current->prev != NULL)
+ current_x = strlen(current->prev->data);
+ }
+
+ if (current != NULL) {
+ while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
+ current_x--;
+ } else {
+ current = fileage;
+ current_x = 0;
+ }
+
+ 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);
+ }
+ 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);
+ }
+ }
+ }
+ update_line(current, current_x);
+ }
+ return 0;
+}
+#endif /* !NANO_SMALL */
+
+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;
+}
+
+void wrap_reset(void)
+{
+ UNSET(SAMELINEWRAP);
+}
+
+#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)
+{
+ 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 */
+
+/* There are three steps. First, we decide where to wrap. Then, we
+ * create the new wrap line. Finally, we clean up. */
+
+/* Step 1, finding where to wrap. We are going to add a new-line
+ * after a white-space character. 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 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. */
+
+#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 (word_back != i && wrap_line[1] != ' ' && wrap_line[1] != '\t')
+ wrap_loc = i;
+ }
+ if (wrap_loc < 0 || i == len)
+ return 0;
+
+/* 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). */
+
+ /* 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));
+
+ /* new_line_len will later be increased by the lengths of indentation
+ * and wrap_line. */
+ new_line_len = after_break_len;
+
+ /* 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);
+
+ /* +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 (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
+
+ /* Now we allocate the new line and copy into it. */
+ newline = charalloc(new_line_len + 1); /* +1 for \0 */
+ *newline = '\0';
+
+#ifndef NANO_SMALL
+ if (ISSET(AUTOINDENT)) {
+ strncpy(newline, indentation, indent_len);
+ newline[indent_len] = '\0';
+ }
+#endif
+ strcat(newline, after_break);
+ /* We end the old line after wrap_loc. Note this does not eat the
+ * space. */
+ null_at(&inptr->data, wrap_loc + 1);
+ totsize++;
+ if (wrapping) {
+ /* In this case, totsize increases by 1 since we add a space
+ * between after_break and wrap_line. If the line already ends
+ * in a tab or a space, we don't add a space and decrement
+ * totsize to account for that. */
+ if (!isspace((int) newline[strlen(newline) - 1]))
+ strcat(newline, " ");
+ else
+ totsize--;
+ 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 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 is NULL, then temp is the last line of the
+ * file, so we must set filebot. */
+ if (temp->next != NULL)
+ temp->next->prev = temp;
+ else
+ filebot = temp;
+ }
+
+/* Step 3, clean up. Here we reposition the cursor and mark, and do some
+ * other sundry things. */
+
+ /* later wraps of this line will be prepended to the next line. */
+ SET(SAMELINEWRAP);
+
+ /* 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
+ /* 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 */
+
+ /* Place the cursor. */
+ reset_cursor();
+
+ return 1;
+}
+#endif /* !DISABLE_WRAPPING */
+
+#ifndef DISABLE_SPELLER
+/* A word is misspelled in the file. Let the user replace it. We
+ * return False if the user cancels. */
+int do_int_spell_fix(const char *word)
+{
+ char *save_search;
+ char *save_replace;
+ filestruct *current_save = current;
+ int current_x_save = current_x;
+ filestruct *edittop_save = edittop;
+ /* Save where we are. */
+ int i = 0;
+ /* The return value. */
+ int reverse_search_set = ISSET(REVERSE_SEARCH);
+#ifndef NANO_SMALL
+ int case_sens_set = ISSET(CASE_SENSITIVE);
+ int mark_set = ISSET(MARK_ISSET);
+
+ SET(CASE_SENSITIVE);
+ /* Make sure the marking highlight is off during Spell Check */
+ UNSET(MARK_ISSET);
+#endif
+ /* Make sure Spell Check goes forward only */
+ UNSET(REVERSE_SEARCH);
+
+ /* 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 = -1;
+
+ search_last_line = FALSE;
+
+ /* We find the first whole-word occurrence of word. */
+ while (findnextstr(TRUE, TRUE, fileage, -1, word))
+ if (is_whole_word(current_x, current->data, word)) {
+ edit_refresh();
+
+ do_replace_highlight(TRUE, word);
+
+ /* allow replace word to be corrected */
+ i = statusq(0, spell_list, word,
+#ifndef NANO_SMALL
+ NULL,
+#endif
+ _("Edit a replacement"));
+
+ do_replace_highlight(FALSE, word);
+
+ if (i != -1 && strcmp(word, answer)) {
+ int j = 0;
+
+ search_last_line = FALSE;
+ current_x--;
+ do_replace_loop(word, current_save, ¤t_x_save, TRUE, &j);
+ }
+
+ break;
+ }
+
+ /* restore the search/replace strings */
+ free(last_search); last_search=save_search;
+ free(last_replace); last_replace=save_replace;
+
+ /* restore where we were */
+ current = current_save;
+ current_x = current_x_save;
+ edittop = edittop_save;
+
+ /* restore Search/Replace direction */
+ if (reverse_search_set)
+ SET(REVERSE_SEARCH);
+
+#ifndef NANO_SMALL
+ if (!case_sens_set)
+ UNSET(CASE_SENSITIVE);
+
+ /* restore marking highlight */
+ if (mark_set)
+ SET(MARK_ISSET);
+#endif
+
+ return i != -1;
+}
+
+/* Integrated spell checking using 'spell' program. Return value: NULL
+ * for normal termination, otherwise the error string. */
+char *do_int_speller(char *tempfile_name)
+{
+ char *read_buff, *read_buff_ptr, *read_buff_word;
+ size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
+ int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
+ pid_t pid_spell, pid_sort, pid_uniq;
+ int spell_status, sort_status, uniq_status;
+
+ /* Create all three pipes up front */
+
+ if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 || pipe(uniq_fd) == -1)
+ return _("Could not create pipe");
+
+ statusbar(_("Creating misspelled word list, please wait..."));
+ /* A new process to run spell in */
+
+ if ((pid_spell = fork()) == 0) {
+
+ /* Child continues, (i.e. future spell process) */
+
+ close(spell_fd[0]);
+
+ /* replace the standard in with the tempfile */
+ if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
+ goto close_pipes_and_exit;
+
+ if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
+ goto close_pipes_and_exit;
+
+ close(tempfile_fd);
+
+ /* send spell's standard out to the pipe */
+ if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
+ goto close_pipes_and_exit;
+
+ close(spell_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);
+ }
+
+ /* Parent continues here */
+ close(spell_fd[1]);
+
+ /* A new process to run sort in */
+ if ((pid_sort = fork()) == 0) {
+
+ /* Child continues, (i.e. future spell process) */
+ /* replace the standard in with output of the old pipe */
+ if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
+ goto close_pipes_and_exit;
+
+ close(spell_fd[0]);
+
+ /* send sort's standard out to the new pipe */
+ if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
+ goto close_pipes_and_exit;
+
+ close(sort_fd[1]);
+
+ /* Start sort program. Use -f to remove mixed case without having
+ to have ANOTHER pipe for tr. If this isn't portable, let me know. */
+ execlp("sort", "sort", "-f", NULL);
+
+ /* Should not be reached, if sort is found */
+ exit(1);
+ }
+
+ close(spell_fd[0]);
+ close(sort_fd[1]);
+
+ /* A new process to run uniq in */
+ if ((pid_uniq = fork()) == 0) {
+
+ /* Child continues, (i.e. future uniq process) */
+ /* replace the standard in with output of the old pipe */
+ if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
+ goto close_pipes_and_exit;
+
+ close(sort_fd[0]);
+
+ /* send uniq's standard out to the new pipe */
+ if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
+ goto close_pipes_and_exit;
+
+ close(uniq_fd[1]);
+
+ /* Start uniq program, we are using PATH */
+ execlp("uniq", "uniq", NULL);
+
+ /* Should not be reached, if uniq is found */
+ exit(1);
+ }
+
+ close(sort_fd[0]);
+ close(uniq_fd[1]);
+
+ /* Child process was not forked successfully */
+ if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
+ close(uniq_fd[0]);
+ return _("Could not fork");
+ }
+
+ /* Get system pipe buffer size */
+ if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
+ close(uniq_fd[0]);
+ return _("Could not get size of pipe buffer");
+ }
+
+ /* 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(uniq_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
+ read_buff_read += bytesread;
+ read_buff_size += pipe_buff_size;
+ read_buff = read_buff_ptr = charealloc(read_buff, read_buff_size);
+ read_buff_ptr += read_buff_read;
+
+ }
+
+ *read_buff_ptr = (char)NULL;
+ close(uniq_fd[0]);
+
+ /* Process the spelling errors */
+ read_buff_word = read_buff_ptr = read_buff;
+
+ while (*read_buff_ptr != '\0') {
+
+ 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++;
+ }
+
+ /* 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();
+ edit_refresh();
+
+ /* Process end of spell process */
+
+ waitpid(pid_spell, &spell_status, 0);
+ waitpid(pid_sort, &sort_status, 0);
+ waitpid(pid_uniq, &uniq_status, 0);
+
+ if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
+ return _("Error invoking \"spell\"");
+
+ if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status))
+ return _("Error invoking \"sort -f\"");
+
+ if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
+ return _("Error invoking \"uniq\"");
+
+ /* Otherwise... */
+ return NULL;
+
+close_pipes_and_exit:
+
+ /* Don't leak any handles */
+ close(tempfile_fd);
+ close(spell_fd[0]);
+ close(spell_fd[1]);
+ close(sort_fd[0]);
+ close(sort_fd[1]);
+ close(uniq_fd[0]);
+ close(uniq_fd[1]);
+ exit(1);
+}
+
+/* External spell checking. Return value: NULL for normal termination,
+ * otherwise the error string. */
+char *do_alt_speller(char *tempfile_name)
+{
+ 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
+
+ endwin();
+
+ /* Set up an argument list to pass the execvp function */
+ if (spellargs == NULL) {
+ spellargs = (char **)nmalloc(arglen * sizeof(char *));
+
+ spellargs[0] = strtok(alt_speller, " ");
+ while ((ptr = strtok(NULL, " ")) != NULL) {
+ arglen++;
+ spellargs = (char **)nrealloc(spellargs, arglen * sizeof(char *));
+ spellargs[arglen - 3] = ptr;
+ }
+ spellargs[arglen - 1] = NULL;
+ }
+ spellargs[arglen - 2] = tempfile_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);
+
+ /* Should not be reached, if alternate speller is found!!! */
+ exit(1);
+ }
+
+ /* Could not fork?? */
+ if (pid_spell < 0)
+ return _("Could not fork");
+
+ /* Wait for alternate speller to complete */
+
+ wait(&alt_spell_status);
+ if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0) {
+ char *altspell_error = NULL;
+ char *invoke_error = _("Could not invoke \"%s\"");
+ int msglen = strlen(invoke_error) + strlen(alt_speller) + 2;
+
+ altspell_error = charalloc(msglen);
+ snprintf(altspell_error, msglen, invoke_error, alt_speller);
+ return altspell_error;
+ }
+
+ 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
+
+ /* 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 NULL;
+}
+#endif
+
+int do_spell(void)
+{
+#ifdef DISABLE_SPELLER
+ nano_disabled_msg();
+ return TRUE;
+#else
+ char *temp, *spell_msg;
+
+ if ((temp = safe_tempnam(0, "nano.")) == NULL) {
+ statusbar(_("Could not create a temporary filename: %s"),
+ strerror(errno));
+ return 0;
+ }
+
+ if (write_file(temp, 1, 0, 0) == -1) {
+ statusbar(_("Spell checking failed: unable to write temp file!"));
+ free(temp);
+ return 0;
+ }
+
+#ifdef ENABLE_MULTIBUFFER
+ /* update the current open_files entry before spell-checking, in case
+ any problems occur */
+ add_open_file(1);
+#endif
+
+ if (alt_speller != NULL)
+ spell_msg = do_alt_speller(temp);
+ else
+ spell_msg = do_int_speller(temp);
+ remove(temp);
+ free(temp);
+
+ if (spell_msg != NULL) {
+ statusbar(_("Spell checking failed: %s"), spell_msg);
+ return 0;
+ }
+
+ statusbar(_("Finished checking spelling"));
+ return 1;
+#endif
+}
+
+#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 len = 0;
+
+ assert(line != NULL);
+ while (*line == ' ' || *line == '\t') {
+ line++;
+ len++;
+ }
+ return len;
+}
+#endif /* !DISABLE_WRAPPING && !NANO_SMALL || !DISABLE_JUSTIFY */
+
+#ifndef DISABLE_JUSTIFY
+/* justify_format() replaces Tab by Space and multiple spaces by 1 (except
+ * it maintains 2 after a . ! or ?). Note the terminating \0
+ * counts as a space.
+ *
+ * If !changes_allowed and justify_format() needs to make a change, it
+ * returns 1, otherwise returns 0.
+ *
+ * If changes_allowed, justify_format() might make line->data
+ * shorter, and change the actual pointer with null_at().
+ *
+ * justify_format() will not look at the first skip characters of line.
+ * skip should be at most strlen(line->data). The skip+1st character must
+ * not be whitespace. */
+int justify_format(int changes_allowed, filestruct *line, size_t skip)
+{
+ const char *punct = ".?!";
+ const char *brackets = "'\")}]>";
+ char *back, *front;
+
+ /* These four asserts are assumptions about the input data. */
+ assert(line != NULL);
+ assert(line->data != NULL);
+ assert(skip < strlen(line->data));
+ assert(line->data[skip] != ' ' && line->data[skip] != '\t');
+
+ back = line->data + skip;
+ front = back;
+ for (front = back; ; front++) {
+ int remove_space = 0;
+ /* Do we want to remove this space? */
+
+ if (*front == '\t') {
+ if (!changes_allowed)
+ return 1;
+ *front = ' ';
+ }
+ /* these tests are safe since line->data + skip is not a space */
+ if ((*front == '\0' || *front == ' ') && *(front - 1) == ' ') {
+ const char *bob = front - 2;
+
+ remove_space = 1;
+ for (bob = back - 2; bob >= line->data + skip; bob--) {
+ if (strchr(punct, *bob) != NULL) {
+ remove_space = 0;
+ break;
+ }
+ if (strchr(brackets, *bob) == NULL)
+ break;
+ }
+ }
+
+ if (remove_space) {
+ /* Now *front is a space we want to remove. We do that by
+ * simply failing to assign it to *back. */
+ if (!changes_allowed)
+ return 1;
+#ifndef NANO_SMALL
+ if (mark_beginbuf == line && back - line->data < mark_beginx)
+ mark_beginx--;
+#endif
+ if (*front == '\0')
+ *(back - 1) = '\0';
+ } else {
+ *back = *front;
+ back++;
+ }
+ if (*front == '\0')
+ break;
+ }
+
+ back--;
+ assert(*back == '\0' && *front == '\0');
+
+ /* This assert merely documents a fact about the loop above. */
+ assert(changes_allowed != 0 || back == front);
+
+ /* Now back is the new end of line->data. */
+ if (back != front) {
+ totsize -= front - back;
+ null_at(&line->data, back - line->data);
+#ifndef NANO_SMALL
+ if (mark_beginbuf == line && back - line->data < mark_beginx)
+ mark_beginx = back - line->data;
+#endif
+ }
+ return 0;
+}
+
+/* The "quote part" of a line is the largest initial substring matching
+ * the quote string. This function returns the length of the quote part
+ * of the given line.
+ *
+ * Note that if !HAVE_REGEX_H then we match concatenated copies of
+ * quotestr. */
+#ifdef HAVE_REGEX_H
+size_t quote_length(const char *line, const regex_t *qreg)
+{
+ regmatch_t matches;
+ int rc = regexec(qreg, line, 1, &matches, 0);
+
+ if (rc == REG_NOMATCH || matches.rm_so == (regoff_t) -1)
+ return 0;
+ /* matches.rm_so should be 0, since the quote string should start with
+ * the caret ^. */
+ return matches.rm_eo;
+}
+#else /* !HAVE_REGEX_H */
+size_t quote_length(const char *line)
+{
+ size_t qdepth = 0;
+ size_t qlen = strlen(quotestr);
+
+ /* Compute quote depth level */
+ while (!strcmp(line + qdepth, quotestr))
+ qdepth += qlen;
+ return qdepth;
+}
+#endif /* !HAVE_REGEX_H */
+
+/* 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))
+{
+ /* Here is the assumption about a_quote: */
+ assert(a_quote == quote_length(IFREG(a_line, qreg)));
+ return a_quote == quote_length(IFREG(b_line, qreg)) &&
+ !strncmp(a_line, b_line, a_quote);
+}
+
+/* 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)
+{
+ assert(a_indent == indent_length(a_line));
+ assert(b_indent == indent_length(b_line));
+
+ return b_indent <= a_indent && !strncmp(a_line, b_line, b_indent);
+}
+
+/* Put the next par_len lines, starting with first_line, in the cut
+ * buffer. We assume there are enough lines after first_line. We leave
+ * 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)
+{
+ /* 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
+ * get the actual same paragraph back, not a copy. */
+ filestruct *alice = first_line;
+
+ set_modified();
+ cutbuffer = NULL;
+ for(; par_len > 0; par_len--) {
+ filestruct *bob = copy_node(alice);
+
+ if (alice == first_line)
+ first_line = bob;
+ if (alice == current)
+ current = bob;
+ if (alice == edittop)
+ edittop = bob;
+#ifndef NANO_SMALL
+ if (alice == mark_beginbuf)
+ mark_beginbuf = bob;
+#endif
+ justify_format(1, bob,
+ quote_len + indent_length(bob->data + quote_len));
+
+ assert(alice != NULL && bob != NULL);
+ add_to_cutbuffer(alice);
+ splice_node(bob->prev, bob, bob->next);
+ alice = bob->next;
+ }
+ return first_line;
+}
+
+/* Is it possible to break line at or before goal? */
+int breakable(const char *line, int goal)
+{
+ for(; *line != '\0' && goal >= 0; line++) {
+ if (*line == ' ' || *line == '\t')
+ return TRUE;
+
+ if (is_cntrl_char(*line) != 0)
+ goal -= 2;
+ else
+ goal -= 1;
+ }
+ /* If goal is not negative, the whole line (one word) was short
+ * enough. */
+ return goal >= 0;
+}
+
+/* We are trying to break a chunk off line. We find the last space such
+ * that the display length to there is at most goal + 1. If there is
+ * no such space, and force is not 0, then we find the first space.
+ * Anyway, we then take the last space in that group of spaces. The
+ * terminating '\0' counts as a space. */
+int break_line(const char *line, int goal, int force)
+{
+ /* Note that we use int instead of size_t, since goal is at most COLS,
+ * the screen width, which will always be reasonably small. */
+ int space_loc = -1;
+ /* Current tentative return value. Index of the last space we
+ * found with short enough display width. */
+ int cur_loc = 0;
+ /* Current index in line */
+
+ assert(line != NULL);
+ for(; *line != '\0' && goal >= 0; line++, cur_loc++) {
+ if (*line == ' ')
+ space_loc = cur_loc;
+ assert(*line != '\t');
+
+ if (is_cntrl_char(*line))
+ goal -= 2;
+ else
+ goal--;
+ }
+ if (goal >= 0)
+ /* In fact, the whole line displays shorter than goal. */
+ return cur_loc;
+ if (space_loc == -1) {
+ /* No space found short enough. */
+ if (force)
+ for(; *line != '\0'; line++, cur_loc++)
+ if (*line == ' ' && *(line + 1) != ' ' && *(line + 1) != '\0')
+ return cur_loc;
+ return -1;
+ }
+ /* Perhaps the character after space_loc is a space. But because
+ * of justify_format(), there can be only two adjacent. */
+ if (*(line - cur_loc + space_loc + 1) == ' ' ||
+ *(line - cur_loc + space_loc + 1) == '\0')
+ space_loc++;
+ return space_loc;
+}
+
+/* This function performs operations on paragraphs: justify, go to
+ * beginning, and go to end. */
+int do_para_operation(int operation)
+{
+/* operation == 0 means we're justifying the paragraph, operation == 1
+ * means we're moving to the beginning line of the paragraph, and
+ * operation == 2 means we're moving to the ending line of the
+ * paragraph.
+ *
+ * To explain the justifying algorithm, I first need to define some
+ * phrases about paragraphs and quotation:
+ * A line of text consists of a "quote part", followed by an
+ * "indentation part", followed by text. The functions quote_length()
+ * and indent_length() calculate these parts.
+ *
+ * A line is "part of a paragraph" if it has a part not in the quote
+ * part or the indentation.
+ *
+ * A line is "the beginning of a paragraph" if it is part of a paragraph
+ * and
+ * 1) it is the top line of the file, or
+ * 2) the line above it is not part of a paragraph, or
+ * 3) the line above it does not have precisely the same quote
+ * part, or
+ * 4) the indentation of this line is not an initial substring of the
+ * indentation of the previous line, or
+ * 5) this line has no quote part and some indentation, and
+ * AUTOINDENT is not set.
+ * The reason for number 5) is that if AUTOINDENT is not set, then an
+ * indented line is expected to start a paragraph, like in books. Thus,
+ * nano can justify an indented paragraph only if AUTOINDENT is turned
+ * on.
+ *
+ * A contiguous set of lines is a "paragraph" if each line is part of
+ * a paragraph and only the first line is the beginning of a paragraph.
+ */
+
+ size_t quote_len;
+ /* Length of the initial quotation of the paragraph we justify. */
+ size_t par_len;
+ /* Number of lines in that paragraph. */
+ filestruct *first_mod_line = NULL;
+ /* Will be the first line of the resulting justified paragraph
+ * that differs from the original. For restoring after uncut. */
+ filestruct *last_par_line = current;
+ /* Will be the last line of the result, also for uncut. */
+ filestruct *cutbuffer_save = cutbuffer;
+ /* When the paragraph gets modified, all lines from the changed
+ * one down are stored in the cut buffer. We back up the original
+ * to restore it later. */
+
+ /* We save these global variables to be restored if the user
+ * unjustifies. Note we don't need to save totlines. */
+ int current_x_save = current_x;
+ int current_y_save = current_y;
+ filestruct *current_save = current;
+ int flags_save = flags;
+ long totsize_save = totsize;
+ filestruct *edittop_save = edittop;
+ filestruct *editbot_save = editbot;
+#ifndef NANO_SMALL
+ filestruct *mark_beginbuf_save = mark_beginbuf;
+ int mark_beginx_save = mark_beginx;
+#endif
+
+ size_t indent_len; /* generic indentation length */
+ filestruct *line; /* generic line of text */
+ size_t i; /* generic loop variable */
+
+ static int no_restart = 0;
+ /* whether we're blocking restarting when searching for the
+ * beginning line of the paragraph */
+
+#ifdef HAVE_REGEX_H
+ regex_t qreg; /* qreg is the compiled quotation regexp.
+ * We no longer care about quotestr. */
+ int rc = regcomp(&qreg, quotestr, REG_EXTENDED);
+
+ if (rc) {
+ size_t size = regerror(rc, &qreg, NULL, 0);
+ char *strerror = charalloc(size);
+
+ regerror(rc, &qreg, strerror, size);
+ statusbar(_("Bad quote string %s: %s"), quotestr, strerror);
+ free(strerror);
+ return -1;
+ }
+#endif
+
+ /* Here is an assumption that is always true anyway. */
+ assert(current != NULL);
+
+ current_x = 0;
+
+ restart_bps:
+/* Here we find the first line of the paragraph to justify. If the
+ * current line is in a paragraph, then we move back to the first line.
+ * Otherwise we move down to the first line that is in a paragraph. */
+ quote_len = quote_length(IFREG(current->data, &qreg));
+ indent_len = indent_length(current->data + quote_len);
+
+ if (current->data[quote_len + indent_len] != '\0') {
+ /* This line is part of a paragraph. So we must search back to
+ * the first line of this paragraph. First we check items 1) and
+ * 3) above. */
+ while (current->prev != NULL && quotes_match(current->data,
+ quote_len, IFREG(current->prev->data, &qreg))) {
+ size_t temp_id_len =
+ indent_length(current->prev->data + quote_len);
+ /* The indentation length of the previous line. */
+
+ /* Is this line the beginning of a paragraph, according to
+ items 2), 5), or 4) above? If so, stop. */
+ if (current->prev->data[quote_len + temp_id_len] == '\0' ||
+ (quote_len == 0 && indent_len > 0
+#ifndef NANO_SMALL
+ && !ISSET(AUTOINDENT)
+#endif
+ ) ||
+ !indents_match(current->prev->data + quote_len,
+ temp_id_len, current->data + quote_len, indent_len))
+ break;
+ indent_len = temp_id_len;
+ current = current->prev;
+ current_y--;
+ }
+ } else if (operation == 1) {
+ /* This line is not part of a paragraph. Move up until we get
+ * to a non "blank" line, and then move down once. */
+ do {
+ /* There is no previous paragraph, so nothing to move to. */
+ if (current->prev == NULL) {
+ placewewant = 0;
+ if (current_y < 0)
+ edit_update(current, CENTER);
+ else
+ edit_refresh();
+ return 0;
+ }
+ current = current->prev;
+ current_y--;
+ quote_len = quote_length(IFREG(current->data, &qreg));
+ indent_len = indent_length(current->data + quote_len);
+ } while (current->data[quote_len + indent_len] == '\0');
+ current = current->next;
+ } else {
+ /* This line is not part of a paragraph. Move down until we get
+ * to a non "blank" line. */
+ do {
+ /* There is no next paragraph, so nothing to justify. */
+ if (current->next == NULL) {
+ placewewant = 0;
+ edit_refresh();
+#ifdef HAVE_REGEX_H
+ regfree(&qreg);
+#endif
+ return 0;
+ }
+ current = current->next;
+ current_y++;
+ quote_len = quote_length(IFREG(current->data, &qreg));
+ indent_len = indent_length(current->data + quote_len);
+ } while (current->data[quote_len + indent_len] == '\0');
+ }
+/* Now current is the first line of the paragraph, and quote_len
+ * is the quotation length of that line. */
+
+/* Next step, compute par_len, the number of lines in this paragraph. */
+ line = current;
+ par_len = 1;
+ indent_len = indent_length(line->data + quote_len);
+
+ while (line->next != NULL && quotes_match(current->data, quote_len,
+ IFREG(line->next->data, &qreg))) {
+ size_t temp_id_len = indent_length(line->next->data + quote_len);
+
+ if (!indents_match(line->data + quote_len, indent_len,
+ line->next->data + quote_len, temp_id_len) ||
+ line->next->data[quote_len + temp_id_len] == '\0' ||
+ (quote_len == 0 && temp_id_len > 0
+#ifndef NANO_SMALL
+ && !ISSET(AUTOINDENT)
+#endif
+ ))
+ break;
+ indent_len = temp_id_len;
+ line = line->next;
+ par_len++;
+ }
+#ifdef HAVE_REGEX_H
+ /* We no longer need to check quotation, unless we're searching for
+ * the beginning of the paragraph. */
+ if (operation != 1)
+ regfree(&qreg);
+#endif
+/* Now par_len is the number of lines in this paragraph. Should never
+ * call quotes_match() or quote_length() again. */
+
+ /* If we're searching for the beginning of the paragraph, skip the
+ * justification. If we're searching for the end of the paragraph,
+ * move down the number of lines in the paragraph and skip the
+ * justification. */
+ if (operation == 1)
+ goto skip_justify;
+ else if (operation == 2) {
+ while (par_len > 0) {
+ current = current->next;
+ current_y++;
+ par_len--;
+ }
+ goto skip_justify;
+ }
+
+/* Next step, we loop through the lines of this paragraph, justifying
+ * each one individually. */
+ SET(JUSTIFY_MODE);
+ for(; par_len > 0; current_y++, par_len--) {
+ size_t line_len;
+ size_t display_len;
+ /* The width of current in screen columns. */
+ int break_pos;
+ /* Where we will break the line. */
+
+ indent_len = indent_length(current->data + quote_len) +
+ quote_len;
+ /* justify_format() removes excess spaces from the line, and
+ * changes tabs to spaces. The first argument, 0, means don't
+ * change the line, just say whether there are changes to be
+ * made. If there are, we do backup_lines(), which copies the
+ * original paragraph to the cutbuffer for unjustification, and
+ * then calls justify_format() on the remaining lines. */
+ if (first_mod_line == NULL && justify_format(0, current, indent_len))
+ first_mod_line = backup_lines(current, par_len, quote_len);
+
+ line_len = strlen(current->data);
+ display_len = strlenpt(current->data);
+
+ if (display_len > fill) {
+ /* The line is too long. Try to wrap it to the next. */
+ break_pos = break_line(current->data + indent_len,
+ fill - strnlenpt(current->data, indent_len),
+ 1);
+ if (break_pos == -1 || break_pos + indent_len == line_len)
+ /* We can't break the line, or don't need to, so just go
+ * on to the next. */
+ goto continue_loc;
+ break_pos += indent_len;
+ assert(break_pos < line_len);
+ /* If we haven't backed up the paragraph, do it now. */
+ if (first_mod_line == NULL)
+ first_mod_line = backup_lines(current, par_len, quote_len);
+ if (par_len == 1) {
+ /* There is no next line in this paragraph. We make a new
+ * line and copy text after break_pos into it. */
+ splice_node(current, make_new_node(current),
+ current->next);
+ /* In a non-quoted paragraph, we copy the indent only if
+ AUTOINDENT is turned on. */
+ if (quote_len == 0)
+#ifndef NANO_SMALL
+ if (!ISSET(AUTOINDENT))
+#endif
+ indent_len = 0;
+ current->next->data = charalloc(indent_len + line_len -
+ break_pos);
+ strncpy(current->next->data, current->data,
+ indent_len);
+ strcpy(current->next->data + indent_len,
+ current->data + break_pos + 1);
+ assert(strlen(current->next->data) ==
+ indent_len + line_len - break_pos - 1);
+ totlines++;
+ totsize += indent_len;
+ par_len++;
+ } else {
+ size_t next_line_len = strlen(current->next->data);
+
+ indent_len = quote_len +
+ indent_length(current->next->data + quote_len);
+ current->next->data = charealloc(current->next->data,
+ next_line_len + line_len - break_pos + 1);
+
+ memmove(current->next->data + indent_len + line_len - break_pos,
+ current->next->data + indent_len,
+ next_line_len - indent_len + 1);
+ strcpy(current->next->data + indent_len,
+ current->data + break_pos + 1);
+ current->next->data[indent_len + line_len - break_pos - 1]
+ = ' ';
+#ifndef NANO_SMALL
+ if (mark_beginbuf == current->next) {
+ if (mark_beginx < indent_len)
+ mark_beginx = indent_len;
+ mark_beginx += line_len - break_pos;
+ }
+#endif
+ }
+#ifndef NANO_SMALL
+ if (mark_beginbuf == current && mark_beginx > break_pos) {
+ mark_beginbuf = current->next;
+ mark_beginx -= break_pos + 1 - indent_len;
+ }
+#endif
+ null_at(¤t->data, break_pos);
+ current = current->next;
+ } else if (display_len < fill && par_len > 1) {
+ size_t next_line_len;
+
+ indent_len = quote_len +
+ indent_length(current->next->data + quote_len);
+ /* If we can't pull a word from the next line up to this one,
+ * just go on. */
+ if (!breakable(current->next->data + indent_len,
+ fill - display_len - 1))
+ goto continue_loc;
+
+ /* If we haven't backed up the paragraph, do it now. */
+ if (first_mod_line == NULL)
+ first_mod_line = backup_lines(current, par_len, quote_len);
+
+ break_pos = break_line(current->next->data + indent_len,
+ fill - display_len - 1, FALSE);
+ assert(break_pos != -1);
+
+ current->data = charealloc(current->data,
+ line_len + break_pos + 2);
+ current->data[line_len] = ' ';
+ strncpy(current->data + line_len + 1,
+ current->next->data + indent_len, break_pos);
+ current->data[line_len + break_pos + 1] = '\0';
+#ifndef NANO_SMALL
+ if (mark_beginbuf == current->next) {
+ if (mark_beginx < indent_len + break_pos) {
+ mark_beginbuf = current;
+ if (mark_beginx <= indent_len)
+ mark_beginx = line_len + 1;
+ else
+ mark_beginx = line_len + 1 + mark_beginx - indent_len;
+ } else
+ mark_beginx -= break_pos + 1;
+ }
+#endif
+ next_line_len = strlen(current->next->data);
+ if (indent_len + break_pos == next_line_len) {
+ line = current->next;
+
+ /* Don't destroy edittop! */
+ if (line == edittop)
+ edittop = current;
+
+ unlink_node(line);
+ delete_node(line);
+ totlines--;
+ totsize -= indent_len;
+ current_y--;
+ } else {
+ memmove(current->next->data + indent_len,
+ current->next->data + indent_len + break_pos + 1,
+ next_line_len - break_pos - indent_len);
+ null_at(¤t->next->data,
+ next_line_len - break_pos);
+ current = current->next;
+ }
+ } else
+ continue_loc:
+ current = current->next;
+ }
+ UNSET(JUSTIFY_MODE);
+
+/* We are now done justifying the paragraph. There are cleanup things
+ * to do, and we check for unjustify. */
+
+ /* totlines, totsize, and current_y have been maintained above. We
+ * now set last_par_line to the new end of the paragraph, update
+ * fileage, set current_x. Also, edit_refresh() needs the line
+ * numbers to be right, so we renumber(). */
+ last_par_line = current->prev;
+ if (first_mod_line != NULL) {
+ if (first_mod_line->prev == NULL)
+ fileage = first_mod_line;
+ renumber(first_mod_line);
+ }
+
+ skip_justify:
+ if (operation == 1) {
+ /* We're on the same line we started on. Search for the first
+ * non-"blank" line before the line we're on (if there is one),
+ * continually restart that search from the current position
+ * until we find a line that's part of a paragraph, and then
+ * search once more from there, so that we end up on the first
+ * line of that paragraph. In the process, skip over lines
+ * consisting only of spacing characters, as searching for the
+ * end of the paragraph does. Then update the screen. */
+ if (current != fileage && current == current_save && !no_restart) {
+ while (current->prev != NULL) {
+ int j, j_space = 0;
+ current = current->prev;
+ current_y--;
+ for (j = 0; j < strlen(current->data); j++) {
+ if (isspace(current->data[j]))
+ j_space++;
+ else {
+ j = -1;
+ break;
+ }
+ }
+ if (j != j_space && strlen(current->data) >=
+ (quote_len + indent_len) &&
+ current->data[quote_len + indent_len] != '\0') {
+ no_restart = 1;
+ break;
+ }
+ }
+ goto restart_bps;
+ } else
+ no_restart = 0;
+#ifdef HAVE_REGEX_H
+ /* We no longer need to check quotation, if we were
+ * searching for the beginning of the paragraph. */
+ regfree(&qreg);
+#endif
+ if (current_y < 0)
+ edit_update(current, CENTER);
+ else
+ edit_refresh();
+ return 0;
+ } else if (operation == 2) {
+ /* We've already moved to the end of the paragraph. Update the
+ * screen. */
+ if (current_y > editwinrows - 1)
+ edit_update(current, CENTER);
+ else
+ edit_refresh();
+ return 0;
+ }
+
+ if (current_y > editwinrows - 1)
+ edit_update(current, CENTER);
+ else
+ edit_refresh();
+
+ statusbar(_("Can now UnJustify!"));
+ /* Change the shortcut list to display the unjustify code */
+ shortcut_init(1);
+ display_main_list();
+ reset_cursor();
+
+ /* Now get a keystroke and see if it's unjustify; if not, unget the
+ * keystroke and return. */
+
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ /* If it was a mouse click, parse it with do_mouse() and it might
+ * become the unjustify key. Else give it back to the input stream. */
+ if ((i = wgetch(edit)) == KEY_MOUSE)
+ do_mouse();
+ else
+ ungetch(i);
+#endif
+
+ if ((i = wgetch(edit)) != NANO_UNJUSTIFY_KEY) {
+ ungetch(i);
+ /* Did we back up anything at all? */
+ if (cutbuffer != cutbuffer_save)
+ free_filestruct(cutbuffer);
+ placewewant = 0;
+ } else {
+ /* Else restore the justify we just did (ungrateful user!) */
+ current = current_save;
+ current_x = current_x_save;
+ current_y = current_y_save;
+ edittop = edittop_save;
+ editbot = editbot_save;
+ if (first_mod_line != NULL) {
+ filestruct *cutbottom = get_cutbottom();
+
+ /* Splice the cutbuffer back into the file. */
+ cutbottom->next = last_par_line->next;
+ cutbottom->next->prev = cutbottom;
+ /* The line numbers after the end of the paragraph have
+ * been changed, so we change them back. */
+ renumber(cutbottom->next);
+ if (first_mod_line->prev != NULL) {
+ cutbuffer->prev = first_mod_line->prev;
+ cutbuffer->prev->next = cutbuffer;
+ } else
+ fileage = cutbuffer;
+ cutbuffer = NULL;
+
+ last_par_line->next = NULL;
+ free_filestruct(first_mod_line);
+
+ /* Restore global variables from before justify */
+ totsize = totsize_save;
+ totlines = filebot->lineno;
+#ifndef NANO_SMALL
+ mark_beginbuf = mark_beginbuf_save;
+ mark_beginx = mark_beginx_save;
+#endif
+ flags = flags_save;
+ if (!ISSET(MODIFIED)) {
+ titlebar(NULL);
+ wrefresh(topwin);
+ }
+ }
+ edit_refresh();
+ }
+ cutbuffer = cutbuffer_save;
+ blank_statusbar_refresh();
+ /* display shortcut list with UnCut */
+ shortcut_init(0);
+ display_main_list();
+
+ return 0;
+}
+#endif /* !DISABLE_JUSTIFY */
+
+int do_justify(void)
+{
+#ifdef DISABLE_JUSTIFY
+ nano_disabled_msg();
+ return 1;
+#else
+ return do_para_operation(0);
+#endif
+}
+
+#ifndef DISABLE_JUSTIFY
+int do_para_begin(void)
+{
+ return do_para_operation(1);
+}
+
+int do_para_end(void)
+{
+ return do_para_operation(2);
+}
+#endif
+
+int do_exit(void)
+{
+ int i;
+
+ if (!ISSET(MODIFIED)) {
+
+#ifdef ENABLE_MULTIBUFFER
+ if (!close_open_file()) {
+ display_main_list();
+ return 1;
+ }
+ else
+#endif
+ finish(0);
+ }
+
+ 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
+
+ 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 and SIGTERM cuz we want to write the file out. */
+ act.sa_handler = handle_hupterm;
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+#ifndef NANO_SMALL
+ act.sa_handler = handle_sigwinch;
+ sigaction(SIGWINCH, &act, NULL);
+#endif
+
+#ifdef _POSIX_VDISABLE
+ tcgetattr(0, &term);
+
+ if (!ISSET(PRESERVE)) {
+ /* Ignore ^S and ^Q, much to Chris' chagrin */
+ term.c_cc[VSTOP] = _POSIX_VDISABLE;
+ term.c_cc[VSTART] = _POSIX_VDISABLE;
+ }
+#ifdef VDSUSP
+ term.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif /* VDSUSP */
+
+#endif /* _POSIX_VDISABLE */
+
+ if (!ISSET(SUSPEND)) {
+ /* Insane! */
+#ifdef _POSIX_VDISABLE
+ term.c_cc[VSUSP] = _POSIX_VDISABLE;
+#else
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGTSTP, &act, NULL);
+#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);
+
+ act.sa_handler = do_suspend;
+ sigaction(SIGTSTP, &act, NULL);
+
+ act.sa_handler = do_cont;
+ sigaction(SIGCONT, &act, NULL);
+ }
+
+#ifdef _POSIX_VDISABLE
+ tcsetattr(0, TCSANOW, &term);
+#endif
+}
+
+/* Handler for SIGHUP and SIGTERM */
+RETSIGTYPE handle_hupterm(int signal)
+{
+ die(_("Received SIGHUP or SIGTERM\n"));
+}
+
+/* What do we do when we catch the suspend signal */
+RETSIGTYPE do_suspend(int signal)
+{
+ endwin();
+ printf("\n\n\n\n\n%s\n", _("Use \"fg\" to return to nano"));
+ fflush(stdout);
+
+ /* Restore the terminal settings for the disabled keys */
+ tcsetattr(0, TCSANOW, &oldterm);
+
+ /* 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);
+}
+
+/* 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. */
+
+ doupdate();
+ /* The Hurd seems to need this, otherwise a ^Y after a ^Z will
+ start suspending again. */
+ signal_init();
+
+#ifndef NANO_SMALL
+ /* 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 == NULL)
+ 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;
+ editwinrows = LINES - 5 + no_help();
+ if (editwinrows < MIN_EDITOR_ROWS || COLS < MIN_EDITOR_COLS)
+ die_too_small();
+
+#ifndef DISABLE_WRAPJUSTIFY
+ fill = wrap_at;
+ if (fill <= 0)
+ fill += COLS;
+ if (fill < 0)
+ fill = 0;
+#endif
+
+ hblank = charealloc(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 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;
+ }
+}
+
+#ifndef NANO_SMALL
+void do_toggle(const toggle *which)
+{
+ int enabled;
+
+ /* Even easier! */
+ TOGGLE(which->flag);
+
+ switch (which->val) {
+ case TOGGLE_SUSPEND_KEY:
+ signal_init();
+ break;
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ case TOGGLE_MOUSE_KEY:
+ mouse_init();
+ break;
+#endif
+ case TOGGLE_NOHELP_KEY:
+ wclear(bottomwin);
+ wrefresh(bottomwin);
+ window_init();
+ fix_editbot();
+ edit_refresh();
+ display_main_list();
+ break;
+ case TOGGLE_DOS_KEY:
+ UNSET(MAC_FILE);
+ break;
+ case TOGGLE_MAC_KEY:
+ UNSET(DOS_FILE);
+ break;
+#ifdef ENABLE_COLOR
+ case TOGGLE_SYNTAX_KEY:
+ edit_refresh();
+ break;
+#endif
+ }
+
+ /* We are assuming here that shortcut_init() above didn't free and
+ * reallocate the toggles. */
+ enabled = ISSET(which->flag);
+ if (which->val == TOGGLE_NOHELP_KEY || which->val == TOGGLE_WRAP_KEY)
+ enabled = !enabled;
+ statusbar("%s %s", which->desc,
+ enabled ? _("enabled") : _("disabled"));
+}
+#endif /* !NANO_SMALL */
+
+/* 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]. */
+int abcd(int input)
+{
+ switch (input) {
+ case 'A':
+ case 'a':
+ return KEY_UP;
+ case 'B':
+ case 'b':
+ return KEY_DOWN;
+ case 'C':
+ case 'c':
+ return KEY_RIGHT;
+ case 'D':
+ case 'd':
+ return KEY_LEFT;
+ default:
+ return 0;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int optchr;
+ int startline = 0; /* Line to try and start at */
+ int modify_control_seq = 0;
+ int fill_flag_used = 0; /* Was the fill option used? */
+ const shortcut *s;
+ int keyhandled = 0; /* Have we handled the keystroke yet? */
+ int kbinput = -1; /* Input from keyboard */
+ int meta;
+
+#ifndef NANO_SMALL
+ const toggle *t;
+#endif
+#ifdef _POSIX_VDISABLE
+ struct termios term;
+#endif
+#ifdef HAVE_GETOPT_LONG
+ int option_index = 0;
+ const struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+#ifdef ENABLE_MULTIBUFFER
+ {"multibuffer", 0, 0, 'F'},
+#endif
+#ifdef ENABLE_NANORC
+#ifndef NANO_SMALL
+ {"historylog", 0, 0, 'H'},
+#endif
+ {"ignorercfiles", 0, 0, 'I'},
+#endif
+#ifndef DISABLE_JUSTIFY
+ {"quotestr", 1, 0, 'Q'},
+#endif
+#ifdef HAVE_REGEX_H
+ {"regexp", 0, 0, 'R'},
+#endif
+ {"tabsize", 1, 0, 'T'},
+ {"version", 0, 0, 'V'},
+#ifdef ENABLE_COLOR
+ {"syntax", 1, 0, 'Y'},
+#endif
+ {"const", 0, 0, 'c'},
+ {"rebinddelete", 0, 0, 'd'},
+ {"nofollow", 0, 0, 'l'},
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ {"mouse", 0, 0, 'm'},
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ {"operatingdir", 1, 0, 'o'},
+#endif
+ {"preserve", 0, 0, 'p'},
+#ifndef DISABLE_WRAPJUSTIFY
+ {"fill", 1, 0, 'r'},
+#endif
+#ifndef DISABLE_SPELLER
+ {"speller", 1, 0, 's'},
+#endif
+ {"tempfile", 0, 0, 't'},
+ {"view", 0, 0, 'v'},
+#ifndef DISABLE_WRAPPING
+ {"nowrap", 0, 0, 'w'},
+#endif
+ {"nohelp", 0, 0, 'x'},
+ {"suspend", 0, 0, 'z'},
+#ifndef NANO_SMALL
+ {"backup", 0, 0, 'B'},
+ {"dos", 0, 0, 'D'},
+ {"mac", 0, 0, 'M'},
+ {"noconvert", 0, 0, 'N'},
+ {"smooth", 0, 0, 'S'},
+ {"autoindent", 0, 0, 'i'},
+ {"cut", 0, 0, 'k'},
+#endif
+ {0, 0, 0, 0}
+ };
+#endif
+
+#ifdef ENABLE_NLS
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
+ /* if we don't have rcfile support, we're root, and
+ --disable-wrapping-as-root is used, turn wrapping off */
+ if (geteuid() == 0)
+ SET(NO_WRAP);
+#endif
+
+#ifdef HAVE_GETOPT_LONG
+ while ((optchr = getopt_long(argc, argv, "h?BDFHIMNQ:RST:VY:abcdefgijklmo:pr:s:tvwxz",
+ long_options, &option_index)) != -1) {
+#else
+ while ((optchr =
+ getopt(argc, argv, "h?BDFHIMNQ:RST:VY:abcdefgijklmo:pr:s:tvwxz")) != -1) {
+#endif
+
+ switch (optchr) {
+
+ case 'a':
+ case 'b':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'j':
+ /* Pico compatibility flags */
+ break;
+#ifndef NANO_SMALL
+ case 'B':
+ SET(BACKUP_FILE);
+ break;
+ case 'D':
+ SET(DOS_FILE);
+ break;
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ case 'F':
+ SET(MULTIBUFFER);
+ break;
+#endif
+#ifdef ENABLE_NANORC
+#ifndef NANO_SMALL
+ case 'H':
+ SET(HISTORYLOG);
+ break;
+#endif
+ case 'I':
+ SET(NO_RCFILE);
+ break;
+#endif
+#ifndef NANO_SMALL
+ case 'M':
+ SET(MAC_FILE);
+ break;
+ case 'N':
+ SET(NO_CONVERT);
+ break;
+#endif
+#ifndef DISABLE_JUSTIFY
+ case 'Q':
+ quotestr = mallocstrcpy(quotestr, optarg);
+ break;
+#endif
+#ifdef HAVE_REGEX_H
+ case 'R':
+ SET(USE_REGEXP);
+ break;
+#endif
+#ifndef NANO_SMALL
+ case 'S':
+ SET(SMOOTHSCROLL);
+ break;
+#endif
+ case 'T':
+ {
+ int i;
+ char *first_error;
+
+ /* Using strtol() instead of atoi() lets us accept 0
+ * while checking other errors. */
+ i = (int)strtol(optarg, &first_error, 10);
+ if (errno == ERANGE || *optarg == '\0' || *first_error != '\0')
+ usage();
+ else
+ tabsize = i;
+ if (tabsize <= 0) {
+ fprintf(stderr, _("Tab size is too small for nano...\n"));
+ exit(1);
+ }
+ }
+ break;
+ case 'V':
+ version();
+ exit(0);
+#ifdef ENABLE_COLOR
+ case 'Y':
+ syntaxstr = mallocstrcpy(syntaxstr, optarg);
+ break;
+#endif
+ case 'c':
+ SET(CONSTUPDATE);
+ break;
+ case 'd':
+ SET(REBIND_DELETE);
+ break;
+#ifndef NANO_SMALL
+ case 'i':
+ SET(AUTOINDENT);
+ break;
+ case 'k':
+ SET(CUT_TO_END);
+ break;
+#endif
+ case 'l':
+ SET(NOFOLLOW_SYMLINKS);
+ break;
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ case 'm':
+ SET(USE_MOUSE);
+ break;
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ case 'o':
+ operating_dir = mallocstrcpy(operating_dir, optarg);
+ break;
+#endif
+ case 'p':
+ SET(PRESERVE);
+#ifdef HAVE_GETOPT_LONG
+#endif
+ break;
+#ifndef DISABLE_WRAPJUSTIFY
+ case 'r':
+ {
+ int i;
+ char *first_error;
+
+ /* Using strtol() instead of atoi() lets us accept 0
+ * while checking other errors. */
+ i = (int)strtol(optarg, &first_error, 10);
+ if (errno == ERANGE || *optarg == '\0' || *first_error != '\0')
+ usage();
+ else
+ wrap_at = i;
+ }
+ fill_flag_used = 1;
+ break;
+#endif
+#ifndef DISABLE_SPELLER
+ case 's':
+ alt_speller = mallocstrcpy(alt_speller, optarg);
+ break;
+#endif
+ case 't':
+ SET(TEMP_OPT);
+ break;
+ case 'v':
+ SET(VIEW_MODE);
+ break;
+#ifndef DISABLE_WRAPPING
+ case 'w':
+ SET(NO_WRAP);
+ break;
+#endif
+ case 'x':
+ SET(NO_HELP);
+ break;
+ case 'z':
+ SET(SUSPEND);
+ break;
+ default:
+ usage();
+ }
+ }
+
+/* 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 (!ISSET(NO_RCFILE)) {
+#ifndef DISABLE_OPERATINGDIR
+ char *operating_dir_cpy = operating_dir;
+#endif
+#ifndef DISABLE_WRAPPING
+ int wrap_at_cpy = wrap_at;
+#endif
+#ifndef DISABLE_JUSTIFY
+ char *quotestr_cpy = quotestr;
+#endif
+#ifndef DISABLE_SPELLER
+ char *alt_speller_cpy = alt_speller;
+#endif
+ int tabsize_cpy = tabsize;
+ long flags_cpy = flags;
+
+#ifndef DISABLE_OPERATINGDIR
+ operating_dir = NULL;
+#endif
+#ifndef DISABLE_JUSTIFY
+ quotestr = NULL;
+#endif
+#ifndef DISABLE_SPELLER
+ alt_speller = NULL;
+#endif
+
+ do_rcfile();
+
+#ifndef DISABLE_OPERATINGDIR
+ if (operating_dir_cpy != NULL) {
+ free(operating_dir);
+ operating_dir = operating_dir_cpy;
+ }
+#endif
+#ifndef DISABLE_WRAPPING
+ if (fill_flag_used)
+ wrap_at = wrap_at_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 > 0)
+ tabsize = tabsize_cpy;
+ flags |= flags_cpy;
+ }
+#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
+ else if (geteuid() == 0)
+ SET(NO_WRAP);
+#endif
+#endif /* ENABLE_NANORC */
+
+#ifndef NANO_SMALL
+ history_init();
+#ifdef ENABLE_NANORC
+ if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
+ load_history();
+#endif
+#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 (quotestr == NULL)
+#ifdef HAVE_REGEX_H
+ quotestr = mallocstrcpy(NULL, "^([ \t]*[|>:}#])+");
+#else
+ quotestr = mallocstrcpy(NULL, "> ");
+#endif
+#endif /* !DISABLE_JUSTIFY */
+ if (tabsize == -1)
+ tabsize = 8;
+
+ /* Clear the filename we'll be using */
+ filename = charalloc(1);
+ filename[0] = '\0';
+
+ /* If there's a +LINE flag, it is the first non-option argument. */
+ if (0 < optind && optind < argc && argv[optind][0] == '+') {
+ startline = atoi(&argv[optind][1]);
+ optind++;
+ }
+ if (0 < optind && optind < argc)
+ filename = mallocstrcpy(filename, argv[optind]);
+
+ /* See if there's a non-option in argv (first non-option is the
+ filename, if +LINE is not given) */
+ if (argc > 1 && argc > optind) {
+ /* Look for the +line flag... */
+ if (argv[optind][0] == '+') {
+ startline = atoi(&argv[optind][1]);
+ optind++;
+ if (argc > optind)
+ filename = mallocstrcpy(filename, argv[optind]);
+ } else
+ filename = mallocstrcpy(filename, argv[optind]);
+ }
+
+ /* First back up the old settings so they can be restored, duh */
+ tcgetattr(0, &oldterm);
+
+#ifdef _POSIX_VDISABLE
+ term = oldterm;
+ term.c_cc[VINTR] = _POSIX_VDISABLE;
+ term.c_cc[VQUIT] = _POSIX_VDISABLE;
+ term.c_lflag &= ~IEXTEN;
+ tcsetattr(0, TCSANOW, &term);
+#endif
+
+ /* now ncurses init stuff... */
+ initscr();
+ savetty();
+ nonl();
+ cbreak();
+ noecho();
+
+ /* Set up some global variables */
+ global_init(0);
+ shortcut_init(0);
+ signal_init();
+
+#ifdef DEBUG
+ fprintf(stderr, "Main: set up windows\n");
+#endif
+
+ window_init();
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ mouse_init();
+#endif
+
+ keypad(edit, TRUE);
+ keypad(bottomwin, TRUE);
+
+#ifdef DEBUG
+ fprintf(stderr, "Main: bottom win\n");
+#endif
+ /* Set up bottom of window */
+ display_main_list();
+
+#ifdef DEBUG
+ fprintf(stderr, "Main: open file\n");
+#endif
+
+ open_file(filename, 0, 1);
+#ifdef ENABLE_MULTIBUFFER
+ /* If we're using multibuffers and more than one file is specified
+ on the command line, load them all and switch to the first one
+ afterward */
+ if (ISSET(MULTIBUFFER) && optind + 1 < argc) {
+ for (optind++; optind < argc; optind++) {
+ add_open_file(1);
+ new_file();
+ filename = mallocstrcpy(filename, argv[optind]);
+ open_file(filename, 0, 0);
+ load_file(0);
+ }
+ open_nextfile_void();
+ }
+#endif
+
+ titlebar(NULL);
+
+ if (startline > 0)
+ do_gotoline(startline, 0);
+
+ /* Return here after a sigwinch */
+ sigsetjmp(jmpbuf, 1);
+
+ /* SHUT UP GCC! */
+ startline = 0;
+ fill_flag_used = 0;
+ keyhandled = 0;
+
+ /* This variable should be initialized after the sigsetjmp(), so we
+ can't do Esc-Esc then quickly resize and muck things up. */
+ modify_control_seq = 0;
+
+ edit_refresh();
+ reset_cursor();
+
+ while (1) {
+ keyhandled = 0;
+
+ if (ISSET(CONSTUPDATE))
+ do_cursorpos(1);
+
+#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+ currshortcut = main_list;
+#endif
+
+#ifndef _POSIX_VDISABLE
+ /* We're going to have to do it the old way, i.e. on cygwin */
+ raw();
+#endif
+
+ kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE));
+#ifdef DEBUG
+ fprintf(stderr, "AHA! %c (%d)\n", kbinput, kbinput);
+#endif
+ if (meta == 1) {
+ switch (kbinput) {
+#ifdef ENABLE_MULTIBUFFER
+ case NANO_OPENPREV_KEY:
+ case NANO_OPENPREV_ALTKEY:
+ open_prevfile_void();
+ keyhandled = 1;
+ break;
+ case NANO_OPENNEXT_KEY:
+ case NANO_OPENNEXT_ALTKEY:
+ open_nextfile_void();
+ keyhandled = 1;
+ break;
+#endif
+ default:
+ /* Check for the altkey defs.... */
+ for (s = main_list; s != NULL; s = s->next)
+ if (kbinput == s->altval || (kbinput >= 'A' &&
+ kbinput <= 'Z' && kbinput == s->altval - 32)) {
+ if (ISSET(VIEW_MODE) && !s->viewok)
+ print_view_warning();
+ else {
+ if (s->func != do_cut_text)
+ UNSET(KEEP_CUTBUFFER);
+ s->func();
+ }
+ keyhandled = 1;
+ break;
+ }
+#ifndef NANO_SMALL
+ if (!keyhandled)
+ /* And for toggle switches */
+ for (t = toggles; t != NULL; t = t->next)
+ if (kbinput == t->val || (t->val >= 'a' &&
+ t->val <= 'z' && kbinput == t->val - 32)) {
+ UNSET(KEEP_CUTBUFFER);
+ do_toggle(t);
+ keyhandled = 1;
+ break;
+ }
+#endif
+#ifdef DEBUG
+ fprintf(stderr, "I got Alt-%c! (%d)\n", kbinput,
+ kbinput);
+#endif
+ }
+ }
+
+ /* Look through the main shortcut list to see if we've hit a
+ shortcut key */
+
+ if (!keyhandled)
+#if !defined(DISABLE_BROWSER) || !defined (DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+ for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
+#else
+ for (s = main_list; s != NULL && !keyhandled; s = s->next) {
+#endif
+ if (kbinput == s->val ||
+ (s->misc1 && kbinput == s->misc1) ||
+ (s->misc2 && kbinput == s->misc2)) {
+ if (ISSET(VIEW_MODE) && !s->viewok)
+ print_view_warning();
+ else {
+ if (s->func != do_cut_text)
+ UNSET(KEEP_CUTBUFFER);
+ 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 (!keyhandled)
+ UNSET(KEEP_CUTBUFFER);
+
+#ifdef _POSIX_VDISABLE
+ /* Don't even think about changing this string */
+ if (kbinput == NANO_CONTROL_Q)
+ statusbar(_("XON ignored, mumble mumble."));
+ if (kbinput == NANO_CONTROL_S)
+ statusbar(_("XOFF ignored, mumble mumble."));
+#endif
+ /* If we're in raw mode or using Alt-Alt-x, we have to catch
+ Control-S and Control-Q */
+ if (kbinput == NANO_CONTROL_Q || kbinput == NANO_CONTROL_S)
+ keyhandled = 1;
+
+ /* Catch ^Z by hand when triggered also */
+ if (kbinput == NANO_SUSPEND_KEY) {
+ if (ISSET(SUSPEND))
+ do_suspend(0);
+ keyhandled = 1;
+ }
+
+ /* Last gasp, stuff that's not in the main lists */
+ if (!keyhandled)
+ switch (kbinput) {
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ case KEY_MOUSE:
+ do_mouse();
+ break;
+#endif
+
+ case NANO_CONTROL_3: /* Ctrl-[ (Esc), which should
+ * have been handled before we
+ * got here */
+ case NANO_CONTROL_5: /* Ctrl-] */
+ break;
+ default:
+#ifdef DEBUG
+ fprintf(stderr, "I got %c (%d)!\n", kbinput, kbinput);
+#endif
+ /* We no longer stop unhandled sequences so that people with
+ odd character sets can type... */
+
+ if (ISSET(VIEW_MODE))
+ print_view_warning();
+ else
+ do_char(kbinput);
+ }
+
+ reset_cursor();
+ wrefresh(edit);
+ }
+ assert(0);
+}
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * nano.h *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef NANO_H
+#define NANO_H 1
+
+/* Macros for the flags int... */
+#define SET(bit) flags |= bit
+#define UNSET(bit) flags &= ~bit
+#define ISSET(bit) (flags & bit)
+#define TOGGLE(bit) flags ^= bit
+
+/* Define charalloc as a macro rather than duplicating code */
+#define charalloc(howmuch) (char *)nmalloc((howmuch) * sizeof(char))
+#define charealloc(ptr, howmuch) (char *)nrealloc(ptr, (howmuch) * sizeof(char))
+#ifdef BROKEN_REGEXEC
+#define regexec(preg, string, nmatch, pmatch, eflags) regexec_safe(preg, string, nmatch, pmatch, eflags)
+#endif
+
+#ifndef NANO_SMALL
+ /* For the backup file copy ... */
+# define COPYFILEBLOCKSIZE 1024
+#endif
+
+#ifdef USE_SLANG /* Slang support enabled */
+#include <slcurses.h>
+#define KEY_IC SL_KEY_IC
+#define KEY_DC SL_KEY_DELETE
+#define KEY_SUSPEND -1
+#elif defined(HAVE_NCURSES_H)
+#include <ncurses.h>
+#else /* Uh oh */
+#include <curses.h>
+#endif /* CURSES_H */
+
+#ifdef ENABLE_NLS
+# ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# endif
+# define _(string) gettext(string)
+# define P_(singular, plural, number) ngettext(singular, plural, number)
+#else
+# define _(string) (string)
+# define P_(singular, plural, number) (number == 1 ? singular : plural)
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
+#include <glib.h>
+# ifndef HAVE_SNPRINTF
+# define snprintf g_snprintf
+# endif
+# ifndef HAVE_VSNPRINTF
+# define vsnprintf g_vsnprintf
+# endif
+#endif
+
+#ifndef HAVE_STRCASECMP
+#define strcasecmp nstricmp
+#endif
+
+#ifndef HAVE_STRNCASECMP
+#define strncasecmp nstrnicmp
+#endif
+
+/* HP-UX 10 & 11 do not seem to support KEY_HOME and KEY_END */
+#ifndef KEY_HOME
+#define KEY_HOME -1
+#endif /* KEY_HOME */
+
+#ifndef KEY_END
+#define KEY_END -1
+#endif /* KEY_END */
+
+/* Snatch these out of the ncurses defs, so we can use them in search
+ history regardless of whether we're using ncurses or not */
+#if !defined(KEY_UP) || !defined(KEY_DOWN)
+#define KEY_UP 0403
+#define KEY_DOWN 0402
+#endif /* !KEY_UP || !KEY_DOWN */
+
+#define VERMSG "GNU nano " VERSION
+
+#if defined(DISABLE_WRAPPING) && defined(DISABLE_JUSTIFY)
+#define DISABLE_WRAPJUSTIFY 1
+#endif
+
+/* Structure types */
+typedef struct filestruct {
+ char *data;
+ struct filestruct *next; /* Next node */
+ struct filestruct *prev; /* Previous node */
+ int lineno; /* The line number */
+} filestruct;
+
+#ifdef ENABLE_MULTIBUFFER
+typedef struct openfilestruct {
+ char *filename;
+#ifndef NANO_SMALL
+ struct stat originalfilestat;
+#endif
+ struct openfilestruct *next; /* Next node */
+ struct openfilestruct *prev; /* Previous node */
+ struct filestruct *fileage; /* Current file */
+ struct filestruct *filebot; /* Current file's last line */
+#ifndef NANO_SMALL
+ struct filestruct *file_mark_beginbuf;
+ /* Current file's beginning marked line */
+ int file_mark_beginx; /* Current file's beginning marked line's
+ x-coordinate position */
+#endif
+ int file_current_x; /* Current file's x-coordinate position */
+ int file_current_y; /* Current file's y-coordinate position */
+ int file_flags; /* Current file's flags: modification
+ status (and marking status, if
+ available) */
+ int file_placewewant; /* Current file's place we want */
+ int file_totlines; /* Current file's total number of lines */
+ long file_totsize; /* Current file's total size */
+ int file_lineno; /* Current file's line number */
+} openfilestruct;
+#endif
+
+typedef struct shortcut {
+ int val; /* Actual sequence that generates the keystroke */
+ int altval; /* Alt key # for this function, or 0 for none */
+ int misc1; /* Other int functions we want bound */
+ int misc2;
+ int viewok; /* is this function legal in view mode? */
+ int (*func) (void); /* Function to call when we catch this key */
+ const char *desc; /* Description, e.g. "Page Up" */
+#ifndef DISABLE_HELP
+ const char *help; /* Help file entry text */
+#endif
+ struct shortcut *next;
+} shortcut;
+
+#ifndef NANO_SMALL
+typedef struct toggle {
+ int val; /* Sequence to toggle the key. Should only need 1 */
+ const char *desc; /* Description for when toggle is, uh, toggled,
+ e.g. "Pico Messages"; we'll append Enabled or
+ Disabled */
+ int flag; /* What flag actually gets toggled */
+ struct toggle *next;
+} toggle;
+#endif /* !NANO_SMALL */
+
+#ifdef ENABLE_NANORC
+typedef struct rcoption {
+ const char *name;
+ int flag;
+} rcoption;
+#endif /* ENABLE_NANORC */
+
+#ifdef ENABLE_COLOR
+
+typedef struct colortype {
+ int fg; /* fg color */
+ int bg; /* bg color */
+ int bright; /* Is this color A_BOLD? */
+ int pairnum; /* Color pair number used for this fg/bg */
+ regex_t start; /* Start (or all) of the regex string */
+ regex_t *end; /* End of the regex string */
+ struct colortype *next;
+} colortype;
+
+typedef struct exttype {
+ regex_t val; /* The extensions that match this syntax. */
+ struct exttype *next;
+} exttype;
+
+typedef struct syntaxtype {
+ char *desc; /* Name of this syntax type */
+ exttype *extensions; /* List of extensions that this applies to */
+ colortype *color; /* color struct for this syntax */
+ struct syntaxtype *next;
+} syntaxtype;
+
+#endif /* ENABLE_COLOR */
+
+#ifndef NANO_SMALL
+typedef struct historytype {
+ struct historytype *next;
+ struct historytype *prev;
+ char *data;
+} historytype;
+typedef struct historyheadtype {
+ struct historytype *next; /* keep *next and *prev members together */
+ struct historytype *prev; /* and in same order as in historytype */
+ struct historytype *tail;
+ struct historytype *current;
+ int count;
+ int len;
+} historyheadtype;
+#endif /* !NANO_SMALL */
+
+/* Bitwise flags so we can save space (or more correctly, not waste it) */
+
+#define MODIFIED (1<<0)
+#define KEEP_CUTBUFFER (1<<1)
+#define CASE_SENSITIVE (1<<2)
+#define MARK_ISSET (1<<3)
+#define CONSTUPDATE (1<<4)
+#define NO_HELP (1<<5)
+#define NOFOLLOW_SYMLINKS (1<<6)
+#define SUSPEND (1<<7)
+#define NO_WRAP (1<<8)
+#define AUTOINDENT (1<<9)
+#define SAMELINEWRAP (1<<10)
+#define VIEW_MODE (1<<11)
+#define USE_MOUSE (1<<12)
+#define USE_REGEXP (1<<13)
+#define REGEXP_COMPILED (1<<14)
+#define TEMP_OPT (1<<15)
+#define CUT_TO_END (1<<16)
+#define REVERSE_SEARCH (1<<17)
+#define MULTIBUFFER (1<<18)
+#define DOS_FILE (1<<19)
+#define MAC_FILE (1<<20)
+#define SMOOTHSCROLL (1<<21)
+#define DISABLE_CURPOS (1<<22) /* Damn, we still need it */
+#define REBIND_DELETE (1<<23)
+#define NO_CONVERT (1<<24)
+#define BACKUP_FILE (1<<25)
+#define NO_RCFILE (1<<26)
+#define COLOR_SYNTAX (1<<27)
+#define PRESERVE (1<<28)
+#define HISTORY_CHANGED (1<<29)
+#define HISTORYLOG (1<<30)
+#define JUSTIFY_MODE (1<<31)
+
+/* Control key sequences, changing these would be very very bad */
+
+#define NANO_CONTROL_SPACE 0
+#define NANO_CONTROL_A 1
+#define NANO_CONTROL_B 2
+#define NANO_CONTROL_C 3
+#define NANO_CONTROL_D 4
+#define NANO_CONTROL_E 5
+#define NANO_CONTROL_F 6
+#define NANO_CONTROL_G 7
+#define NANO_CONTROL_H 8
+#define NANO_CONTROL_I 9
+#define NANO_CONTROL_J 10
+#define NANO_CONTROL_K 11
+#define NANO_CONTROL_L 12
+#define NANO_CONTROL_M 13
+#define NANO_CONTROL_N 14
+#define NANO_CONTROL_O 15
+#define NANO_CONTROL_P 16
+#define NANO_CONTROL_Q 17
+#define NANO_CONTROL_R 18
+#define NANO_CONTROL_S 19
+#define NANO_CONTROL_T 20
+#define NANO_CONTROL_U 21
+#define NANO_CONTROL_V 22
+#define NANO_CONTROL_W 23
+#define NANO_CONTROL_X 24
+#define NANO_CONTROL_Y 25
+#define NANO_CONTROL_Z 26
+#define NANO_CONTROL_3 27
+#define NANO_CONTROL_4 28
+#define NANO_CONTROL_5 29
+#define NANO_CONTROL_6 30
+#define NANO_CONTROL_7 31
+#define NANO_CONTROL_8 127
+
+#define NANO_ALT_A 'a'
+#define NANO_ALT_B 'b'
+#define NANO_ALT_C 'c'
+#define NANO_ALT_D 'd'
+#define NANO_ALT_E 'e'
+#define NANO_ALT_F 'f'
+#define NANO_ALT_G 'g'
+#define NANO_ALT_H 'h'
+#define NANO_ALT_I 'i'
+#define NANO_ALT_J 'j'
+#define NANO_ALT_K 'k'
+#define NANO_ALT_L 'l'
+#define NANO_ALT_M 'm'
+#define NANO_ALT_N 'n'
+#define NANO_ALT_O 'o'
+#define NANO_ALT_P 'p'
+#define NANO_ALT_Q 'q'
+#define NANO_ALT_R 'r'
+#define NANO_ALT_S 's'
+#define NANO_ALT_T 't'
+#define NANO_ALT_U 'u'
+#define NANO_ALT_V 'v'
+#define NANO_ALT_W 'w'
+#define NANO_ALT_X 'x'
+#define NANO_ALT_Y 'y'
+#define NANO_ALT_Z 'z'
+#define NANO_ALT_PERIOD '.'
+#define NANO_ALT_COMMA ','
+#define NANO_ALT_LCARAT '<'
+#define NANO_ALT_RCARAT '>'
+#define NANO_ALT_BRACKET ']'
+#define NANO_ALT_SPACE ' '
+
+/* Some semi-changeable keybindings; don't play with unless you're sure you
+know what you're doing */
+
+#define NANO_INSERTFILE_KEY NANO_CONTROL_R
+#define NANO_INSERTFILE_FKEY KEY_F(5)
+#define NANO_EXIT_KEY NANO_CONTROL_X
+#define NANO_EXIT_FKEY KEY_F(2)
+#define NANO_WRITEOUT_KEY NANO_CONTROL_O
+#define NANO_WRITEOUT_FKEY KEY_F(3)
+#define NANO_GOTO_KEY NANO_CONTROL_7
+#define NANO_GOTO_FKEY KEY_F(13)
+#define NANO_ALT_GOTO_KEY NANO_ALT_G
+#define NANO_HELP_KEY NANO_CONTROL_G
+#define NANO_HELP_FKEY KEY_F(1)
+#define NANO_WHEREIS_KEY NANO_CONTROL_W
+#define NANO_WHEREIS_FKEY KEY_F(6)
+#define NANO_WHEREIS_NEXT_KEY NANO_ALT_W
+#define NANO_REPLACE_KEY NANO_CONTROL_4
+#define NANO_REPLACE_FKEY KEY_F(14)
+#define NANO_ALT_REPLACE_KEY NANO_ALT_R
+#define NANO_OTHERSEARCH_KEY NANO_CONTROL_R
+#define NANO_PREVPAGE_KEY NANO_CONTROL_Y
+#define NANO_PREVPAGE_FKEY KEY_F(7)
+#define NANO_NEXTPAGE_KEY NANO_CONTROL_V
+#define NANO_NEXTPAGE_FKEY KEY_F(8)
+#define NANO_CUT_KEY NANO_CONTROL_K
+#define NANO_CUT_FKEY KEY_F(9)
+#define NANO_UNCUT_KEY NANO_CONTROL_U
+#define NANO_UNCUT_FKEY KEY_F(10)
+#define NANO_CURSORPOS_KEY NANO_CONTROL_C
+#define NANO_CURSORPOS_FKEY KEY_F(11)
+#define NANO_SPELL_KEY NANO_CONTROL_T
+#define NANO_SPELL_FKEY KEY_F(12)
+#define NANO_FIRSTLINE_KEY NANO_PREVPAGE_KEY
+#define NANO_LASTLINE_KEY NANO_NEXTPAGE_KEY
+#define NANO_CANCEL_KEY NANO_CONTROL_C
+#define NANO_REFRESH_KEY NANO_CONTROL_L
+#define NANO_JUSTIFY_KEY NANO_CONTROL_J
+#define NANO_JUSTIFY_FKEY KEY_F(4)
+#define NANO_UNJUSTIFY_KEY NANO_CONTROL_U
+#define NANO_UP_KEY NANO_CONTROL_P
+#define NANO_DOWN_KEY NANO_CONTROL_N
+#define NANO_FORWARD_KEY NANO_CONTROL_F
+#define NANO_BACK_KEY NANO_CONTROL_B
+#define NANO_MARK_KEY NANO_CONTROL_6
+#define NANO_ALT_MARK_KEY NANO_ALT_A
+#define NANO_HOME_KEY NANO_CONTROL_A
+#define NANO_END_KEY NANO_CONTROL_E
+#define NANO_DELETE_KEY NANO_CONTROL_D
+#define NANO_BACKSPACE_KEY NANO_CONTROL_H
+#define NANO_TAB_KEY NANO_CONTROL_I
+#define NANO_SUSPEND_KEY NANO_CONTROL_Z
+#define NANO_ENTER_KEY NANO_CONTROL_M
+#define NANO_FROMSEARCHTOGOTO_KEY NANO_CONTROL_T
+#define NANO_TOFILES_KEY NANO_CONTROL_T
+#define NANO_APPEND_KEY NANO_ALT_A
+#define NANO_PREPEND_KEY NANO_ALT_P
+#define NANO_OPENPREV_KEY NANO_ALT_LCARAT
+#define NANO_OPENNEXT_KEY NANO_ALT_RCARAT
+#define NANO_OPENPREV_ALTKEY NANO_ALT_COMMA
+#define NANO_OPENNEXT_ALTKEY NANO_ALT_PERIOD
+#define NANO_BRACKET_KEY NANO_ALT_BRACKET
+#define NANO_EXTCMD_KEY NANO_CONTROL_X
+#define NANO_NEXTWORD_KEY NANO_CONTROL_SPACE
+#define NANO_PREVWORD_KEY NANO_ALT_SPACE
+#define NANO_PARABEGIN_KEY NANO_CONTROL_W
+#define NANO_PARAEND_KEY NANO_CONTROL_O
+
+#ifndef NANO_SMALL
+/* Toggles do not exist with NANO_SMALL. */
+#define TOGGLE_CONST_KEY NANO_ALT_C
+#define TOGGLE_AUTOINDENT_KEY NANO_ALT_I
+#define TOGGLE_SUSPEND_KEY NANO_ALT_Z
+#define TOGGLE_NOHELP_KEY NANO_ALT_X
+#define TOGGLE_MOUSE_KEY NANO_ALT_M
+#define TOGGLE_CUTTOEND_KEY NANO_ALT_K
+#define TOGGLE_REGEXP_KEY NANO_ALT_R
+#define TOGGLE_WRAP_KEY NANO_ALT_L
+#define TOGGLE_BACKWARDS_KEY NANO_ALT_B
+#define TOGGLE_CASE_KEY NANO_ALT_C
+#define TOGGLE_LOAD_KEY NANO_ALT_F
+#define TOGGLE_DOS_KEY NANO_ALT_D
+#define TOGGLE_MAC_KEY NANO_ALT_O
+#define TOGGLE_SMOOTH_KEY NANO_ALT_S
+#define TOGGLE_NOCONVERT_KEY NANO_ALT_N
+#define TOGGLE_BACKUP_KEY NANO_ALT_B
+#define TOGGLE_SYNTAX_KEY NANO_ALT_Y
+#endif /* !NANO_SMALL */
+
+#define MAIN_VISIBLE 12
+
+#define VIEW 1
+#define NOVIEW 0
+
+typedef enum {
+ CENTER, TOP, NONE
+} topmidbotnone;
+
+/* Minimum editor window rows required for nano to work correctly */
+#define MIN_EDITOR_ROWS 3
+
+/* Minimum editor window cols required for nano to work correctly */
+#define MIN_EDITOR_COLS 10
+
+/* Default number of characters from end-of-line where text wrapping
+ occurs */
+#define CHARS_FROM_EOL 8
+
+/* Maximum number of search history strings saved, same value used for
+ replace history */
+#define MAX_SEARCH_HISTORY 100
+
+#endif /* !NANO_H */
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * proto.h *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+/* Externs */
+
+#include <sys/stat.h>
+
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+
+#include "nano.h"
+
+extern int wrap_at;
+extern int editwinrows;
+extern int current_x, current_y, totlines;
+extern int placewewant;
+#ifndef NANO_SMALL
+extern int mark_beginx;
+#endif
+extern long totsize;
+extern int temp_opt;
+extern int wrap_at, flags, tabsize;
+extern int search_last_line;
+extern int search_offscreen;
+extern int currslen;
+
+#ifndef DISABLE_JUSTIFY
+extern char *quotestr;
+#endif
+
+extern WINDOW *edit, *topwin, *bottomwin;
+extern char *filename;
+extern struct stat originalfilestat;
+extern char *answer;
+extern char *hblank;
+#ifndef DISABLE_HELP
+extern char *help_text;
+#endif
+extern char *last_search;
+extern char *last_replace;
+#ifndef DISABLE_OPERATINGDIR
+extern char *operating_dir;
+extern char *full_operating_dir;
+#endif
+#ifndef DISABLE_SPELLER
+extern char *alt_speller;
+#endif
+
+extern int resetstatuspos;
+extern struct stat fileinfo;
+extern filestruct *current, *fileage, *edittop, *editbot, *filebot;
+extern filestruct *cutbuffer;
+#ifndef NANO_SMALL
+extern filestruct *mark_beginbuf;
+#endif
+
+#ifdef ENABLE_MULTIBUFFER
+extern openfilestruct *open_files;
+#endif
+
+#ifdef ENABLE_COLOR
+extern const colortype *colorstrings;
+extern syntaxtype *syntaxes;
+extern char *syntaxstr;
+#endif
+
+extern shortcut *shortcut_list;
+extern shortcut *main_list, *whereis_list;
+extern shortcut *replace_list, *goto_list;
+extern shortcut *writefile_list, *insertfile_list;
+extern shortcut *replace_list_2;
+#ifndef NANO_SMALL
+extern shortcut *extcmd_list;
+#endif
+#ifndef DISABLE_HELP
+extern shortcut *help_list;
+#endif
+#ifndef DISABLE_SPELLER
+extern shortcut *spell_list;
+#endif
+#ifndef DISABLE_BROWSER
+extern shortcut *browser_list, *gotodir_list;
+#endif
+
+#if !defined(DISABLE_BROWSER) || !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+extern const shortcut *currshortcut;
+#endif
+
+#ifdef HAVE_REGEX_H
+extern regex_t search_regexp;
+extern regmatch_t regmatches[10];
+#ifdef ENABLE_COLOR
+extern regex_t syntaxfile_regexp;
+extern regmatch_t synfilematches[1];
+#endif /* ENABLE_COLOR */
+#endif /* HAVE_REGEX_H */
+
+#ifndef NANO_SMALL
+extern toggle *toggles;
+#endif
+
+#ifndef NANO_SMALL
+extern historyheadtype search_history;
+extern historyheadtype replace_history;
+#endif
+
+extern int curses_ended;
+
+/* Functions we want available */
+
+/* Public functions in color.c */
+#ifdef ENABLE_COLOR
+void set_colorpairs(void);
+void do_colorinit(void);
+void update_color(void);
+#endif /* ENABLE_COLOR */
+
+/* Public functions in cut.c */
+filestruct *get_cutbottom(void);
+void add_to_cutbuffer(filestruct *inptr);
+void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot,
+ size_t bot_x, int destructive);
+int do_cut_text(void);
+int do_uncut_text(void);
+
+/* Public functions in files.c */
+void load_file(int update);
+void new_file(void);
+filestruct *read_line(char *buf, filestruct *prev, int *line1ins, int len);
+int read_file(FILE *f, const char *filename, int quiet);
+int open_file(const char *filename, int insert, int quiet);
+char *get_next_filename(const char *name);
+int do_insertfile(int loading_file);
+int do_insertfile_void(void);
+#ifdef ENABLE_MULTIBUFFER
+openfilestruct *make_new_opennode(openfilestruct *prevnode);
+void splice_opennode(openfilestruct *begin, openfilestruct *newnode, openfilestruct *end);
+void unlink_opennode(const openfilestruct *fileptr);
+void delete_opennode(openfilestruct *fileptr);
+void free_openfilestruct(openfilestruct *src);
+int add_open_file(int update);
+int load_open_file(void);
+int open_prevfile(int closing_file);
+int open_prevfile_void(void);
+int open_nextfile(int closing_file);
+int open_nextfile_void(void);
+int close_open_file(void);
+#endif
+#if !defined(DISABLE_SPELLER) || !defined(DISABLE_OPERATINGDIR)
+char *get_full_path(const char *origpath);
+#endif
+#ifndef DISABLE_SPELLER
+char *check_writable_directory(const char *path);
+char *safe_tempnam(const char *dirname, const char *filename_prefix);
+#endif
+#ifndef DISABLE_OPERATINGDIR
+void init_operating_dir(void);
+int check_operating_dir(const char *currpath, int allow_tabcomp);
+#endif
+int write_file(const char *name, int tmp, int append, int nonamechange);
+int do_writeout(const char *path, int exiting, int append);
+int do_writeout_void(void);
+char *real_dir_from_tilde(const char *buf);
+#ifndef DISABLE_TABCOMP
+int append_slash_if_dir(char *buf, int *lastwastab, int *place);
+char **username_tab_completion(char *buf, int *num_matches);
+char **cwd_tab_completion(char *buf, int *num_matches);
+char *input_tab(char *buf, int place, int *lastwastab, int *newplace, int *list);
+#endif
+#ifndef DISABLE_BROWSER
+struct stat filestat(const char *path);
+int diralphasort(const void *va, const void *vb);
+void free_charptrarray(char **array, int len);
+const char *tail(const char *foo);
+void striponedir(char *foo);
+int readable_dir(const char *path);
+char **browser_init(const char *path, int *longest, int *numents);
+char *do_browser(const char *inpath);
+char *do_browse_from(const char *inpath);
+#endif
+
+/* Public functions in global.c */
+int length_of_list(const shortcut *s);
+void sc_init_one(shortcut **shortcutage, int key, const char *desc,
+#ifndef DISABLE_HELP
+ const char *help,
+#endif
+ int alt, int misc1, int misc2, int view, int (*func) (void));
+#ifndef NANO_SMALL
+void toggle_init_one(int val, const char *desc, int flag);
+void toggle_init(void);
+#ifdef DEBUG
+void free_toggles(void);
+#endif
+#endif
+void free_shortcutage(shortcut **shortcutage);
+void shortcut_init(int unjustify);
+#ifdef DEBUG
+void thanks_for_all_the_fish(void);
+#endif
+
+/* Public functions in move.c */
+int do_home(void);
+int do_end(void);
+void page_up(void);
+int do_page_up(void);
+int do_page_down(void);
+int do_up(void);
+int do_down(void);
+int do_left(void);
+int do_right(void);
+
+/* Public functions in nano.c */
+RETSIGTYPE finish(int sigage);
+void die(const char *msg, ...);
+void die_save_file(const char *die_filename);
+void die_too_small(void);
+void print_view_warning(void);
+void global_init(int save_cutbuffer);
+void window_init(void);
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+void mouse_init(void);
+#endif
+#ifndef DISABLE_HELP
+void help_init(void);
+#endif
+filestruct *make_new_node(filestruct *prevnode);
+filestruct *copy_node(const filestruct *src);
+void splice_node(filestruct *begin, filestruct *newnode, filestruct *end);
+void unlink_node(const filestruct *fileptr);
+void delete_node(filestruct *fileptr);
+filestruct *copy_filestruct(const filestruct *src);
+void free_filestruct(filestruct *src);
+void renumber_all(void);
+void renumber(filestruct *fileptr);
+void print1opt(const char *shortflag, const char *longflag,
+ const char *desc);
+void usage(void);
+void version(void);
+void do_early_abort(void);
+int no_help(void);
+#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP) || defined(NANO_SMALL)
+void nano_disabled_msg(void);
+#endif
+#ifndef NANO_SMALL
+RETSIGTYPE cancel_fork(int signal);
+int open_pipe(const char *command);
+#endif
+#ifndef DISABLE_MOUSE
+#ifdef NCURSES_MOUSE_VERSION
+void do_mouse(void);
+#endif
+#endif
+void do_char(char ch);
+int do_backspace(void);
+int do_delete(void);
+int do_tab(void);
+int do_enter(void);
+int do_next_word(void);
+int do_prev_word(void);
+int do_mark(void);
+void wrap_reset(void);
+#ifndef DISABLE_WRAPPING
+int do_wrap(filestruct *inptr);
+#endif
+#ifndef DISABLE_SPELLER
+int do_int_spell_fix(const char *word);
+char *do_int_speller(char *tempfile_name);
+char *do_alt_speller(char *tempfile_name);
+#endif
+int do_spell(void);
+#if !defined(DISABLE_WRAPPING) && !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
+size_t indent_length(const char *line);
+#endif
+#ifndef DISABLE_JUSTIFY
+int justify_format(int changes_allowed, filestruct *line, size_t skip);
+#ifdef HAVE_REGEX_H
+size_t quote_length(const char *line, const regex_t *qreg);
+#else
+size_t quote_length(const char *line);
+#endif
+#ifdef HAVE_REGEX_H
+# define IFREG(a, b) a, b
+#else
+# define IFREG(a, b) a
+#endif
+int quotes_match(const char *a_line, size_t a_quote,
+ IFREG(const char *b_line, const regex_t *qreg));
+size_t indents_match(const char *a_line, size_t a_indent,
+ const char *b_line, size_t b_indent);
+filestruct *backup_lines(filestruct *first_line, size_t par_len,
+ size_t quote_len);
+int breakable(const char *line, int goal);
+int break_line(const char *line, int goal, int force);
+int do_para_operation(int operation);
+#endif /* !DISABLE_JUSTIFY */
+int do_justify(void);
+#ifndef DISABLE_JUSTIFY
+int do_para_begin(void);
+int do_para_end(void);
+#endif
+int do_exit(void);
+void signal_init(void);
+RETSIGTYPE handle_hupterm(int signal);
+RETSIGTYPE do_suspend(int signal);
+RETSIGTYPE do_cont(int signal);
+#ifndef NANO_SMALL
+void handle_sigwinch(int s);
+#endif
+void print_numlock_warning(void);
+#ifndef NANO_SMALL
+void do_toggle(const toggle *which);
+#endif
+int abcd(int input);
+
+/* Public functions in rcfile.c */
+#ifdef ENABLE_NANORC
+void rcfile_error(const char *msg, ...);
+void rcfile_msg(const char *msg, ...);
+char *parse_next_word(char *ptr);
+char *parse_argument(char *ptr);
+#ifdef ENABLE_COLOR
+int colortoint(const char *colorname, int *bright);
+char *parse_next_regex(char *ptr);
+int nregcomp(regex_t *preg, const char *regex, int flags);
+void parse_syntax(char *ptr);
+void parse_colors(char *ptr);
+#endif /* ENABLE_COLOR */
+void parse_rcfile(FILE *rcstream);
+void do_rcfile(void);
+#endif /* ENABLE_NANORC */
+
+/* Public functions in search.c */
+#ifdef HAVE_REGEX_H
+int regexp_init(const char *regexp);
+void regexp_cleanup(void);
+#endif
+void not_found_msg(const char *str);
+void search_abort(void);
+void search_init_globals(void);
+int search_init(int replacing);
+int is_whole_word(int curr_pos, const char *datastr, const char *searchword);
+filestruct *findnextstr(int quiet, int bracket_mode,
+ const filestruct *begin, int beginx,
+ const char *needle);
+int do_search(void);
+int do_research(void);
+void replace_abort(void);
+#ifdef HAVE_REGEX_H
+int replace_regexp(char *string, int create_flag);
+#endif
+char *replace_line(void);
+int do_replace_loop(const char *prevanswer, const filestruct *begin,
+ int *beginx, int wholewords, int *i);
+int do_replace(void);
+int do_gotoline(int line, int save_pos);
+int do_gotoline_void(void);
+#if defined (ENABLE_MULTIBUFFER) || !defined (DISABLE_SPELLER)
+void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant);
+#endif
+int do_find_bracket(void);
+#ifndef NANO_SMALL
+void history_init(void);
+historytype *find_node(historytype *h, char *s);
+void remove_node(historytype *r);
+void insert_node(historytype *h, const char *s);
+void update_history(historyheadtype *h, char *s);
+char *get_history_older(historyheadtype *h);
+char *get_history_newer(historyheadtype *h);
+char *get_history_completion(historyheadtype *h, char *s);
+void free_history(historyheadtype *h);
+#ifdef ENABLE_NANORC
+void load_history(void);
+void save_history(void);
+#endif
+#endif
+
+/* Public functions in utils.c */
+#ifdef BROKEN_REGEXEC
+int regexec_safe(const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags);
+#endif
+int is_cntrl_char(int c);
+int num_of_digits(int n);
+void align(char **strp);
+void null_at(char **data, size_t index);
+void unsunder(char *str, size_t true_len);
+void sunder(char *str);
+#ifndef HAVE_STRCASECMP
+int nstricmp(const char *s1, const char *s2);
+#endif
+#ifndef HAVE_STRNCASECMP
+int nstrnicmp(const char *s1, const char *s2, size_t n);
+#endif
+#ifndef NANO_SMALL
+const char *revstrstr(const char *haystack, const char *needle,
+ const char *rev_start);
+const char *revstristr(const char *haystack, const char *needle,
+ const char *rev_start);
+#endif
+const char *stristr(const char *haystack, const char *needle);
+const char *strstrwrapper(const char *haystack, const char *needle,
+ const char *rev_start, int line_pos);
+void nperror(const char *s);
+void *nmalloc(size_t howmuch);
+void *nrealloc(void *ptr, size_t howmuch);
+char *mallocstrcpy(char *dest, const char *src);
+void new_magicline(void);
+#ifndef DISABLE_TABCOMP
+int check_wildcard_match(const char *text, const char *pattern);
+#endif
+
+/* Public functions in winio.c */
+int get_kbinput(WINDOW *win, int *meta, int rebind_delete);
+char *get_verbatim_kbinput(WINDOW *win, int *kbinput_len);
+int get_ignored_kbinput(WINDOW *win);
+int get_accepted_kbinput(WINDOW *win, int kbinput, int *meta,
+ int rebind_delete);
+int get_ascii_kbinput(WINDOW *win, int kbinput);
+int get_escape_seq_kbinput(WINDOW *win, int kbinput);
+int get_skip_tilde_kbinput(WINDOW *win, int errval, int retval);
+int do_first_line(void);
+int do_last_line(void);
+int xpt(const filestruct *fileptr, int index);
+size_t xplustabs(void);
+size_t actual_x(const filestruct *fileptr, size_t xplus);
+size_t strnlenpt(const char *buf, size_t size);
+size_t strlenpt(const char *buf);
+void blank_bottombars(void);
+void blank_bottomwin(void);
+void blank_edit(void);
+void blank_statusbar(void);
+void blank_statusbar_refresh(void);
+void check_statblank(void);
+void nanoget_repaint(const char *buf, const char *inputbuf, int x);
+int nanogetstr(int allowtabs, const char *buf, const char *def,
+#ifndef NANO_SMALL
+ historyheadtype *history_list,
+#endif
+ const shortcut *s
+#ifndef DISABLE_TABCOMP
+ , int *list
+#endif
+ );
+void set_modified(void);
+void titlebar(const char *path);
+void bottombars(const shortcut *s);
+void onekey(const char *keystroke, const char *desc, int len);
+#ifndef NDEBUG
+int check_linenumbers(const filestruct *fileptr);
+#endif
+int get_page_start(int column);
+void reset_cursor(void);
+void add_marked_sameline(int begin, int end, filestruct *fileptr, int y,
+ int virt_cur_x, int this_page);
+void edit_add(const filestruct *fileptr, int yval, int start
+#ifndef NANO_SMALL
+ , int virt_mark_beginx, int virt_cur_x
+#endif
+ );
+void update_line(filestruct *fileptr, int index);
+void update_cursor(void);
+void center_cursor(void);
+void edit_refresh(void);
+void edit_refresh_clearok(void);
+void edit_update(filestruct *fileptr, topmidbotnone location);
+int statusq(int tabs, const shortcut *s, const char *def,
+#ifndef NANO_SMALL
+ historyheadtype *history_list,
+#endif
+ const char *msg, ...);
+int do_yesno(int all, int leavecursor, const char *msg, ...);
+int total_refresh(void);
+void display_main_list(void);
+void statusbar(const char *msg, ...);
+int do_cursorpos(int constant);
+int do_cursorpos_void(void);
+int line_len(const char *ptr);
+int do_help(void);
+void do_replace_highlight(int highlight_flag, const char *word);
+void fix_editbot(void);
+#ifdef DEBUG
+void dump_buffer(const filestruct *inptr);
+void dump_buffer_reverse(void);
+#endif
+#ifdef NANO_EXTRA
+void do_credits(void);
+#endif
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * rcfile.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+#ifdef ENABLE_NANORC
+
+const static rcoption rcopts[] = {
+#ifndef NANO_SMALL
+ {"autoindent", AUTOINDENT},
+ {"backup", BACKUP_FILE},
+#endif
+ {"const", CONSTUPDATE},
+#ifndef NANO_SMALL
+ {"cut", CUT_TO_END},
+#endif
+#ifndef DISABLE_WRAPJUSTIFY
+ {"fill", 0},
+#endif
+#ifndef NANO_SMALL
+ {"historylog", HISTORYLOG},
+#endif
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ {"mouse", USE_MOUSE},
+#endif
+#ifdef ENABLE_MULTIBUFFER
+ {"multibuffer", MULTIBUFFER},
+#endif
+#ifndef NANO_SMALL
+ {"noconvert", NO_CONVERT},
+#endif
+ {"nofollow", NOFOLLOW_SYMLINKS},
+ {"nohelp", NO_HELP},
+#ifndef DISABLE_WRAPPING
+ {"nowrap", NO_WRAP},
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ {"operatingdir", 0},
+#endif
+ {"preserve", PRESERVE},
+ {"rebinddelete", REBIND_DELETE},
+#ifndef DISABLE_JUSTIFY
+ {"quotestr", 0},
+#endif
+#ifdef HAVE_REGEX_H
+ {"regexp", USE_REGEXP},
+#endif
+#ifndef NANO_SMALL
+ {"smooth", SMOOTHSCROLL},
+#endif
+#ifndef DISABLE_SPELLER
+ {"speller", 0},
+#endif
+ {"suspend", SUSPEND},
+ {"tabsize", 0},
+ {"tempfile", TEMP_OPT},
+ {"view", VIEW_MODE},
+ {NULL, 0}
+};
+
+static int errors = 0;
+static int lineno = 0;
+static char *nanorc;
+
+/* We have an error in some part of the rcfile; put it on stderr and
+ make the user hit return to continue starting up nano. */
+void rcfile_error(const char *msg, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "\n");
+ if (lineno > 0)
+ fprintf(stderr, _("Error in %s on line %d: "), nanorc, lineno);
+
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+ fprintf(stderr, _("\nPress return to continue starting nano\n"));
+
+ while (getchar() != '\n');
+}
+
+/* Just print the error (one of many, perhaps) but don't abort, yet. */
+void rcfile_msg(const char *msg, ...)
+{
+ va_list ap;
+
+ if (!errors) {
+ errors = 1;
+ fprintf(stderr, "\n");
+ }
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+/* Parse the next word from the string. Returns NULL if we hit EOL. */
+char *parse_next_word(char *ptr)
+{
+ while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n' && *ptr != '\0')
+ ptr++;
+
+ if (*ptr == '\0')
+ return NULL;
+
+ /* Null terminate and advance ptr */
+ *ptr++ = 0;
+
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ return ptr;
+}
+
+/* The keywords operatingdir, fill, tabsize, speller, and quotestr take
+ * an argument when set. Among these, operatingdir, speller, and
+ * quotestr have to allow tabs and spaces in the argument. Thus, if the
+ * next word starts with a ", we say it ends with the last " of the line.
+ * Otherwise, the word is interpreted as usual. That is so the arguments
+ * can contain "s too. */
+char *parse_argument(char *ptr)
+{
+ const char *ptr_bak = ptr;
+ char *last_quote = NULL;
+
+ assert(ptr != NULL);
+
+ if (*ptr != '"')
+ return parse_next_word(ptr);
+
+ do {
+ ptr++;
+ if (*ptr == '"')
+ last_quote = ptr;
+ } while (*ptr != '\n' && *ptr != '\0');
+
+ if (last_quote == NULL) {
+ if (*ptr == '\0')
+ ptr = NULL;
+ else
+ *ptr++ = '\0';
+ rcfile_error(_("Argument %s has unterminated \""), ptr_bak);
+ } else {
+ *last_quote = '\0';
+ ptr = last_quote + 1;
+ }
+ if (ptr != NULL)
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+ return ptr;
+}
+
+#ifdef ENABLE_COLOR
+
+int colortoint(const char *colorname, int *bright)
+{
+ int mcolor = 0;
+
+ if (colorname == NULL)
+ return -1;
+
+ if (!strncasecmp(colorname, "bright", 6)) {
+ *bright = 1;
+ colorname += 6;
+ }
+
+ if (!strcasecmp(colorname, "green"))
+ mcolor = COLOR_GREEN;
+ else if (!strcasecmp(colorname, "red"))
+ mcolor = COLOR_RED;
+ else if (!strcasecmp(colorname, "blue"))
+ mcolor = COLOR_BLUE;
+ else if (!strcasecmp(colorname, "white"))
+ mcolor = COLOR_WHITE;
+ else if (!strcasecmp(colorname, "yellow"))
+ mcolor = COLOR_YELLOW;
+ else if (!strcasecmp(colorname, "cyan"))
+ mcolor = COLOR_CYAN;
+ else if (!strcasecmp(colorname, "magenta"))
+ mcolor = COLOR_MAGENTA;
+ else if (!strcasecmp(colorname, "black"))
+ mcolor = COLOR_BLACK;
+ else {
+ rcfile_error(_("Color %s not understood.\n"
+ "Valid colors are \"green\", \"red\", \"blue\", \n"
+ "\"white\", \"yellow\", \"cyan\", \"magenta\" and \n"
+ "\"black\", with the optional prefix \"bright\" \n"
+ "for foreground colors.\n"), colorname);
+ mcolor = -1;
+ }
+ return mcolor;
+}
+
+char *parse_next_regex(char *ptr)
+{
+ while ((*ptr != '"' || (*(ptr + 1) != ' ' && *(ptr + 1) != '\n'))
+ && *ptr != '\n' && *ptr != '\0')
+ ptr++;
+
+ if (*ptr == '\0')
+ return NULL;
+
+ /* Null terminate and advance ptr */
+ *ptr++ = '\0';
+
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ return ptr;
+}
+
+/* Compile the regular expression regex to preg. Returns FALSE on success,
+ TRUE if the expression is invalid. */
+int nregcomp(regex_t *preg, const char *regex, int flags)
+{
+ int rc = regcomp(preg, regex, REG_EXTENDED | flags);
+
+ if (rc != 0) {
+ size_t len = regerror(rc, preg, NULL, 0);
+ char *str = charalloc(len);
+
+ regerror(rc, preg, str, len);
+ rcfile_error(_("Bad regex \"%s\": %s"), regex, str);
+ free(str);
+ }
+ return rc != 0;
+}
+
+void parse_syntax(char *ptr)
+{
+ syntaxtype *tmpsyntax = NULL;
+ const char *fileregptr = NULL, *nameptr = NULL;
+ exttype *endext = NULL;
+ /* The end of the extensions list for this syntax. */
+
+ while (*ptr == ' ')
+ ptr++;
+
+ if (*ptr == '\n' || *ptr == '\0')
+ return;
+
+ if (*ptr != '"') {
+ rcfile_error(_("Regex strings must begin and end with a \" character\n"));
+ return;
+ }
+ ptr++;
+
+ nameptr = ptr;
+ ptr = parse_next_regex(ptr);
+
+ if (ptr == NULL) {
+ rcfile_error(_("Missing syntax name"));
+ return;
+ }
+
+ if (syntaxes == NULL) {
+ syntaxes = (syntaxtype *)nmalloc(sizeof(syntaxtype));
+ tmpsyntax = syntaxes;
+ SET(COLOR_SYNTAX);
+ } else {
+ for (tmpsyntax = syntaxes; tmpsyntax->next != NULL;
+ tmpsyntax = tmpsyntax->next)
+ ;
+ tmpsyntax->next = (syntaxtype *)nmalloc(sizeof(syntaxtype));
+ tmpsyntax = tmpsyntax->next;
+#ifdef DEBUG
+ fprintf(stderr, "Adding new syntax after 1st\n");
+#endif
+ }
+ tmpsyntax->desc = mallocstrcpy(NULL, nameptr);
+ tmpsyntax->color = NULL;
+ tmpsyntax->extensions = NULL;
+ tmpsyntax->next = NULL;
+#ifdef DEBUG
+ fprintf(stderr, "Starting a new syntax type\n");
+ fprintf(stderr, "string val=%s\n", nameptr);
+#endif
+
+ /* Now load in the extensions to their part of the struct */
+ while (*ptr != '\n' && *ptr != '\0') {
+ exttype *newext;
+ /* The new extension structure. */
+
+ while (*ptr != '"' && *ptr != '\n' && *ptr != '\0')
+ ptr++;
+
+ if (*ptr == '\n' || *ptr == '\0')
+ return;
+ ptr++;
+
+ fileregptr = ptr;
+ ptr = parse_next_regex(ptr);
+
+ newext = (exttype *)nmalloc(sizeof(exttype));
+ if (nregcomp(&newext->val, fileregptr, REG_NOSUB))
+ free(newext);
+ else {
+ if (endext == NULL)
+ tmpsyntax->extensions = newext;
+ else
+ endext->next = newext;
+ endext = newext;
+ endext->next = NULL;
+ }
+ }
+}
+
+/* Parse the color stuff into the colorstrings array */
+void parse_colors(char *ptr)
+{
+ int fg, bg, bright = 0;
+ int expectend = 0; /* Do we expect an end= line? */
+ char *fgstr;
+ colortype *tmpcolor = NULL;
+ syntaxtype *tmpsyntax = NULL;
+
+ fgstr = ptr;
+ ptr = parse_next_word(ptr);
+
+ if (ptr == NULL) {
+ rcfile_error(_("Missing color name"));
+ return;
+ }
+
+ if (strstr(fgstr, ",")) {
+ char *bgcolorname;
+ strtok(fgstr, ",");
+ bgcolorname = strtok(NULL, ",");
+ if (!strncasecmp(bgcolorname, "bright", 6)) {
+ rcfile_error(_("Background color %s cannot be bright"), bgcolorname);
+ return;
+ }
+ bg = colortoint(bgcolorname, &bright);
+ } else
+ bg = -1;
+
+ fg = colortoint(fgstr, &bright);
+
+ /* Don't try and parse screwed up fg colors */
+ if (fg == -1)
+ return;
+
+ if (syntaxes == NULL) {
+ rcfile_error(_("Cannot add a color directive without a syntax line"));
+ return;
+ }
+
+ for (tmpsyntax = syntaxes; tmpsyntax->next != NULL;
+ tmpsyntax = tmpsyntax->next)
+ ;
+
+ /* Now the fun part, start adding regexps to individual strings
+ in the colorstrings array, woo! */
+
+ while (*ptr != '\0') {
+ colortype *newcolor;
+ /* The new color structure. */
+ int cancelled = 0;
+ /* The start expression was bad. */
+
+ while (*ptr == ' ')
+ ptr++;
+
+ if (*ptr == '\n' || *ptr == '\0')
+ break;
+
+ if (!strncasecmp(ptr, "start=", 6)) {
+ ptr += 6;
+ expectend = 1;
+ }
+
+ if (*ptr != '"') {
+ rcfile_error(_("Regex strings must begin and end with a \" character\n"));
+ ptr = parse_next_regex(ptr);
+ continue;
+ }
+ ptr++;
+
+ newcolor = (colortype *)nmalloc(sizeof(colortype));
+ fgstr = ptr;
+ ptr = parse_next_regex(ptr);
+ if (nregcomp(&newcolor->start, fgstr, 0)) {
+ free(newcolor);
+ cancelled = 1;
+ } else {
+ newcolor->fg = fg;
+ newcolor->bg = bg;
+ newcolor->bright = bright;
+ newcolor->next = NULL;
+ newcolor->end = NULL;
+
+ if (tmpsyntax->color == NULL) {
+ tmpsyntax->color = newcolor;
+#ifdef DEBUG
+ fprintf(stderr, "Starting a new colorstring for fg %d bg %d\n",
+ fg, bg);
+#endif
+ } else {
+ for (tmpcolor = tmpsyntax->color; tmpcolor->next != NULL;
+ tmpcolor = tmpcolor->next)
+ ;
+#ifdef DEBUG
+ fprintf(stderr, "Adding new entry for fg %d bg %d\n", fg, bg);
+#endif
+ tmpcolor->next = newcolor;
+ }
+ }
+
+ if (expectend) {
+ if (ptr == NULL || strncasecmp(ptr, "end=", 4)) {
+ rcfile_error(_
+ ("\"start=\" requires a corresponding \"end=\""));
+ return;
+ }
+
+ ptr += 4;
+
+ if (*ptr != '"') {
+ rcfile_error(_
+ ("Regex strings must begin and end with a \" character\n"));
+ continue;
+ }
+ ptr++;
+
+ fgstr = ptr;
+ ptr = parse_next_regex(ptr);
+
+ /* If the start regex was invalid, skip past the end regex to
+ * stay in sync. */
+ if (cancelled)
+ continue;
+ newcolor->end = (regex_t *)nmalloc(sizeof(regex_t));
+ if (nregcomp(newcolor->end, fgstr, 0)) {
+ free(newcolor->end);
+ newcolor->end = NULL;
+ }
+ }
+ }
+}
+
+#endif /* ENABLE_COLOR */
+
+/* Parse the RC file, once it has been opened successfully */
+void parse_rcfile(FILE *rcstream)
+{
+ char *buf, *ptr, *keyword, *option;
+ int set = 0, i, j;
+
+ buf = charalloc(1024);
+ while (fgets(buf, 1023, rcstream) != 0) {
+ lineno++;
+ ptr = buf;
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ if (*ptr == '\n' || *ptr == '\0')
+ continue;
+
+ if (*ptr == '#') {
+#ifdef DEBUG
+ fprintf(stderr, "%s: Read a comment\n", "parse_rcfile()");
+#endif
+ continue; /* Skip past commented lines */
+ }
+
+ /* Else skip to the next space */
+ keyword = ptr;
+ ptr = parse_next_word(ptr);
+ if (ptr == NULL)
+ continue;
+
+ /* Else try to parse the keyword */
+ if (!strcasecmp(keyword, "set"))
+ set = 1;
+ else if (!strcasecmp(keyword, "unset"))
+ set = -1;
+#ifdef ENABLE_COLOR
+ else if (!strcasecmp(keyword, "syntax"))
+ parse_syntax(ptr);
+ else if (!strcasecmp(keyword, "color"))
+ parse_colors(ptr);
+#endif /* ENABLE_COLOR */
+ else {
+ rcfile_msg(_("command %s not understood"), keyword);
+ continue;
+ }
+
+ option = ptr;
+ ptr = parse_next_word(ptr);
+ /* We don't care if ptr == NULL, as it should if using proper syntax */
+
+ if (set != 0) {
+ for (i = 0; rcopts[i].name != NULL; i++) {
+ if (!strcasecmp(option, rcopts[i].name)) {
+#ifdef DEBUG
+ fprintf(stderr, "%s: Parsing option %s\n",
+ "parse_rcfile()", rcopts[i].name);
+#endif
+ if (set == 1) {
+ if (!strcasecmp(rcopts[i].name, "tabsize")
+#ifndef DISABLE_OPERATINGDIR
+ || !strcasecmp(rcopts[i].name, "operatingdir")
+#endif
+#ifndef DISABLE_WRAPJUSTIFY
+ || !strcasecmp(rcopts[i].name, "fill")
+#endif
+#ifndef DISABLE_JUSTIFY
+ || !strcasecmp(rcopts[i].name, "quotestr")
+#endif
+#ifndef DISABLE_SPELLER
+ || !strcasecmp(rcopts[i].name, "speller")
+#endif
+ ) {
+ if (*ptr == '\n' || *ptr == '\0') {
+ rcfile_error(_
+ ("Option %s requires an argument"),
+ rcopts[i].name);
+ continue;
+ }
+ option = ptr;
+ if (*option == '"')
+ option++;
+ ptr = parse_argument(ptr);
+#ifdef DEBUG
+ fprintf(stderr, "option = %s\n", option);
+#endif
+#ifndef DISABLE_OPERATINGDIR
+ if (!strcasecmp(rcopts[i].name, "operatingdir"))
+ operating_dir = mallocstrcpy(NULL, option);
+ else
+#endif
+#ifndef DISABLE_WRAPJUSTIFY
+ if (!strcasecmp(rcopts[i].name, "fill")) {
+ char *first_error;
+
+ /* Using strtol() instead of atoi() lets
+ * us accept 0 while checking other
+ * errors. */
+ j = (int)strtol(option, &first_error, 10);
+ if (errno == ERANGE || *option == '\0' || *first_error != '\0')
+ rcfile_error(_("Requested fill size %d invalid"),
+ j);
+ else
+ wrap_at = j;
+ } else
+#endif
+#ifndef DISABLE_JUSTIFY
+ if (!strcasecmp(rcopts[i].name, "quotestr"))
+ quotestr = mallocstrcpy(NULL, option);
+ else
+#endif
+#ifndef DISABLE_SPELLER
+ if (!strcasecmp(rcopts[i].name, "speller"))
+ alt_speller = mallocstrcpy(NULL, option);
+ else
+#endif
+ {
+ char *first_error;
+
+ /* Using strtol instead of atoi lets us
+ * accept 0 while checking other
+ * errors. */
+ j = (int)strtol(option, &first_error, 10);
+ if (errno == ERANGE || *option == '\0' || *first_error != '\0')
+ rcfile_error(_("Requested tab size %d invalid"),
+ j);
+ else
+ tabsize = j;
+ }
+ } else
+ SET(rcopts[i].flag);
+#ifdef DEBUG
+ fprintf(stderr, "set flag %d!\n",
+ rcopts[i].flag);
+#endif
+ } else {
+ UNSET(rcopts[i].flag);
+#ifdef DEBUG
+ fprintf(stderr, "unset flag %d!\n",
+ rcopts[i].flag);
+#endif
+ }
+ }
+ }
+ }
+ }
+ free(buf);
+ if (errors)
+ rcfile_error(_("Errors found in .nanorc file"));
+
+ return;
+}
+
+/* The main rc file function, tries to open the rc file */
+void do_rcfile(void)
+{
+ FILE *rcstream;
+ const struct passwd *userage;
+ uid_t euid = geteuid();
+ char *homenv = getenv("HOME");
+
+#ifdef SYSCONFDIR
+ assert(sizeof(SYSCONFDIR) == strlen(SYSCONFDIR) + 1);
+ nanorc = charalloc(sizeof(SYSCONFDIR) + 7);
+ sprintf(nanorc, "%s/nanorc", SYSCONFDIR);
+ /* Try to open system nanorc */
+ if ((rcstream = fopen(nanorc, "r")) != NULL) {
+ /* Parse it! */
+ parse_rcfile(rcstream);
+ fclose(rcstream);
+ }
+#endif
+
+ lineno = 0;
+
+ /* Rely on $HOME, fall back on getpwuid() */
+ if (homenv != NULL) {
+ nanorc = charealloc(nanorc, strlen(homenv) + 10);
+ sprintf(nanorc, "%s/.nanorc", homenv);
+ } else {
+ userage = getpwuid(euid);
+ endpwent();
+
+ if (userage == NULL) {
+ rcfile_error(_("I can't find my home directory! Wah!"));
+ SET(NO_RCFILE);
+ } else {
+ nanorc = charealloc(nanorc, strlen(userage->pw_dir) + 9);
+ sprintf(nanorc, "%s/.nanorc", userage->pw_dir);
+
+ }
+ }
+
+ if (!ISSET(NO_RCFILE)) {
+
+#if defined(DISABLE_ROOTWRAP) && !defined(DISABLE_WRAPPING)
+ /* If we've already read $SYSCONFDIR/nanorc (if it's there), we're
+ root, and --disable-wrapping-as-root is used, turn wrapping off */
+ if (euid == 0)
+ SET(NO_WRAP);
+#endif
+ if ((rcstream = fopen(nanorc, "r")) == NULL) {
+ /* Don't complain about the file not existing */
+ if (errno != ENOENT) {
+ rcfile_error(_("Unable to open ~/.nanorc file, %s"),
+ strerror(errno));
+ SET(NO_RCFILE);
+ }
+ } else {
+ parse_rcfile(rcstream);
+ fclose(rcstream);
+ }
+ }
+ lineno = 0;
+
+ free(nanorc);
+#ifdef ENABLE_COLOR
+ set_colorpairs();
+#endif
+}
+
+#endif /* ENABLE_NANORC */
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * search.c *
+ * *
+ * Copyright (C) 2000-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+/* Regular expression helper functions */
+
+#ifdef HAVE_REGEX_H
+int regexp_init(const char *regexp)
+{
+ /* Hmm, perhaps we should check for whether regcomp returns successfully */
+ if (regcomp(&search_regexp, regexp, (ISSET(CASE_SENSITIVE) ? 0 : REG_ICASE)
+ | REG_EXTENDED) != 0)
+ return 0;
+
+ SET(REGEXP_COMPILED);
+ return 1;
+}
+
+void regexp_cleanup(void)
+{
+ UNSET(REGEXP_COMPILED);
+ regfree(&search_regexp);
+}
+#endif
+
+void not_found_msg(const char *str)
+{
+ if (strlen(str) <= COLS / 2)
+ statusbar(_("\"%s\" not found"), str);
+ else {
+ char *foo = mallocstrcpy(NULL, str);
+
+ foo[COLS / 2] = '\0';
+ statusbar(_("\"%s...\" not found"), foo);
+ free(foo);
+ }
+}
+
+void search_abort(void)
+{
+ display_main_list();
+ wrefresh(bottomwin);
+ if (ISSET(MARK_ISSET))
+ edit_refresh_clearok();
+
+#ifdef HAVE_REGEX_H
+ if (ISSET(REGEXP_COMPILED))
+ regexp_cleanup();
+#endif
+}
+
+void search_init_globals(void)
+{
+ if (last_search == NULL) {
+ last_search = charalloc(1);
+ last_search[0] = '\0';
+ }
+ if (last_replace == NULL) {
+ last_replace = charalloc(1);
+ last_replace[0] = '\0';
+ }
+}
+
+/* Set up the system variables for a search or replace. Return -1 on
+ * abort, 0 on success, and 1 on rerun calling program. Return -2 to
+ * run opposite program (search -> replace, replace -> search).
+ *
+ * replacing = 1 if we call from do_replace(), 0 if called from
+ * do_search(). */
+int search_init(int replacing)
+{
+ int i = 0;
+ char *buf;
+ static char *backupstring = NULL;
+#ifdef HAVE_REGEX_H
+ const char *regex_error = _("Invalid regex \"%s\"");
+#endif /* HAVE_REGEX_H */
+
+ search_init_globals();
+
+ if (backupstring == NULL)
+ backupstring = mallocstrcpy(backupstring, "");
+
+#ifndef NANO_SMALL
+ search_history.current = (historytype *)&search_history.next;
+#endif
+
+ if (last_search[0] != '\0') {
+ buf = charalloc(COLS / 3 + 7);
+ /* We use COLS / 3 here because we need to see more on the line */
+ sprintf(buf, " [%.*s%s]", COLS / 3, last_search,
+ strlen(last_search) > COLS / 3 ? "..." : "");
+ } else {
+ buf = charalloc(1);
+ buf[0] = '\0';
+ }
+
+ /* This is now one simple call. It just does a lot */
+ i = statusq(0, replacing ? replace_list : whereis_list, backupstring,
+#ifndef NANO_SMALL
+ &search_history,
+#endif
+ "%s%s%s%s%s%s",
+ _("Search"),
+
+ /* This string is just a modifier for the search prompt,
+ no grammar is implied */
+ ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : "",
+
+ /* This string is just a modifier for the search prompt,
+ no grammar is implied */
+ ISSET(USE_REGEXP) ? _(" [Regexp]") : "",
+
+ /* This string is just a modifier for the search prompt,
+ no grammar is implied */
+ ISSET(REVERSE_SEARCH) ? _(" [Backwards]") : "",
+
+ replacing ? _(" (to replace)") : "",
+ buf);
+
+ /* Release buf now that we don't need it anymore */
+ free(buf);
+
+ /* Cancel any search, or just return with no previous search */
+ if (i == -1 || (i < 0 && last_search[0] == '\0')) {
+ statusbar(_("Search Cancelled"));
+ reset_cursor();
+ free(backupstring);
+ backupstring = NULL;
+#ifndef NANO_SMALL
+ search_history.current = search_history.next;
+#endif
+ return -1;
+ } else {
+ switch (i) {
+ case -2: /* Same string */
+#ifdef HAVE_REGEX_H
+ if (ISSET(USE_REGEXP))
+ /* If answer is "", use last_search! */
+ if (regexp_init(last_search) == 0) {
+ statusbar(regex_error, last_search);
+ reset_cursor();
+ free(backupstring);
+ backupstring = NULL;
+ return -3;
+ }
+#endif
+ break;
+ case 0: /* They entered something new */
+#ifdef HAVE_REGEX_H
+ if (ISSET(USE_REGEXP))
+ if (regexp_init(answer) == 0) {
+ statusbar(regex_error, answer);
+ reset_cursor();
+ free(backupstring);
+ backupstring = NULL;
+#ifndef NANO_SMALL
+ search_history.current = search_history.next;
+#endif
+ return -3;
+ }
+#endif
+ free(backupstring);
+ backupstring = NULL;
+ last_replace[0] = '\0';
+ break;
+#ifndef NANO_SMALL
+ case TOGGLE_CASE_KEY:
+ TOGGLE(CASE_SENSITIVE);
+ backupstring = mallocstrcpy(backupstring, answer);
+ return 1;
+ case TOGGLE_BACKWARDS_KEY:
+ TOGGLE(REVERSE_SEARCH);
+ backupstring = mallocstrcpy(backupstring, answer);
+ return 1;
+#ifdef HAVE_REGEX_H
+ case TOGGLE_REGEXP_KEY:
+ TOGGLE(USE_REGEXP);
+ backupstring = mallocstrcpy(backupstring, answer);
+ return 1;
+#endif
+#endif /* !NANO_SMALL */
+ case NANO_OTHERSEARCH_KEY:
+ backupstring = mallocstrcpy(backupstring, answer);
+ return -2; /* Call the opposite search function */
+ case NANO_FROMSEARCHTOGOTO_KEY:
+ free(backupstring);
+ backupstring = NULL;
+#ifndef NANO_SMALL
+ search_history.current = search_history.next;
+#endif
+ i = (int)strtol(answer, &buf, 10); /* Just testing answer here */
+ if (!(errno == ERANGE || *answer == '\0' || *buf != '\0'))
+ do_gotoline(-1, 0);
+ else
+ do_gotoline_void();
+ return -3;
+ default:
+ do_early_abort();
+ free(backupstring);
+ backupstring = NULL;
+ return -3;
+ }
+ }
+ return 0;
+}
+
+int is_whole_word(int curr_pos, const char *datastr, const char *searchword)
+{
+ size_t sln = curr_pos + strlen(searchword);
+
+ /* start of line or previous character not a letter and end of line
+ or next character not a letter */
+ return (curr_pos < 1 || !isalpha((int)datastr[curr_pos - 1])) &&
+ (sln == strlen(datastr) || !isalpha((int)datastr[sln]));
+}
+
+filestruct *findnextstr(int quiet, int bracket_mode,
+ const filestruct *begin, int beginx,
+ const char *needle)
+{
+ filestruct *fileptr = current;
+ const char *searchstr, *rev_start = NULL, *found = NULL;
+ int current_x_find = 0;
+
+ search_offscreen = 0;
+
+ if (!ISSET(REVERSE_SEARCH)) { /* forward search */
+ /* Argh, current_x is set to -1 by nano.c:do_int_spell_fix(), and
+ * strlen returns size_t, which is unsigned. */
+ assert(current_x < 0 || current_x <= strlen(fileptr->data));
+ current_x_find = current_x;
+ if (current_x_find < 0 || fileptr->data[current_x_find] != '\0')
+ current_x_find++;
+
+ searchstr = &fileptr->data[current_x_find];
+
+ /* Look for needle in searchstr */
+ while ((found = strstrwrapper(searchstr, needle, rev_start, current_x_find)) == NULL) {
+
+ /* finished processing file, get out */
+ if (search_last_line) {
+ if (!quiet)
+ not_found_msg(needle);
+ update_line(fileptr, current_x);
+ return NULL;
+ }
+
+ update_line(fileptr, 0);
+
+ /* reset current_x_find between lines */
+ current_x_find = 0;
+
+ fileptr = fileptr->next;
+
+ if (fileptr == editbot)
+ search_offscreen = 1;
+
+ /* EOF reached?, wrap around once */
+ if (fileptr == NULL) {
+ /* don't wrap if looking for bracket match */
+ if (bracket_mode)
+ return NULL;
+ fileptr = fileage;
+ search_offscreen = 1;
+ if (!quiet)
+ statusbar(_("Search Wrapped"));
+ }
+
+ /* Original start line reached */
+ if (fileptr == begin)
+ search_last_line = 1;
+
+ searchstr = fileptr->data;
+ }
+
+ /* We found an instance */
+ current_x_find = found - fileptr->data;
+ /* Ensure we haven't wrapped around again! */
+ if (search_last_line && current_x_find > beginx) {
+ if (!quiet)
+ not_found_msg(needle);
+ return NULL;
+ }
+ }
+#ifndef NANO_SMALL
+ else { /* reverse search */
+ current_x_find = current_x - 1;
+ /* Make sure we haven't passed the beginning of the string */
+ rev_start = &fileptr->data[current_x_find];
+ searchstr = fileptr->data;
+
+ /* Look for needle in searchstr */
+ while ((found = strstrwrapper(searchstr, needle, rev_start, current_x_find)) == NULL) {
+ /* finished processing file, get out */
+ if (search_last_line) {
+ if (!quiet)
+ not_found_msg(needle);
+ return NULL;
+ }
+
+ update_line(fileptr, 0);
+
+ /* reset current_x_find between lines */
+ current_x_find = 0;
+
+ fileptr = fileptr->prev;
+
+ if (fileptr == edittop->prev)
+ search_offscreen = 1;
+
+ /* SOF reached?, wrap around once */
+/* ? */ if (fileptr == NULL) {
+ if (bracket_mode)
+ return NULL;
+ fileptr = filebot;
+ search_offscreen = 1;
+ if (!quiet)
+ statusbar(_("Search Wrapped"));
+ }
+ /* Original start line reached */
+ if (fileptr == begin)
+ search_last_line = 1;
+
+ searchstr = fileptr->data;
+ rev_start = fileptr->data + strlen(fileptr->data);
+ }
+
+ /* We found an instance */
+ current_x_find = found - fileptr->data;
+ /* Ensure we haven't wrapped around again! */
+ if ((search_last_line) && (current_x_find < beginx)) {
+ if (!quiet)
+ not_found_msg(needle);
+ return NULL;
+ }
+ }
+#endif /* !NANO_SMALL */
+
+ /* Set globals now that we are sure we found something */
+ current = fileptr;
+ current_x = current_x_find;
+
+ if (!bracket_mode) {
+ update_line(current, current_x);
+ placewewant = xplustabs();
+ reset_cursor();
+ }
+ return fileptr;
+}
+
+/* Search for a string. */
+int do_search(void)
+{
+ int i;
+ filestruct *fileptr = current, *didfind;
+ int fileptr_x = current_x;
+
+ wrap_reset();
+ i = search_init(0);
+ switch (i) {
+ case -1:
+ current = fileptr;
+ search_abort();
+ return 0;
+ case -3:
+ search_abort();
+ return 0;
+ case -2:
+ do_replace();
+ return 0;
+ case 1:
+ do_search();
+ search_abort();
+ return 1;
+ }
+
+ /* If answer is now "", copy last_search into answer... */
+ if (answer[0] == '\0')
+ answer = mallocstrcpy(answer, last_search);
+ else
+ last_search = mallocstrcpy(last_search, answer);
+
+#ifndef NANO_SMALL
+ /* add this search string to the search history list */
+ if (answer[0] != '\0')
+ update_history(&search_history, answer);
+#endif /* !NANO_SMALL */
+
+ search_last_line = 0;
+ didfind = findnextstr(FALSE, FALSE, current, current_x, answer);
+
+ if (fileptr == current && fileptr_x == current_x && didfind != NULL)
+ statusbar(_("This is the only occurrence"));
+ else if (current->lineno <= edittop->lineno
+ || current->lineno >= editbot->lineno)
+ edit_update(current, CENTER);
+
+ search_abort();
+
+ return 1;
+}
+
+/* Search for the next string without prompting. */
+int do_research(void)
+{
+ filestruct *fileptr = current, *didfind;
+ int fileptr_x = current_x;
+#ifdef HAVE_REGEX_H
+ const char *regex_error = _("Invalid regex \"%s\"");
+#endif /* HAVE_REGEX_H */
+
+ wrap_reset();
+ search_init_globals();
+
+ if (last_search[0] != '\0') {
+
+#ifdef HAVE_REGEX_H
+ if (ISSET(USE_REGEXP))
+ if (regexp_init(last_search) == 0) {
+ statusbar(regex_error, last_search);
+ reset_cursor();
+ return -3;
+ }
+#endif
+
+ search_last_line = 0;
+ didfind = findnextstr(FALSE, FALSE, current, current_x, last_search);
+
+ if (fileptr == current && fileptr_x == current_x && didfind != NULL)
+ statusbar(_("This is the only occurrence"));
+ else if (current->lineno <= edittop->lineno
+ || current->lineno >= editbot->lineno)
+ edit_update(current, CENTER);
+
+ } else
+ statusbar(_("No current search pattern"));
+
+ search_abort();
+
+ return 1;
+}
+
+void replace_abort(void)
+{
+ /* Identical to search_abort, so we'll call it here. If it
+ does something different later, we can change it back. For now
+ it's just a waste to duplicate code */
+ search_abort();
+ placewewant = xplustabs();
+}
+
+#ifdef HAVE_REGEX_H
+int replace_regexp(char *string, int create_flag)
+{
+ /* Split personality here - if create_flag is NULL, just calculate
+ * the size of the replacement line (necessary because of
+ * subexpressions like \1 \2 \3 in the replaced text). */
+
+ char *c;
+ int new_size = strlen(current->data) + 1;
+ int search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
+
+ new_size -= search_match_count;
+
+ /* Iterate through the replacement text to handle subexpression
+ * replacement using \1, \2, \3, etc. */
+
+ c = last_replace;
+ while (*c != '\0') {
+ if (*c != '\\') {
+ if (create_flag)
+ *string++ = *c;
+ c++;
+ new_size++;
+ } else {
+ int num = (int) *(c + 1) - (int) '0';
+ if (num >= 1 && num <= 9) {
+
+ int i = regmatches[num].rm_eo - regmatches[num].rm_so;
+
+ if (num > search_regexp.re_nsub) {
+ /* Ugh, they specified a subexpression that doesn't
+ * exist. */
+ return -1;
+ }
+
+ /* Skip over the replacement expression */
+ c += 2;
+
+ /* But add the length of the subexpression to new_size */
+ new_size += i;
+
+ /* And if create_flag is set, append the result of the
+ * subexpression match to the new line */
+ if (create_flag) {
+ strncpy(string, current->data + current_x +
+ regmatches[num].rm_so, i);
+ string += i;
+ }
+
+ } else {
+ if (create_flag)
+ *string++ = *c;
+ c++;
+ new_size++;
+ }
+ }
+ }
+
+ if (create_flag)
+ *string = '\0';
+
+ return new_size;
+}
+#endif
+
+char *replace_line(void)
+{
+ char *copy, *tmp;
+ int new_line_size;
+ int search_match_count;
+
+ /* Calculate size of new line */
+#ifdef HAVE_REGEX_H
+ if (ISSET(USE_REGEXP)) {
+ search_match_count = regmatches[0].rm_eo - regmatches[0].rm_so;
+ new_line_size = replace_regexp(NULL, 0);
+ /* If they specified an invalid subexpression in the replace
+ * text, return NULL, indicating an error */
+ if (new_line_size < 0)
+ return NULL;
+ } else {
+#else
+ {
+#endif
+ search_match_count = strlen(last_search);
+ new_line_size = strlen(current->data) - strlen(last_search) +
+ strlen(last_replace) + 1;
+ }
+
+ /* Create buffer */
+ copy = charalloc(new_line_size);
+
+ /* Head of Original Line */
+ strncpy(copy, current->data, current_x);
+ copy[current_x] = '\0';
+
+ /* Replacement Text */
+ if (!ISSET(USE_REGEXP))
+ strcat(copy, last_replace);
+#ifdef HAVE_REGEX_H
+ else
+ replace_regexp(copy + current_x, 1);
+#endif
+
+ /* The tail of the original line */
+
+ /* This may expose other bugs, because it no longer goes through
+ * each character in the string and tests for string goodness. But
+ * because we can assume the invariant that current->data is less
+ * than current_x + strlen(last_search) long, this should be safe.
+ * Or it will expose bugs ;-) */
+ tmp = current->data + current_x + search_match_count;
+ strcat(copy, tmp);
+
+ return copy;
+}
+
+/* Step through each replace word and prompt user before replacing
+ * word. Return -1 if the string to replace isn't found at all.
+ * Otherwise, return the number of replacements made. */
+int do_replace_loop(const char *prevanswer, const filestruct *begin,
+ int *beginx, int wholewords, int *i)
+{
+ int replaceall = 0, numreplaced = -1;
+
+ filestruct *fileptr = NULL;
+ char *copy;
+
+ switch (*i) {
+ case -1: /* Aborted enter */
+ if (last_replace[0] != '\0')
+ answer = mallocstrcpy(answer, last_replace);
+ statusbar(_("Replace Cancelled"));
+ replace_abort();
+ return 0;
+ case 0: /* They actually entered something */
+ break;
+ default:
+ if (*i != -2) { /* First page, last page, for example, could
+ get here */
+ do_early_abort();
+ replace_abort();
+ return 0;
+ }
+ }
+
+ last_replace = mallocstrcpy(last_replace, answer);
+ while (1) {
+ /* Sweet optimization by Rocco here */
+ fileptr = findnextstr(fileptr || replaceall || search_last_line,
+ FALSE, begin, *beginx, prevanswer);
+
+ if (current->lineno <= edittop->lineno
+ || current->lineno >= editbot->lineno)
+ edit_update(current, CENTER);
+
+ /* No more matches. Done! */
+ if (fileptr == NULL)
+ break;
+
+ /* Make sure only whole words are found */
+ if (wholewords && !is_whole_word(current_x, fileptr->data, prevanswer))
+ continue;
+
+ /* If we're here, we've found the search string */
+ if (numreplaced == -1)
+ numreplaced = 0;
+
+ if (!replaceall) {
+ curs_set(0);
+ do_replace_highlight(TRUE, prevanswer);
+
+ *i = do_yesno(1, 1, _("Replace this instance?"));
+
+ do_replace_highlight(FALSE, prevanswer);
+ curs_set(1);
+ }
+
+ if (*i > 0 || replaceall) { /* Yes, replace it!!!! */
+ long length_change;
+ size_t match_len;
+
+ if (*i == 2)
+ replaceall = 1;
+
+ copy = replace_line();
+ if (copy == NULL) {
+ statusbar(_("Replace failed: unknown subexpression!"));
+ replace_abort();
+ return 0;
+ }
+
+ length_change = strlen(copy) - strlen(current->data);
+
+#ifdef HAVE_REGEX_H
+ if (ISSET(USE_REGEXP))
+ match_len = regmatches[0].rm_eo - regmatches[0].rm_so;
+ else
+#endif
+ match_len = strlen(prevanswer);
+
+#ifndef NANO_SMALL
+ if (current == mark_beginbuf && mark_beginx > current_x) {
+ if (mark_beginx < current_x + match_len)
+ mark_beginx = current_x;
+ else
+ mark_beginx += length_change;
+ }
+#endif
+
+ assert(0 <= match_len + length_change);
+ if (current == begin && current_x <= *beginx) {
+ if (*beginx < current_x + match_len)
+ *beginx = current_x + match_len;
+ *beginx += length_change;
+ }
+
+ /* Set the cursor at the last character of the replacement
+ * text, so searching will resume after the replacement text.
+ * Note that current_x might be set to -1 here. */
+#ifndef NANO_SMALL
+ if (!ISSET(REVERSE_SEARCH))
+#endif
+ current_x += match_len + length_change - 1;
+
+ /* Cleanup */
+ totsize += length_change;
+ free(current->data);
+ current->data = copy;
+
+ edit_refresh();
+ set_modified();
+ numreplaced++;
+ } else if (*i == -1) /* Abort, else do nothing and continue
+ loop */
+ break;
+ }
+
+ /* If text has been added to the magicline, make a new magicline. */
+ if (filebot->data[0] != '\0')
+ new_magicline();
+
+ return numreplaced;
+}
+
+/* Replace a string. */
+int do_replace(void)
+{
+ int i, numreplaced, beginx;
+ filestruct *begin;
+ char *prevanswer = NULL;
+
+ if (ISSET(VIEW_MODE)) {
+ print_view_warning();
+ replace_abort();
+ return 0;
+ }
+
+ i = search_init(1);
+ switch (i) {
+ case -1:
+ statusbar(_("Replace Cancelled"));
+ replace_abort();
+ return 0;
+ case 1:
+ do_replace();
+ return 1;
+ case -2:
+ do_search();
+ return 0;
+ case -3:
+ replace_abort();
+ return 0;
+ }
+
+#ifndef NANO_SMALL
+ if (answer[0] != '\0')
+ update_history(&search_history, answer);
+#endif /* !NANO_SMALL */
+
+ /* If answer is now == "", copy last_search into answer
+ (and prevanswer)... */
+ if (answer[0] == '\0')
+ answer = mallocstrcpy(answer, last_search);
+ else
+ last_search = mallocstrcpy(last_search, answer);
+
+ prevanswer = mallocstrcpy(prevanswer, last_search);
+
+#ifndef NANO_SMALL
+ replace_history.current = (historytype *)&replace_history.next;
+ last_replace = mallocstrcpy(last_replace, "");
+#endif
+
+ i = statusq(0, replace_list_2, last_replace,
+#ifndef NANO_SMALL
+ &replace_history,
+#endif
+ _("Replace with"));
+
+#ifndef NANO_SMALL
+ if (i == 0 && answer[0] != '\0')
+ update_history(&replace_history, answer);
+#endif /* !NANO_SMALL */
+
+ begin = current;
+ beginx = current_x;
+ search_last_line = 0;
+
+ numreplaced = do_replace_loop(prevanswer, begin, &beginx, FALSE, &i);
+
+ /* restore where we were */
+ current = begin;
+ current_x = beginx;
+ renumber_all();
+ edit_update(current, CENTER);
+
+ if (numreplaced >= 0)
+ statusbar(P_("Replaced %d occurrence", "Replaced %d occurrences",
+ numreplaced), numreplaced);
+ else
+ not_found_msg(prevanswer);
+
+ free(prevanswer);
+ replace_abort();
+ return 1;
+}
+
+int do_gotoline(int line, int save_pos)
+{
+ if (line <= 0) { /* Ask for it */
+ int st = statusq(FALSE, goto_list, line != 0 ? answer : "",
+#ifndef NANO_SMALL
+ NULL,
+#endif
+ _("Enter line number"));
+
+ /* Cancel, or Enter with blank string. */
+ if (st == -1 || st == -2)
+ statusbar(_("Aborted"));
+ if (st != 0) {
+ display_main_list();
+ return 0;
+ }
+
+ line = atoi(answer);
+
+ /* Bounds check */
+ if (line <= 0) {
+ statusbar(_("Come on, be reasonable"));
+ display_main_list();
+ return 0;
+ }
+ }
+
+ for (current = fileage; current->next != NULL && line > 1; line--)
+ current = current->next;
+
+ current_x = 0;
+
+ /* if save_pos is nonzero, don't change the cursor position when
+ updating the edit window */
+ if (save_pos)
+ edit_update(current, NONE);
+ else
+ edit_update(current, CENTER);
+ placewewant = 0;
+ display_main_list();
+ return 1;
+}
+
+int do_gotoline_void(void)
+{
+ return do_gotoline(0, 0);
+}
+
+#if defined(ENABLE_MULTIBUFFER) || !defined(DISABLE_SPELLER)
+void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant)
+{
+ /* since do_gotoline() resets the x-coordinate but not the
+ y-coordinate, set the coordinates up this way */
+ current_y = pos_y;
+ do_gotoline(line, 1);
+
+ /* make sure that the x-coordinate is sane here */
+ if (pos_x > strlen(current->data))
+ pos_x = strlen(current->data);
+
+ /* set the rest of the coordinates up */
+ current_x = pos_x;
+ placewewant = pos_placewewant;
+ update_line(current, pos_x);
+}
+#endif
+
+#if !defined(NANO_SMALL) && defined(HAVE_REGEX_H)
+int do_find_bracket(void)
+{
+ char ch_under_cursor, wanted_ch;
+ const char *pos, *brackets = "([{<>}])";
+ char regexp_pat[] = "[ ]";
+ int offset, have_search_offscreen = 0, flagsave, current_x_save, count = 1;
+ filestruct *current_save;
+
+ ch_under_cursor = current->data[current_x];
+
+/* if ((!(pos = strchr(brackets, ch_under_cursor))) || (!((offset = pos - brackets) < 8))) { */
+
+ if (((pos = strchr(brackets, ch_under_cursor)) == NULL) || (((offset = pos - brackets) < 8) == 0)) {
+ statusbar(_("Not a bracket"));
+ return 1;
+ }
+
+ blank_statusbar_refresh();
+
+ wanted_ch = *(brackets + ((strlen(brackets) - (offset + 1))));
+
+ current_x_save = current_x;
+ current_save = current;
+ flagsave = flags;
+ SET(USE_REGEXP);
+
+/* apparent near redundancy with regexp_pat[] here is needed, [][] works, [[]] doesn't */
+
+ if (offset < (strlen(brackets) / 2)) { /* on a left bracket */
+ regexp_pat[1] = wanted_ch;
+ regexp_pat[2] = ch_under_cursor;
+ UNSET(REVERSE_SEARCH);
+ } else { /* on a right bracket */
+ regexp_pat[1] = ch_under_cursor;
+ regexp_pat[2] = wanted_ch;
+ SET(REVERSE_SEARCH);
+ }
+
+ regexp_init(regexp_pat);
+
+ while (1) {
+ search_last_line = 0;
+ if (findnextstr(1, 1, current, current_x, regexp_pat) != NULL) {
+ have_search_offscreen |= search_offscreen;
+
+ /* found identical bracket */
+ if (current->data[current_x] == ch_under_cursor)
+ count++;
+ else {
+
+ /* found complementary bracket */
+ if (!(--count)) {
+ if (have_search_offscreen)
+ edit_update(current, CENTER);
+ else
+ update_line(current, current_x);
+ placewewant = xplustabs();
+ reset_cursor();
+ break;
+ }
+ }
+ } else {
+
+ /* didn't find either left or right bracket */
+ statusbar(_("No matching bracket"));
+ current_x = current_x_save;
+ current = current_save;
+ update_line(current, current_x);
+ break;
+ }
+ }
+
+ if (ISSET(REGEXP_COMPILED))
+ regexp_cleanup();
+ flags = flagsave;
+ return 0;
+}
+#endif
+
+#ifndef NANO_SMALL
+/*
+ * search and replace history list support functions
+ */
+
+/* initialize search and replace history lists */
+void history_init(void)
+{
+ search_history.next = (historytype *)&search_history.prev;
+ search_history.prev = NULL;
+ search_history.tail = (historytype *)&search_history.next;
+ search_history.current = search_history.next;
+ search_history.count = 0;
+ search_history.len = 0;
+
+ replace_history.next = (historytype *)&replace_history.prev;
+ replace_history.prev = NULL;
+ replace_history.tail = (historytype *)&replace_history.next;
+ replace_history.current = replace_history.next;
+ replace_history.count = 0;
+ replace_history.len = 0;
+}
+
+/* find first node containing string *s in history list *h */
+historytype *find_node(historytype *h, char *s)
+{
+ for (; h->next != NULL; h = h->next)
+ if (strcmp(s, h->data) == 0)
+ return h;
+ return NULL;
+}
+
+/* remove node *r */
+void remove_node(historytype *r)
+{
+ r->prev->next = r->next;
+ r->next->prev = r->prev;
+ free(r->data);
+ free(r);
+}
+
+/* add a node after node *h */
+void insert_node(historytype *h, const char *s)
+{
+ historytype *a;
+
+ a = (historytype *)nmalloc(sizeof(historytype));
+ a->next = h->next;
+ a->prev = h->next->prev;
+ h->next->prev = a;
+ h->next = a;
+ a->data = mallocstrcpy(NULL, s);
+}
+
+/* update history list */
+void update_history(historyheadtype *h, char *s)
+{
+ historytype *p;
+
+ if ((p = find_node(h->next, s)) != NULL) {
+ if (p == h->next) /* catch delete and re-insert of
+ same string in 1st node */
+ goto up_hs;
+ remove_node(p); /* delete identical older string */
+ h->count--;
+ }
+ if (h->count == MAX_SEARCH_HISTORY) { /* list 'full', delete oldest */
+ remove_node(h->tail);
+ h->count--;
+ }
+ insert_node((historytype *)h, s);
+ h->count++;
+ SET(HISTORY_CHANGED);
+ up_hs:
+ h->current = h->next;
+}
+
+/* return a pointer to either the next older history or NULL if no more */
+char *get_history_older(historyheadtype *h)
+{
+ if (h->current->next != NULL) { /* any older entries? */
+ h->current = h->current->next; /* yes */
+ return h->current->data; /* return it */
+ }
+ return NULL; /* end of list */
+}
+
+char *get_history_newer(historyheadtype *h)
+{
+ if (h->current->prev != NULL) {
+ h->current = h->current->prev;
+ if (h->current->prev != NULL)
+ return h->current->data;
+ }
+ return NULL;
+}
+
+/* get a completion */
+char *get_history_completion(historyheadtype *h, char *s)
+{
+ historytype *p;
+
+ for (p = h->current->next; p->next != NULL; p = p->next) {
+ if (strncmp(s, p->data, h->len) == 0 && strlen(p->data) != h->len) {
+ h->current = p;
+ return p->data;
+ }
+ }
+ h->current = (historytype*)h;
+ null_at(&s, h->len);
+ return s;
+}
+
+/* free a history list */
+void free_history(historyheadtype *h)
+{
+ historytype *p, *n;
+
+ for (p = h->next; (n = p->next); p = n)
+ remove_node(p);
+}
+
+/* end of history support functions */
+#endif /* !NANO_SMALL */
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * utils.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+#ifdef BROKEN_REGEXEC
+#undef regexec
+int regexec_safe(const regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags)
+{
+ if (string != NULL && *string != '\0')
+ return regexec(preg, string, nmatch, pmatch, eflags);
+ return REG_NOMATCH;
+}
+#define regexec(preg, string, nmatch, pmatch, eflags) regexec_safe(preg, string, nmatch, pmatch, eflags)
+#endif
+
+int is_cntrl_char(int c)
+{
+ return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
+ (127 <= c && c < 160);
+}
+
+int num_of_digits(int n)
+{
+ int i = 1;
+
+ if (n < 0)
+ n = -n;
+
+ while (n > 10) {
+ n /= 10;
+ i++;
+ }
+
+ return i;
+}
+
+/* Fix the memory allocation for a string. */
+void align(char **strp)
+{
+ assert(strp != NULL);
+ if (*strp != NULL)
+ *strp = charealloc(*strp, strlen(*strp) + 1);
+}
+
+/* Null a string at a certain index and align it. */
+void null_at(char **data, size_t index)
+{
+ assert(data != NULL);
+ *data = charealloc(*data, index + 1);
+ (*data)[index] = '\0';
+}
+
+/* For non-null-terminated lines. A line, by definition, shouldn't
+ * normally have newlines in it, so encode its nulls as newlines. */
+void unsunder(char *str, size_t true_len)
+{
+ assert(str != NULL);
+ for(; true_len > 0; true_len--, str++)
+ if (*str == '\0')
+ *str = '\n';
+}
+
+/* For non-null-terminated lines. A line, by definition, shouldn't
+ * normally have newlines in it, so decode its newlines into nulls. */
+void sunder(char *str)
+{
+ assert(str != NULL);
+ for(; *str != '\0'; str++)
+ if (*str == '\n')
+ *str = '\0';
+}
+
+#ifndef HAVE_STRCASECMP
+/* This function is equivalent to strcasecmp(). */
+int nstricmp(const char *s1, const char *s2)
+{
+ assert(s1 != NULL && s2 != NULL);
+ for (; *s1 != '\0' && *s2 != '\0'; s1++, s2++) {
+ if (tolower(*s1) != tolower(*s2))
+ break;
+ }
+ return (tolower(*s1) - tolower(*s2));
+}
+#endif
+
+#ifndef HAVE_STRNCASECMP
+/* This function is equivalent to strncasecmp(). */
+int nstrnicmp(const char *s1, const char *s2, size_t n)
+{
+ assert(s1 != NULL && s2 != NULL);
+ for (; n > 0 && *s1 != '\0' && *s2 != '\0'; n--, s1++, s2++) {
+ if (tolower(*s1) != tolower(*s2))
+ break;
+ }
+ if (n > 0)
+ return (tolower(*s1) - tolower(*s2));
+ else if (n == 0)
+ return 0;
+ else if (n < 0)
+ return -1;
+}
+#endif
+
+/* None of this is needed if we're using NANO_SMALL! */
+#ifndef NANO_SMALL
+const char *revstrstr(const char *haystack, const char *needle,
+ const char *rev_start)
+{
+ for(; rev_start >= haystack ; rev_start--) {
+ const char *r, *q;
+
+ for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++)
+ ;
+ if (*q == '\0')
+ return rev_start;
+ }
+ return NULL;
+}
+
+const char *revstristr(const char *haystack, const char *needle,
+ const char *rev_start)
+{
+ for (; rev_start >= haystack; rev_start--) {
+ const char *r = rev_start, *q = needle;
+
+ for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++)
+ ;
+ if (*q == '\0')
+ return rev_start;
+ }
+ return NULL;
+}
+#endif /* !NANO_SMALL */
+
+/* This is now mutt's version (called mutt_stristr) because it doesn't
+ * use memory allocation to do a simple search (yuck). */
+const char *stristr(const char *haystack, const char *needle)
+{
+ const char *p, *q;
+
+ if (haystack == NULL)
+ return NULL;
+ if (needle == NULL)
+ return haystack;
+
+ while (*(p = haystack) != '\0') {
+ for (q = needle; *p != 0 && *q != 0 && tolower(*p) == tolower(*q); p++, q++)
+ ;
+ if (*q == 0)
+ return haystack;
+ haystack++;
+ }
+ return NULL;
+}
+
+/* If we are searching backwards, we will find the last match
+ * that starts no later than rev_start. If we are doing a regexp search,
+ * then line_pos should be 0 if haystack starts at the beginning of a
+ * line, and positive otherwise. In the regexp case, we fill in the
+ * global variable regmatches with at most 9 subexpression matches. Also,
+ * all .rm_so elements are relative to the start of the whole match, so
+ * regmatches[0].rm_so == 0. */
+const char *strstrwrapper(const char *haystack, const char *needle,
+ const char *rev_start, int line_pos)
+{
+#ifdef HAVE_REGEX_H
+ if (ISSET(USE_REGEXP)) {
+#ifndef NANO_SMALL
+ if (ISSET(REVERSE_SEARCH)) {
+ /* When doing a backwards search, haystack is a whole line. */
+ if (!regexec(&search_regexp, haystack, 1, regmatches, 0) &&
+ haystack + regmatches[0].rm_so <= rev_start) {
+ const char *retval = haystack + regmatches[0].rm_so;
+
+ /* Search forward until there is no more match. */
+ while (!regexec(&search_regexp, retval + 1, 1, regmatches,
+ REG_NOTBOL) &&
+ retval + 1 + regmatches[0].rm_so <= rev_start)
+ retval += 1 + regmatches[0].rm_so;
+ /* Finally, put the subexpression matches in global
+ * variable regmatches. The REG_NOTBOL flag doesn't
+ * matter now. */
+ regexec(&search_regexp, retval, 10, regmatches, 0);
+ return retval;
+ }
+ } else
+#endif /* !NANO_SMALL */
+ if (!regexec(&search_regexp, haystack, 10, regmatches,
+ line_pos > 0 ? REG_NOTBOL : 0)) {
+ const char *retval = haystack + regmatches[0].rm_so;
+
+ regexec(&search_regexp, retval, 10, regmatches, 0);
+ return retval;
+ }
+ return NULL;
+ }
+#endif /* HAVE_REGEX_H */
+#ifndef NANO_SMALL
+ if (ISSET(CASE_SENSITIVE)) {
+ if (ISSET(REVERSE_SEARCH))
+ return revstrstr(haystack, needle, rev_start);
+ else
+ return strstr(haystack, needle);
+ } else if (ISSET(REVERSE_SEARCH))
+ return revstristr(haystack, needle, rev_start);
+#endif
+ return stristr(haystack, needle);
+}
+
+/* This is a wrapper for the perror function. The wrapper takes care of
+ * ncurses, calls perror (which writes to STDERR), then refreshes the
+ * screen. Note that nperror causes the window to flicker once. */
+void nperror(const char *s)
+{
+ /* leave ncurses mode, go to the terminal */
+ if (endwin() != ERR) {
+ perror(s); /* print the error */
+ total_refresh(); /* return to ncurses and repaint */
+ }
+}
+
+/* Thanks BG, many ppl have been asking for this... */
+void *nmalloc(size_t howmuch)
+{
+ void *r = malloc(howmuch);
+
+ if (r == NULL && howmuch != 0)
+ die(_("nano is out of memory!"));
+
+ return r;
+}
+
+void *nrealloc(void *ptr, size_t howmuch)
+{
+ void *r = realloc(ptr, howmuch);
+
+ if (r == NULL && howmuch != 0)
+ die(_("nano is out of memory!"));
+
+ return r;
+}
+
+/* Copy one malloc()ed string to another pointer. Should be used as:
+ * dest = mallocstrcpy(dest, src); */
+char *mallocstrcpy(char *dest, const char *src)
+{
+ if (src == dest)
+ return dest;
+
+ if (dest != NULL)
+ free(dest);
+
+ if (src == NULL)
+ return NULL;
+
+ dest = charalloc(strlen(src) + 1);
+ strcpy(dest, src);
+
+ return dest;
+}
+
+/* Append a new magic-line to filebot. */
+void new_magicline(void)
+{
+ filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
+ filebot->next->data = charalloc(1);
+ filebot->next->data[0] = '\0';
+ filebot->next->prev = filebot;
+ filebot->next->next = NULL;
+ filebot->next->lineno = filebot->lineno + 1;
+ filebot = filebot->next;
+ totlines++;
+ totsize++;
+}
+
+#ifndef DISABLE_TABCOMP
+/*
+ * Routine to see if a text string is matched by a wildcard pattern.
+ * Returns TRUE if the text is matched, or FALSE if it is not matched
+ * or if the pattern is invalid.
+ * * matches zero or more characters
+ * ? matches a single character
+ * [abc] matches 'a', 'b' or 'c'
+ * \c quotes character c
+ * Adapted from code written by Ingo Wilken, and
+ * then taken from sash, Copyright (c) 1999 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ * Permission to distribute this code under the GPL has been granted.
+ */
+int check_wildcard_match(const char *text, const char *pattern)
+{
+ const char *retrypat;
+ const char *retrytext;
+ int ch;
+ int found;
+ int len;
+
+ retrypat = NULL;
+ retrytext = NULL;
+
+ while (*text != '\0' || *pattern != '\0') {
+ ch = *pattern++;
+
+ switch (ch) {
+ case '*':
+ retrypat = pattern;
+ retrytext = text;
+ break;
+
+ case '[':
+ found = FALSE;
+
+ while ((ch = *pattern++) != ']') {
+ if (ch == '\\')
+ ch = *pattern++;
+
+ if (ch == '\0')
+ return FALSE;
+
+ if (*text == ch)
+ found = TRUE;
+ }
+ len = strlen(text);
+ if (found == FALSE && len != 0) {
+ return FALSE;
+ }
+ if (found == TRUE) {
+ if (strlen(pattern) == 0 && len == 1) {
+ return TRUE;
+ }
+ if (len != 0) {
+ text++;
+ continue;
+ }
+ }
+
+ /* fall into next case */
+
+ case '?':
+ if (*text++ == '\0')
+ return FALSE;
+
+ break;
+
+ case '\\':
+ ch = *pattern++;
+
+ if (ch == '\0')
+ return FALSE;
+
+ /* fall into next case */
+
+ default:
+ if (*text == ch) {
+ if (*text != '\0')
+ text++;
+ break;
+ }
+
+ if (*text != '\0') {
+ pattern = retrypat;
+ text = ++retrytext;
+ break;
+ }
+
+ return FALSE;
+ }
+
+ if (pattern == NULL)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
--- /dev/null
+/* $Id$ */
+/**************************************************************************
+ * winio.c *
+ * *
+ * Copyright (C) 1999-2003 Chris Allegretta *
+ * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ * *
+ **************************************************************************/
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <assert.h>
+#include "proto.h"
+#include "nano.h"
+
+static int statblank = 0; /* Number of keystrokes left after
+ we call statusbar(), before we
+ actually blank the statusbar */
+
+/* Read in a single input character. If it's ignored, swallow it and go
+ * on. Otherwise, try to translate it from ASCII and extended (keypad)
+ * input. Assume nodelay(win) is FALSE. */
+int get_kbinput(WINDOW *win, int *meta, int rebind_delete)
+{
+ int kbinput, retval;
+
+ kbinput = get_ignored_kbinput(win);
+ retval = get_accepted_kbinput(win, kbinput, meta, rebind_delete);
+
+ return retval;
+}
+
+/* Read in a string of input characters (e. g. an escape sequence)
+ * verbatim, and return the length of the string in kbinput_len. Assume
+ * nodelay(win) is FALSE. */
+char *get_verbatim_kbinput(WINDOW *win, int *kbinput_len)
+{
+ char *verbatim_kbinput;
+ int kbinput = wgetch(win);
+
+ verbatim_kbinput = charalloc(1);
+ verbatim_kbinput[0] = kbinput;
+ *kbinput_len = 1;
+
+ if (kbinput >= '0' && kbinput <= '2')
+ /* Entering a three-digit decimal ASCII code from 000-255 in
+ * verbatim mode will produce the corresponding ASCII
+ * character. */
+ verbatim_kbinput[0] = (char)get_ascii_kbinput(win, kbinput);
+ else {
+ nodelay(win, TRUE);
+ while ((kbinput = wgetch(win)) != ERR) {
+ *kbinput_len++;
+ verbatim_kbinput = charealloc(verbatim_kbinput, *kbinput_len);
+ verbatim_kbinput[*kbinput_len - 1] = (char)kbinput;
+ }
+ nodelay(win, FALSE);
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "get_verbatim_kbinput(): verbatim_kbinput = %s\n", verbatim_kbinput);
+#endif
+ return verbatim_kbinput;
+}
+
+/* Swallow input characters that should be quietly ignored, and return
+ * the first input character that shouldn't be. */
+int get_ignored_kbinput(WINDOW *win)
+{
+ int kbinput;
+
+ while (1) {
+ kbinput = wgetch(win);
+ switch (kbinput) {
+ case ERR:
+ case KEY_RESIZE:
+#ifdef PDCURSES
+ case KEY_SHIFT_L:
+ case KEY_SHIFT_R:
+ case KEY_CONTROL_L:
+ case KEY_CONTROL_R:
+ case KEY_ALT_L:
+ case KEY_ALT_R:
+#endif
+#ifdef DEBUG
+ fprintf(stderr, "get_ignored_kbinput(): kbinput = %d\n", kbinput);
+#endif
+ break;
+ default:
+ return kbinput;
+ }
+ }
+}
+
+/* Translate acceptable ASCII and extended (keypad) input. Set meta to
+ * 1 if we get a Meta sequence. Assume nodelay(win) is FALSE. */
+int get_accepted_kbinput(WINDOW *win, int kbinput, int *meta,
+ int rebind_delete)
+{
+ *meta = 0;
+
+ switch (kbinput) {
+ case NANO_CONTROL_3: /* Escape */
+ switch (kbinput = wgetch(win)) {
+ case NANO_CONTROL_3: /* Escape */
+ kbinput = wgetch(win);
+ /* Esc Esc [three-digit decimal ASCII code from
+ * 000-255] == [corresponding ASCII character];
+ Esc Esc 2 obviously can't be Ctrl-2 here */
+ if (kbinput >= '0' && kbinput <= '2')
+ kbinput = get_ascii_kbinput(win, kbinput);
+ /* Esc Esc [character] == Ctrl-[character];
+ * Ctrl-Space (Ctrl-2) == Ctrl-@ == Ctrl-` */
+ else if (kbinput == ' ' || kbinput == '@' || kbinput == '`')
+ kbinput = NANO_CONTROL_SPACE;
+ /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-_) */
+ else if (kbinput >= '3' && kbinput <= '7')
+ kbinput -= 24;
+ /* Ctrl-8 (Ctrl-?) */
+ else if (kbinput == '8' || kbinput == '?')
+ kbinput = NANO_CONTROL_8;
+ /* Ctrl-A to Ctrl-_ */
+ else if (kbinput >= 'A' && kbinput <= '_')
+ kbinput -= 64;
+ /* Ctrl-A to Ctrl-Z */
+ else if (kbinput >= 'a' && kbinput <= 'z')
+ kbinput -= 96;
+ break;
+ /* Terminal breakage, part 1: We shouldn't get an escape
+ * sequence here for terminals that support Delete, but
+ * we do sometimes on FreeBSD. Thank you, Wouter van
+ * Hemel. */
+ case '[':
+ nodelay(win, TRUE);
+ kbinput = wgetch(win);
+ switch (kbinput) {
+ case ERR:
+ kbinput = '[';
+ *meta = 1;
+ break;
+ default:
+ kbinput = get_escape_seq_kbinput(win, kbinput);
+ }
+ nodelay(win, FALSE);
+ break;
+ case '`':
+ /* Esc Space == Esc ` */
+ kbinput = ' ';
+ break;
+ default:
+ /* Esc [character] == Meta-[character] */
+ if (isupper(kbinput))
+ kbinput = tolower(kbinput);
+ *meta = 1;
+ }
+ break;
+ case KEY_DOWN:
+ kbinput = NANO_DOWN_KEY;
+ break;
+ case KEY_UP:
+ kbinput = NANO_UP_KEY;
+ break;
+ case KEY_LEFT:
+ kbinput = NANO_BACK_KEY;
+ break;
+ case KEY_RIGHT:
+ kbinput = NANO_FORWARD_KEY;
+ break;
+ case KEY_HOME:
+ kbinput = NANO_HOME_KEY;
+ break;
+ case KEY_BACKSPACE:
+ kbinput = NANO_BACKSPACE_KEY;
+ break;
+ case KEY_DC:
+ /* Terminal breakage, part 2: We should only get KEY_DC when
+ * hitting Delete, but we get it when hitting Backspace
+ * sometimes on FreeBSD. Thank you, Lee Nelson. */
+ kbinput = (rebind_delete) ? NANO_BACKSPACE_KEY : NANO_DELETE_KEY;
+ break;
+ case KEY_IC:
+ kbinput = NANO_INSERTFILE_KEY;
+ break;
+ case KEY_NPAGE:
+ kbinput = NANO_NEXTPAGE_KEY;
+ break;
+ case KEY_PPAGE:
+ kbinput = NANO_PREVPAGE_KEY;
+ break;
+ case KEY_ENTER:
+ kbinput = NANO_ENTER_KEY;
+ break;
+ case KEY_END:
+ kbinput = NANO_END_KEY;
+ break;
+ case KEY_SUSPEND:
+ kbinput = NANO_SUSPEND_KEY;
+ break;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "get_accepted_kbinput(): kbinput = %d, meta = %d\n", kbinput, *meta);
+#endif
+ return kbinput;
+}
+
+/* Translate a three-digit decimal ASCII code from 000-255 into the
+ * corresponding ASCII character. */
+int get_ascii_kbinput(WINDOW *win, int kbinput)
+{
+ int retval;
+
+ switch (kbinput) {
+ case '0':
+ case '1':
+ case '2':
+ retval = (kbinput - 48) * 100;
+ break;
+ default:
+ return kbinput;
+ }
+
+ kbinput = wgetch(win);
+ switch (kbinput) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ retval += (kbinput - 48) * 10;
+ break;
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (retval < 200) {
+ retval += (kbinput - 48) * 10;
+ break;
+ }
+ default:
+ return kbinput;
+ }
+
+ kbinput = wgetch(win);
+ switch (kbinput) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ retval += kbinput - 48;
+ break;
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (retval < 250) {
+ retval += kbinput - 48;
+ break;
+ }
+ default:
+ return kbinput;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "get_ascii_kbinput(): kbinput = %d\n", kbinput);
+#endif
+ return retval;
+}
+
+/* Translate common escape sequences for some keys. These are generated
+ * when the terminal doesn't support those keys. Assume nodelay(win) is
+ * TRUE. */
+int get_escape_seq_kbinput(WINDOW *win, int kbinput)
+{
+ switch (kbinput) {
+ case '3':
+ /* Esc [ 3 ~ == kdch1 on many terminals. */
+ kbinput = get_skip_tilde_kbinput(win, kbinput, NANO_DELETE_KEY);
+ break;
+ }
+ return kbinput;
+}
+
+/* If there is no next character, return the passed-in error value. If
+ * the next character's a tilde, eat it and return the passed-in
+ * return value. Otherwise, return the next character. Assume
+ * nodelay(win) is TRUE. */
+int get_skip_tilde_kbinput(WINDOW *win, int errval, int retval)
+{
+ int kbinput = wgetch(win);
+ switch (kbinput) {
+ case ERR:
+ return errval;
+ case '~':
+ return retval;
+ default:
+ return kbinput;
+ }
+}
+
+int do_first_line(void)
+{
+ current = fileage;
+ placewewant = 0;
+ current_x = 0;
+ edit_update(current, CENTER);
+ return 1;
+}
+
+int do_last_line(void)
+{
+ current = filebot;
+ placewewant = 0;
+ current_x = 0;
+ edit_update(current, CENTER);
+ return 1;
+}
+
+/* Return the placewewant associated with current_x. That is, xplustabs
+ * is the zero-based column position of the cursor. Value is no smaller
+ * than current_x. */
+size_t xplustabs(void)
+{
+ return strnlenpt(current->data, current_x);
+}
+
+/* Return what current_x should be, given xplustabs() for the line. */
+size_t actual_x(const filestruct *fileptr, size_t xplus)
+{
+ size_t i = 0;
+ /* the position in fileptr->data, returned */
+ size_t length = 0;
+ /* the screen display width to data[i] */
+ char *c;
+ /* fileptr->data + i */
+
+ assert(fileptr != NULL && fileptr->data != NULL);
+
+ for (c = fileptr->data; length < xplus && *c != '\0'; i++, c++) {
+ if (*c == '\t')
+ length += tabsize - length % tabsize;
+ else if (is_cntrl_char((int)*c))
+ length += 2;
+ else
+ length++;
+ }
+ assert(length == strnlenpt(fileptr->data, i));
+ assert(i <= strlen(fileptr->data));
+
+ if (length > xplus)
+ i--;
+
+#ifdef DEBUG
+ fprintf(stderr, "actual_x for xplus=%d returns %d\n", xplus, i);
+#endif
+
+ return i;
+}
+
+/* A strlen with tabs factored in, similar to xplustabs(). */
+size_t strnlenpt(const char *buf, size_t size)
+{
+ size_t length = 0;
+
+ if (buf != NULL)
+ for (; *buf != '\0' && size != 0; size--, buf++) {
+ if (*buf == '\t')
+ length += tabsize - (length % tabsize);
+ else if (is_cntrl_char((int)*buf))
+ length += 2;
+ else
+ length++;
+ }
+ return length;
+}
+
+size_t strlenpt(const char *buf)
+{
+ return strnlenpt(buf, -1);
+}
+
+void blank_bottombars(void)
+{
+ if (!no_help()) {
+ mvwaddstr(bottomwin, 1, 0, hblank);
+ mvwaddstr(bottomwin, 2, 0, hblank);
+ }
+}
+
+void blank_bottomwin(void)
+{
+ if (ISSET(NO_HELP))
+ return;
+
+ mvwaddstr(bottomwin, 1, 0, hblank);
+ mvwaddstr(bottomwin, 2, 0, hblank);
+}
+
+void blank_edit(void)
+{
+ int i;
+ for (i = 0; i <= editwinrows - 1; i++)
+ mvwaddstr(edit, i, 0, hblank);
+}
+
+
+void blank_statusbar(void)
+{
+ mvwaddstr(bottomwin, 0, 0, hblank);
+}
+
+void blank_statusbar_refresh(void)
+{
+ blank_statusbar();
+ wrefresh(bottomwin);
+}
+
+void check_statblank(void)
+{
+ if (statblank > 1)
+ statblank--;
+ else if (statblank == 1 && !ISSET(CONSTUPDATE)) {
+ statblank--;
+ blank_statusbar_refresh();
+ }
+}
+
+/* Repaint the statusbar when getting a character in nanogetstr(). buf
+ * should be no longer than COLS - 4.
+ *
+ * Note that we must turn on A_REVERSE here, since do_help() turns it
+ * off! */
+void nanoget_repaint(const char *buf, const char *inputbuf, int x)
+{
+ int len = strlen(buf) + 2;
+ int wid = COLS - len;
+
+ assert(wid >= 2);
+ assert(0 <= x && x <= strlen(inputbuf));
+
+ wattron(bottomwin, A_REVERSE);
+ blank_statusbar();
+ mvwaddstr(bottomwin, 0, 0, buf);
+ waddch(bottomwin, ':');
+ waddch(bottomwin, x < wid ? ' ' : '$');
+ waddnstr(bottomwin, &inputbuf[wid * (x / wid)], wid);
+ wmove(bottomwin, 0, (x % wid) + len);
+ wattroff(bottomwin, A_REVERSE);
+}
+
+/* Get the input from the kb; this should only be called from
+ * statusq(). */
+int nanogetstr(int allowtabs, const char *buf, const char *def,
+#ifndef NANO_SMALL
+ historyheadtype *history_list,
+#endif
+ const shortcut *s
+#ifndef DISABLE_TABCOMP
+ , int *list
+#endif
+ )
+{
+ int kbinput;
+ int meta;
+ static int x = -1;
+ /* the cursor position in 'answer' */
+ int xend;
+ /* length of 'answer', the status bar text */
+ int tabbed = 0;
+ /* used by input_tab() */
+ const shortcut *t;
+
+#ifndef NANO_SMALL
+ /* for history */
+ char *history = NULL;
+ char *currentbuf = NULL;
+ char *complete = NULL;
+ int last_kbinput = 0;
+
+ /* This variable is used in the search history code. use_cb == 0
+ means that we're using the existing history and ignoring
+ currentbuf. use_cb == 1 means that the entry in answer should be
+ moved to currentbuf or restored from currentbuf to answer.
+ use_cb == 2 means that the entry in currentbuf should be moved to
+ answer or restored from answer to currentbuf. */
+ int use_cb = 0;
+#endif
+ xend = strlen(def);
+
+ /* Only put x at the end of the string if it's uninitialized or if
+ it would be past the end of the string as it is. Otherwise,
+ leave it alone. This is so the cursor position stays at the same
+ place if a prompt-changing toggle is pressed. */
+ if (x == -1 || x > xend || resetstatuspos)
+ x = xend;
+
+ answer = charealloc(answer, xend + 1);
+ if (xend > 0)
+ strcpy(answer, def);
+ else
+ answer[0] = '\0';
+
+#if !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
+ currshortcut = s;
+#endif
+
+ /* Get the input! */
+
+ nanoget_repaint(buf, answer, x);
+
+ /* Make sure any editor screen updates are displayed before getting
+ input */
+ wrefresh(edit);
+
+ while ((kbinput = get_kbinput(bottomwin, &meta, ISSET(REBIND_DELETE))) != NANO_ENTER_KEY) {
+ for (t = s; t != NULL; t = t->next) {
+#ifdef DEBUG
+ fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, kbinput);
+#endif
+
+ if (kbinput == t->val && kbinput < 32) {
+
+#ifndef DISABLE_HELP
+ /* Have to do this here, it would be too late to do it
+ in statusq() */
+ if (kbinput == NANO_HELP_KEY || kbinput == NANO_HELP_FKEY) {
+ do_help();
+ break;
+ }
+#endif
+
+ return t->val;
+ }
+ }
+ assert(0 <= x && x <= xend && xend == strlen(answer));
+
+ if (kbinput != '\t')
+ tabbed = 0;
+
+ switch (kbinput) {
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ case KEY_MOUSE:
+ do_mouse();
+ break;
+#endif
+ case NANO_HOME_KEY:
+ x = 0;
+ break;
+ case NANO_END_KEY:
+ x = xend;
+ break;
+ case NANO_FORWARD_KEY:
+ if (x < xend)
+ x++;
+ break;
+ case NANO_DELETE_KEY:
+ if (x < xend) {
+ memmove(answer + x, answer + x + 1, xend - x);
+ xend--;
+ }
+ break;
+ case NANO_CUT_KEY:
+ case NANO_UNCUT_KEY:
+ null_at(&answer, 0);
+ xend = 0;
+ x = 0;
+ break;
+ case NANO_BACKSPACE_KEY:
+ if (x > 0) {
+ memmove(answer + x - 1, answer + x, xend - x + 1);
+ x--;
+ xend--;
+ }
+ break;
+ case NANO_TAB_KEY:
+#ifndef NANO_SMALL
+ /* tab history completion */
+ if (history_list != NULL) {
+ if (!complete || last_kbinput != NANO_TAB_KEY) {
+ history_list->current = (historytype *)history_list;
+ history_list->len = strlen(answer);
+ }
+
+ if (history_list->len > 0) {
+ complete = get_history_completion(history_list, answer);
+ xend = strlen(complete);
+ x = xend;
+ answer = mallocstrcpy(answer, complete);
+ }
+ }
+#ifndef DISABLE_TABCOMP
+ else
+#endif
+#endif
+#ifndef DISABLE_TABCOMP
+ if (allowtabs) {
+ int shift = 0;
+
+ answer = input_tab(answer, x, &tabbed, &shift, list);
+ xend = strlen(answer);
+ x += shift;
+ if (x > xend)
+ x = xend;
+ }
+#endif
+ break;
+ case NANO_BACK_KEY:
+ if (x > 0)
+ x--;
+ break;
+ case NANO_UP_KEY:
+#ifndef NANO_SMALL
+ if (history_list != NULL) {
+
+ /* if currentbuf is NULL, or if use_cb is 1, currentbuf
+ isn't NULL, and currentbuf is different from answer,
+ it means that we're scrolling up at the top of the
+ search history, and we need to save the current
+ answer in currentbuf; do this and reset use_cb to
+ 0 */
+ if (currentbuf == NULL || (use_cb == 1 && strcmp(currentbuf, answer))) {
+ currentbuf = mallocstrcpy(currentbuf, answer);
+ use_cb = 0;
+ }
+
+ /* if currentbuf isn't NULL, use_cb is 2, and currentbuf
+ is different from answer, it means that we're
+ scrolling up at the bottom of the search history, and
+ we need to make the string in currentbuf the current
+ answer; do this, blow away currentbuf since we don't
+ need it anymore, and reset use_cb to 0 */
+ if (currentbuf != NULL && use_cb == 2 && strcmp(currentbuf, answer)) {
+ answer = mallocstrcpy(answer, currentbuf);
+ free(currentbuf);
+ currentbuf = NULL;
+ xend = strlen(answer);
+ use_cb = 0;
+
+ /* else get older search from the history list and save
+ it in answer; if there is no older search, blank out
+ answer */
+ } else if ((history = get_history_older(history_list)) != NULL) {
+ answer = mallocstrcpy(answer, history);
+ xend = strlen(history);
+ } else {
+ answer = mallocstrcpy(answer, "");
+ xend = 0;
+ }
+ x = xend;
+ }
+#endif
+ break;
+ case NANO_DOWN_KEY:
+#ifndef NANO_SMALL
+ if (history_list != NULL) {
+
+ /* get newer search from the history list and save it
+ in answer */
+ if ((history = get_history_newer(history_list)) != NULL) {
+ answer = mallocstrcpy(answer, history);
+ xend = strlen(history);
+
+ /* if there is no newer search, we're here */
+
+ /* if currentbuf isn't NULL and use_cb isn't 2, it means
+ that we're scrolling down at the bottom of the search
+ history and we need to make the string in currentbuf
+ the current answer; do this, blow away currentbuf
+ since we don't need it anymore, and set use_cb to
+ 1 */
+ } else if (currentbuf != NULL && use_cb != 2) {
+ answer = mallocstrcpy(answer, currentbuf);
+ free(currentbuf);
+ currentbuf = NULL;
+ xend = strlen(answer);
+ use_cb = 1;
+
+ /* otherwise, if currentbuf is NULL and use_cb isn't 2,
+ it means that we're scrolling down at the bottom of
+ the search history and the current answer (if it's
+ not blank) needs to be saved in currentbuf; do this,
+ blank out answer (if necessary), and set use_cb to
+ 2 */
+ } else if (use_cb != 2) {
+ if (answer[0] != '\0') {
+ currentbuf = mallocstrcpy(currentbuf, answer);
+ answer = mallocstrcpy(answer, "");
+ }
+ xend = 0;
+ use_cb = 2;
+ }
+ x = xend;
+ }
+#endif
+ break;
+ default:
+
+ for (t = s; t != NULL; t = t->next) {
+#ifdef DEBUG
+ fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput,
+ kbinput);
+#endif
+ if (meta == 1 && (kbinput == t->val || kbinput == t->val - 32))
+ /* We hit an Alt key. Do like above. We don't
+ just ungetch() the letter and let it get
+ caught above cause that screws the
+ keypad... */
+ return t->val;
+ }
+
+ if (kbinput < 32)
+ break;
+ answer = charealloc(answer, xend + 2);
+ memmove(answer + x + 1, answer + x, xend - x + 1);
+ xend++;
+ answer[x] = kbinput;
+ x++;
+
+#ifdef DEBUG
+ fprintf(stderr, "input \'%c\' (%d)\n", kbinput, kbinput);
+#endif
+ } /* switch (kbinput) */
+#ifndef NANO_SMALL
+ last_kbinput = kbinput;
+#endif
+ nanoget_repaint(buf, answer, x);
+ wrefresh(bottomwin);
+ } /* while (kbinput ...) */
+
+ /* We finished putting in an answer; reset x */
+ x = -1;
+
+ /* Just check for a blank answer here */
+ if (answer[0] == '\0')
+ return -2;
+ else
+ return 0;
+}
+
+/* If modified is not already set, set it and update titlebar. */
+void set_modified(void)
+{
+ if (!ISSET(MODIFIED)) {
+ SET(MODIFIED);
+ titlebar(NULL);
+ wrefresh(topwin);
+ }
+}
+
+void titlebar(const char *path)
+{
+ int namelen, space;
+ const char *what = path;
+
+ if (path == NULL)
+ what = filename;
+
+ wattron(topwin, A_REVERSE);
+
+ mvwaddstr(topwin, 0, 0, hblank);
+ mvwaddnstr(topwin, 0, 2, VERMSG, COLS - 3);
+
+ space = COLS - sizeof(VERMSG) - 23;
+
+ namelen = strlen(what);
+
+ if (space > 0) {
+ if (what[0] == '\0')
+ mvwaddnstr(topwin, 0, COLS / 2 - 6, _("New Buffer"),
+ COLS / 2 + COLS % 2 - 6);
+ else if (namelen > space) {
+ if (path == NULL)
+ waddstr(topwin, _(" File: ..."));
+ else
+ waddstr(topwin, _(" DIR: ..."));
+ waddstr(topwin, &what[namelen - space]);
+ } else {
+ if (path == NULL)
+ mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1),
+ _("File: "));
+ else
+ mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1),
+ _(" DIR: "));
+ waddstr(topwin, what);
+ }
+ } /* If we don't have space, we shouldn't bother */
+ if (ISSET(MODIFIED))
+ mvwaddnstr(topwin, 0, COLS - 11, _(" Modified "), 11);
+ else if (ISSET(VIEW_MODE))
+ mvwaddnstr(topwin, 0, COLS - 11, _(" View "), 11);
+
+ wattroff(topwin, A_REVERSE);
+
+ wrefresh(topwin);
+ reset_cursor();
+}
+
+void bottombars(const shortcut *s)
+{
+ int i, j, numcols;
+ char keystr[9];
+ int slen;
+
+ if (ISSET(NO_HELP))
+ return;
+
+ if (s == main_list) {
+ slen = MAIN_VISIBLE;
+ assert(MAIN_VISIBLE <= length_of_list(s));
+ } else
+ slen = length_of_list(s);
+
+ /* There will be this many columns of shortcuts */
+ numcols = (slen + (slen % 2)) / 2;
+
+ blank_bottomwin();
+
+ for (i = 0; i < numcols; i++) {
+ for (j = 0; j <= 1; j++) {
+
+ wmove(bottomwin, 1 + j, i * (COLS / numcols));
+
+ /* Yucky sentinel values we can't handle a better way */
+ if (s->val == NANO_CONTROL_SPACE)
+ strcpy(keystr, "^ ");
+#ifndef NANO_SMALL
+ else if (s->val == KEY_UP)
+ strncpy(keystr, _("Up"), 8);
+#endif /* NANO_SMALL */
+ else if (s->val > 0) {
+ if (s->val < 64)
+ sprintf(keystr, "^%c", s->val + 64);
+ else
+ sprintf(keystr, "M-%c", s->val - 32);
+ } else if (s->altval > 0)
+ sprintf(keystr, "M-%c", s->altval);
+
+ onekey(keystr, s->desc, COLS / numcols);
+
+ s = s->next;
+ if (s == NULL)
+ goto break_completely_out;
+ }
+ }
+
+ break_completely_out:
+ wrefresh(bottomwin);
+}
+
+/* Write a shortcut key to the help area at the bottom of the window.
+ * keystroke is e.g. "^G" and desc is e.g. "Get Help".
+ * We are careful to write exactly len characters, even if len is
+ * very small and keystroke and desc are long. */
+void onekey(const char *keystroke, const char *desc, int len)
+{
+
+ wattron(bottomwin, A_REVERSE);
+ waddnstr(bottomwin, keystroke, len);
+ wattroff(bottomwin, A_REVERSE);
+ len -= strlen(keystroke);
+ if (len > 0) {
+ waddch(bottomwin, ' ');
+ len--;
+ waddnstr(bottomwin, desc, len);
+ len -= strlen(desc);
+ for (; len > 0; len--)
+ waddch(bottomwin, ' ');
+ }
+}
+
+/* And so start the display update routines. */
+
+#ifndef NDEBUG
+int check_linenumbers(const filestruct *fileptr)
+{
+ int check_line = 0;
+ const filestruct *filetmp;
+
+ for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
+ check_line++;
+ return check_line;
+}
+#endif
+
+ /* nano scrolls horizontally within a line in chunks. This function
+ * returns the column number of the first character displayed in the
+ * window when the cursor is at the given column. */
+int get_page_start(int column)
+{
+ assert(COLS > 9);
+ return column < COLS - 1 ? 0 : column - 7 - (column - 8) % (COLS - 9);
+}
+
+/* Resets current_y, based on the position of current, and puts the
+ * cursor at (current_y, current_x). */
+void reset_cursor(void)
+{
+ const filestruct *ptr = edittop;
+ size_t x;
+
+ /* Yuck. This condition can be true after open_file() when opening
+ * the first file. */
+ if (edittop == NULL)
+ return;
+
+ current_y = 0;
+
+ while (ptr != current && ptr != editbot && ptr->next != NULL) {
+ ptr = ptr->next;
+ current_y++;
+ }
+
+ x = xplustabs();
+ wmove(edit, current_y, x - get_page_start(x));
+}
+
+/* edit_add() takes care of the job of actually painting a line into
+ * the edit window. Called only from update_line(). Expects a
+ * converted-to-not-have-tabs line. */
+void edit_add(const filestruct *fileptr, int yval, int start
+#ifndef NANO_SMALL
+ , int virt_mark_beginx, int virt_cur_x
+#endif
+ )
+{
+#ifdef DEBUG
+ fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno,
+ current->lineno);
+#endif
+
+ /* Just paint the string in any case (we'll add color or reverse on
+ just the text that needs it */
+ mvwaddnstr(edit, yval, 0, &fileptr->data[start], COLS);
+
+#ifdef ENABLE_COLOR
+ if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
+ const colortype *tmpcolor = colorstrings;
+
+ for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
+ int x_start;
+ /* Starting column for mvwaddnstr. Zero-based. */
+ int paintlen;
+ /* number of chars to paint on this line. There are COLS
+ * characters on a whole line. */
+ regmatch_t startmatch; /* match position for start_regexp*/
+ regmatch_t endmatch; /* match position for end_regexp*/
+
+ if (tmpcolor->bright)
+ wattron(edit, A_BOLD);
+ wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
+ /* Two notes about regexec. Return value 0 means there is a
+ * match. Also, rm_eo is the first non-matching character
+ * after the match. */
+
+ /* First case, tmpcolor is a single-line expression. */
+ if (tmpcolor->end == NULL) {
+ size_t k = 0;
+
+ /* We increment k by rm_eo, to move past the end of the
+ last match. Even though two matches may overlap, we
+ want to ignore them, so that we can highlight C-strings
+ correctly. */
+ while (k < start + COLS) {
+ /* Note the fifth parameter to regexec. It says not to
+ * match the beginning-of-line character unless
+ * k == 0. If regexec returns nonzero, there are no
+ * more matches in the line. */
+ if (regexec(&tmpcolor->start, &fileptr->data[k], 1,
+ &startmatch, k == 0 ? 0 : REG_NOTBOL))
+ break;
+ /* Translate the match to the beginning of the line. */
+ startmatch.rm_so += k;
+ startmatch.rm_eo += k;
+ if (startmatch.rm_so == startmatch.rm_eo) {
+ startmatch.rm_eo++;
+ statusbar(_("Refusing 0 length regex match"));
+ } else if (startmatch.rm_so < start + COLS &&
+ startmatch.rm_eo > start) {
+ x_start = startmatch.rm_so - start;
+ if (x_start < 0)
+ x_start = 0;
+ paintlen = startmatch.rm_eo - start - x_start;
+ if (paintlen > COLS - x_start)
+ paintlen = COLS - x_start;
+
+ assert(0 <= x_start && 0 < paintlen &&
+ x_start + paintlen <= COLS);
+ mvwaddnstr(edit, yval, x_start,
+ fileptr->data + start + x_start, paintlen);
+ }
+ k = startmatch.rm_eo;
+ }
+ } else {
+ /* This is a multi-line regexp. There are two steps.
+ * First, we have to see if the beginning of the line is
+ * colored by a start on an earlier line, and an end on
+ * this line or later.
+ *
+ * We find the first line before fileptr matching the
+ * start. If every match on that line is followed by an
+ * end, then go to step two. Otherwise, find the next line
+ * after start_line matching the end. If that line is not
+ * before fileptr, then paint the beginning of this line. */
+
+ const filestruct *start_line = fileptr->prev;
+ /* the first line before fileptr matching start*/
+ regoff_t start_col;
+ /* where it starts in that line */
+ const filestruct *end_line;
+ int searched_later_lines = 0;
+ /* Used in step 2. Have we looked for an end on
+ * lines after fileptr? */
+
+ while (start_line != NULL &&
+ regexec(&tmpcolor->start, start_line->data, 1,
+ &startmatch, 0)) {
+ /* If there is an end on this line, there is no need
+ * to look for starts on earlier lines. */
+ if (!regexec(tmpcolor->end, start_line->data, 1,
+ &endmatch, 0))
+ goto step_two;
+ start_line = start_line->prev;
+ }
+ /* No start found, so skip to the next step. */
+ if (start_line == NULL)
+ goto step_two;
+ /* Now start_line is the first line before fileptr
+ * containing a start match. Is there a start on this
+ * line not followed by an end on this line? */
+
+ start_col = 0;
+ while (1) {
+ start_col += startmatch.rm_so;
+ startmatch.rm_eo -= startmatch.rm_so;
+ if (regexec(tmpcolor->end,
+ start_line->data + start_col + startmatch.rm_eo,
+ 1, &endmatch,
+ start_col + startmatch.rm_eo == 0 ? 0 : REG_NOTBOL))
+ /* No end found after this start */
+ break;
+ start_col++;
+ if (regexec(&tmpcolor->start,
+ start_line->data + start_col, 1, &startmatch,
+ REG_NOTBOL))
+ /* No later start on this line. */
+ goto step_two;
+ }
+ /* Indeed, there is a start not followed on this line by an
+ * end. */
+
+ /* We have already checked that there is no end before
+ * fileptr and after the start. Is there an end after
+ * the start at all? We don't paint unterminated starts. */
+ end_line = fileptr;
+ while (end_line != NULL &&
+ regexec(tmpcolor->end, end_line->data, 1, &endmatch, 0))
+ end_line = end_line->next;
+
+ /* No end found, or it is too early. */
+ if (end_line == NULL || end_line->lineno < fileptr->lineno ||
+ (end_line == fileptr && endmatch.rm_eo <= start))
+ goto step_two;
+
+ /* Now paint the start of fileptr. */
+ paintlen = end_line != fileptr
+ ? COLS : endmatch.rm_eo - start;
+ if (paintlen > COLS)
+ paintlen = COLS;
+
+ assert(0 < paintlen && paintlen <= COLS);
+ mvwaddnstr(edit, yval, 0, fileptr->data + start, paintlen);
+
+ /* We have already painted the whole line. */
+ if (paintlen == COLS)
+ goto skip_step_two;
+
+ step_two: /* Second step, we look for starts on this line. */
+ start_col = 0;
+ while (start_col < start + COLS) {
+ if (regexec(&tmpcolor->start, fileptr->data + start_col, 1,
+ &startmatch, start_col == 0 ? 0 : REG_NOTBOL)
+ || start_col + startmatch.rm_so >= start + COLS)
+ /* No more starts on this line. */
+ break;
+ /* Translate the match to be relative to the
+ * beginning of the line. */
+ startmatch.rm_so += start_col;
+ startmatch.rm_eo += start_col;
+
+ x_start = startmatch.rm_so - start;
+ if (x_start < 0) {
+ x_start = 0;
+ startmatch.rm_so = start;
+ }
+ if (!regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo,
+ 1, &endmatch,
+ startmatch.rm_eo == 0 ? 0 : REG_NOTBOL)) {
+ /* Translate the end match to be relative to the
+ beginning of the line. */
+ endmatch.rm_so += startmatch.rm_eo;
+ endmatch.rm_eo += startmatch.rm_eo;
+ /* There is an end on this line. But does it
+ appear on this page, and is the match more than
+ zero characters long? */
+ if (endmatch.rm_eo > start &&
+ endmatch.rm_eo > startmatch.rm_so) {
+ paintlen = endmatch.rm_eo - start - x_start;
+ if (x_start + paintlen > COLS)
+ paintlen = COLS - x_start;
+
+ assert(0 <= x_start && 0 < paintlen &&
+ x_start + paintlen <= COLS);
+ mvwaddnstr(edit, yval, x_start,
+ fileptr->data + start + x_start, paintlen);
+ }
+ } else if (!searched_later_lines) {
+ searched_later_lines = 1;
+ /* There is no end on this line. But we haven't
+ * yet looked for one on later lines. */
+ end_line = fileptr->next;
+ while (end_line != NULL && regexec(tmpcolor->end,
+ end_line->data, 1, &endmatch, 0))
+ end_line = end_line->next;
+ if (end_line != NULL) {
+ assert(0 <= x_start && x_start < COLS);
+ mvwaddnstr(edit, yval, x_start,
+ fileptr->data + start + x_start,
+ COLS - x_start);
+ /* We painted to the end of the line, so
+ * don't bother checking any more starts. */
+ break;
+ }
+ }
+ start_col = startmatch.rm_so + 1;
+ } /* while start_col < start + COLS */
+ } /* if (tmp_color->end != NULL) */
+
+ skip_step_two:
+ wattroff(edit, A_BOLD);
+ wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
+ } /* for tmpcolor in colorstrings */
+ }
+#endif /* ENABLE_COLOR */
+
+#ifndef NANO_SMALL
+ if (ISSET(MARK_ISSET)
+ && (fileptr->lineno <= mark_beginbuf->lineno
+ || fileptr->lineno <= current->lineno)
+ && (fileptr->lineno >= mark_beginbuf->lineno
+ || fileptr->lineno >= current->lineno)) {
+ /* fileptr is at least partially selected. */
+
+ int x_start;
+ /* Starting column for mvwaddnstr. Zero-based. */
+ int paintlen;
+ /* number of chars to paint on this line. There are COLS
+ * characters on a whole line. */
+
+ if (mark_beginbuf == fileptr && current == fileptr) {
+ x_start = virt_mark_beginx < virt_cur_x ? virt_mark_beginx
+ : virt_cur_x;
+ paintlen = abs(virt_mark_beginx - virt_cur_x);
+ } else {
+ if (mark_beginbuf->lineno < fileptr->lineno ||
+ current->lineno < fileptr->lineno)
+ x_start = 0;
+ else
+ x_start = mark_beginbuf == fileptr ? virt_mark_beginx
+ : virt_cur_x;
+
+ if (mark_beginbuf->lineno > fileptr->lineno ||
+ current->lineno > fileptr->lineno)
+ paintlen = start + COLS;
+ else
+ paintlen = mark_beginbuf == fileptr ? virt_mark_beginx
+ : virt_cur_x;
+ }
+ x_start -= start;
+ if (x_start < 0) {
+ paintlen += x_start;
+ x_start = 0;
+ }
+ if (x_start + paintlen > COLS)
+ paintlen = COLS - x_start;
+ if (paintlen > 0) {
+ wattron(edit, A_REVERSE);
+ assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS);
+ mvwaddnstr(edit, yval, x_start,
+ fileptr->data + start + x_start, paintlen);
+ wattroff(edit, A_REVERSE);
+ }
+ }
+#endif /* !NANO_SMALL */
+}
+
+/* Just update one line in the edit buffer. Basically a wrapper for
+ * edit_add(). If fileptr != current, then index is considered 0.
+ * The line will be displayed starting with fileptr->data[index].
+ * Likely args are current_x or 0. */
+void update_line(filestruct *fileptr, int index)
+{
+ int line;
+ /* line in the edit window for CURSES calls */
+#ifndef NANO_SMALL
+ int virt_cur_x;
+ int virt_mark_beginx;
+#endif
+ char *original;
+ /* The original string fileptr->data. */
+ char *converted;
+ /* fileptr->data converted to have tabs and control characters
+ * expanded. */
+ size_t pos;
+ size_t page_start;
+
+ if (fileptr == NULL)
+ return;
+
+ line = fileptr->lineno - edittop->lineno;
+
+ /* We assume the line numbers are valid. Is that really true? */
+ assert(line < 0 || line == check_linenumbers(fileptr));
+
+ if (line < 0 || line >= editwinrows)
+ return;
+
+ /* First, blank out the line (at a minimum) */
+ mvwaddstr(edit, line, 0, hblank);
+
+ original = fileptr->data;
+ converted = charalloc(strlenpt(original) + 1);
+
+ /* Next, convert all the tabs to spaces, so everything else is easy.
+ * Note the internal speller sends us index == -1. */
+ index = fileptr == current && index > 0 ? strnlenpt(original, index) : 0;
+#ifndef NANO_SMALL
+ virt_cur_x = fileptr == current ? strnlenpt(original, current_x) : current_x;
+ virt_mark_beginx = fileptr == mark_beginbuf ? strnlenpt(original, mark_beginx) : mark_beginx;
+#endif
+
+ pos = 0;
+ for (; *original != '\0'; original++) {
+ if (*original == '\t')
+ do {
+ converted[pos++] = ' ';
+ } while (pos % tabsize);
+ else if (is_cntrl_char(*original)) {
+ converted[pos++] = '^';
+ if (*original == 127)
+ converted[pos++] = '?';
+ else if (*original == '\n')
+ /* Treat newlines (ASCII 10's) embedded in a line as encoded
+ * nulls (ASCII 0's); the line in question should be run
+ * through unsunder() before reaching here */
+ converted[pos++] = '@';
+ else
+ converted[pos++] = *original + 64;
+ } else
+ converted[pos++] = *original;
+ }
+ converted[pos] = '\0';
+
+ /* Now, paint the line */
+ original = fileptr->data;
+ fileptr->data = converted;
+ page_start = get_page_start(index);
+ edit_add(fileptr, line, page_start
+#ifndef NANO_SMALL
+ , virt_mark_beginx, virt_cur_x
+#endif
+ );
+ free(converted);
+ fileptr->data = original;
+
+ if (page_start > 0)
+ mvwaddch(edit, line, 0, '$');
+ if (pos > page_start + COLS)
+ mvwaddch(edit, line, COLS - 1, '$');
+}
+
+/* This function updates current, based on where current_y is;
+ * reset_cursor() does the opposite. */
+void update_cursor(void)
+{
+ int i = 0;
+
+#ifdef DEBUG
+ fprintf(stderr, "Moved to (%d, %d) in edit buffer\n", current_y,
+ current_x);
+#endif
+
+ current = edittop;
+ while (i < current_y && current->next != NULL) {
+ current = current->next;
+ i++;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "current->data = \"%s\"\n", current->data);
+#endif
+}
+
+void center_cursor(void)
+{
+ current_y = editwinrows / 2;
+ wmove(edit, current_y, current_x);
+}
+
+/* Refresh the screen without changing the position of lines. */
+void edit_refresh(void)
+{
+ /* Neither of these conditions should occur, but they do. edittop is
+ * NULL when you open an existing file on the command line, and
+ * ENABLE_COLOR is defined. Yuck. */
+ if (current == NULL)
+ return;
+ if (edittop == NULL)
+ edittop = current;
+
+ if (current->lineno < edittop->lineno ||
+ current->lineno >= edittop->lineno + editwinrows)
+ /* Note that edit_update() changes edittop so that
+ * current->lineno = edittop->lineno + editwinrows / 2. Thus
+ * when it then calls edit_refresh(), there is no danger of
+ * getting an infinite loop. */
+ edit_update(current, CENTER);
+ else {
+ int nlines = 0;
+
+ /* Don't make the cursor jump around the screen whilst updating */
+ leaveok(edit, TRUE);
+
+ editbot = edittop;
+ while (nlines < editwinrows) {
+ update_line(editbot, current_x);
+ nlines++;
+ if (editbot->next == NULL)
+ break;
+ editbot = editbot->next;
+ }
+ while (nlines < editwinrows) {
+ mvwaddstr(edit, nlines, 0, hblank);
+ nlines++;
+ }
+ /* What the hell are we expecting to update the screen if this
+ isn't here? Luck? */
+ wrefresh(edit);
+ leaveok(edit, FALSE);
+ }
+}
+
+/* Same as above, but touch the window first, so everything is
+ * redrawn. */
+void edit_refresh_clearok(void)
+{
+ clearok(edit, TRUE);
+ edit_refresh();
+ clearok(edit, FALSE);
+}
+
+/*
+ * Nice generic routine to update the edit buffer, given a pointer to the
+ * file struct =)
+ */
+void edit_update(filestruct *fileptr, topmidbotnone location)
+{
+ if (fileptr == NULL)
+ return;
+
+ if (location != TOP) {
+ int goal = location == NONE ? current_y - 1 : editwinrows / 2;
+
+ for (; goal >= 0 && fileptr->prev != NULL; goal--)
+ fileptr = fileptr->prev;
+ }
+ edittop = fileptr;
+ fix_editbot();
+
+ edit_refresh();
+}
+
+/*
+ * Ask a question on the statusbar. Answer will be stored in answer
+ * global. Returns -1 on aborted enter, -2 on a blank string, and 0
+ * otherwise, the valid shortcut key caught. Def is any editable text we
+ * want to put up by default.
+ *
+ * New arg tabs tells whether or not to allow tab completion.
+ */
+int statusq(int tabs, const shortcut *s, const char *def,
+#ifndef NANO_SMALL
+ historyheadtype *which_history,
+#endif
+ const char *msg, ...)
+{
+ va_list ap;
+ char *foo = charalloc(COLS - 3);
+ int ret;
+#ifndef DISABLE_TABCOMP
+ int list = 0;
+#endif
+
+ bottombars(s);
+
+ va_start(ap, msg);
+ vsnprintf(foo, COLS - 4, msg, ap);
+ va_end(ap);
+ foo[COLS - 4] = '\0';
+
+ ret = nanogetstr(tabs, foo, def,
+#ifndef NANO_SMALL
+ which_history,
+#endif
+ s
+#ifndef DISABLE_TABCOMP
+ , &list
+#endif
+ );
+ free(foo);
+ resetstatuspos = 0;
+
+ switch (ret) {
+ case NANO_FIRSTLINE_KEY:
+ do_first_line();
+ resetstatuspos = 1;
+ break;
+ case NANO_LASTLINE_KEY:
+ do_last_line();
+ resetstatuspos = 1;
+ break;
+#ifndef DISABLE_JUSTIFY
+ case NANO_PARABEGIN_KEY:
+ do_para_begin();
+ resetstatuspos = 1;
+ break;
+ case NANO_PARAEND_KEY:
+ do_para_end();
+ resetstatuspos = 1;
+ break;
+#endif
+ case NANO_CANCEL_KEY:
+ ret = -1;
+ resetstatuspos = 1;
+ break;
+ }
+ blank_statusbar();
+
+#ifdef DEBUG
+ fprintf(stderr, "I got \"%s\"\n", answer);
+#endif
+
+#ifndef DISABLE_TABCOMP
+ /* if we've done tab completion, there might be a list of
+ filename matches on the edit window at this point; make sure
+ they're cleared off */
+ if (list)
+ edit_refresh();
+#endif
+
+ return ret;
+}
+
+/*
+ * Ask a simple yes/no question on the statusbar. Returns 1 for Y, 0
+ * for N, 2 for All (if all is nonzero when passed in) and -1 for abort
+ * (^C).
+ */
+int do_yesno(int all, int leavecursor, const char *msg, ...)
+{
+ va_list ap;
+ char *foo;
+ int ok = -2;
+ const char *yesstr; /* String of yes characters accepted */
+ const char *nostr; /* Same for no */
+ const char *allstr; /* And all, surprise! */
+
+ /* Yes, no and all are strings of any length. Each string consists of
+ all characters accepted as a valid character for that value.
+ The first value will be the one displayed in the shortcuts. */
+ yesstr = _("Yy");
+ nostr = _("Nn");
+ allstr = _("Aa");
+
+ /* Remove gettext call for keybindings until we clear the thing up */
+ if (!ISSET(NO_HELP)) {
+ char shortstr[3]; /* Temp string for Y, N, A */
+
+ /* Write the bottom of the screen */
+ blank_bottombars();
+
+ sprintf(shortstr, " %c", yesstr[0]);
+ wmove(bottomwin, 1, 0);
+ onekey(shortstr, _("Yes"), 16);
+
+ if (all) {
+ wmove(bottomwin, 1, 16);
+ shortstr[1] = allstr[0];
+ onekey(shortstr, _("All"), 16);
+ }
+
+ wmove(bottomwin, 2, 0);
+ shortstr[1] = nostr[0];
+ onekey(shortstr, _("No"), 16);
+
+ wmove(bottomwin, 2, 16);
+ onekey("^C", _("Cancel"), 16);
+ }
+
+ foo = charalloc(COLS);
+ va_start(ap, msg);
+ vsnprintf(foo, COLS, msg, ap);
+ va_end(ap);
+ foo[COLS - 1] = '\0';
+
+ wattron(bottomwin, A_REVERSE);
+
+ blank_statusbar();
+ mvwaddstr(bottomwin, 0, 0, foo);
+ free(foo);
+
+ wattroff(bottomwin, A_REVERSE);
+
+ wrefresh(bottomwin);
+
+ do {
+ int kbinput = wgetch(edit);
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ MEVENT mevent;
+#endif
+
+ if (kbinput == NANO_CONTROL_C)
+ ok = -1;
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ /* Look ma! We get to duplicate lots of code from do_mouse!! */
+ else if (kbinput == KEY_MOUSE && getmouse(&mevent) != ERR &&
+ wenclose(bottomwin, mevent.y, mevent.x) &&
+ !ISSET(NO_HELP) && mevent.x < 32 &&
+ mevent.y >= editwinrows + 3) {
+ int x = mevent.x /= 16;
+ /* Did we click in the first column of shortcuts, or the
+ second? */
+ int y = mevent.y - editwinrows - 3;
+ /* Did we click in the first row of shortcuts? */
+
+ assert(0 <= x && x <= 1 && 0 <= y && y <= 1);
+ /* x = 0 means they clicked Yes or No.
+ y = 0 means Yes or All. */
+ ok = -2 * x * y + x - y + 1;
+
+ if (ok == 2 && !all)
+ ok = -2;
+ }
+#endif
+ /* Look for the kbinput in the yes, no and (optionally) all str */
+ else if (strchr(yesstr, kbinput) != NULL)
+ ok = 1;
+ else if (strchr(nostr, kbinput) != NULL)
+ ok = 0;
+ else if (all && strchr(allstr, kbinput) != NULL)
+ ok = 2;
+ } while (ok == -2);
+
+ /* Then blank the statusbar. */
+ blank_statusbar_refresh();
+
+ return ok;
+}
+
+int total_refresh(void)
+{
+ clearok(edit, TRUE);
+ clearok(topwin, TRUE);
+ clearok(bottomwin, TRUE);
+ wnoutrefresh(edit);
+ wnoutrefresh(topwin);
+ wnoutrefresh(bottomwin);
+ doupdate();
+ clearok(edit, FALSE);
+ clearok(topwin, FALSE);
+ clearok(bottomwin, FALSE);
+ edit_refresh();
+ titlebar(NULL);
+ return 1;
+}
+
+void display_main_list(void)
+{
+ bottombars(main_list);
+}
+
+void statusbar(const char *msg, ...)
+{
+ va_list ap;
+ char *foo;
+ int start_x = 0;
+ size_t foo_len;
+
+ va_start(ap, msg);
+
+ /* Curses mode is turned off. If we use wmove() now, it will muck up
+ the terminal settings. So we just use vfprintf(). */
+ if (curses_ended) {
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+ return;
+ }
+
+ assert(COLS >= 4);
+ foo = charalloc(COLS - 3);
+
+ vsnprintf(foo, COLS - 3, msg, ap);
+ va_end(ap);
+
+ foo[COLS - 4] = '\0';
+ foo_len = strlen(foo);
+ start_x = (COLS - foo_len - 4) / 2;
+
+ /* Blank out line */
+ blank_statusbar();
+
+ wmove(bottomwin, 0, start_x);
+
+ wattron(bottomwin, A_REVERSE);
+
+ waddstr(bottomwin, "[ ");
+ waddstr(bottomwin, foo);
+ free(foo);
+ waddstr(bottomwin, " ]");
+
+ wattroff(bottomwin, A_REVERSE);
+
+ wrefresh(bottomwin);
+
+ SET(DISABLE_CURPOS);
+ statblank = 26;
+}
+
+/*
+ * If constant is false, the user typed ^C so we unconditionally display
+ * the cursor position. Otherwise, we display it only if the character
+ * position changed, and DISABLE_CURPOS is not set.
+ *
+ * If constant and DISABLE_CURPOS is set, we unset it and update old_i and
+ * old_totsize. That way, we leave the current statusbar alone, but next
+ * time we will display. */
+int do_cursorpos(int constant)
+{
+ const filestruct *fileptr;
+ unsigned long i = 0;
+ static unsigned long old_i = 0;
+ static long old_totsize = -1;
+
+ assert(current != NULL && fileage != NULL && totlines != 0);
+
+ if (old_totsize == -1)
+ old_totsize = totsize;
+
+ for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
+ assert(fileptr != NULL);
+ i += strlen(fileptr->data) + 1;
+ }
+ i += current_x;
+
+ if (constant && ISSET(DISABLE_CURPOS)) {
+ UNSET(DISABLE_CURPOS);
+ old_i = i;
+ old_totsize = totsize;
+ return 0;
+ }
+
+ /* if constant is false, display the position on the statusbar
+ unconditionally; otherwise, only display the position when the
+ character values have changed */
+ if (!constant || old_i != i || old_totsize != totsize) {
+ unsigned long xpt = xplustabs() + 1;
+ unsigned long cur_len = strlenpt(current->data) + 1;
+ int linepct = 100 * current->lineno / totlines;
+ int colpct = 100 * xpt / cur_len;
+ int bytepct = totsize == 0 ? 0 : 100 * i / totsize;
+
+ statusbar(
+ _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%ld (%d%%)"),
+ current->lineno, totlines, linepct,
+ xpt, cur_len, colpct,
+ i, totsize, bytepct);
+ UNSET(DISABLE_CURPOS);
+ }
+
+ old_i = i;
+ old_totsize = totsize;
+
+ reset_cursor();
+ return 0;
+}
+
+int do_cursorpos_void(void)
+{
+ return do_cursorpos(0);
+}
+
+/* Calculate the next line of help_text, starting at ptr. */
+int line_len(const char *ptr)
+{
+ int j = 0;
+
+ while (*ptr != '\n' && *ptr != '\0' && j < COLS - 5) {
+ ptr++;
+ j++;
+ }
+ if (j == COLS - 5) {
+ /* Don't wrap at the first of two spaces following a period. */
+ if (*ptr == ' ' && *(ptr + 1) == ' ')
+ j++;
+ /* Don't print half a word if we've run out of space */
+ while (*ptr != ' ' && j > 0) {
+ ptr--;
+ j--;
+ }
+ /* Word longer than COLS - 5 chars just gets broken */
+ if (j == 0)
+ j = COLS - 5;
+ }
+ assert(j >= 0 && j <= COLS - 4 && (j > 0 || *ptr == '\n'));
+ return j;
+}
+
+/* Our shortcut-list-compliant help function, which is
+ * better than nothing, and dynamic! */
+int do_help(void)
+{
+#ifndef DISABLE_HELP
+ int i, page = 0, kbinput = -1, meta, no_more = 0;
+ int no_help_flag = 0;
+ const shortcut *oldshortcut;
+
+ blank_edit();
+ curs_set(0);
+ wattroff(bottomwin, A_REVERSE);
+ blank_statusbar();
+
+ /* set help_text as the string to display */
+ help_init();
+ assert(help_text != NULL);
+
+ oldshortcut = currshortcut;
+
+ currshortcut = help_list;
+
+ if (ISSET(NO_HELP)) {
+
+ /* Well, if we're going to do this, we should at least
+ do it the right way */
+ no_help_flag = 1;
+ UNSET(NO_HELP);
+ window_init();
+ bottombars(help_list);
+
+ } else
+ bottombars(help_list);
+
+ do {
+ const char *ptr = help_text;
+
+ switch (kbinput) {
+#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
+ case KEY_MOUSE:
+ do_mouse();
+ break;
+#endif
+ case NANO_NEXTPAGE_KEY:
+ case NANO_NEXTPAGE_FKEY:
+ if (!no_more) {
+ blank_edit();
+ page++;
+ }
+ break;
+ case NANO_PREVPAGE_KEY:
+ case NANO_PREVPAGE_FKEY:
+ if (page > 0) {
+ no_more = 0;
+ blank_edit();
+ page--;
+ }
+ break;
+ }
+
+ /* Calculate where in the text we should be, based on the page */
+ for (i = 1; i < page * (editwinrows - 1); i++) {
+ ptr += line_len(ptr);
+ if (*ptr == '\n')
+ ptr++;
+ }
+
+ for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
+ int j = line_len(ptr);
+
+ mvwaddnstr(edit, i, 0, ptr, j);
+ ptr += j;
+ if (*ptr == '\n')
+ ptr++;
+ }
+
+ if (*ptr == '\0') {
+ no_more = 1;
+ continue;
+ }
+ } while ((kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE))) != NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
+
+ currshortcut = oldshortcut;
+
+ if (no_help_flag) {
+ blank_bottombars();
+ wrefresh(bottomwin);
+ SET(NO_HELP);
+ window_init();
+ } else
+ bottombars(currshortcut);
+
+ curs_set(1);
+ edit_refresh();
+
+ /* The help_init() at the beginning allocated help_text, which has
+ now been written to screen. */
+ free(help_text);
+ help_text = NULL;
+
+#elif defined(DISABLE_HELP)
+ nano_disabled_msg();
+#endif
+
+ return 1;
+}
+
+/* Highlight the current word being replaced or spell checked. */
+void do_replace_highlight(int highlight_flag, const char *word)
+{
+ char *highlight_word = NULL;
+ int x, y, word_len;
+
+ highlight_word =
+ mallocstrcpy(highlight_word, ¤t->data[current_x]);
+
+#ifdef HAVE_REGEX_H
+ if (ISSET(USE_REGEXP))
+ /* if we're using regexps, the highlight is the length of the
+ search result, not the length of the regexp string */
+ word_len = regmatches[0].rm_eo - regmatches[0].rm_so;
+ else
+#endif
+ word_len = strlen(word);
+
+ highlight_word[word_len] = '\0';
+
+ /* adjust output when word extends beyond screen */
+
+ x = xplustabs();
+ y = get_page_start(x) + COLS;
+
+ if ((COLS - (y - x) + word_len) > COLS) {
+ highlight_word[y - x - 1] = '$';
+ highlight_word[y - x] = '\0';
+ }
+
+ /* OK display the output */
+
+ reset_cursor();
+
+ if (highlight_flag)
+ wattron(edit, A_REVERSE);
+
+ waddstr(edit, highlight_word);
+
+ if (highlight_flag)
+ wattroff(edit, A_REVERSE);
+
+ free(highlight_word);
+}
+
+/* Fix editbot, based on the assumption that edittop is correct. */
+void fix_editbot(void)
+{
+ int i;
+
+ editbot = edittop;
+ for (i = 0; i < editwinrows && editbot->next != NULL; i++)
+ editbot = editbot->next;
+}
+
+#ifdef DEBUG
+/* Dump the current file structure to stderr */
+void dump_buffer(const filestruct *inptr) {
+ if (inptr == fileage)
+ fprintf(stderr, "Dumping file buffer to stderr...\n");
+ else if (inptr == cutbuffer)
+ fprintf(stderr, "Dumping cutbuffer to stderr...\n");
+ else
+ fprintf(stderr, "Dumping a buffer to stderr...\n");
+
+ while (inptr != NULL) {
+ fprintf(stderr, "(%d) %s\n", inptr->lineno, inptr->data);
+ inptr = inptr->next;
+ }
+}
+#endif /* DEBUG */
+
+#ifdef DEBUG
+void dump_buffer_reverse(void)
+{
+ const filestruct *fileptr = filebot;
+
+ while (fileptr != NULL) {
+ fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data);
+ fileptr = fileptr->prev;
+ }
+}
+#endif /* DEBUG */
+
+#ifdef NANO_EXTRA
+#define CREDIT_LEN 53
+#define XLCREDIT_LEN 8
+
+void do_credits(void)
+{
+ int i, j = 0, k, place = 0, start_x;
+
+ const char *what;
+ const char *xlcredits[XLCREDIT_LEN];
+
+ const char *credits[CREDIT_LEN] = {
+ "0", /* "The nano text editor" */
+ "1", /* "version" */
+ VERSION,
+ "",
+ "2", /* "Brought to you by:" */
+ "Chris Allegretta",
+ "Jordi Mallach",
+ "Adam Rogoyski",
+ "Rob Siemborski",
+ "Rocco Corsi",
+ "David Lawrence Ramsey",
+ "David Benbennick",
+ "Ken Tyler",
+ "Sven Guckes",
+ "Florian König",
+ "Pauli Virtanen",
+ "Daniele Medri",
+ "Clement Laforet",
+ "Tedi Heriyanto",
+ "Bill Soudan",
+ "Christian Weisgerber",
+ "Erik Andersen",
+ "Big Gaute",
+ "Joshua Jensen",
+ "Ryan Krebs",
+ "Albert Chin",
+ "",
+ "3", /* "Special thanks to:" */
+ "Plattsburgh State University",
+ "Benet Laboratories",
+ "Amy Allegretta",
+ "Linda Young",
+ "Jeremy Robichaud",
+ "Richard Kolb II",
+ "4", /* "The Free Software Foundation" */
+ "Linus Torvalds",
+ "5", /* "For ncurses:" */
+ "Thomas Dickey",
+ "Pavel Curtis",
+ "Zeyd Ben-Halim",
+ "Eric S. Raymond",
+ "6", /* "and anyone else we forgot..." */
+ "7", /* "Thank you for using nano!\n" */
+ "", "", "", "",
+ "(c) 1999-2003 Chris Allegretta",
+ "", "", "", "",
+ "http://www.nano-editor.org/"
+ };
+
+ xlcredits[0] = _("The nano text editor");
+ xlcredits[1] = _("version ");
+ xlcredits[2] = _("Brought to you by:");
+ xlcredits[3] = _("Special thanks to:");
+ xlcredits[4] = _("The Free Software Foundation");
+ xlcredits[5] = _("For ncurses:");
+ xlcredits[6] = _("and anyone else we forgot...");
+ xlcredits[7] = _("Thank you for using nano!\n");
+
+ curs_set(0);
+ nodelay(edit, TRUE);
+ blank_bottombars();
+ mvwaddstr(topwin, 0, 0, hblank);
+ blank_edit();
+ wrefresh(edit);
+ wrefresh(bottomwin);
+ wrefresh(topwin);
+
+ while (wgetch(edit) == ERR) {
+ for (k = 0; k <= 1; k++) {
+ blank_edit();
+ for (i = editwinrows / 2 - 1; i >= (editwinrows / 2 - 1 - j);
+ i--) {
+ mvwaddstr(edit, i * 2 - k, 0, hblank);
+
+ if (place - (editwinrows / 2 - 1 - i) < CREDIT_LEN) {
+ what = credits[place - (editwinrows / 2 - 1 - i)];
+
+ /* God I've missed hacking. If what is exactly
+ 1 char long, it's a sentinel for a translated
+ string, so use that instead. This means no
+ thanking people with 1 character long names ;-) */
+ if (strlen(what) == 1)
+ what = xlcredits[atoi(what)];
+ } else
+ what = "";
+
+ start_x = COLS / 2 - strlen(what) / 2 - 1;
+ mvwaddstr(edit, i * 2 - k, start_x, what);
+ }
+ usleep(700000);
+ wrefresh(edit);
+ }
+ if (j < editwinrows / 2 - 1)
+ j++;
+
+ place++;
+
+ if (place >= CREDIT_LEN + editwinrows / 2)
+ break;
+ }
+
+ nodelay(edit, FALSE);
+ curs_set(1);
+ display_main_list();
+ total_refresh();
+}
+#endif
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * utils.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-#ifdef BROKEN_REGEXEC
-#undef regexec
-int regexec_safe(const regex_t *preg, const char *string, size_t nmatch,
- regmatch_t pmatch[], int eflags)
-{
- if (string != NULL && *string != '\0')
- return regexec(preg, string, nmatch, pmatch, eflags);
- return REG_NOMATCH;
-}
-#define regexec(preg, string, nmatch, pmatch, eflags) regexec_safe(preg, string, nmatch, pmatch, eflags)
-#endif
-
-int is_cntrl_char(int c)
-{
- return (-128 <= c && c < -96) || (0 <= c && c < 32) ||
- (127 <= c && c < 160);
-}
-
-int num_of_digits(int n)
-{
- int i = 1;
-
- if (n < 0)
- n = -n;
-
- while (n > 10) {
- n /= 10;
- i++;
- }
-
- return i;
-}
-
-/* Fix the memory allocation for a string. */
-void align(char **strp)
-{
- assert(strp != NULL);
- if (*strp != NULL)
- *strp = charealloc(*strp, strlen(*strp) + 1);
-}
-
-/* Null a string at a certain index and align it. */
-void null_at(char **data, size_t index)
-{
- assert(data != NULL);
- *data = charealloc(*data, index + 1);
- (*data)[index] = '\0';
-}
-
-/* For non-null-terminated lines. A line, by definition, shouldn't
- * normally have newlines in it, so encode its nulls as newlines. */
-void unsunder(char *str, size_t true_len)
-{
- assert(str != NULL);
- for(; true_len > 0; true_len--, str++)
- if (*str == '\0')
- *str = '\n';
-}
-
-/* For non-null-terminated lines. A line, by definition, shouldn't
- * normally have newlines in it, so decode its newlines into nulls. */
-void sunder(char *str)
-{
- assert(str != NULL);
- for(; *str != '\0'; str++)
- if (*str == '\n')
- *str = '\0';
-}
-
-#ifndef HAVE_STRCASECMP
-/* This function is equivalent to strcasecmp(). */
-int nstricmp(const char *s1, const char *s2)
-{
- assert(s1 != NULL && s2 != NULL);
- for (; *s1 != '\0' && *s2 != '\0'; s1++, s2++) {
- if (tolower(*s1) != tolower(*s2))
- break;
- }
- return (tolower(*s1) - tolower(*s2));
-}
-#endif
-
-#ifndef HAVE_STRNCASECMP
-/* This function is equivalent to strncasecmp(). */
-int nstrnicmp(const char *s1, const char *s2, size_t n)
-{
- assert(s1 != NULL && s2 != NULL);
- for (; n > 0 && *s1 != '\0' && *s2 != '\0'; n--, s1++, s2++) {
- if (tolower(*s1) != tolower(*s2))
- break;
- }
- if (n > 0)
- return (tolower(*s1) - tolower(*s2));
- else if (n == 0)
- return 0;
- else if (n < 0)
- return -1;
-}
-#endif
-
-/* None of this is needed if we're using NANO_SMALL! */
-#ifndef NANO_SMALL
-const char *revstrstr(const char *haystack, const char *needle,
- const char *rev_start)
-{
- for(; rev_start >= haystack ; rev_start--) {
- const char *r, *q;
-
- for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++)
- ;
- if (*q == '\0')
- return rev_start;
- }
- return NULL;
-}
-
-const char *revstristr(const char *haystack, const char *needle,
- const char *rev_start)
-{
- for (; rev_start >= haystack; rev_start--) {
- const char *r = rev_start, *q = needle;
-
- for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++)
- ;
- if (*q == '\0')
- return rev_start;
- }
- return NULL;
-}
-#endif /* !NANO_SMALL */
-
-/* This is now mutt's version (called mutt_stristr) because it doesn't
- * use memory allocation to do a simple search (yuck). */
-const char *stristr(const char *haystack, const char *needle)
-{
- const char *p, *q;
-
- if (haystack == NULL)
- return NULL;
- if (needle == NULL)
- return haystack;
-
- while (*(p = haystack) != '\0') {
- for (q = needle; *p != 0 && *q != 0 && tolower(*p) == tolower(*q); p++, q++)
- ;
- if (*q == 0)
- return haystack;
- haystack++;
- }
- return NULL;
-}
-
-/* If we are searching backwards, we will find the last match
- * that starts no later than rev_start. If we are doing a regexp search,
- * then line_pos should be 0 if haystack starts at the beginning of a
- * line, and positive otherwise. In the regexp case, we fill in the
- * global variable regmatches with at most 9 subexpression matches. Also,
- * all .rm_so elements are relative to the start of the whole match, so
- * regmatches[0].rm_so == 0. */
-const char *strstrwrapper(const char *haystack, const char *needle,
- const char *rev_start, int line_pos)
-{
-#ifdef HAVE_REGEX_H
- if (ISSET(USE_REGEXP)) {
-#ifndef NANO_SMALL
- if (ISSET(REVERSE_SEARCH)) {
- /* When doing a backwards search, haystack is a whole line. */
- if (!regexec(&search_regexp, haystack, 1, regmatches, 0) &&
- haystack + regmatches[0].rm_so <= rev_start) {
- const char *retval = haystack + regmatches[0].rm_so;
-
- /* Search forward until there is no more match. */
- while (!regexec(&search_regexp, retval + 1, 1, regmatches,
- REG_NOTBOL) &&
- retval + 1 + regmatches[0].rm_so <= rev_start)
- retval += 1 + regmatches[0].rm_so;
- /* Finally, put the subexpression matches in global
- * variable regmatches. The REG_NOTBOL flag doesn't
- * matter now. */
- regexec(&search_regexp, retval, 10, regmatches, 0);
- return retval;
- }
- } else
-#endif /* !NANO_SMALL */
- if (!regexec(&search_regexp, haystack, 10, regmatches,
- line_pos > 0 ? REG_NOTBOL : 0)) {
- const char *retval = haystack + regmatches[0].rm_so;
-
- regexec(&search_regexp, retval, 10, regmatches, 0);
- return retval;
- }
- return NULL;
- }
-#endif /* HAVE_REGEX_H */
-#ifndef NANO_SMALL
- if (ISSET(CASE_SENSITIVE)) {
- if (ISSET(REVERSE_SEARCH))
- return revstrstr(haystack, needle, rev_start);
- else
- return strstr(haystack, needle);
- } else if (ISSET(REVERSE_SEARCH))
- return revstristr(haystack, needle, rev_start);
-#endif
- return stristr(haystack, needle);
-}
-
-/* This is a wrapper for the perror function. The wrapper takes care of
- * ncurses, calls perror (which writes to STDERR), then refreshes the
- * screen. Note that nperror causes the window to flicker once. */
-void nperror(const char *s)
-{
- /* leave ncurses mode, go to the terminal */
- if (endwin() != ERR) {
- perror(s); /* print the error */
- total_refresh(); /* return to ncurses and repaint */
- }
-}
-
-/* Thanks BG, many ppl have been asking for this... */
-void *nmalloc(size_t howmuch)
-{
- void *r = malloc(howmuch);
-
- if (r == NULL && howmuch != 0)
- die(_("nano is out of memory!"));
-
- return r;
-}
-
-void *nrealloc(void *ptr, size_t howmuch)
-{
- void *r = realloc(ptr, howmuch);
-
- if (r == NULL && howmuch != 0)
- die(_("nano is out of memory!"));
-
- return r;
-}
-
-/* Copy one malloc()ed string to another pointer. Should be used as:
- * dest = mallocstrcpy(dest, src); */
-char *mallocstrcpy(char *dest, const char *src)
-{
- if (src == dest)
- return dest;
-
- if (dest != NULL)
- free(dest);
-
- if (src == NULL)
- return NULL;
-
- dest = charalloc(strlen(src) + 1);
- strcpy(dest, src);
-
- return dest;
-}
-
-/* Append a new magic-line to filebot. */
-void new_magicline(void)
-{
- filebot->next = (filestruct *)nmalloc(sizeof(filestruct));
- filebot->next->data = charalloc(1);
- filebot->next->data[0] = '\0';
- filebot->next->prev = filebot;
- filebot->next->next = NULL;
- filebot->next->lineno = filebot->lineno + 1;
- filebot = filebot->next;
- totlines++;
- totsize++;
-}
-
-#ifndef DISABLE_TABCOMP
-/*
- * Routine to see if a text string is matched by a wildcard pattern.
- * Returns TRUE if the text is matched, or FALSE if it is not matched
- * or if the pattern is invalid.
- * * matches zero or more characters
- * ? matches a single character
- * [abc] matches 'a', 'b' or 'c'
- * \c quotes character c
- * Adapted from code written by Ingo Wilken, and
- * then taken from sash, Copyright (c) 1999 by David I. Bell
- * Permission is granted to use, distribute, or modify this source,
- * provided that this copyright notice remains intact.
- * Permission to distribute this code under the GPL has been granted.
- */
-int check_wildcard_match(const char *text, const char *pattern)
-{
- const char *retrypat;
- const char *retrytext;
- int ch;
- int found;
- int len;
-
- retrypat = NULL;
- retrytext = NULL;
-
- while (*text != '\0' || *pattern != '\0') {
- ch = *pattern++;
-
- switch (ch) {
- case '*':
- retrypat = pattern;
- retrytext = text;
- break;
-
- case '[':
- found = FALSE;
-
- while ((ch = *pattern++) != ']') {
- if (ch == '\\')
- ch = *pattern++;
-
- if (ch == '\0')
- return FALSE;
-
- if (*text == ch)
- found = TRUE;
- }
- len = strlen(text);
- if (found == FALSE && len != 0) {
- return FALSE;
- }
- if (found == TRUE) {
- if (strlen(pattern) == 0 && len == 1) {
- return TRUE;
- }
- if (len != 0) {
- text++;
- continue;
- }
- }
-
- /* fall into next case */
-
- case '?':
- if (*text++ == '\0')
- return FALSE;
-
- break;
-
- case '\\':
- ch = *pattern++;
-
- if (ch == '\0')
- return FALSE;
-
- /* fall into next case */
-
- default:
- if (*text == ch) {
- if (*text != '\0')
- text++;
- break;
- }
-
- if (*text != '\0') {
- pattern = retrypat;
- text = ++retrytext;
- break;
- }
-
- return FALSE;
- }
-
- if (pattern == NULL)
- return FALSE;
- }
-
- return TRUE;
-}
-#endif
+++ /dev/null
-/* $Id$ */
-/**************************************************************************
- * winio.c *
- * *
- * Copyright (C) 1999-2003 Chris Allegretta *
- * 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. *
- * *
- **************************************************************************/
-
-#include "config.h"
-
-#include <stdarg.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <assert.h>
-#include "proto.h"
-#include "nano.h"
-
-static int statblank = 0; /* Number of keystrokes left after
- we call statusbar(), before we
- actually blank the statusbar */
-
-/* Read in a single input character. If it's ignored, swallow it and go
- * on. Otherwise, try to translate it from ASCII and extended (keypad)
- * input. Assume nodelay(win) is FALSE. */
-int get_kbinput(WINDOW *win, int *meta, int rebind_delete)
-{
- int kbinput, retval;
-
- kbinput = get_ignored_kbinput(win);
- retval = get_accepted_kbinput(win, kbinput, meta, rebind_delete);
-
- return retval;
-}
-
-/* Read in a string of input characters (e. g. an escape sequence)
- * verbatim, and return the length of the string in kbinput_len. Assume
- * nodelay(win) is FALSE. */
-char *get_verbatim_kbinput(WINDOW *win, int *kbinput_len)
-{
- char *verbatim_kbinput;
- int kbinput = wgetch(win);
-
- verbatim_kbinput = charalloc(1);
- verbatim_kbinput[0] = kbinput;
- *kbinput_len = 1;
-
- if (kbinput >= '0' && kbinput <= '2')
- /* Entering a three-digit decimal ASCII code from 000-255 in
- * verbatim mode will produce the corresponding ASCII
- * character. */
- verbatim_kbinput[0] = (char)get_ascii_kbinput(win, kbinput);
- else {
- nodelay(win, TRUE);
- while ((kbinput = wgetch(win)) != ERR) {
- *kbinput_len++;
- verbatim_kbinput = charealloc(verbatim_kbinput, *kbinput_len);
- verbatim_kbinput[*kbinput_len - 1] = (char)kbinput;
- }
- nodelay(win, FALSE);
- }
-
-#ifdef DEBUG
- fprintf(stderr, "get_verbatim_kbinput(): verbatim_kbinput = %s\n", verbatim_kbinput);
-#endif
- return verbatim_kbinput;
-}
-
-/* Swallow input characters that should be quietly ignored, and return
- * the first input character that shouldn't be. */
-int get_ignored_kbinput(WINDOW *win)
-{
- int kbinput;
-
- while (1) {
- kbinput = wgetch(win);
- switch (kbinput) {
- case ERR:
- case KEY_RESIZE:
-#ifdef PDCURSES
- case KEY_SHIFT_L:
- case KEY_SHIFT_R:
- case KEY_CONTROL_L:
- case KEY_CONTROL_R:
- case KEY_ALT_L:
- case KEY_ALT_R:
-#endif
-#ifdef DEBUG
- fprintf(stderr, "get_ignored_kbinput(): kbinput = %d\n", kbinput);
-#endif
- break;
- default:
- return kbinput;
- }
- }
-}
-
-/* Translate acceptable ASCII and extended (keypad) input. Set meta to
- * 1 if we get a Meta sequence. Assume nodelay(win) is FALSE. */
-int get_accepted_kbinput(WINDOW *win, int kbinput, int *meta,
- int rebind_delete)
-{
- *meta = 0;
-
- switch (kbinput) {
- case NANO_CONTROL_3: /* Escape */
- switch (kbinput = wgetch(win)) {
- case NANO_CONTROL_3: /* Escape */
- kbinput = wgetch(win);
- /* Esc Esc [three-digit decimal ASCII code from
- * 000-255] == [corresponding ASCII character];
- Esc Esc 2 obviously can't be Ctrl-2 here */
- if (kbinput >= '0' && kbinput <= '2')
- kbinput = get_ascii_kbinput(win, kbinput);
- /* Esc Esc [character] == Ctrl-[character];
- * Ctrl-Space (Ctrl-2) == Ctrl-@ == Ctrl-` */
- else if (kbinput == ' ' || kbinput == '@' || kbinput == '`')
- kbinput = NANO_CONTROL_SPACE;
- /* Ctrl-3 (Ctrl-[, Esc) to Ctrl-7 (Ctrl-_) */
- else if (kbinput >= '3' && kbinput <= '7')
- kbinput -= 24;
- /* Ctrl-8 (Ctrl-?) */
- else if (kbinput == '8' || kbinput == '?')
- kbinput = NANO_CONTROL_8;
- /* Ctrl-A to Ctrl-_ */
- else if (kbinput >= 'A' && kbinput <= '_')
- kbinput -= 64;
- /* Ctrl-A to Ctrl-Z */
- else if (kbinput >= 'a' && kbinput <= 'z')
- kbinput -= 96;
- break;
- /* Terminal breakage, part 1: We shouldn't get an escape
- * sequence here for terminals that support Delete, but
- * we do sometimes on FreeBSD. Thank you, Wouter van
- * Hemel. */
- case '[':
- nodelay(win, TRUE);
- kbinput = wgetch(win);
- switch (kbinput) {
- case ERR:
- kbinput = '[';
- *meta = 1;
- break;
- default:
- kbinput = get_escape_seq_kbinput(win, kbinput);
- }
- nodelay(win, FALSE);
- break;
- case '`':
- /* Esc Space == Esc ` */
- kbinput = ' ';
- break;
- default:
- /* Esc [character] == Meta-[character] */
- if (isupper(kbinput))
- kbinput = tolower(kbinput);
- *meta = 1;
- }
- break;
- case KEY_DOWN:
- kbinput = NANO_DOWN_KEY;
- break;
- case KEY_UP:
- kbinput = NANO_UP_KEY;
- break;
- case KEY_LEFT:
- kbinput = NANO_BACK_KEY;
- break;
- case KEY_RIGHT:
- kbinput = NANO_FORWARD_KEY;
- break;
- case KEY_HOME:
- kbinput = NANO_HOME_KEY;
- break;
- case KEY_BACKSPACE:
- kbinput = NANO_BACKSPACE_KEY;
- break;
- case KEY_DC:
- /* Terminal breakage, part 2: We should only get KEY_DC when
- * hitting Delete, but we get it when hitting Backspace
- * sometimes on FreeBSD. Thank you, Lee Nelson. */
- kbinput = (rebind_delete) ? NANO_BACKSPACE_KEY : NANO_DELETE_KEY;
- break;
- case KEY_IC:
- kbinput = NANO_INSERTFILE_KEY;
- break;
- case KEY_NPAGE:
- kbinput = NANO_NEXTPAGE_KEY;
- break;
- case KEY_PPAGE:
- kbinput = NANO_PREVPAGE_KEY;
- break;
- case KEY_ENTER:
- kbinput = NANO_ENTER_KEY;
- break;
- case KEY_END:
- kbinput = NANO_END_KEY;
- break;
- case KEY_SUSPEND:
- kbinput = NANO_SUSPEND_KEY;
- break;
- }
-#ifdef DEBUG
- fprintf(stderr, "get_accepted_kbinput(): kbinput = %d, meta = %d\n", kbinput, *meta);
-#endif
- return kbinput;
-}
-
-/* Translate a three-digit decimal ASCII code from 000-255 into the
- * corresponding ASCII character. */
-int get_ascii_kbinput(WINDOW *win, int kbinput)
-{
- int retval;
-
- switch (kbinput) {
- case '0':
- case '1':
- case '2':
- retval = (kbinput - 48) * 100;
- break;
- default:
- return kbinput;
- }
-
- kbinput = wgetch(win);
- switch (kbinput) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- retval += (kbinput - 48) * 10;
- break;
- case '6':
- case '7':
- case '8':
- case '9':
- if (retval < 200) {
- retval += (kbinput - 48) * 10;
- break;
- }
- default:
- return kbinput;
- }
-
- kbinput = wgetch(win);
- switch (kbinput) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- retval += kbinput - 48;
- break;
- case '6':
- case '7':
- case '8':
- case '9':
- if (retval < 250) {
- retval += kbinput - 48;
- break;
- }
- default:
- return kbinput;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "get_ascii_kbinput(): kbinput = %d\n", kbinput);
-#endif
- return retval;
-}
-
-/* Translate common escape sequences for some keys. These are generated
- * when the terminal doesn't support those keys. Assume nodelay(win) is
- * TRUE. */
-int get_escape_seq_kbinput(WINDOW *win, int kbinput)
-{
- switch (kbinput) {
- case '3':
- /* Esc [ 3 ~ == kdch1 on many terminals. */
- kbinput = get_skip_tilde_kbinput(win, kbinput, NANO_DELETE_KEY);
- break;
- }
- return kbinput;
-}
-
-/* If there is no next character, return the passed-in error value. If
- * the next character's a tilde, eat it and return the passed-in
- * return value. Otherwise, return the next character. Assume
- * nodelay(win) is TRUE. */
-int get_skip_tilde_kbinput(WINDOW *win, int errval, int retval)
-{
- int kbinput = wgetch(win);
- switch (kbinput) {
- case ERR:
- return errval;
- case '~':
- return retval;
- default:
- return kbinput;
- }
-}
-
-int do_first_line(void)
-{
- current = fileage;
- placewewant = 0;
- current_x = 0;
- edit_update(current, CENTER);
- return 1;
-}
-
-int do_last_line(void)
-{
- current = filebot;
- placewewant = 0;
- current_x = 0;
- edit_update(current, CENTER);
- return 1;
-}
-
-/* Return the placewewant associated with current_x. That is, xplustabs
- * is the zero-based column position of the cursor. Value is no smaller
- * than current_x. */
-size_t xplustabs(void)
-{
- return strnlenpt(current->data, current_x);
-}
-
-/* Return what current_x should be, given xplustabs() for the line. */
-size_t actual_x(const filestruct *fileptr, size_t xplus)
-{
- size_t i = 0;
- /* the position in fileptr->data, returned */
- size_t length = 0;
- /* the screen display width to data[i] */
- char *c;
- /* fileptr->data + i */
-
- assert(fileptr != NULL && fileptr->data != NULL);
-
- for (c = fileptr->data; length < xplus && *c != '\0'; i++, c++) {
- if (*c == '\t')
- length += tabsize - length % tabsize;
- else if (is_cntrl_char((int)*c))
- length += 2;
- else
- length++;
- }
- assert(length == strnlenpt(fileptr->data, i));
- assert(i <= strlen(fileptr->data));
-
- if (length > xplus)
- i--;
-
-#ifdef DEBUG
- fprintf(stderr, "actual_x for xplus=%d returns %d\n", xplus, i);
-#endif
-
- return i;
-}
-
-/* A strlen with tabs factored in, similar to xplustabs(). */
-size_t strnlenpt(const char *buf, size_t size)
-{
- size_t length = 0;
-
- if (buf != NULL)
- for (; *buf != '\0' && size != 0; size--, buf++) {
- if (*buf == '\t')
- length += tabsize - (length % tabsize);
- else if (is_cntrl_char((int)*buf))
- length += 2;
- else
- length++;
- }
- return length;
-}
-
-size_t strlenpt(const char *buf)
-{
- return strnlenpt(buf, -1);
-}
-
-void blank_bottombars(void)
-{
- if (!no_help()) {
- mvwaddstr(bottomwin, 1, 0, hblank);
- mvwaddstr(bottomwin, 2, 0, hblank);
- }
-}
-
-void blank_bottomwin(void)
-{
- if (ISSET(NO_HELP))
- return;
-
- mvwaddstr(bottomwin, 1, 0, hblank);
- mvwaddstr(bottomwin, 2, 0, hblank);
-}
-
-void blank_edit(void)
-{
- int i;
- for (i = 0; i <= editwinrows - 1; i++)
- mvwaddstr(edit, i, 0, hblank);
-}
-
-
-void blank_statusbar(void)
-{
- mvwaddstr(bottomwin, 0, 0, hblank);
-}
-
-void blank_statusbar_refresh(void)
-{
- blank_statusbar();
- wrefresh(bottomwin);
-}
-
-void check_statblank(void)
-{
- if (statblank > 1)
- statblank--;
- else if (statblank == 1 && !ISSET(CONSTUPDATE)) {
- statblank--;
- blank_statusbar_refresh();
- }
-}
-
-/* Repaint the statusbar when getting a character in nanogetstr(). buf
- * should be no longer than COLS - 4.
- *
- * Note that we must turn on A_REVERSE here, since do_help() turns it
- * off! */
-void nanoget_repaint(const char *buf, const char *inputbuf, int x)
-{
- int len = strlen(buf) + 2;
- int wid = COLS - len;
-
- assert(wid >= 2);
- assert(0 <= x && x <= strlen(inputbuf));
-
- wattron(bottomwin, A_REVERSE);
- blank_statusbar();
- mvwaddstr(bottomwin, 0, 0, buf);
- waddch(bottomwin, ':');
- waddch(bottomwin, x < wid ? ' ' : '$');
- waddnstr(bottomwin, &inputbuf[wid * (x / wid)], wid);
- wmove(bottomwin, 0, (x % wid) + len);
- wattroff(bottomwin, A_REVERSE);
-}
-
-/* Get the input from the kb; this should only be called from
- * statusq(). */
-int nanogetstr(int allowtabs, const char *buf, const char *def,
-#ifndef NANO_SMALL
- historyheadtype *history_list,
-#endif
- const shortcut *s
-#ifndef DISABLE_TABCOMP
- , int *list
-#endif
- )
-{
- int kbinput;
- int meta;
- static int x = -1;
- /* the cursor position in 'answer' */
- int xend;
- /* length of 'answer', the status bar text */
- int tabbed = 0;
- /* used by input_tab() */
- const shortcut *t;
-
-#ifndef NANO_SMALL
- /* for history */
- char *history = NULL;
- char *currentbuf = NULL;
- char *complete = NULL;
- int last_kbinput = 0;
-
- /* This variable is used in the search history code. use_cb == 0
- means that we're using the existing history and ignoring
- currentbuf. use_cb == 1 means that the entry in answer should be
- moved to currentbuf or restored from currentbuf to answer.
- use_cb == 2 means that the entry in currentbuf should be moved to
- answer or restored from answer to currentbuf. */
- int use_cb = 0;
-#endif
- xend = strlen(def);
-
- /* Only put x at the end of the string if it's uninitialized or if
- it would be past the end of the string as it is. Otherwise,
- leave it alone. This is so the cursor position stays at the same
- place if a prompt-changing toggle is pressed. */
- if (x == -1 || x > xend || resetstatuspos)
- x = xend;
-
- answer = charealloc(answer, xend + 1);
- if (xend > 0)
- strcpy(answer, def);
- else
- answer[0] = '\0';
-
-#if !defined(DISABLE_HELP) || (!defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION))
- currshortcut = s;
-#endif
-
- /* Get the input! */
-
- nanoget_repaint(buf, answer, x);
-
- /* Make sure any editor screen updates are displayed before getting
- input */
- wrefresh(edit);
-
- while ((kbinput = get_kbinput(bottomwin, &meta, ISSET(REBIND_DELETE))) != NANO_ENTER_KEY) {
- for (t = s; t != NULL; t = t->next) {
-#ifdef DEBUG
- fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, kbinput);
-#endif
-
- if (kbinput == t->val && kbinput < 32) {
-
-#ifndef DISABLE_HELP
- /* Have to do this here, it would be too late to do it
- in statusq() */
- if (kbinput == NANO_HELP_KEY || kbinput == NANO_HELP_FKEY) {
- do_help();
- break;
- }
-#endif
-
- return t->val;
- }
- }
- assert(0 <= x && x <= xend && xend == strlen(answer));
-
- if (kbinput != '\t')
- tabbed = 0;
-
- switch (kbinput) {
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- case KEY_MOUSE:
- do_mouse();
- break;
-#endif
- case NANO_HOME_KEY:
- x = 0;
- break;
- case NANO_END_KEY:
- x = xend;
- break;
- case NANO_FORWARD_KEY:
- if (x < xend)
- x++;
- break;
- case NANO_DELETE_KEY:
- if (x < xend) {
- memmove(answer + x, answer + x + 1, xend - x);
- xend--;
- }
- break;
- case NANO_CUT_KEY:
- case NANO_UNCUT_KEY:
- null_at(&answer, 0);
- xend = 0;
- x = 0;
- break;
- case NANO_BACKSPACE_KEY:
- if (x > 0) {
- memmove(answer + x - 1, answer + x, xend - x + 1);
- x--;
- xend--;
- }
- break;
- case NANO_TAB_KEY:
-#ifndef NANO_SMALL
- /* tab history completion */
- if (history_list != NULL) {
- if (!complete || last_kbinput != NANO_TAB_KEY) {
- history_list->current = (historytype *)history_list;
- history_list->len = strlen(answer);
- }
-
- if (history_list->len > 0) {
- complete = get_history_completion(history_list, answer);
- xend = strlen(complete);
- x = xend;
- answer = mallocstrcpy(answer, complete);
- }
- }
-#ifndef DISABLE_TABCOMP
- else
-#endif
-#endif
-#ifndef DISABLE_TABCOMP
- if (allowtabs) {
- int shift = 0;
-
- answer = input_tab(answer, x, &tabbed, &shift, list);
- xend = strlen(answer);
- x += shift;
- if (x > xend)
- x = xend;
- }
-#endif
- break;
- case NANO_BACK_KEY:
- if (x > 0)
- x--;
- break;
- case NANO_UP_KEY:
-#ifndef NANO_SMALL
- if (history_list != NULL) {
-
- /* if currentbuf is NULL, or if use_cb is 1, currentbuf
- isn't NULL, and currentbuf is different from answer,
- it means that we're scrolling up at the top of the
- search history, and we need to save the current
- answer in currentbuf; do this and reset use_cb to
- 0 */
- if (currentbuf == NULL || (use_cb == 1 && strcmp(currentbuf, answer))) {
- currentbuf = mallocstrcpy(currentbuf, answer);
- use_cb = 0;
- }
-
- /* if currentbuf isn't NULL, use_cb is 2, and currentbuf
- is different from answer, it means that we're
- scrolling up at the bottom of the search history, and
- we need to make the string in currentbuf the current
- answer; do this, blow away currentbuf since we don't
- need it anymore, and reset use_cb to 0 */
- if (currentbuf != NULL && use_cb == 2 && strcmp(currentbuf, answer)) {
- answer = mallocstrcpy(answer, currentbuf);
- free(currentbuf);
- currentbuf = NULL;
- xend = strlen(answer);
- use_cb = 0;
-
- /* else get older search from the history list and save
- it in answer; if there is no older search, blank out
- answer */
- } else if ((history = get_history_older(history_list)) != NULL) {
- answer = mallocstrcpy(answer, history);
- xend = strlen(history);
- } else {
- answer = mallocstrcpy(answer, "");
- xend = 0;
- }
- x = xend;
- }
-#endif
- break;
- case NANO_DOWN_KEY:
-#ifndef NANO_SMALL
- if (history_list != NULL) {
-
- /* get newer search from the history list and save it
- in answer */
- if ((history = get_history_newer(history_list)) != NULL) {
- answer = mallocstrcpy(answer, history);
- xend = strlen(history);
-
- /* if there is no newer search, we're here */
-
- /* if currentbuf isn't NULL and use_cb isn't 2, it means
- that we're scrolling down at the bottom of the search
- history and we need to make the string in currentbuf
- the current answer; do this, blow away currentbuf
- since we don't need it anymore, and set use_cb to
- 1 */
- } else if (currentbuf != NULL && use_cb != 2) {
- answer = mallocstrcpy(answer, currentbuf);
- free(currentbuf);
- currentbuf = NULL;
- xend = strlen(answer);
- use_cb = 1;
-
- /* otherwise, if currentbuf is NULL and use_cb isn't 2,
- it means that we're scrolling down at the bottom of
- the search history and the current answer (if it's
- not blank) needs to be saved in currentbuf; do this,
- blank out answer (if necessary), and set use_cb to
- 2 */
- } else if (use_cb != 2) {
- if (answer[0] != '\0') {
- currentbuf = mallocstrcpy(currentbuf, answer);
- answer = mallocstrcpy(answer, "");
- }
- xend = 0;
- use_cb = 2;
- }
- x = xend;
- }
-#endif
- break;
- default:
-
- for (t = s; t != NULL; t = t->next) {
-#ifdef DEBUG
- fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput,
- kbinput);
-#endif
- if (meta == 1 && (kbinput == t->val || kbinput == t->val - 32))
- /* We hit an Alt key. Do like above. We don't
- just ungetch() the letter and let it get
- caught above cause that screws the
- keypad... */
- return t->val;
- }
-
- if (kbinput < 32)
- break;
- answer = charealloc(answer, xend + 2);
- memmove(answer + x + 1, answer + x, xend - x + 1);
- xend++;
- answer[x] = kbinput;
- x++;
-
-#ifdef DEBUG
- fprintf(stderr, "input \'%c\' (%d)\n", kbinput, kbinput);
-#endif
- } /* switch (kbinput) */
-#ifndef NANO_SMALL
- last_kbinput = kbinput;
-#endif
- nanoget_repaint(buf, answer, x);
- wrefresh(bottomwin);
- } /* while (kbinput ...) */
-
- /* We finished putting in an answer; reset x */
- x = -1;
-
- /* Just check for a blank answer here */
- if (answer[0] == '\0')
- return -2;
- else
- return 0;
-}
-
-/* If modified is not already set, set it and update titlebar. */
-void set_modified(void)
-{
- if (!ISSET(MODIFIED)) {
- SET(MODIFIED);
- titlebar(NULL);
- wrefresh(topwin);
- }
-}
-
-void titlebar(const char *path)
-{
- int namelen, space;
- const char *what = path;
-
- if (path == NULL)
- what = filename;
-
- wattron(topwin, A_REVERSE);
-
- mvwaddstr(topwin, 0, 0, hblank);
- mvwaddnstr(topwin, 0, 2, VERMSG, COLS - 3);
-
- space = COLS - sizeof(VERMSG) - 23;
-
- namelen = strlen(what);
-
- if (space > 0) {
- if (what[0] == '\0')
- mvwaddnstr(topwin, 0, COLS / 2 - 6, _("New Buffer"),
- COLS / 2 + COLS % 2 - 6);
- else if (namelen > space) {
- if (path == NULL)
- waddstr(topwin, _(" File: ..."));
- else
- waddstr(topwin, _(" DIR: ..."));
- waddstr(topwin, &what[namelen - space]);
- } else {
- if (path == NULL)
- mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1),
- _("File: "));
- else
- mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1),
- _(" DIR: "));
- waddstr(topwin, what);
- }
- } /* If we don't have space, we shouldn't bother */
- if (ISSET(MODIFIED))
- mvwaddnstr(topwin, 0, COLS - 11, _(" Modified "), 11);
- else if (ISSET(VIEW_MODE))
- mvwaddnstr(topwin, 0, COLS - 11, _(" View "), 11);
-
- wattroff(topwin, A_REVERSE);
-
- wrefresh(topwin);
- reset_cursor();
-}
-
-void bottombars(const shortcut *s)
-{
- int i, j, numcols;
- char keystr[9];
- int slen;
-
- if (ISSET(NO_HELP))
- return;
-
- if (s == main_list) {
- slen = MAIN_VISIBLE;
- assert(MAIN_VISIBLE <= length_of_list(s));
- } else
- slen = length_of_list(s);
-
- /* There will be this many columns of shortcuts */
- numcols = (slen + (slen % 2)) / 2;
-
- blank_bottomwin();
-
- for (i = 0; i < numcols; i++) {
- for (j = 0; j <= 1; j++) {
-
- wmove(bottomwin, 1 + j, i * (COLS / numcols));
-
- /* Yucky sentinel values we can't handle a better way */
- if (s->val == NANO_CONTROL_SPACE)
- strcpy(keystr, "^ ");
-#ifndef NANO_SMALL
- else if (s->val == KEY_UP)
- strncpy(keystr, _("Up"), 8);
-#endif /* NANO_SMALL */
- else if (s->val > 0) {
- if (s->val < 64)
- sprintf(keystr, "^%c", s->val + 64);
- else
- sprintf(keystr, "M-%c", s->val - 32);
- } else if (s->altval > 0)
- sprintf(keystr, "M-%c", s->altval);
-
- onekey(keystr, s->desc, COLS / numcols);
-
- s = s->next;
- if (s == NULL)
- goto break_completely_out;
- }
- }
-
- break_completely_out:
- wrefresh(bottomwin);
-}
-
-/* Write a shortcut key to the help area at the bottom of the window.
- * keystroke is e.g. "^G" and desc is e.g. "Get Help".
- * We are careful to write exactly len characters, even if len is
- * very small and keystroke and desc are long. */
-void onekey(const char *keystroke, const char *desc, int len)
-{
-
- wattron(bottomwin, A_REVERSE);
- waddnstr(bottomwin, keystroke, len);
- wattroff(bottomwin, A_REVERSE);
- len -= strlen(keystroke);
- if (len > 0) {
- waddch(bottomwin, ' ');
- len--;
- waddnstr(bottomwin, desc, len);
- len -= strlen(desc);
- for (; len > 0; len--)
- waddch(bottomwin, ' ');
- }
-}
-
-/* And so start the display update routines. */
-
-#ifndef NDEBUG
-int check_linenumbers(const filestruct *fileptr)
-{
- int check_line = 0;
- const filestruct *filetmp;
-
- for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next)
- check_line++;
- return check_line;
-}
-#endif
-
- /* nano scrolls horizontally within a line in chunks. This function
- * returns the column number of the first character displayed in the
- * window when the cursor is at the given column. */
-int get_page_start(int column)
-{
- assert(COLS > 9);
- return column < COLS - 1 ? 0 : column - 7 - (column - 8) % (COLS - 9);
-}
-
-/* Resets current_y, based on the position of current, and puts the
- * cursor at (current_y, current_x). */
-void reset_cursor(void)
-{
- const filestruct *ptr = edittop;
- size_t x;
-
- /* Yuck. This condition can be true after open_file() when opening
- * the first file. */
- if (edittop == NULL)
- return;
-
- current_y = 0;
-
- while (ptr != current && ptr != editbot && ptr->next != NULL) {
- ptr = ptr->next;
- current_y++;
- }
-
- x = xplustabs();
- wmove(edit, current_y, x - get_page_start(x));
-}
-
-/* edit_add() takes care of the job of actually painting a line into
- * the edit window. Called only from update_line(). Expects a
- * converted-to-not-have-tabs line. */
-void edit_add(const filestruct *fileptr, int yval, int start
-#ifndef NANO_SMALL
- , int virt_mark_beginx, int virt_cur_x
-#endif
- )
-{
-#ifdef DEBUG
- fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno,
- current->lineno);
-#endif
-
- /* Just paint the string in any case (we'll add color or reverse on
- just the text that needs it */
- mvwaddnstr(edit, yval, 0, &fileptr->data[start], COLS);
-
-#ifdef ENABLE_COLOR
- if (colorstrings != NULL && ISSET(COLOR_SYNTAX)) {
- const colortype *tmpcolor = colorstrings;
-
- for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) {
- int x_start;
- /* Starting column for mvwaddnstr. Zero-based. */
- int paintlen;
- /* number of chars to paint on this line. There are COLS
- * characters on a whole line. */
- regmatch_t startmatch; /* match position for start_regexp*/
- regmatch_t endmatch; /* match position for end_regexp*/
-
- if (tmpcolor->bright)
- wattron(edit, A_BOLD);
- wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
- /* Two notes about regexec. Return value 0 means there is a
- * match. Also, rm_eo is the first non-matching character
- * after the match. */
-
- /* First case, tmpcolor is a single-line expression. */
- if (tmpcolor->end == NULL) {
- size_t k = 0;
-
- /* We increment k by rm_eo, to move past the end of the
- last match. Even though two matches may overlap, we
- want to ignore them, so that we can highlight C-strings
- correctly. */
- while (k < start + COLS) {
- /* Note the fifth parameter to regexec. It says not to
- * match the beginning-of-line character unless
- * k == 0. If regexec returns nonzero, there are no
- * more matches in the line. */
- if (regexec(&tmpcolor->start, &fileptr->data[k], 1,
- &startmatch, k == 0 ? 0 : REG_NOTBOL))
- break;
- /* Translate the match to the beginning of the line. */
- startmatch.rm_so += k;
- startmatch.rm_eo += k;
- if (startmatch.rm_so == startmatch.rm_eo) {
- startmatch.rm_eo++;
- statusbar(_("Refusing 0 length regex match"));
- } else if (startmatch.rm_so < start + COLS &&
- startmatch.rm_eo > start) {
- x_start = startmatch.rm_so - start;
- if (x_start < 0)
- x_start = 0;
- paintlen = startmatch.rm_eo - start - x_start;
- if (paintlen > COLS - x_start)
- paintlen = COLS - x_start;
-
- assert(0 <= x_start && 0 < paintlen &&
- x_start + paintlen <= COLS);
- mvwaddnstr(edit, yval, x_start,
- fileptr->data + start + x_start, paintlen);
- }
- k = startmatch.rm_eo;
- }
- } else {
- /* This is a multi-line regexp. There are two steps.
- * First, we have to see if the beginning of the line is
- * colored by a start on an earlier line, and an end on
- * this line or later.
- *
- * We find the first line before fileptr matching the
- * start. If every match on that line is followed by an
- * end, then go to step two. Otherwise, find the next line
- * after start_line matching the end. If that line is not
- * before fileptr, then paint the beginning of this line. */
-
- const filestruct *start_line = fileptr->prev;
- /* the first line before fileptr matching start*/
- regoff_t start_col;
- /* where it starts in that line */
- const filestruct *end_line;
- int searched_later_lines = 0;
- /* Used in step 2. Have we looked for an end on
- * lines after fileptr? */
-
- while (start_line != NULL &&
- regexec(&tmpcolor->start, start_line->data, 1,
- &startmatch, 0)) {
- /* If there is an end on this line, there is no need
- * to look for starts on earlier lines. */
- if (!regexec(tmpcolor->end, start_line->data, 1,
- &endmatch, 0))
- goto step_two;
- start_line = start_line->prev;
- }
- /* No start found, so skip to the next step. */
- if (start_line == NULL)
- goto step_two;
- /* Now start_line is the first line before fileptr
- * containing a start match. Is there a start on this
- * line not followed by an end on this line? */
-
- start_col = 0;
- while (1) {
- start_col += startmatch.rm_so;
- startmatch.rm_eo -= startmatch.rm_so;
- if (regexec(tmpcolor->end,
- start_line->data + start_col + startmatch.rm_eo,
- 1, &endmatch,
- start_col + startmatch.rm_eo == 0 ? 0 : REG_NOTBOL))
- /* No end found after this start */
- break;
- start_col++;
- if (regexec(&tmpcolor->start,
- start_line->data + start_col, 1, &startmatch,
- REG_NOTBOL))
- /* No later start on this line. */
- goto step_two;
- }
- /* Indeed, there is a start not followed on this line by an
- * end. */
-
- /* We have already checked that there is no end before
- * fileptr and after the start. Is there an end after
- * the start at all? We don't paint unterminated starts. */
- end_line = fileptr;
- while (end_line != NULL &&
- regexec(tmpcolor->end, end_line->data, 1, &endmatch, 0))
- end_line = end_line->next;
-
- /* No end found, or it is too early. */
- if (end_line == NULL || end_line->lineno < fileptr->lineno ||
- (end_line == fileptr && endmatch.rm_eo <= start))
- goto step_two;
-
- /* Now paint the start of fileptr. */
- paintlen = end_line != fileptr
- ? COLS : endmatch.rm_eo - start;
- if (paintlen > COLS)
- paintlen = COLS;
-
- assert(0 < paintlen && paintlen <= COLS);
- mvwaddnstr(edit, yval, 0, fileptr->data + start, paintlen);
-
- /* We have already painted the whole line. */
- if (paintlen == COLS)
- goto skip_step_two;
-
- step_two: /* Second step, we look for starts on this line. */
- start_col = 0;
- while (start_col < start + COLS) {
- if (regexec(&tmpcolor->start, fileptr->data + start_col, 1,
- &startmatch, start_col == 0 ? 0 : REG_NOTBOL)
- || start_col + startmatch.rm_so >= start + COLS)
- /* No more starts on this line. */
- break;
- /* Translate the match to be relative to the
- * beginning of the line. */
- startmatch.rm_so += start_col;
- startmatch.rm_eo += start_col;
-
- x_start = startmatch.rm_so - start;
- if (x_start < 0) {
- x_start = 0;
- startmatch.rm_so = start;
- }
- if (!regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo,
- 1, &endmatch,
- startmatch.rm_eo == 0 ? 0 : REG_NOTBOL)) {
- /* Translate the end match to be relative to the
- beginning of the line. */
- endmatch.rm_so += startmatch.rm_eo;
- endmatch.rm_eo += startmatch.rm_eo;
- /* There is an end on this line. But does it
- appear on this page, and is the match more than
- zero characters long? */
- if (endmatch.rm_eo > start &&
- endmatch.rm_eo > startmatch.rm_so) {
- paintlen = endmatch.rm_eo - start - x_start;
- if (x_start + paintlen > COLS)
- paintlen = COLS - x_start;
-
- assert(0 <= x_start && 0 < paintlen &&
- x_start + paintlen <= COLS);
- mvwaddnstr(edit, yval, x_start,
- fileptr->data + start + x_start, paintlen);
- }
- } else if (!searched_later_lines) {
- searched_later_lines = 1;
- /* There is no end on this line. But we haven't
- * yet looked for one on later lines. */
- end_line = fileptr->next;
- while (end_line != NULL && regexec(tmpcolor->end,
- end_line->data, 1, &endmatch, 0))
- end_line = end_line->next;
- if (end_line != NULL) {
- assert(0 <= x_start && x_start < COLS);
- mvwaddnstr(edit, yval, x_start,
- fileptr->data + start + x_start,
- COLS - x_start);
- /* We painted to the end of the line, so
- * don't bother checking any more starts. */
- break;
- }
- }
- start_col = startmatch.rm_so + 1;
- } /* while start_col < start + COLS */
- } /* if (tmp_color->end != NULL) */
-
- skip_step_two:
- wattroff(edit, A_BOLD);
- wattroff(edit, COLOR_PAIR(tmpcolor->pairnum));
- } /* for tmpcolor in colorstrings */
- }
-#endif /* ENABLE_COLOR */
-
-#ifndef NANO_SMALL
- if (ISSET(MARK_ISSET)
- && (fileptr->lineno <= mark_beginbuf->lineno
- || fileptr->lineno <= current->lineno)
- && (fileptr->lineno >= mark_beginbuf->lineno
- || fileptr->lineno >= current->lineno)) {
- /* fileptr is at least partially selected. */
-
- int x_start;
- /* Starting column for mvwaddnstr. Zero-based. */
- int paintlen;
- /* number of chars to paint on this line. There are COLS
- * characters on a whole line. */
-
- if (mark_beginbuf == fileptr && current == fileptr) {
- x_start = virt_mark_beginx < virt_cur_x ? virt_mark_beginx
- : virt_cur_x;
- paintlen = abs(virt_mark_beginx - virt_cur_x);
- } else {
- if (mark_beginbuf->lineno < fileptr->lineno ||
- current->lineno < fileptr->lineno)
- x_start = 0;
- else
- x_start = mark_beginbuf == fileptr ? virt_mark_beginx
- : virt_cur_x;
-
- if (mark_beginbuf->lineno > fileptr->lineno ||
- current->lineno > fileptr->lineno)
- paintlen = start + COLS;
- else
- paintlen = mark_beginbuf == fileptr ? virt_mark_beginx
- : virt_cur_x;
- }
- x_start -= start;
- if (x_start < 0) {
- paintlen += x_start;
- x_start = 0;
- }
- if (x_start + paintlen > COLS)
- paintlen = COLS - x_start;
- if (paintlen > 0) {
- wattron(edit, A_REVERSE);
- assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS);
- mvwaddnstr(edit, yval, x_start,
- fileptr->data + start + x_start, paintlen);
- wattroff(edit, A_REVERSE);
- }
- }
-#endif /* !NANO_SMALL */
-}
-
-/* Just update one line in the edit buffer. Basically a wrapper for
- * edit_add(). If fileptr != current, then index is considered 0.
- * The line will be displayed starting with fileptr->data[index].
- * Likely args are current_x or 0. */
-void update_line(filestruct *fileptr, int index)
-{
- int line;
- /* line in the edit window for CURSES calls */
-#ifndef NANO_SMALL
- int virt_cur_x;
- int virt_mark_beginx;
-#endif
- char *original;
- /* The original string fileptr->data. */
- char *converted;
- /* fileptr->data converted to have tabs and control characters
- * expanded. */
- size_t pos;
- size_t page_start;
-
- if (fileptr == NULL)
- return;
-
- line = fileptr->lineno - edittop->lineno;
-
- /* We assume the line numbers are valid. Is that really true? */
- assert(line < 0 || line == check_linenumbers(fileptr));
-
- if (line < 0 || line >= editwinrows)
- return;
-
- /* First, blank out the line (at a minimum) */
- mvwaddstr(edit, line, 0, hblank);
-
- original = fileptr->data;
- converted = charalloc(strlenpt(original) + 1);
-
- /* Next, convert all the tabs to spaces, so everything else is easy.
- * Note the internal speller sends us index == -1. */
- index = fileptr == current && index > 0 ? strnlenpt(original, index) : 0;
-#ifndef NANO_SMALL
- virt_cur_x = fileptr == current ? strnlenpt(original, current_x) : current_x;
- virt_mark_beginx = fileptr == mark_beginbuf ? strnlenpt(original, mark_beginx) : mark_beginx;
-#endif
-
- pos = 0;
- for (; *original != '\0'; original++) {
- if (*original == '\t')
- do {
- converted[pos++] = ' ';
- } while (pos % tabsize);
- else if (is_cntrl_char(*original)) {
- converted[pos++] = '^';
- if (*original == 127)
- converted[pos++] = '?';
- else if (*original == '\n')
- /* Treat newlines (ASCII 10's) embedded in a line as encoded
- * nulls (ASCII 0's); the line in question should be run
- * through unsunder() before reaching here */
- converted[pos++] = '@';
- else
- converted[pos++] = *original + 64;
- } else
- converted[pos++] = *original;
- }
- converted[pos] = '\0';
-
- /* Now, paint the line */
- original = fileptr->data;
- fileptr->data = converted;
- page_start = get_page_start(index);
- edit_add(fileptr, line, page_start
-#ifndef NANO_SMALL
- , virt_mark_beginx, virt_cur_x
-#endif
- );
- free(converted);
- fileptr->data = original;
-
- if (page_start > 0)
- mvwaddch(edit, line, 0, '$');
- if (pos > page_start + COLS)
- mvwaddch(edit, line, COLS - 1, '$');
-}
-
-/* This function updates current, based on where current_y is;
- * reset_cursor() does the opposite. */
-void update_cursor(void)
-{
- int i = 0;
-
-#ifdef DEBUG
- fprintf(stderr, "Moved to (%d, %d) in edit buffer\n", current_y,
- current_x);
-#endif
-
- current = edittop;
- while (i < current_y && current->next != NULL) {
- current = current->next;
- i++;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "current->data = \"%s\"\n", current->data);
-#endif
-}
-
-void center_cursor(void)
-{
- current_y = editwinrows / 2;
- wmove(edit, current_y, current_x);
-}
-
-/* Refresh the screen without changing the position of lines. */
-void edit_refresh(void)
-{
- /* Neither of these conditions should occur, but they do. edittop is
- * NULL when you open an existing file on the command line, and
- * ENABLE_COLOR is defined. Yuck. */
- if (current == NULL)
- return;
- if (edittop == NULL)
- edittop = current;
-
- if (current->lineno < edittop->lineno ||
- current->lineno >= edittop->lineno + editwinrows)
- /* Note that edit_update() changes edittop so that
- * current->lineno = edittop->lineno + editwinrows / 2. Thus
- * when it then calls edit_refresh(), there is no danger of
- * getting an infinite loop. */
- edit_update(current, CENTER);
- else {
- int nlines = 0;
-
- /* Don't make the cursor jump around the screen whilst updating */
- leaveok(edit, TRUE);
-
- editbot = edittop;
- while (nlines < editwinrows) {
- update_line(editbot, current_x);
- nlines++;
- if (editbot->next == NULL)
- break;
- editbot = editbot->next;
- }
- while (nlines < editwinrows) {
- mvwaddstr(edit, nlines, 0, hblank);
- nlines++;
- }
- /* What the hell are we expecting to update the screen if this
- isn't here? Luck? */
- wrefresh(edit);
- leaveok(edit, FALSE);
- }
-}
-
-/* Same as above, but touch the window first, so everything is
- * redrawn. */
-void edit_refresh_clearok(void)
-{
- clearok(edit, TRUE);
- edit_refresh();
- clearok(edit, FALSE);
-}
-
-/*
- * Nice generic routine to update the edit buffer, given a pointer to the
- * file struct =)
- */
-void edit_update(filestruct *fileptr, topmidbotnone location)
-{
- if (fileptr == NULL)
- return;
-
- if (location != TOP) {
- int goal = location == NONE ? current_y - 1 : editwinrows / 2;
-
- for (; goal >= 0 && fileptr->prev != NULL; goal--)
- fileptr = fileptr->prev;
- }
- edittop = fileptr;
- fix_editbot();
-
- edit_refresh();
-}
-
-/*
- * Ask a question on the statusbar. Answer will be stored in answer
- * global. Returns -1 on aborted enter, -2 on a blank string, and 0
- * otherwise, the valid shortcut key caught. Def is any editable text we
- * want to put up by default.
- *
- * New arg tabs tells whether or not to allow tab completion.
- */
-int statusq(int tabs, const shortcut *s, const char *def,
-#ifndef NANO_SMALL
- historyheadtype *which_history,
-#endif
- const char *msg, ...)
-{
- va_list ap;
- char *foo = charalloc(COLS - 3);
- int ret;
-#ifndef DISABLE_TABCOMP
- int list = 0;
-#endif
-
- bottombars(s);
-
- va_start(ap, msg);
- vsnprintf(foo, COLS - 4, msg, ap);
- va_end(ap);
- foo[COLS - 4] = '\0';
-
- ret = nanogetstr(tabs, foo, def,
-#ifndef NANO_SMALL
- which_history,
-#endif
- s
-#ifndef DISABLE_TABCOMP
- , &list
-#endif
- );
- free(foo);
- resetstatuspos = 0;
-
- switch (ret) {
- case NANO_FIRSTLINE_KEY:
- do_first_line();
- resetstatuspos = 1;
- break;
- case NANO_LASTLINE_KEY:
- do_last_line();
- resetstatuspos = 1;
- break;
-#ifndef DISABLE_JUSTIFY
- case NANO_PARABEGIN_KEY:
- do_para_begin();
- resetstatuspos = 1;
- break;
- case NANO_PARAEND_KEY:
- do_para_end();
- resetstatuspos = 1;
- break;
-#endif
- case NANO_CANCEL_KEY:
- ret = -1;
- resetstatuspos = 1;
- break;
- }
- blank_statusbar();
-
-#ifdef DEBUG
- fprintf(stderr, "I got \"%s\"\n", answer);
-#endif
-
-#ifndef DISABLE_TABCOMP
- /* if we've done tab completion, there might be a list of
- filename matches on the edit window at this point; make sure
- they're cleared off */
- if (list)
- edit_refresh();
-#endif
-
- return ret;
-}
-
-/*
- * Ask a simple yes/no question on the statusbar. Returns 1 for Y, 0
- * for N, 2 for All (if all is nonzero when passed in) and -1 for abort
- * (^C).
- */
-int do_yesno(int all, int leavecursor, const char *msg, ...)
-{
- va_list ap;
- char *foo;
- int ok = -2;
- const char *yesstr; /* String of yes characters accepted */
- const char *nostr; /* Same for no */
- const char *allstr; /* And all, surprise! */
-
- /* Yes, no and all are strings of any length. Each string consists of
- all characters accepted as a valid character for that value.
- The first value will be the one displayed in the shortcuts. */
- yesstr = _("Yy");
- nostr = _("Nn");
- allstr = _("Aa");
-
- /* Remove gettext call for keybindings until we clear the thing up */
- if (!ISSET(NO_HELP)) {
- char shortstr[3]; /* Temp string for Y, N, A */
-
- /* Write the bottom of the screen */
- blank_bottombars();
-
- sprintf(shortstr, " %c", yesstr[0]);
- wmove(bottomwin, 1, 0);
- onekey(shortstr, _("Yes"), 16);
-
- if (all) {
- wmove(bottomwin, 1, 16);
- shortstr[1] = allstr[0];
- onekey(shortstr, _("All"), 16);
- }
-
- wmove(bottomwin, 2, 0);
- shortstr[1] = nostr[0];
- onekey(shortstr, _("No"), 16);
-
- wmove(bottomwin, 2, 16);
- onekey("^C", _("Cancel"), 16);
- }
-
- foo = charalloc(COLS);
- va_start(ap, msg);
- vsnprintf(foo, COLS, msg, ap);
- va_end(ap);
- foo[COLS - 1] = '\0';
-
- wattron(bottomwin, A_REVERSE);
-
- blank_statusbar();
- mvwaddstr(bottomwin, 0, 0, foo);
- free(foo);
-
- wattroff(bottomwin, A_REVERSE);
-
- wrefresh(bottomwin);
-
- do {
- int kbinput = wgetch(edit);
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- MEVENT mevent;
-#endif
-
- if (kbinput == NANO_CONTROL_C)
- ok = -1;
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- /* Look ma! We get to duplicate lots of code from do_mouse!! */
- else if (kbinput == KEY_MOUSE && getmouse(&mevent) != ERR &&
- wenclose(bottomwin, mevent.y, mevent.x) &&
- !ISSET(NO_HELP) && mevent.x < 32 &&
- mevent.y >= editwinrows + 3) {
- int x = mevent.x /= 16;
- /* Did we click in the first column of shortcuts, or the
- second? */
- int y = mevent.y - editwinrows - 3;
- /* Did we click in the first row of shortcuts? */
-
- assert(0 <= x && x <= 1 && 0 <= y && y <= 1);
- /* x = 0 means they clicked Yes or No.
- y = 0 means Yes or All. */
- ok = -2 * x * y + x - y + 1;
-
- if (ok == 2 && !all)
- ok = -2;
- }
-#endif
- /* Look for the kbinput in the yes, no and (optionally) all str */
- else if (strchr(yesstr, kbinput) != NULL)
- ok = 1;
- else if (strchr(nostr, kbinput) != NULL)
- ok = 0;
- else if (all && strchr(allstr, kbinput) != NULL)
- ok = 2;
- } while (ok == -2);
-
- /* Then blank the statusbar. */
- blank_statusbar_refresh();
-
- return ok;
-}
-
-int total_refresh(void)
-{
- clearok(edit, TRUE);
- clearok(topwin, TRUE);
- clearok(bottomwin, TRUE);
- wnoutrefresh(edit);
- wnoutrefresh(topwin);
- wnoutrefresh(bottomwin);
- doupdate();
- clearok(edit, FALSE);
- clearok(topwin, FALSE);
- clearok(bottomwin, FALSE);
- edit_refresh();
- titlebar(NULL);
- return 1;
-}
-
-void display_main_list(void)
-{
- bottombars(main_list);
-}
-
-void statusbar(const char *msg, ...)
-{
- va_list ap;
- char *foo;
- int start_x = 0;
- size_t foo_len;
-
- va_start(ap, msg);
-
- /* Curses mode is turned off. If we use wmove() now, it will muck up
- the terminal settings. So we just use vfprintf(). */
- if (curses_ended) {
- vfprintf(stderr, msg, ap);
- va_end(ap);
- return;
- }
-
- assert(COLS >= 4);
- foo = charalloc(COLS - 3);
-
- vsnprintf(foo, COLS - 3, msg, ap);
- va_end(ap);
-
- foo[COLS - 4] = '\0';
- foo_len = strlen(foo);
- start_x = (COLS - foo_len - 4) / 2;
-
- /* Blank out line */
- blank_statusbar();
-
- wmove(bottomwin, 0, start_x);
-
- wattron(bottomwin, A_REVERSE);
-
- waddstr(bottomwin, "[ ");
- waddstr(bottomwin, foo);
- free(foo);
- waddstr(bottomwin, " ]");
-
- wattroff(bottomwin, A_REVERSE);
-
- wrefresh(bottomwin);
-
- SET(DISABLE_CURPOS);
- statblank = 26;
-}
-
-/*
- * If constant is false, the user typed ^C so we unconditionally display
- * the cursor position. Otherwise, we display it only if the character
- * position changed, and DISABLE_CURPOS is not set.
- *
- * If constant and DISABLE_CURPOS is set, we unset it and update old_i and
- * old_totsize. That way, we leave the current statusbar alone, but next
- * time we will display. */
-int do_cursorpos(int constant)
-{
- const filestruct *fileptr;
- unsigned long i = 0;
- static unsigned long old_i = 0;
- static long old_totsize = -1;
-
- assert(current != NULL && fileage != NULL && totlines != 0);
-
- if (old_totsize == -1)
- old_totsize = totsize;
-
- for (fileptr = fileage; fileptr != current; fileptr = fileptr->next) {
- assert(fileptr != NULL);
- i += strlen(fileptr->data) + 1;
- }
- i += current_x;
-
- if (constant && ISSET(DISABLE_CURPOS)) {
- UNSET(DISABLE_CURPOS);
- old_i = i;
- old_totsize = totsize;
- return 0;
- }
-
- /* if constant is false, display the position on the statusbar
- unconditionally; otherwise, only display the position when the
- character values have changed */
- if (!constant || old_i != i || old_totsize != totsize) {
- unsigned long xpt = xplustabs() + 1;
- unsigned long cur_len = strlenpt(current->data) + 1;
- int linepct = 100 * current->lineno / totlines;
- int colpct = 100 * xpt / cur_len;
- int bytepct = totsize == 0 ? 0 : 100 * i / totsize;
-
- statusbar(
- _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%ld (%d%%)"),
- current->lineno, totlines, linepct,
- xpt, cur_len, colpct,
- i, totsize, bytepct);
- UNSET(DISABLE_CURPOS);
- }
-
- old_i = i;
- old_totsize = totsize;
-
- reset_cursor();
- return 0;
-}
-
-int do_cursorpos_void(void)
-{
- return do_cursorpos(0);
-}
-
-/* Calculate the next line of help_text, starting at ptr. */
-int line_len(const char *ptr)
-{
- int j = 0;
-
- while (*ptr != '\n' && *ptr != '\0' && j < COLS - 5) {
- ptr++;
- j++;
- }
- if (j == COLS - 5) {
- /* Don't wrap at the first of two spaces following a period. */
- if (*ptr == ' ' && *(ptr + 1) == ' ')
- j++;
- /* Don't print half a word if we've run out of space */
- while (*ptr != ' ' && j > 0) {
- ptr--;
- j--;
- }
- /* Word longer than COLS - 5 chars just gets broken */
- if (j == 0)
- j = COLS - 5;
- }
- assert(j >= 0 && j <= COLS - 4 && (j > 0 || *ptr == '\n'));
- return j;
-}
-
-/* Our shortcut-list-compliant help function, which is
- * better than nothing, and dynamic! */
-int do_help(void)
-{
-#ifndef DISABLE_HELP
- int i, page = 0, kbinput = -1, meta, no_more = 0;
- int no_help_flag = 0;
- const shortcut *oldshortcut;
-
- blank_edit();
- curs_set(0);
- wattroff(bottomwin, A_REVERSE);
- blank_statusbar();
-
- /* set help_text as the string to display */
- help_init();
- assert(help_text != NULL);
-
- oldshortcut = currshortcut;
-
- currshortcut = help_list;
-
- if (ISSET(NO_HELP)) {
-
- /* Well, if we're going to do this, we should at least
- do it the right way */
- no_help_flag = 1;
- UNSET(NO_HELP);
- window_init();
- bottombars(help_list);
-
- } else
- bottombars(help_list);
-
- do {
- const char *ptr = help_text;
-
- switch (kbinput) {
-#if !defined(DISABLE_MOUSE) && defined(NCURSES_MOUSE_VERSION)
- case KEY_MOUSE:
- do_mouse();
- break;
-#endif
- case NANO_NEXTPAGE_KEY:
- case NANO_NEXTPAGE_FKEY:
- if (!no_more) {
- blank_edit();
- page++;
- }
- break;
- case NANO_PREVPAGE_KEY:
- case NANO_PREVPAGE_FKEY:
- if (page > 0) {
- no_more = 0;
- blank_edit();
- page--;
- }
- break;
- }
-
- /* Calculate where in the text we should be, based on the page */
- for (i = 1; i < page * (editwinrows - 1); i++) {
- ptr += line_len(ptr);
- if (*ptr == '\n')
- ptr++;
- }
-
- for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
- int j = line_len(ptr);
-
- mvwaddnstr(edit, i, 0, ptr, j);
- ptr += j;
- if (*ptr == '\n')
- ptr++;
- }
-
- if (*ptr == '\0') {
- no_more = 1;
- continue;
- }
- } while ((kbinput = get_kbinput(edit, &meta, ISSET(REBIND_DELETE))) != NANO_EXIT_KEY && kbinput != NANO_EXIT_FKEY);
-
- currshortcut = oldshortcut;
-
- if (no_help_flag) {
- blank_bottombars();
- wrefresh(bottomwin);
- SET(NO_HELP);
- window_init();
- } else
- bottombars(currshortcut);
-
- curs_set(1);
- edit_refresh();
-
- /* The help_init() at the beginning allocated help_text, which has
- now been written to screen. */
- free(help_text);
- help_text = NULL;
-
-#elif defined(DISABLE_HELP)
- nano_disabled_msg();
-#endif
-
- return 1;
-}
-
-/* Highlight the current word being replaced or spell checked. */
-void do_replace_highlight(int highlight_flag, const char *word)
-{
- char *highlight_word = NULL;
- int x, y, word_len;
-
- highlight_word =
- mallocstrcpy(highlight_word, ¤t->data[current_x]);
-
-#ifdef HAVE_REGEX_H
- if (ISSET(USE_REGEXP))
- /* if we're using regexps, the highlight is the length of the
- search result, not the length of the regexp string */
- word_len = regmatches[0].rm_eo - regmatches[0].rm_so;
- else
-#endif
- word_len = strlen(word);
-
- highlight_word[word_len] = '\0';
-
- /* adjust output when word extends beyond screen */
-
- x = xplustabs();
- y = get_page_start(x) + COLS;
-
- if ((COLS - (y - x) + word_len) > COLS) {
- highlight_word[y - x - 1] = '$';
- highlight_word[y - x] = '\0';
- }
-
- /* OK display the output */
-
- reset_cursor();
-
- if (highlight_flag)
- wattron(edit, A_REVERSE);
-
- waddstr(edit, highlight_word);
-
- if (highlight_flag)
- wattroff(edit, A_REVERSE);
-
- free(highlight_word);
-}
-
-/* Fix editbot, based on the assumption that edittop is correct. */
-void fix_editbot(void)
-{
- int i;
-
- editbot = edittop;
- for (i = 0; i < editwinrows && editbot->next != NULL; i++)
- editbot = editbot->next;
-}
-
-#ifdef DEBUG
-/* Dump the current file structure to stderr */
-void dump_buffer(const filestruct *inptr) {
- if (inptr == fileage)
- fprintf(stderr, "Dumping file buffer to stderr...\n");
- else if (inptr == cutbuffer)
- fprintf(stderr, "Dumping cutbuffer to stderr...\n");
- else
- fprintf(stderr, "Dumping a buffer to stderr...\n");
-
- while (inptr != NULL) {
- fprintf(stderr, "(%d) %s\n", inptr->lineno, inptr->data);
- inptr = inptr->next;
- }
-}
-#endif /* DEBUG */
-
-#ifdef DEBUG
-void dump_buffer_reverse(void)
-{
- const filestruct *fileptr = filebot;
-
- while (fileptr != NULL) {
- fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data);
- fileptr = fileptr->prev;
- }
-}
-#endif /* DEBUG */
-
-#ifdef NANO_EXTRA
-#define CREDIT_LEN 53
-#define XLCREDIT_LEN 8
-
-void do_credits(void)
-{
- int i, j = 0, k, place = 0, start_x;
-
- const char *what;
- const char *xlcredits[XLCREDIT_LEN];
-
- const char *credits[CREDIT_LEN] = {
- "0", /* "The nano text editor" */
- "1", /* "version" */
- VERSION,
- "",
- "2", /* "Brought to you by:" */
- "Chris Allegretta",
- "Jordi Mallach",
- "Adam Rogoyski",
- "Rob Siemborski",
- "Rocco Corsi",
- "David Lawrence Ramsey",
- "David Benbennick",
- "Ken Tyler",
- "Sven Guckes",
- "Florian König",
- "Pauli Virtanen",
- "Daniele Medri",
- "Clement Laforet",
- "Tedi Heriyanto",
- "Bill Soudan",
- "Christian Weisgerber",
- "Erik Andersen",
- "Big Gaute",
- "Joshua Jensen",
- "Ryan Krebs",
- "Albert Chin",
- "",
- "3", /* "Special thanks to:" */
- "Plattsburgh State University",
- "Benet Laboratories",
- "Amy Allegretta",
- "Linda Young",
- "Jeremy Robichaud",
- "Richard Kolb II",
- "4", /* "The Free Software Foundation" */
- "Linus Torvalds",
- "5", /* "For ncurses:" */
- "Thomas Dickey",
- "Pavel Curtis",
- "Zeyd Ben-Halim",
- "Eric S. Raymond",
- "6", /* "and anyone else we forgot..." */
- "7", /* "Thank you for using nano!\n" */
- "", "", "", "",
- "(c) 1999-2003 Chris Allegretta",
- "", "", "", "",
- "http://www.nano-editor.org/"
- };
-
- xlcredits[0] = _("The nano text editor");
- xlcredits[1] = _("version ");
- xlcredits[2] = _("Brought to you by:");
- xlcredits[3] = _("Special thanks to:");
- xlcredits[4] = _("The Free Software Foundation");
- xlcredits[5] = _("For ncurses:");
- xlcredits[6] = _("and anyone else we forgot...");
- xlcredits[7] = _("Thank you for using nano!\n");
-
- curs_set(0);
- nodelay(edit, TRUE);
- blank_bottombars();
- mvwaddstr(topwin, 0, 0, hblank);
- blank_edit();
- wrefresh(edit);
- wrefresh(bottomwin);
- wrefresh(topwin);
-
- while (wgetch(edit) == ERR) {
- for (k = 0; k <= 1; k++) {
- blank_edit();
- for (i = editwinrows / 2 - 1; i >= (editwinrows / 2 - 1 - j);
- i--) {
- mvwaddstr(edit, i * 2 - k, 0, hblank);
-
- if (place - (editwinrows / 2 - 1 - i) < CREDIT_LEN) {
- what = credits[place - (editwinrows / 2 - 1 - i)];
-
- /* God I've missed hacking. If what is exactly
- 1 char long, it's a sentinel for a translated
- string, so use that instead. This means no
- thanking people with 1 character long names ;-) */
- if (strlen(what) == 1)
- what = xlcredits[atoi(what)];
- } else
- what = "";
-
- start_x = COLS / 2 - strlen(what) / 2 - 1;
- mvwaddstr(edit, i * 2 - k, start_x, what);
- }
- usleep(700000);
- wrefresh(edit);
- }
- if (j < editwinrows / 2 - 1)
- j++;
-
- place++;
-
- if (place >= CREDIT_LEN + editwinrows / 2)
- break;
- }
-
- nodelay(edit, FALSE);
- curs_set(1);
- display_main_list();
- total_refresh();
-}
-#endif