Commit ad40fdba authored by David Lawrence Ramsey's avatar David Lawrence Ramsey
Browse files

DLR: prototype overhaul, etc.

git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@1270 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
No related merge requests found
Showing with 2001 additions and 1822 deletions
+2001 -1822
......@@ -11,13 +11,27 @@ CVS code -
need to keep the translatable strings, and (b) added a
variable inspath to keep track of what the string was before
toggling. I'm sure there's bugs, have at it.
- Make sure all functions have prototypes in proto.h, and swap
some functions around to put similar functions closer
together (for this, rename clear_bottombars() to
blank_bottombars()). (DLR; suggested by David Benbennick)
- configure.ac:
- Added pt_BR to ALL_LINGUAS (Jordi).
- Changed --enable-color warning to be slightly less severe.
- Put the configure options in more or less alphabetical order,
and remove --enable-undo, since it doesn't do anything. (DLR)
- files.c:
open_file()
- String change: "File "x" is a directory" -> ""x" is a
directory". (Jordi)
do_insertfile()
- Disallow multibuffer toggling at the "Insert File" prompt if
we're in both view and multibuffer mode, so as to keep proper
integration between the two, and make sure the toggle
actually works all the time otherwise. Also, use
NANO_LOAD_KEY as an alias for TOGGLE_LOAD_KEY, so
--enable-tiny and --enable-multibuffer can be used together
again. (DLR)
open_prevfile_void(), open_nextfile_void()
- Return the return values of open_prevfile() and
open_nextfile(), respectively, instead of (incorrectly)
......@@ -29,6 +43,14 @@ CVS code -
- Most likely fixed the check marked with FIXME, so that tab
completion works properly when we're trying to tab-complete a
username and the string already contains data. (DLR)
- global.c:
shortcut_init()
- Use NANO_LOAD_KEY as an alias for TOGGLE_LOAD_KEY, so
--enable-tiny and --enable-multibuffer can be used together
again. (DLR)
thanks_for_all_the_fish()
- Make sure the reference to help_text is #ifdefed out when
--disable-help is used. (DLR)
- move.c:
page_up()
- Fix bug where current is moved up two lines when the up arrow
......@@ -42,6 +64,12 @@ CVS code -
off, pressing the down arrow on that last line centers the
cursor without updating the edit window. (Jeff DeFouw)
- nano.c:
version()
- Put the listed configure options in more or less alphabetical
order. (DLR)
open_pipe()
- If we're in view mode here (in which case we're also in
multibuffer mode), don't set the modification flag. (DLR)
do_next_word(), do_prev_word()
- If we're on the last/first line of the file, don't center the
screen; Pico doesn't in the former case. (DLR)
......@@ -65,6 +93,10 @@ CVS code -
do_justify()
- Fix cosmetic problems caused when justifying on the
magicline. (David Benbennick)
main()
- When searching through the main shortcut list looking for a
shortcut key, stop searching after finding one; this avoids a
rare segfault. (DLR)
- nano.h:
- Change search toggles for case sensitive searching and regexp
searching to M-C and M-R, respectively. (DLR; suggested by
......
......@@ -29,70 +29,55 @@ if test "$debug_support" != "yes"; then
AC_DEFINE(NDEBUG, 1, [Shut up the assert warnings :-)])
fi
AC_ARG_ENABLE(extra,
[ --enable-extra Enable extra (optional) functions, including easter eggs],
[if test x$enableval = xyes; then
AC_DEFINE(NANO_EXTRA, 1, [Define this to enable the extra stuff.]) extra_support=yes
AC_DEFINE(ENABLE_MULTIBUFFER, 1, [Define this to enable multiple file buffers.]) multibuffer_support=yes
fi])
AC_ARG_ENABLE(tiny,
[ --enable-tiny Disable features for the sake of size
(currently disables detailed help and i18n)],
[if test x$enableval = xyes; then
AC_DEFINE(NANO_SMALL, 1, [Define this to make the nano executable as small as possible.]) tiny_support=yes
AC_DEFINE(DISABLE_TABCOMP, 1, [Define to disable the tab completion code Chris worked so hard on!])
AC_DEFINE(DISABLE_SPELLER, 1, [Define this to disable the use(full|less) spelling functions.])
AC_DEFINE(DISABLE_BROWSER, 1, [Define this to disable the built-in (crappy) file browser.])
AC_DEFINE(DISABLE_HELP, 1, [Define this to disable the ^G help menu.])
AC_DEFINE(DISABLE_JUSTIFY, 1, [Define this to disable the justify routine.])
AC_DEFINE(DISABLE_BROWSER, 1, [Define this to disable the built-in (crappy) file browser.])
AC_DEFINE(DISABLE_MOUSE, 1, [Define this to disable the mouse functions.])
AC_DEFINE(DISABLE_OPERATINGDIR, 1, [Define this to disable setting of the operating directory (chroot of sorts).])
AC_DEFINE(DISABLE_SPELLER, 1, [Define this to disable the use(full|less) spelling functions.])
AC_DEFINE(DISABLE_TABCOMP, 1, [Define to disable the tab completion code Chris worked so hard on!])
fi])
AC_ARG_ENABLE(extra,
[ --enable-extra Enable extra (optional) functions, including easter eggs],
[if test x$enableval = xyes; then
AC_DEFINE(NANO_EXTRA, 1, [Define this to enable the extra stuff.]) extra_support=yes
AC_DEFINE(ENABLE_MULTIBUFFER, 1, [Define this to enable multiple file buffers.]) multibuffer_support=yes
AC_DEFINE(ENABLE_UNDO, 1, [Define this to enable undoing... something]) undo_support=yes
fi])
AC_ARG_ENABLE(undo,
[ --enable-undo Enable undo support],
[if test x$enableval = xyes && test x$tiny_support != xyes; then
AC_DEFINE(ENABLE_UNDO, 1, [Define this to enable undoing... something.]) undo_support=yes
AC_ARG_ENABLE(browser,
[ --disable-browser Disable mini file browser],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_BROWSER, 1, [Define this to disable the built-in (crappy) file browser.])
fi])
AC_ARG_ENABLE(multibuffer,
[ --enable-multibuffer Enable multiple file buffers],
[if test x$enableval = xyes; then
AC_DEFINE(ENABLE_MULTIBUFFER, 1, [Define this to enable multiple file buffers.]) multibuffer_support=yes
AC_ARG_ENABLE(help,
[ --disable-help Disable help function (^G)],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_HELP, 1, [Define this to disable the ^G help menu.])
fi])
AC_ARG_ENABLE(nanorc,
[ --enable-nanorc Enable use of .nanorc file],
[if test x$enableval = xyes; then
AC_DEFINE(ENABLE_NANORC, 1, [Define this to use the .nanorc file.]) nanorc_support=yes
AC_ARG_ENABLE(justify,
[ --disable-justify Disable justify/unjustify function],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_JUSTIFY, 1, [Define this to disable the justify routine.])
fi])
AC_ARG_ENABLE(color,
[ --enable-color Enable color and syntax highlighting],
[if test x$enableval = xyes; then
AC_DEFINE(ENABLE_NANORC, 1, [Define this to use the .nanorc file.]) nanorc_support=yes
AC_DEFINE(ENABLE_COLOR, 1, [Define this to have syntax highlighting, requires ENABLE_NANORC too!]) color_support=yes
AC_MSG_WARN([
***********************************************************************
*** WARNING: Color support is far from perfect, but functional. ***
*** Be careful with syntax in your .nanorc or nano may malfunction. ***
***********************************************************************
])
fi])
AC_ARG_ENABLE(tabcomp,
[ --disable-tabcomp Disable tab completion code for a smaller binary],
AC_ARG_ENABLE(mouse,
[ --disable-mouse Disable mouse support (and -m flag)],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_TABCOMP, 1, [Define to disable the tab completion code Chris worked so hard on!])
AC_DEFINE(DISABLE_MOUSE, 1, [Define this to disable the mouse functions.])
fi])
AC_ARG_ENABLE(justify,
[ --disable-justify Disable justify/unjustify function],
AC_ARG_ENABLE(operatingdir,
[ --disable-operatingdir Disable setting of operating directory (chroot of sorts)],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_JUSTIFY, 1, [Define this to disable the justify routine.])
AC_DEFINE(DISABLE_OPERATINGDIR, 1, [Define this to disable setting of the operating directory (chroot of sorts).])
fi])
AC_ARG_ENABLE(speller,
......@@ -101,16 +86,10 @@ AC_ARG_ENABLE(speller,
AC_DEFINE(DISABLE_SPELLER, 1, [Define this to disable the use(full|less) spelling functions.])
fi])
AC_ARG_ENABLE(help,
[ --disable-help Disable help function (^G)],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_HELP, 1, [Define this to disable the ^G help menu.])
fi])
AC_ARG_ENABLE(browser,
[ --disable-browser Disable mini file browser],
AC_ARG_ENABLE(tabcomp,
[ --disable-tabcomp Disable tab completion code for a smaller binary],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_BROWSER, 1, [Define this to disable the built-in (crappy) file browser.])
AC_DEFINE(DISABLE_TABCOMP, 1, [Define to disable the tab completion code Chris worked so hard on!])
fi])
AC_ARG_ENABLE(wrapping,
......@@ -119,16 +98,30 @@ AC_ARG_ENABLE(wrapping,
AC_DEFINE(DISABLE_WRAPPING, 1, [Define this to disable any and all text wrapping.])
fi])
AC_ARG_ENABLE(mouse,
[ --disable-mouse Disable mouse support (and -m flag)],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_MOUSE, 1, [Define this to disable the mouse functions.])
AC_ARG_ENABLE(color,
[ --enable-color Enable color and syntax highlighting],
[if test x$enableval = xyes; then
AC_DEFINE(ENABLE_NANORC, 1, [Define this to use the .nanorc file.]) nanorc_support=yes
AC_DEFINE(ENABLE_COLOR, 1, [Define this to have syntax highlighting, requires ENABLE_NANORC too!]) color_support=yes
AC_MSG_WARN([
***********************************************************************
*** WARNING: Color support is far from perfect, but functional. ***
*** Be careful with syntax in your .nanorc or nano may malfunction. ***
***********************************************************************
])
fi])
AC_ARG_ENABLE(multibuffer,
[ --enable-multibuffer Enable multiple file buffers],
[if test x$enableval = xyes; then
AC_DEFINE(ENABLE_MULTIBUFFER, 1, [Define this to enable multiple file buffers.]) multibuffer_support=yes
fi])
AC_ARG_ENABLE(operatingdir,
[ --disable-operatingdir Disable setting of operating directory (chroot of sorts)],
[if test x$enableval != xyes; then
AC_DEFINE(DISABLE_OPERATINGDIR, 1, [Define this to disable setting of the operating directory (chroot of sorts).])
AC_ARG_ENABLE(nanorc,
[ --enable-nanorc Enable use of .nanorc file],
[if test x$enableval = xyes; then
AC_DEFINE(ENABLE_NANORC, 1, [Define this to use the .nanorc file.]) nanorc_support=yes
fi])
AC_MSG_CHECKING([whether to use slang])
......
......@@ -103,7 +103,6 @@ void new_file(void)
#ifdef ENABLE_COLOR
update_color();
#endif
}
filestruct *read_line(char *buf, filestruct *prev, int *line1ins, int len)
......@@ -453,14 +452,11 @@ int do_insertfile(int loading_file)
i = statusq(1, insertfile_list, inspath, _("File to insert [from ./] "));
if (i != -1) {
inspath = mallocstrcpy(inspath, answer);
#ifdef DEBUG
fprintf(stderr, _("filename is %s\n"), answer);
#endif
#ifndef DISABLE_TABCOMP
realname = real_dir_from_tilde(answer);
#else
......@@ -494,9 +490,12 @@ int do_insertfile(int loading_file)
#endif
#ifdef ENABLE_MULTIBUFFER
if (i == TOGGLE_LOAD_KEY) {
TOGGLE(MULTIBUFFER);
return do_insertfile(loading_file);
if (i == NANO_LOAD_KEY) {
/* don't allow toggling if we're in both view mode and
multibuffer mode now */
if (!ISSET(VIEW_MODE) || !ISSET(MULTIBUFFER))
TOGGLE(MULTIBUFFER);
return do_insertfile(ISSET(MULTIBUFFER));
}
#endif
#ifndef NANO_SMALL
......@@ -574,8 +573,6 @@ int do_insertfile(int loading_file)
}
#endif
/* If we've gone off the bottom, recenter; otherwise, just redraw */
if (current->lineno > editbot->lineno)
edit_update(current, CENTER);
......@@ -1139,8 +1136,8 @@ char *get_full_path(char *origpath)
* 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(char *path) {
char *check_writable_directory(char *path)
{
char *full_path = get_full_path(path);
int writable;
struct stat fileinfo;
......@@ -1178,8 +1175,8 @@ char *check_writable_directory(char *path) {
* implementation is to go on generating random filenames regardless of
* it.
*/
char *safe_tempnam(const char *dirname, const char *filename_prefix) {
char *safe_tempnam(const char *dirname, const char *filename_prefix)
{
char *buf, *tempdir = NULL, *full_tempdir = NULL;
int filedesc;
......@@ -2221,6 +2218,12 @@ char *input_tab(char *buf, int place, int *lastwastab, int *newplace, int *list)
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. */
......@@ -2416,51 +2419,6 @@ int diralphasort(const void *va, const void *vb)
}
/* Initialize the browser code, including the list of files in *path */
char **browser_init(char *path, int *longest, int *numents)
{
DIR *dir;
struct dirent *next;
char **filelist = (char **) NULL;
int i = 0;
dir = opendir(path);
if (!dir)
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 = nmalloc(*numents * sizeof (char *));
while ((next = readdir(dir)) != NULL) {
if (!strcmp(next->d_name, "."))
continue;
filelist[i] = charalloc(strlen(next->d_name) + strlen(path) + 2);
if (!strcmp(path, "/"))
snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 1,
"%s%s", path, next->d_name);
else
snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 2,
"%s/%s", path, next->d_name);
i++;
}
if (*longest > COLS - 1)
*longest = COLS - 1;
return filelist;
}
/* Free our malloc()ed memory */
void free_charptrarray(char **array, int len)
{
......@@ -2513,6 +2471,51 @@ void striponedir(char *foo)
return;
}
/* Initialize the browser code, including the list of files in *path */
char **browser_init(char *path, int *longest, int *numents)
{
DIR *dir;
struct dirent *next;
char **filelist = (char **) NULL;
int i = 0;
dir = opendir(path);
if (!dir)
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 = nmalloc(*numents * sizeof (char *));
while ((next = readdir(dir)) != NULL) {
if (!strcmp(next->d_name, "."))
continue;
filelist[i] = charalloc(strlen(next->d_name) + strlen(path) + 2);
if (!strcmp(path, "/"))
snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 1,
"%s%s", path, next->d_name);
else
snprintf(filelist[i], strlen(next->d_name) + strlen(path) + 2,
"%s/%s", path, next->d_name);
i++;
}
if (*longest > COLS - 1)
*longest = COLS - 1;
return filelist;
}
/* Our browser function. inpath is the path to start browsing from */
char *do_browser(char *inpath)
{
......
......@@ -217,19 +217,6 @@ void toggle_init_one(int val, const char *desc, int flag)
u->next = NULL;
}
#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
void toggle_init(void)
{
char *toggle_const_msg, *toggle_autoindent_msg, *toggle_suspend_msg,
......@@ -288,6 +275,19 @@ void toggle_init(void)
toggle_init_one(TOGGLE_BACKUP_KEY, toggle_backup_msg, BACKUP_FILE);
toggle_init_one(TOGGLE_SMOOTH_KEY, toggle_smooth_msg, SMOOTHSCROLL);
}
#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. */
......@@ -325,8 +325,8 @@ void shortcut_init(int unjustify)
"", *nano_backup_msg = "";
#ifdef ENABLE_MULTIBUFFER
char *nano_openprev_msg = "", *nano_opennext_msg = "",
*nano_multibuffer_msg = "";
char *nano_openprev_msg = "", *nano_opennext_msg =
"", *nano_multibuffer_msg = "";
#endif
#ifdef HAVE_REGEX_H
char *nano_regexp_msg = "", *nano_bracket_msg = "";
......@@ -727,7 +727,7 @@ void shortcut_init(int unjustify)
IFHELP(nano_execute_msg, 0), 0, 0, NOVIEW, 0);
#endif
#ifdef ENABLE_MULTIBUFFER
sc_init_one(&insertfile_list, TOGGLE_LOAD_KEY, _("New Buffer"),
sc_init_one(&insertfile_list, NANO_LOAD_KEY, _("New Buffer"),
IFHELP(nano_multibuffer_msg, 0), 0, 0, NOVIEW, 0);
#endif
......@@ -811,8 +811,10 @@ void thanks_for_all_the_fish(void)
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)
......
......@@ -35,43 +35,6 @@
#define _(string) (string)
#endif
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();
} 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();
UNSET(KEEP_CUTBUFFER);
check_statblank();
return 1;
}
int do_home(void)
{
UNSET(KEEP_CUTBUFFER);
......@@ -138,6 +101,43 @@ int do_page_up(void)
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();
} 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();
UNSET(KEEP_CUTBUFFER);
check_statblank();
return 1;
}
int do_up(void)
{
wrap_reset();
......@@ -197,15 +197,13 @@ int do_down(void) {
return 1;
}
int do_right(void)
int do_left(void)
{
assert(current_x <= strlen(current->data));
if (current->data[current_x] != '\0')
current_x++;
else if (current->next) {
do_down();
current_x = 0;
if (current_x > 0)
current_x--;
else if (current != fileage) {
do_up();
current_x = strlen(current->data);
}
placewewant = xplustabs();
update_line(current, current_x);
......@@ -214,13 +212,15 @@ int do_right(void)
return 1;
}
int do_left(void)
int do_right(void)
{
if (current_x > 0)
current_x--;
else if (current != fileage) {
do_up();
current_x = strlen(current->data);
assert(current_x <= strlen(current->data));
if (current->data[current_x] != '\0')
current_x++;
else if (current->next) {
do_down();
current_x = 0;
}
placewewant = xplustabs();
update_line(current, current_x);
......
......@@ -222,6 +222,258 @@ void global_init(int save_cutbuffer)
hblank[COLS] = '\0';
}
void window_init(void)
{
if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
die_too_small();
/* Set up the main text window */
edit = newwin(editwinrows, COLS, 2, 0);
/* And the other windows */
topwin = newwin(2, COLS, 0, 0);
bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
#ifdef PDCURSES
/* Oops, I guess we need this again.
Moved here so the keypad still works after a Meta-X, for example */
keypad(edit, TRUE);
keypad(bottomwin, TRUE);
#endif
}
void mouse_init(void)
{
#ifndef DISABLE_MOUSE
#ifdef NCURSES_MOUSE_VERSION
if (ISSET(USE_MOUSE)) {
keypad_on(edit, 1);
keypad_on(bottomwin, 1);
mousemask(BUTTON1_RELEASED, NULL);
mouseinterval(50);
} else
mousemask(0, NULL);
#endif
#endif
}
#ifndef DISABLE_HELP
/* This function allocates help_text, and stores the help string in it.
* help_text should be NULL initially. */
void help_init(void)
{
size_t allocsize = 1; /* space needed for help_text */
char *ptr = NULL;
#ifndef NANO_SMALL
const toggle *t;
#endif
const shortcut *s;
/* First set up the initial help text for the current function */
if (currshortcut == whereis_list || currshortcut == replace_list
|| currshortcut == replace_list_2)
ptr = _("Search Command Help Text\n\n "
"Enter the words or characters you would like to search "
"for, then hit enter. If there is a match for the text you "
"entered, the screen will be updated to the location of the "
"nearest match for the search string.\n\n "
"If using Pico Mode via the -p or --pico flags, the "
"Meta-P toggle, or a nanorc file, the previous search "
"string will be shown in brackets after the Search: prompt. "
"Hitting Enter without entering any text will perform the "
"previous search. Otherwise, the previous string will be "
"placed before the cursor, and can be edited or deleted "
"before hitting enter.\n\n The following function keys are "
"available in Search mode:\n\n");
else if (currshortcut == goto_list)
ptr = _("Go To Line Help Text\n\n "
"Enter the line number that you wish to go to and hit "
"Enter. If there are fewer lines of text than the "
"number you entered, you will be brought to the last line "
"of the file.\n\n The following function keys are "
"available in Go To Line mode:\n\n");
else if (currshortcut == insertfile_list)
ptr = _("Insert File Help Text\n\n "
"Type in the name of a file to be inserted into the current "
"file buffer at the current cursor location.\n\n "
"If you have compiled nano with multiple file buffer "
"support, and enable multiple buffers with the -F "
"or --multibuffer command line flags, the Meta-F toggle, or "
"a nanorc file, inserting a file will cause it to be "
"loaded into a separate buffer (use Meta-< and > to switch "
"between file buffers).\n\n If you need another blank "
"buffer, do not enter any filename, or type in a "
"nonexistent filename at the prompt and press "
"Enter.\n\n The following function keys are "
"available in Insert File mode:\n\n");
else if (currshortcut == writefile_list)
ptr = _("Write File Help Text\n\n "
"Type the name that you wish to save the current file "
"as and hit Enter to save the file.\n\n If you have "
"selected text with Ctrl-^, you will be prompted to "
"save only the selected portion to a separate file. To "
"reduce the chance of overwriting the current file with "
"just a portion of it, the current filename is not the "
"default in this mode.\n\n The following function keys "
"are available in Write File mode:\n\n");
#ifndef DISABLE_BROWSER
else if (currshortcut == browser_list)
ptr = _("File Browser Help Text\n\n "
"The file browser is used to visually browse the "
"directory structure to select a file for reading "
"or writing. You may use the arrow keys or Page Up/"
"Down to browse through the files, and S or Enter to "
"choose the selected file or enter the selected "
"directory. To move up one level, select the directory "
"called \"..\" at the top of the file list.\n\n The "
"following function keys are available in the file "
"browser:\n\n");
else if (currshortcut == gotodir_list)
ptr = _("Browser Go To Directory Help Text\n\n "
"Enter the name of the directory you would like to "
"browse to.\n\n If tab completion has not been disabled, "
"you can use the TAB key to (attempt to) automatically "
"complete the directory name.\n\n The following function "
"keys are available in Browser Go To Directory mode:\n\n");
#endif
else if (currshortcut == spell_list)
ptr = _("Spell Check Help Text\n\n "
"The spell checker checks the spelling of all text "
"in the current file. When an unknown word is "
"encountered, it is highlighted and a replacement can "
"be edited. It will then prompt to replace every "
"instance of the given misspelled word in the "
"current file.\n\n The following other functions are "
"available in Spell Check mode:\n\n");
#ifndef NANO_SMALL
else if (currshortcut == extcmd_list)
ptr = _("External Command Help Text\n\n "
"This menu allows you to insert the output of a command "
"run by the shell into the current buffer (or a new "
"buffer in multibuffer mode).\n\n The following keys are "
"available in this mode:\n\n");
#endif
else /* Default to the main help list */
ptr = _(" nano help text\n\n "
"The nano editor is designed to emulate the functionality and "
"ease-of-use of the UW Pico text editor. There are four main "
"sections of the editor: The top line shows the program "
"version, the current filename being edited, and whether "
"or not the file has been modified. Next is the main editor "
"window showing the file being edited. The status line is "
"the third line from the bottom and shows important messages. "
"The bottom two lines show the most commonly used shortcuts "
"in the editor.\n\n "
"The notation for shortcuts is as follows: Control-key "
"sequences are notated with a caret (^) symbol and are entered "
"with the Control (Ctrl) key. Escape-key sequences are notated "
"with the Meta (M) symbol and can be entered using either the "
"Esc, Alt or Meta key depending on your keyboard setup. The "
"following keystrokes are available in the main editor window. "
"Alternative keys are shown in parentheses:\n\n");
allocsize += strlen(ptr);
/* The space needed for the shortcut lists, at most COLS characters,
* plus '\n'. */
allocsize += (COLS + 1) * length_of_list(currshortcut);
#ifndef NANO_SMALL
/* If we're on the main list, we also count the toggle help text.
* Each line has "M-%c\t\t\t", which fills 24 columns, plus at most
* COLS - 24 characters, plus '\n'.*/
if (currshortcut == main_list)
for (t = toggles; t != NULL; t = t->next)
allocsize += COLS - 17;
#endif /* !NANO_SMALL */
/* help_text has been freed and set to NULL unless the user resized
* while in the help screen. */
free(help_text);
/* Allocate space for the help text */
help_text = charalloc(allocsize);
/* Now add the text we want */
strcpy(help_text, ptr);
ptr = help_text + strlen(help_text);
/* Now add our shortcut info */
for (s = currshortcut; s != NULL; s = s->next) {
/* true if the character in s->altval is shown in first column */
int meta_shortcut = 0;
if (s->val > 0 && s->val < 32)
ptr += sprintf(ptr, "^%c", s->val + 64);
#ifndef NANO_SMALL
else if (s->val == NANO_CONTROL_SPACE)
ptr += sprintf(ptr, "^%.6s", _("Space"));
else if (s->altval == NANO_ALT_SPACE) {
meta_shortcut = 1;
ptr += sprintf(ptr, "M-%.5s", _("Space"));
}
#endif
else if (s->altval > 0) {
meta_shortcut = 1;
ptr += sprintf(ptr, "M-%c", s->altval -
(('A' <= s->altval && s->altval <= 'Z') ||
'a' <= s->altval ? 32 : 0));
}
/* Hack */
else if (s->val >= 'a') {
meta_shortcut = 1;
ptr += sprintf(ptr, "M-%c", s->val - 32);
}
*(ptr++) = '\t';
if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
ptr += sprintf(ptr, "(F%d)", s->misc1 - KEY_F0);
*(ptr++) = '\t';
if (!meta_shortcut && s->altval > 0)
ptr += sprintf(ptr, "(M-%c)", s->altval -
(('A' <= s->altval && s->altval <= 'Z') || 'a' <= s->altval
? 32 : 0));
*(ptr++) = '\t';
assert(s->help != NULL);
ptr += sprintf(ptr, "%.*s\n", COLS - 24, s->help);
}
#ifndef NANO_SMALL
/* And the toggles... */
if (currshortcut == main_list)
for (t = toggles; t != NULL; t = t->next) {
ptr += sprintf(ptr, "M-%c\t\t\t", t->val - 32);
assert(t->desc != NULL);
ptr += sprintf(ptr, _("%.*s enable/disable\n"), COLS - 24, t->desc);
}
#endif /* !NANO_SMALL */
/* If all went well, we didn't overwrite the allocated space for
help_text. */
assert(strlen(help_text) < allocsize);
}
#endif
/* Create a new filestruct node. Note that we specifically do not set
* prevnode->next equal to the new line. */
filestruct *make_new_node(filestruct *prevnode)
{
filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
newnode->data = NULL;
newnode->prev = prevnode;
newnode->next = NULL;
newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1;
return newnode;
}
/* Make a copy of a node to a pointer (space will be malloc()ed). */
filestruct *copy_node(const filestruct *src)
{
......@@ -238,6 +490,19 @@ filestruct *copy_node(const filestruct *src)
return dst;
}
/* Splice a node into an existing filestruct. */
void splice_node(filestruct *begin, filestruct *newnode, filestruct *end)
{
if (newnode != NULL) {
newnode->next = end;
newnode->prev = begin;
}
if (begin != NULL)
begin->next = newnode;
if (end != NULL)
end->prev = newnode;
}
/* Unlink a node from the rest of the filestruct. */
void unlink_node(const filestruct *fileptr)
{
......@@ -329,7 +594,7 @@ void renumber(filestruct *fileptr)
* strings to translate and takes out the parts that shouldn't be
* translatable (the flag names). */
void print1opt(const char *shortflag, const char *longflag,
const char *desc)
const char *desc)
{
printf(" %s\t", shortflag);
if (strlen(shortflag) < 8)
......@@ -430,46 +695,51 @@ void version(void)
(" Email: nano@nano-editor.org Web: http://www.nano-editor.org"));
printf(_("\n Compiled options:"));
#ifdef DEBUG
printf(" --enable-debug");
#endif
#ifdef NANO_EXTRA
printf(" --enable-extra");
#endif
#ifdef ENABLE_MULTIBUFFER
printf(" --enable-multibuffer");
#endif
#ifdef ENABLE_NANORC
printf(" --enable-nanorc");
#endif
#ifdef ENABLE_COLOR
printf(" --enable-color");
#endif
#ifdef NANO_SMALL
printf(" --enable-tiny");
#else
#ifdef DISABLE_BROWSER
printf(" --disable-browser");
#endif
#ifdef DISABLE_TABCOMP
printf(" --disable-tabcomp");
#ifdef DISABLE_HELP
printf(" --disable-help");
#endif
#ifdef DISABLE_JUSTIFY
printf(" --disable-justify");
#endif
#ifdef DISABLE_SPELLER
printf(" --disable-speller");
#endif
#ifdef DISABLE_HELP
printf(" --disable-help");
#endif
#ifdef DISABLE_MOUSE
printf(" --disable-mouse");
#endif
#ifdef DISABLE_OPERATINGDIR
printf(" --disable-operatingdir");
#endif
#endif /* NANO_SMALL */
#ifdef DISABLE_WRAPPING
printf(" --disable-wrapping");
#ifdef DISABLE_SPELLER
printf(" --disable-speller");
#endif
#ifdef DISABLE_TABCOMP
printf(" --disable-tabcomp");
#endif
#endif /* NANO_SMALL */
#ifdef DISABLE_WRAPPING
printf(" --disable-wrapping");
#endif
#ifdef ENABLE_COLOR
printf(" --enable-color");
#endif
#ifdef ENABLE_MULTIBUFFER
printf(" --enable-multibuffer");
#endif
#ifdef ENABLE_NANORC
printf(" --enable-nanorc");
#endif
#ifdef ENABLE_UNDO
printf(" --enable-undo");
#endif
#ifdef USE_SLANG
printf(" --with-slang");
......@@ -477,50 +747,11 @@ void version(void)
printf("\n");
}
/* Create a new filestruct node. Note that we specifically do not set
* prevnode->next equal to the new line. */
filestruct *make_new_node(filestruct *prevnode)
{
filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
newnode->data = NULL;
newnode->prev = prevnode;
newnode->next = NULL;
newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1;
return newnode;
}
/* Splice a node into an existing filestruct. */
void splice_node(filestruct *begin, filestruct *newnode, filestruct *end)
{
if (newnode != NULL) {
newnode->next = end;
newnode->prev = begin;
}
if (begin != NULL)
begin->next = newnode;
if (end != NULL)
end->prev = newnode;
}
int do_mark(void)
/* Stuff we do when we abort from programs and want to clean up the
* screen. This doesn't do much right now. */
void do_early_abort(void)
{
#ifdef NANO_SMALL
nano_disabled_msg();
#else
if (!ISSET(MARK_ISSET)) {
statusbar(_("Mark Set"));
SET(MARK_ISSET);
mark_beginbuf = current;
mark_beginx = current_x;
} else {
statusbar(_("Mark UNset"));
UNSET(MARK_ISSET);
edit_refresh();
}
#endif
return 1;
blank_statusbar_refresh();
}
int no_help(void)
......@@ -535,448 +766,264 @@ void nano_disabled_msg(void)
}
#endif
/* The user typed a printable character; add it to the edit buffer. */
void do_char(char ch)
{
size_t current_len = strlen(current->data);
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
int refresh = 0;
/* Do we have to run edit_refresh(), or can we get away with
* update_line()? */
#endif
/* magic-line: when a character is inserted on the current magic line,
* it means we need a new one! */
if (filebot == current && current->data[0] == '\0') {
new_magicline();
fix_editbot();
}
/* more dangerousness fun =) */
current->data = nrealloc(current->data, current_len + 2);
assert(current_x <= current_len);
memmove(&current->data[current_x + 1],
&current->data[current_x],
current_len - current_x + 1);
current->data[current_x] = ch;
totsize++;
set_modified();
#ifndef NANO_SMALL
/* note that current_x has not yet been incremented */
if (current == mark_beginbuf && current_x < mark_beginx)
mark_beginx++;
#endif
do_right();
#ifndef DISABLE_WRAPPING
if (!ISSET(NO_WRAP) && ch != '\t')
refresh = do_wrap(current);
#endif
#ifdef ENABLE_COLOR
refresh = 1;
#endif
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
if (refresh)
edit_refresh();
#endif
check_statblank();
UNSET(KEEP_CUTBUFFER);
}
static int pid; /* This is the PID of the newly forked process
* below. It must be global since the signal
* handler needs it. */
/* Someone hits return *gasp!* */
int do_enter(void)
RETSIGTYPE cancel_fork(int signal)
{
filestruct *newnode;
char *tmp;
newnode = make_new_node(current);
assert(current != NULL && current->data != NULL);
tmp = &current->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(&current->data);
/* The logic here is as follows:
* -> If we are at the bottom of the buffer, we want to recenter
* (read: rebuild) the screen and forcibly move the cursor.
* -> otherwise, we want simply to redraw the screen and update
* where we think the cursor is.
*/
if (current_y == editwinrows - 1) {
edit_update(current, CENTER);
reset_cursor();
} else {
current_y++;
edit_refresh();
update_cursor();
}
totlines++;
set_modified();
placewewant = xplustabs();
return 1;
if (kill(pid, SIGKILL)==-1) nperror("kill");
}
#ifndef NANO_SMALL
int do_next_word(void)
int open_pipe(const char *command)
{
filestruct *old = current;
assert(current != NULL && current->data != NULL);
/* Skip letters in this word first. */
while (current->data[current_x] != '\0' &&
isalnum((int)current->data[current_x]))
current_x++;
for (; current != NULL; current = current->next) {
while (current->data[current_x] != '\0' &&
!isalnum((int)current->data[current_x]))
current_x++;
if (current->data[current_x] != '\0')
break;
current_x = 0;
}
if (current == NULL)
current = filebot;
int fd[2];
FILE *f;
struct sigaction oldaction, newaction;
/* original and temporary handlers for SIGINT */
#ifdef _POSIX_VDISABLE
struct termios term, newterm;
#endif /* _POSIX_VDISABLE */
int cancel_sigs = 0;
/* cancel_sigs==1 means that sigaction failed without changing the
* signal handlers. cancel_sigs==2 means the signal handler was
* changed, but the tcsetattr didn't succeed.
* I use this variable since it is important to put things back when
* we finish, even if we get errors. */
placewewant = xplustabs();
/* Make our pipes. */
if (current->lineno >= editbot->lineno) {
/* If we're on the last line, don't center the screen. */
if (current->lineno == filebot->lineno)
edit_refresh();
else
edit_update(current, CENTER);
}
else {
/* If we've jumped lines, refresh the old line. We can't just
use current->prev here, because we may have skipped over some
blank lines, in which case the previous line is the wrong
one. */
if (current != old) {
update_line(old, 0);
/* If the mark was set, then the lines between old and
current have to be updated too. */
if (ISSET(MARK_ISSET)) {
while (old->next != current) {
old = old->next;
update_line(old, 0);
}
}
}
update_line(current, current_x);
if (pipe(fd) == -1) {
statusbar(_("Could not pipe"));
return 1;
}
return 0;
}
/* The same thing for backwards. */
int do_prev_word(void)
{
filestruct *old = current;
assert(current != NULL);
/* Skip letters in this word first. */
while (current_x >= 0 && isalnum((int)current->data[current_x]))
current_x--;
for (; current != NULL; current = current->prev) {
while (current_x >= 0 && !isalnum((int)current->data[current_x]))
current_x--;
/* Fork a child. */
if (current_x >= 0)
break;
if ((pid = fork()) == 0) {
close(fd[0]);
dup2(fd[1], fileno(stdout));
dup2(fd[1], fileno(stderr));
/* If execl() returns at all, there was an error. */
execl("/bin/sh","sh","-c",command,0);
exit(0);
}
if (current->prev != NULL)
current_x = strlen(current->prev->data);
/* Else continue as parent. */
close(fd[1]);
if (pid == -1) {
close(fd[0]);
statusbar(_("Could not fork"));
return 1;
}
if (current != NULL) {
while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
current_x--;
/* Before we start reading the forked command's output, we set
* things up so that ^C will cancel the new process. */
if (sigaction(SIGINT, NULL, &newaction)==-1) {
cancel_sigs = 1;
nperror("sigaction");
} else {
current = fileage;
current_x = 0;
newaction.sa_handler = cancel_fork;
if (sigaction(SIGINT, &newaction, &oldaction)==-1) {
cancel_sigs = 1;
nperror("sigaction");
}
}
/* Note that now oldaction is the previous SIGINT signal handler,
* to be restored later. */
placewewant = xplustabs();
if (current->lineno <= edittop->lineno) {
/* If we're on the first line, don't center the screen. */
if (current->lineno == fileage->lineno)
edit_refresh();
else
edit_update(current, CENTER);
/* See if the platform supports disabling individual control
* characters. */
#ifdef _POSIX_VDISABLE
if (!cancel_sigs && tcgetattr(0, &term) == -1) {
cancel_sigs = 2;
nperror("tcgetattr");
}
else {
/* If we've jumped lines, refresh the old line. We can't just
use current->prev here, because we may have skipped over some
blank lines, in which case the previous line is the wrong
one. */
if (current != old) {
update_line(old, 0);
/* If the mark was set, then the lines between old and
current have to be updated too. */
if (ISSET(MARK_ISSET)) {
while (old->prev != current) {
old = old->prev;
update_line(old, 0);
}
}
if (!cancel_sigs) {
newterm = term;
/* Grab oldterm's VINTR key :-) */
newterm.c_cc[VINTR] = oldterm.c_cc[VINTR];
if (tcsetattr(0, TCSANOW, &newterm) == -1) {
cancel_sigs = 2;
nperror("tcsetattr");
}
update_line(current, current_x);
}
#endif /* _POSIX_VDISABLE */
f = fdopen(fd[0], "rb");
if (!f)
nperror("fdopen");
read_file(f, "stdin", 0);
/* if multibuffer mode is on, we could be here in view mode; if so,
don't set the modification flag */
if (!ISSET(VIEW_MODE))
set_modified();
if (wait(NULL) == -1)
nperror("wait");
#ifdef _POSIX_VDISABLE
if (!cancel_sigs && tcsetattr(0, TCSANOW, &term) == -1)
nperror("tcsetattr");
#endif /* _POSIX_VDISABLE */
if (cancel_sigs!=1 && sigaction(SIGINT, &oldaction, NULL) == -1)
nperror("sigaction");
return 0;
}
#endif /* !NANO_SMALL */
#endif /* NANO_SMALL */
#ifndef DISABLE_WRAPPING
/* We wrap the given line. Precondition: we assume the cursor has been
* moved forward since the last typed character. Return value:
* whether we wrapped. */
int do_wrap(filestruct *inptr)
#ifndef DISABLE_MOUSE
#ifdef NCURSES_MOUSE_VERSION
void do_mouse(void)
{
size_t len = strlen(inptr->data); /* length of the line we wrap */
int i = 0; /* generic loop variable */
int wrap_loc = -1; /* index of inptr->data where we wrap */
int word_back = -1;
#ifndef NANO_SMALL
const char *indentation = NULL;
/* indentation to prepend to the new line */
int indent_len = 0; /* strlen(indentation) */
#endif
const char *after_break; /* text after the wrap point */
int after_break_len; /* strlen(after_break) */
int wrapping = 0; /* do we prepend to the next line? */
const char *wrap_line = NULL;
/* the next line, minus indentation */
int wrap_line_len = 0; /* strlen(wrap_line) */
char *newline = NULL; /* the line we create */
int new_line_len = 0; /* eventual length of newline */
MEVENT mevent;
int currslen;
const shortcut *s = currshortcut;
/* There are three steps. First, we decide where to wrap. Then, we
* create the new wrap line. Finally, we clean up. */
if (getmouse(&mevent) == ERR)
return;
/* Step 1, finding where to wrap. We are going to replace a white-space
* character with a new-line. In this step, we set wrap_loc as the
* location of this replacement.
*
* Where should we break the line? We need the last "legal wrap point"
* such that the last word before it ended at or before fill. If there
* is no such point, we settle for the first legal wrap point.
*
* A "legal wrap point" is a white-space character that is not the last
* typed character and is not followed by white-space.
*
* If there is no legal wrap point or we found the last character of the
* line, we should return without wrapping.
*
* Note that the initial indentation does not count as a legal wrap
* point if we are going to auto-indent!
*
* Note that the code below could be optimised, by not calling strnlenpt()
* so often. */
/* If mouse not in edit or bottom window, return */
if (wenclose(edit, mevent.y, mevent.x)) {
#ifndef NANO_SMALL
if (ISSET(AUTOINDENT))
i = indent_length(inptr->data);
#endif
wrap_line = inptr->data + i;
for(; i < len; i++, wrap_line++) {
/* record where the last word ended */
if (*wrap_line != ' ' && *wrap_line != '\t')
word_back = i;
/* if we have found a "legal wrap point" and the current word
* extends too far, then we stop */
if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
break;
/* we record the latest "legal wrap point" */
if (i != current_x - 1 && word_back != i &&
wrap_line[1] != ' ' && wrap_line[1] != '\t')
wrap_loc = i;
}
if (wrap_loc < 0 || i == len)
return 0;
/* Don't let people screw with the marker when they're in a
* subfunction. */
if (currshortcut != main_list)
return;
/* Step 2, making the new wrap line. It will consist of indentation +
* after_break + " " + wrap_line (although indentation and wrap_line are
* conditional on flags and #defines). */
/* Subtract out size of topwin. Perhaps we need a constant
* somewhere? */
mevent.y -= 2;
/* after_break is the text that will be moved to the next line. */
after_break = inptr->data + wrap_loc + 1;
after_break_len = len - wrap_loc - 1;
assert(after_break_len == strlen(after_break));
/* Selecting where the cursor is sets the mark. Selecting
* beyond the line length with the cursor at the end of the line
* sets the mark as well. */
if ((mevent.y == current_y) &&
((mevent.x == current_x) || (current_x == strlen(current->data)
&& (mevent.x >
strlen(current->data))))) {
if (ISSET(VIEW_MODE)) {
print_view_warning();
return;
}
do_mark();
} else if (mevent.y > current_y) {
while (mevent.y > current_y) {
if (current->next != NULL)
current = current->next;
else
break;
current_y++;
}
} else if (mevent.y < current_y) {
while (mevent.y < current_y) {
if (current->prev != NULL)
current = current->prev;
else
break;
current_y--;
}
}
current_x = actual_x(current, mevent.x);
placewewant = current_x;
update_cursor();
edit_refresh();
} else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
int i, k;
/* new_line_len will later be increased by the lengths of indentation
* and wrap_line. */
new_line_len = after_break_len;
if (currshortcut == main_list)
currslen = MAIN_VISIBLE;
else
currslen = length_of_list(currshortcut);
/* We prepend the wrapped text to the next line, if the flag is set,
* and there is a next line, and prepending would not make the line
* too long. */
if (ISSET(SAMELINEWRAP) && inptr->next) {
wrap_line = inptr->next->data;
wrap_line_len = strlen(wrap_line);
if (currslen < 2)
k = COLS / 6;
else
k = COLS / ((currslen + (currslen %2)) / 2);
/* +1 for the space between after_break and wrap_line */
if ((new_line_len + 1 + wrap_line_len) <= fill) {
wrapping = 1;
new_line_len += (1 + wrap_line_len);
}
}
/* Determine what shortcut list was clicked */
mevent.y -= (editwinrows + 3);
#ifndef NANO_SMALL
if (ISSET(AUTOINDENT)) {
/* indentation comes from the next line if wrapping, else from
* this line */
indentation = (wrapping ? wrap_line : inptr->data);
indent_len = indent_length(indentation);
if (wrapping)
/* The wrap_line text should not duplicate indentation. Note
* in this case we need not increase new_line_len. */
wrap_line += indent_len;
else
new_line_len += indent_len;
}
#endif
if (mevent.y < 0) /* They clicked on the statusbar */
return;
/* Don't select stuff beyond list length */
if (mevent.x / k >= currslen)
return;
/* Now we allocate the new line and copy into it. */
newline = charalloc(new_line_len + 1); /* +1 for \0 */
*newline = '\0';
for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
s = s->next;
#ifndef NANO_SMALL
if (ISSET(AUTOINDENT)) {
strncpy(newline, indentation, indent_len);
newline[indent_len] = '\0';
/* And ungetch that value */
ungetch(s->val);
/* And if it's an alt-key sequence, we should probably send alt
too ;-) */
if (s->val >= 'a' && s->val <= 'z')
ungetch(27);
}
}
#endif
strcat(newline, after_break);
/* We end the old line at wrap_loc. Note this eats the space. */
null_at(&inptr->data, wrap_loc);
if (wrapping) {
/* In this case, totsize does not change. We ate a space in the
* null_at() above, but we add a space between after_break and
* wrap_line below. */
strcat(newline, " ");
strcat(newline, wrap_line);
free(inptr->next->data);
inptr->next->data = newline;
} else {
filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
/* In this case, the file size changes by -1 for the eaten
* space, +1 for the new line, and +indent_len for the new
* indentation. */
#ifndef NANO_SMALL
totsize += indent_len;
#endif
totlines++;
temp->data = newline;
temp->prev = inptr;
temp->next = inptr->next;
temp->prev->next = temp;
/* If !temp->next, then temp is the last line of the file, so we
* must set filebot */
if (temp->next)
temp->next->prev = temp;
else
filebot = temp;
}
/* Step 3, clean up. Here we reposition the cursor and mark, and do some
* other sundry things. */
/* The user typed a printable character; add it to the edit buffer. */
void do_char(char ch)
{
size_t current_len = strlen(current->data);
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
int refresh = 0;
/* Do we have to run edit_refresh(), or can we get away with
* update_line()? */
#endif
/* later wraps of this line will be prepended to the next line. */
SET(SAMELINEWRAP);
/* magic-line: when a character is inserted on the current magic line,
* it means we need a new one! */
if (filebot == current && current->data[0] == '\0') {
new_magicline();
fix_editbot();
}
/* Each line knows its line number. We recalculate these if we
* inserted a new line. */
if (!wrapping)
renumber(inptr);
/* more dangerousness fun =) */
current->data = nrealloc(current->data, current_len + 2);
assert(current_x <= current_len);
memmove(&current->data[current_x + 1],
&current->data[current_x],
current_len - current_x + 1);
current->data[current_x] = ch;
totsize++;
set_modified();
/* If the cursor was after the break point, we must move it. */
if (current_x > wrap_loc) {
current = current->next;
current_x -=
#ifndef NANO_SMALL
-indent_len +
/* note that current_x has not yet been incremented */
if (current == mark_beginbuf && current_x < mark_beginx)
mark_beginx++;
#endif
wrap_loc + 1;
wrap_reset();
placewewant = xplustabs();
}
#ifndef NANO_SMALL
/* If the mark was on this line after the wrap point, we move it down.
* If it was on the next line and we wrapped, we must move it
* right. */
if (mark_beginbuf == inptr && mark_beginx > wrap_loc) {
mark_beginbuf = inptr->next;
mark_beginx -= wrap_loc - indent_len + 1;
} else if (wrapping && mark_beginbuf == inptr->next)
mark_beginx += after_break_len;
#endif /* !NANO_SMALL */
do_right();
/* Place the cursor. */
reset_cursor();
#ifndef DISABLE_WRAPPING
if (!ISSET(NO_WRAP) && ch != '\t')
refresh = do_wrap(current);
#endif
return 1;
}
#endif /* !DISABLE_WRAPPING */
#ifdef ENABLE_COLOR
refresh = 1;
#endif
/* Stuff we do when we abort from programs and want to clean up the
* screen. This doesn't do much right now. */
void do_early_abort(void)
{
blank_statusbar_refresh();
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
if (refresh)
edit_refresh();
#endif
check_statblank();
UNSET(KEEP_CUTBUFFER);
}
int do_backspace(void)
......@@ -1116,841 +1163,774 @@ int do_delete(void)
return 1;
}
void wrap_reset(void)
int do_tab(void)
{
UNSET(SAMELINEWRAP);
do_char('\t');
return 1;
}
#ifndef DISABLE_SPELLER
int do_int_spell_fix(const char *word)
/* Someone hits return *gasp!* */
int do_enter(void)
{
char *save_search;
char *save_replace;
filestruct *begin;
int i = 0, j = 0, beginx, beginx_top, reverse_search_set;
#ifndef NANO_SMALL
int mark_set;
#endif
/* save where we are */
begin = current;
beginx = current_x + 1;
filestruct *newnode;
char *tmp;
/* Make sure Spell Check goes forward only */
reverse_search_set = ISSET(REVERSE_SEARCH);
UNSET(REVERSE_SEARCH);
newnode = make_new_node(current);
assert(current != NULL && current->data != NULL);
tmp = &current->data[current_x];
#ifndef NANO_SMALL
/* Make sure the marking highlight is off during Spell Check */
mark_set = ISSET(MARK_ISSET);
UNSET(MARK_ISSET);
#endif
/* save the current search/replace strings */
search_init_globals();
save_search = last_search;
save_replace = last_replace;
/* set search/replace strings to mis-spelt word */
last_search = mallocstrcpy(NULL, word);
last_replace = mallocstrcpy(NULL, word);
/* start from the top of file */
current = fileage;
current_x = beginx_top = -1;
search_last_line = FALSE;
edit_update(fileage, TOP);
while (1) {
/* make sure word is still mis-spelt (i.e. when multi-errors) */
if (findnextstr(TRUE, FALSE, fileage, beginx_top, word) != NULL) {
/* find whole words only */
if (!is_whole_word(current_x, current->data, word))
continue;
do_replace_highlight(TRUE, word);
/* allow replace word to be corrected */
i = statusq(0, spell_list, last_replace, _("Edit a replacement"));
do_replace_highlight(FALSE, word);
/* start from the start of this line again */
current = fileage;
current_x = beginx_top;
search_last_line = FALSE;
/* Do auto-indenting, like the neolithic Turbo Pascal editor. */
if (ISSET(AUTOINDENT)) {
int extra = 0;
const char *spc = current->data;
if (strcmp(word, answer)) {
j = i;
do_replace_loop(word, fileage, &beginx_top, TRUE, &j);
}
while (*spc == ' ' || *spc == '\t') {
extra++;
spc++;
}
break;
}
/* restore the search/replace strings */
free(last_search); last_search=save_search;
free(last_replace); last_replace=save_replace;
/* If current_x < extra, then we are breaking the line in the
* indentation. Autoindenting should add only current_x
* characters of indentation. */
if (current_x < extra)
extra = current_x;
else
current_x = extra;
totsize += extra;
/* restore where we were */
current = begin;
current_x = beginx - 1;
newnode->data = charalloc(strlen(tmp) + extra + 1);
strncpy(newnode->data, current->data, extra);
strcpy(&newnode->data[extra], tmp);
} else
#endif
{
current_x = 0;
newnode->data = charalloc(strlen(tmp) + 1);
strcpy(newnode->data, tmp);
}
*tmp = '\0';
/* restore Search/Replace direction */
if (reverse_search_set)
SET(REVERSE_SEARCH);
if (current->next == NULL) {
filebot = newnode;
editbot = newnode;
}
splice_node(current, newnode, current->next);
#ifndef NANO_SMALL
/* restore marking highlight */
if (mark_set)
SET(MARK_ISSET);
#endif
totsize++;
renumber(current);
current = newnode;
align(&current->data);
edit_update(current, CENTER);
/* The logic here is as follows:
* -> If we are at the bottom of the buffer, we want to recenter
* (read: rebuild) the screen and forcibly move the cursor.
* -> otherwise, we want simply to redraw the screen and update
* where we think the cursor is.
*/
if (current_y == editwinrows - 1) {
edit_update(current, CENTER);
reset_cursor();
} else {
current_y++;
edit_refresh();
update_cursor();
}
if (i == -1)
return FALSE;
totlines++;
set_modified();
return TRUE;
placewewant = xplustabs();
return 1;
}
/* Integrated spell checking using 'spell' program. */
int do_int_speller(char *tempfile_name)
#ifndef NANO_SMALL
int do_next_word(void)
{
char *read_buff, *read_buff_ptr, *read_buff_word;
size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
int in_fd[2], tempfile_fd, spell_status;
pid_t pid_spell;
/* Create a pipe to spell program */
if (pipe(in_fd) == -1)
return FALSE;
/* A new process to run spell in */
if ((pid_spell = fork()) == 0) {
filestruct *old = current;
/* Child continues, (i.e. future spell process) */
assert(current != NULL && current->data != NULL);
close(in_fd[0]);
/* Skip letters in this word first. */
while (current->data[current_x] != '\0' &&
isalnum((int)current->data[current_x]))
current_x++;
/* replace the standard in with the tempfile */
for (; current != NULL; current = current->next) {
while (current->data[current_x] != '\0' &&
!isalnum((int)current->data[current_x]))
current_x++;
if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1) {
close(in_fd[1]);
exit(1);
}
if (current->data[current_x] != '\0')
break;
if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) {
close(tempfile_fd);
close(in_fd[1]);
exit(1);
}
close(tempfile_fd);
current_x = 0;
}
if (current == NULL)
current = filebot;
/* send spell's standard out to the pipe */
placewewant = xplustabs();
if (dup2(in_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
close(in_fd[1]);
exit(1);
if (current->lineno >= editbot->lineno) {
/* If we're on the last line, don't center the screen. */
if (current->lineno == filebot->lineno)
edit_refresh();
else
edit_update(current, CENTER);
}
else {
/* If we've jumped lines, refresh the old line. We can't just
use current->prev here, because we may have skipped over some
blank lines, in which case the previous line is the wrong
one. */
if (current != old) {
update_line(old, 0);
/* If the mark was set, then the lines between old and
current have to be updated too. */
if (ISSET(MARK_ISSET)) {
while (old->next != current) {
old = old->next;
update_line(old, 0);
}
}
}
close(in_fd[1]);
/* Start spell program, we are using the PATH here!?!? */
execlp("spell", "spell", NULL);
/* Should not be reached, if spell is found!!! */
exit(1);
update_line(current, current_x);
}
return 0;
}
/* Parent continues here */
/* The same thing for backwards. */
int do_prev_word(void)
{
filestruct *old = current;
close(in_fd[1]);
assert(current != NULL);
/* Child process was not forked successfully */
/* Skip letters in this word first. */
while (current_x >= 0 && isalnum((int)current->data[current_x]))
current_x--;
if (pid_spell < 0) {
close(in_fd[0]);
return FALSE;
}
for (; current != NULL; current = current->prev) {
while (current_x >= 0 && !isalnum((int)current->data[current_x]))
current_x--;
/* Get system pipe buffer size */
if (current_x >= 0)
break;
if ((pipe_buff_size = fpathconf(in_fd[0], _PC_PIPE_BUF)) < 1) {
close(in_fd[0]);
return FALSE;
if (current->prev != NULL)
current_x = strlen(current->prev->data);
}
/* Read-in the returned spelling errors */
read_buff_read = 0;
read_buff_size = pipe_buff_size + 1;
read_buff = read_buff_ptr = charalloc(read_buff_size);
while ((bytesread = read(in_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
read_buff_read += bytesread;
read_buff_size += pipe_buff_size;
read_buff = read_buff_ptr = nrealloc(read_buff, read_buff_size);
read_buff_ptr += read_buff_read;
if (current != NULL) {
while (current_x > 0 && isalnum((int)current->data[current_x - 1]))
current_x--;
} else {
current = fileage;
current_x = 0;
}
*read_buff_ptr = (char) NULL;
close(in_fd[0]);
/* Process the spelling errors */
read_buff_word = read_buff_ptr = read_buff;
while (*read_buff_ptr) {
placewewant = xplustabs();
if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
*read_buff_ptr = (char) NULL;
if (read_buff_word != read_buff_ptr) {
if (!do_int_spell_fix(read_buff_word)) {
read_buff_word = read_buff_ptr;
break;
if (current->lineno <= edittop->lineno) {
/* If we're on the first line, don't center the screen. */
if (current->lineno == fileage->lineno)
edit_refresh();
else
edit_update(current, CENTER);
}
else {
/* If we've jumped lines, refresh the old line. We can't just
use current->prev here, because we may have skipped over some
blank lines, in which case the previous line is the wrong
one. */
if (current != old) {
update_line(old, 0);
/* If the mark was set, then the lines between old and
current have to be updated too. */
if (ISSET(MARK_ISSET)) {
while (old->prev != current) {
old = old->prev;
update_line(old, 0);
}
}
read_buff_word = read_buff_ptr + 1;
}
read_buff_ptr++;
update_line(current, current_x);
}
return 0;
}
#endif /* !NANO_SMALL */
/* special case where last word doesn't end with \n or \r */
if (read_buff_word != read_buff_ptr)
do_int_spell_fix(read_buff_word);
free(read_buff);
replace_abort();
/* Process end of spell process */
wait(&spell_status);
if (WIFEXITED(spell_status)) {
if (WEXITSTATUS(spell_status) != 0)
return FALSE;
} else
return FALSE;
int do_mark(void)
{
#ifdef NANO_SMALL
nano_disabled_msg();
#else
if (!ISSET(MARK_ISSET)) {
statusbar(_("Mark Set"));
SET(MARK_ISSET);
mark_beginbuf = current;
mark_beginx = current_x;
} else {
statusbar(_("Mark UNset"));
UNSET(MARK_ISSET);
edit_refresh();
}
#endif
return 1;
}
return TRUE;
void wrap_reset(void)
{
UNSET(SAMELINEWRAP);
}
/* External spell checking. */
int do_alt_speller(char *file_name)
#ifndef DISABLE_WRAPPING
/* We wrap the given line. Precondition: we assume the cursor has been
* moved forward since the last typed character. Return value:
* whether we wrapped. */
int do_wrap(filestruct *inptr)
{
int alt_spell_status, lineno_cur = current->lineno;
int x_cur = current_x, y_cur = current_y, pww_cur = placewewant;
pid_t pid_spell;
char *ptr;
static int arglen = 3;
static char **spellargs = (char **)NULL;
size_t len = strlen(inptr->data); /* length of the line we wrap */
int i = 0; /* generic loop variable */
int wrap_loc = -1; /* index of inptr->data where we wrap */
int word_back = -1;
#ifndef NANO_SMALL
int mark_set = ISSET(MARK_ISSET);
int mbb_lineno_cur = 0;
/* We're going to close the current file, and open the output of
the alternate spell command. The line that mark_beginbuf
points to will be freed, so we save the line number and restore
afterwards. */
if (mark_set) {
mbb_lineno_cur = mark_beginbuf->lineno;
UNSET(MARK_ISSET);
}
const char *indentation = NULL;
/* indentation to prepend to the new line */
int indent_len = 0; /* strlen(indentation) */
#endif
const char *after_break; /* text after the wrap point */
int after_break_len; /* strlen(after_break) */
int wrapping = 0; /* do we prepend to the next line? */
const char *wrap_line = NULL;
/* the next line, minus indentation */
int wrap_line_len = 0; /* strlen(wrap_line) */
char *newline = NULL; /* the line we create */
int new_line_len = 0; /* eventual length of newline */
endwin();
/* There are three steps. First, we decide where to wrap. Then, we
* create the new wrap line. Finally, we clean up. */
/* Set up an argument list to pass the execvp function */
if (spellargs == NULL) {
spellargs = nmalloc(arglen * sizeof(char *));
/* Step 1, finding where to wrap. We are going to replace a white-space
* character with a new-line. In this step, we set wrap_loc as the
* location of this replacement.
*
* Where should we break the line? We need the last "legal wrap point"
* such that the last word before it ended at or before fill. If there
* is no such point, we settle for the first legal wrap point.
*
* A "legal wrap point" is a white-space character that is not the last
* typed character and is not followed by white-space.
*
* If there is no legal wrap point or we found the last character of the
* line, we should return without wrapping.
*
* Note that the initial indentation does not count as a legal wrap
* point if we are going to auto-indent!
*
* Note that the code below could be optimised, by not calling strnlenpt()
* so often. */
spellargs[0] = strtok(alt_speller, " ");
while ((ptr = strtok(NULL, " ")) != NULL) {
arglen++;
spellargs = nrealloc(spellargs, arglen * sizeof(char *));
spellargs[arglen - 3] = ptr;
}
spellargs[arglen - 1] = NULL;
#ifndef NANO_SMALL
if (ISSET(AUTOINDENT))
i = indent_length(inptr->data);
#endif
wrap_line = inptr->data + i;
for(; i < len; i++, wrap_line++) {
/* record where the last word ended */
if (*wrap_line != ' ' && *wrap_line != '\t')
word_back = i;
/* if we have found a "legal wrap point" and the current word
* extends too far, then we stop */
if (wrap_loc != -1 && strnlenpt(inptr->data, word_back + 1) > fill)
break;
/* we record the latest "legal wrap point" */
if (i != current_x - 1 && word_back != i &&
wrap_line[1] != ' ' && wrap_line[1] != '\t')
wrap_loc = i;
}
spellargs[arglen - 2] = file_name;
/* Start a new process for the alternate speller */
if ((pid_spell = fork()) == 0) {
/* Start alternate spell program; we are using the PATH here!?!? */
execvp(spellargs[0], spellargs);
if (wrap_loc < 0 || i == len)
return 0;
/* Should not be reached, if alternate speller is found!!! */
exit(1);
}
/* Step 2, making the new wrap line. It will consist of indentation +
* after_break + " " + wrap_line (although indentation and wrap_line are
* conditional on flags and #defines). */
/* Could not fork?? */
if (pid_spell < 0)
return FALSE;
/* after_break is the text that will be moved to the next line. */
after_break = inptr->data + wrap_loc + 1;
after_break_len = len - wrap_loc - 1;
assert(after_break_len == strlen(after_break));
/* Wait for alternate speller to complete */
/* new_line_len will later be increased by the lengths of indentation
* and wrap_line. */
new_line_len = after_break_len;
wait(&alt_spell_status);
if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0)
return FALSE;
/* We prepend the wrapped text to the next line, if the flag is set,
* and there is a next line, and prepending would not make the line
* too long. */
if (ISSET(SAMELINEWRAP) && inptr->next) {
wrap_line = inptr->next->data;
wrap_line_len = strlen(wrap_line);
refresh();
free_filestruct(fileage);
global_init(1);
open_file(file_name, 0, 1);
/* +1 for the space between after_break and wrap_line */
if ((new_line_len + 1 + wrap_line_len) <= fill) {
wrapping = 1;
new_line_len += (1 + wrap_line_len);
}
}
#ifndef NANO_SMALL
if (mark_set) {
do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
mark_beginbuf = current;
mark_beginx = current_x;
/* In case the line got shorter, assign mark_beginx. */
SET(MARK_ISSET);
if (ISSET(AUTOINDENT)) {
/* indentation comes from the next line if wrapping, else from
* this line */
indentation = (wrapping ? wrap_line : inptr->data);
indent_len = indent_length(indentation);
if (wrapping)
/* The wrap_line text should not duplicate indentation. Note
* in this case we need not increase new_line_len. */
wrap_line += indent_len;
else
new_line_len += indent_len;
}
#endif
/* go back to the old position, mark the file as modified, and make
sure that the titlebar is refreshed */
do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
set_modified();
clearok(topwin, FALSE);
titlebar(NULL);
return TRUE;
}
#endif
int do_spell(void)
{
#ifdef DISABLE_SPELLER
nano_disabled_msg();
return (TRUE);
#else
char *temp;
int spell_res;
if ((temp = safe_tempnam(0, "nano.")) == NULL) {
statusbar(_("Could not create a temporary filename: %s"),
strerror(errno));
return 0;
}
/* Now we allocate the new line and copy into it. */
newline = charalloc(new_line_len + 1); /* +1 for \0 */
*newline = '\0';
if (write_file(temp, 1, 0, 0) == -1) {
statusbar(_("Spell checking failed: unable to write temp file!"));
free(temp);
return 0;
#ifndef NANO_SMALL
if (ISSET(AUTOINDENT)) {
strncpy(newline, indentation, indent_len);
newline[indent_len] = '\0';
}
#ifdef ENABLE_MULTIBUFFER
/* update the current open_files entry before spell-checking, in case
any problems occur */
add_open_file(1);
#endif
strcat(newline, after_break);
/* We end the old line at wrap_loc. Note this eats the space. */
null_at(&inptr->data, wrap_loc);
if (wrapping) {
/* In this case, totsize does not change. We ate a space in the
* null_at() above, but we add a space between after_break and
* wrap_line below. */
strcat(newline, " ");
strcat(newline, wrap_line);
free(inptr->next->data);
inptr->next->data = newline;
} else {
filestruct *temp = (filestruct *)nmalloc(sizeof(filestruct));
if (alt_speller)
spell_res = do_alt_speller(temp);
else
spell_res = do_int_speller(temp);
/* In this case, the file size changes by -1 for the eaten
* space, +1 for the new line, and +indent_len for the new
* indentation. */
#ifndef NANO_SMALL
totsize += indent_len;
#endif
totlines++;
temp->data = newline;
temp->prev = inptr;
temp->next = inptr->next;
temp->prev->next = temp;
/* If !temp->next, then temp is the last line of the file, so we
* must set filebot */
if (temp->next)
temp->next->prev = temp;
else
filebot = temp;
}
remove(temp);
/* Step 3, clean up. Here we reposition the cursor and mark, and do some
* other sundry things. */
if (spell_res)
statusbar(_("Finished checking spelling"));
else
statusbar(_("Spell checking failed"));
/* later wraps of this line will be prepended to the next line. */
SET(SAMELINEWRAP);
free(temp);
return spell_res;
/* Each line knows its line number. We recalculate these if we
* inserted a new line. */
if (!wrapping)
renumber(inptr);
/* If the cursor was after the break point, we must move it. */
if (current_x > wrap_loc) {
current = current->next;
current_x -=
#ifndef NANO_SMALL
-indent_len +
#endif
}
wrap_loc + 1;
wrap_reset();
placewewant = xplustabs();
}
#ifndef NANO_SMALL
static int pid; /* This is the PID of the newly forked process
* below. It must be global since the signal
* handler needs it. */
/* If the mark was on this line after the wrap point, we move it down.
* If it was on the next line and we wrapped, we must move it
* right. */
if (mark_beginbuf == inptr && mark_beginx > wrap_loc) {
mark_beginbuf = inptr->next;
mark_beginx -= wrap_loc - indent_len + 1;
} else if (wrapping && mark_beginbuf == inptr->next)
mark_beginx += after_break_len;
#endif /* !NANO_SMALL */
RETSIGTYPE cancel_fork(int signal)
{
if (kill(pid, SIGKILL)==-1) nperror("kill");
/* Place the cursor. */
reset_cursor();
return 1;
}
#endif /* !DISABLE_WRAPPING */
int open_pipe(const char *command)
#ifndef DISABLE_SPELLER
int do_int_spell_fix(const char *word)
{
int fd[2];
FILE *f;
struct sigaction oldaction, newaction;
/* original and temporary handlers for SIGINT */
#ifdef _POSIX_VDISABLE
struct termios term, newterm;
#endif /* _POSIX_VDISABLE */
int cancel_sigs = 0;
/* cancel_sigs==1 means that sigaction failed without changing the
* signal handlers. cancel_sigs==2 means the signal handler was
* changed, but the tcsetattr didn't succeed.
* I use this variable since it is important to put things back when
* we finish, even if we get errors. */
/* Make our pipes. */
char *save_search;
char *save_replace;
filestruct *begin;
int i = 0, j = 0, beginx, beginx_top, reverse_search_set;
#ifndef NANO_SMALL
int mark_set;
#endif
if (pipe(fd) == -1) {
statusbar(_("Could not pipe"));
return 1;
}
/* save where we are */
begin = current;
beginx = current_x + 1;
/* Fork a child */
/* Make sure Spell Check goes forward only */
reverse_search_set = ISSET(REVERSE_SEARCH);
UNSET(REVERSE_SEARCH);
if ((pid = fork()) == 0) {
close(fd[0]);
dup2(fd[1], fileno(stdout));
dup2(fd[1], fileno(stderr));
/* If execl() returns at all, there was an error. */
execl("/bin/sh","sh","-c",command,0);
exit(0);
}
#ifndef NANO_SMALL
/* Make sure the marking highlight is off during Spell Check */
mark_set = ISSET(MARK_ISSET);
UNSET(MARK_ISSET);
#endif
/* Else continue as parent */
/* save the current search/replace strings */
search_init_globals();
save_search = last_search;
save_replace = last_replace;
close(fd[1]);
/* set search/replace strings to mis-spelt word */
last_search = mallocstrcpy(NULL, word);
last_replace = mallocstrcpy(NULL, word);
if (pid == -1) {
close(fd[0]);
statusbar(_("Could not fork"));
return 1;
}
/* start from the top of file */
current = fileage;
current_x = beginx_top = -1;
/* before we start reading the forked command's output, we set
* things up so that ^C will cancel the new process */
if (sigaction(SIGINT, NULL, &newaction)==-1) {
cancel_sigs = 1;
nperror("sigaction");
} else {
newaction.sa_handler = cancel_fork;
if (sigaction(SIGINT, &newaction, &oldaction)==-1) {
cancel_sigs = 1;
nperror("sigaction");
}
}
/* note that now oldaction is the previous SIGINT signal handler, to
* be restored later */
search_last_line = FALSE;
/* if the platform supports disabling individual control characters */
#ifdef _POSIX_VDISABLE
if (!cancel_sigs && tcgetattr(0, &term) == -1) {
cancel_sigs = 2;
nperror("tcgetattr");
}
if (!cancel_sigs) {
newterm = term;
/* Grab oldterm's VINTR key :-) */
newterm.c_cc[VINTR] = oldterm.c_cc[VINTR];
if (tcsetattr(0, TCSANOW, &newterm) == -1) {
cancel_sigs = 2;
nperror("tcsetattr");
}
}
#endif /* _POSIX_VDISABLE */
edit_update(fileage, TOP);
f = fdopen(fd[0], "rb");
if (!f)
nperror("fdopen");
read_file(f, "stdin", 0);
set_modified();
while (1) {
/* make sure word is still mis-spelt (i.e. when multi-errors) */
if (findnextstr(TRUE, FALSE, fileage, beginx_top, word) != NULL) {
if (wait(NULL) == -1)
nperror("wait");
/* find whole words only */
if (!is_whole_word(current_x, current->data, word))
continue;
#ifdef _POSIX_VDISABLE
if (!cancel_sigs && tcsetattr(0, TCSANOW, &term) == -1)
nperror("tcsetattr");
#endif /* _POSIX_VDISABLE */
do_replace_highlight(TRUE, word);
if (cancel_sigs!=1 && sigaction(SIGINT, &oldaction, NULL) == -1)
nperror("sigaction");
/* allow replace word to be corrected */
i = statusq(0, spell_list, last_replace, _("Edit a replacement"));
return 0;
}
#endif /* NANO_SMALL */
do_replace_highlight(FALSE, word);
int do_exit(void)
{
int i;
/* start from the start of this line again */
current = fileage;
current_x = beginx_top;
if (!ISSET(MODIFIED)) {
search_last_line = FALSE;
#ifdef ENABLE_MULTIBUFFER
if (!close_open_file()) {
display_main_list();
return 1;
if (strcmp(word, answer)) {
j = i;
do_replace_loop(word, fileage, &beginx_top, TRUE, &j);
}
}
else
#endif
finish(0);
break;
}
if (ISSET(TEMP_OPT)) {
i = 1;
} else {
i = do_yesno(0, 0,
_
("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
}
/* restore the search/replace strings */
free(last_search); last_search=save_search;
free(last_replace); last_replace=save_replace;
#ifdef DEBUG
dump_buffer(fileage);
#endif
/* restore where we were */
current = begin;
current_x = beginx - 1;
if (i == 1) {
if (do_writeout(filename, 1, 0) > 0) {
/* restore Search/Replace direction */
if (reverse_search_set)
SET(REVERSE_SEARCH);
#ifdef ENABLE_MULTIBUFFER
if (!close_open_file()) {
display_main_list();
return 1;
}
else
#ifndef NANO_SMALL
/* restore marking highlight */
if (mark_set)
SET(MARK_ISSET);
#endif
finish(0);
}
} else if (i == 0) {
#ifdef ENABLE_MULTIBUFFER
if (!close_open_file()) {
display_main_list();
return 1;
}
else
#endif
finish(0);
} else
statusbar(_("Cancelled"));
edit_update(current, CENTER);
display_main_list();
return 1;
if (i == -1)
return FALSE;
return TRUE;
}
#ifndef DISABLE_MOUSE
#ifdef NCURSES_MOUSE_VERSION
void do_mouse(void)
/* Integrated spell checking using 'spell' program. */
int do_int_speller(char *tempfile_name)
{
MEVENT mevent;
int currslen;
const shortcut *s = currshortcut;
char *read_buff, *read_buff_ptr, *read_buff_word;
size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
int in_fd[2], tempfile_fd, spell_status;
pid_t pid_spell;
if (getmouse(&mevent) == ERR)
return;
/* Create a pipe to spell program */
/* If mouse not in edit or bottom window, return */
if (wenclose(edit, mevent.y, mevent.x)) {
if (pipe(in_fd) == -1)
return FALSE;
/* Don't let people screw with the marker when they're in a
* subfunction. */
if (currshortcut != main_list)
return;
/* A new process to run spell in */
/* Subtract out size of topwin. Perhaps we need a constant
* somewhere? */
mevent.y -= 2;
if ((pid_spell = fork()) == 0) {
/* Selecting where the cursor is sets the mark. Selecting
* beyond the line length with the cursor at the end of the line
* sets the mark as well. */
if ((mevent.y == current_y) &&
((mevent.x == current_x) || (current_x == strlen(current->data)
&& (mevent.x >
strlen(current->data))))) {
if (ISSET(VIEW_MODE)) {
print_view_warning();
return;
}
do_mark();
} else if (mevent.y > current_y) {
while (mevent.y > current_y) {
if (current->next != NULL)
current = current->next;
else
break;
current_y++;
}
} else if (mevent.y < current_y) {
while (mevent.y < current_y) {
if (current->prev != NULL)
current = current->prev;
else
break;
current_y--;
}
}
current_x = actual_x(current, mevent.x);
placewewant = current_x;
update_cursor();
edit_refresh();
} else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) {
int i, k;
/* Child continues, (i.e. future spell process) */
if (currshortcut == main_list)
currslen = MAIN_VISIBLE;
else
currslen = length_of_list(currshortcut);
close(in_fd[0]);
if (currslen < 2)
k = COLS / 6;
else
k = COLS / ((currslen + (currslen %2)) / 2);
/* replace the standard in with the tempfile */
/* Determine what shortcut list was clicked */
mevent.y -= (editwinrows + 3);
if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1) {
close(in_fd[1]);
exit(1);
}
if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) {
close(tempfile_fd);
close(in_fd[1]);
exit(1);
}
close(tempfile_fd);
if (mevent.y < 0) /* They clicked on the statusbar */
return;
/* send spell's standard out to the pipe */
/* Don't select stuff beyond list length */
if (mevent.x / k >= currslen)
return;
if (dup2(in_fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
close(in_fd[1]);
exit(1);
}
close(in_fd[1]);
for (i = 0; i < (mevent.x / k) * 2 + mevent.y; i++)
s = s->next;
/* Start spell program, we are using the PATH here!?!? */
execlp("spell", "spell", NULL);
/* And ungetch that value */
ungetch(s->val);
/* Should not be reached, if spell is found!!! */
/* And if it's an alt-key sequence, we should probably send alt
too ;-) */
if (s->val >= 'a' && s->val <= 'z')
ungetch(27);
exit(1);
}
}
#endif
#endif
/* Handler for SIGHUP */
RETSIGTYPE handle_hup(int signal)
{
die(_("Received SIGHUP"));
}
/* Parent continues here */
/* What do we do when we catch the suspend signal */
RETSIGTYPE do_suspend(int signal)
{
endwin();
printf("\n\n\n\n\nUse \"fg\" to return to nano\n");
fflush(stdout);
close(in_fd[1]);
/* Restore the terminal settings for the disabled keys */
tcsetattr(0, TCSANOW, &oldterm);
/* Child process was not forked successfully */
/* We used to re-enable the default SIG_DFL and raise SIGTSTP, but
then we could be (and were) interrupted in the middle of the call.
So we do it the mutt way instead */
kill(0, SIGSTOP);
}
if (pid_spell < 0) {
close(in_fd[0]);
return FALSE;
}
/* Restore the suspend handler when we come back into the prog */
RETSIGTYPE do_cont(int signal)
{
/* Now we just update the screen instead of having to reenable the
SIGTSTP handler. */
/* Get system pipe buffer size */
doupdate();
/* The Hurd seems to need this, otherwise a ^Y after a ^Z will
start suspending again. */
signal_init();
if ((pipe_buff_size = fpathconf(in_fd[0], _PC_PIPE_BUF)) < 1) {
close(in_fd[0]);
return FALSE;
}
#ifndef NANO_SMALL
/* Perhaps the user resized the window while we slept. */
handle_sigwinch(0);
#endif
}
/* Read-in the returned spelling errors */
#ifndef NANO_SMALL
void handle_sigwinch(int s)
{
const char *tty = ttyname(0);
int fd;
int result = 0;
struct winsize win;
read_buff_read = 0;
read_buff_size = pipe_buff_size + 1;
read_buff = read_buff_ptr = charalloc(read_buff_size);
if (!tty)
return;
fd = open(tty, O_RDWR);
if (fd == -1)
return;
result = ioctl(fd, TIOCGWINSZ, &win);
close(fd);
if (result == -1)
return;
while ((bytesread = read(in_fd[0], read_buff_ptr, pipe_buff_size)) > 0) {
read_buff_read += bytesread;
read_buff_size += pipe_buff_size;
read_buff = read_buff_ptr = nrealloc(read_buff, read_buff_size);
read_buff_ptr += read_buff_read;
}
/* Could check whether the COLS or LINES changed, and return
* otherwise. EXCEPT, that COLS and LINES are ncurses global
* variables, and in some cases ncurses has already updated them.
* But not in all cases, argh. */
COLS = win.ws_col;
LINES = win.ws_row;
if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
die_too_small();
*read_buff_ptr = (char) NULL;
close(in_fd[0]);
#ifndef DISABLE_WRAPJUSTIFY
fill = wrap_at;
if (fill <= 0)
fill += COLS;
if (fill < MIN_FILL_LENGTH)
die_too_small();
#endif
/* Process the spelling errors */
hblank = nrealloc(hblank, COLS + 1);
memset(hblank, ' ', COLS);
hblank[COLS] = '\0';
read_buff_word = read_buff_ptr = read_buff;
#ifdef HAVE_RESIZETERM
resizeterm(LINES, COLS);
#ifdef HAVE_WRESIZE
if (wresize(topwin, 2, COLS) == ERR)
die(_("Cannot resize top win"));
if (mvwin(topwin, 0, 0) == ERR)
die(_("Cannot move top win"));
if (wresize(edit, editwinrows, COLS) == ERR)
die(_("Cannot resize edit win"));
if (mvwin(edit, 2, 0) == ERR)
die(_("Cannot move edit win"));
if (wresize(bottomwin, 3 - no_help(), COLS) == ERR)
die(_("Cannot resize bottom win"));
if (mvwin(bottomwin, LINES - 3 + no_help(), 0) == ERR)
die(_("Cannot move bottom win"));
#endif /* HAVE_WRESIZE */
#endif /* HAVE_RESIZETERM */
while (*read_buff_ptr) {
fix_editbot();
if ((*read_buff_ptr == '\n') || (*read_buff_ptr == '\r')) {
*read_buff_ptr = (char) NULL;
if (read_buff_word != read_buff_ptr) {
if (!do_int_spell_fix(read_buff_word)) {
read_buff_word = read_buff_ptr;
break;
}
}
read_buff_word = read_buff_ptr + 1;
}
read_buff_ptr++;
}
if (current_y > editwinrows - 1)
edit_update(editbot, CENTER);
erase();
/* special case where last word doesn't end with \n or \r */
if (read_buff_word != read_buff_ptr)
do_int_spell_fix(read_buff_word);
/* Do these b/c width may have changed... */
refresh();
titlebar(NULL);
edit_refresh();
display_main_list();
blank_statusbar();
total_refresh();
free(read_buff);
replace_abort();
/* Turn cursor back on for sure */
curs_set(1);
/* Process end of spell process */
/* Jump back to main loop */
siglongjmp(jmpbuf, 1);
wait(&spell_status);
if (WIFEXITED(spell_status)) {
if (WEXITSTATUS(spell_status) != 0)
return FALSE;
} else
return FALSE;
return TRUE;
}
#endif
void signal_init(void)
/* External spell checking. */
int do_alt_speller(char *tempfile_name)
{
#ifdef _POSIX_VDISABLE
struct termios term;
int alt_spell_status, lineno_cur = current->lineno;
int x_cur = current_x, y_cur = current_y, pww_cur = placewewant;
pid_t pid_spell;
char *ptr;
static int arglen = 3;
static char **spellargs = (char **)NULL;
#ifndef NANO_SMALL
int mark_set = ISSET(MARK_ISSET);
int mbb_lineno_cur = 0;
/* We're going to close the current file, and open the output of
the alternate spell command. The line that mark_beginbuf
points to will be freed, so we save the line number and restore
afterwards. */
if (mark_set) {
mbb_lineno_cur = mark_beginbuf->lineno;
UNSET(MARK_ISSET);
}
#endif
/* Trap SIGINT and SIGQUIT cuz we want them to do useful things. */
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = SIG_IGN;
sigaction(SIGINT, &act, NULL);
endwin();
/* Trap SIGHUP cuz we want to write the file out. */
act.sa_handler = handle_hup;
sigaction(SIGHUP, &act, NULL);
/* Set up an argument list to pass the execvp function */
if (spellargs == NULL) {
spellargs = nmalloc(arglen * sizeof(char *));
#ifndef NANO_SMALL
act.sa_handler = handle_sigwinch;
sigaction(SIGWINCH, &act, NULL);
#endif
spellargs[0] = strtok(alt_speller, " ");
while ((ptr = strtok(NULL, " ")) != NULL) {
arglen++;
spellargs = nrealloc(spellargs, arglen * sizeof(char *));
spellargs[arglen - 3] = ptr;
}
spellargs[arglen - 1] = NULL;
}
spellargs[arglen - 2] = tempfile_name;
#ifdef _POSIX_VDISABLE
tcgetattr(0, &term);
/* Start a new process for the alternate speller */
if ((pid_spell = fork()) == 0) {
/* Start alternate spell program; we are using the PATH here!?!? */
execvp(spellargs[0], spellargs);
#ifdef VDSUSP
term.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif /* VDSUSP */
/* Should not be reached, if alternate speller is found!!! */
exit(1);
}
#endif /* _POSIX_VDISABLE */
/* Could not fork?? */
if (pid_spell < 0)
return FALSE;
/* Wait for alternate speller to complete */
wait(&alt_spell_status);
if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0)
return FALSE;
refresh();
free_filestruct(fileage);
global_init(1);
open_file(tempfile_name, 0, 1);
#ifndef NANO_SMALL
if (mark_set) {
do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
mark_beginbuf = current;
mark_beginx = current_x;
/* In case the line got shorter, assign mark_beginx. */
SET(MARK_ISSET);
}
#endif
if (!ISSET(SUSPEND)) {
/* go back to the old position, mark the file as modified, and make
sure that the titlebar is refreshed */
do_gotopos(lineno_cur, x_cur, y_cur, pww_cur);
set_modified();
clearok(topwin, FALSE);
titlebar(NULL);
/* Insane! */
#ifdef _POSIX_VDISABLE
term.c_cc[VSUSP] = _POSIX_VDISABLE;
#else
act.sa_handler = SIG_IGN;
sigaction(SIGTSTP, &act, NULL);
return TRUE;
}
#endif
} else {
/* If we don't do this, it seems other stuff interrupts the
suspend handler! Try using nano with mutt without this
line. */
sigfillset(&act.sa_mask);
int do_spell(void)
{
#ifdef DISABLE_SPELLER
nano_disabled_msg();
return (TRUE);
#else
char *temp;
int spell_res;
act.sa_handler = do_suspend;
sigaction(SIGTSTP, &act, NULL);
if ((temp = safe_tempnam(0, "nano.")) == NULL) {
statusbar(_("Could not create a temporary filename: %s"),
strerror(errno));
return 0;
}
act.sa_handler = do_cont;
sigaction(SIGCONT, &act, NULL);
if (write_file(temp, 1, 0, 0) == -1) {
statusbar(_("Spell checking failed: unable to write temp file!"));
free(temp);
return 0;
}
#ifdef _POSIX_VDISABLE
tcsetattr(0, TCSANOW, &term);
#ifdef ENABLE_MULTIBUFFER
/* update the current open_files entry before spell-checking, in case
any problems occur */
add_open_file(1);
#endif
}
void window_init(void)
{
if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
die_too_small();
/* Set up the main text window */
edit = newwin(editwinrows, COLS, 2, 0);
if (alt_speller)
spell_res = do_alt_speller(temp);
else
spell_res = do_int_speller(temp);
/* And the other windows */
topwin = newwin(2, COLS, 0, 0);
bottomwin = newwin(3 - no_help(), COLS, LINES - 3 + no_help(), 0);
remove(temp);
#ifdef PDCURSES
/* Oops, I guess we need this again.
Moved here so the keypad still works after a Meta-X, for example */
keypad(edit, TRUE);
keypad(bottomwin, TRUE);
#endif
}
if (spell_res)
statusbar(_("Finished checking spelling"));
else
statusbar(_("Spell checking failed"));
void mouse_init(void)
{
#ifndef DISABLE_MOUSE
#ifdef NCURSES_MOUSE_VERSION
if (ISSET(USE_MOUSE)) {
keypad_on(edit, 1);
keypad_on(bottomwin, 1);
free(temp);
return spell_res;
mousemask(BUTTON1_RELEASED, NULL);
mouseinterval(50);
} else
mousemask(0, NULL);
#endif
#endif
}
int do_tab(void)
{
do_char('\t');
return 1;
}
#if !defined(DISABLE_WRAPPING) && !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
/* The "indentation" of a line is the white-space between the quote part
* and the non-white-space of the line. */
size_t indent_length(const char *line) {
size_t indent_length(const char *line)
{
size_t len = 0;
assert(line != NULL);
......@@ -2067,17 +2047,11 @@ size_t quote_length(const char *line)
}
#endif /* !HAVE_REGEX_H */
#ifdef HAVE_REGEX_H
# define IFREG(a, b) a, b
#else
# define IFREG(a, b) a
#endif
/* a_line and b_line are lines of text. The quotation part of a_line is
* the first a_quote characters. Check that the quotation part of
* b_line is the same. */
int quotes_match(const char *a_line, size_t a_quote,
IFREG(const char *b_line, const regex_t *qreg))
IFREG(const char *b_line, const regex_t *qreg))
{
/* Here is the assumption about a_quote: */
assert(a_quote == quote_length(IFREG(a_line, qreg)));
......@@ -2088,7 +2062,7 @@ int quotes_match(const char *a_line, size_t 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)
const char *b_line, size_t b_indent)
{
assert(a_indent == indent_length(a_line));
assert(b_indent == indent_length(b_line));
......@@ -2101,7 +2075,7 @@ size_t indents_match(const char *a_line, size_t a_indent,
* copies of the lines in place, too. We return the new copy of
* first_line. */
filestruct *backup_lines(filestruct *first_line, size_t par_len,
size_t quote_len)
size_t quote_len)
{
/* We put the original lines, not copies, into the cut buffer, just
* out of a misguided sense of consistency, so if you un-cut, you
......@@ -2515,7 +2489,7 @@ int do_justify(void)
reset_cursor();
/* Now get a keystroke and see if it's unjustify; if not, unget the
* keystroke and return */
* keystroke and return. */
#ifndef DISABLE_MOUSE
#ifdef NCURSES_MOUSE_VERSION
......@@ -2585,207 +2559,249 @@ int do_justify(void)
#endif
}
#ifndef DISABLE_HELP
/* This function allocates help_text, and stores the help string in it.
* help_text should be NULL initially. */
void help_init(void)
int do_exit(void)
{
size_t allocsize = 1; /* space needed for help_text */
char *ptr = NULL;
#ifndef NANO_SMALL
const toggle *t;
int i;
if (!ISSET(MODIFIED)) {
#ifdef ENABLE_MULTIBUFFER
if (!close_open_file()) {
display_main_list();
return 1;
}
else
#endif
const shortcut *s;
finish(0);
}
/* First set up the initial help text for the current function */
if (currshortcut == whereis_list || currshortcut == replace_list
|| currshortcut == replace_list_2)
ptr = _("Search Command Help Text\n\n "
"Enter the words or characters you would like to search "
"for, then hit enter. If there is a match for the text you "
"entered, the screen will be updated to the location of the "
"nearest match for the search string.\n\n "
"If using Pico Mode via the -p or --pico flags, the "
"Meta-P toggle, or a nanorc file, the previous search "
"string will be shown in brackets after the Search: prompt. "
"Hitting Enter without entering any text will perform the "
"previous search. Otherwise, the previous string will be "
"placed before the cursor, and can be edited or deleted "
"before hitting enter.\n\n The following function keys are "
"available in Search mode:\n\n");
else if (currshortcut == goto_list)
ptr = _("Go To Line Help Text\n\n "
"Enter the line number that you wish to go to and hit "
"Enter. If there are fewer lines of text than the "
"number you entered, you will be brought to the last line "
"of the file.\n\n The following function keys are "
"available in Go To Line mode:\n\n");
else if (currshortcut == insertfile_list)
ptr = _("Insert File Help Text\n\n "
"Type in the name of a file to be inserted into the current "
"file buffer at the current cursor location.\n\n "
"If you have compiled nano with multiple file buffer "
"support, and enable multiple buffers with the -F "
"or --multibuffer command line flags, the Meta-F toggle, or "
"a nanorc file, inserting a file will cause it to be "
"loaded into a separate buffer (use Meta-< and > to switch "
"between file buffers).\n\n If you need another blank "
"buffer, do not enter any filename, or type in a "
"nonexistent filename at the prompt and press "
"Enter.\n\n The following function keys are "
"available in Insert File mode:\n\n");
else if (currshortcut == writefile_list)
ptr = _("Write File Help Text\n\n "
"Type the name that you wish to save the current file "
"as and hit Enter to save the file.\n\n If you have "
"selected text with Ctrl-^, you will be prompted to "
"save only the selected portion to a separate file. To "
"reduce the chance of overwriting the current file with "
"just a portion of it, the current filename is not the "
"default in this mode.\n\n The following function keys "
"are available in Write File mode:\n\n");
#ifndef DISABLE_BROWSER
else if (currshortcut == browser_list)
ptr = _("File Browser Help Text\n\n "
"The file browser is used to visually browse the "
"directory structure to select a file for reading "
"or writing. You may use the arrow keys or Page Up/"
"Down to browse through the files, and S or Enter to "
"choose the selected file or enter the selected "
"directory. To move up one level, select the directory "
"called \"..\" at the top of the file list.\n\n The "
"following function keys are available in the file "
"browser:\n\n");
else if (currshortcut == gotodir_list)
ptr = _("Browser Go To Directory Help Text\n\n "
"Enter the name of the directory you would like to "
"browse to.\n\n If tab completion has not been disabled, "
"you can use the TAB key to (attempt to) automatically "
"complete the directory name.\n\n The following function "
"keys are available in Browser Go To Directory mode:\n\n");
if (ISSET(TEMP_OPT)) {
i = 1;
} else {
i = do_yesno(0, 0,
_
("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES) ? "));
}
#ifdef DEBUG
dump_buffer(fileage);
#endif
else if (currshortcut == spell_list)
ptr = _("Spell Check Help Text\n\n "
"The spell checker checks the spelling of all text "
"in the current file. When an unknown word is "
"encountered, it is highlighted and a replacement can "
"be edited. It will then prompt to replace every "
"instance of the given misspelled word in the "
"current file.\n\n The following other functions are "
"available in Spell Check mode:\n\n");
if (i == 1) {
if (do_writeout(filename, 1, 0) > 0) {
#ifdef ENABLE_MULTIBUFFER
if (!close_open_file()) {
display_main_list();
return 1;
}
else
#endif
finish(0);
}
} else if (i == 0) {
#ifdef ENABLE_MULTIBUFFER
if (!close_open_file()) {
display_main_list();
return 1;
}
else
#endif
finish(0);
} else
statusbar(_("Cancelled"));
display_main_list();
return 1;
}
void signal_init(void)
{
#ifdef _POSIX_VDISABLE
struct termios term;
#endif
/* Trap SIGINT and SIGQUIT cuz we want them to do useful things. */
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = SIG_IGN;
sigaction(SIGINT, &act, NULL);
/* Trap SIGHUP cuz we want to write the file out. */
act.sa_handler = handle_hup;
sigaction(SIGHUP, &act, NULL);
#ifndef NANO_SMALL
else if (currshortcut == extcmd_list)
ptr = _("External Command Help Text\n\n "
"This menu allows you to insert the output of a command "
"run by the shell into the current buffer (or a new "
"buffer in multibuffer mode).\n\n The following keys are "
"available in this mode:\n\n");
act.sa_handler = handle_sigwinch;
sigaction(SIGWINCH, &act, NULL);
#endif
else /* Default to the main help list */
ptr = _(" nano help text\n\n "
"The nano editor is designed to emulate the functionality and "
"ease-of-use of the UW Pico text editor. There are four main "
"sections of the editor: The top line shows the program "
"version, the current filename being edited, and whether "
"or not the file has been modified. Next is the main editor "
"window showing the file being edited. The status line is "
"the third line from the bottom and shows important messages. "
"The bottom two lines show the most commonly used shortcuts "
"in the editor.\n\n "
"The notation for shortcuts is as follows: Control-key "
"sequences are notated with a caret (^) symbol and are entered "
"with the Control (Ctrl) key. Escape-key sequences are notated "
"with the Meta (M) symbol and can be entered using either the "
"Esc, Alt or Meta key depending on your keyboard setup. The "
"following keystrokes are available in the main editor window. "
"Alternative keys are shown in parentheses:\n\n");
allocsize += strlen(ptr);
#ifdef _POSIX_VDISABLE
tcgetattr(0, &term);
/* The space needed for the shortcut lists, at most COLS characters,
* plus '\n'. */
allocsize += (COLS + 1) * length_of_list(currshortcut);
#ifdef VDSUSP
term.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif /* VDSUSP */
#ifndef NANO_SMALL
/* If we're on the main list, we also count the toggle help text.
* Each line has "M-%c\t\t\t", which fills 24 columns, plus at most
* COLS - 24 characters, plus '\n'.*/
if (currshortcut == main_list)
for (t = toggles; t != NULL; t = t->next)
allocsize += COLS - 17;
#endif /* !NANO_SMALL */
#endif /* _POSIX_VDISABLE */
/* help_text has been freed and set to NULL unless the user resized
* while in the help screen. */
free(help_text);
if (!ISSET(SUSPEND)) {
/* Allocate space for the help text */
help_text = charalloc(allocsize);
/* Insane! */
#ifdef _POSIX_VDISABLE
term.c_cc[VSUSP] = _POSIX_VDISABLE;
#else
act.sa_handler = SIG_IGN;
sigaction(SIGTSTP, &act, NULL);
#endif
/* Now add the text we want */
strcpy(help_text, ptr);
ptr = help_text + strlen(help_text);
} else {
/* If we don't do this, it seems other stuff interrupts the
suspend handler! Try using nano with mutt without this
line. */
sigfillset(&act.sa_mask);
/* Now add our shortcut info */
for (s = currshortcut; s != NULL; s = s->next) {
/* true if the character in s->altval is shown in first column */
int meta_shortcut = 0;
act.sa_handler = do_suspend;
sigaction(SIGTSTP, &act, NULL);
if (s->val > 0 && s->val < 32)
ptr += sprintf(ptr, "^%c", s->val + 64);
#ifndef NANO_SMALL
else if (s->val == NANO_CONTROL_SPACE)
ptr += sprintf(ptr, "^%.6s", _("Space"));
else if (s->altval == NANO_ALT_SPACE) {
meta_shortcut = 1;
ptr += sprintf(ptr, "M-%.5s", _("Space"));
}
act.sa_handler = do_cont;
sigaction(SIGCONT, &act, NULL);
}
#ifdef _POSIX_VDISABLE
tcsetattr(0, TCSANOW, &term);
#endif
else if (s->altval > 0) {
meta_shortcut = 1;
ptr += sprintf(ptr, "M-%c", s->altval -
(('A' <= s->altval && s->altval <= 'Z') ||
'a' <= s->altval ? 32 : 0));
}
/* Hack */
else if (s->val >= 'a') {
meta_shortcut = 1;
ptr += sprintf(ptr, "M-%c", s->val - 32);
}
}
*(ptr++) = '\t';
/* Handler for SIGHUP */
RETSIGTYPE handle_hup(int signal)
{
die(_("Received SIGHUP"));
}
if (s->misc1 > KEY_F0 && s->misc1 <= KEY_F(64))
ptr += sprintf(ptr, "(F%d)", s->misc1 - KEY_F0);
/* What do we do when we catch the suspend signal */
RETSIGTYPE do_suspend(int signal)
{
endwin();
printf("\n\n\n\n\nUse \"fg\" to return to nano\n");
fflush(stdout);
*(ptr++) = '\t';
/* Restore the terminal settings for the disabled keys */
tcsetattr(0, TCSANOW, &oldterm);
if (!meta_shortcut && s->altval > 0)
ptr += sprintf(ptr, "(M-%c)", s->altval -
(('A' <= s->altval && s->altval <= 'Z') || 'a' <= s->altval
? 32 : 0));
/* We used to re-enable the default SIG_DFL and raise SIGTSTP, but
then we could be (and were) interrupted in the middle of the call.
So we do it the mutt way instead */
kill(0, SIGSTOP);
}
*(ptr++) = '\t';
/* Restore the suspend handler when we come back into the prog */
RETSIGTYPE do_cont(int signal)
{
/* Now we just update the screen instead of having to reenable the
SIGTSTP handler. */
assert(s->help != NULL);
ptr += sprintf(ptr, "%.*s\n", COLS - 24, s->help);
}
doupdate();
/* The Hurd seems to need this, otherwise a ^Y after a ^Z will
start suspending again. */
signal_init();
#ifndef NANO_SMALL
/* And the toggles... */
if (currshortcut == main_list)
for (t = toggles; t != NULL; t = t->next) {
ptr += sprintf(ptr, "M-%c\t\t\t", t->val - 32);
assert(t->desc != NULL);
ptr += sprintf(ptr, _("%.*s enable/disable\n"), COLS - 24, t->desc);
}
/* Perhaps the user resized the window while we slept. */
handle_sigwinch(0);
#endif
}
#ifndef NANO_SMALL
void handle_sigwinch(int s)
{
const char *tty = ttyname(0);
int fd;
int result = 0;
struct winsize win;
if (!tty)
return;
fd = open(tty, O_RDWR);
if (fd == -1)
return;
result = ioctl(fd, TIOCGWINSZ, &win);
close(fd);
if (result == -1)
return;
/* Could check whether the COLS or LINES changed, and return
* otherwise. EXCEPT, that COLS and LINES are ncurses global
* variables, and in some cases ncurses has already updated them.
* But not in all cases, argh. */
COLS = win.ws_col;
LINES = win.ws_row;
if ((editwinrows = LINES - 5 + no_help()) < MIN_EDITOR_ROWS)
die_too_small();
#ifndef DISABLE_WRAPJUSTIFY
fill = wrap_at;
if (fill <= 0)
fill += COLS;
if (fill < MIN_FILL_LENGTH)
die_too_small();
#endif
hblank = nrealloc(hblank, COLS + 1);
memset(hblank, ' ', COLS);
hblank[COLS] = '\0';
#ifdef HAVE_RESIZETERM
resizeterm(LINES, COLS);
#ifdef HAVE_WRESIZE
if (wresize(topwin, 2, COLS) == ERR)
die(_("Cannot resize top win"));
if (mvwin(topwin, 0, 0) == ERR)
die(_("Cannot move top win"));
if (wresize(edit, editwinrows, COLS) == ERR)
die(_("Cannot resize edit win"));
if (mvwin(edit, 2, 0) == ERR)
die(_("Cannot move edit win"));
if (wresize(bottomwin, 3 - no_help(), COLS) == ERR)
die(_("Cannot resize bottom win"));
if (mvwin(bottomwin, LINES - 3 + no_help(), 0) == ERR)
die(_("Cannot move bottom win"));
#endif /* HAVE_WRESIZE */
#endif /* HAVE_RESIZETERM */
fix_editbot();
if (current_y > editwinrows - 1)
edit_update(editbot, CENTER);
erase();
/* Do these b/c width may have changed... */
refresh();
titlebar(NULL);
edit_refresh();
display_main_list();
blank_statusbar();
total_refresh();
/* Turn cursor back on for sure */
curs_set(1);
/* Jump back to main loop */
siglongjmp(jmpbuf, 1);
}
#endif /* !NANO_SMALL */
/* If all went well, we didn't overwrite the allocated space for
help_text. */
assert(strlen(help_text) < allocsize);
/* If the NumLock key has made the keypad go awry, print an error
message; hopefully we can address it later. */
void print_numlock_warning(void)
{
static int didmsg = 0;
if (!didmsg) {
statusbar(_
("NumLock glitch detected. Keypad will malfunction with NumLock off"));
didmsg = 1;
}
}
#endif
#ifndef NANO_SMALL
void do_toggle(const toggle *which)
......@@ -2833,18 +2849,6 @@ void do_toggle(const toggle *which)
}
#endif /* !NANO_SMALL */
/* If the NumLock key has made the keypad go awry, print an error
message; hopefully we can address it later. */
void print_numlock_warning(void)
{
static int didmsg = 0;
if (!didmsg) {
statusbar(_
("NumLock glitch detected. Keypad will malfunction with NumLock off"));
didmsg = 1;
}
}
/* This function returns the correct keystroke, given the A,B,C or D
input key. This is a common sequence of many terms which send
Esc-O-[A-D] or Esc-[-[A-D]. */
......@@ -3460,7 +3464,7 @@ int main(int argc, char *argv[])
/* Look through the main shortcut list to see if we've hit a
shortcut key */
#if !defined(DISABLE_BROWSER) || !defined(DISABLE_MOUSE) || !defined (DISABLE_HELP)
for (s = currshortcut; s != NULL && !keyhandled; s = s->next) {
#else
......@@ -3474,6 +3478,10 @@ int main(int argc, char *argv[])
else
s->func();
keyhandled = 1;
/* rarely, the value of s can change after s->func(),
leading to problems; get around this by breaking out
explicitly once we successfully handle a shortcut */
break;
}
}
/* If we're in raw mode or using Alt-Alt-x, we have to catch
......@@ -3489,7 +3497,6 @@ int main(int argc, char *argv[])
keyhandled = 1;
}
#ifndef USE_SLANG
/* Hack, make insert key do something useful, like insert file */
if (kbinput == KEY_IC) {
......
......@@ -326,6 +326,7 @@ know what you're doing */
#define NANO_TOFILES_KEY NANO_CONTROL_T
#define NANO_APPEND_KEY NANO_ALT_A
#define NANO_PREPEND_KEY NANO_ALT_P
#define NANO_LOAD_KEY NANO_ALT_F
#define NANO_OPENPREV_KEY NANO_ALT_LCARAT
#define NANO_OPENNEXT_KEY NANO_ALT_RCARAT
#define NANO_OPENPREV_ALTKEY NANO_ALT_COMMA
......
......@@ -59,13 +59,7 @@ extern char *operating_dir;
extern char *full_operating_dir;
#endif
#ifndef DISABLE_SPELLER
extern char *alt_speller;
#endif
#ifndef DISABLE_TABCOMP
char *real_dir_from_tilde(char *buf);
#endif
#ifndef DISABLE_BROWSER
char *do_browse_from(char *inpath);
extern char *alt_speller;
#endif
extern struct stat fileinfo;
......@@ -116,23 +110,28 @@ extern toggle *toggles;
/* 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 */
int do_cut_text(void);
int do_uncut_text(void);
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 write_file(char *name, int tmpfile, int append, int nonamechange);
int open_file(const char *filename, int insert, int quiet);
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);
......@@ -140,157 +139,299 @@ void unlink_opennode(const openfilestruct *fileptr);
void delete_opennode(openfilestruct *fileptr);
void free_openfilestruct(openfilestruct *src);
int add_open_file(int update);
int close_open_file(void);
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(char *origpath);
#endif
#ifndef DISABLE_SPELLER
char *check_writable_directory(char *path);
char *safe_tempnam(const char *dirname, const char *filename_prefix);
#endif
#ifndef DISABLE_OPERATINGDIR
int check_operating_dir(char *currpath, int allow_tabcomp);
#endif
int write_file(char *name, int tmp, int append, int nonamechange);
int do_writeout(char *path, int exiting, int append);
char *input_tab(char *buf, int place, int *lastwastab, int *newplace, int *list);
void new_file(void);
int do_writeout_void(void);
int do_insertfile_void(void);
char *get_next_filename(const char *name);
#ifndef DISABLE_SPELLER
char *safe_tempnam(const char *dirname, const char *filename_prefix);
#ifndef DISABLE_TABCOMP
char *real_dir_from_tilde(char *buf);
#endif
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);
#ifndef DISABLE_BROWSER
struct stat filestat(const char *path);
int diralphasort(const void *va, const void *vb);
void free_charptrarray(char **array, int len);
char *tail(char *foo);
void striponedir(char *foo);
char **browser_init(char *path, int *longest, int *numents);
char *do_browser(char *inpath);
char *do_browse_from(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_first_line(void);
int do_last_line(void);
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 reset_cursor(void);
void blank_bottombars(void);
void blank_edit(void);
void blank_statusbar(void);
void blank_statusbar_refresh(void);
void check_statblank(void);
void titlebar(const char *path);
void bottombars(const shortcut *s);
void set_modified(void);
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);
void page_up(void);
int do_page_up(void);
int do_page_down(void);
int do_home(void);
int do_end(void);
/* Public functions in nano.c */
void renumber(filestruct *fileptr);
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);
void mouse_init(void);
#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);
int no_help(void);
void renumber_all(void);
int open_pipe(const char *command);
int do_prev_word(void);
int do_next_word(void);
void delete_node(filestruct *fileptr);
void wrap_reset(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);
void die(const char *msg, ...);
void splice_node(filestruct *begin, filestruct *newnode, filestruct *end);
int no_help(void);
#if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || defined(DISABLE_HELP) || defined(NANO_SMALL)
void nano_disabled_msg(void);
void window_init(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);
void print_view_warning(void);
int do_exit(void);
int do_spell(void);
int do_mark(void);
int do_delete(void);
#endif
#endif
void do_char(char ch);
int do_backspace(void);
int do_delete(void);
int do_tab(void);
int do_justify(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);
int do_int_speller(char *tempfile_name);
int 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 break_line(const char *line, int goal, int force);
#endif /* !DISABLE_JUSTIFY */
int do_justify(void);
int do_exit(void);
void signal_init(void);
RETSIGTYPE handle_hup(int signal);
RETSIGTYPE do_suspend(int signal);
RETSIGTYPE do_cont(int signal);
#ifndef NANO_SMALL
void handle_sigwinch(int s);
void die_save_file(const char *die_filename);
size_t indent_length(const char *line);
filestruct *copy_node(const filestruct *src);
filestruct *copy_filestruct(const filestruct *src);
filestruct *make_new_node(filestruct *prevnode);
#ifndef DISABLE_HELP
void help_init(void);
#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);
void parse_syntax(char *ptr);
void parse_colors(char *ptr);
#endif /* ENABLE_COLOR */
void parse_rcfile(FILE *rcstream);
void do_rcfile(void);
#endif
#endif /* ENABLE_NANORC */
/* Public functions in search.c */
int do_gotoline(int line, int save_pos);
#ifdef HAVE_REGEX_H
void 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);
void replace_abort(void);
#ifdef HAVE_REGEX_H
int replace_regexp(char *string, int create_flag);
#endif
char *replace_line(void);
void print_replaced(int num);
int do_replace_loop(const char *prevanswer, const filestruct *begin,
int *beginx, int wholewords, int *i);
int do_find_bracket(void);
int do_replace(void);
void goto_abort(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
void search_init_globals(void);
void replace_abort(void);
int do_gotoline_void(void);
int do_search(void);
int do_replace(void);
filestruct *findnextstr(int quiet, int bracket_mode, const filestruct *begin,
int beginx, const char *needle);
int do_find_bracket(void);
/* Public functions in utils.c */
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);
int is_cntrl_char(int c);
int num_of_digits(int n);
int check_wildcard_match(const char *text, const char *pattern);
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 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);
char *mallocstrcpy(char *dest, const char *src);
void *nmalloc(size_t howmuch);
char *charalloc(size_t howmuch);
void *nrealloc(void *ptr, size_t howmuch);
char *mallocstrcpy(char *dest, const char *src);
void new_magicline(void);
char *charalloc(size_t howmuch);
#ifndef DISABLE_TABCOMP
int check_wildcard_match(const char *text, const char *pattern);
#endif
/* Public functions in winio.c */
int do_yesno(int all, int leavecursor, const char *msg, ...);
int statusq(int allowtabs, const shortcut *s, const char *def,
const char *msg, ...);
void do_replace_highlight(int highlight_flag, const char *word);
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,
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);
int get_page_start_virtual(int page);
int get_page_from_virtual(int virtual);
int get_page_end_virtual(int page);
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(filestruct *fileptr, int yval, int start, int virt_cur_x,
int virt_mark_beginx, int this_page);
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);
void update_cursor(void);
int statusq(int tabs, const shortcut *s, const char *def,
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 do_help(void);
int keypad_on(WINDOW *win, int newval);
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
void update_line(filestruct *fileptr, int index);
void fix_editbot(void);
void statusbar(const char *msg, ...);
void center_cursor(void);
void display_main_list(void);
#ifdef NANO_EXTRA
void do_credits(void);
#endif
int do_cursorpos(int constant);
int do_cursorpos_void(void);
int total_refresh(void);
int do_help(void);
int keypad_on(WINDOW *win, int newval);
......@@ -188,24 +188,6 @@ char *parse_argument(char *ptr)
#ifdef ENABLE_COLOR
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;
}
int colortoint(const char *colorname, int *bright)
{
int mcolor = 0;
......@@ -245,6 +227,24 @@ int colortoint(const char *colorname, int *bright)
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;
}
void parse_syntax(char *ptr)
{
syntaxtype *tmpsyntax = NULL;
......
......@@ -53,6 +53,33 @@ void regexp_cleanup(void)
}
#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)
{
UNSET(KEEP_CUTBUFFER);
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) {
......@@ -67,10 +94,11 @@ void search_init_globals(void)
/* Set up the system variables for a search or replace. Returns -1 on
abort, 0 on success, and 1 on rerun calling program
Return -2 to run opposite program (search -> replace, replace -> search)
Return -2 to run opposite program (search -> replace, replace ->
search).
replacing = 1 if we call from do_replace, 0 if called from do_search func.
*/
replacing = 1 if we call from do_replace, 0 if called from do_search
func. */
int search_init(int replacing)
{
int i = 0;
......@@ -196,19 +224,6 @@ int search_init(int replacing)
return 0;
}
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);
}
}
int is_whole_word(int curr_pos, const char *datastr, const char *searchword)
{
size_t sln = curr_pos + strlen(searchword);
......@@ -223,7 +238,8 @@ static int past_editbuff;
/* findnextstr() is now searching lines not displayed */
filestruct *findnextstr(int quiet, int bracket_mode,
const filestruct *begin, int beginx, const char *needle)
const filestruct *begin, int beginx,
const char *needle)
{
filestruct *fileptr = current;
const char *searchstr, *rev_start = NULL, *found = NULL;
......@@ -363,20 +379,6 @@ filestruct *findnextstr(int quiet, int bracket_mode,
return fileptr;
}
void search_abort(void)
{
UNSET(KEEP_CUTBUFFER);
display_main_list();
wrefresh(bottomwin);
if (ISSET(MARK_ISSET))
edit_refresh_clearok();
#ifdef HAVE_REGEX_H
if (ISSET(REGEXP_COMPILED))
regexp_cleanup();
#endif
}
/* Search for a string. */
int do_search(void)
{
......@@ -430,14 +432,6 @@ int do_search(void)
return 1;
}
void print_replaced(int num)
{
if (num > 1)
statusbar(_("Replaced %d occurrences"), num);
else if (num == 1)
statusbar(_("Replaced 1 occurrence"));
}
void replace_abort(void)
{
/* Identical to search_abort, so we'll call it here. If it
......@@ -561,6 +555,14 @@ char *replace_line(void)
return copy;
}
void print_replaced(int num)
{
if (num > 1)
statusbar(_("Replaced %d occurrences"), num);
else if (num == 1)
statusbar(_("Replaced 1 occurrence"));
}
/* step through each replace word and prompt user before replacing word */
int do_replace_loop(const char *prevanswer, const filestruct *begin,
int *beginx, int wholewords, int *i)
......
......@@ -97,7 +97,7 @@ void sunder(char *str)
/* 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)
const char *rev_start)
{
for(; rev_start >= haystack ; rev_start--) {
const char *r, *q;
......@@ -111,7 +111,7 @@ const char *revstrstr(const char *haystack, const char *needle,
}
const char *revstristr(const char *haystack, const char *needle,
const char *rev_start)
const char *rev_start)
{
for (; rev_start >= haystack; rev_start--) {
const char *r = rev_start, *q = needle;
......@@ -147,7 +147,7 @@ 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)
const char *rev_start, int line_pos)
{
#ifdef HAVE_REGEX_H
if (ISSET(USE_REGEXP)) {
......@@ -195,7 +195,8 @@ const char *strstrwrapper(const char *haystack, const char *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) {
void nperror(const char *s)
{
/* leave ncurses mode, go to the terminal */
if (endwin() != ERR) {
perror(s); /* print the error */
......
......@@ -155,6 +155,15 @@ void blank_bottombars(void)
}
}
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;
......@@ -210,11 +219,11 @@ void nanoget_repaint(const char *buf, const char *inputbuf, int x)
/* Get the input from the kb; this should only be called from
* statusq(). */
int nanogetstr(int allowtabs, const char *buf, const char *def,
const shortcut *s
const shortcut *s
#ifndef DISABLE_TABCOMP
, int *list
, int *list
#endif
)
)
{
int kbinput;
int x;
......@@ -241,7 +250,8 @@ int nanogetstr(int allowtabs, const char *buf, const char *def,
nanoget_repaint(buf, answer, x);
/* Make sure any editor screen updates are displayed before getting input */
/* Make sure any editor screen updates are displayed before getting
input */
wrefresh(edit);
while ((kbinput = wgetch(bottomwin)) != 13) {
......@@ -253,7 +263,8 @@ int nanogetstr(int allowtabs, const char *buf, const char *def,
if (kbinput == t->val && kbinput < 32) {
#ifndef DISABLE_HELP
/* Have to do this here, it would be too late to do it in statusq */
/* 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;
......@@ -435,6 +446,16 @@ int nanogetstr(int allowtabs, const char *buf, const char *def,
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;
......@@ -483,35 +504,6 @@ void titlebar(const char *path)
reset_cursor();
}
/* 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, ' ');
}
}
void clear_bottomwin(void)
{
if (ISSET(NO_HELP))
return;
mvwaddstr(bottomwin, 1, 0, hblank);
mvwaddstr(bottomwin, 2, 0, hblank);
}
void bottombars(const shortcut *s)
{
int i, j, numcols;
......@@ -530,7 +522,7 @@ void bottombars(const shortcut *s)
/* There will be this many columns of shortcuts */
numcols = (slen + (slen % 2)) / 2;
clear_bottomwin();
blank_bottomwin();
for (i = 0; i < numcols; i++) {
for (j = 0; j <= 1; j++) {
......@@ -562,20 +554,30 @@ void bottombars(const shortcut *s)
wrefresh(bottomwin);
}
/* If modified is not already set, set it and update titlebar. */
void set_modified(void)
/* 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)
{
if (!ISSET(MODIFIED)) {
SET(MODIFIED);
titlebar(NULL);
wrefresh(topwin);
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 */
/* Given a column, this returns the "page" it is on */
/* "page" in the case of the display columns, means which set of 80 */
/* characters is viewable (e.g.: page 1 shows from 1 to COLS) */
/* And so start the display update routines. Given a column, this
* returns the "page" it is on. "page", in the case of the display
* columns, means which set of 80 characters is viewable (e.g. page 1
* shows from 1 to COLS). */
int get_page_from_virtual(int virtual)
{
int page = 2;
......@@ -637,12 +639,10 @@ void reset_cursor(void)
}
#ifndef NANO_SMALL
/* This takes care of the case where there is a mark that covers only */
/* the current line. */
/* It expects a line with no tab characters (i.e.: the type that edit_add */
/* deals with */
void add_marked_sameline(int begin, int end, filestruct * fileptr, int y,
/* This takes care of the case where there is a mark that covers only
* the current line. It expects a line with no tab characters (i.e.
* the type that edit_add() deals with. */
void add_marked_sameline(int begin, int end, filestruct *fileptr, int y,
int virt_cur_x, int this_page)
{
/*
......@@ -691,12 +691,10 @@ void add_marked_sameline(int begin, int end, filestruct * fileptr, int y,
}
#endif
/* 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(filestruct * fileptr, int yval, int start, int virt_cur_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(filestruct *fileptr, int yval, int start, int virt_cur_x,
int virt_mark_beginx, int this_page)
{
......@@ -1007,12 +1005,10 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x,
/*
* Just update one line in the edit buffer. Basically a wrapper for
* edit_add
*
* index gives us a place in the string to update starting from.
* Likely args are current_x or 0.
* edit_add(). index gives us a place in the string to update starting
* from. Likely args are current_x or 0.
*/
void update_line(filestruct * fileptr, int index)
void update_line(filestruct *fileptr, int index)
{
filestruct *filetmp;
int line = 0, col = 0;
......@@ -1112,6 +1108,28 @@ void update_line(filestruct * fileptr, int index)
free(tmp);
}
/* 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;
......@@ -1194,28 +1212,6 @@ void edit_update(filestruct *fileptr, topmidbotnone location)
edit_refresh();
}
/* 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
}
/*
* 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
......@@ -1278,8 +1274,9 @@ int statusq(int tabs, const shortcut *s, const char *def,
}
/*
* Ask a simple yes/no question on the statusbar. Returns 1 for Y, 0 for
* N, 2 for All (if all is non-zero when passed in) and -1 for abort (^C)
* Ask a simple yes/no question on the statusbar. Returns 1 for Y, 0
* for N, 2 for All (if all is non-zero when passed in) and -1 for
* abort (^C).
*/
int do_yesno(int all, int leavecursor, const char *msg, ...)
{
......@@ -1303,7 +1300,7 @@ int do_yesno(int all, int leavecursor, const char *msg, ...)
allstr = _("Aa");
/* Write the bottom of the screen */
clear_bottomwin();
blank_bottomwin();
/* Remove gettext call for keybindings until we clear the thing up */
if (!ISSET(NO_HELP)) {
......@@ -1412,6 +1409,28 @@ int do_yesno(int all, int leavecursor, const char *msg, ...)
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;
......@@ -1452,28 +1471,6 @@ void statusbar(const char *msg, ...)
statblank = 25;
}
void display_main_list(void)
{
bottombars(main_list);
}
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;
}
int do_cursorpos(int constant)
{
filestruct *fileptr;
......@@ -1703,45 +1700,20 @@ int do_help(void)
return 1;
}
#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 */
/* Fix editbot, based on the assumption that edittop is correct */
void fix_editbot(void)
int keypad_on(WINDOW * win, int newval)
{
int i;
editbot = edittop;
for (i = 0; i < editwinrows && editbot->next != NULL; i++)
editbot = editbot->next;
/* This is taken right from aumix. Don't sue me. */
#ifdef HAVE_USEKEYPAD
int old = win->_use_keypad;
keypad(win, newval);
return old;
#else
keypad(win, newval);
return 1;
#endif /* HAVE_USEKEYPAD */
}
/* highlight the current word being replaced or spell checked */
/* Highlight the current word being replaced or spell checked. */
void do_replace_highlight(int highlight_flag, const char *word)
{
char *highlight_word = NULL;
......@@ -1786,6 +1758,44 @@ void do_replace_highlight(int highlight_flag, const char *word)
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 52
#define XLCREDIT_LEN 8
......@@ -1904,16 +1914,3 @@ void do_credits(void)
total_refresh();
}
#endif
int keypad_on(WINDOW * win, int newval)
{
/* This is taken right from aumix. Don't sue me. */
#ifdef HAVE_USEKEYPAD
int old = win->_use_keypad;
keypad(win, newval);
return old;
#else
keypad(win, newval);
return 1;
#endif /* HAVE_USEKEYPAD */
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment