#include "proto.h"
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <locale.h>
#include <termios.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifndef NANO_TINY
#include <sys/ioctl.h>
#endif
#ifdef ENABLE_NANORC
static bool no_rcfiles = FALSE;
#endif
static struct termios oldterm;
static struct sigaction act;
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;
}
filestruct *copy_node(const filestruct *src)
{
filestruct *dst;
assert(src != NULL);
dst = (filestruct *)nmalloc(sizeof(filestruct));
dst->data = mallocstrcpy(NULL, src->data);
dst->next = src->next;
dst->prev = src->prev;
dst->lineno = src->lineno;
return dst;
}
void splice_node(filestruct *begin, filestruct *newnode, filestruct
*end)
{
assert(newnode != NULL && begin != NULL);
newnode->next = end;
newnode->prev = begin;
begin->next = newnode;
if (end != NULL)
end->prev = newnode;
}
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;
}
void delete_node(filestruct *fileptr)
{
assert(fileptr != NULL && fileptr->data != NULL);
if (fileptr->data != NULL)
free(fileptr->data);
free(fileptr);
}
filestruct *copy_filestruct(const filestruct *src)
{
filestruct *head, *copy;
assert(src != NULL);
copy = copy_node(src);
copy->prev = NULL;
head = copy;
src = src->next;
while (src != NULL) {
copy->next = copy_node(src);
copy->next->prev = copy;
copy = copy->next;
src = src->next;
}
copy->next = NULL;
return head;
}
void free_filestruct(filestruct *src)
{
assert(src != NULL);
while (src->next != NULL) {
src = src->next;
delete_node(src->prev);
}
delete_node(src);
}
void renumber(filestruct *fileptr)
{
ssize_t line;
assert(fileptr != NULL);
line = (fileptr->prev == NULL) ? 0 : fileptr->prev->lineno;
assert(fileptr != fileptr->next);
for (; fileptr != NULL; fileptr = fileptr->next)
fileptr->lineno = ++line;
}
partition *partition_filestruct(filestruct *top, size_t top_x,
filestruct *bot, size_t bot_x)
{
partition *p;
assert(top != NULL && bot != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
p = (partition *)nmalloc(sizeof(partition));
if (top != openfile->fileage) {
p->fileage = openfile->fileage;
openfile->fileage = top;
} else
p->fileage = NULL;
if (bot != openfile->filebot) {
p->filebot = openfile->filebot;
openfile->filebot = bot;
} else
p->filebot = NULL;
p->top_prev = top->prev;
top->prev = NULL;
p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
p->top_data[top_x] = '\0';
p->bot_next = bot->next;
bot->next = NULL;
p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
null_at(&bot->data, bot_x);
charmove(top->data, top->data + top_x, strlen(top->data) -
top_x + 1);
align(&top->data);
return p;
}
void unpartition_filestruct(partition **p)
{
char *tmp;
assert(p != NULL && openfile->fileage != NULL && openfile->filebot != NULL);
tmp = mallocstrcpy(NULL, openfile->fileage->data);
openfile->fileage->prev = (*p)->top_prev;
if (openfile->fileage->prev != NULL)
openfile->fileage->prev->next = openfile->fileage;
openfile->fileage->data = charealloc(openfile->fileage->data,
strlen((*p)->top_data) + strlen(openfile->fileage->data) + 1);
strcpy(openfile->fileage->data, (*p)->top_data);
free((*p)->top_data);
strcat(openfile->fileage->data, tmp);
free(tmp);
openfile->filebot->next = (*p)->bot_next;
if (openfile->filebot->next != NULL)
openfile->filebot->next->prev = openfile->filebot;
openfile->filebot->data = charealloc(openfile->filebot->data,
strlen(openfile->filebot->data) + strlen((*p)->bot_data) + 1);
strcat(openfile->filebot->data, (*p)->bot_data);
free((*p)->bot_data);
if ((*p)->fileage != NULL)
openfile->fileage = (*p)->fileage;
if ((*p)->filebot != NULL)
openfile->filebot = (*p)->filebot;
free(*p);
*p = NULL;
}
void move_to_filestruct(filestruct **file_top, filestruct **file_bot,
filestruct *top, size_t top_x, filestruct *bot, size_t bot_x)
{
filestruct *top_save;
bool edittop_inside;
#ifndef NANO_TINY
bool mark_inside = FALSE;
#endif
assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL);
if (top == bot && top_x == bot_x)
return;
filepart = partition_filestruct(top, top_x, bot, bot_x);
edittop_inside = (openfile->edittop->lineno >=
openfile->fileage->lineno && openfile->edittop->lineno <=
openfile->filebot->lineno);
#ifndef NANO_TINY
if (openfile->mark_set)
mark_inside = (openfile->mark_begin->lineno >=
openfile->fileage->lineno &&
openfile->mark_begin->lineno <=
openfile->filebot->lineno &&
(openfile->mark_begin != openfile->fileage ||
openfile->mark_begin_x >= top_x) &&
(openfile->mark_begin != openfile->filebot ||
openfile->mark_begin_x <= bot_x));
#endif
openfile->totsize -= get_totsize(top, bot);
if (*file_top == NULL) {
*file_top = openfile->fileage;
*file_bot = openfile->filebot;
renumber(*file_top);
} else {
filestruct *file_bot_save = *file_bot;
(*file_bot)->data = charealloc((*file_bot)->data,
strlen((*file_bot)->data) +
strlen(openfile->fileage->data) + 1);
strcat((*file_bot)->data, openfile->fileage->data);
(*file_bot)->next = openfile->fileage->next;
if ((*file_bot)->next != NULL) {
(*file_bot)->next->prev = *file_bot;
*file_bot = openfile->filebot;
}
if (file_bot_save->next != NULL)
renumber(file_bot_save->next);
}
openfile->fileage = (filestruct *)nmalloc(sizeof(filestruct));
openfile->fileage->data = mallocstrcpy(NULL, "");
openfile->filebot = openfile->fileage;
#ifndef NANO_TINY
if (mark_inside) {
openfile->mark_begin = openfile->fileage;
openfile->mark_begin_x = top_x;
}
#endif
openfile->current = openfile->fileage;
openfile->current_x = top_x;
top_save = openfile->fileage;
unpartition_filestruct(&filepart);
if (edittop_inside)
edit_update(
#ifndef NANO_TINY
ISSET(SMOOTH_SCROLL) ? NONE :
#endif
CENTER);
renumber(top_save);
if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
new_magicline();
}
void copy_from_filestruct(filestruct *file_top, filestruct *file_bot)
{
filestruct *top_save;
bool edittop_inside;
assert(file_top != NULL && file_bot != NULL);
filepart = partition_filestruct(openfile->current,
openfile->current_x, openfile->current, openfile->current_x);
edittop_inside = (openfile->edittop == openfile->fileage);
openfile->fileage = copy_filestruct(file_top);
openfile->filebot = openfile->fileage;
while (openfile->filebot->next != NULL)
openfile->filebot = openfile->filebot->next;
openfile->current = openfile->filebot;
openfile->current_x = strlen(openfile->filebot->data);
if (openfile->fileage == openfile->filebot)
openfile->current_x += strlen(filepart->top_data);
openfile->totsize += get_totsize(openfile->fileage,
openfile->filebot);
openfile->current_y += openfile->filebot->lineno - 1;
top_save = openfile->fileage;
if (edittop_inside)
openfile->edittop = openfile->fileage;
unpartition_filestruct(&filepart);
renumber(top_save);
if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
new_magicline();
}
openfilestruct *make_new_opennode(void)
{
openfilestruct *newnode =
(openfilestruct *)nmalloc(sizeof(openfilestruct));
newnode->filename = NULL;
newnode->fileage = NULL;
newnode->filebot = NULL;
newnode->edittop = NULL;
newnode->current = NULL;
return newnode;
}
void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
openfilestruct *end)
{
assert(newnode != NULL && begin != NULL);
newnode->next = end;
newnode->prev = begin;
begin->next = newnode;
if (end != NULL)
end->prev = newnode;
}
void unlink_opennode(openfilestruct *fileptr)
{
assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next);
fileptr->prev->next = fileptr->next;
fileptr->next->prev = fileptr->prev;
delete_opennode(fileptr);
}
void delete_opennode(openfilestruct *fileptr)
{
assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);
free(fileptr->filename);
free_filestruct(fileptr->fileage);
#ifndef NANO_TINY
if (fileptr->current_stat != NULL)
free(fileptr->current_stat);
#endif
free(fileptr);
}
#ifdef DEBUG
void free_openfilestruct(openfilestruct *src)
{
assert(src != NULL);
while (src != src->next) {
src = src->next;
delete_opennode(src->prev);
}
delete_opennode(src);
}
#endif
void print_view_warning(void)
{
statusbar(_("Key invalid in view mode"));
}
void finish(void)
{
if (!ISSET(NO_HELP))
blank_bottombars();
else
blank_statusbar();
wrefresh(bottomwin);
endwin();
tcsetattr(0, TCSANOW, &oldterm);
#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
if (!no_rcfiles && ISSET(HISTORYLOG))
save_history();
#endif
#ifdef DEBUG
thanks_for_all_the_fish();
#endif
exit(0);
}
void die(const char *msg, ...)
{
va_list ap;
endwin();
tcsetattr(0, TCSANOW, &oldterm);
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
if (openfile && openfile->modified) {
if (filepart != NULL)
unpartition_filestruct(&filepart);
die_save_file(openfile->filename);
}
#ifdef ENABLE_MULTIBUFFER
if (openfile != NULL) {
openfilestruct *tmp = openfile;
while (tmp != openfile->next) {
openfile = openfile->next;
if (openfile->modified)
die_save_file(openfile->filename);
}
}
#endif
exit(1);
}
void die_save_file(const char *die_filename)
{
char *retval;
bool failed = TRUE;
if (ISSET(RESTRICTED))
return;
if (die_filename[0] == '\0')
die_filename = "nano";
retval = get_next_filename(die_filename, ".save");
if (retval[0] != '\0')
failed = !write_file(retval, NULL, TRUE, OVERWRITE, TRUE);
if (!failed)
fprintf(stderr, _("\nBuffer written to %s\n"), retval);
else if (retval[0] != '\0')
fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval,
strerror(errno));
else
fprintf(stderr, _("\nBuffer not written: %s\n"),
_("Too many backup files?"));
free(retval);
}
void window_init(void)
{
editwinrows = LINES - 5 + no_more_space() + no_help();
if (COLS < MIN_EDITOR_COLS || editwinrows < MIN_EDITOR_ROWS)
die(_("Window size is too small for nano...\n"));
#ifndef DISABLE_WRAPJUSTIFY
fill = wrap_at;
if (fill <= 0)
fill += COLS;
if (fill < 0)
fill = 0;
#endif
if (topwin != NULL)
delwin(topwin);
if (edit != NULL)
delwin(edit);
if (bottomwin != NULL)
delwin(bottomwin);
topwin = newwin(2 - no_more_space(), COLS, 0, 0);
edit = newwin(editwinrows, COLS, 2 - no_more_space(), 0);
bottomwin = newwin(3 - no_help(), COLS, editwinrows + (2 -
no_more_space()), 0);
if (!ISSET(REBIND_KEYPAD)) {
keypad(topwin, TRUE);
keypad(edit, TRUE);
keypad(bottomwin, TRUE);
}
}
#ifndef DISABLE_MOUSE
void disable_mouse_support(void)
{
mousemask(0, NULL);
}
void enable_mouse_support(void)
{
mousemask(ALL_MOUSE_EVENTS, NULL);
mouseinterval(50);
}
void mouse_init(void)
{
if (ISSET(USE_MOUSE))
enable_mouse_support();
else
disable_mouse_support();
}
#endif
#ifdef HAVE_GETOPT_LONG
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, longflag, desc)
#else
#define print_opt(shortflag, longflag, desc) print_opt_full(shortflag, desc)
#endif
void print_opt_full(const char *shortflag
#ifdef HAVE_GETOPT_LONG
, const char *longflag
#endif
, 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
if (desc != NULL)
printf("%s", _(desc));
printf("\n");
}
void usage(void)
{
printf(_("Usage: nano [OPTIONS] [[+LINE,COLUMN] FILE]...\n\n"));
printf(
#ifdef HAVE_GETOPT_LONG
_("Option\t\tGNU long option\t\tMeaning\n")
#else
_("Option\t\tMeaning\n")
#endif
);
print_opt("-h, -?", "--help", N_("Show this message"));
print_opt(_("+LINE,COLUMN"), "",
N_("Start at line LINE, column COLUMN"));
#ifndef NANO_TINY
print_opt("-A", "--smarthome", N_("Enable smart home key"));
print_opt("-B", "--backup", N_("Save backups of existing files"));
print_opt(_("-C <dir>"), _("--backupdir=<dir>"),
N_("Directory for saving unique backup files"));
#endif
print_opt("-D", "--boldtext",
N_("Use bold instead of reverse video text"));
#ifndef NANO_TINY
print_opt("-E", "--tabstospaces",
N_("Convert typed tabs to spaces"));
#endif
#ifdef ENABLE_MULTIBUFFER
print_opt("-F", "--multibuffer", N_("Enable multiple file buffers"));
#endif
#ifdef ENABLE_NANORC
#ifndef NANO_TINY
print_opt("-H", "--historylog",
N_("Log & read search/replace string history"));
#endif
print_opt("-I", "--ignorercfiles",
N_("Don't look at nanorc files"));
#endif
print_opt("-K", "--rebindkeypad",
N_("Fix numeric keypad key confusion problem"));
print_opt("-L", "--nonewlines",
N_("Don't add newlines to the ends of files"));
#ifndef NANO_TINY
print_opt("-N", "--noconvert",
N_("Don't convert files from DOS/Mac format"));
#endif
print_opt("-O", "--morespace", N_("Use one more line for editing"));
#ifndef DISABLE_JUSTIFY
print_opt(_("-Q <str>"), _("--quotestr=<str>"),
N_("Quoting string"));
#endif
print_opt("-R", "--restricted", N_("Restricted mode"));
#ifndef NANO_TINY
print_opt("-S", "--smooth", N_("Smooth scrolling"));
#endif
print_opt(_("-T <#cols>"), _("--tabsize=<#cols>"),
N_("Set width of a tab to #cols columns"));
#ifndef NANO_TINY
print_opt("-U", "--quickblank", N_("Do quick statusbar blanking"));
#endif
print_opt("-V", "--version",
N_("Print version information and exit"));
#ifndef NANO_TINY
print_opt("-W", "--wordbounds",
N_("Detect word boundaries more accurately"));
#endif
#ifdef ENABLE_COLOR
print_opt(_("-Y <str>"), _("--syntax=<str>"),
N_("Syntax definition to use for coloring"));
#endif
print_opt("-c", "--const", N_("Constantly show cursor position"));
print_opt("-d", "--rebinddelete",
N_("Fix Backspace/Delete confusion problem"));
#ifndef NANO_TINY
print_opt("-i", "--autoindent",
N_("Automatically indent new lines"));
print_opt("-k", "--cut", N_("Cut from cursor to end of line"));
#endif
print_opt("-l", "--nofollow",
N_("Don't follow symbolic links, overwrite"));
#ifndef DISABLE_MOUSE
print_opt("-m", "--mouse", N_("Enable the use of the mouse"));
#endif
#ifndef DISABLE_OPERATINGDIR
print_opt(_("-o <dir>"), _("--operatingdir=<dir>"),
N_("Set operating directory"));
#endif
print_opt("-p", "--preserve",
N_("Preserve XON (^Q) and XOFF (^S) keys"));
#ifndef DISABLE_WRAPJUSTIFY
print_opt(_("-r <#cols>"), _("--fill=<#cols>"),
N_("Set wrapping point at column #cols"));
#endif
#ifndef DISABLE_SPELLER
print_opt(_("-s <prog>"), _("--speller=<prog>"),
N_("Enable alternate speller"));
#endif
print_opt("-t", "--tempfile",
N_("Auto save on exit, don't prompt"));
print_opt("-v", "--view", N_("View mode (read-only)"));
#ifndef DISABLE_WRAPPING
print_opt("-w", "--nowrap", N_("Don't wrap long lines"));
#endif
print_opt("-x", "--nohelp", N_("Don't show the two help lines"));
print_opt("-z", "--suspend", N_("Enable suspension"));
print_opt("-a, -b, -e,", "", NULL);
print_opt("-f, -g, -j", "", N_("(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:"));
#ifdef DISABLE_BROWSER
printf(" --disable-browser");
#endif
#ifdef DISABLE_HELP
printf(" --disable-help");
#endif
#ifdef DISABLE_JUSTIFY
printf(" --disable-justify");
#endif
#ifdef DISABLE_MOUSE
printf(" --disable-mouse");
#endif
#ifndef ENABLE_NLS
printf(" --disable-nls");
#endif
#ifdef DISABLE_OPERATINGDIR
printf(" --disable-operatingdir");
#endif
#ifdef DISABLE_SPELLER
printf(" --disable-speller");
#endif
#ifdef DISABLE_TABCOMP
printf(" --disable-tabcomp");
#endif
#ifdef DISABLE_WRAPPING
printf(" --disable-wrapping");
#endif
#ifdef DISABLE_ROOTWRAPPING
printf(" --disable-wrapping-as-root");
#endif
#ifdef ENABLE_COLOR
printf(" --enable-color");
#endif
#ifdef DEBUG
printf(" --enable-debug");
#endif
#ifdef NANO_EXTRA
printf(" --enable-extra");
#endif
#ifdef ENABLE_MULTIBUFFER
printf(" --enable-multibuffer");
#endif
#ifdef ENABLE_NANORC
printf(" --enable-nanorc");
#endif
#ifdef NANO_TINY
printf(" --enable-tiny");
#endif
#ifdef ENABLE_UTF8
printf(" --enable-utf8");
#endif
#ifdef USE_SLANG
printf(" --with-slang");
#endif
printf("\n");
}
int no_more_space(void)
{
return ISSET(MORE_SPACE) ? 1 : 0;
}
int no_help(void)
{
return ISSET(NO_HELP) ? 2 : 0;
}
void nano_disabled_msg(void)
{
statusbar(_("Sorry, support for this function has been disabled"));
}
void do_exit(void)
{
int i;
if (!openfile->modified)
i = 0;
else if (ISSET(TEMP_FILE))
i = 1;
else
i = do_yesno_prompt(FALSE,
_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
#ifdef DEBUG
dump_filestruct(openfile->fileage);
#endif
if (i == 0 || (i == 1 && do_writeout(TRUE))) {
#ifdef ENABLE_MULTIBUFFER
if (!close_buffer())
#endif
finish();
} else if (i != 1)
statusbar(_("Cancelled"));
display_main_list();
}
void signal_init(void)
{
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = SIG_IGN;
sigaction(SIGINT, &act, NULL);
sigaction(SIGQUIT, &act, NULL);
act.sa_handler = handle_hupterm;
sigaction(SIGHUP, &act, NULL);
sigaction(SIGTERM, &act, NULL);
#ifndef NANO_TINY
act.sa_handler = handle_sigwinch;
sigaction(SIGWINCH, &act, NULL);
allow_pending_sigwinch(FALSE);
#endif
if (!ISSET(SUSPEND)) {
act.sa_handler = SIG_IGN;
sigaction(SIGTSTP, &act, NULL);
} else {
sigfillset(&act.sa_mask);
act.sa_handler = do_suspend;
sigaction(SIGTSTP, &act, NULL);
act.sa_handler = do_continue;
sigaction(SIGCONT, &act, NULL);
}
}
RETSIGTYPE handle_hupterm(int signal)
{
die(_("Received SIGHUP or SIGTERM\n"));
}
RETSIGTYPE do_suspend(int signal)
{
#ifndef DISABLE_MOUSE
disable_mouse_support();
#endif
erase();
move(LINES - 1, 0);
refresh();
printf(_("Use \"fg\" to return to nano.\n"));
fflush(stdout);
tcsetattr(0, TCSANOW, &oldterm);
act.sa_handler = handle_hupterm;
sigaction(SIGHUP, &act, NULL);
sigaction(SIGTERM, &act, NULL);
kill(0, SIGSTOP);
}
RETSIGTYPE do_continue(int signal)
{
#ifndef DISABLE_MOUSE
if (ISSET(USE_MOUSE))
enable_mouse_support();
#endif
#ifndef NANO_TINY
handle_sigwinch(0);
#else
terminal_init();
curs_set(1);
blank_statusbar();
wnoutrefresh(bottomwin);
total_refresh();
#endif
}
#ifndef NANO_TINY
RETSIGTYPE handle_sigwinch(int signal)
{
const char *tty = ttyname(0);
int fd, result = 0;
struct winsize win;
if (tty == NULL)
return;
fd = open(tty, O_RDWR);
if (fd == -1)
return;
result = ioctl(fd, TIOCGWINSZ, &win);
close(fd);
if (result == -1)
return;
COLS = win.ws_col;
LINES = win.ws_row;
if (filepart != NULL)
unpartition_filestruct(&filepart);
#ifdef USE_SLANG
SLsmg_reset_smg();
SLsmg_init_smg();
#else
endwin();
doupdate();
#endif
terminal_init();
curs_set(1);
window_init();
blank_statusbar();
wnoutrefresh(bottomwin);
currshortcut = main_list;
total_refresh();
siglongjmp(jump_buf, 1);
}
void allow_pending_sigwinch(bool allow)
{
sigset_t winch;
sigemptyset(&winch);
sigaddset(&winch, SIGWINCH);
sigprocmask(allow ? SIG_UNBLOCK : SIG_BLOCK, &winch, NULL);
}
#endif
#ifndef NANO_TINY
void do_toggle(const toggle *which)
{
bool enabled;
TOGGLE(which->flag);
switch (which->val) {
#ifndef DISABLE_MOUSE
case TOGGLE_MOUSE_KEY:
mouse_init();
break;
#endif
case TOGGLE_MORESPACE_KEY:
case TOGGLE_NOHELP_KEY:
window_init();
total_refresh();
break;
case TOGGLE_SUSPEND_KEY:
signal_init();
break;
#ifdef ENABLE_NANORC
case TOGGLE_WHITESPACE_KEY:
titlebar(NULL);
edit_refresh();
break;
#endif
#ifdef ENABLE_COLOR
case TOGGLE_SYNTAX_KEY:
edit_refresh();
break;
#endif
}
enabled = ISSET(which->flag);
if (which->val == TOGGLE_NOHELP_KEY
#ifndef DISABLE_WRAPPING
|| which->val == TOGGLE_WRAP_KEY
#endif
#ifdef ENABLE_COLOR
|| which->val == TOGGLE_SYNTAX_KEY
#endif
)
enabled = !enabled;
statusbar("%s %s", which->desc, enabled ? _("enabled") :
_("disabled"));
}
#endif
void disable_extended_io(void)
{
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~IEXTEN;
term.c_oflag &= ~OPOST;
tcsetattr(0, TCSANOW, &term);
}
void disable_signals(void)
{
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ISIG;
tcsetattr(0, TCSANOW, &term);
}
#ifndef NANO_TINY
void enable_signals(void)
{
struct termios term;
tcgetattr(0, &term);
term.c_lflag |= ISIG;
tcsetattr(0, TCSANOW, &term);
}
#endif
void disable_flow_control(void)
{
struct termios term;
tcgetattr(0, &term);
term.c_iflag &= ~IXON;
tcsetattr(0, TCSANOW, &term);
}
void enable_flow_control(void)
{
struct termios term;
tcgetattr(0, &term);
term.c_iflag |= IXON;
tcsetattr(0, TCSANOW, &term);
}
void terminal_init(void)
{
#ifdef USE_SLANG
static struct termios newterm;
static bool newterm_set = FALSE;
if (!newterm_set) {
#endif
cbreak();
nonl();
noecho();
disable_extended_io();
disable_signals();
if (!ISSET(PRESERVE))
disable_flow_control();
#ifdef USE_SLANG
tcgetattr(0, &newterm);
newterm_set = TRUE;
} else
tcsetattr(0, TCSANOW, &newterm);
#endif
}
int do_input(bool *meta_key, bool *func_key, bool *s_or_t, bool
*ran_func, bool *finished, bool allow_funcs)
{
int input;
static int *kbinput = NULL;
static size_t kbinput_len = 0;
bool cut_copy = FALSE;
const shortcut *s;
bool have_shortcut;
#ifndef NANO_TINY
const toggle *t;
bool have_toggle;
#endif
*s_or_t = FALSE;
*ran_func = FALSE;
*finished = FALSE;
input = get_kbinput(edit, meta_key, func_key);
#ifndef DISABLE_MOUSE
if (allow_funcs) {
if (*func_key && input == KEY_MOUSE) {
if (do_mouse())
input = get_kbinput(edit, meta_key, func_key);
else {
*meta_key = FALSE;
*func_key = FALSE;
input = ERR;
}
}
}
#endif
s = get_shortcut(main_list, &input, meta_key, func_key);
have_shortcut = (s != NULL || input == NANO_XON_KEY ||
input == NANO_XOFF_KEY || input == NANO_SUSPEND_KEY);
#ifndef NANO_TINY
t = get_toggle(input, *meta_key);
have_toggle = (t != NULL);
#endif
*s_or_t = (have_shortcut
#ifndef NANO_TINY
|| have_toggle
#endif
);
if (!*s_or_t) {
if (is_ascii_cntrl_char(input) || *meta_key || *func_key) {
statusbar(_("Unknown Command"));
beep();
*meta_key = FALSE;
*func_key = FALSE;
input = ERR;
}
}
if (allow_funcs) {
if (input != ERR && !*s_or_t) {
if (ISSET(VIEW_MODE))
print_view_warning();
else {
kbinput_len++;
kbinput = (int *)nrealloc(kbinput, kbinput_len *
sizeof(int));
kbinput[kbinput_len - 1] = input;
}
}
if (*s_or_t || get_key_buffer_len() == 0) {
#ifndef DISABLE_WRAPPING
if (*s_or_t && (!have_shortcut || s == NULL || s->func !=
do_verbatim_input))
wrap_reset();
#endif
if (kbinput != NULL) {
char *output = charalloc(kbinput_len + 1);
size_t i;
for (i = 0; i < kbinput_len; i++)
output[i] = (char)kbinput[i];
output[i] = '\0';
do_output(output, kbinput_len, FALSE);
free(output);
kbinput_len = 0;
free(kbinput);
kbinput = NULL;
}
}
if (have_shortcut) {
switch (input) {
case NANO_XON_KEY:
statusbar(_("XON ignored, mumble mumble"));
break;
case NANO_XOFF_KEY:
statusbar(_("XOFF ignored, mumble mumble"));
break;
case NANO_SUSPEND_KEY:
if (ISSET(SUSPEND))
do_suspend(0);
break;
default:
if (s->func == do_cut_text_void
#ifndef NANO_TINY
|| s->func == do_copy_text || s->func ==
do_cut_till_end
#endif
)
cut_copy = TRUE;
if (s->func != NULL) {
*ran_func = TRUE;
if (ISSET(VIEW_MODE) && !s->viewok)
print_view_warning();
else
s->func();
}
*finished = TRUE;
break;
}
}
#ifndef NANO_TINY
else if (have_toggle) {
if (allow_funcs)
do_toggle(t);
}
#endif
}
if (!cut_copy)
cutbuffer_reset();
return input;
}
#ifndef DISABLE_MOUSE
bool do_mouse(void)
{
int mouse_x, mouse_y;
bool retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);
if (!retval) {
if (wenclose(edit, mouse_y, mouse_x)) {
bool sameline;
const filestruct *current_save = openfile->current;
size_t current_x_save = openfile->current_x;
size_t pww_save = openfile->placewewant;
mouse_y -= 2 - no_more_space();
sameline = (mouse_y == openfile->current_y);
for (; openfile->current_y < mouse_y &&
openfile->current != openfile->filebot;
openfile->current_y++)
openfile->current = openfile->current->next;
for (; openfile->current_y > mouse_y &&
openfile->current != openfile->fileage;
openfile->current_y--)
openfile->current = openfile->current->prev;
openfile->current_x = actual_x(openfile->current->data,
get_page_start(xplustabs()) + mouse_x);
openfile->placewewant = xplustabs();
#ifndef NANO_TINY
if (sameline && openfile->current_x == current_x_save)
do_mark();
#endif
edit_redraw(current_save, pww_save);
}
}
return retval;
}
#endif
void do_output(char *output, size_t output_len, bool allow_cntrls)
{
size_t current_len, i = 0;
bool do_refresh = FALSE;
char *char_buf = charalloc(mb_cur_max());
int char_buf_len;
assert(openfile->current != NULL && openfile->current->data != NULL);
current_len = strlen(openfile->current->data);
while (i < output_len) {
if (allow_cntrls) {
if (output[i] == '\0')
output[i] = '\n';
else if (output[i] == '\n') {
do_enter();
i++;
continue;
}
}
char_buf_len = parse_mbchar(output + i, char_buf, NULL);
i += char_buf_len;
if (!allow_cntrls && is_ascii_cntrl_char(*(output + i -
char_buf_len)))
continue;
if (!ISSET(NO_NEWLINES) && openfile->filebot ==
openfile->current)
new_magicline();
openfile->current->data = charealloc(openfile->current->data,
current_len + (char_buf_len * 2));
assert(openfile->current_x <= current_len);
charmove(openfile->current->data + openfile->current_x +
char_buf_len, openfile->current->data +
openfile->current_x, current_len - openfile->current_x +
char_buf_len);
strncpy(openfile->current->data + openfile->current_x, char_buf,
char_buf_len);
current_len += char_buf_len;
openfile->totsize++;
set_modified();
#ifndef NANO_TINY
if (openfile->mark_set && openfile->current ==
openfile->mark_begin && openfile->current_x <
openfile->mark_begin_x)
openfile->mark_begin_x += char_buf_len;
#endif
openfile->current_x += char_buf_len;
#ifndef DISABLE_WRAPPING
if (!ISSET(NO_WRAP)) {
bool do_refresh_save = do_refresh;
do_refresh = do_wrap(openfile->current);
if (do_refresh_save)
do_refresh = TRUE;
}
#endif
#ifdef ENABLE_COLOR
if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX))
do_refresh = TRUE;
#endif
}
free(char_buf);
openfile->placewewant = xplustabs();
if (do_refresh)
edit_refresh();
else
update_line(openfile->current, openfile->current_x);
}
int main(int argc, char **argv)
{
int optchr;
ssize_t startline = 1;
ssize_t startcol = 1;
#ifndef DISABLE_WRAPJUSTIFY
bool fill_used = FALSE;
#endif
#ifdef ENABLE_MULTIBUFFER
bool old_multibuffer;
#endif
#ifdef HAVE_GETOPT_LONG
const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"boldtext", 0, NULL, 'D'},
#ifdef ENABLE_MULTIBUFFER
{"multibuffer", 0, NULL, 'F'},
#endif
#ifdef ENABLE_NANORC
{"ignorercfiles", 0, NULL, 'I'},
#endif
{"rebindkeypad", 0, NULL, 'K'},
{"nonewlines", 0, NULL, 'L'},
{"morespace", 0, NULL, 'O'},
#ifndef DISABLE_JUSTIFY
{"quotestr", 1, NULL, 'Q'},
#endif
{"restricted", 0, NULL, 'R'},
{"tabsize", 1, NULL, 'T'},
{"version", 0, NULL, 'V'},
#ifdef ENABLE_COLOR
{"syntax", 1, NULL, 'Y'},
#endif
{"const", 0, NULL, 'c'},
{"rebinddelete", 0, NULL, 'd'},
{"nofollow", 0, NULL, 'l'},
#ifndef DISABLE_MOUSE
{"mouse", 0, NULL, 'm'},
#endif
#ifndef DISABLE_OPERATINGDIR
{"operatingdir", 1, NULL, 'o'},
#endif
{"preserve", 0, NULL, 'p'},
#ifndef DISABLE_WRAPJUSTIFY
{"fill", 1, NULL, 'r'},
#endif
#ifndef DISABLE_SPELLER
{"speller", 1, NULL, 's'},
#endif
{"tempfile", 0, NULL, 't'},
{"view", 0, NULL, 'v'},
#ifndef DISABLE_WRAPPING
{"nowrap", 0, NULL, 'w'},
#endif
{"nohelp", 0, NULL, 'x'},
{"suspend", 0, NULL, 'z'},
#ifndef NANO_TINY
{"smarthome", 0, NULL, 'A'},
{"backup", 0, NULL, 'B'},
{"backupdir", 1, NULL, 'C'},
{"tabstospaces", 0, NULL, 'E'},
{"historylog", 0, NULL, 'H'},
{"noconvert", 0, NULL, 'N'},
{"smooth", 0, NULL, 'S'},
{"quickblank", 0, NULL, 'U'},
{"wordbounds", 0, NULL, 'W'},
{"autoindent", 0, NULL, 'i'},
{"cut", 0, NULL, 'k'},
#endif
{NULL, 0, NULL, 0}
};
#endif
#ifdef ENABLE_UTF8
{
char *locale = setlocale(LC_ALL, "");
if (locale != NULL && (strcasestr(locale, "UTF8") != NULL ||
strcasestr(locale, "UTF-8") != NULL)) {
#ifdef USE_SLANG
SLutf8_enable(1);
#endif
utf8_init();
}
}
#else
setlocale(LC_ALL, "");
#endif
#ifdef ENABLE_NLS
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
#endif
#if !defined(ENABLE_NANORC) && defined(DISABLE_ROOTWRAPPING)
if (geteuid() == NANO_ROOT_UID)
SET(NO_WRAP);
#endif
while ((optchr =
#ifdef HAVE_GETOPT_LONG
getopt_long(argc, argv,
"h?ABC:DEFHIKLNOQ:RST:UVWY:abcdefgijklmo:pr:s:tvwxz",
long_options, NULL)
#else
getopt(argc, argv,
"h?ABC:DEFHIKLNOQ:RST:UVWY:abcdefgijklmo:pr:s:tvwxz")
#endif
) != -1) {
switch (optchr) {
case 'a':
case 'b':
case 'e':
case 'f':
case 'g':
case 'j':
break;
#ifndef NANO_TINY
case 'A':
SET(SMART_HOME);
break;
case 'B':
SET(BACKUP_FILE);
break;
case 'C':
backup_dir = mallocstrcpy(backup_dir, optarg);
break;
#endif
case 'D':
SET(BOLD_TEXT);
break;
#ifndef NANO_TINY
case 'E':
SET(TABS_TO_SPACES);
break;
#endif
#ifdef ENABLE_MULTIBUFFER
case 'F':
SET(MULTIBUFFER);
break;
#endif
#ifdef ENABLE_NANORC
#ifndef NANO_TINY
case 'H':
SET(HISTORYLOG);
break;
#endif
case 'I':
no_rcfiles = TRUE;
break;
#endif
case 'K':
SET(REBIND_KEYPAD);
break;
case 'L':
SET(NO_NEWLINES);
break;
#ifndef NANO_TINY
case 'N':
SET(NO_CONVERT);
break;
#endif
case 'O':
SET(MORE_SPACE);
break;
#ifndef DISABLE_JUSTIFY
case 'Q':
quotestr = mallocstrcpy(quotestr, optarg);
break;
#endif
case 'R':
SET(RESTRICTED);
break;
#ifndef NANO_TINY
case 'S':
SET(SMOOTH_SCROLL);
break;
#endif
case 'T':
if (!parse_num(optarg, &tabsize) || tabsize <= 0) {
fprintf(stderr, _("Requested tab size \"%s\" is invalid"), optarg);
fprintf(stderr, "\n");
exit(1);
}
break;
#ifndef NANO_TINY
case 'U':
SET(QUICK_BLANK);
break;
#endif
case 'V':
version();
exit(0);
#ifndef NANO_TINY
case 'W':
SET(WORD_BOUNDS);
break;
#endif
#ifdef ENABLE_COLOR
case 'Y':
syntaxstr = mallocstrcpy(syntaxstr, optarg);
break;
#endif
case 'c':
SET(CONST_UPDATE);
break;
case 'd':
SET(REBIND_DELETE);
break;
#ifndef NANO_TINY
case 'i':
SET(AUTOINDENT);
break;
case 'k':
SET(CUT_TO_END);
break;
#endif
case 'l':
SET(NOFOLLOW_SYMLINKS);
break;
#ifndef DISABLE_MOUSE
case 'm':
SET(USE_MOUSE);
break;
#endif
#ifndef DISABLE_OPERATINGDIR
case 'o':
operating_dir = mallocstrcpy(operating_dir, optarg);
break;
#endif
case 'p':
SET(PRESERVE);
break;
#ifndef DISABLE_WRAPJUSTIFY
case 'r':
if (!parse_num(optarg, &wrap_at)) {
fprintf(stderr, _("Requested fill size \"%s\" is invalid"), optarg);
fprintf(stderr, "\n");
exit(1);
}
fill_used = TRUE;
break;
#endif
#ifndef DISABLE_SPELLER
case 's':
alt_speller = mallocstrcpy(alt_speller, optarg);
break;
#endif
case 't':
SET(TEMP_FILE);
break;
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();
}
}
if (*(tail(argv[0])) == 'r')
SET(RESTRICTED);
if (ISSET(RESTRICTED)) {
UNSET(SUSPEND);
UNSET(BACKUP_FILE);
#ifdef ENABLE_NANORC
no_rcfiles = TRUE;
#endif
}
#ifdef ENABLE_NANORC
if (!no_rcfiles) {
#ifndef DISABLE_OPERATINGDIR
char *operating_dir_cpy = operating_dir;
#endif
#ifndef DISABLE_WRAPJUSTIFY
ssize_t wrap_at_cpy = wrap_at;
#endif
#ifndef NANO_TINY
char *backup_dir_cpy = backup_dir;
#endif
#ifndef DISABLE_JUSTIFY
char *quotestr_cpy = quotestr;
#endif
#ifndef DISABLE_SPELLER
char *alt_speller_cpy = alt_speller;
#endif
ssize_t tabsize_cpy = tabsize;
long flags_cpy = flags;
#ifndef DISABLE_OPERATINGDIR
operating_dir = NULL;
#endif
#ifndef NANO_TINY
backup_dir = NULL;
#endif
#ifndef DISABLE_JUSTIFY
quotestr = NULL;
#endif
#ifndef DISABLE_SPELLER
alt_speller = NULL;
#endif
do_rcfile();
#ifndef DISABLE_OPERATINGDIR
if (operating_dir_cpy != NULL) {
free(operating_dir);
operating_dir = operating_dir_cpy;
}
#endif
#ifndef DISABLE_WRAPJUSTIFY
if (fill_used)
wrap_at = wrap_at_cpy;
#endif
#ifndef NANO_TINY
if (backup_dir_cpy != NULL) {
free(backup_dir);
backup_dir = backup_dir_cpy;
}
#endif
#ifndef DISABLE_JUSTIFY
if (quotestr_cpy != NULL) {
free(quotestr);
quotestr = quotestr_cpy;
}
#endif
#ifndef DISABLE_SPELLER
if (alt_speller_cpy != NULL) {
free(alt_speller);
alt_speller = alt_speller_cpy;
}
#endif
if (tabsize_cpy != -1)
tabsize = tabsize_cpy;
flags |= flags_cpy;
}
#ifdef DISABLE_ROOTWRAPPING
else if (geteuid() == NANO_ROOT_UID)
SET(NO_WRAP);
#endif
#endif
if (ISSET(BOLD_TEXT))
reverse_attr = A_BOLD;
#ifndef NANO_TINY
history_init();
#ifdef ENABLE_NANORC
if (!no_rcfiles && ISSET(HISTORYLOG))
load_history();
#endif
#endif
#ifndef NANO_TINY
if (!ISSET(RESTRICTED))
init_backup_dir();
#endif
#ifndef DISABLE_OPERATINGDIR
init_operating_dir();
#endif
#ifndef DISABLE_JUSTIFY
if (punct == NULL)
punct = mallocstrcpy(NULL, "!.?");
if (brackets == NULL)
brackets = mallocstrcpy(NULL, "\"')>]}");
if (quotestr == NULL)
quotestr = mallocstrcpy(NULL,
#ifdef HAVE_REGEX_H
"^([ \t]*[#:>|}])+"
#else
"> "
#endif
);
#ifdef HAVE_REGEX_H
quoterc = regcomp("ereg, quotestr, REG_EXTENDED);
if (quoterc == 0) {
free(quotestr);
quotestr = NULL;
} else {
size_t size = regerror(quoterc, "ereg, NULL, 0);
quoteerr = charalloc(size);
regerror(quoterc, "ereg, quoteerr, size);
}
#else
quotelen = strlen(quotestr);
#endif
#endif
#ifndef DISABLE_SPELLER
if (!ISSET(RESTRICTED) && alt_speller == NULL) {
char *spellenv = getenv("SPELL");
if (spellenv != NULL)
alt_speller = mallocstrcpy(NULL, spellenv);
}
#endif
#ifndef NANO_TINY
if (matchbrackets == NULL)
matchbrackets = mallocstrcpy(NULL, "(<[{)>]}");
#endif
#if !defined(NANO_TINY) && defined(ENABLE_NANORC)
if (whitespace == NULL) {
whitespace = mallocstrcpy(NULL, " ");
whitespace_len[0] = 1;
whitespace_len[1] = 1;
}
#endif
if (tabsize == -1)
tabsize = WIDTH_OF_TAB;
tcgetattr(0, &oldterm);
initscr();
terminal_init();
curs_set(1);
#ifdef DEBUG
fprintf(stderr, "Main: set up windows\n");
#endif
window_init();
signal_init();
shortcut_init(FALSE);
#ifndef DISABLE_MOUSE
mouse_init();
#endif
#ifdef DEBUG
fprintf(stderr, "Main: open file\n");
#endif
if (0 < optind && optind < argc - 1 && argv[optind][0] == '+') {
parse_line_column(&argv[optind][1], &startline, &startcol);
optind++;
}
#ifdef ENABLE_MULTIBUFFER
old_multibuffer = ISSET(MULTIBUFFER);
SET(MULTIBUFFER);
{
int i = optind + 1;
ssize_t iline = 1, icol = 1;
for (; i < argc; i++) {
if (i < argc - 1 && argv[i][0] == '+' && iline == 1 &&
icol == 1)
parse_line_column(&argv[i][1], &iline, &icol);
else {
open_buffer(argv[i]);
if (iline > 1 || icol > 1) {
do_gotolinecolumn(iline, icol, FALSE, FALSE, FALSE,
FALSE);
iline = 1;
icol = 1;
}
}
}
}
#endif
if (optind < argc)
open_buffer(argv[optind]);
if (openfile == NULL) {
open_buffer("");
UNSET(VIEW_MODE);
}
#ifdef ENABLE_MULTIBUFFER
if (!old_multibuffer)
UNSET(MULTIBUFFER);
#endif
#ifdef DEBUG
fprintf(stderr, "Main: top and bottom win\n");
#endif
if (startline > 1 || startcol > 1)
do_gotolinecolumn(startline, startcol, FALSE, FALSE, FALSE,
FALSE);
display_main_list();
display_buffer();
while (TRUE) {
bool meta_key, func_key, s_or_t, ran_func, finished;
reset_cursor();
#ifndef NANO_TINY
if (!jump_buf_main) {
jump_buf_main = TRUE;
sigsetjmp(jump_buf, 1);
}
#endif
do_prompt_abort();
if (ISSET(CONST_UPDATE) && get_key_buffer_len() == 0)
do_cursorpos(TRUE);
currshortcut = main_list;
do_input(&meta_key, &func_key, &s_or_t, &ran_func, &finished,
TRUE);
}
assert(FALSE);
}