diff --git a/BUGS b/BUGS index 77feb495..31f5477e 100644 --- a/BUGS +++ b/BUGS @@ -105,7 +105,7 @@ higuita@cadernoverde.com) (54) [FIXED]. - When using autoindent (-i), wrapped text does not get autoindented (55, discovered by Mark Senior) [FIXED]. -- When using -R (regex) and -p (pico mode), subsequent searches after +- When using -R (regex) and -p (Pico mode), subsequent searches after the first fail if no string is entered (56) [FIXED]. - Page down on a file of editwinrows fails (again). Reported by Ryan Krebs (57) [FIXED]. diff --git a/ChangeLog b/ChangeLog index 0bb9f5e9..6706322b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,8 +14,9 @@ CVS Code - - Many fixes to the help browser and shortcut lists: efficiency updates, consistency fixes, help text fixes and improvements, and spacing improvements. (David Benbennick) - - Make more functions use const variables when possible. (David - Benbennick) + - Make some functions use const variables when possible, and + also make them static when necessary. (David Benbennick, + necessary redefined by Chris ;-) - Add Carl Drinkwater's backup file option (-B or --backup on the command line, M-B in nano's global shortcuts). If the original file is unchanged from when it was loaded, it is backed up to @@ -33,20 +34,57 @@ CVS Code - --regexp as an alternative for -R; they were listed in nano's usage information, but weren't actually in nano. Also, display "-?" as an alternative for "-h" in nano's usage information, - put the command line options in a more consistent (i. e. mostly + put the command line options in a more consistent (i.e. mostly alphabetical) order in nano, put the long options in a more consistent order in rcfile.c and nanorc.sample, don't include rcfile options if their equivalent command line options are disabled, and remove obsolete relative option from nanorc.sample. (DLR) + - Removed much unnecessary parts to the color code and reworks + some of the color display (David Benbennick). - Change "File Name to Append/Prepend" to "File Name to Append/Prepend to". The original prompt could confusingly imply that we are appending/prepending another file to the current file, when we are actually appending/prepending the current file to another file. (DLR) - - Put nano.1, nano.1.html, and nano.texi up to date. (DLR) - - Typo fixes for the ChangeLog. (David Benbennick) + - Put nano.1, nano.1.html, and nano.texi up to date, and fix a + few inconsistencies in them. (DLR) + - Typo fixes for the ChangeLog. (David Benbennick and DLR) + - Complete rewrite of justification code to fix some bugs and + improve its functionality. (David Benbennick) + - If a variable isn't going to be used in tiny mode, #define it + out when possible. (David Benbennick) + - Major reworking of the cutting/screen-updating code in cut.c, + some functions in utils.c, the cursor placement code in + winio.c, and many, many other areas to increase efficiency. + (David Benbennick) + - Rework handling of prompts when there's a list of partial + filename matches on the screen: remove kludgy case-by-case + handling (which didn't even handle every case), and have + statusq() handle it directly for all cases. (David Benbennick + and DLR) + - Fix some warnings and errors that show up when using gcc's + -pedantic option. (DLR) + - Add a comment to nanorc.sample warning that an out-of-range + negative value for fill can make nano die complaining that + the screen is too small (which may not be immediately + obvious. (DLR) + - There were some opendir() calls in files.c without + corresponding closedir() calls; add them. (DLR) + - Move align() and null_at() from nano.c to utils.c, and move + the openfilestruct handling functions from nano.c to files.c. + (DLR) + - In color.c, start the "#ifdef ENABLE_COLOR" block after + including all the header files, as rcfile.c does; this fixes + a warning about ANSI C'S inability to handle blank files. + (DLR) + - Add command line option -I/--ignorercfiles to ignore + /etc/nanorc and ~/.nanorc. (Carl Drinkwater) - files.c: + new_file(): + - Make sure current_x is zero; this fixes a problem where the + current cursor position wasn't reset when reading in a file in + multibuffer mode. (David Benbennick) read_file(), read_line(): - Rework to properly handle nulls in the input file, fix detection of binary files to properly mark a file as binary if @@ -66,8 +104,19 @@ CVS Code - --enable-tiny is used. Since formatstr isn't ever used in tiny mode, don't bother even creating the variable. (David Benbennick and DLR) + do_insertfile(): + - Memory leak fix. (David Benbennick) + get_full_path(): + - Memory leak fix. Also, make it properly interpret ~/ notation + so, among other things, the option "--operatingdir ~" works. + (David Benbennick) + check_operating_dir(): + - Memory leak fix. (David Benbennick) + cwd_tab_completion(): + - Changed a variable name: dirName -> dirname. (DLR) append_slash_if_dir(), input_tab(): - - Changed a variable name: lastWasTab -> lastwastab. (DLR) + - Changed variable names: lastWasTab -> lastwastab, matchBuf -> + matchbuf. (DLR) - global.c: free_toggles(): - Only include if we're not using tiny mode. (David Benbennick) @@ -79,6 +128,8 @@ CVS Code - do_toggle() if Pico emulation mode is the toggle in question. Don't free the toggles here, either; it's unnecessary after the above change. (David Benbennick) + - If wrapping is disabled, don't include the toggle for it. + (DLR) shortcut_init(): - Rework IFHELP macro (David Benbennick). - move.c: @@ -90,6 +141,26 @@ CVS Code - - nanorc.sample: - Put in much less crappy example regex rules for c-file. - nano.c: + do_char(): + - Fix a problem where, if ENABLE_COLOR wasn't used, typing + characters on a marked line before the beginning of the mark + would make the highlight short by one. (David Benbennick) + die(): + - Rework slightly to remove redundant printing of last message + and print all messages after resetting the terminal. (DLR) + global_init(): + - Call die_too_small() when fill is 0. (DLR) + usage(): + - List the options that are ignored for the purpose of Pico + compatibility, and make some minor consistency fixes. (DLR) + do_next_word(), do_prev_word(): + - Fix a problem where highlighting isn't done properly after + calling either of these, and another problem where the cursor + would move back too far in certain cases with do_prev_word(). + (David Benbennick) + do_backspace(): + - Make sure placewewant is set properly, and that the mark is + moved backwards. (David Benbennick) clear_filename(): - Remove this function, as it has unneeded functionality, is short enough to be inlined, and is only called in two spots @@ -97,6 +168,16 @@ CVS Code - do_int_spell(), do_alt_spell(): - Rework to save the marked selection before doing spell checking and restore it afterward. (DLR) + do_cont(): + - Handle the case where the window was resized while we were + stopped. (David Benbennick) + handle_sigwinch(): + - Make sure we adjust fill when the window is resized. (David + Benbennick) + - Call die_too_small() when fill is 0. (DLR) + help_init(): + - Since the return value of snprintf() isn't well defined, use + sprintf() instead. (David Benbennick) do_toggle(): - Since the search mode toggles aren't global anymore, we don't need to explicitly block them here anymore (which will end up @@ -109,8 +190,23 @@ CVS Code - hold a line number. (DLR) - Properly handle multiple -r settings on the command line. (Carl Drinkwater) + - Fix a bug that prevented file insertion via the Insert key + from working at all when --enable-multibuffer wasn't used + (oops). (DLR) + - Adapt David Benbennick's fix to get fill to accept negative + numbers properly in parse_rcfile() (see below) to the + handlers for the -r and -T options as well, so that -r/-T 0 + can be treated separately from -r/-T string. (DLR) +- proto.h: + - Remove external declaration of the global int fill, since + it's now static. (DLR) - rcfile.c: - colortoint(): + parse_rcfile(): + - Add David Benbennick's fix that allows fill to accept + negative numbers properly. Specifically, use strtol() there + instead of atoi() so that errors can be detected. Also + adapted for tabsize by DLR. + parse_next_regex(), colortoint(): - Only include if ENABLE_COLOR is defined. (DLR) - search.c: search_init(): @@ -118,6 +214,9 @@ CVS Code - part of this function referencing them so that they still work. (DLR) - Remove unneeded toggles variable. (David Benbennick) + - Fix a problem where the first character of buf was overwritten + if the last search string was one third the number of columns + plus one. (David Benbennick) findnextstr(): - Update the current line at current_x if we don't find a match. Also, pass current_x_find to strstrwrapper() so we know whether @@ -134,10 +233,16 @@ CVS Code - other than the terminating null in strings to newlines and back; they're used to handle null characters in files properly. (DLR) + lowercase(): + - Remove, since it isn't actually used anywhere. (David + Benbennick) strstrwrapper(): Set REG_NOTBOL when we're not at the beginning of a string, to avoid false positives when searching for regular expressions prefixed with ^. Make it take a new parameter, line_pos, to determine where we are in the string. (DLR) + check_wildcard_match(): + - Changed variable names: retryPat -> retrypat, retryText -> + retrytext. (DLR) - winio.c: actual_x_from_start(): - Overhaul to make cursor placement more like that of Pico: add @@ -158,16 +263,28 @@ CVS Code - (which should never occur under normal circumstances; they will only be there if the line had nulls in it and was unsunder()ed beforehand) as ^@'s. (DLR) + - Display ASCII 0x80-0x9f as backslashes followed by 3-digit + octal values to keep xterm from screwing up the display when + some of them are printed as-is. (David Benbennick) + statusbar(): + - Limit statusbar display to the number of columns less four, and + don't allow it to go over its original row. (David Benbennick) do_help(): - Add support for the handled keyboard escape sequences in the help menu, as they are needed with some terminals (e.g. xterm with TERM=ansi). (DLR) + do_replace_highlight(): + - When using regexps, make sure the highlight is the length of + the search result and not the regexp string. (DLR) - configure.ac: - Added ms to ALL_LINGUAS (Jordi). - Merged acconfig.h in (Jordi). + - Fixed so that --enable-debug defines DEBUG and undefines + NDEBUG. (Carl Drinkwater) - THANKS: - Completed a bit (Jordi). - - Fixed David Benbennick's email address (David Benbennick). + - Fixed David Benbennick's email address. (David Benbennick) + - Typo fix. (DLR) GNU nano 1.1.9 - 05/12/2002 - General: - Typos n misspellings all over the place (David Benbennick). @@ -963,7 +1080,7 @@ nano-1.1.0 - 07/15/2001 - Moved extension functions (Case Sensitive, Regexp, and Backwards Search, Append key in write file function) to Meta keys, as people are complaining loudly about nano not being control-key - compatible with Pico, which is a Bag Thing (TM). Changes to + compatible with Pico, which is a Bad Thing (TM). Changes to shortcut_init, toggle_init, new toggles for backwards and regexp (and you can now toggle all search options including regexp at the Search: prompt!) Changes to nanogetstr to enable Meta @@ -1420,7 +1537,7 @@ General by Rocco Corsi. help_init() - Fix off by one error that was making ^G help in normal mode and - ^_ in pico mode not be displayed in the help (bug discovered by + ^_ in Pico mode not be displayed in the help (bug discovered by Rocco Corsi). do_toggle() - Added fix_editbot() call to fix improper redisplay of edit @@ -2248,10 +2365,10 @@ nano-0.9.3 - 04/29/2000 nano-0.9.2 - 04/15/2000 - This release just fixes the serious segfault problem if nano is invoked any way other than using the absolute path. The bug was - in the new code for checking whether nano is invoked as pico. + in the new code for checking whether nano is invoked as 'pico'. nano-0.9.1 - 04/14/2000 -- Added pico compatibility for ^T when in search or switch to switch +- Added Pico compatibility for ^T when in search or switch to switch to the opposite function. Added one to REPLACE_LIST_LEN and WHEREIS_LIST_LEN in nano.h, new args to sc_init_one in global.c and new strings that will have to be gettext()ed. New argument 'replacing' @@ -2373,7 +2490,7 @@ nano-0.8.4 - 02/11/2000 - README: Updated my email address and the nano web page. nano-0.8.3 - 02/08/2000 -- New pico mode (-p, --pico), toggles (more) compatibility with the +- New Pico mode (-p, --pico), toggles (more) compatibility with the Pico messages displayed in the shortcut list. Note that there are still small differences in this mode. - nano.h: New shortcut struct format, for the benefit of i18n and diff --git a/NEWS b/NEWS index 103b8c71..e19fe965 100644 --- a/NEWS +++ b/NEWS @@ -221,7 +221,7 @@ The --nofollow option also works again for those who are real security nuts. There are also some display and search fixes, and the --disable-spell function was renamed - to --disable-speller to be in line with nano and pico's + to --disable-speller to be in line with nano and Pico's "speller" term. 12/02/2000 - Nano 0.9.22 is released, with many more changes and @@ -416,13 +416,13 @@ 04/15/2000 - Nano 0.9.2 just fixes the serious segfault problem if nano is invoked any way other than using the absolute path. The bug was in the new code for checking whether - nano is invoked as pico. + nano is invoked as 'pico'. 04/14/2000 - 0.9.1 has some more Pico compatibility built-in. The option to switch to/from Search and Search/Replace (^T) is now available, and nano now displays the more Pico-like shortcut list when invoked as 'pico' - (i.e. if pico is a symlink to nano). There is an + (i.e. if 'pico' is a symlink to nano). There is an important change to the handling of symbolic links as well. Now, nano does the "correct" thing and automatically writes to the object of the symlink, diff --git a/THANKS b/THANKS index 8203e98e..ea254eab 100644 --- a/THANKS +++ b/THANKS @@ -1,5 +1,5 @@ The following people have helped GNU nano in some way or another. -If we missed you here, let us now! +If we missed you here, let us know! Sharuzzaman Ahmat Raslan Bahasa Melayu Translator diff --git a/color.c b/color.c index 7471f491..3d174160 100644 --- a/color.c +++ b/color.c @@ -21,8 +21,6 @@ #include "config.h" -#ifdef ENABLE_COLOR - #include #include #include @@ -33,6 +31,8 @@ #include "proto.h" #include "nano.h" +#ifdef ENABLE_COLOR + #ifdef ENABLE_NLS #include #define _(string) gettext(string) @@ -40,62 +40,7 @@ #define _(string) (string) #endif -void color_on(WINDOW *win, int whatever) -{ - /* Temporary fallback, if the color value hasn't been set, - turn on highlighting */ - if (!colors[whatever - FIRST_COLORNUM].set) { - wattron(win, A_REVERSE); - return; - } - - if (colors[whatever - FIRST_COLORNUM].bold) - wattron(win, A_BOLD); - - /* If the foreground color is black, we've switched fg and bg (see - the comment in colorinit_one() about this) so turn on reverse so - it looks like it's supposed to */ - if (colors[whatever - FIRST_COLORNUM].fg == COLOR_BLACK) - wattron(win, A_REVERSE); - - wattron(win, COLOR_PAIR(whatever)); - -} - -void color_off(WINDOW *win, int whatever) -{ - if (!colors[whatever - FIRST_COLORNUM].set) { - wattroff(win, A_REVERSE); - return; - } - - wattroff(win, COLOR_PAIR(whatever)); - - if (colors[whatever - FIRST_COLORNUM].fg == COLOR_BLACK) - wattroff(win, A_REVERSE); - - if (colors[whatever - FIRST_COLORNUM].bold) - wattroff(win, A_BOLD); -} - -void colorinit_one(int colortoset, short fg, short bg, int bold) -{ - colors[colortoset - FIRST_COLORNUM].fg = fg; - colors[colortoset - FIRST_COLORNUM].bg = bg; - colors[colortoset - FIRST_COLORNUM].bold = bold; - colors[colortoset - FIRST_COLORNUM].set = 1; - - /* Okay, so if they want a black foreground, do a switch on the fg - and bg, because specifying black as the foreground color gives - this ugly grey color. Then in color_on we will turn A_REVERSE - which is probably what they want it to look like... */ - if (fg == COLOR_BLACK) - init_pair(colortoset, bg, fg); - else - init_pair(colortoset, fg, bg); -} - -int do_colorinit(void) +void do_colorinit(void) { int i; colortype *tmpcolor = NULL, *beforenow = NULL; @@ -142,38 +87,7 @@ int do_colorinit(void) } } -/* - if (use_default_colors() != ERR) { - init_pair(COLOR_BLACK, -1, -1); - init_pair(COLOR_GREEN, COLOR_GREEN, -1); - init_pair(COLOR_WHITE, COLOR_WHITE, -1); - init_pair(COLOR_RED, COLOR_RED, -1); - init_pair(COLOR_CYAN, COLOR_CYAN, -1); - init_pair(COLOR_MAGENTA, COLOR_MAGENTA, -1); - init_pair(COLOR_BLUE, COLOR_BLUE, -1); - init_pair(COLOR_YELLOW, COLOR_YELLOW, -1); - - } else { - init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK); - init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); - init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK); - init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK); - init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK); - init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK); - init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK); - init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK); - } -*/ - - /* Okay I'll be nice and comment these out for the commit =) - colorinit_one(COLOR_TITLEBAR, COLOR_GREEN, COLOR_BLUE, 1); - colorinit_one(COLOR_BOTTOMBARS, COLOR_GREEN, COLOR_BLUE, 1); - colorinit_one(COLOR_STATUSBAR, COLOR_BLACK, COLOR_CYAN, 0); - colorinit_one(COLOR_TEXT, COLOR_WHITE, COLOR_BLACK, 0); - colorinit_one(COLOR_MARKER, COLOR_BLACK, COLOR_CYAN, 0); - } -*/ - return 0; + return; } /* Update the color information based on the current filename */ diff --git a/configure.ac b/configure.ac index c229842b..dd97b63b 100644 --- a/configure.ac +++ b/configure.ac @@ -17,10 +17,18 @@ dnl Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS(fcntl.h unistd.h malloc.h termios.h termio.h limits.h getopt.h regex.h) -dnl Turn off assert statements. -AC_DEFINE(NDEBUG, 1, [Shut up the assert warnings :-)]) - dnl options +AC_ARG_ENABLE(debug, +[ --enable-debug Enable debugging (disabled by default)], + [if test x$enableval = xyes; then + AC_DEFINE(DEBUG, 1, [Define this to enable nano debug messages and assert warnings.]) + debug_support=yes + fi]) + +if test "$debug_support" != "yes"; then + AC_DEFINE(NDEBUG, 1, [Shut up the assert warnings :-)]) +fi + AC_ARG_ENABLE(tiny, [ --enable-tiny Disable features for the sake of size (currently disables detailed help and i18n)], @@ -31,8 +39,8 @@ AC_ARG_ENABLE(tiny, 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_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).]) fi]) AC_ARG_ENABLE(extra, @@ -118,7 +126,7 @@ AC_ARG_ENABLE(mouse, fi]) AC_ARG_ENABLE(operatingdir, -[ --disable-operatingdir Disable setting of operating directory (chroot of sorts)], +[ --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).]) fi]) @@ -296,8 +304,6 @@ dnl Parse any configure options LIBS="$LIBS $CURSES_LIB" -AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging (def disabled)],) - AC_SUBST(CURSES_LIB) if test "x$glib_cflags" != "x" diff --git a/cut.c b/cut.c index a8161a05..97c13009 100644 --- a/cut.c +++ b/cut.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "proto.h" #include "nano.h" @@ -34,17 +35,21 @@ #define _(string) (string) #endif -static int marked_cut; /* Is the cutbuffer from a mark */ -static filestruct *cutbottom = NULL; /* Pointer to end of cutbuffer */ +static int marked_cut; /* Is the cutbuffer from a mark? */ +static filestruct *cutbottom = NULL; /* Pointer to end of cutbuffer */ -void add_to_cutbuffer(filestruct * inptr) +filestruct *get_cutbottom(void) +{ + return cutbottom; +} + +void add_to_cutbuffer(filestruct *inptr) { #ifdef DEBUG fprintf(stderr, _("add_to_cutbuffer called with inptr->data = %s\n"), inptr->data); #endif - totsize -= strlen(inptr->data); if (cutbuffer == NULL) { cutbuffer = inptr; inptr->prev = NULL; @@ -58,186 +63,168 @@ void add_to_cutbuffer(filestruct * inptr) } #ifndef NANO_SMALL -/* Cut a marked segment instead of a whole line. Only called from - do_cut_text(). - destructive is whether to actually modify the file structure, if not then - just copy the buffer into cutbuffer and don't pull it from the file */ - -void cut_marked_segment(filestruct * top, int top_x, filestruct * bot, - int bot_x, int destructive) +/* Cut a marked segment instead of a whole line. + * The first cut character is top->data[top_x]. Unless top == bot, the + * last cut line has length bot_x. That is, if bot_x > 0 then we cut to + * bot->data[bot_x - 1]. + * + * destructive is whether to actually modify the file structure, if not + * then just copy the buffer into cutbuffer and don't pull it from the + * file. + * + * If destructive, then we maintain totsize, totlines, filebot, the + * magic line, and line numbers. Also, we set current and current_x so + * the cursor will be on the first character after what was cut. We do + * not do any screen updates. */ +void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot, + size_t bot_x, int destructive) { - filestruct *tmp, *next, *botcopy; - char *tmpstr; - int newsize; + filestruct *tmp, *next; + size_t newsize; + + if (top == bot && top_x == bot_x) + return; + assert(top != NULL && bot != NULL); + + /* Make top be no later than bot. */ + if (top->lineno > bot->lineno) { + filestruct *swap = top; + int swap2 = top_x; + + top = bot; + bot = swap; + + top_x = bot_x; + bot_x = swap2; + } else if (top == bot && top_x > bot_x) { + /* And bot_x can't be an earlier character than top_x. */ + int swap = top_x; + + top_x = bot_x; + bot_x = swap; + } + + /* Make the first cut line manually. */ + tmp = copy_node(top); + newsize = (top == bot ? bot_x - top_x : strlen(top->data + top_x)); + memmove(tmp->data, top->data + top_x, newsize); + null_at(&tmp->data, newsize); + add_to_cutbuffer(tmp); + + /* And make the remainder line manually too. */ + if (destructive) { + current_x = top_x; + totsize -= newsize; + totlines -= bot->lineno - top->lineno; + + newsize = top_x + strlen(bot->data + bot_x) + 1; + if (top == bot) { + /* In this case, the remainder line is shorter, so we must + move text from the end forward first. */ + memmove(top->data + top_x, bot->data + bot_x, + newsize - top_x); + top->data = (char *)nrealloc(top->data, + sizeof(char) * newsize); + } else { + totsize -= bot_x; + + /* Here, the remainder line might get longer, so we realloc + it first. */ + top->data = (char *)nrealloc(top->data, + sizeof(char) * newsize); + memmove(top->data + top_x, bot->data + bot_x, + newsize - top_x); + } + } - /* Special case for cutting part of one line */ if (top == bot) { - int swap; - - tmp = copy_node(top); - newsize = abs(bot_x - top_x) + 1; - tmpstr = charalloc(newsize + 1); - - /* Make top_x always be before bot_x */ - if (top_x > bot_x) { - swap = top_x; - top_x = bot_x; - bot_x = swap; - } - - strncpy(tmpstr, &top->data[top_x], newsize); - - if (destructive) { - memmove(&top->data[top_x], &top->data[bot_x], - strlen(&top->data[bot_x]) + 1); - align(&top->data); - current_x = top_x; - update_cursor(); - } - tmpstr[newsize - 1] = '\0'; - tmp->data = tmpstr; - add_to_cutbuffer(tmp); +#ifdef DEBUG dump_buffer(cutbuffer); - +#endif return; } - /* Set up the beginning of the cutbuffer */ - tmp = copy_node(top); - tmpstr = charalloc(strlen(&top->data[top_x]) + 1); - strcpy(tmpstr, &top->data[top_x]); - free(tmp->data); - tmp->data = tmpstr; - - /* Chop off the end of the first line */ - tmpstr = charalloc(top_x + 1); - strncpy(tmpstr, top->data, top_x); - - if (destructive) { - free(top->data); - top->data = tmpstr; - } - - do { + tmp = top->next; + while (tmp != bot) { next = tmp->next; - if (destructive) - add_to_cutbuffer(tmp); - else { - filestruct *tmpcopy = NULL; - - tmpcopy = copy_node(tmp); - add_to_cutbuffer(tmpcopy); - } - totlines--; - totsize--; /* newline (add_to_cutbuffer doesn't count newlines) */ + if (!destructive) + tmp = copy_node(tmp); + else + totsize -= strlen(tmp->data); + add_to_cutbuffer(tmp); tmp = next; } - while (next != bot && next != NULL); + /* Make the last cut line manually. */ + tmp = copy_node(bot); + null_at(&tmp->data, bot_x); + add_to_cutbuffer(tmp); +#ifdef DEBUG dump_buffer(cutbuffer); - if (next == NULL) - return; - - /* Now, paste bot[bot_x] into top[top_x] */ - if (destructive) { - - tmpstr = charalloc(top_x + strlen(&bot->data[bot_x]) + 1); - strncpy(tmpstr, top->data, top_x); - strcpy(&tmpstr[top_x], &bot->data[bot_x]); - free(top->data); - top->data = tmpstr; - - /* We explicitly don't decrement totlines here because we don't snarf - * up a newline when we're grabbing the last line of the mark. For - * the same reason, we don't do an extra totsize decrement. */ - } - - /* I honestly do not know why this is needed. After many hours of - using gdb on an OpenBSD box, I can honestly say something is - screwed somewhere. Not doing this causes update_line to annihilate - the last line copied into the cutbuffer when the mark is set ?!?!? */ - botcopy = copy_node(bot); - null_at(&botcopy->data, bot_x); - next = botcopy->next; - add_to_cutbuffer(botcopy); - +#endif if (destructive) { - free(bot); - - top->next = next; - if (next != NULL) - next->prev = top; - - dump_buffer(cutbuffer); + top->next = bot->next; + if (top->next != NULL) + top->next->prev = top; + delete_node(bot); renumber(top); current = top; - current_x = top_x; - - /* If we're hitting the end of the buffer, we should clean that up. */ if (bot == filebot) { - if (next != NULL) { - filebot = next; - } else { - filebot = top; - if (top_x > 0) - new_magicline(); - } + filebot = top; + assert(bot_x == 0); + if (top_x > 0) + new_magicline(); } - if (top->lineno < edittop->lineno) - edit_update(top, CENTER); } - } #endif int do_cut_text(void) { - filestruct *tmp, *fileptr = current; + filestruct *fileptr; #ifndef NANO_SMALL int dontupdate = 0; - int cuttingtoend = 0; #endif + assert(current != NULL && current->data != NULL); check_statblank(); - if (fileptr == NULL || fileptr->data == NULL) - return 0; - - tmp = fileptr->next; if (!ISSET(KEEP_CUTBUFFER)) { free_filestruct(cutbuffer); cutbuffer = NULL; - marked_cut = 0; #ifdef DEBUG fprintf(stderr, _("Blew away cutbuffer =)\n")); #endif } - /* Must let cutbuffer get blown away first before we do this... */ - if (fileptr == filebot && !ISSET(MARK_ISSET)) + /* You can't cut the magic line except with the mark. But + trying does clear the cutbuffer if KEEP_CUTBUFFER is not set. */ + if (current == filebot +#ifndef NANO_SMALL + && !ISSET(MARK_ISSET) +#endif + ) return 0; #ifndef NANO_SMALL if (ISSET(CUT_TO_END) && !ISSET(MARK_ISSET)) { - if (current_x == strlen(current->data)) { + assert(current_x >= 0 && current_x <= strlen(current->data)); - /* If the line is empty and we didn't just cut a non-blank - line, create a dummy line and add it to the cutbuffer */ + if (current->data[current_x] == '\0') { + /* If the line is empty and we didn't just cut a non-blank + line, create a dummy line and add it to the cutbuffer */ if (marked_cut != 1 && current->next != filebot) { + filestruct *junk = make_new_node(current); - filestruct *junk; - - junk = NULL; - junk = make_new_node(current); junk->data = charalloc(1); junk->data[0] = '\0'; - add_to_cutbuffer(junk); +#ifdef DEBUG dump_buffer(cutbuffer); - +#endif } do_delete(); @@ -250,104 +237,69 @@ int do_cut_text(void) mark_beginx = strlen(current->data); mark_beginbuf = current; - cuttingtoend = 1; + dontupdate = 1; } } if (ISSET(MARK_ISSET)) { - if (current->lineno <= mark_beginbuf->lineno) { - /* Don't do_update and move the screen position if the marked - area lies entirely within the screen buffer */ - if (current->lineno == mark_beginbuf->lineno - || (current->lineno >= edittop->lineno - && mark_beginbuf->lineno <= editbot->lineno)) - dontupdate = 1; - - cut_marked_segment(current, current_x, mark_beginbuf, - mark_beginx, 1); - } - else { - /* Same as above, easier logic since we know it's a multi-line - cut and mark_beginbuf is before current */ - if (mark_beginbuf->lineno >= edittop->lineno - && current->lineno <= editbot->lineno) - dontupdate = 1; - - cut_marked_segment(mark_beginbuf, mark_beginx, current, - current_x, 1); - } - + /* Don't do_update() and move the screen position if the marked + area lies entirely within the screen buffer */ + dontupdate |= current->lineno >= edittop->lineno && + current->lineno <= editbot->lineno && + mark_beginbuf->lineno >= edittop->lineno && + mark_beginbuf->lineno <= editbot->lineno; + cut_marked_segment(current, current_x, mark_beginbuf, + mark_beginx, 1); placewewant = xplustabs(); UNSET(MARK_ISSET); marked_cut = 1; set_modified(); - if (dontupdate || cuttingtoend) { + if (dontupdate) { fix_editbot(); edit_refresh(); } else edit_update(current, CENTER); return 1; -#else - if (0) { -#endif - } else if (fileptr == fileage) { - /* we're cutting the first line */ - if (fileptr->next != NULL) { - fileptr = fileptr->next; - tmp = fileptr; - fileage = fileptr; - add_to_cutbuffer(fileptr->prev); - totsize--; /* get the newline */ - totlines--; - fileptr->prev = NULL; - current = fileptr; - edit_update(fileage, CENTER); - } else { - add_to_cutbuffer(fileptr); - fileage = make_new_node(NULL); - fileage->data = charalloc(1); - fileage->data[0] = '\0'; - current = fileage; - } - } else { - if (fileptr->prev != NULL) - fileptr->prev->next = fileptr->next; - - if (fileptr->next != NULL) { - (fileptr->next)->prev = fileptr->prev; - current = fileptr->next; - totlines--; - totsize--; /* get the newline */ - } - /* No longer an else here, because we never get here anymore... - No need to cut the magic line, as it's empty */ - add_to_cutbuffer(fileptr); } +#endif /* !NANO_SMALL */ + + totlines--; + totsize -= strlen(current->data) + 1; + fileptr = current; + current = current->next; + current->prev = fileptr->prev; + add_to_cutbuffer(fileptr); +#ifdef DEBUG + dump_buffer(cutbuffer); +#endif + + if (fileptr == fileage) + fileage = current; + else + current->prev->next = current; if (fileptr == edittop) edittop = current; + renumber(current); edit_refresh(); - - dump_buffer(cutbuffer); - reset_cursor(); - set_modified(); marked_cut = 0; current_x = 0; placewewant = 0; - update_cursor(); - renumber(tmp); + reset_cursor(); SET(KEEP_CUTBUFFER); return 1; } int do_uncut_text(void) { - filestruct *tmp = current, *fileptr = current, *newbuf, *newend; + filestruct *tmp = current, *fileptr = current; + filestruct *newbuf = NULL; + filestruct *newend = NULL; #ifndef NANO_SMALL char *tmpstr, *tmpstr2; filestruct *hold = current; @@ -359,29 +311,35 @@ int do_uncut_text(void) if (cutbuffer == NULL || fileptr == NULL) return 0; /* AIEEEEEEEEEEEE */ - newbuf = copy_filestruct(cutbuffer); - for (newend = newbuf; newend->next != NULL && newend != NULL; - newend = newend->next) { - totlines++; +#ifndef NANO_SMALL + if (!marked_cut || cutbuffer->next != NULL) +#endif + { + newbuf = copy_filestruct(cutbuffer); + for (newend = newbuf; newend->next != NULL && newend != NULL; + newend = newend->next) + totlines++; } /* Hook newbuf into fileptr */ #ifndef NANO_SMALL if (marked_cut) { + int recenter_me = 0; + /* Should we eventually use edit_update(CENTER)? */ + /* If there's only one line in the cutbuffer */ if (cutbuffer->next == NULL) { - tmpstr = - charalloc(strlen(current->data) + strlen(cutbuffer->data) + - 1); - strncpy(tmpstr, current->data, current_x); - strcpy(&tmpstr[current_x], cutbuffer->data); - strcat(tmpstr, ¤t->data[current_x]); - free(current->data); - current->data = tmpstr; - current_x += strlen(cutbuffer->data); - totsize += strlen(cutbuffer->data); - if (strlen(cutbuffer->data) == 0) - totlines++; + size_t buf_len = strlen(cutbuffer->data); + size_t cur_len = strlen(current->data); + + current->data = nrealloc(current->data, cur_len + buf_len + 1); + memmove(current->data + current_x + buf_len, + current->data + current_x, cur_len - current_x + 1); + strncpy(current->data + current_x, cutbuffer->data, buf_len); + /* Use strncpy to not copy the terminal '\0'. */ + + current_x += buf_len; + totsize += buf_len; /* If we've uncut a line, make sure there's a magicline after it */ if (current->next == NULL) @@ -417,8 +375,8 @@ int do_uncut_text(void) newend->next = tmp; /* If tmp isn't null, we're in the middle: update the - * prev pointer. If it IS null, we're at the end; update - * the filebot pointer */ + prev pointer. If it IS null, we're at the end; update + the filebot pointer */ if (tmp != NULL) tmp->prev = newend; @@ -434,21 +392,16 @@ int do_uncut_text(void) for (tmp = current->next; tmp != newend; tmp = tmp->next) totsize += strlen(tmp->data) + 1; - i = editbot->lineno; - current = newend; - if (i < newend->lineno) { - edit_update(current, CENTER); - } - else { - edit_refresh(); - } + if (editbot->lineno < newend->lineno) + recenter_me = 1; } /* If marked cut == 2, that means that we're doing a cut to end and we don't want anything else on the line, so we have to - screw up all the work we just did and separate the line. There - must be a better way to do this, but not at 1AM on a work night. */ + screw up all the work we just did and separate the line. + There must be a better way to do this, but not at 1AM on a + work night. */ if (marked_cut == 2) { tmp = make_new_node(current); @@ -467,24 +420,27 @@ int do_uncut_text(void) /* Renumber from BEFORE where we pasted ;) */ renumber(hold); +#ifdef DEBUG dump_buffer(fileage); dump_buffer(cutbuffer); +#endif set_modified(); - edit_refresh(); + if (recenter_me) + edit_update(current, CENTER); + else + edit_refresh(); UNSET(KEEP_CUTBUFFER); return 0; -#else - if (0) { + } #endif - } else if (fileptr != fileage) { + + if (fileptr != fileage) { tmp = fileptr->prev; tmp->next = newbuf; newbuf->prev = tmp; - totlines++; /* Unmarked uncuts don't split lines */ - } else { + } else fileage = newbuf; - totlines++; /* Unmarked uncuts don't split lines */ - } + totlines++; /* Unmarked uncuts don't split lines */ /* This is so uncutting at the top of the buffer will work => */ if (current_y == 0) @@ -494,23 +450,22 @@ int do_uncut_text(void) newend->next = fileptr; fileptr->prev = newend; - /* recalculate size *sigh* */ + /* Recalculate size *sigh* */ for (tmp = newbuf; tmp != fileptr; tmp = tmp->next) totsize += strlen(tmp->data) + 1; i = editbot->lineno; renumber(newbuf); - if (i < newend->lineno) { + if (i < newend->lineno) edit_update(fileptr, CENTER); - } - else { + else edit_refresh(); - } - dump_buffer_reverse(fileptr); +#ifdef DEBUG + dump_buffer_reverse(); +#endif set_modified(); UNSET(KEEP_CUTBUFFER); - edit_refresh(); return 1; } diff --git a/faq.html b/faq.html index 8767c280..265ecb28 100644 --- a/faq.html +++ b/faq.html @@ -474,7 +474,7 @@ to have a completely consistent user interface across all user input functions. This means that regardless of whether you're being asked for a filename to insert or write, or a string to search for, the previous value is already inserted before the cursor. If you prefer the -old behavior, use the pico emulation mode (-p or --pico) or just hit +old behavior, use the Pico emulation mode (-p or --pico) or just hit Meta-P while in nano (see the ^G help text for more details). diff --git a/files.c b/files.c index 781ef0a5..bb4fdcd4 100644 --- a/files.c +++ b/files.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "proto.h" #include "nano.h" @@ -501,7 +502,9 @@ int do_insertfile(int loading_file) free(realname); +#ifdef DEBUG dump_buffer(fileage); +#endif #ifdef ENABLE_MULTIBUFFER if (loading_file) @@ -578,6 +581,75 @@ int do_insertfile_void(void) } #ifdef ENABLE_MULTIBUFFER +/* Create a new openfilestruct node. */ +openfilestruct *make_new_opennode(openfilestruct *prevnode) +{ + openfilestruct *newnode = nmalloc(sizeof(openfilestruct)); + + newnode->filename = NULL; + newnode->fileage = NULL; + newnode->filebot = NULL; + + newnode->prev = prevnode; + newnode->next = NULL; + + return newnode; +} + +/* Splice a node into an existing openfilestruct. */ +void splice_opennode(openfilestruct *begin, openfilestruct *newnode, + openfilestruct *end) +{ + newnode->next = end; + newnode->prev = begin; + begin->next = newnode; + if (end != NULL) + end->prev = newnode; +} + +/* Unlink a node from the rest of the openfilestruct. */ +void unlink_opennode(const openfilestruct *fileptr) +{ + assert(fileptr != NULL); + + if (fileptr->prev != NULL) + fileptr->prev->next = fileptr->next; + + if (fileptr->next != NULL) + fileptr->next->prev = fileptr->prev; +} + +/* Delete a node from the openfilestruct. */ +void delete_opennode(openfilestruct *fileptr) +{ + if (fileptr != NULL) { + if (fileptr->filename != NULL) + free(fileptr->filename); + if (fileptr->fileage != NULL) + free_filestruct(fileptr->fileage); + free(fileptr); + } +} + +/* Deallocate all memory associated with this and later files, + * including the lines of text. */ +void free_openfilestruct(openfilestruct *src) +{ + if (src != NULL) { + while (src->next != NULL) { + src = src->next; + delete_opennode(src->prev); +#ifdef DEBUG + fprintf(stderr, _("delete_opennode(): free'd a node, YAY!\n")); +#endif + } + delete_opennode(src); +#ifdef DEBUG + fprintf(stderr, _("delete_opennode(): free'd last node.\n")); +#endif + } +} + /* * Add/update an entry to the open_files openfilestruct. If update is * zero, a new entry is created; otherwise, the current entry is updated. @@ -1414,7 +1486,9 @@ int write_file(char *name, int tmp, int append, int nonamechange) } } +#ifdef DEBUG dump_buffer(fileage); +#endif f = fdopen(fd, append == 1 ? "ab" : "wb"); if (!f) { @@ -2742,20 +2816,11 @@ char *do_browser(char *inpath) /* Hilight the currently selected file/dir */ if (j == selected) { -#ifdef ENABLE_COLOR - color_on(edit, COLOR_STATUSBAR); -#else wattron(edit, A_REVERSE); - -#endif } waddnstr(edit, foo, strlen(foo)); if (j == selected) { -#ifdef ENABLE_COLOR - color_off(edit, COLOR_STATUSBAR); -#else wattroff(edit, A_REVERSE); -#endif } /* And add some space between the cols */ diff --git a/global.c b/global.c index 37b569ae..243e7067 100644 --- a/global.c +++ b/global.c @@ -37,6 +37,12 @@ * Global variables */ +/* wrap_at might be set in rcfile.c or nano.c */ +int wrap_at = -CHARS_FROM_EOL;/* Right justified fill value, allows resize */ +char *last_search = NULL; /* Last string we searched for */ +char *last_replace = NULL; /* Last replacement string */ +int search_last_line; /* Is this the last search line? */ + int flags = 0; /* Our new flag containing many options */ WINDOW *edit; /* The file portion of the editor */ WINDOW *topwin; /* Top line of screen */ @@ -66,10 +72,14 @@ openfilestruct *open_files = NULL; /* The list of open files */ #endif #ifndef DISABLE_JUSTIFY +#ifdef HAVE_REGEX_H +char *quotestr = "^([ \t]*[|>:}#])+"; +#else char *quotestr = "> "; /* Quote string */ #endif +#endif -char *answer = NULL; /* Answer str to many questions */ +char *answer = NULL; /* Answer str to many questions */ int totlines = 0; /* Total number of lines in the file */ long totsize = 0; /* Total number of bytes in the file */ int placewewant = 0; /* The column we'd like the cursor @@ -79,7 +89,9 @@ int placewewant = 0; /* The column we'd like the cursor int tabsize = 8; /* Our internal tabsize variable */ char *hblank; /* A horizontal blank line */ +#ifndef DISABLE_HELP char *help_text; /* The text in the help window */ +#endif /* More stuff for the marker select */ @@ -113,14 +125,13 @@ shortcut *browser_list = NULL; #endif #ifdef ENABLE_COLOR - colorstruct colors[NUM_NCOLORS]; colortype *colorstrings = NULL; syntaxtype *syntaxes = NULL; char *syntaxstr = NULL; #endif #if !defined(DISABLE_BROWSER) || !defined(DISABLE_MOUSE) || !defined(DISABLE_HELP) -shortcut *currshortcut; /* Current shortcut list we're using */ +const shortcut *currshortcut; /* Current shortcut list we're using */ #endif #ifndef NANO_SMALL @@ -133,23 +144,21 @@ toggle *toggles = NULL; regex_t search_regexp; /* Global to store compiled search regexp */ regmatch_t regmatches[10]; /* Match positions for parenthetical subexpressions, max of 10 */ +#endif #ifdef ENABLE_COLOR -regex_t color_regexp; /* Global to store compiled search regexp */ -regmatch_t colormatches[1]; /* Match positions for parenthetical */ +regex_t color_regexp; /* Global to store compiled search regexp */ +regmatch_t colormatches[1]; /* Match positions for parenthetical */ -regex_t syntaxfile_regexp; /* Global to store compiled search regexp */ -regmatch_t synfilematches[1]; /* Match positions for parenthetical */ +regex_t syntaxfile_regexp; /* Global to store compiled search regexp */ +regmatch_t synfilematches[1]; /* Match positions for parenthetical */ #endif /* ENABLE_COLOR */ -#endif int length_of_list(const shortcut *s) { int i = 0; - for (; s != NULL; s = s->next) i++; - return i; } @@ -211,17 +220,15 @@ static void toggle_init_one(int val, const char *desc, int flag) } #ifdef DEBUG -/* Deallocate all of the toggles */ +/* Deallocate all of the toggles. */ static void free_toggles(void) { - toggle *pt; /* Think "previous toggle" */ - while (toggles != NULL) { - pt = toggles; + toggle *pt = toggles; /* Think "previous toggle" */ + toggles = toggles->next; free(pt); } - toggles = NULL; } #endif @@ -229,10 +236,11 @@ static void toggle_init(void) { char *toggle_const_msg, *toggle_autoindent_msg, *toggle_suspend_msg, *toggle_nohelp_msg, *toggle_picomode_msg, *toggle_mouse_msg, - *toggle_cuttoend_msg, *toggle_wrap_msg, *toggle_noconvert_msg, - *toggle_dos_msg, *toggle_mac_msg, *toggle_backup_msg, - *toggle_smooth_msg; - + *toggle_cuttoend_msg, *toggle_noconvert_msg, *toggle_dos_msg, + *toggle_mac_msg, *toggle_backup_msg, *toggle_smooth_msg; +#ifndef DISABLE_WRAPPING + char *toggle_wrap_msg; +#endif #ifdef ENABLE_MULTIBUFFER char *toggle_load_msg; #endif @@ -256,7 +264,9 @@ static void toggle_init(void) toggle_mac_msg = _("Writing file in Mac format"); toggle_backup_msg = _("Backing up file"); toggle_smooth_msg = _("Smooth scrolling"); +#ifndef DISABLE_WRAPPING toggle_wrap_msg = _("Auto wrap"); +#endif #ifdef ENABLE_MULTIBUFFER toggle_load_msg = _("Multiple file buffers"); #endif @@ -266,7 +276,9 @@ static void toggle_init(void) toggle_init_one(TOGGLE_SUSPEND_KEY, toggle_suspend_msg, SUSPEND); toggle_init_one(TOGGLE_NOHELP_KEY, toggle_nohelp_msg, NO_HELP); toggle_init_one(TOGGLE_PICOMODE_KEY, toggle_picomode_msg, PICO_MODE); +#ifndef DISABLE_WRAPPING toggle_init_one(TOGGLE_WRAP_KEY, toggle_wrap_msg, NO_WRAP); +#endif toggle_init_one(TOGGLE_MOUSE_KEY, toggle_mouse_msg, USE_MOUSE); toggle_init_one(TOGGLE_CUTTOEND_KEY, toggle_cuttoend_msg, CUT_TO_END); #ifdef ENABLE_MULTIBUFFER @@ -283,16 +295,12 @@ static void toggle_init(void) /* Deallocate the given shortcut. */ static void free_shortcutage(shortcut **shortcutage) { - shortcut *s, *ps; - assert(shortcutage != NULL); - s = *shortcutage; - while (s != NULL) { - ps = s; - s = s->next; + while (*shortcutage != NULL) { + shortcut *ps = *shortcutage; + *shortcutage = (*shortcutage)->next; free(ps); } - *shortcutage = NULL; } void shortcut_init(int unjustify) @@ -432,7 +440,6 @@ void shortcut_init(int unjustify) #endif if (ISSET(PICO_MODE)) - #ifdef ENABLE_MULTIBUFFER /* this is so we can view multiple files */ sc_init_one(&main_list, NANO_INSERTFILE_KEY, _("Read File"), @@ -535,7 +542,7 @@ void shortcut_init(int unjustify) sc_init_one(&main_list, NANO_ENTER_KEY, _("Enter"), IFHELP(nano_enter_msg, 0), - KEY_ENTER, NANO_CONTROL_M, NOVIEW, do_enter_void); + KEY_ENTER, NANO_CONTROL_M, NOVIEW, do_enter); sc_init_one(&main_list, NANO_GOTO_KEY, _("Go To Line"), IFHELP(nano_goto_msg, NANO_ALT_GOTO_KEY), @@ -544,11 +551,11 @@ void shortcut_init(int unjustify) #ifndef NANO_SMALL sc_init_one(&main_list, NANO_NEXTWORD_KEY, _("Next Word"), IFHELP(_("Move forward one word"), 0), - 0, 0, VIEW, do_next_word_void); + 0, 0, VIEW, do_next_word); sc_init_one(&main_list, -9, _("Prev Word"), IFHELP(_("Move backward one word"), NANO_PREVWORD_KEY), 0, 0, - VIEW, do_prev_word_void); + VIEW, do_prev_word); #endif #if !defined(NANO_SMALL) && defined(HAVE_REGEX_H) sc_init_one(&main_list, -9, _("Find Other Bracket"), @@ -654,10 +661,10 @@ void shortcut_init(int unjustify) IFHELP(nano_cancel_msg, 0), 0, 0, VIEW, 0); sc_init_one(&goto_list, NANO_FIRSTLINE_KEY, _("First Line"), - IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, &do_first_line); + IFHELP(nano_firstline_msg, 0), 0, 0, VIEW, do_first_line); sc_init_one(&goto_list, NANO_LASTLINE_KEY, _("Last Line"), - IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, &do_last_line); + IFHELP(nano_lastline_msg, 0), 0, 0, VIEW, do_last_line); free_shortcutage(&help_list); @@ -781,11 +788,10 @@ void shortcut_init(int unjustify) /* This function is called just before calling exit(). Practically, the * only effect is to cause a segmentation fault if the various data * structures got bolloxed earlier. Thus, we don't bother having this - * function unless debugging is turned on. - */ + * function unless debugging is turned on. */ #ifdef DEBUG /* added by SPK for memory cleanup, gracefully return our malloc()s */ -void thanks_for_all_the_fish(void) +void thanks_for_all_the_fish(void) { #ifndef DISABLE_OPERATINGDIR if (operating_dir != NULL) @@ -816,15 +822,18 @@ void thanks_for_all_the_fish(void) free_shortcutage(&whereis_list); free_shortcutage(&replace_list); free_shortcutage(&replace_list_2); - free_shortcutage(&help_list); + free_shortcutage(&goto_list); + free_shortcutage(&gotodir_list); free_shortcutage(&writefile_list); free_shortcutage(&insertfile_list); + free_shortcutage(&help_list); free_shortcutage(&spell_list); +#ifndef NANO_SMALL + free_shortcutage(&extcmd_list); +#endif #ifndef DISABLE_BROWSER free_shortcutage(&browser_list); #endif - free_shortcutage(&gotodir_list); - free_shortcutage(&goto_list); #ifndef NANO_SMALL free_toggles(); @@ -833,19 +842,39 @@ void thanks_for_all_the_fish(void) #ifdef ENABLE_MULTIBUFFER if (open_files != NULL) { /* We free the memory associated with each open file. */ - openfilestruct *next; - while (open_files->prev != NULL) open_files = open_files->prev; - do { - next = open_files->next; - free_openfilestruct(open_files); - open_files = next; - } while (open_files != NULL); + free_openfilestruct(open_files); } #else if (fileage != NULL) free_filestruct(fileage); #endif + +#ifdef ENABLE_COLOR + free(syntaxstr); + while (syntaxes != NULL) { + syntaxtype *bill = syntaxes; + + free(syntaxes->desc); + while (syntaxes->extensions != NULL) { + exttype *bob = syntaxes->extensions; + + syntaxes->extensions = bob->next; + free(bob->val); + free(bob); + } + while (syntaxes->color != NULL) { + colortype *bob = syntaxes->color; + + syntaxes->color = bob->next; + free(bob->start); + free(bob->end); + free(bob); + } + syntaxes = syntaxes->next; + free(bill); + } +#endif /* ENABLE_COLOR */ } #endif /* DEBUG */ diff --git a/move.c b/move.c index 78766953..6f57d91f 100644 --- a/move.c +++ b/move.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "proto.h" #include "nano.h" @@ -34,23 +35,6 @@ #define _(string) (string) #endif -void page_down(void) -{ - if (!ISSET(SMOOTHSCROLL)) { - if (editbot != filebot) { - edit_update(editbot->next, CENTER); - center_cursor(); - } else { - while (current != filebot) - current = current->next; - edit_update(current, CENTER); - } - } else { - edit_update(editbot, NONE); - } - update_cursor(); -} - int do_page_down(void) { wrap_reset(); @@ -61,7 +45,7 @@ int do_page_down(void) 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! */ + edit_update, just move the cursor to editbot! */ if (edittop == fileage && editbot == filebot && totlines < editwinrows) { current = editbot; reset_cursor(); @@ -103,42 +87,18 @@ int do_end(void) current_x = strlen(current->data); placewewant = xplustabs(); update_line(current, current_x); - - return 1; -} - -/* What happens when we want to go past the bottom of the buffer */ -int do_down(void) -{ - wrap_reset(); - if (current->next != NULL) { - if (placewewant > 0) - current_x = actual_x(current->next, placewewant); - else if (current_x > strlen(current->next->data)) - current_x = strlen(current->next->data); - } else { - UNSET(KEEP_CUTBUFFER); - check_statblank(); - return 0; - } - - if (current_y < editwinrows - 1 && current != editbot) - current_y++; - else - page_down(); - - update_cursor(); - update_line(current->prev, 0); - update_line(current, current_x); - UNSET(KEEP_CUTBUFFER); - check_statblank(); return 1; } void page_up(void) { if (edittop != fileage) { - if (!ISSET(SMOOTHSCROLL)) { +#ifndef NANO_SMALL + if (ISSET(SMOOTHSCROLL)) + edit_update(edittop->prev, TOP); + else +#endif + { edit_update(edittop, CENTER); /* Now that we've updated the edit window, edittop might be at the top of the file; if so, just move the cursor up one @@ -149,8 +109,6 @@ void page_up(void) current = current->prev; reset_cursor(); } - } else { - edit_update(edittop->prev, NONE); } } else current_y = 0; @@ -186,35 +144,72 @@ int do_up(void) { wrap_reset(); if (current->prev != NULL) { - if (placewewant > 0) - current_x = actual_x(current->prev, placewewant); - else if (current_x > strlen(current->prev->data)) - current_x = strlen(current->prev->data); + current_x = actual_x(current->prev, placewewant); + current = current->prev; + if (current_y > 0) { + update_line(current->next, 0); + /* It is necessary to change current first, so the mark + display will change! */ + current_y--; + update_line(current, current_x); + } else + page_up(); + UNSET(KEEP_CUTBUFFER); + check_statblank(); } - if (current_y > 0) - current_y--; - else - page_up(); + return 1; +} - update_cursor(); - update_line(current->next, 0); - update_line(current, current_x); +/* Return value 1 means we moved down, 0 means we were already at the + * bottom. */ +int do_down(void) { + wrap_reset(); UNSET(KEEP_CUTBUFFER); check_statblank(); + + if (current->next == NULL) + return 0; + + current = current->next; + current_x = actual_x(current, placewewant); + + /* Note current_y is zero-based. This test checks for the cursor + * being on the last row of the edit window. */ + if (current_y == editwinrows - 1) { +#ifndef NANO_SMALL + if (ISSET(SMOOTHSCROLL)) { + /* In this case current_y does not change. The cursor + * remains at the bottom of the edit window. */ + edittop = edittop->next; + editbot = editbot->next; + edit_refresh(); + } else +#endif + { + edit_update(editbot->next, CENTER); + /* sets edittop so editbot->next is centered */ + center_cursor(); + /* sets current_y = editwinrows / 2 */ + } + } else { + update_line(current->prev, 0); + update_line(current, current_x); + current_y++; + } return 1; } int do_right(void) { - if (current_x < strlen(current->data)) { - current_x++; - } else { - if (do_down()) - current_x = 0; - } + 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_cursor(); update_line(current, current_x); UNSET(KEEP_CUTBUFFER); check_statblank(); @@ -226,13 +221,10 @@ int do_left(void) if (current_x > 0) current_x--; else if (current != fileage) { - placewewant = 0; - current_x = strlen(current->prev->data); do_up(); + current_x = strlen(current->data); } placewewant = xplustabs(); - - update_cursor(); update_line(current, current_x); UNSET(KEEP_CUTBUFFER); check_statblank(); diff --git a/nano.1 b/nano.1 index e4852ae3..284a10aa 100644 --- a/nano.1 +++ b/nano.1 @@ -6,31 +6,31 @@ .\" Public License for copying conditions. There is NO warranty. .\" .\" $Id$ -.TH NANO 1 "June 27, 2002" +.TH NANO 1 "July 6, 2002" .\" Please adjust this date whenever revising the manpage. .\" .SH NAME nano \- Nano's ANOther editor, an enhanced free Pico Clone .SH SYNOPSIS .B nano -.RI [options]\ [+LINE] " file" +.I [\+LINE]\ [options]\ [file] .br .SH DESCRIPTION -This manual page documents briefly the -.B nano -command. +This manual page documents briefly the \fBnano\fP command. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invoke bold face and italics, .\" respectively. \fBnano\fP is a small, free and friendly editor which aims to replace Pico, the default editor included in the non-free Pine package. Rather -than just copying Pico's look and feel, -.B nano -also implements some missing (or disabled by default) features in Pico, -such as "search and replace" and "goto line number". +than just copying Pico's look and feel, \fBnano\fP also implements some +missing (or disabled by default) features in Pico, such as "search and +replace" and "go to line number". .SH OPTIONS .TP +.B \+\fILINE\fP +Places cursor at \fILINE\fP on startup. +.TP .B \-B (\-\-backup) When saving a file, back up the previous version of it to the current filename suffixed with a ~. @@ -52,7 +52,7 @@ Write file in Mac format. .B \-N (\-\-noconvert) Disable automatic conversion of files from DOS/Mac format. .TP -.B \-Q [str] (\-\-quotestr=[str]) +.B \-Q \fI[str]\fP (\-\-quotestr=\fI[str]\fP) Set the quoting string for justifying. The default is "> ". .TP .B \-R (\-\-regexp) @@ -61,15 +61,15 @@ Enable regular expression matching for search strings, as well as .TP .B \-S (\-\-smooth) Enable smooth scrolling. Text will scroll line-by-line, instead of the -usual chunk-by-chunk behaviour. +usual chunk-by-chunk behavior. .TP -.B \-T [num] (\-\-tabsize=[num]) +.B \-T \fI[num]\fP (\-\-tabsize=\fI[num]\fP) Set the size (width) of a tab. .TP .B \-V (\-\-version) Show the current version number and author. .TP -.B \-Y (\-\-syntax=[str]) +.B \-Y \fI[str]\fP (\-\-syntax=\fI[str]\fP) Specify a specific syntax highlighting from the .nanorc to use, if available. .TP @@ -93,7 +93,7 @@ a new file, do not follow it. Good for editing files in /tmp, perhaps? .B \-m (\-\-mouse) Enable mouse support (if available for your system). .TP -.B \-o [dir] (\-\-operatingdir=[dir]) +.B \-o \fI[dir]\fP (\-\-operatingdir=\fI[dir]\fP) Set operating directory. Makes nano set up something similar to a chroot. .TP @@ -102,13 +102,13 @@ Emulate Pico as closely as possible. This affects both the "shortcut list" at the bottom of the screen, as well as the display and entry of previous search and replace strings. .TP -.B \-r [#cols] (\-\-fill=[#cols]) +.B \-r \fI[#cols]\fP (\-\-fill=\fI[#cols]\fP) Wrap lines at column #cols. By default, this is the width of the screen, less eight. If this value is negative, wrapping will occur at #cols from the right of the screen, allowing it to vary along with the screen width if the screen is resized. .TP -.B \-s [prog] (\-\-speller=[prog]) +.B \-s \fI[prog]\fP (\-\-speller=\fI[prog]\fP) Enable alternative spell checker command. .TP .B \-t (\-\-tempfile) @@ -128,25 +128,22 @@ Enable suspend ability. .TP .B \-a, \-b, \-e, \-f, \-g, \-j Ignored, for compatibility with Pico. -.TP -.B \+LINE -Places cursor at LINE on startup. .SH NOTES -Nano will try to dump the buffer into an emergency file in some cases. -Mainly, this will happen if Nano receives a SIGHUP or runs out of -memory, when it will write the buffer into a file named "nano.save" if -the buffer didn't have a name already, or will add a ".save" suffix to -the current filename. If an emergency file with that name already exists -in the current directory, ".save" and a number (e.g. ".save.1") will be -suffixed to the current filename in order to make it unique. In -multibuffer mode, nano will write all the open buffers to the respective -emergency files. +\fBnano\fP will try to dump the buffer into an emergency file in some +cases. Mainly, this will happen if \fBnano\fP receives a SIGHUP or runs +out of memory, when it will write the buffer into a file named +"nano.save" if the buffer didn't have a name already, or will add a +".save" suffix to the current filename. If an emergency file with that +name already exists in the current directory, ".save" and a number (e.g. +".save.1") will be suffixed to the current filename in order to make it +unique. In multibuffer mode, \fBnano\fP will write all the open buffers +to the respective emergency files. .SH BUGS Please send any comments or bug reports to .br .B nano@nano-editor.org. .br -The nano mailing list is available from +The \fBnano\fP mailing list is available from .br .B nano-devel@gnu.org. To subscribe, email to nano-devel-request@gnu.org with a diff --git a/nano.1.html b/nano.1.html index 86aaaa18..6f494ae7 100644 --- a/nano.1.html +++ b/nano.1.html @@ -3,7 +3,7 @@ Content-type: text/html Manpage of NANO

NANO

-Section: User Commands (1)
Updated: June 27, 2002
Index +Section: User Commands (1)
Updated: July 5, 2002
Index Return to Main Contents
@@ -17,17 +17,14 @@ nano - Nano's ANOther editor, an enhanced free Pico Clone nano -[options] [+LINE] file +[+LINE] [options] [file]
 

DESCRIPTION

-This manual page documents briefly the -nano - -command. +This manual page documents briefly the nano command.

@@ -35,15 +32,17 @@ command. nano is a small, free and friendly editor which aims to replace Pico, the default editor included in the non-free Pine package. Rather -than just copying Pico's look and feel, -nano - -also implements some missing (or disabled by default) features in Pico, -such as "search and replace" and "goto line number". +than just copying Pico's look and feel, nano also implements some +missing (or disabled by default) features in Pico, such as "search and +replace" and "go to line number".  

OPTIONS

+
+LINE + +
+Places cursor at LINE on startup.
-B (--backup)
@@ -71,7 +70,7 @@ Write file in Mac format.
Disable automatic conversion of files from DOS/Mac format. -
-Q [str] (--quotestr=[str]) +
-Q [str] (--quotestr=[str])
Set the quoting string for justifying. The default is "> ". @@ -84,8 +83,8 @@ Enable regular expression matching for search strings, as well as
Enable smooth scrolling. Text will scroll line-by-line, instead of the -usual chunk-by-chunk behaviour. -
-T [num] (--tabsize=[num]) +usual chunk-by-chunk behavior. +
-T [num] (--tabsize=[num])
Set the size (width) of a tab. @@ -93,7 +92,7 @@ Set the size (width) of a tab.
Show the current version number and author. -
-Y (--syntax=[str]) +
-Y [str] (--syntax=[str])
Specify a specific syntax highlighting from the .nanorc to use, if @@ -124,7 +123,7 @@ a new file, do not follow it. Good for editing files in /tmp, perhaps?
Enable mouse support (if available for your system). -
-o [dir] (--operatingdir=[dir]) +
-o [dir] (--operatingdir=[dir])
Set operating directory. Makes nano set up something similar to a @@ -135,14 +134,14 @@ chroot. Emulate Pico as closely as possible. This affects both the "shortcut list" at the bottom of the screen, as well as the display and entry of previous search and replace strings. -
-r [#cols] (--fill=[#cols]) +
-r [#cols] (--fill=[#cols])
Wrap lines at column #cols. By default, this is the width of the screen, less eight. If this value is negative, wrapping will occur at #cols from the right of the screen, allowing it to vary along with the screen width if the screen is resized. -
-s [prog] (--speller=[prog]) +
-s [prog] (--speller=[prog])
Enable alternative spell checker command. @@ -170,23 +169,19 @@ Enable suspend ability.
Ignored, for compatibility with Pico. -
+LINE - -
-Places cursor at LINE on startup.
 

NOTES

-Nano will try to dump the buffer into an emergency file in some cases. -Mainly, this will happen if Nano receives a SIGHUP or runs out of -memory, when it will write the buffer into a file named "nano.save" if -the buffer didn't have a name already, or will add a ".save" suffix to -the current filename. If an emergency file with that name already exists -in the current directory, ".save" and a number (e.g. ".save.1") will be -suffixed to the current filename in order to make it unique. In -multibuffer mode, nano will write all the open buffers to the respective -emergency files. +nano will try to dump the buffer into an emergency file in some +cases. Mainly, this will happen if nano receives a SIGHUP or runs +out of memory, when it will write the buffer into a file named +"nano.save" if the buffer didn't have a name already, or will add a +".save" suffix to the current filename. If an emergency file with that +name already exists in the current directory, ".save" and a number (e.g. +".save.1") will be suffixed to the current filename in order to make it +unique. In multibuffer mode, nano will write all the open buffers +to the respective emergency files.  

BUGS

@@ -197,7 +192,7 @@ Please send any comments or bug reports to
-The nano mailing list is available from +The nano mailing list is available from
nano-devel@gnu.org. @@ -234,6 +229,6 @@ used by others). This document was created by man2html, using the manual pages.
-Time: 21:45:15 GMT, June 27, 2002 +Time: 03:38:25 GMT, July 06, 2002 diff --git a/nano.c b/nano.c index 104e35cd..7e17c345 100644 --- a/nano.c +++ b/nano.c @@ -62,24 +62,18 @@ #endif #ifndef DISABLE_WRAPJUSTIFY -/* Former globals, now static */ -int fill = 0;/* Fill - where to wrap lines, basically */ -int wrap_at = 0;/* Right justified fill value, allows resize */ +/* Former global, now static */ +static int fill = 0; /* Fill - where to wrap lines, basically */ #endif -struct termios oldterm; /* The user's original term settings */ +static struct termios oldterm; /* The user's original term settings */ static struct sigaction act; /* For all our fun signal handlers */ -char *last_search = NULL; /* Last string we searched for */ -char *last_replace = NULL; /* Last replacement string */ -int search_last_line; /* Is this the last search line? */ - static sigjmp_buf jmpbuf; /* Used to return to mainloop after SIGWINCH */ /* What we do when we're all set to exit */ -RETSIGTYPE finish(int sigage) +static RETSIGTYPE finish(int sigage) { - keypad(edit, TRUE); keypad(bottomwin, TRUE); @@ -103,14 +97,10 @@ RETSIGTYPE finish(int sigage) } /* Die (gracefully?) */ -void die(char *msg, ...) +void die(const char *msg, ...) { va_list ap; - va_start(ap, msg); - vfprintf(stderr, msg, ap); - va_end(ap); - /* Restore the old term settings */ tcsetattr(0, TCSANOW, &oldterm); @@ -119,7 +109,9 @@ void die(char *msg, ...) resetty(); endwin(); - fprintf(stderr, msg); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); /* save the currently loaded file if it's been modified */ if (ISSET(MODIFIED)) @@ -150,57 +142,47 @@ void die(char *msg, ...) if (open_files->file_flags & MODIFIED) die_save_file(open_files->filename); } - open_files = open_files->next; } - } #endif - exit(1); /* We have a problem: exit w/ errorlevel(1) */ + exit(1); /* We have a problem: exit w/ errorlevel(1) */ } -void die_save_file(char *die_filename) +void die_save_file(const char *die_filename) { - char *name, *ret; + char *ret; int i = -1; - /* if we can't save we have REAL bad problems, - * but we might as well TRY. */ - if (die_filename[0] == '\0') { - name = "nano.save"; - ret = get_next_filename(name); - if (ret[0] != '\0') - i = write_file(ret, 1, 0, 0); - name = ret; - } + /* If we can't save, we have REAL bad problems, but we might as well + TRY. */ + if (die_filename[0] == '\0') + ret = get_next_filename("nano.save"); else { char *buf = charalloc(strlen(die_filename) + 6); + strcpy(buf, die_filename); strcat(buf, ".save"); ret = get_next_filename(buf); - if (ret[0] != '\0') - i = write_file(ret, 1, 0, 0); - name = ret; free(buf); } + if (ret[0] != '\0') + i = write_file(ret, 1, 0, 0); if (i != -1) - fprintf(stderr, _("\nBuffer written to %s\n"), name); + fprintf(stderr, _("\nBuffer written to %s\n"), ret); else - fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), name); + fprintf(stderr, _("\nNo %s written (too many backup files?)\n"), ret); free(ret); } /* Die with an error message that the screen was too small if, well, the - screen is too small */ -void die_too_small(void) + * screen is too small. */ +static void die_too_small(void) { - char *too_small_msg = _("Window size is too small for Nano..."); - - die(too_small_msg); - + die(_("Window size is too small for nano...\n")); } void print_view_warning(void) @@ -209,8 +191,8 @@ void print_view_warning(void) } /* Initialize global variables - no better way for now. If - save_cutbuffer is nonzero, don't set cutbuffer to NULL. */ -void global_init(int save_cutbuffer) + * save_cutbuffer is nonzero, don't set cutbuffer to NULL. */ +static void global_init(int save_cutbuffer) { current_x = 0; current_y = 0; @@ -229,11 +211,9 @@ void global_init(int save_cutbuffer) placewewant = 0; #ifndef DISABLE_WRAPJUSTIFY - if (wrap_at) - fill = COLS + wrap_at; - else if (!fill) - fill = COLS - CHARS_FROM_EOL; - + fill = wrap_at; + if (fill < 0) + fill += COLS; if (fill < MIN_FILL_LENGTH) die_too_small(); #endif @@ -241,20 +221,18 @@ void global_init(int save_cutbuffer) hblank = charalloc(COLS + 1); memset(hblank, ' ', COLS); hblank[COLS] = '\0'; - } /* Make a copy of a node to a pointer (space will be malloc()ed). */ -filestruct *copy_node(filestruct * src) +filestruct *copy_node(const filestruct *src) { - filestruct *dst; + filestruct *dst = (filestruct *)nmalloc(sizeof(filestruct)); + + assert(src != NULL); - dst = nmalloc(sizeof(filestruct)); dst->data = charalloc(strlen(src->data) + 1); - dst->next = src->next; dst->prev = src->prev; - strcpy(dst->data, src->data); dst->lineno = src->lineno; @@ -262,8 +240,10 @@ filestruct *copy_node(filestruct * src) } /* Unlink a node from the rest of the filestruct. */ -void unlink_node(filestruct * fileptr) +void unlink_node(const filestruct *fileptr) { + assert(fileptr != NULL); + if (fileptr->prev != NULL) fileptr->prev->next = fileptr->next; @@ -271,173 +251,86 @@ void unlink_node(filestruct * fileptr) fileptr->next->prev = fileptr->prev; } -#ifdef ENABLE_MULTIBUFFER -/* Unlink a node from the rest of the openfilestruct. */ -void unlink_opennode(openfilestruct * fileptr) -{ - if (fileptr->prev != NULL) - fileptr->prev->next = fileptr->next; - - if (fileptr->next != NULL) - fileptr->next->prev = fileptr->prev; -} -#endif - /* Delete a node from the filestruct. */ -void delete_node(filestruct * fileptr) +void delete_node(filestruct *fileptr) { - if (fileptr == NULL) - return; - - if (fileptr->data != NULL) - free(fileptr->data); - free(fileptr); + if (fileptr != NULL) { + if (fileptr->data != NULL) + free(fileptr->data); + free(fileptr); + } } -#ifdef ENABLE_MULTIBUFFER -/* Delete a node from the openfilestruct. */ -void delete_opennode(openfilestruct * fileptr) -{ - if (fileptr == NULL) - return; - - if (fileptr->filename != NULL) - free(fileptr->filename); - if (fileptr->fileage != NULL) - free_filestruct(fileptr->fileage); - free(fileptr); -} -#endif - /* Okay, now let's duplicate a whole struct! */ -filestruct *copy_filestruct(filestruct * src) +filestruct *copy_filestruct(const filestruct *src) { - filestruct *dst, *tmp, *head, *prev; + filestruct *head; /* copy of src, top of the copied list */ + filestruct *prev; /* temp that traverses the list */ - head = copy_node(src); - dst = head; /* Else we barf on copying just one line */ - head->prev = NULL; - tmp = src->next; - prev = head; + assert(src != NULL); - while (tmp != NULL) { - dst = copy_node(tmp); - dst->prev = prev; - prev->next = dst; + prev = copy_node(src); + prev->prev = NULL; + head = prev; + src = src->next; + while (src != NULL) { + prev->next = copy_node(src); + prev->next->prev = prev; + prev = prev->next; - prev = dst; - tmp = tmp->next; + src = src->next; } - dst->next = NULL; + prev->next = NULL; return head; } /* Frees a filestruct. */ -int free_filestruct(filestruct * src) +void free_filestruct(filestruct *src) { - filestruct *fileptr = src; - - if (src == NULL) - return 0; - - while (fileptr->next != NULL) { - fileptr = fileptr->next; - delete_node(fileptr->prev); - + if (src != NULL) { + while (src->next != NULL) { + src = src->next; + delete_node(src->prev); #ifdef DEBUG - fprintf(stderr, _("delete_node(): free'd a node, YAY!\n")); + fprintf(stderr, _("delete_node(): free'd a node, YAY!\n")); +#endif + } + delete_node(src); +#ifdef DEBUG + fprintf(stderr, _("delete_node(): free'd last node.\n")); #endif } - delete_node(fileptr); -#ifdef DEBUG - fprintf(stderr, _("delete_node(): free'd last node.\n")); -#endif - - return 1; } -#ifdef ENABLE_MULTIBUFFER -/* Frees an openfilestruct. */ -int free_openfilestruct(openfilestruct * src) -{ - openfilestruct *fileptr = src; - - if (src == NULL) - return 0; - - while (fileptr->next != NULL) { - fileptr = fileptr->next; - delete_opennode(fileptr->prev); - -#ifdef DEBUG - fprintf(stderr, _("delete_opennode(): free'd a node, YAY!\n")); -#endif - } - delete_opennode(fileptr); -#ifdef DEBUG - fprintf(stderr, _("delete_opennode(): free'd last node.\n")); -#endif - - return 1; -} -#endif - -int renumber_all(void) +void renumber_all(void) { filestruct *temp; int i = 1; - assert(fileage==NULL || fileage!=fileage->next); - for (temp = fileage; temp != NULL; temp = temp->next) { + assert(fileage == NULL || fileage != fileage->next); + for (temp = fileage; temp != NULL; temp = temp->next) temp->lineno = i++; - } - - return 0; } -int renumber(filestruct * fileptr) +void renumber(filestruct *fileptr) { - filestruct *temp; - - if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage) { + if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage) renumber_all(); - return 0; + else { + int lineno = fileptr->prev->lineno; + + assert(fileptr != fileptr->next); + for (; fileptr != NULL; fileptr = fileptr->next) + fileptr->lineno = ++lineno; } - assert(fileptr==NULL || fileptr!=fileptr->next); - for (temp = fileptr; temp != NULL; temp = temp->next) { - if (temp->prev != NULL) - temp->lineno = temp->prev->lineno + 1; - else - temp->lineno = 1; - } - - return 0; } -/* Fix the memory allocation for a string */ -void align(char **strp) -{ - /* There was a serious bug here: the new address was never - stored anywhere... */ - - *strp = nrealloc(*strp, strlen(*strp) + 1); -} - -/* Null a string at a certain index and align it */ -void null_at(char **data, int index) -{ - - /* Ahh! Damn dereferencing */ - (*data)[index] = '\0'; - align(data); -} - - /* Print one usage string to the screen, removes lots of duplicate - strings to translate and takes out the parts that shouldn't be - translatable (the flag names) */ -void print1opt(char *shortflag, char *longflag, char *desc) + * strings to translate and takes out the parts that shouldn't be + * translatable (the flag names). */ +static void print1opt(const char *shortflag, const char *longflag, + const char *desc) { printf(" %s\t", shortflag); if (strlen(shortflag) < 8) @@ -454,23 +347,27 @@ void print1opt(char *shortflag, char *longflag, char *desc) printf("%s\n", desc); } -void usage(void) +static void usage(void) { #ifdef HAVE_GETOPT_LONG - printf(_("Usage: nano [GNU long option] [option] +LINE \n\n")); - printf(_("Option Long option Meaning\n")); + printf(_("Usage: nano [+LINE] [GNU long option] [option] [file]\n\n")); + printf(_("Option\t\tLong option\t\tMeaning\n")); #else - printf(_("Usage: nano [option] +LINE \n\n")); - printf(_("Option Meaning\n")); + printf(_("Usage: nano [+LINE] [option] [file]\n\n")); + printf(_("Option\t\tMeaning\n")); #endif /* HAVE_GETOPT_LONG */ print1opt("-h, -?", "--help", _("Show this message")); + print1opt(_("+LINE"), "", _("Start at line number LINE")); #ifndef NANO_SMALL print1opt("-B", "--backup", _("Backup existing files on save")); print1opt("-D", "--dos", _("Write file in DOS format")); #endif #ifdef ENABLE_MULTIBUFFER print1opt("-F", "--multibuffer", _("Enable multiple file buffers")); +#endif +#ifdef ENABLE_NANORC + print1opt("-I", "--ignorercfiles", _("Don't look at nanorc files")); #endif print1opt("-K", "--keypad", _("Use alternate keypad routines")); #ifndef NANO_SMALL @@ -478,7 +375,7 @@ void usage(void) print1opt("-N", "--noconvert", _("Don't convert files from DOS/Mac format")); #endif #ifndef DISABLE_JUSTIFY - print1opt(_("-Q [str]"), _("--quotestr [str]"), _("Quoting string, default \"> \"")); + print1opt(_("-Q [str]"), _("--quotestr=[str]"), _("Quoting string, default \"> \"")); #endif #ifdef HAVE_REGEX_H print1opt("-R", "--regexp", _("Do regular expression searches")); @@ -519,12 +416,14 @@ void usage(void) #endif print1opt("-x", "--nohelp", _("Don't show help window")); print1opt("-z", "--suspend", _("Enable suspend")); - print1opt(_("+LINE"), "", _("Start at line number LINE")); + + /* this is a special case */ + printf(" %s\t\t\t%s\n","-a, -b, -e, -f, -g, -j", _("(ignored, for Pico compatibility)")); exit(0); } -void version(void) +static void version(void) { printf(_(" GNU nano version %s (compiled %s, %s)\n"), VERSION, __TIME__, __DATE__); @@ -570,7 +469,6 @@ void version(void) printf(" --disable-operatingdir"); #endif #endif /* NANO_SMALL */ - #ifdef DISABLE_WRAPPING printf(" --disable-wrapping"); #endif @@ -578,68 +476,35 @@ void version(void) printf(" --with-slang"); #endif printf("\n"); - } -/* Create a new filestruct node. */ -filestruct *make_new_node(filestruct * prevnode) +/* 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 *newnode = (filestruct *)nmalloc(sizeof(filestruct)); - newnode = nmalloc(sizeof(filestruct)); newnode->data = NULL; - newnode->prev = prevnode; newnode->next = NULL; - - if (prevnode != NULL) - newnode->lineno = prevnode->lineno + 1; + newnode->lineno = prevnode != NULL ? prevnode->lineno + 1 : 1; return newnode; } -#ifdef ENABLE_MULTIBUFFER -/* Create a new openfilestruct node. */ -openfilestruct *make_new_opennode(openfilestruct * prevnode) -{ - openfilestruct *newnode; - - newnode = nmalloc(sizeof(openfilestruct)); - newnode->filename = NULL; - newnode->fileage = NULL; - newnode->filebot = NULL; - - newnode->prev = prevnode; - newnode->next = NULL; - - return newnode; -} -#endif - /* Splice a node into an existing filestruct. */ -void splice_node(filestruct * begin, filestruct * newnode, - filestruct * end) +void splice_node(filestruct *begin, filestruct *newnode, filestruct *end) { - newnode->next = end; - newnode->prev = begin; - begin->next = newnode; + if (newnode != NULL) { + newnode->next = end; + newnode->prev = begin; + } + if (begin != NULL) + begin->next = newnode; if (end != NULL) end->prev = newnode; } -#ifdef ENABLE_MULTIBUFFER -/* Splice a node into an existing openfilestruct. */ -void splice_opennode(openfilestruct * begin, openfilestruct * newnode, - openfilestruct * end) -{ - newnode->next = end; - newnode->prev = begin; - begin->next = newnode; - if (end != NULL) - end->prev = newnode; -} -#endif - int do_mark(void) { #ifdef NANO_SMALL @@ -653,9 +518,6 @@ int do_mark(void) } else { statusbar(_("Mark UNset")); UNSET(MARK_ISSET); - mark_beginbuf = NULL; - mark_beginx = 0; - edit_refresh(); } #endif @@ -664,24 +526,27 @@ int do_mark(void) int no_help(void) { - if ISSET - (NO_HELP) - return 2; - else - return 0; + return ISSET(NO_HELP) ? 2 : 0; } #if defined(DISABLE_JUSTIFY) || defined(DISABLE_SPELLER) || \ defined(DISABLE_HELP) || defined(NANO_SMALL) void nano_disabled_msg(void) { - statusbar("Sorry, support for this function has been disabled"); + statusbar(_("Sorry, support for this function has been disabled")); } #endif -/* The user typed a printable character; add it to the edit buffer */ +/* 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') { @@ -689,56 +554,65 @@ void do_char(char ch) fix_editbot(); } - /* More dangerousness fun =) */ - current->data = nrealloc(current->data, strlen(current->data) + 2); + /* more dangerousness fun =) */ + current->data = nrealloc(current->data, current_len + 2); + assert(current_x <= current_len); memmove(¤t->data[current_x + 1], ¤t->data[current_x], - strlen(current->data) - current_x + 1); + current_len - current_x + 1); current->data[current_x] = ch; - do_right(); + totsize++; + set_modified(); - /* note that current_x has already been incremented */ - if (current == mark_beginbuf && mark_beginx >= current_x) +#ifndef NANO_SMALL + /* note that current_x has not yet been incremented */ + if (current == mark_beginbuf && current_x < mark_beginx) mark_beginx++; - -#ifdef ENABLE_COLOR - edit_refresh(); #endif + do_right(); + #ifndef DISABLE_WRAPPING if (!ISSET(NO_WRAP) && (ch != '\t')) - check_wrap(current); + refresh = do_wrap(current); +#endif + +#ifdef ENABLE_COLOR + refresh = 1; +#endif + +#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR) + if (refresh) + edit_refresh(); #endif - set_modified(); check_statblank(); UNSET(KEEP_CUTBUFFER); - totsize++; - } /* Someone hits return *gasp!* */ -int do_enter(filestruct * inptr) +int do_enter(void) { filestruct *newnode; char *tmp; - newnode = make_new_node(inptr); - assert(current->data != NULL); + newnode = make_new_node(current); + assert(current != NULL && current->data != NULL); tmp = ¤t->data[current_x]; #ifndef NANO_SMALL - /* Do auto-indenting, like the neolithic Turbo Pascal editor */ + /* Do auto-indenting, like the neolithic Turbo Pascal editor. */ if (ISSET(AUTOINDENT)) { int extra = 0; - char *spc = current->data; - while ((*spc == ' ') || (*spc == '\t')) { + const char *spc = current->data; + + while (*spc == ' ' || *spc == '\t') { extra++; spc++; } - /* If the cursor is in the indentation, only indent to the cursor. - * Ex, if cursor is at col 0, don't indent at all. - */ + /* 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 @@ -755,13 +629,13 @@ int do_enter(filestruct * inptr) newnode->data = charalloc(strlen(tmp) + 1); strcpy(newnode->data, tmp); } - *tmp = 0; + *tmp = '\0'; - if (inptr->next == NULL) { + if (current->next == NULL) { filebot = newnode; editbot = newnode; } - splice_node(inptr, newnode, inptr->next); + splice_node(current, newnode, current->next); totsize++; renumber(current); @@ -790,155 +664,120 @@ int do_enter(filestruct * inptr) return 1; } -int do_enter_void(void) -{ - return do_enter(current); -} - #ifndef NANO_SMALL -void do_next_word(void) +int do_next_word(void) { - filestruct *fileptr, *old; - int i; + filestruct *old = current; - if (current == NULL) - return; + assert(current != NULL && current->data != NULL); - old = current; - i = current_x; - for (fileptr = current; fileptr != NULL; fileptr = fileptr->next) { - if (fileptr == current) { - while (isalnum((int) fileptr->data[i]) - && fileptr->data[i] != 0) - i++; + /* Skip letters in this word first. */ + while (current->data[current_x] != '\0' && + isalnum((int)current->data[current_x])) + current_x++; - if (fileptr->data[i] == 0) { - i = 0; - continue; - } - } - while (!isalnum((int) fileptr->data[i]) && fileptr->data[i] != 0) - i++; + for (; current != NULL; current = current->next) { + while (current->data[current_x] != '\0' && + !isalnum((int)current->data[current_x])) + current_x++; - if (fileptr->data[i] != 0) + if (current->data[current_x] != '\0') break; - i = 0; + current_x = 0; } - if (fileptr == NULL) + if (current == NULL) current = filebot; - else - current = fileptr; - current_x = i; placewewant = xplustabs(); if (current->lineno >= editbot->lineno) 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) + /* 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); } -} - -int do_next_word_void(void) { - do_next_word(); return 0; } -/* the same thing for backwards */ -void do_prev_word(void) +/* The same thing for backwards. */ +int do_prev_word(void) { - filestruct *fileptr, *old; - int i; + filestruct *old = current; - if (current == NULL) - return; + assert(current != NULL); - old = current; - i = current_x; - for (fileptr = current; fileptr != NULL; fileptr = fileptr->prev) { - if (fileptr == current) { - while (isalnum((int) fileptr->data[i]) - && i != 0) - i--; + /* Skip letters in this word first. */ + while (current_x >= 0 && isalnum((int)current->data[current_x])) + current_x--; - if (i == 0) { - if (fileptr->prev != NULL) - i = strlen(fileptr->prev->data); - else if (fileptr == fileage && filebot != NULL) { - current_x = 0; - return; - } - continue; - } - } + for (; current != NULL; current = current->prev) { + while (current_x >= 0 && !isalnum((int)current->data[current_x])) + current_x--; - while (!isalnum((int) fileptr->data[i]) && i != 0) - i--; + if (current_x >= 0) + break; - if (i > 0) { - i--; - - while (isalnum((int) fileptr->data[i]) && i != 0) - i--; - - if (!isalnum((int) fileptr->data[i])) - i++; - - if (i != 0 || i != current_x) - break; - - } - if (fileptr->prev != NULL) - i = strlen(fileptr->prev->data); - else if (fileptr == fileage && filebot != NULL) { - current_x = 0; - return; - } + if (current->prev != NULL) + current_x = strlen(current->prev->data); + } + + if (current != NULL) { + while (current_x > 0 && isalnum((int)current->data[current_x - 1])) + current_x--; + } else { + current = fileage; + current_x = 0; } - if (fileptr == NULL) - current = fileage; - else - current = fileptr; - current_x = i; placewewant = xplustabs(); if (current->lineno <= edittop->lineno) 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) + /* If we've jumped lines, refresh the old line. We can't just + use current->prev here, because we may have skipped over some + blank lines, in which case the previous line is the wrong + one. */ + if (current != old) { update_line(old, 0); - + /* If the mark was set, then the lines between old and + current have to be updated too. */ + if (ISSET(MARK_ISSET)) { + while (old->prev != current) { + old = old->prev; + update_line(old, 0); + } + } + } update_line(current, current_x); } - -} - -int do_prev_word_void(void) { - do_prev_word(); 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. */ -void do_wrap(filestruct * inptr) + * moved forward since the last typed character. Return value: + * whether we wrapped. */ +int do_wrap(filestruct *inptr) { - int len = strlen(inptr->data); /* length of the line we wrap */ - int i; /* generic loop variable */ + 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 @@ -956,9 +795,9 @@ void do_wrap(filestruct * inptr) /* There are three steps. First, we decide where to wrap. Then, we * create the new wrap line. Finally, we clean up. */ - /* We need the following assertion, since otherwise we would wrap the - * last word to the next line regardless. */ - assert(strlenpt(inptr->data) > fill); + /* Is it necessary to do anything? */ + if (strlenpt(inptr->data) <= fill) + return 0; /* 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 @@ -982,33 +821,28 @@ void do_wrap(filestruct * inptr) * the next loop calling isspace(inptr->data[i]). Oh well, fixing the * first point would entail expanding the definition of strnlenpt, which * I won't do since it will probably change soon. Fixing the second - * point would entail nested loops. - */ + * point would entail nested loops. */ - i = 0; #ifndef NANO_SMALL - if (ISSET(AUTOINDENT)) { - while (inptr->data[i] == ' ' || inptr->data[i] == '\t') - i++; - } + if (ISSET(AUTOINDENT)) + i = indent_length(inptr->data); #endif - for(; idata[i])) + if (!isspace((int)inptr->data[i])) 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) > fill) break; /* we record the latest "legal wrap point" */ - if (i != (current_x - 1) && isspace((int) inptr->data[i]) && + if (i != (current_x - 1) && isspace((int)inptr->data[i]) && (i == (len - 1) || !isspace((int)inptr->data[i + 1]))) { wrap_loc = i; } } if (wrap_loc < 0 || wrap_loc == (len - 1)) - return; - + return 0; /* Step 2, making the new wrap line. It will consist of indentation + * after_break + " " + wrap_line (although indentation and wrap_line are @@ -1042,9 +876,7 @@ void do_wrap(filestruct * inptr) /* indentation comes from the next line if wrapping, else from * this line */ indentation = (wrapping ? wrap_line : inptr->data); - while (indentation[indent_len] == ' ' || - indentation[indent_len] == '\t') - indent_len++; + 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. */ @@ -1059,8 +891,10 @@ void do_wrap(filestruct * inptr) *newline = '\0'; #ifndef NANO_SMALL - if (ISSET(AUTOINDENT)) + if (ISSET(AUTOINDENT)) { strncpy(newline, indentation, indent_len); + newline[indent_len] = '\0'; + } #endif strcat(newline, after_break); after_break = NULL; @@ -1076,6 +910,7 @@ void do_wrap(filestruct * inptr) 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. */ @@ -1095,7 +930,6 @@ void do_wrap(filestruct * inptr) filebot = temp; } - /* Step 3, clean up. Here we reposition the cursor and mark, and do some * other sundry things. */ @@ -1106,93 +940,43 @@ void do_wrap(filestruct * inptr) * inserted a new line. */ if (!wrapping) renumber(inptr); - edit_update(edittop, TOP); - /* if the cursor was after the break point, we must move it */ + /* If the cursor was after the break point, we must move it. */ if (current_x > wrap_loc) { - /* We move it right by the number of characters that come before - * its corresponding position in the new line. That is, - * current_x - wrap_loc + indent_len. We actually need to go one - * further for the new line, but remember that current_x has - * already been incremented. */ - int right = + current = current->next; + current_x -= #ifndef NANO_SMALL - indent_len + + -indent_len + #endif - current_x - wrap_loc; - - /* note that do_right depends on the value of current_x */ - current_x = wrap_loc; - while (right--) - do_right(); + 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 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; - } else if (wrapping && mark_beginbuf == inptr->next) { + mark_beginx -= +#ifndef NANO_SMALL + -indent_len + +#endif + wrap_loc + 1; + } else if (wrapping && mark_beginbuf == inptr->next) mark_beginx += after_break_len; - } +#endif /* !NANO_SMALL */ -/* The following lines are all copied from do_wrap() in version 1.1.7. It - * is not clear whether they are necessary. It looks like do_right() - * takes care of these things. It also appears that do_right() is very - * inefficient. */ - /* Perhaps the global variable editbot, the last visible line in the - * editor, needs to change. */ - fix_editbot(); /* Place the cursor. */ reset_cursor(); - /* Display the changes on the screen. */ - edit_refresh(); + + return 1; } - -/* Check to see if we've just caused the line to wrap to a new line */ -void check_wrap(filestruct * inptr) -{ - int len = strlenpt(inptr->data); -#ifdef DEBUG - fprintf(stderr, _("check_wrap called with inptr->data=\"%s\"\n"), - inptr->data); -#endif - - if (len <= fill) - return; - else { - int i = actual_x(inptr, fill); - - /* Do not wrap if there are no words on or after wrap point. */ - int char_found = 0; - - while (isspace((int) inptr->data[i]) && inptr->data[i]) - i++; - - if (!inptr->data[i]) - return; - - /* String must be at least 1 character long. */ - for (i = strlen(inptr->data) - 1; i >= 0; i--) { - if (isspace((int) inptr->data[i])) { - if (!char_found) - continue; - char_found = 2; /* 2 for yes do wrap. */ - break; - } else - char_found = 1; /* 1 for yes found a word, but must check further. */ - } - - if (char_found == 2) - do_wrap(inptr); - } -} -#endif /* DISABLE_WRAPPING */ +#endif /* !DISABLE_WRAPPING */ /* Stuff we do when we abort from programs and want to clean up the - * screen. This doesn't do much right now. - */ + * screen. This doesn't do much right now. */ void do_early_abort(void) { blank_statusbar_refresh(); @@ -1200,9 +984,8 @@ void do_early_abort(void) int do_backspace(void) { - filestruct *previous, *tmp; - - if (current_x != 0) { + if (current_x > 0) { + assert(current_x <= strlen(current->data)); /* Let's get dangerous */ memmove(¤t->data[current_x - 1], ¤t->data[current_x], strlen(current->data) - current_x + 1); @@ -1210,34 +993,39 @@ int do_backspace(void) fprintf(stderr, _("current->data now = \"%s\"\n"), current->data); #endif align(¤t->data); +#ifndef NANO_SMALL + if (current_x <= mark_beginx && mark_beginbuf == current) + mark_beginx--; +#endif do_left(); } else { + filestruct *previous; + const filestruct *tmp; + if (current == fileage) return 0; /* Can't delete past top of file */ previous = current->prev; current_x = strlen(previous->data); + placewewant = strlenpt(previous->data); +#ifndef NANO_SMALL + if (current == mark_beginbuf) { + mark_beginx += current_x; + mark_beginbuf = previous; + } +#endif previous->data = nrealloc(previous->data, - strlen(previous->data) + - strlen(current->data) + 1); - strcat(previous->data, current->data); + current_x + strlen(current->data) + 1); + strcpy(previous->data + current_x, current->data); - tmp = current; unlink_node(current); delete_node(current); - if (current == edittop) { - if (previous->next) - current = previous->next; - else - current = previous; + tmp = current; + current = (previous->next ? previous->next : previous); + renumber(current); + /* We had to renumber before doing update_line. */ + if (tmp == edittop) page_up(); - } else { - if (previous->next) - current = previous->next; - else - current = previous; - update_line(current, current_x); - } /* Ooops, sanity check */ if (tmp == filebot) { @@ -1247,7 +1035,7 @@ int do_backspace(void) /* Recreate the magic line if we're deleting it AND if the line we're on now is NOT blank. if it is blank we can just use IT for the magic line. This is how Pico - appears to do it, in any case */ + appears to do it, in any case. */ if (current->data[0] != '\0') { new_magicline(); fix_editbot(); @@ -1255,19 +1043,18 @@ int do_backspace(void) } current = previous; - renumber(current); - previous_line(); + if (current_y > 0) + current_y--; totlines--; #ifdef DEBUG fprintf(stderr, _("After, data = \"%s\"\n"), current->data); #endif - + UNSET(KEEP_CUTBUFFER); + edit_refresh(); } totsize--; set_modified(); - UNSET(KEEP_CUTBUFFER); - edit_refresh(); return 1; } @@ -1288,7 +1075,7 @@ int do_delete(void) } else if (current->next != NULL && (current->next != filebot || blbf)) { /* We can delete the line before filebot only if it is blank: it - * becomes the new magic line then. */ + becomes the new magic line then. */ filestruct *foo; @@ -1305,8 +1092,9 @@ int do_delete(void) unlink_node(foo); delete_node(foo); - update_line(current, current_x); renumber(current); + /* Have to renumber before doing update_line(). */ + update_line(current, current_x); totlines--; } else return 0; @@ -1324,10 +1112,10 @@ void wrap_reset(void) } #ifndef DISABLE_SPELLER - -int do_int_spell_fix(char *word) +static int do_int_spell_fix(const char *word) { - char *prevanswer = NULL, *save_search = NULL, *save_replace = NULL; + char *save_search; + char *save_replace; filestruct *begin; int i = 0, j = 0, beginx, beginx_top, reverse_search_set; #ifndef NANO_SMALL @@ -1350,13 +1138,12 @@ int do_int_spell_fix(char *word) /* save the current search/replace strings */ search_init_globals(); - save_search = mallocstrcpy(save_search, last_search); - save_replace = mallocstrcpy(save_replace, last_replace); + save_search = last_search; + save_replace = last_replace; /* set search/replace strings to mis-spelt word */ - prevanswer = mallocstrcpy(prevanswer, word); - last_search = mallocstrcpy(last_search, word); - last_replace = mallocstrcpy(last_replace, word); + last_search = mallocstrcpy(NULL, word); + last_replace = mallocstrcpy(NULL, word); /* start from the top of file */ current = fileage; @@ -1367,20 +1154,19 @@ int do_int_spell_fix(char *word) 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, prevanswer) != NULL) { + if (findnextstr(TRUE, FALSE, fileage, beginx_top, word) != NULL) { - /* find wholewords only */ - if (!is_whole_word(current_x, current, prevanswer)) + /* find whole words only */ + if (!is_whole_word(current_x, current->data, word)) continue; - do_replace_highlight(TRUE, prevanswer); + 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, prevanswer); + do_replace_highlight(FALSE, word); /* start from the start of this line again */ current = fileage; @@ -1388,19 +1174,17 @@ int do_int_spell_fix(char *word) search_last_line = FALSE; - if (strcmp(prevanswer, answer)) { + if (strcmp(word, answer)) { j = i; - do_replace_loop(prevanswer, fileage, &beginx_top, TRUE, &j); + do_replace_loop(word, fileage, &beginx_top, TRUE, &j); } } - break; } /* restore the search/replace strings */ free(last_search); last_search=save_search; free(last_replace); last_replace=save_replace; - free(prevanswer); /* restore where we were */ current = begin; @@ -1424,7 +1208,7 @@ int do_int_spell_fix(char *word) return TRUE; } -/* Integrated spell checking using 'spell' program */ +/* Integrated spell checking using 'spell' program. */ int do_int_speller(char *tempfile_name) { char *read_buff, *read_buff_ptr, *read_buff_word; @@ -1448,13 +1232,11 @@ int do_int_speller(char *tempfile_name) /* replace the standard in with the tempfile */ 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); @@ -1464,7 +1246,6 @@ int do_int_speller(char *tempfile_name) /* send spell's standard out to the pipe */ if (dup2(in_fd[1], STDOUT_FILENO) != STDOUT_FILENO) { - close(in_fd[1]); exit(1); } @@ -1485,7 +1266,6 @@ int do_int_speller(char *tempfile_name) /* Child process was not forked successfully */ if (pid_spell < 0) { - close(in_fd[0]); return FALSE; } @@ -1493,7 +1273,6 @@ int do_int_speller(char *tempfile_name) /* Get system pipe buffer size */ if ((pipe_buff_size = fpathconf(in_fd[0], _PC_PIPE_BUF)) < 1) { - close(in_fd[0]); return FALSE; } @@ -1505,7 +1284,6 @@ int do_int_speller(char *tempfile_name) 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); @@ -1529,10 +1307,8 @@ int do_int_speller(char *tempfile_name) break; } } - read_buff_word = read_buff_ptr + 1; } - read_buff_ptr++; } @@ -1555,15 +1331,14 @@ int do_int_speller(char *tempfile_name) return TRUE; } -/* External spell checking */ +/* External spell checking. */ int do_alt_speller(char *file_name) { int alt_spell_status, lineno_cur = current->lineno; int x_cur = current_x, y_cur = current_y, pww_cur = placewewant; #ifndef NANO_SMALL - int mark_set = 0, mbb_lineno_cur, mbx_cur; + int mark_set; #endif - pid_t pid_spell; char *ptr; static int arglen = 3; @@ -1571,12 +1346,7 @@ int do_alt_speller(char *file_name) #ifndef NANO_SMALL mark_set = ISSET(MARK_ISSET); - if (mark_set) { - /* Save the marking position */ - mbb_lineno_cur = mark_beginbuf->lineno; - mbx_cur = mark_beginx; - UNSET(MARK_ISSET); - } + UNSET(MARK_ISSET); #endif endwin(); @@ -1597,27 +1367,21 @@ int do_alt_speller(char *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); /* Should not be reached, if alternate speller is found!!! */ - exit(1); } /* Could not fork?? */ - if (pid_spell < 0) return FALSE; /* Wait for alternate speller to complete */ wait(&alt_spell_status); - if (WIFEXITED(alt_spell_status)) { - if (WEXITSTATUS(alt_spell_status) != 0) - return FALSE; - } else + if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0) return FALSE; refresh(); @@ -1626,12 +1390,8 @@ int do_alt_speller(char *file_name) open_file(file_name, 0, 1); #ifndef NANO_SMALL - if (mark_set) { - /* Restore the marking position */ - do_gotopos(mbb_lineno_cur, mbx_cur, y_cur, 0); - mark_beginbuf = current; + if (mark_set) SET(MARK_ISSET); - } #endif /* go back to the old position, mark the file as modified, and make @@ -1647,7 +1407,6 @@ int do_alt_speller(char *file_name) int do_spell(void) { - #ifdef DISABLE_SPELLER nano_disabled_msg(); return (TRUE); @@ -1692,15 +1451,16 @@ int do_spell(void) } #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. - */ +static int pid; /* This is the PID of the newly forked process + * below. It must be global since the signal + * handler needs it. */ -RETSIGTYPE cancel_fork(int signal) { +RETSIGTYPE cancel_fork(int signal) +{ if (kill(pid, SIGKILL)==-1) nperror("kill"); } -int open_pipe(char *command) +int open_pipe(const char *command) { int fd[2]; FILE *f; @@ -1714,8 +1474,7 @@ int open_pipe(char *command) * 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. - */ + * we finish, even if we get errors. */ /* Make our pipes. */ @@ -1747,8 +1506,7 @@ int open_pipe(char *command) } /* before we start reading the forked command's output, we set - * things up so that ^C will cancel the new process. - */ + * things up so that ^C will cancel the new process */ if (sigaction(SIGINT, NULL, &newaction)==-1) { cancel_sigs = 1; nperror("sigaction"); @@ -1814,7 +1572,6 @@ int do_exit(void) } else #endif - finish(0); } @@ -1840,7 +1597,6 @@ int do_exit(void) } else #endif - finish(0); } } else if (i == 0) { @@ -1852,7 +1608,6 @@ int do_exit(void) } else #endif - finish(0); } else statusbar(_("Cancelled")); @@ -1866,9 +1621,8 @@ int do_exit(void) void do_mouse(void) { MEVENT mevent; - int foo = 0, tab_found = 0; int currslen; - shortcut *s = currshortcut; + const shortcut *s = currshortcut; if (getmouse(&mevent) == ERR) return; @@ -1877,17 +1631,17 @@ void do_mouse(void) if (wenclose(edit, mevent.y, mevent.x)) { /* Don't let people screw with the marker when they're in a - subfunction */ + * subfunction. */ if (currshortcut != main_list) return; - /* Subtract out size of topwin. Perhaps we need a constant somewhere? */ + /* Subtract out size of topwin. Perhaps we need a constant + * somewhere? */ mevent.y -= 2; - /* 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. - */ + /* 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 > @@ -1914,30 +1668,11 @@ void do_mouse(void) current_y--; } } - current_x = mevent.x; + current_x = actual_x(current, mevent.x); placewewant = current_x; - while (foo < current_x) { - if (current->data[foo] == NANO_CONTROL_I) { - current_x -= tabsize - (foo % tabsize); - tab_found = 1; - } else if (current->data[foo] & 0x80); - else if (current->data[foo] < 32) - current_x--; - foo++; - } - /* This is where tab_found comes in. I can't figure out why, - * but without it any line with a tab will place the cursor - * one character behind. Whatever, this fixes it. */ - if (tab_found == 1) - current_x++; - - if (current_x > strlen(current->data)) - current_x = strlen(current->data); - update_cursor(); edit_refresh(); } else if (wenclose(bottomwin, mevent.y, mevent.x) && !ISSET(NO_HELP)) { - int i, k; if (currshortcut == main_list) @@ -1998,45 +1733,54 @@ RETSIGTYPE do_suspend(int signal) } /* Restore the suspend handler when we come back into the prog */ -RETSIGTYPE do_cont(int signal) +static RETSIGTYPE do_cont(int signal) { - /* Now we just update the screen instead of having to reenable the - SIGTSTP handler */ + SIGTSTP handler. */ doupdate(); /* The Hurd seems to need this, otherwise a ^Y after a ^Z will - start suspending again */ - signal_init(); + start suspending again. */ + signal_init(); + +#ifndef NANO_SMALL + /* Perhaps the user resized the window while we slept. */ + handle_sigwinch(0); +#endif } +#ifndef NANO_SMALL void handle_sigwinch(int s) { -#ifndef NANO_SMALL - char *tty = NULL; - int fd = 0; + const char *tty = ttyname(0); + int fd; int result = 0; struct winsize win; - tty = ttyname(0); 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 - if ((fill = COLS - CHARS_FROM_EOL) < MIN_FILL_LENGTH) + fill = wrap_at; + if (fill < 0) + fill += COLS; + if (fill < MIN_FILL_LENGTH) die_too_small(); #endif @@ -2064,9 +1808,8 @@ void handle_sigwinch(int s) fix_editbot(); - if (current_y > editwinrows - 1) { + if (current_y > editwinrows - 1) edit_update(editbot, CENTER); - } erase(); /* Do these b/c width may have changed... */ @@ -2080,11 +1823,10 @@ void handle_sigwinch(int s) /* Turn cursor back on for sure */ curs_set(1); - /* Jump back to mainloop */ + /* Jump back to main loop */ siglongjmp(jmpbuf, 1); - -#endif } +#endif void signal_init(void) { @@ -2101,9 +1843,10 @@ void signal_init(void) act.sa_handler = handle_hup; sigaction(SIGHUP, &act, NULL); +#ifndef NANO_SMALL act.sa_handler = handle_sigwinch; sigaction(SIGWINCH, &act, NULL); - +#endif #ifdef _POSIX_VDISABLE tcgetattr(0, &term); @@ -2125,8 +1868,9 @@ void signal_init(void) #endif } else { - /* if we don't do this, it seems other stuff interrupts the - suspend handler! Try using nano with mutt without this line */ + /* If we don't do this, it seems other stuff interrupts the + suspend handler! Try using nano with mutt without this + line. */ sigfillset(&act.sa_mask); act.sa_handler = do_suspend; @@ -2136,12 +1880,9 @@ void signal_init(void) sigaction(SIGCONT, &act, NULL); } - #ifdef _POSIX_VDISABLE tcsetattr(0, TCSANOW, &term); #endif - - } void window_init(void) @@ -2162,7 +1903,6 @@ void window_init(void) keypad(edit, TRUE); keypad(bottomwin, TRUE); #endif - } void mouse_init(void) @@ -2175,13 +1915,10 @@ void mouse_init(void) mousemask(BUTTON1_RELEASED, NULL); mouseinterval(50); - } else mousemask(0, NULL); - #endif #endif - } int do_tab(void) @@ -2190,301 +1927,630 @@ int do_tab(void) return 1; } +#if !defined(DISABLE_WRAPJUSTIFY) && !defined(NANO_SMALL) +/* The "indentation" of a line is the white-space between the quote part + * and the non-white-space of the line. */ +size_t indent_length(const char *line) { + size_t len = 0; + + assert(line != NULL); + while (*line == ' ' || *line == '\t') { + line++; + len++; + } + return len; +} +#endif /* !DISABLE_WRAPJUSTIFY && !NANO_SMALL */ + #ifndef DISABLE_JUSTIFY -int empty_line(const char *data) -{ - while (*data) { - if (!isspace((int) *data)) - return 0; +/* justify_format() replaces Tab by Space and multiple spaces by 1 (except + * it maintains 2 after a . ! or ?). Note the terminating \0 + * counts as a space. + * + * If !changes_allowed and justify_format needs to make a change, it + * returns 1, otherwise returns 0. + * + * If changes_allowed, justify_format() might make line->data + * shorter, and change the actual pointer with null_at(). + * + * justify_format will not look at the first skip characters of line. + * skip should be at most strlen(line->data). The skip+1st character must + * not be whitespace. */ +static int justify_format(int changes_allowed, filestruct *line, + size_t skip) { + char *back, *front; - data++; - } + /* These four asserts are assumptions about the input data. */ + assert(line != NULL); + assert(line->data != NULL); + assert(skip <= strlen(line->data)); + assert(line->data[skip] != ' ' && line->data[skip] != '\t'); - return 1; -} - -int no_spaces(const char *data) -{ - while (*data) { - if (isspace((int) *data)) - return 0; - - data++; - } - - return 1; -} - -void justify_format(char *data) -{ - int i = 0; - int len = strlen(data); - - /* Skip leading whitespace. */ - for (i = 0; i < len; i++) { - if (!isspace((int) data[i])) - break; - } - - i++; /* (i) is now at least 2. */ - - /* No double spaces allowed unless following a period. Tabs -> space. No double tabs. */ - for (; i < len; i++) { - if (isspace((int) data[i]) && isspace((int) data[i - 1]) - && (data[i - 2] != '.') - && (data[i-2]!='!') && (data[i-2]!='?')) { - memmove(data + i, data + i + 1, len - i); - len--; - i--; + back = line->data + skip; + front = back; + for (; *front; front++) { + if (*front == '\t') { + if (!changes_allowed) + return 1; + *front = ' '; + } + /* these tests are safe since line->data + skip is not a space */ + if (*front == ' ' && *(front-1) == ' ' && *(front-2) != '.' && + *(front-2) != '!' && *(front-2) != '?') { + /* Now *front is a space we want to remove. We do that by + * simply failing to assign it to *back */ + if (!changes_allowed) + return 1; +#ifndef NANO_SMALL + if (mark_beginbuf == line && back - line->data < mark_beginx) + mark_beginx--; +#endif + } else { + *back = *front; + back++; } } - /* Skip trailing whitespace. - * i<=len iff there was a non-space in the line. In that case, we - * strip spaces from the end of the line. Note that "line" means the - * whole paragraph. */ - if (i<=len) { - for(i=len-1; i>0 && isspace((int) data[i]); i--); - data[i+1] = '\0'; - } + + /* Remove spaces from the end of the line, except maintain 1 after a + * sentence punctuation. */ + while (line->data < back && *(back-1) == ' ') + back--; + if (line->data < back && *back == ' ' && + (*(back-1) == '.' || *(back-1) == '!' || *(back-1) == '?')) + back++; + if (!changes_allowed && back != front) + return 1; + + /* This assert merely documents a fact about the loop above. */ + assert(changes_allowed || back==front); + + /* Now back is the new end of line->data. */ + if (back != front) { + totsize += back - line->data - strlen(line->data); + null_at(&line->data, back - line->data); +#ifndef NANO_SMALL + if (mark_beginbuf == line && back - line->data < mark_beginx) + mark_beginx = back - line->data; +#endif + } + return 0; } + +/* The "quote part" of a line is the largest initial substring matching + * the quote string. This function returns the length of the quote part + * of the given line. + * + * Note that if !HAVE_REGEX_H then we match concatenated copies of + * quotestr. */ +#ifdef HAVE_REGEX_H +static size_t quote_length(const char *line, const regex_t *qreg) { + regmatch_t matches; + int rc = regexec(qreg, line, 1, &matches, 0); + + if (rc == REG_NOMATCH || matches.rm_so == (regoff_t) -1) + return 0; + /* matches.rm_so should be 0, since the quote string should start with + * the caret ^. */ + return matches.rm_eo; +} +#else /* !HAVE_REGEX_H */ +static size_t quote_length(const char *line) { + size_t qdepth = 0; + size_t qlen = strlen(quotestr); + + /* Compute quote depth level */ + while (!strcmp(line + qdepth, quotestr)) + qdepth += qlen; + return qdepth; +} +#endif /* !HAVE_REGEX_H */ + +#ifdef HAVE_REGEX_H +# define IFREG(a, b) a, b +#else +# define IFREG(a, b) a #endif -int do_justify(void) -{ +/* 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. */ +static int quotes_match(const char *a_line, size_t a_quote, + IFREG(const char *b_line, const regex_t *qreg)) { + /* Here is the assumption about a_quote: */ + assert(a_quote == quote_length(IFREG(a_line, qreg))); + return a_quote==quote_length(IFREG(b_line, qreg)) && + !strncmp(a_line, b_line, a_quote); +} + +/* We assume a_line and b_line have no quote part. Then, we return whether + * b_line could follow a_line in a paragraph. */ +static size_t indents_match(const char *a_line, size_t a_indent, + const char *b_line, size_t b_indent) { + assert(a_indent == indent_length(a_line)); + assert(b_indent == indent_length(b_line)); + + return b_indent <= a_indent && !strncmp(a_line, b_line, b_indent); +} + +/* Put the next par_len lines, starting with first_line, in the cut + * buffer. We assume there are enough lines after first_line. We leave + * copies of the lines in place, too. We return the new copy of + * first_line. */ +static filestruct *backup_lines(filestruct *first_line, size_t par_len, + size_t quote_len) { + /* We put the original lines, not copies, into the cut buffer, just + * out of a misguided sense of consistency, so if you un-cut, you + * get the actual same paragraph back, not a copy. */ + filestruct *alice = first_line; + + set_modified(); + cutbuffer = NULL; + for(; par_len > 0; par_len--) { + filestruct *bob = copy_node(alice); + + if (alice == first_line) + first_line = bob; + if (alice == current) + current = bob; + if (alice == edittop) + edittop = bob; +#ifndef NANO_SMALL + if (alice == mark_beginbuf) + mark_beginbuf = bob; +#endif + justify_format(1, bob, + quote_len + indent_length(bob->data + quote_len)); + + assert(alice != NULL && bob != NULL); + add_to_cutbuffer(alice); + splice_node(bob->prev, bob, bob->next); + alice = bob->next; + } + return first_line; +} + +/* We are trying to break a chunk off line. We find the last space such + * that the display length to there is at most goal + 1. If there is + * no such space, and force is not 0, then we find the first space. + * Anyway, we then take the last space in that group of spaces. The + * terminating '\0' counts as a space. */ +static int break_line(const char *line, int goal, int force) { + /* Note that we use int instead of size_t, since goal is at most COLS, + * the screen width, which will always be reasonably small. */ + int space_loc = -1; + /* Current tentative return value. Index of the last space we + * found with short enough display width. */ + int cur_loc = 0; + /* Current index in line */ + + assert(line != NULL); + for(; *line != '\0' && goal >= 0; line++, cur_loc++) { + if (*line == ' ') + space_loc = cur_loc; + assert(*line != '\t'); + + if (iscntrl(*line)) + goal -= 2; + else if ((unsigned char) *line >= 0x80 && (unsigned char) *line <= 0x9f) + goal -= 4; + else + goal--; + } + if (goal >= 0) + /* In fact, the whole line displays shorter than goal. */ + return cur_loc; + if (space_loc == -1) { + /* No space found short enough. */ + if (force) + for(; *line != '\0'; line++, cur_loc++) + if (*line == ' ' && *(line + 1) != ' ') + return cur_loc; + return -1; + } + /* Perhaps the character after space_loc is a space. But because + * of justify_format, there can be only two adjacent. */ + if (*(line - cur_loc + space_loc + 1) == ' ' || + *(line - cur_loc + space_loc + 1) == '\0') + space_loc++; + return space_loc; +} +#endif /* !DISABLE_JUSTIFY */ + +/* This function justifies the current paragraph. */ +int do_justify(void) { #ifdef DISABLE_JUSTIFY nano_disabled_msg(); return 1; #else - int slen = 0; /* length of combined lines on one line. */ - int initial_y, kbinput = 0; - long totbak; - filestruct *initial = NULL, *tmpjust = NULL, *cutbak, *tmptop, *tmpbot; - filestruct *samecheck = current; - int qdepth = 0; - /* Compute quote depth level */ - while (!strncmp(¤t->data[qdepth], quotestr, strlen(quotestr))) - qdepth += strlen(quotestr); - - if (empty_line(¤t->data[qdepth])) { - /* Justify starting at first non-empty line. */ - do { - if (!current->next) - return 1; - - current = current->next; - current_y++; - } - while (strlen(current->data) >= qdepth - && !strncmp(current->data, samecheck->data, qdepth) - && empty_line(¤t->data[qdepth])); - - } else { - /* Search back for the beginning of the paragraph, where - * Paragraph is 1) A line with leading whitespace - * or 2) A line following an empty line. - */ - while (current->prev != NULL) { - if (strncmp(current->data, samecheck->data, qdepth) - - /* Don't keep going back if the previous line is more - intented quotestr-wise than samecheck */ - || !strncmp(¤t->data[qdepth], quotestr, strlen(quotestr)) - || isspace((int) current->data[qdepth]) - || empty_line(¤t->data[qdepth])) - break; - - current = current->prev; - current_y--; - } - - /* First line with leading whitespace may be empty. */ - if (strncmp(current->data, samecheck->data, qdepth) - || !strncmp(¤t->data[qdepth], quotestr, strlen(quotestr)) - || empty_line(¤t->data[qdepth])) { - if (current->next) { - current = current->next; - current_y++; - } else - return 1; - } - } - initial = current; - initial_y = current_y; - - set_modified(); - cutbak = cutbuffer; /* Got to like cutbak ;) */ - totbak = totsize; - cutbuffer = NULL; - - tmptop = current; - tmpjust = copy_node(current); - samecheck = tmpjust; - - /* This is annoying because it mucks with totsize */ - add_to_cutbuffer(tmpjust); - - /* Put the whole paragraph into one big line. */ - while (current->next && !isspace((int) current->next->data[0]) - && !strncmp(current->next->data, samecheck->data, qdepth) - - /* Don't continue if current->next is indented more! */ - && strncmp(¤t->next->data[qdepth], quotestr, strlen(quotestr)) - && !empty_line(¤t->next->data[qdepth])) { - filestruct *tmpnode = current->next; - int len = strlen(current->data); - int len2 = strlen(current->next->data) - qdepth; - - tmpjust = NULL; - tmpjust = copy_node(current->next); - add_to_cutbuffer(tmpjust); - - /* Wiping out a newline */ - totsize--; - - /* length of both strings plus space between strings and ending \0. */ - current->data = nrealloc(current->data, len + len2 + 2); - current->data[len++] = ' '; - current->data[len] = '\0'; - - strncat(current->data, ¤t->next->data[qdepth], len2); - - unlink_node(tmpnode); - delete_node(tmpnode); - } - - justify_format(current->data); - - slen = strlen(current->data); - totsize += slen; - - while (strlenpt(current->data) > fill - && !no_spaces(current->data + qdepth)) { - int i = 0, j = 0; - filestruct *tmpline = nmalloc(sizeof(filestruct)); - -/* The following code maybe could be better. In particular, can we - * merely increment instead of calling strnlenpt for each new character? - * In fact, can we assume the only tabs are at the beginning of the line? +/* To explain the justifying algorithm, I first need to define some + * phrases about paragraphs and quotation: + * A line of text consists of a "quote part", followed by an + * "indentation part", followed by text. The functions quote_length() + * and indent_length() calculate these parts. + * + * A line is "part of a paragraph" if it has a part not in the quote + * part or the indentation. + * + * A line is "the beginning of a paragraph" if it is part of a paragraph + * and + * 1) it is the top line of the file, or + * 2) the line above it is not part of a paragraph, or + * 3) the line above it does not have precisely the same quote + * part, or + * 4) the indentation of this line is not a subset of the + * indentation of the previous line, or + * 5) this line has no quote part and some indentation, and + * AUTOINDENT is not set. + * The reason for number 5) is that if AUTOINDENT is not set, then an + * indented line is expected to start a paragraph, like in books. Thus, + * nano can justify an indented paragraph only if AUTOINDENT is turned + * on. + * + * A contiguous set of lines is a "paragraph" if each line is part of + * a paragraph and only the first line is the beginning of a paragraph. */ -/* Note that we CAN break before the first word, since that is how - * pico does it. */ - int last_space = -1; /* index of the last breakpoint */ - for(i=qdepth; idata[i])) - last_space = i; - /* Note we must look at the length of the first i+1 chars. */ - if (last_space!=-1 && - strnlenpt(current->data,i+1) > fill) { - i = last_space; - break; + size_t quote_len; + /* Length of the initial quotation of the paragraph we justify. */ + size_t par_len; + /* Number of lines in that paragraph. */ + filestruct *first_mod_line = NULL; + /* Will be the first line of the resulting justified paragraph + * that differs from the original. For restoring after uncut. */ + filestruct *last_par_line = current; + /* Will be the last line of the result, also for uncut. */ + filestruct *cutbuffer_save = cutbuffer; + /* When the paragraph gets modified, all lines from the changed + * one down are stored in the cut buffer. We back up the original + * to restore it later. */ + + /* We save these global variables to be restored if the user + * unjustifies. Note we don't need to save totlines. */ + int current_x_save = current_x; + int current_y_save = current_y; + filestruct *current_save = current; + int flags_save = flags; + long totsize_save = totsize; + filestruct *edittop_save = edittop; + filestruct *editbot_save = editbot; +#ifndef NANO_SMALL + filestruct *mark_beginbuf_save = mark_beginbuf; + int mark_beginx_save = mark_beginx; +#endif + + size_t indent_len; /* generic indentation length */ + filestruct *line; /* generic line of text */ + size_t i; /* generic loop variable */ + +#ifdef HAVE_REGEX_H + regex_t qreg; /* qreg is the compiled quotation regexp. + * We no longer care about quotestr */ + int rc = regcomp(&qreg, quotestr, REG_EXTENDED); + + if (rc) { + size_t size = regerror(rc, &qreg, NULL, 0); + char *strerror = charalloc(size); + + regerror(rc, &qreg, strerror, size); + statusbar(_("Bad quote string %s: %s"), quotestr, strerror); + free(strerror); + return -1; + } +#endif + + /* Here is an assumption that is always true anyway. */ + assert(current != NULL); + +/* Here we find the first line of the paragraph to justify. If the + * current line is in a paragraph, then we move back to the first line. + * Otherwise we move down to the first line that is in a paragraph. */ + quote_len = quote_length(IFREG(current->data, &qreg)); + indent_len = indent_length(current->data + quote_len); + + if (current->data[quote_len + indent_len] != '\0') { + /* This line is part of a paragraph. So we must search back to + * the first line of this paragraph. */ + if (quote_len > 0 || indent_len == 0 +#ifndef NANO_SMALL + || ISSET(AUTOINDENT) +#endif + ) { + /* We don't justify indented paragraphs unless AUTOINDENT is + * turned on. See 5) above. */ + while (current->prev && quotes_match(current->data, + quote_len, IFREG(current->prev->data, &qreg))) { + /* indentation length of the previous line */ + size_t temp_id_len = + indent_length(current->prev->data + quote_len); + if (!indents_match(current->prev->data + quote_len, + temp_id_len, current->data + quote_len, + indent_len) || + current->prev->data[quote_len + temp_id_len] == '\0') + break; + indent_len = temp_id_len; + current = current->prev; } } -/* Now data[i] is a space. We want to break at the LAST space in this - * group. Probably, the only possibility is two in a row, but let's be - * generic. Note that we actually replace this final space with \0. Is - * this okay? It seems to work fine. */ - for(; idata[i+1]); i++) - ; - - current->data[i] = '\0'; - - slen -= i + 1 - qdepth; /* note i > qdepth */ - tmpline->data = charalloc(slen + 1); - - for (j = 0; j < qdepth; j += strlen(quotestr)) - strcpy(&tmpline->data[j], quotestr); - - /* Skip the white space in current. */ - memcpy(&tmpline->data[qdepth], current->data + i + 1, slen - qdepth); - tmpline->data[slen] = '\0'; - - current->data = nrealloc(current->data, i + 1); - - tmpline->prev = current; - tmpline->next = current->next; - if (current->next != NULL) - current->next->prev = tmpline; - - current->next = tmpline; - current = tmpline; - current_y++; - } /* end of while (!no_spaces) */ - tmpbot = current; - - if (current->next) - current = current->next; - else - filebot = current; - current_x = 0; - placewewant = 0; - - renumber(initial); - totlines = filebot->lineno; - - werase(edit); - - if ((current_y < 0) || (current_y >= editwinrows - 1) - || (initial_y <= 0)) { - edit_update(current, CENTER); - center_cursor(); } else { - fix_editbot(); + /* this line is not part of a paragraph. Move down until we get + * to a non "blank" line */ + do { + /* There is no next paragraph, so nothing to justify. */ + if (current->next == NULL) + return 0; + current = current->next; + quote_len = quote_length(IFREG(current->data, &qreg)); + indent_len = indent_length(current->data + quote_len); + } while (current->data[quote_len + indent_len] == '\0'); } +/* Now current is the first line of the paragraph, and quote_len + * is the quotation length of that line. */ + +/* Next step, compute par_len, the number of lines in this paragraph. */ + line = current; + par_len = 1; + indent_len = indent_length(line->data + quote_len); + + while (line->next && quotes_match(current->data, quote_len, + IFREG(line->next->data, &qreg))) { + size_t temp_id_len = indent_length(line->next->data + quote_len); + + if (!indents_match(line->data + quote_len, indent_len, + line->next->data + quote_len, temp_id_len) || + line->next->data[quote_len + temp_id_len] == '\0' || + (quote_len == 0 && temp_id_len > 0 +#ifndef NANO_SMALL + && !ISSET(AUTOINDENT) +#endif + )) + break; + indent_len = temp_id_len; + line = line->next; + par_len++; + } +#ifdef HAVE_REGEX_H + /* We no longer need to check quotation. */ + regfree(&qreg); +#endif +/* Now par_len is the number of lines in this paragraph. Should never + * call quotes_match() or quote_length() again. */ + +/* Next step, we loop through the lines of this paragraph, justifying + * each one individually. */ + for(; par_len > 0; current_y++, par_len--) { + size_t line_len; + size_t display_len; + /* The width of current in screen columns. */ + int break_pos; + /* Where we will break the line. */ + + indent_len = indent_length(current->data + quote_len) + + quote_len; + /* justify_format() removes excess spaces from the line, and + * changes tabs to spaces. The first argument, 0, means don't + * change the line, just say whether there are changes to be + * made. If there are, we do backup_lines(), which copies the + * original paragraph to the cutbuffer for unjustification, and + * then calls justify_format on the remaining lines. */ + if (first_mod_line == NULL && + justify_format(0, current, indent_len)) + first_mod_line = backup_lines(current, par_len, quote_len); + + line_len = strlen(current->data); + display_len = strlenpt(current->data); + + if (display_len > fill) { + /* The line is too long. Try to wrap it to the next. */ + break_pos = break_line(current->data + indent_len, + fill - strnlenpt(current->data, indent_len), + 1); + if (break_pos == -1 || break_pos + indent_len == line_len) + /* We can't break the line, or don't need to, so just go + * on to the next. */ + goto continue_loc; + break_pos += indent_len; + assert(break_pos < line_len); + /* If we haven't backed up the paragraph, do it now. */ + if (first_mod_line == NULL) + first_mod_line = backup_lines(current, par_len, quote_len); + if (par_len == 1) { + /* There is no next line in this paragraph. We make a new + * line and copy text after break_pos into it. */ + splice_node(current, make_new_node(current), + current->next); + current->next->data = charalloc(indent_len + line_len - + break_pos); + strncpy(current->next->data, current->data, + indent_len); + strcpy(current->next->data + indent_len, + current->data + break_pos + 1); + assert(strlen(current->next->data) == + indent_len + line_len - break_pos - 1); + totlines++; + totsize += indent_len; + par_len++; + } else { + size_t next_line_len = strlen(current->next->data); + + indent_len = quote_len + + indent_length(current->next->data + quote_len); + current->next->data = (char *)nrealloc(current->next->data, + sizeof(char) * (next_line_len + line_len - + break_pos + 1)); + + memmove(current->next->data + indent_len + line_len - break_pos, + current->next->data + indent_len, + next_line_len - indent_len + 1); + strcpy(current->next->data + indent_len, + current->data + break_pos + 1); + current->next->data[indent_len + line_len - break_pos - 1] + = ' '; +#ifndef NANO_SMALL + if (mark_beginbuf == current->next) { + if (mark_beginx < indent_len) + mark_beginx = indent_len; + mark_beginx += line_len - break_pos; + } +#endif + } +#ifndef NANO_SMALL + if (mark_beginbuf == current && mark_beginx > break_pos) { + mark_beginbuf = current->next; + mark_beginx -= break_pos + 1 - indent_len; + } +#endif + null_at(¤t->data, break_pos); + current = current->next; + } else if (display_len < fill && par_len > 1) { + size_t next_line_len = strlen(current->next->data); + + indent_len = quote_len + + indent_length(current->next->data + quote_len); + break_pos = break_line(current->next->data + indent_len, + fill - display_len - 1, 0); + if (break_pos == -1) + /* We can't pull a word from the next line up to this one, + * so just go on. */ + goto continue_loc; + + /* If we haven't backed up the paragraph, do it now. */ + if (first_mod_line == NULL) + first_mod_line = backup_lines(current, par_len, quote_len); + current->data = (char *)nrealloc(current->data, + line_len + break_pos + 2); + current->data[line_len] = ' '; + strncpy(current->data + line_len + 1, + current->next->data + indent_len, break_pos); + current->data[line_len + break_pos + 1] = '\0'; +#ifndef NANO_SMALL + if (mark_beginbuf == current->next) { + if (mark_beginx < indent_len + break_pos) { + mark_beginbuf = current; + if (mark_beginx <= indent_len) + mark_beginx = line_len + 1; + else + mark_beginx = line_len + 1 + mark_beginx - indent_len; + } else + mark_beginx -= break_pos + 1; + } +#endif + if (indent_len + break_pos == next_line_len) { + line = current->next; + unlink_node(line); + delete_node(line); + totlines--; + totsize -= indent_len; + current_y--; + } else { + memmove(current->next->data + indent_len, + current->next->data + indent_len + break_pos + 1, + next_line_len - break_pos - indent_len); + null_at(¤t->next->data, + next_line_len - break_pos); + current = current->next; + } + } else +continue_loc: + current = current->next; + } +/* We are now done justifying the paragraph. There are cleanup things to + * do, and we check for unjustify. */ + + /* totlines, totsize, and current_y have been maintained above. We + * now set last_par_line to the new end of the paragraph, update + * fileage, set current_x. Also, edit_refresh() needs the line + * numbers to be right, so we renumber(). */ + last_par_line = current->prev; + if (first_mod_line != NULL && first_mod_line->prev == NULL) + fileage = first_mod_line; + current_x = 0; + if (first_mod_line != NULL) + renumber(first_mod_line); + + if (current_y > editwinrows - 4) + edit_update(current, CENTER); + else + edit_refresh(); - edit_refresh(); statusbar(_("Can now UnJustify!")); /* Change the shortcut list to display the unjustify code */ shortcut_init(1); display_main_list(); reset_cursor(); - /* Now get a keystroke and see if it's unjustify; if not, unget the keystroke - and return */ + /* Now get a keystroke and see if it's unjustify; if not, unget the + * keystroke and return */ #ifndef DISABLE_MOUSE #ifdef NCURSES_MOUSE_VERSION - - /* If it was a mouse click, parse it with do_mouse and it might become - the unjustify key. Else give it back to the input stream. */ - if ((kbinput = wgetch(edit)) == KEY_MOUSE) + /* If it was a mouse click, parse it with do_mouse() and it might + * become the unjustify key. Else give it back to the input stream. */ + if ((i = wgetch(edit)) == KEY_MOUSE) do_mouse(); else - ungetch(kbinput); + ungetch(i); #endif #endif - if ((kbinput = wgetch(edit)) != NANO_UNJUSTIFY_KEY) { - ungetch(kbinput); - blank_statusbar_refresh(); + if ((i = wgetch(edit)) != NANO_UNJUSTIFY_KEY) { + ungetch(i); + /* Did we back up anything at all? */ + if (cutbuffer != cutbuffer_save) + free_filestruct(cutbuffer); + placewewant = 0; } else { /* Else restore the justify we just did (ungrateful user!) */ - if (tmptop->prev != NULL) - tmptop->prev->next = tmpbot->next; - else - fileage = current; - tmpbot->next->prev = tmptop->prev; - current = tmpbot->next; - tmpbot->next = NULL; - do_uncut_text(); - if (tmptop->prev == NULL) - edit_refresh(); + current = current_save; + current_x = current_x_save; + current_y = current_y_save; + edittop = edittop_save; + editbot = editbot_save; + if (first_mod_line != NULL) { + filestruct *cutbottom = get_cutbottom(); - /* Restore totsize from before justify */ - totsize = totbak; - free_filestruct(tmptop); - blank_statusbar_refresh(); + /* Splice the cutbuffer back into the file. */ + cutbottom->next = last_par_line->next; + cutbottom->next->prev = cutbottom; + /* The line numbers after the end of the paragraph have + * been changed, so we change them back. */ + renumber(cutbottom->next); + if (first_mod_line->prev != NULL) { + cutbuffer->prev = first_mod_line->prev; + cutbuffer->prev->next = cutbuffer; + } else + fileage = cutbuffer; + cutbuffer = NULL; + + last_par_line->next = NULL; + free_filestruct(first_mod_line); + + /* Restore global variables from before justify */ + totsize = totsize_save; + totlines = filebot->lineno; +#ifndef NANO_SMALL + mark_beginbuf = mark_beginbuf_save; + mark_beginx = mark_beginx_save; +#endif + flags = flags_save; + if (!ISSET(MODIFIED)) { + titlebar(NULL); + wrefresh(topwin); + } + } + edit_refresh(); } + cutbuffer = cutbuffer_save; + blank_statusbar_refresh(); + /* display shortcut list without UnCut */ shortcut_init(0); display_main_list(); - free_filestruct(cutbuffer); - cutbuffer = cutbak; - return 1; + return 0; #endif } + #ifndef DISABLE_HELP /* This function allocates help_text, and stores the help string in it. * help_text should be NULL initially. */ @@ -2600,34 +2666,24 @@ void help_init(void) "following keystrokes are available in the main editor window. " "Alternative keys are shown in parentheses:\n\n"); - assert(currshortcut != NULL); - /* Compute the space needed for the shortcut lists */ - for (s = currshortcut; s != NULL; s = s->next) { - assert(s->help != NULL); - /* Each shortcut has at most 24 chars for the shortcut keys, plus - * the help description, plus 1 for \n. */ - allocsize += strlen(s->help) + 25; - } - -#ifndef NANO_SMALL - /* If we're on the main list, we also count the toggle help text. */ - if (currshortcut == main_list) { - for (t = toggles; t != NULL; t = t->next) { - size_t len; - - assert(t->desc != NULL); - len = strlen(t->desc); - - /* 6 for "M-%c\t\t\t", which fills 24 columns. */ - allocsize += 6 + (len < COLS-24 ? len : COLS-24); - } - } -#endif /* !NANO_SMALL */ - allocsize += strlen(ptr); - /* Other routines should have set help_text to NULL before */ - assert(help_text == NULL); + /* 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); @@ -2645,10 +2701,10 @@ void help_init(void) ptr += sprintf(ptr, "^%c", s->val + 64); #ifndef NANO_SMALL else if (s->val == NANO_CONTROL_SPACE) - ptr += snprintf(ptr, 8, "^%s", _("Space")); + ptr += sprintf(ptr, "^%.6s", _("Space")); else if (s->altval == NANO_ALT_SPACE) { meta_shortcut = 1; - ptr += snprintf(ptr, 8, "M-%s", _("Space")); + ptr += sprintf(ptr, "M-%.5s", _("Space")); } #endif else if (s->altval > 0) { @@ -2677,7 +2733,8 @@ void help_init(void) *(ptr++) = '\t'; - ptr += sprintf(ptr, "%s\n", s->help); + assert(s->help != NULL); + ptr += sprintf(ptr, "%.*s\n", COLS - 24, s->help); } #ifndef NANO_SMALL @@ -2685,8 +2742,8 @@ void help_init(void) if (currshortcut == main_list) for (t = toggles; t != NULL; t = t->next) { ptr += sprintf(ptr, "M-%c\t\t\t", t->val - 32); - ptr += snprintf(ptr, COLS-24, _("%s enable/disable\n"), - t->desc); + assert(t->desc != NULL); + ptr += sprintf(ptr, _("%.*s enable/disable\n"), COLS - 24, t->desc); } #endif /* !NANO_SMALL */ @@ -2697,10 +2754,9 @@ void help_init(void) #endif #ifndef NANO_SMALL -void do_toggle(toggle *which) +static void do_toggle(const toggle *which) { - char *enabled = _("enabled"); - char *disabled = _("disabled"); + int enabled; /* Even easier! */ TOGGLE(which->flag); @@ -2733,19 +2789,13 @@ void do_toggle(toggle *which) break; } - if (!ISSET(which->flag)) { - if (which->val == TOGGLE_NOHELP_KEY || - which->val == TOGGLE_WRAP_KEY) - statusbar("%s %s", which->desc, enabled); - else - statusbar("%s %s", which->desc, disabled); - } else { - if (which->val == TOGGLE_NOHELP_KEY || - which->val == TOGGLE_WRAP_KEY) - statusbar("%s %s", which->desc, disabled); - else - statusbar("%s %s", which->desc, enabled); - } + /* We are assuming here that shortcut_init() above didn't free and + * reallocate the toggles. */ + enabled = ISSET(which->flag); + if (which->val == TOGGLE_NOHELP_KEY || which->val == TOGGLE_WRAP_KEY) + enabled = !enabled; + statusbar("%s %s", which->desc, + enabled ? _("enabled") : _("disabled")); } #endif /* !NANO_SMALL */ @@ -2791,10 +2841,10 @@ int main(int argc, char *argv[]) int startline = 0; /* Line to try and start at */ int keyhandled; /* Have we handled the keystroke yet? */ int modify_control_seq; - char *argv0; - shortcut *s; + const char *argv0; + const shortcut *s; #ifndef NANO_SMALL - toggle *t; + const toggle *t; #endif #ifdef _POSIX_VDISABLE @@ -2807,6 +2857,9 @@ int main(int argc, char *argv[]) {"help", 0, 0, 'h'}, #ifdef ENABLE_MULTIBUFFER {"multibuffer", 0, 0, 'F'}, +#endif +#ifdef ENABLE_NANORC + {"ignorercfiles", 0, 0, 'I'}, #endif {"keypad", 0, 0, 'K'}, #ifndef DISABLE_JUSTIFY @@ -2863,15 +2916,43 @@ int main(int argc, char *argv[]) #endif #ifdef ENABLE_NANORC - do_rcfile(); -#endif /* ENABLE_NANORC */ + /* scan through the options and handle -I/--ignorercfiles first, so + that it's handled before we call do_rcfile() and read the other + options */ + + /* stop getopt throwing up an error if we supply other options + as arguments */ + opterr = 0; #ifdef HAVE_GETOPT_LONG - while ((optchr = getopt_long(argc, argv, "h?BDFKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz", + while ((optchr = getopt_long(argc, argv, "I", long_options, &option_index)) != EOF) { #else while ((optchr = - getopt(argc, argv, "h?BDFKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz")) != EOF) { + getopt(argc, argv, "I")) != EOF) { +#endif + switch (optchr) { + case 'I': + SET(NO_RCFILE); + break; + } + } + + if (!ISSET(NO_RCFILE)) + do_rcfile(); + + /* reset the getopt variables so we can read through the command line + arguments again */ + optind = 1; + opterr = 1; +#endif /* ENABLE_NANORC */ + +#ifdef HAVE_GETOPT_LONG + while ((optchr = getopt_long(argc, argv, "h?BDFIKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz", + long_options, &option_index)) != EOF) { +#else + while ((optchr = + getopt(argc, argv, "h?BDFIKMNQ:RST:VY:abcefgijklmo:pr:s:tvwxz")) != EOF) { #endif switch (optchr) { @@ -2900,6 +2981,12 @@ int main(int argc, char *argv[]) case 'F': SET(MULTIBUFFER); break; +#endif +#ifdef ENABLE_NANORC + /* we need -I/--ignorercfiles again to stop getopt giving us an + error if we've already supplied it */ + case 'I': + break; #endif case 'K': SET(ALT_KEYPAD); @@ -2917,8 +3004,8 @@ int main(int argc, char *argv[]) quotestr = optarg; break; #else - usage(); /* To stop bogus data for tab width */ - finish(1); + usage(); + exit(1); #endif #ifdef HAVE_REGEX_H case 'R': @@ -2931,10 +3018,22 @@ int main(int argc, char *argv[]) break; #endif case 'T': - tabsize = atoi(optarg); - if (tabsize <= 0) { - usage(); /* To stop bogus data for tab width */ - finish(1); + { + int i; + char *first_error; + + /* Using strtol instead of atoi lets us accept 0 while + * checking other errors. */ + i = (int)strtol(optarg, &first_error, 10); + if (errno == ERANGE || *optarg == '\0' || *first_error != '\0') { + usage(); + exit(1); + } else + tabsize = i; + if (tabsize <= 0) { + fprintf(stderr, _("Tab size is too small for nano...\n")); + exit(1); + } } break; case 'V': @@ -2967,33 +3066,31 @@ int main(int argc, char *argv[]) operating_dir = mallocstrcpy(operating_dir, optarg); /* make sure we're inside the operating directory */ - if (check_operating_dir(".", 0)) { - if (chdir(operating_dir) == -1) { - free(operating_dir); - operating_dir = NULL; - } + if (check_operating_dir(".", 0) && chdir(operating_dir) == -1) { + free(operating_dir); + operating_dir = NULL; } break; #endif case 'p': SET(PICO_MODE); break; - case 'r': #ifndef DISABLE_WRAPJUSTIFY - fill = atoi(optarg); - if (fill < 0) - wrap_at = fill; - else if (fill > 0) - wrap_at = 0; - else if (fill == 0) { - usage(); /* To stop bogus data (like a string) */ - finish(1); + case 'r': + { + int i; + char *first_error; + + /* Using strtol instead of atoi lets us accept 0 while + * checking other errors. */ + i = (int)strtol(optarg, &first_error, 10); + if (errno == ERANGE || *optarg == '\0' || *first_error != '\0') { + usage(); + exit(1); + } else + wrap_at = i; } break; -#else - usage(); - exit(0); - #endif #ifndef DISABLE_SPELLER case 's': @@ -3013,7 +3110,7 @@ int main(int argc, char *argv[]) #else SET(NO_WRAP); break; -#endif /* DISABLE_WRAPPING */ +#endif /* DISABLE_WRAPPING */ case 'x': SET(NO_HELP); break; @@ -3043,7 +3140,7 @@ int main(int argc, char *argv[]) if (argv[optind][0] == '+') { startline = atoi(&argv[optind][1]); optind++; - if (argc > 1 && argc > optind) + if (argc > optind) filename = mallocstrcpy(filename, argv[optind]); } else filename = mallocstrcpy(filename, argv[optind]); @@ -3086,7 +3183,6 @@ int main(int argc, char *argv[]) #ifdef ENABLE_COLOR do_colorinit(); - #endif /* ENABLE_COLOR */ #ifdef DEBUG @@ -3139,7 +3235,6 @@ int main(int argc, char *argv[]) #ifdef DEBUG fprintf(stderr, _("AHA! %c (%d)\n"), kbinput, kbinput); #endif - if (kbinput == 27) { /* Grab Alt-key stuff first */ switch (kbinput = wgetch(edit)) { /* Alt-O, suddenly very important ;) */ @@ -3178,7 +3273,6 @@ int main(int argc, char *argv[]) wgetch(edit); } else if (kbinput == '~') kbinput = KEY_HOME; - #ifdef DEBUG else { fprintf(stderr, _("I got Alt-[-1-%c! (%d)\n"), @@ -3186,7 +3280,6 @@ int main(int argc, char *argv[]) break; } #endif - break; case '2': /* Alt-[-2-[0,1,3,4] = F9-F12 in many terms */ kbinput = wgetch(edit); @@ -3215,7 +3308,6 @@ int main(int argc, char *argv[]) kbinput, kbinput); break; #endif - } break; case '3': /* Alt-[-3 = Delete? */ @@ -3290,16 +3382,15 @@ int main(int argc, char *argv[]) #ifdef ENABLE_MULTIBUFFER case NANO_OPENPREV_KEY: case NANO_OPENPREV_ALTKEY: - open_prevfile(0); + open_prevfile_void(); keyhandled = 1; break; case NANO_OPENNEXT_KEY: case NANO_OPENNEXT_ALTKEY: - open_nextfile(0); + open_nextfile_void(); keyhandled = 1; break; #endif - default: /* Check for the altkey defs.... */ for (s = main_list; s != NULL; s = s->next) @@ -3388,7 +3479,10 @@ int main(int argc, char *argv[]) exception, so use it here */ do_insertfile_void(); #else - print_view_warning(); + if (!ISSET(VIEW_MODE)) + do_insertfile_void(); + else + print_view_warning(); #endif keyhandled = 1; @@ -3443,5 +3537,4 @@ int main(int argc, char *argv[]) getchar(); finish(0); - } diff --git a/nano.h b/nano.h index 7cb9d477..f157646c 100644 --- a/nano.h +++ b/nano.h @@ -124,6 +124,7 @@ typedef struct shortcut { struct shortcut *next; } shortcut; +#ifndef NANO_SMALL typedef struct toggle { int val; /* Sequence to toggle the key. Should only need 1 */ const char *desc; /* Description for when toggle is, uh, toggled, @@ -132,19 +133,17 @@ typedef struct toggle { int flag; /* What flag actually gets toggled */ struct toggle *next; } toggle; +#endif /* !NANO_SMALL */ #ifdef ENABLE_NANORC typedef struct rcoption { char *name; int flag; } rcoption; - #endif /* ENABLE_NANORC */ #ifdef ENABLE_COLOR -#define COLORSTRNUM 16 - typedef struct colortype { int fg; /* fg color */ int bg; /* bg color */ @@ -162,9 +161,10 @@ typedef struct exttype { typedef struct syntaxtype { char *desc; /* Name of this syntax type */ - struct exttype *extensions; /* List of extensions that this applies to */ + exttype *extensions; /* List of extensions that this applies to */ colortype *color; /* color struct for this syntax */ struct syntaxtype *next; + } syntaxtype; #endif /* ENABLE_COLOR */ @@ -200,12 +200,11 @@ typedef struct syntaxtype { #define ALT_KEYPAD (1<<25) #define NO_CONVERT (1<<26) #define BACKUP_FILE (1<<27) +#define NO_RCFILE (1<<28) /* Control key sequences, changing these would be very very bad */ -#ifndef NANO_SMALL -# define NANO_CONTROL_SPACE 0 -#endif +#define NANO_CONTROL_SPACE 0 #define NANO_CONTROL_A 1 #define NANO_CONTROL_B 2 #define NANO_CONTROL_C 3 @@ -336,6 +335,8 @@ know what you're doing */ #define NANO_NEXTWORD_KEY NANO_CONTROL_SPACE #define NANO_PREVWORD_KEY NANO_ALT_SPACE +#ifndef NANO_SMALL +/* Toggles do not exist with NANO_SMALL. */ #define TOGGLE_CONST_KEY NANO_ALT_C #define TOGGLE_AUTOINDENT_KEY NANO_ALT_I #define TOGGLE_SUSPEND_KEY NANO_ALT_Z @@ -353,18 +354,18 @@ know what you're doing */ #define TOGGLE_SMOOTH_KEY NANO_ALT_S #define TOGGLE_NOCONVERT_KEY NANO_ALT_N #define TOGGLE_BACKUP_KEY NANO_ALT_B +#endif /* !NANO_SMALL */ #define MAIN_VISIBLE 12 #define VIEW 1 #define NOVIEW 0 -#define NONE 3 -#define TOP 2 -#define CENTER 1 -#define BOTTOM 0 +typedef enum { + CENTER, TOP, NONE +} topmidbotnone; -/* Minimum editor window rows required for Nano to work correctly */ +/* Minimum editor window rows required for nano to work correctly */ #define MIN_EDITOR_ROWS 3 /* Default number of characters from end-of-line where text wrapping occurs */ @@ -373,25 +374,4 @@ know what you're doing */ /* Minimum fill length (space available for text before wrapping occurs) */ #define MIN_FILL_LENGTH 10 -/* Color specific defines */ -#ifdef ENABLE_COLOR -typedef struct colorstruct { - int fg; - int bg; - int bold; - int set; -} colorstruct; - -#define FIRST_COLORNUM 16 - -#define COLOR_TITLEBAR 16 -#define COLOR_BOTTOMBARS 17 -#define COLOR_STATUSBAR 18 -#define COLOR_TEXT 19 -#define COLOR_MARKER 20 - -#define NUM_NCOLORS 5 - -#endif /* ENABLE_COLOR */ - -#endif /* ifndef NANO_H */ +#endif /* !NANO_H */ diff --git a/nano.texi b/nano.texi index 2b664648..8b91f950 100644 --- a/nano.texi +++ b/nano.texi @@ -13,7 +13,7 @@ @dircategory Editors @direntry -* Nano: (nano). Small and friendly text editor. +* nano: (nano). Small and friendly text editor. @end direntry @c tex @@ -35,26 +35,26 @@ This manual is part of the GNU @code{nano} distribution.@* @sp4 Copyright (C) 1999, 2000, 2001, 2002 Chris Allegretta. -Permission is granted to make and distribute verbatim copies of -this manual provided the copyright notice and this permission notice -are preserved on all copies. +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. @iftex Permission is granted to process this file through TeX and print the -results, provided the printed document carries copying permission -notice identical to this one except for the removal of this paragraph -(this paragraph not being relevant to the printed manual). +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). @end iftex Permission is granted to copy and distribute modified versions of this -manual under the conditions for verbatim copying, provided that the entire -resulting derived work is distributed under the terms of a permission -notice identical to this one. +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, -except that this permission notice may be stated in a translation approved -by the Foundation. +except that this permission notice may be stated in a translation +approved by the Foundation. You may contact the author by: e-mail: @email{chrisa@@asty.org}@* @@ -62,7 +62,8 @@ e-mail: @email{chrisa@@asty.org}@* @node Top, Introduction, (dir), (dir) -This manual documents GNU nano, a small and friendly text editor. +This manual documents GNU @code{nano}, a small and friendly text +editor. @menu * Introduction:: @@ -77,11 +78,10 @@ This manual documents GNU nano, a small and friendly text editor. @node Introduction, Editor Basics, Top, Top @chapter Introduction -GNU @code{nano} is a small and friendly text editor. Besides basic -text editing, @code{nano} offers many extra features like an -interactive search and replace, goto line number, auto-indentation, -feature toggles, internationalization support, and filename tab -completion. +GNU @code{nano} is a small and friendly text editor. Besides basic text +editing, @code{nano} offers many extra features like an interactive +search and replace, goto line number, auto-indentation, feature toggles, +internationalization support, and filename tab completion. @menu * Overview:: @@ -94,7 +94,7 @@ completion. @code{nano} [GNU long option] [option] +LINE [ @var{ file ...} ] The original goal for @code{nano} was a complete bug-for-bug compatible -emulation of Pico, but consistency is now a slightly higher priority. +emulation of Pico, but consistency is now a slightly higher priority. There is a flag to implement (nearly) complete Pico emulation, (option -p or GNU long option --pico). This can also be toggled from within @code{nano} by typing Meta-P. @xref{Pico Compatibility}, for more @@ -122,7 +122,7 @@ Enable multiple file buffers, if available. @item -K, --keypad Do not use the ncurses keypad() call unless necessary. Try this flag if you find that the arrow keys on the numeric keypad do not work for you -under nano. +under @code{nano}. @item -M, --mac Write file in Mac format. @@ -172,7 +172,8 @@ Enables the use of the mouse to select text (currently only useful for running under the X window system). @item -o [dir], --operatingdir=[dir] -Set operating directory. Makes nano set up something similar to a chroot. +Set operating directory. Makes @code{nano} set up something similar to a +chroot. @item -p, --pico Emulate Pico as closely as possible, sacrificing consistency for correct @@ -272,8 +273,8 @@ current file name, or "New Buffer" if the file has not yet been named. The section on the right will display "Modified" if the file has been modified since it was last saved or opened. -Special modes: When nano is in "File browser" mode, the center section -will display the current directory instead of the filename. +Special modes: When @code{nano} is in "File browser" mode, the center +section will display the current directory instead of the filename. @xref{The File Browser}. @node The Statusbar, Shortcut Lists, The Titlebar, Editor Basics @@ -378,7 +379,7 @@ statusbar and the file browser is exited. @node Pico Compatibility, Building and Configure Options, The File Browser, Top @chapter Pico Compatibility -Nano does not completely emulate Pico by default. The following +nano does not completely emulate Pico by default. The following differences apply to the default mode and Pico Compatibility mode: @table @code @@ -423,17 +424,17 @@ to force these functions to behave in the Pico fashion. @node Building and Configure Options, , Pico Compatibility, Top @chapter Building and Configure Options -Building nano from source is fairly straightforward if you are familiar +Building @code{nano} from source is fairly straightforward if you are familiar with compiling programs with autoconf support: -tar xvfz nano-x.y.z.tar.gz (where x.y.z is the version of nano) +tar xvfz nano-x.y.z.tar.gz (where x.y.z is the version of @code{nano}) cd nano-x.y.z/ ./configure make make install -if you are looking to optimize nano for size, you may want to consider -the following command line options: +if you are looking to optimize @code{nano} for size, you may want to +consider the following command line options: @table @code @@ -448,38 +449,39 @@ Disable the justify (^J)/unjustify (^U) functions in the editor. Disables spell checker ability. @item --disable-help -Disables the help function (^G). Disabling this option makes the -binary much smaller, but makes it difficult for new users -to learn more than very basic things about using the editor. +Disables the help function (^G). Disabling this option makes the binary +much smaller, but makes it difficult for new users to learn more than +very basic things about using the editor. @item --disable-browser Disables the mini file browser (^O) when reading or writing files. @item --enable-tiny -This options disables all the above. It also disables some of -the larger internals of the editor, like the marker code (^^) and -the cut to line (-k) option which it depends on to work properly. -It also disables the function toggles and mouse support. +This options disables all the above. It also disables some of the larger +internals of the editor, like the marker code (^^) and the cut to line +(-k) option which it depends on to work properly. It also disables the +function toggles and mouse support. @item --disable-wrapping -Disables all word wrapping in the editor. This also eliminates the --w command line flag, as nonwrapping is then the default behavior. +Disables all word wrapping in the editor. This also eliminates the -w +command line flag, as nonwrapping is then the default behavior. @item --disable-mouse -Disables all mouse functionality. This also disables the -m command line -flag which enables the mouse functions. +Disables all mouse functionality. This also disables the -m command +line flag which enables the mouse functions. @item --disable-operatingdir Disables setting of operating directory. This also disables the -o command line flag. @item --disable-nls -Disables Native Language support. This will make the available GNU nano -translations unusable. +Disables Native Language support. This will make the available GNU +@code{nano} translations unusable. @item --with-slang -Compiling GNU nano with Slang is supported, and will make the binary -notably smaller than if compiled with ncurses or other curses libraries. +Compiling GNU @code{nano} with Slang is supported, and will make the +binary notably smaller than if compiled with ncurses or other curses +libraries. @end table diff --git a/nanorc.sample b/nanorc.sample index 951b8ff6..297db542 100644 --- a/nanorc.sample +++ b/nanorc.sample @@ -17,6 +17,8 @@ # set cut # Use this value instead of the default +# An out-of-range negative value can make nano die complaining that the +# screen is too small # set fill -8 # Use alternate keypad routines @@ -46,9 +48,12 @@ # Use Pico Compatibility mode for the shortcut lists and search text # set pico -# Use this as the quoting string. You shouldn't need to change this, -# but...... default "> " -# set quotestr "// " +# The email-quote string. This is a "regular expression" if your +# system supports them, otherwise a literal string. Default +# set quotestr "^([ \t]*[|>:}#])+" if you have regexps, otherwise +# set quotestr "> ". +# You can get old nano quoted-justify behavior via: +# set quotestr "(> )+" # Do regular expression searches by default # set regexp @@ -62,7 +67,7 @@ # Allow nano to be suspended with ^Z # set suspend -# Use this tab size instead of the default +# Use this tab size instead of the default; it must be greater than 0 # set tabsize 4 # Save automatically on exit, don't prompt @@ -85,7 +90,7 @@ # color will use a transparent color. If you don't want this, be sure # to set the background color to black or white. -# syntax "c-file" ".*\.c" ".*\.h" +# syntax "c-file" ".*\.c$" ".*\.h$" # color red "\<[A-Z_]\{2,\}\>" # color green "\" "\" "\" "\" # color green "\" "\" "\" @@ -99,7 +104,7 @@ # color brightyellow "<[^= ]*>" "\"[^"]*\"" # color brightblue "//.*" -# color brightblue start="/\*.*" end="\*/" +# color brightblue start="/\*" end="\*/" # For this you will probably want to set your editor to "nano -Y mutt" diff --git a/proto.h b/proto.h index c12cfdb4..491d28d8 100644 --- a/proto.h +++ b/proto.h @@ -29,13 +29,14 @@ #include "nano.h" +extern int wrap_at; extern int editwinrows; extern int current_x, current_y, totlines; extern int placewewant; -extern int mark_beginx, samelinewrap; +extern int mark_beginx; extern long totsize; extern int temp_opt; -extern int fill, wrap_at, flags,tabsize; +extern int wrap_at, flags, tabsize; extern int search_last_line; extern int currslen; @@ -47,7 +48,10 @@ extern WINDOW *edit, *topwin, *bottomwin; extern char *filename; extern struct stat originalfilestat; extern char *answer; -extern char *hblank, *help_text; +extern char *hblank; +#ifndef DISABLE_HELP +extern char *help_text; +#endif extern char *last_search; extern char *last_replace; #ifndef DISABLE_OPERATINGDIR @@ -57,6 +61,13 @@ extern char *full_operating_dir; #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); +#endif + extern struct stat fileinfo; extern filestruct *current, *fileage, *edittop, *editbot, *filebot; extern filestruct *cutbuffer, *mark_beginbuf; @@ -69,6 +80,8 @@ extern openfilestruct *open_files; extern colortype *colorstrings; extern syntaxtype *syntaxes; extern char *syntaxstr; +extern regex_t color_regexp; +extern regmatch_t colormatches[1]; #endif extern shortcut *shortcut_list; @@ -83,235 +96,200 @@ extern shortcut *help_list; #ifndef DISABLE_BROWSER extern shortcut *browser_list, *gotodir_list; #endif -extern shortcut *currshortcut; +extern const shortcut *currshortcut; #ifdef HAVE_REGEX_H extern int use_regexp, regexp_compiled; extern regex_t search_regexp; extern regmatch_t regmatches[10]; - #ifdef ENABLE_COLOR -extern regex_t color_regexp; -extern regmatch_t colormatches[1]; - extern regex_t syntaxfile_regexp; extern regmatch_t synfilematches[1]; #endif /* ENABLE_COLOR */ +#endif /* HAVE_REGEX_H */ + +#ifndef NANO_SMALL +extern toggle *toggles; #endif -extern toggle *toggles; +/* Functions we want available */ -/* Programs we want available */ +/* Public functions in color.c */ +#ifdef ENABLE_COLOR +void set_colorpairs(void); +void do_colorinit(void); +void update_color(void); +#endif /* ENABLE_COLOR */ -/* public functions in global.c */ +/* 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); + +/* Public functions in files.c */ +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); +#ifdef ENABLE_MULTIBUFFER +openfilestruct *make_new_opennode(openfilestruct *prevnode); +void splice_opennode(openfilestruct *begin, openfilestruct *newnode, openfilestruct *end); +void unlink_opennode(const openfilestruct *fileptr); +void delete_opennode(openfilestruct *fileptr); +void free_openfilestruct(openfilestruct *src); +int add_open_file(int update); +int close_open_file(void); +int open_prevfile_void(void); +int open_nextfile_void(void); +#endif +#ifndef DISABLE_OPERATINGDIR +int check_operating_dir(char *currpath, int allow_tabcomp); +#endif +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); +#endif + +/* Public functions in global.c */ int length_of_list(const shortcut *s); void shortcut_init(int unjustify); #ifdef DEBUG void thanks_for_all_the_fish(void); #endif - - -char *revstrstr(char *haystack, char *needle, char *rev_start); -char *stristr(char *haystack, char *needle); -char *revstristr(char *haystack, char *needle, char *rev_start); -char *strstrwrapper(char *haystack, char *needle, char *rev_start, int line_pos); -int search_init(int replacing); -int renumber(filestruct * fileptr); -int free_filestruct(filestruct * src); - -#ifdef ENABLE_MULTIBUFFER -int free_openfilestruct(openfilestruct * src); -#endif - -int xplustabs(void); -int do_yesno(int all, int leavecursor, char *msg, ...); -int actual_x(filestruct * fileptr, int xplus); -int strnlenpt(char *buf, int size); -int strlenpt(char *buf); -int statusq(int allowtabs, shortcut s[], char *def, char *msg, ...); -int write_file(char *name, int tmpfile, int append, int nonamechange); -int do_cut_text(void); -int do_uncut_text(void); -int no_help(void); -int renumber_all(void); -int open_file(const char *filename, int insert, int quiet); -int do_insertfile(int loading_file); -int num_of_digits(int n); -int open_pipe(char *command); -int read_file(FILE *f, const char *filename, int quiet); - -#ifdef ENABLE_MULTIBUFFER -int add_open_file(int update); -#endif - -#ifndef DISABLE_OPERATINGDIR -int check_operating_dir(char *currpath, int allow_tabcomp); -#endif - -#ifndef NANO_SMALL -int do_next_word_void(void); -int do_prev_word_void(void); -#endif /* !NANO_SMALL */ - -int do_writeout(char *path, int exiting, int append); -int do_gotoline(int line, int save_pos); -int is_whole_word(int curr_pos, filestruct *fileptr, char *searchword); -int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, - int wholewords, int *i); -int do_find_bracket(void); - -#if defined (ENABLE_MULTIBUFFER) || !defined (DISABLE_SPELLER) -void do_gotopos(int line, int pos_x, int pos_y, int pos_placewewant); -#endif - -/* Now in move.c */ +/* 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_up(void); int do_down(void); int do_left(void); int do_right(void); -int check_wildcard_match(const char *text, const char *pattern); - -char *input_tab(char *buf, int place, int *lastwastab, int *newplace, int *list); -char *real_dir_from_tilde(char *buf); - -void signal_init(void); -void unsunder(char *str, int true_len); -void sunder(char *str); -void lowercase(char *src); -void blank_bottombars(void); -void check_wrap(filestruct * inptr); -void dump_buffer(filestruct * inptr); -void align(char **strp); -void edit_refresh(void), edit_refresh_clearok(void); -void edit_update(filestruct * fileptr, int topmidbotnone); -void update_cursor(void); -void delete_node(filestruct * fileptr); - -#ifdef ENABLE_MULTIBUFFER -void delete_opennode(openfilestruct * fileptr); -#endif - -void set_modified(void); -void dump_buffer_reverse(filestruct * inptr); -void reset_cursor(void); -void check_statblank(void); -void update_line(filestruct * fileptr, int index); -void fix_editbot(void); -void statusbar(char *msg, ...); -void blank_statusbar(void); -void titlebar(char *path); -void previous_line(void); -void center_cursor(void); -void bottombars(const shortcut *s); -void blank_statusbar_refresh(void); -void nperror(const char *s); -void *mallocstrcpy(char *dest, char *src); -void wrap_reset(void); -void display_main_list(void); -void nano_small_msg(void); -void nano_disable_msg(void); -void do_early_abort(void); -void *nmalloc(size_t howmuch); -void *nrealloc(void *ptr, size_t howmuch); -void die(char *msg, ...); -void die_save_file(char *die_filename); -void new_file(void); -void new_magicline(void); -void splice_node(filestruct *begin, filestruct *newnode, filestruct *end); - -#ifdef ENABLE_MULTIBUFFER -void splice_opennode(openfilestruct *begin, openfilestruct *newnode, openfilestruct *end); -#endif - -void null_at(char **data, int index); void page_up(void); -void blank_edit(void); -void search_init_globals(void); -void replace_abort(void); -void add_to_cutbuffer(filestruct * inptr); -void do_replace_highlight(int highlight_flag, char *word); +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); +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 do_early_abort(void); +void die(const char *msg, ...); +void splice_node(filestruct *begin, filestruct *newnode, filestruct *end); void nano_disabled_msg(void); void window_init(void); void do_mouse(void); void print_view_warning(void); -void unlink_node(filestruct * fileptr); +int do_exit(void); +int do_spell(void); +int do_mark(void); +int do_delete(void); +int do_backspace(void); +int do_tab(void); +int do_justify(void); +int do_enter(void); +int do_wrap(filestruct *inptr); +void signal_init(void); +void handle_sigwinch(int s); +void die_save_file(const char *die_filename); +size_t indent_length(const char *line); -#ifdef ENABLE_MULTIBUFFER -void unlink_opennode(openfilestruct * fileptr); +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 cut_marked_segment(filestruct * top, int top_x, filestruct * bot, - int bot_x, int destructive); - +/* Public functions in rcfile.c */ #ifdef ENABLE_NANORC void do_rcfile(void); #endif +/* Public functions in search.c */ +int do_gotoline(int line, int save_pos); +int is_whole_word(int curr_pos, const char *datastr, const char *searchword); +int do_replace_loop(const char *prevanswer, const filestruct *begin, + int *beginx, int wholewords, int *i); +int do_find_bracket(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); + +/* 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 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); +void nperror(const char *s); +char *mallocstrcpy(char *dest, const char *src); +void *nmalloc(size_t howmuch); +void *nrealloc(void *ptr, size_t howmuch); +void new_magicline(void); +char *charalloc(size_t howmuch); + +/* 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); +void edit_refresh(void); +void edit_refresh_clearok(void); +void edit_update(filestruct *fileptr, topmidbotnone location); +void update_cursor(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_writeout_void(void), do_exit(void), do_gotoline_void(void); -int do_insertfile_void(void), do_search(void); - -#ifdef ENABLE_MULTIBUFFER -int load_open_file(void), close_open_file(void); -#endif - -int do_page_up(void), do_page_down(void); -int do_cursorpos(int constant), do_cursorpos_void(void), do_spell(void); -int do_home(void), do_end(void), total_refresh(void), do_mark(void); -int do_delete(void), do_backspace(void), do_tab(void), do_justify(void); -int do_first_line(void), do_last_line(void); -int do_replace(void), do_help(void), do_enter_void(void); -int keypad_on(WINDOW * win, int newval); - -#ifdef ENABLE_MULTIBUFFER -int open_prevfile(int closing_file), open_nextfile(int closing_file); -int open_prevfile_void(void), open_nextfile_void(void); -#endif - -char *charalloc (size_t howmuch); -char *get_next_filename(const char *name); - -#if !defined (DISABLE_SPELLER) || !defined (DISABLE_OPERATINGDIR) -char *get_full_path(const char *origpath); -#endif - -#ifndef DISABLE_SPELLER -char *check_writable_directory(const char *path); -char *safe_tempnam(const char *dirname, const char *filename_prefix); -#endif - -#ifndef DISABLE_BROWSER -char *do_browser(char *path); -struct stat filestat(const char *path); -char *do_browse_from(char *inpath); -#endif - -#ifdef ENABLE_COLOR -int do_colorinit(void); -void color_on(WINDOW *win, int whatever); -void color_off(WINDOW *win, int whatever); -void update_color(void); - -extern colorstruct colors[NUM_NCOLORS]; -#endif /* ENABLE_COLOR */ - -RETSIGTYPE main_loop (int junk); - -filestruct *copy_node(filestruct * src); -filestruct *copy_filestruct(filestruct * src); -filestruct *make_new_node(filestruct * prevnode); - -#ifdef ENABLE_MULTIBUFFER -openfilestruct *make_new_opennode(openfilestruct * prevnode); -#endif - -filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, - int beginx, char *needle); - -#ifndef DISABLE_HELP -void help_init(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); diff --git a/rcfile.c b/rcfile.c index 93c5a4f1..06d2ad98 100644 --- a/rcfile.c +++ b/rcfile.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "config.h" #include "proto.h" #include "nano.h" @@ -43,7 +44,7 @@ #endif /* Static stuff for the nanorc file */ -rcoption rcopts[] = { +static rcoption rcopts[] = { #ifndef NANO_SMALL {"autoindent", AUTOINDENT}, {"backup", BACKUP_FILE}, @@ -52,7 +53,9 @@ rcoption rcopts[] = { #ifndef NANO_SMALL {"cut", CUT_TO_END}, #endif +#ifndef DISABLE_WRAPJUSTIFY {"fill", 0}, +#endif {"keypad", ALT_KEYPAD}, #ifndef DISABLE_MOUSE {"mouse", USE_MOUSE}, @@ -65,7 +68,9 @@ rcoption rcopts[] = { #endif {"nofollow", FOLLOW_SYMLINKS}, {"nohelp", NO_HELP}, +#ifndef DISABLE_WRAPPING {"nowrap", NO_WRAP}, +#endif #ifndef DISABLE_OPERATINGDIR {"operatingdir", 0}, #endif @@ -94,8 +99,8 @@ static int lineno = 0; static char *nanorc; /* We have an error in some part of the rcfile; put it on stderr and - make the user hit return to continue starting up nano */ -void rcfile_error(char *msg, ...) + make the user hit return to continue starting up nano. */ +static void rcfile_error(const char *msg, ...) { va_list ap; @@ -109,11 +114,10 @@ void rcfile_error(char *msg, ...) fprintf(stderr, _("\nPress return to continue starting nano\n")); while (getchar() != '\n'); - } -/* Just print the error (one of many, perhaps) but don't abort, yet */ -void rcfile_msg(char *msg, ...) +/* Just print the error (one of many, perhaps) but don't abort, yet. */ +static void rcfile_msg(const char *msg, ...) { va_list ap; @@ -125,11 +129,10 @@ void rcfile_msg(char *msg, ...) vfprintf(stderr, msg, ap); va_end(ap); fprintf(stderr, "\n"); - } -/* Parse the next word from the string. Returns NULL if we hit EOL */ -char *parse_next_word(char *ptr) +/* Parse the next word from the string. Returns NULL if we hit EOL. */ +static char *parse_next_word(char *ptr) { while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n' && *ptr != '\0') ptr++; @@ -140,15 +143,54 @@ char *parse_next_word(char *ptr) /* Null terminate and advance ptr */ *ptr++ = 0; - while ((*ptr == ' ' || *ptr == '\t') && *ptr != '\0') + while (*ptr == ' ' || *ptr == '\t') ptr++; return ptr; } -char *parse_next_regex(char *ptr) +/* The keywords operatingdir, fill, tabsize, speller, and quotestr take + * an argument when set. Among these, operatingdir, speller, and + * quotestr have to allow tabs and spaces in the argument. Thus, if the + * next word starts with a ", we say it ends with the last " of the line. + * Otherwise, the word is interpreted as usual. That is so the arguments + * can contain "s too. */ +static char *parse_argument(char *ptr) { + const char *ptr_bak = ptr; + char *last_quote = NULL; + + assert(ptr != NULL); + + if (*ptr != '"') + return parse_next_word(ptr); + + do { + ptr++; + if (*ptr == '"') + last_quote = ptr; + } while (*ptr != '\n' && *ptr != '\0'); + + if (last_quote == NULL) { + if (*ptr == '\0') + ptr = NULL; + else + *ptr++ = '\0'; + rcfile_error(_("argument %s has unterminated \""), ptr_bak); + } else { + *last_quote = '\0'; + ptr = last_quote + 1; + } + if (ptr != NULL) + while (*ptr == ' ' || *ptr == '\t') + ptr++; + return ptr; +} + +#ifdef ENABLE_COLOR + +static char *parse_next_regex(char *ptr) { - while ((*ptr != '"' || (*(ptr+1) != ' ' && *(ptr+1) != '\n')) + while ((*ptr != '"' || (*(ptr + 1) != ' ' && *(ptr + 1) != '\n')) && *ptr != '\n' && *ptr != '\0') ptr++; @@ -156,45 +198,42 @@ char *parse_next_regex(char *ptr) return NULL; /* Null terminate and advance ptr */ - *ptr++ = 0; + *ptr++ = '\0'; - while ((*ptr == ' ' || *ptr == '\t') && *ptr != '\0') + while (*ptr == ' ' || *ptr == '\t') ptr++; return ptr; - } -#ifdef ENABLE_COLOR - -int colortoint(char *colorname, int *bright) +static int colortoint(const char *colorname, int *bright) { int mcolor = 0; if (colorname == NULL) return -1; - if (stristr(colorname, "bright")) { + if (!strncasecmp(colorname, "bright", 6)) { *bright = 1; colorname += 6; } if (!strcasecmp(colorname, "green")) - mcolor += COLOR_GREEN; + mcolor = COLOR_GREEN; else if (!strcasecmp(colorname, "red")) - mcolor += COLOR_RED; + mcolor = COLOR_RED; else if (!strcasecmp(colorname, "blue")) - mcolor += COLOR_BLUE; + mcolor = COLOR_BLUE; else if (!strcasecmp(colorname, "white")) - mcolor += COLOR_WHITE; + mcolor = COLOR_WHITE; else if (!strcasecmp(colorname, "yellow")) - mcolor += COLOR_YELLOW; + mcolor = COLOR_YELLOW; else if (!strcasecmp(colorname, "cyan")) - mcolor += COLOR_CYAN; + mcolor = COLOR_CYAN; else if (!strcasecmp(colorname, "magenta")) - mcolor += COLOR_MAGENTA; + mcolor = COLOR_MAGENTA; else if (!strcasecmp(colorname, "black")) - mcolor += COLOR_BLACK; + mcolor = COLOR_BLACK; else { rcfile_error(_("color %s not understood.\n" "Valid colors are \"green\", \"red\", \"blue\", \n" @@ -206,10 +245,10 @@ int colortoint(char *colorname, int *bright) return mcolor; } -void parse_syntax(FILE * rcstream, char *buf, char *ptr) +static void parse_syntax(char *ptr) { syntaxtype *tmpsyntax = NULL; - char *fileregptr = NULL, *nameptr = NULL; + const char *fileregptr = NULL, *nameptr = NULL; exttype *exttmp = NULL; while (*ptr == ' ') @@ -232,39 +271,30 @@ void parse_syntax(FILE * rcstream, char *buf, char *ptr) return; } - if (syntaxes == NULL) { - syntaxes = nmalloc(sizeof(syntaxtype)); - syntaxes->desc = NULL; - syntaxes->desc = mallocstrcpy(syntaxes->desc, nameptr); - syntaxes->color = NULL; - syntaxes->extensions = NULL; - syntaxes->next = NULL; - tmpsyntax = syntaxes; + if (syntaxes == NULL) { + syntaxes = (syntaxtype *)nmalloc(sizeof(syntaxtype)); + tmpsyntax = syntaxes; + } else { + for (tmpsyntax = syntaxes; tmpsyntax->next != NULL; + tmpsyntax = tmpsyntax->next) + ; + tmpsyntax->next = (syntaxtype *)nmalloc(sizeof(syntaxtype)); + tmpsyntax = tmpsyntax->next; #ifdef DEBUG - fprintf(stderr, - _("Starting a new syntax type\n")); - fprintf(stderr, "string val=%s\n", nameptr); + fprintf(stderr, _("Adding new syntax after 1st\n")); #endif - - } else { - for (tmpsyntax = syntaxes; - tmpsyntax->next != NULL; tmpsyntax = tmpsyntax->next); + } + tmpsyntax->desc = mallocstrcpy(NULL, nameptr); + tmpsyntax->color = NULL; + tmpsyntax->extensions = NULL; + tmpsyntax->next = NULL; #ifdef DEBUG - fprintf(stderr, _("Adding new syntax after 1st\n")); + fprintf(stderr, _("Starting a new syntax type\n")); + fprintf(stderr, "string val=%s\n", nameptr); #endif - tmpsyntax->next = nmalloc(sizeof(syntaxtype)); - tmpsyntax->next->desc = NULL; - tmpsyntax->next->desc = mallocstrcpy(tmpsyntax->next->desc, nameptr); - tmpsyntax->next->color = NULL; - tmpsyntax->next->extensions = NULL; - tmpsyntax->next->next = NULL; - tmpsyntax = tmpsyntax->next; - } - /* Now load in the extensions to their part of the struct */ while (*ptr != '\n' && *ptr != '\0') { - while (*ptr != '"' && *ptr != '\n' && *ptr != '\0') ptr++; @@ -276,28 +306,25 @@ void parse_syntax(FILE * rcstream, char *buf, char *ptr) ptr = parse_next_regex(ptr); if (tmpsyntax->extensions == NULL) { - tmpsyntax->extensions = nmalloc(sizeof(exttype)); - tmpsyntax->extensions->val = NULL; - tmpsyntax->extensions->val = mallocstrcpy(tmpsyntax->extensions->val, fileregptr); - tmpsyntax->extensions->next = NULL; - } - else { + tmpsyntax->extensions = (exttype *)nmalloc(sizeof(exttype)); + exttmp = tmpsyntax->extensions; + } else { for (exttmp = tmpsyntax->extensions; exttmp->next != NULL; exttmp = exttmp->next); - exttmp->next = nmalloc(sizeof(exttype)); - exttmp->next->val = NULL; - exttmp->next->val = mallocstrcpy(exttmp->next->val, fileregptr); - exttmp->next->next = NULL; + exttmp->next = (exttype *)nmalloc(sizeof(exttype)); + exttmp = exttmp->next; } + exttmp->val = mallocstrcpy(NULL, fileregptr); + exttmp->next = NULL; } } /* Parse the color stuff into the colorstrings array */ -void parse_colors(FILE * rcstream, char *buf, char *ptr) +static void parse_colors(char *ptr) { int fg, bg, bright = 0; int expectend = 0; /* Do we expect an end= line? */ - char *tmp = NULL, *beginning, *fgstr, *bgstr; + char *fgstr; colortype *tmpcolor = NULL; syntaxtype *tmpsyntax = NULL; @@ -311,12 +338,11 @@ void parse_colors(FILE * rcstream, char *buf, char *ptr) if (strstr(fgstr, ",")) { strtok(fgstr, ","); - bgstr = strtok(NULL, ","); + bg = colortoint(strtok(NULL, ","), &bright); } else - bgstr = NULL; + bg = -1; fg = colortoint(fgstr, &bright); - bg = colortoint(bgstr, &bright); if (syntaxes == NULL) { rcfile_error(_("Cannot add a color directive without a syntax line")); @@ -331,7 +357,6 @@ void parse_colors(FILE * rcstream, char *buf, char *ptr) in the colorstrings array, woo! */ while (*ptr != '\0') { - while (*ptr == ' ') ptr++; @@ -349,45 +374,37 @@ void parse_colors(FILE * rcstream, char *buf, char *ptr) } ptr++; - beginning = ptr; - ptr = parse_next_regex(ptr); - - tmp = NULL; - tmp = mallocstrcpy(tmp, beginning); - if (tmpsyntax->color == NULL) { tmpsyntax->color = nmalloc(sizeof(colortype)); - tmpsyntax->color->fg = fg; - tmpsyntax->color->bg = bg; - tmpsyntax->color->bright = bright; - tmpsyntax->color->start = tmp; - tmpsyntax->color->next = NULL; tmpcolor = tmpsyntax->color; #ifdef DEBUG - fprintf(stderr, - _("Starting a new colorstring for fg %d bg %d\n"), + fprintf(stderr, _("Starting a new colorstring for fg %d bg %d\n"), fg, bg); - fprintf(stderr, _("string val=%s\n"), tmp); #endif - } else { for (tmpcolor = tmpsyntax->color; tmpcolor->next != NULL; tmpcolor = tmpcolor->next); #ifdef DEBUG fprintf(stderr, _("Adding new entry for fg %d bg %d\n"), fg, bg); - fprintf(stderr, _("string val=%s\n"), tmp); #endif - tmpcolor->next = nmalloc(sizeof(colortype)); - tmpcolor->next->fg = fg; - tmpcolor->next->bg = bg; - tmpcolor->next->bright = bright; - tmpcolor->next->start = tmp; - tmpcolor->next->next = NULL; tmpcolor = tmpcolor->next; } + tmpcolor->fg = fg; + tmpcolor->bg = bg; + tmpcolor->bright = bright; + tmpcolor->next = NULL; - if (expectend) { + tmpcolor->start = ptr; + ptr = parse_next_regex(ptr); + tmpcolor->start = mallocstrcpy(NULL, tmpcolor->start); +#ifdef DEBUG + fprintf(stderr, _("string val=%s\n"), tmpcolor->start); +#endif + + if (!expectend) + tmpcolor->end = NULL; + else { if (ptr == NULL || strncasecmp(ptr, "end=", 4)) { rcfile_error(_ ("\n\t\"start=\" requires a corresponding \"end=\"")); @@ -403,37 +420,30 @@ void parse_colors(FILE * rcstream, char *buf, char *ptr) } ptr++; - - beginning = ptr; + tmpcolor->end = ptr; ptr = parse_next_regex(ptr); + tmpcolor->end = mallocstrcpy(NULL, tmpcolor->end); #ifdef DEBUG fprintf(stderr, _("For end part, beginning = \"%s\"\n"), - beginning); + tmpcolor->end); #endif - tmp = NULL; - tmp = mallocstrcpy(tmp, beginning); - tmpcolor->end = tmp; - - } else - tmpcolor->end = NULL; - + } } } #endif /* ENABLE_COLOR */ /* Parse the RC file, once it has been opened successfully */ -void parse_rcfile(FILE * rcstream) +static void parse_rcfile(FILE *rcstream) { char *buf, *ptr, *keyword, *option; int set = 0, i, j; buf = charalloc(1024); - while (fgets(buf, 1023, rcstream) > 0) { + while (fgets(buf, 1023, rcstream) != 0) { lineno++; ptr = buf; - while ((*ptr == ' ' || *ptr == '\t') && - (*ptr != '\n' && *ptr != '\0')) + while (*ptr == ' ' || *ptr == '\t') ptr++; if (*ptr == '\n' || *ptr == '\0') @@ -459,9 +469,9 @@ void parse_rcfile(FILE * rcstream) set = -1; #ifdef ENABLE_COLOR else if (!strcasecmp(keyword, "syntax")) - parse_syntax(rcstream, buf, ptr); + parse_syntax(ptr); else if (!strcasecmp(keyword, "color")) - parse_colors(rcstream, buf, ptr); + parse_colors(ptr); #endif /* ENABLE_COLOR */ else { rcfile_msg(_("command %s not understood"), keyword); @@ -480,21 +490,20 @@ void parse_rcfile(FILE * rcstream) rcopts[i].name); #endif if (set == 1 || rcopts[i].flag == FOLLOW_SYMLINKS) { - if (!strcasecmp(rcopts[i].name, "operatingdir") || - !strcasecmp(rcopts[i].name, "tabsize") || + if (!strcasecmp(rcopts[i].name, "tabsize") +#ifndef DISABLE_OPERATINGDIR + || !strcasecmp(rcopts[i].name, "operatingdir") +#endif #ifndef DISABLE_WRAPJUSTIFY - !strcasecmp(rcopts[i].name, "fill") || + || !strcasecmp(rcopts[i].name, "fill") #endif #ifndef DISABLE_JUSTIFY - !strcasecmp(rcopts[i].name, "quotestr") || + || !strcasecmp(rcopts[i].name, "quotestr") #endif #ifndef DISABLE_SPELLER - !strcasecmp(rcopts[i].name, "speller") -#else - 0 + || !strcasecmp(rcopts[i].name, "speller") #endif - ) { - + ) { if (*ptr == '\n' || *ptr == '\0') { rcfile_error(_ ("option %s requires an argument"), @@ -502,42 +511,54 @@ void parse_rcfile(FILE * rcstream) continue; } option = ptr; - ptr = parse_next_word(ptr); - if (!strcasecmp(rcopts[i].name, "fill")) { + if (*option == '"') + option++; + ptr = parse_argument(ptr); +#ifdef DEBUG + fprintf(stderr, "option = %s\n", option); +#endif +#ifndef DISABLE_OPERATINGDIR + if (!strcasecmp(rcopts[i].name, "operatingdir")) + operating_dir = mallocstrcpy(NULL, option); + else +#endif #ifndef DISABLE_WRAPJUSTIFY + if (!strcasecmp(rcopts[i].name, "fill")) { + char *first_error; - if ((j = atoi(option)) < MIN_FILL_LENGTH) { - rcfile_error(_ - ("requested fill size %d too small"), + /* Using strtol instead of atoi lets us + * accept 0 while checking other + * errors. */ + j = (int)strtol(option, &first_error, 10); + if (errno == ERANGE || *option == '\0' || *first_error != '\0') + rcfile_error(_("requested fill size %d invalid"), j); - } else - fill = j; -#endif + else + wrap_at = j; } else - if (!strcasecmp(rcopts[i].name, "tabsize")) - { - if ((j = atoi(option)) <= 0) { - rcfile_error(_ - ("requested tab size %d too small"), - j); - } else { - tabsize = j; - } +#endif #ifndef DISABLE_JUSTIFY - } else - if (!strcasecmp(rcopts[i].name, "quotestr")) - { - quotestr = NULL; - quotestr = - charalloc(strlen(option) + 1); - strcpy(quotestr, option); + if (!strcasecmp(rcopts[i].name, "quotestr")) + quotestr = mallocstrcpy(NULL, option); + else #endif - } else { #ifndef DISABLE_SPELLER - alt_speller = - charalloc(strlen(option) + 1); - strcpy(alt_speller, option); + if (!strcasecmp(rcopts[i].name, "speller")) + alt_speller = mallocstrcpy(NULL, option); + else #endif + { + char *first_error; + + /* Using strtol instead of atoi lets us + * accept 0 while checking other + * errors. */ + j = (int)strtol(option, &first_error, 10); + if (errno == ERANGE || *option == '\0' || *first_error != '\0') + rcfile_error(_("requested tab size %d invalid"), + j); + else + tabsize = j; } } else SET(rcopts[i].flag); @@ -555,8 +576,8 @@ void parse_rcfile(FILE * rcstream) } } } - } + free(buf); if (errors) rcfile_error(_("Errors found in .nanorc file")); @@ -566,56 +587,48 @@ void parse_rcfile(FILE * rcstream) /* The main rc file function, tries to open the rc file */ void do_rcfile(void) { - char *unable = _("Unable to open ~/.nanorc file, %s"); - struct stat fileinfo; FILE *rcstream; - struct passwd *userage; + const struct passwd *userage; + uid_t euid = geteuid(); - nanorc = charalloc(strlen(SYSCONFDIR) + 10); +#ifdef SYSCONFDIR + assert(sizeof(SYSCONFDIR) == strlen(SYSCONFDIR) + 1); + nanorc = charalloc(sizeof(SYSCONFDIR) + 7); sprintf(nanorc, "%s/nanorc", SYSCONFDIR); - /* Try to open system nanorc */ - if (stat(nanorc, &fileinfo) != -1) - if ((rcstream = fopen(nanorc, "r")) != NULL) { + if ((rcstream = fopen(nanorc, "r")) != NULL) { + /* Parse it! */ + parse_rcfile(rcstream); + fclose(rcstream); + } +#endif - /* Parse it! */ - parse_rcfile(rcstream); - fclose(rcstream); - } + /* Determine home directory using getpwent(), don't rely on $HOME */ + do { + userage = getpwent(); + } while (userage != NULL && userage->pw_uid != euid); + endpwent(); lineno = 0; - /* Determine home directory using getpwent(), don't rely on $HOME */ - for (userage = getpwent(); userage != NULL - && userage->pw_uid != geteuid(); userage = getpwent()) - ; - - if (userage == NULL) { + if (userage == NULL) rcfile_error(_("I can't find my home directory! Wah!")); - return; + else { + nanorc = nrealloc(nanorc, strlen(userage->pw_dir) + 9); + sprintf(nanorc, "%s/.nanorc", userage->pw_dir); + + if ((rcstream = fopen(nanorc, "r")) == NULL) { + /* Don't complain about the file not existing */ + if (errno != ENOENT) + rcfile_error(_("Unable to open ~/.nanorc file, %s"), + strerror(errno)); + } else { + parse_rcfile(rcstream); + fclose(rcstream); + } } - nanorc = charalloc(strlen(userage->pw_dir) + 10); - sprintf(nanorc, "%s/.nanorc", userage->pw_dir); - - if (stat(nanorc, &fileinfo) == -1) { - - /* Abort if the file doesn't exist and there's some other kind - of error stat()ing it */ - if (errno != ENOENT) - rcfile_error(unable, strerror(errno)); - return; - } - - if ((rcstream = fopen(nanorc, "r")) == NULL) { - rcfile_error(unable, strerror(errno)); - return; - } - - - parse_rcfile(rcstream); - fclose(rcstream); - + free(nanorc); } #endif /* ENABLE_NANORC */ diff --git a/search.c b/search.c index fbcb442d..2a22d0ad 100644 --- a/search.c +++ b/search.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "proto.h" #include "nano.h" @@ -70,7 +71,7 @@ void search_init_globals(void) replacing = 1 if we call from do_replace, 0 if called from do_search func. */ -int search_init(int replacing) +static int search_init(int replacing) { int i = 0; char *buf; @@ -78,10 +79,6 @@ int search_init(int replacing) search_init_globals(); - buf = charalloc(strlen(last_search) + 5); - buf[0] = '\0'; - - /* Clear the backupstring if we've changed from Pico mode to regular mode */ if (ISSET(CLEAR_BACKUPSTRING)) { @@ -96,7 +93,7 @@ int search_init(int replacing) old string back up as editable if it's not the same as last_search. Otherwise, if we don't already have a backupstring, set it to - last_search. */ + last_search. */ if (ISSET(PICO_MODE)) { if (backupstring == NULL || !strcmp(backupstring, last_search)) @@ -106,21 +103,20 @@ int search_init(int replacing) backupstring = mallocstrcpy(backupstring, last_search); /* If using Pico messages, we do things the old fashioned way... */ - if (ISSET(PICO_MODE)) { - if (last_search[0]) { + if (ISSET(PICO_MODE) && last_search[0]) { + size_t last_search_len = strlen(last_search); - /* We use COLS / 3 here because we need to see more on the line */ - if (strlen(last_search) > COLS / 3) { - snprintf(buf, COLS / 3 + 3, " [%s", last_search); - sprintf(&buf[COLS / 3 + 2], "...]"); - } else - sprintf(buf, " [%s]", last_search); - } else { - buf[0] = '\0'; - } - } - else + buf = charalloc(last_search_len > COLS / 3 ? + COLS / 3 + 7 : last_search_len + 4); + /* We use COLS / 3 here because we need to see more on the line */ + if (last_search_len > COLS / 3) + snprintf(buf, COLS / 3 + 7, " [%s...]", last_search); + else + sprintf(buf, " [%s]", last_search); + } else { + buf = charalloc(1); buf[0] = '\0'; + } /* This is now one simple call. It just does a lot */ i = statusq(0, replacing ? replace_list : whereis_list, backupstring, @@ -146,120 +142,107 @@ int search_init(int replacing) free(buf); /* Cancel any search, or just return with no previous search */ - if ((i == -1) || (i < 0 && !last_search[0])) { + if (i == -1 || (i < 0 && last_search[0] == '\0')) { statusbar(_("Search Cancelled")); reset_cursor(); free(backupstring); backupstring = NULL; return -1; - } else - switch (i) { - - case -2: /* Same string */ + } else { + switch (i) { + case -2: /* Same string */ #ifdef HAVE_REGEX_H - if (ISSET(USE_REGEXP)) { - - /* If we're in pico mode, answer is "", use last_search! */ - if (ISSET(PICO_MODE)) - regexp_init(last_search); - else + if (ISSET(USE_REGEXP)) + /* If we're in Pico mode, and answer is "", use + last_search! */ + regexp_init(ISSET(PICO_MODE) ? last_search : answer); +#endif + break; + case 0: /* They entered something new */ +#ifdef HAVE_REGEX_H + if (ISSET(USE_REGEXP)) regexp_init(answer); +#endif + free(backupstring); + backupstring = NULL; + last_replace[0] = '\0'; + break; +#ifndef NANO_SMALL + case TOGGLE_CASE_KEY: + TOGGLE(CASE_SENSITIVE); + backupstring = mallocstrcpy(backupstring, answer); + return 1; + case TOGGLE_BACKWARDS_KEY: + TOGGLE(REVERSE_SEARCH); + backupstring = mallocstrcpy(backupstring, answer); + return 1; +#ifdef HAVE_REGEX_H + case TOGGLE_REGEXP_KEY: + TOGGLE(USE_REGEXP); + backupstring = mallocstrcpy(backupstring, answer); + return 1; +#endif +#endif /* !NANO_SMALL */ + case NANO_OTHERSEARCH_KEY: + backupstring = mallocstrcpy(backupstring, answer); + return -2; /* Call the opposite search function */ + case NANO_FROMSEARCHTOGOTO_KEY: + free(backupstring); + backupstring = NULL; + do_gotoline_void(); + return -3; + default: + do_early_abort(); + free(backupstring); + backupstring = NULL; + return -3; } -#endif - break; - case 0: /* They entered something new */ -#ifdef HAVE_REGEX_H - if (ISSET(USE_REGEXP)) - regexp_init(answer); -#endif - free(backupstring); - backupstring = NULL; - last_replace[0] = '\0'; - break; - case TOGGLE_CASE_KEY: - TOGGLE(CASE_SENSITIVE); - goto string_reinit; - case TOGGLE_BACKWARDS_KEY: - TOGGLE(REVERSE_SEARCH); - goto string_reinit; -#ifdef HAVE_REGEX_H - case TOGGLE_REGEXP_KEY: - TOGGLE(USE_REGEXP); -#endif - string_reinit: - free(backupstring); - backupstring = NULL; - backupstring = mallocstrcpy(backupstring, answer); - return 1; - case NANO_OTHERSEARCH_KEY: - backupstring = mallocstrcpy(backupstring, answer); - return -2; /* Call the opposite search function */ - case NANO_FROMSEARCHTOGOTO_KEY: - free(backupstring); - backupstring = NULL; - do_gotoline_void(); - return -3; - default: - do_early_abort(); - free(backupstring); - backupstring = NULL; - return -3; } - return 0; } -void not_found_msg(char *str) +void not_found_msg(const char *str) { if (strlen(str) <= COLS / 2) statusbar(_("\"%s\" not found"), str); else { - char *foo = NULL; + char *foo = mallocstrcpy(NULL, str); - foo = mallocstrcpy(foo, str); foo[COLS / 2] = '\0'; statusbar(_("\"%s...\" not found"), foo); - free(foo); } } -int is_whole_word(int curr_pos, filestruct *fileptr, char *searchword) +int is_whole_word(int curr_pos, const char *datastr, const char *searchword) { - /* start of line or previous character not a letter */ - if ((curr_pos < 1) || (!isalpha((int) fileptr->data[curr_pos-1]))) + size_t sln = curr_pos + strlen(searchword); - /* end of line or next character not a letter */ - if (((curr_pos + strlen(searchword)) == strlen(fileptr->data)) - || (!isalpha((int) fileptr->data[curr_pos + strlen(searchword)]))) - - return TRUE; - - return FALSE; + /* start of line or previous character not a letter and end of line + * or next character not a letter */ + return (curr_pos < 1 || !isalpha((int) datastr[curr_pos - 1])) && + (sln == strlen(datastr) || !isalpha((int) datastr[sln])); } -int past_editbuff; /* findnextstr() is now searching lines not displayed */ +static int past_editbuff; + /* findnextstr() is now searching lines not displayed */ -filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beginx, - char *needle) +filestruct *findnextstr(int quiet, int bracket_mode, + const filestruct *begin, int beginx, const char *needle) { filestruct *fileptr = current; - char *searchstr, *rev_start = NULL, *found = NULL; + const char *searchstr, *rev_start = NULL, *found = NULL; int current_x_find = 0; past_editbuff = 0; if (!ISSET(REVERSE_SEARCH)) { /* forward search */ - - current_x_find = current_x + 1; -#if 0 - /* Are we now back to the place where the search started) */ - if ((fileptr == begin) && (beginx > current_x_find)) - search_last_line = 1; -#endif - /* Make sure we haven't passed the end of the string */ - if (strlen(fileptr->data) < current_x_find) - current_x_find--; + /* Argh, current_x is set to -1 by nano.c:do_int_spell_fix(), and + * strlen returns size_t, which is unsigned. */ + assert(current_x < 0 || current_x <= strlen(fileptr->data)); + current_x_find = current_x; + if (current_x_find < 0 || fileptr->data[current_x_find] != '\0') + current_x_find++; searchstr = &fileptr->data[current_x_find]; @@ -306,35 +289,22 @@ filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beg /* We found an instance */ current_x_find = found - fileptr->data; -#if 1 /* Ensure we haven't wrapped around again! */ if ((search_last_line) && (current_x_find > beginx)) { if (!quiet) not_found_msg(needle); return NULL; } -#endif } #ifndef NANO_SMALL else { /* reverse search */ - current_x_find = current_x - 1; -#if 0 - /* Are we now back to the place where the search started) */ - if ((fileptr == begin) && (current_x_find > beginx)) - search_last_line = 1; -#endif /* Make sure we haven't passed the begining of the string */ -#if 0 /* Is this required here ? */ - if (!(&fileptr->data[current_x_find] - fileptr->data)) - current_x_find++; -#endif rev_start = &fileptr->data[current_x_find]; searchstr = fileptr->data; /* Look for needle in searchstr */ while ((found = strstrwrapper(searchstr, needle, rev_start, current_x_find)) == NULL) { - /* finished processing file, get out */ if (search_last_line) { if (!quiet) @@ -363,7 +333,6 @@ filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beg SET(DISABLE_CURPOS); } } - /* Original start line reached */ if (fileptr == begin) search_last_line = 1; @@ -374,16 +343,14 @@ filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beg /* We found an instance */ current_x_find = found - fileptr->data; -#if 1 /* Ensure we haven't wrapped around again! */ if ((search_last_line) && (current_x_find < beginx)) { if (!quiet) not_found_msg(needle); return NULL; } -#endif } -#endif /* NANO_SMALL */ +#endif /* !NANO_SMALL */ /* Set globals now that we are sure we found something */ current = fileptr; @@ -398,7 +365,6 @@ filestruct *findnextstr(int quiet, int bracket_mode, filestruct * begin, int beg placewewant = xplustabs(); reset_cursor(); } - return fileptr; } @@ -416,7 +382,7 @@ void search_abort(void) #endif } -/* Search for a string */ +/* Search for a string. */ int do_search(void) { int i; @@ -499,8 +465,8 @@ int replace_regexp(char *string, int create_flag) new_size -= search_match_count; - /* Iterate through the replacement text to handle - * subexpression replacement using \1, \2, \3, etc */ + /* Iterate through the replacement text to handle subexpression + * replacement using \1, \2, \3, etc. */ c = last_replace; while (*c) { @@ -517,7 +483,7 @@ int replace_regexp(char *string, int create_flag) if (num > search_regexp.re_nsub) { /* Ugh, they specified a subexpression that doesn't - exist. */ + exist. */ return -1; } @@ -588,12 +554,12 @@ char *replace_line(void) #endif /* The tail of the original line */ - /* This may expose other bugs, because it no longer - goes through each character in the string - and tests for string goodness. But because - we can assume the invariant that current->data - is less than current_x + strlen(last_search) long, - this should be safe. Or it will expose bugs ;-) */ + + /* This may expose other bugs, because it no longer goes through + * each character in the string and tests for string goodness. But + * because we can assume the invariant that current->data is less + * than current_x + strlen(last_search) long, this should be safe. + * Or it will expose bugs ;-) */ tmp = current->data + current_x + search_match_count; strcat(copy, tmp); @@ -601,8 +567,8 @@ char *replace_line(void) } /* step through each replace word and prompt user before replacing word */ -int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, - int wholewords, int *i) +int do_replace_loop(const char *prevanswer, const filestruct *begin, + int *beginx, int wholewords, int *i) { int replaceall = 0, numreplaced = 0; @@ -610,7 +576,7 @@ int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, char *copy; switch (*i) { - case -1: /* Aborted enter */ + case -1: /* Aborted enter */ if (last_replace[0] != '\0') answer = mallocstrcpy(answer, last_replace); statusbar(_("Replace Cancelled")); @@ -619,8 +585,8 @@ int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, case 0: /* They actually entered something */ break; default: - if (*i != -2) { /* First page, last page, for example - could get here */ + if (*i != -2) { /* First page, last page, for example, could + get here */ do_early_abort(); replace_abort(); return 0; @@ -632,28 +598,20 @@ int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, last_replace = mallocstrcpy(last_replace, answer); while (1) { - /* Sweet optimization by Rocco here */ -#if 0 - fileptr = findnextstr(replaceall, FALSE, begin, *beginx, prevanswer); -#else - if (fileptr != 0) - fileptr = findnextstr(1, FALSE, begin, *beginx, prevanswer); - else - fileptr = findnextstr(replaceall || (search_last_line ? 1 : 0), FALSE, begin, *beginx, prevanswer); -#endif + fileptr = findnextstr(fileptr || replaceall || search_last_line, + FALSE, begin, *beginx, prevanswer); /* No more matches. Done! */ if (!fileptr) break; /* Make sure only whole words are found */ - if ((wholewords) && (!is_whole_word(current_x, fileptr, prevanswer))) + if (wholewords && !is_whole_word(current_x, fileptr->data, prevanswer)) continue; /* If we're here, we've found the search string */ if (!replaceall) { - curs_set(0); do_replace_highlight(TRUE, prevanswer); @@ -681,7 +639,8 @@ int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, totsize += strlen(current->data); if (!ISSET(REVERSE_SEARCH)) { - /* Stop bug where we replace a substring of the replacement text */ + /* Stop bug where we replace a substring of the + replacement text */ current_x += strlen(last_replace) - 1; /* Adjust the original cursor position - COULD BE IMPROVED */ @@ -689,6 +648,7 @@ int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, *beginx += strlen(last_replace) - strlen(last_search); /* For strings that cross the search start/end boundary */ + /* Don't go outside of allocated memory */ if (*beginx < 1) *beginx = 1; @@ -708,7 +668,8 @@ int do_replace_loop(char *prevanswer, filestruct *begin, int *beginx, edit_refresh(); set_modified(); numreplaced++; - } else if (*i == -1) /* Abort, else do nothing and continue loop */ + } else if (*i == -1) /* Abort, else do nothing and continue + loop */ break; } @@ -720,7 +681,7 @@ int do_replace(void) { int i, numreplaced, beginx; filestruct *begin; - char *prevanswer = NULL, *buf = NULL; + char *prevanswer = NULL; if (ISSET(VIEW_MODE)) { print_view_warning(); @@ -754,53 +715,37 @@ int do_replace(void) /* If answer is now == "", then PICO_MODE is set. So, copy last_search into answer (and prevanswer)... */ - if (answer[0] == '\0') { + if (answer[0] == '\0') answer = mallocstrcpy(answer, last_search); - prevanswer = mallocstrcpy(prevanswer, last_search); - } else { - last_search = mallocstrcpy(last_search, answer); - prevanswer = mallocstrcpy(prevanswer, answer); - } - - if (ISSET(PICO_MODE)) { - buf = charalloc(strlen(last_replace) + 5); - if (last_replace[0] != '\0') { - if (strlen(last_replace) > (COLS / 3)) { - strncpy(buf, last_replace, COLS / 3); - sprintf(&buf[COLS / 3 - 1], "..."); - } else - sprintf(buf, "%s", last_replace); - - i = statusq(0, replace_list_2, "", - _("Replace with [%s]"), buf); - } - else - i = statusq(0, replace_list_2, "", - _("Replace with")); - } else - i = statusq(0, replace_list_2, last_replace, - _("Replace with")); + last_search = mallocstrcpy(last_search, answer); + prevanswer = mallocstrcpy(prevanswer, last_search); + + if (ISSET(PICO_MODE) && last_replace[0] != '\0') { + if (strlen(last_replace) > COLS / 3) { + char *buf = charalloc(COLS / 3 + 3); + + strncpy(buf, last_replace, COLS / 3 - 1); + strcpy(buf + COLS / 3 - 1, "..."); + i = statusq(0, replace_list_2, "", _("Replace with [%s]"), + buf); + free(buf); + } else + i = statusq(0, replace_list_2, "", _("Replace with [%s]"), + last_replace); + } else + i = statusq(0, replace_list_2, last_replace, _("Replace with")); /* save where we are */ begin = current; -#if 0 - /* why + 1 ? isn't this taken care of in findnextstr() ? */ - beginx = current_x + 1; -#else beginx = current_x; -#endif search_last_line = 0; numreplaced = do_replace_loop(prevanswer, begin, &beginx, FALSE, &i); /* restore where we were */ current = begin; -#if 0 - current_x = beginx - 1; -#else current_x = beginx; -#endif renumber_all(); edit_update(current, CENTER); print_replaced(numreplaced); diff --git a/utils.c b/utils.c index f86ff47b..446b0db9 100644 --- a/utils.c +++ b/utils.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "config.h" #include "proto.h" #include "nano.h" @@ -50,75 +51,75 @@ int num_of_digits(int n) return i; } -/* For non-null-terminated lines. A line, by definition, shouldn't - normally have newlines in it, so encode its nulls as newlines. */ -void unsunder(char *str, int true_len) +/* Fix the memory allocation for a string. */ +void align(char **strp) { - int i; - if (strlen(str) < true_len) { - for (i = 0; i < true_len; i++) { - if (str[i] == '\0') - str[i] = '\n'; - } - } + assert(strp != NULL); + *strp = nrealloc(*strp, strlen(*strp) + 1); +} + +/* Null a string at a certain index and align it. */ +void null_at(char **data, size_t index) +{ + assert(data != NULL); + *data = (char *)nrealloc(*data, sizeof(char) * (index + 1)); + (*data)[index] = '\0'; } /* For non-null-terminated lines. A line, by definition, shouldn't - normally have newlines in it, so decode its newlines into nulls. */ + * normally have newlines in it, so encode its nulls as newlines. */ +void unsunder(char *str, size_t true_len) +{ + assert(str != NULL); + for(; true_len > 0; true_len--, str++) + if (*str == '\0') + *str = '\n'; +} + +/* For non-null-terminated lines. A line, by definition, shouldn't + * normally have newlines in it, so decode its newlines into nulls. */ void sunder(char *str) { - int i, true_len = strlen(str); - if (strchr(str, '\n')) { - for (i = 0; i < true_len; i++) { - if (str[i] == '\n') - str[i] = '\0'; - } - } -} - -/* Lower case a string - must be null terminated */ -void lowercase(char *src) -{ - long i = 0; - - while (src[i] != 0) { - src[i] = (char) tolower(src[i]); - i++; - } + assert(str != NULL); + for(; *str != '\0'; str++) + if (*str == '\n') + *str = '\0'; } /* None of this is needed if we're using NANO_SMALL! */ #ifndef NANO_SMALL -char *revstrstr(char *haystack, char *needle, char *rev_start) +static const char *revstrstr(const char *haystack, const char *needle, + const char *rev_start) { - char *p, *q, *r; + for(; rev_start >= haystack ; rev_start--) { + const char *r, *q; - for(p = rev_start ; p >= haystack ; --p) { - for (r = p, q = needle ; (*q == *r) && (*q != '\0') ; r++, q++) + for (r = rev_start, q = needle ; *q == *r && *q != '\0'; r++, q++) ; if (*q == '\0') - return p; + return rev_start; } - return 0; + return NULL; } -char *revstristr(char *haystack, char *needle, char *rev_start) +static const char *revstristr(const char *haystack, const char *needle, + const char *rev_start) { - char *p, *q, *r; + for (; rev_start >= haystack; rev_start--) { + const char *r = rev_start, *q = needle; - for(p = rev_start ; p >= haystack ; --p) { - for (r = p, q = needle ; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++) + for (; (tolower(*q) == tolower(*r)) && (*q != '\0') ; r++, q++) ; if (*q == '\0') - return p; + return rev_start; } - return 0; + return NULL; } -#endif /* NANO_SMALL */ +#endif /* !NANO_SMALL */ /* This is now mutt's version (called mutt_stristr) because it doesn't use memory allocation to do a simple search (yuck). */ -char *stristr(char *haystack, char *needle) +const char *stristr(const char *haystack, const char *needle) { const char *p, *q; @@ -128,43 +129,41 @@ char *stristr(char *haystack, char *needle) return (haystack); while (*(p = haystack)) { - for (q = needle; *p && *q && tolower (*p) == tolower (*q); p++, q++) + for (q = needle; *p && *q && tolower(*p) == tolower(*q); p++, q++) ; if (!*q) - return (haystack); + return haystack; haystack++; } return NULL; } -char *strstrwrapper(char *haystack, char *needle, char *rev_start, int line_pos) +const char *strstrwrapper(const char *haystack, const char *needle, + const char *rev_start, int line_pos) { - #ifdef HAVE_REGEX_H - int result; - if (ISSET(USE_REGEXP)) { if (!ISSET(REVERSE_SEARCH)) { - result = regexec(&search_regexp, haystack, 10, regmatches, (line_pos > 0) ? REG_NOTBOL : 0); - if (!result) + if (!regexec(&search_regexp, haystack, 10, regmatches, (line_pos > 0) ? REG_NOTBOL : 0)) return haystack + regmatches[0].rm_so; + } #ifndef NANO_SMALL - } else { - char *i, *j; + else { + const char *i, *j; /* do a quick search forward first */ - if (!(regexec(&search_regexp, haystack, 10, regmatches, 0))) { + if (!regexec(&search_regexp, haystack, 10, regmatches, 0)) { /* there's a match somewhere in the line - now search for it backwards, much slower */ for (i = rev_start; i >= haystack; --i) { - if (!(result = regexec(&search_regexp, i, 10, regmatches, (i > haystack) ? REG_NOTBOL : 0))) { + if (!regexec(&search_regexp, i, 10, regmatches, (i > haystack) ? REG_NOTBOL : 0)) { j = i + regmatches[0].rm_so; if (j <= rev_start) return j; } } } -#endif } +#endif return 0; } #endif @@ -174,7 +173,6 @@ char *strstrwrapper(char *haystack, char *needle, char *rev_start, int line_pos) return revstrstr(haystack, needle, rev_start); else return strstr(haystack, needle); - } else { if (ISSET(REVERSE_SEARCH)) return revstristr(haystack, needle, rev_start); @@ -188,8 +186,7 @@ char *strstrwrapper(char *haystack, char *needle, char *rev_start, int line_pos) /* 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. - */ + * screen. Note that nperror causes the window to flicker once. */ void nperror(const char *s) { /* leave ncurses mode, go to the terminal */ if (endwin() != ERR) { @@ -212,17 +209,17 @@ void *nmalloc(size_t howmuch) } /* We're going to need this too - Hopefully this will minimize - the transition cost of moving to the apropriate function. */ + the transition cost of moving to the appropriate function. */ char *charalloc(size_t howmuch) { - void *r; + char *r; /* Panic save? */ - if (!(r = calloc(howmuch, sizeof (char)))) + if (!(r = (char *)calloc(howmuch, sizeof (char)))) die(_("nano: calloc: out of memory!")); - return (char *) r; + return r; } void *nrealloc(void *ptr, size_t howmuch) @@ -235,24 +232,18 @@ void *nrealloc(void *ptr, size_t howmuch) return r; } -/* Copy one malloc()ed string to another pointer. - - Should be used as dest = mallocstrcpy(dest, src); -*/ -void *mallocstrcpy(char *dest, char *src) +/* Copy one malloc()ed string to another pointer. Should be used as: + * dest = mallocstrcpy(dest, src); */ +char *mallocstrcpy(char *dest, const char *src) { - - if (src == dest) - return src; + return dest; - if (dest != NULL) + if (dest) free(dest); - if (src == NULL) { - dest = NULL; - return(dest); - } + if (!src) + return NULL; dest = charalloc(strlen(src) + 1); strcpy(dest, src); @@ -260,8 +251,7 @@ void *mallocstrcpy(char *dest, char *src) return dest; } - -/* Append a new magic-line to filebot */ +/* Append a new magic-line to filebot. */ void new_magicline(void) { filebot->next = nmalloc(sizeof(filestruct)); @@ -292,22 +282,22 @@ void new_magicline(void) */ int check_wildcard_match(const char *text, const char *pattern) { - const char *retryPat; - const char *retryText; + const char *retrypat; + const char *retrytext; int ch; int found; int len; - retryPat = NULL; - retryText = NULL; + retrypat = NULL; + retrytext = NULL; while (*text || *pattern) { ch = *pattern++; switch (ch) { case '*': - retryPat = pattern; - retryText = text; + retrypat = pattern; + retrytext = text; break; case '[': @@ -361,15 +351,15 @@ int check_wildcard_match(const char *text, const char *pattern) } if (*text) { - pattern = retryPat; - text = ++retryText; + pattern = retrypat; + text = ++retrytext; break; } return FALSE; } - if (pattern == NULL) + if (!pattern) return FALSE; } diff --git a/winio.c b/winio.c index 08b0c664..744d9d2e 100644 --- a/winio.c +++ b/winio.c @@ -36,16 +36,13 @@ #define _(string) (string) #endif - /* winio.c statics */ static int statblank = 0; /* Number of keystrokes left after we call statusbar(), before we actually blank the statusbar */ /* Local Function Prototypes for only winio.c */ -inline int get_page_from_virtual(int virtual); -inline int get_page_start_virtual(int page); -inline int get_page_end_virtual(int page); +static int get_page_start(int column); /* Window I/O */ @@ -67,6 +64,7 @@ int do_last_line(void) return 1; } + /* Like xplustabs, but for a specific index of a specific filestruct */ int xpt(const filestruct *fileptr, int index) { @@ -87,102 +85,89 @@ int xpt(const filestruct *fileptr, int index) ; else if (iscntrl((int) fileptr->data[i])) tabs++; - } - + } + return tabs; } -/* Return the actual place on the screen of current->data[current_x], which - should always be > current_x */ -int xplustabs(void) +/* Return the placewewant associated with current_x. That is, xplustabs + * is the zero-based column position of the cursor. Value is no smaller + * than current_x. */ +size_t xplustabs(void) { - return xpt(current, current_x); + return strnlenpt(current->data, current_x); } -/* Return what current_x should be, given xplustabs() for the line, - * given a start position in the filestruct's data */ -int actual_x_from_start(filestruct * fileptr, int xplus, int start) +/* Return what current_x should be, given xplustabs() for the line. */ +size_t actual_x(const filestruct *fileptr, size_t xplus) { - int i, tot = 1; + size_t i = 0; + /* the position in fileptr->data, returned */ + size_t length = 0; + /* the screen display width to data[i] */ + char *c; + /* fileptr->data + i */ - if (fileptr == NULL || fileptr->data == NULL) - return 0; + assert(fileptr != NULL && fileptr->data != NULL); - for (i = start; tot <= xplus && fileptr->data[i] != 0; i++, tot++) - if (fileptr->data[i] == NANO_CONTROL_I) { - if (tot % tabsize != 0) - tot += tabsize - (tot % tabsize); - } else if (fileptr->data[i] & 0x80) - tot++; /* Make 8 bit chars only 1 column (again) */ - else if (iscntrl((int) fileptr->data[i])) { - i++; - tot += 2; - } + for (c = fileptr->data; length < xplus && *c != '\0'; i++, c++) { + if (*c == '\t') + length += tabsize - length % tabsize; + else if (iscntrl((int)*c)) + length += 2; + else if ((unsigned char) *c >= 0x80 && (unsigned char) *c <= 0x9f) + length += 4; + else + length++; + } + assert(length == strnlenpt(fileptr->data, i)); + assert(i <= strlen(fileptr->data)); - if (i > strlen(fileptr->data)) - i = strlen(fileptr->data); - - /* see if we're in the x-plus-tabs column of xplus; if not, look - for the closest column to it */ - if (xpt(fileptr, i) < xplus) { - while (xpt(fileptr, i) < xplus && i < strlen(fileptr->data)) - i++; - } - else if (xpt(fileptr, i) > xplus) { - while (xpt(fileptr, i) > xplus && i > start) - i--; - } + if (length > xplus) + i--; #ifdef DEBUG - fprintf(stderr, _("actual_x_from_start for xplus=%d returned %d\n"), - xplus, i); + fprintf(stderr, _("actual_x for xplus=%d returns %d\n"), xplus, i); #endif - return i - start; + return i; } -/* Opposite of xplustabs */ -int actual_x(filestruct * fileptr, int xplus) +/* A strlen with tabs factored in, similar to xplustabs(). */ +size_t strnlenpt(const char *buf, size_t size) { - return actual_x_from_start(fileptr, xplus, 0); -} + size_t length = 0; -/* a strlen with tabs factored in, similar to xplustabs() */ -int strnlenpt(char *buf, int size) -{ - int i, tabs = 0; - - if (buf == NULL) - return 0; - - for (i = 0; i < size; i++) { - tabs++; - - if (buf[i] == NANO_CONTROL_I) { - if (tabs % tabsize == 0); + if (buf != NULL) + for (; *buf != '\0' && size != 0; size--, buf++) { + if (*buf == '\t') + length += tabsize - (length % tabsize); + else if (iscntrl((int)*buf)) + length += 2; + else if ((unsigned char) *buf >= 0x80 && (unsigned char) *buf <= 0x9f) + length += 4; else - tabs += tabsize - (tabs % tabsize); - } else if (buf[i] & 0x80) - /* Make 8 bit chars only 1 column! */ - ; - else if (iscntrl((int) buf[i])) - tabs++; - } - - return tabs; + length++; + } + return length; } -int strlenpt(char *buf) +size_t strlenpt(const char *buf) { - return strnlenpt(buf, strlen(buf)); + return strnlenpt(buf, -1); } -/* resets current_y, based on the position of current, and puts the cursor at - (current_y, current_x) */ +/* Resets current_y, based on the position of current, and puts the + * cursor at (current_y, current_x). */ void reset_cursor(void) { - filestruct *ptr = edittop; - int x; + const filestruct *ptr = edittop; + size_t x; + + /* Yuck. This condition can be true after open_file when opening the + * first file. */ + if (edittop == NULL) + return; current_y = 0; @@ -192,20 +177,15 @@ void reset_cursor(void) } x = xplustabs(); - if (x <= COLS - 2) - wmove(edit, current_y, x); - else - wmove(edit, current_y, x - - get_page_start_virtual(get_page_from_virtual(x))); - + wmove(edit, current_y, x - get_page_start(x)); } void blank_bottombars(void) { - int i = no_help()? 3 : 1; - - for (; i <= 2; i++) - mvwaddstr(bottomwin, i, 0, hblank); + if (!no_help()) { + mvwaddstr(bottomwin, 1, 0, hblank); + mvwaddstr(bottomwin, 2, 0, hblank); + } } void blank_edit(void) @@ -229,7 +209,6 @@ void blank_statusbar_refresh(void) void check_statblank(void) { - if (statblank > 1) statblank--; else if (statblank == 1 && !ISSET(CONSTUPDATE)) { @@ -238,70 +217,63 @@ void check_statblank(void) } } -/* Repaint the statusbar when getting a character in nanogetstr */ -void nanoget_repaint(char *buf, char *inputbuf, int x) +/* Repaint the statusbar when getting a character in nanogetstr. buf + * should be no longer than COLS - 4. + * + * Note that we must turn on A_REVERSE here, since do_help turns it + * off! */ +static void nanoget_repaint(const char *buf, const char *inputbuf, + int x) { - int len = strlen(buf); + int len = strlen(buf) + 2; int wid = COLS - len; -#ifdef ENABLE_COLOR - color_on(bottomwin, COLOR_STATUSBAR); -#else + assert(wid >= 2); + assert(0 <= x && x <= strlen(inputbuf)); + wattron(bottomwin, A_REVERSE); -#endif blank_statusbar(); - - if (x <= COLS - 1) { - /* Black magic */ - buf[len - 1] = ' '; - - mvwaddstr(bottomwin, 0, 0, buf); - waddnstr(bottomwin, inputbuf, wid); - wmove(bottomwin, 0, (x % COLS)); - } else { - /* Black magic */ - buf[len - 1] = '$'; - - mvwaddstr(bottomwin, 0, 0, buf); - waddnstr(bottomwin, &inputbuf[wid * ((x - len) / (wid))], wid); - wmove(bottomwin, 0, ((x - len) % wid) + len); - } - -#ifdef ENABLE_COLOR - color_off(bottomwin, COLOR_STATUSBAR); -#else + mvwaddstr(bottomwin, 0, 0, buf); + waddch(bottomwin, ':'); + waddch(bottomwin, x < wid ? ' ' : '$'); + waddnstr(bottomwin, &inputbuf[wid * (x / wid)], wid); + wmove(bottomwin, 0, (x % wid) + len); wattroff(bottomwin, A_REVERSE); -#endif } -/* Get the input from the kb; this should only be called from statusq */ -int nanogetstr(int allowtabs, char *buf, char *def, shortcut *s, - int start_x, int list) -{ - int kbinput = 0, x = 0, xend, slen; - int x_left = 0, inputlen, tabbed = 0; - char *inputbuf; - shortcut *t; +/* Get the input from the kb; this should only be called from + * statusq(). */ +static int nanogetstr(int allowtabs, const char *buf, const char *def, + const shortcut *s #ifndef DISABLE_TABCOMP - int shift = 0; + , int *list #endif + ) +{ + int kbinput; + int x; + /* the cursor position in 'answer' */ + int xend; + /* length of 'answer', the status bar text */ + int tabbed = 0; + /* used by input_tab() */ + const shortcut *t; - slen = length_of_list(s); - inputbuf = charalloc(strlen(def) + 1); - inputbuf[0] = '\0'; - - x_left = strlen(buf); - x = strlen(def) + x_left; + xend = strlen(def); + x = xend; + answer = (char *)nrealloc(answer, xend + 1); + if (xend > 0) + strcpy(answer, def); + else + answer[0] = '\0'; #if !defined(DISABLE_HELP) || !defined(DISABLE_MOUSE) currshortcut = s; #endif /* Get the input! */ - if (strlen(def) > 0) - strcpy(inputbuf, def); - nanoget_repaint(buf, inputbuf, x); + nanoget_repaint(buf, answer, x); /* Make sure any editor screen updates are displayed before getting input */ wrefresh(edit); @@ -321,15 +293,10 @@ int nanogetstr(int allowtabs, char *buf, char *def, shortcut *s, break; } #endif - - /* We shouldn't discard the answer it gave, just because - we hit a keystroke, GEEZ! */ - answer = mallocstrcpy(answer, inputbuf); - free(inputbuf); return t->val; } } - xend = strlen(buf) + strlen(inputbuf); + assert(0 <= x && x <= xend && xend == strlen(answer)); if (kbinput != '\t') tabbed = 0; @@ -358,65 +325,55 @@ int nanogetstr(int allowtabs, char *buf, char *def, shortcut *s, #endif case NANO_HOME_KEY: case KEY_HOME: - x = x_left; + x = 0; break; case NANO_END_KEY: case KEY_END: - x = x_left + strlen(inputbuf); + x = xend; break; case KEY_RIGHT: case NANO_FORWARD_KEY: - if (x < xend) x++; - wmove(bottomwin, 0, x); break; case NANO_CONTROL_D: - if (strlen(inputbuf) > 0 && (x - x_left) != strlen(inputbuf)) { - memmove(inputbuf + (x - x_left), - inputbuf + (x - x_left) + 1, - strlen(inputbuf) - (x - x_left) - 1); - inputbuf[strlen(inputbuf) - 1] = '\0'; + if (x < xend) { + memmove(answer + x, answer + x + 1, xend - x); + xend--; } break; case NANO_CONTROL_K: case NANO_CONTROL_U: - *inputbuf = 0; - x = x_left; + null_at(&answer, 0); + xend = 0; + x = 0; break; case KEY_BACKSPACE: case 127: case NANO_CONTROL_H: - if (strlen(inputbuf) > 0) { - if (x == (x_left + strlen(inputbuf))) - inputbuf[strlen(inputbuf) - 1] = '\0'; - else if (x - x_left) { - memmove(inputbuf + (x - x_left) - 1, - inputbuf + (x - x_left), - strlen(inputbuf) - (x - x_left)); - inputbuf[strlen(inputbuf) - 1] = '\0'; - } - } - if (x > strlen(buf)) + if (x > 0) { + memmove(answer + x - 1, answer + x, xend - x + 1); x--; + xend--; + } break; #ifndef DISABLE_TABCOMP case NANO_CONTROL_I: if (allowtabs) { - shift = 0; - inputbuf = input_tab(inputbuf, (x - x_left), - &tabbed, &shift, &list); + int shift = 0; + + answer = input_tab(answer, x, &tabbed, &shift, list); + xend = strlen(answer); x += shift; - if (x - x_left > strlen(inputbuf)) - x = strlen(inputbuf) + x_left; + if (x > xend) + x = xend; } break; #endif case KEY_LEFT: case NANO_BACK_KEY: - if (x > strlen(buf)) + if (x > 0) x--; - wmove(bottomwin, 0, x); break; case KEY_UP: case KEY_DOWN: @@ -430,10 +387,10 @@ int nanogetstr(int allowtabs, char *buf, char *def, shortcut *s, case 'O': switch (kbinput = wgetch(edit)) { case 'F': - x = x_left + strlen(inputbuf); + x = xend; break; case 'H': - x = x_left; + x = 0; break; } break; @@ -442,30 +399,25 @@ int nanogetstr(int allowtabs, char *buf, char *def, shortcut *s, case 'C': if (x < xend) x++; - wmove(bottomwin, 0, x); break; case 'D': - if (x > strlen(buf)) + if (x > 0) x--; - wmove(bottomwin, 0, x); break; case '1': case '7': - x = x_left; + x = 0; goto skip_tilde; case '3': do_deletekey: - if (strlen(inputbuf) > 0 - && (x - x_left) != strlen(inputbuf)) { - memmove(inputbuf + (x - x_left), - inputbuf + (x - x_left) + 1, - strlen(inputbuf) - (x - x_left) - 1); - inputbuf[strlen(inputbuf) - 1] = '\0'; + if (x < xend) { + memmove(answer + x, answer + x + 1, xend - x); + xend--; } goto skip_tilde; case '4': case '8': - x = x_left + strlen(inputbuf); + x = xend; goto skip_tilde; skip_tilde: nodelay(edit, TRUE); @@ -475,6 +427,7 @@ int nanogetstr(int allowtabs, char *buf, char *def, shortcut *s, nodelay(edit, FALSE); break; } + break; default: for (t = s; t != NULL; t = t->next) { @@ -483,118 +436,82 @@ int nanogetstr(int allowtabs, char *buf, char *def, shortcut *s, kbinput); #endif if (kbinput == t->val || kbinput == t->val - 32) { - /* We hit an Alt key. Do like above. We don't just ungetch the letter and let it get caught above cause that screws the keypad... */ - answer = mallocstrcpy(answer, inputbuf); - free(inputbuf); return t->val; } } - } break; default: - if (kbinput < 32) break; - - inputlen = strlen(inputbuf); - inputbuf = nrealloc(inputbuf, inputlen + 2); - - memmove(&inputbuf[x - x_left + 1], - &inputbuf[x - x_left], inputlen - (x - x_left) + 1); - inputbuf[x - x_left] = kbinput; - + answer = nrealloc(answer, xend + 2); + memmove(answer + x + 1, answer + x, xend - x + 1); + xend++; + answer[x] = kbinput; x++; #ifdef DEBUG fprintf(stderr, _("input \'%c\' (%d)\n"), kbinput, kbinput); #endif } - nanoget_repaint(buf, inputbuf, x); + nanoget_repaint(buf, answer, x); wrefresh(bottomwin); - } -#ifndef DISABLE_TABCOMP - /* if we've done tab completion, there might be a list of filename - matches on the edit window at this point; make sure they're - cleared off */ - if (list) - edit_refresh(); -#endif + } /* while (kbinput ...) */ - answer = mallocstrcpy(answer, inputbuf); - free(inputbuf); - - /* In pico mode, just check for a blank answer here */ + /* In Pico mode, just check for a blank answer here */ if (ISSET(PICO_MODE) && answer[0] == '\0') return -2; else return 0; } -void horizbar(WINDOW * win, int y) -{ - wattron(win, A_REVERSE); - mvwaddstr(win, 0, 0, hblank); - wattroff(win, A_REVERSE); -} - -void titlebar(char *path) +void titlebar(const char *path) { int namelen, space; - char *what = path; + const char *what = path; if (path == NULL) what = filename; -#ifdef ENABLE_COLOR - color_on(topwin, COLOR_TITLEBAR); - mvwaddstr(topwin, 0, 0, hblank); -#else - horizbar(topwin, 0); wattron(topwin, A_REVERSE); -#endif + mvwaddstr(topwin, 0, 0, hblank); mvwaddnstr(topwin, 0, 2, VERMSG, COLS - 3); - space = COLS - strlen(VERMSG) - strlen(VERSION) - 21; + space = COLS - sizeof(VERMSG) - 22; namelen = strlen(what); if (space > 0) { if (what[0] == '\0') - mvwaddstr(topwin, 0, COLS / 2 - 6, _("New Buffer")); - else { - if (namelen > space) { - if (path == NULL) - waddstr(topwin, _(" File: ...")); - else - waddstr(topwin, _(" DIR: ...")); - waddstr(topwin, &what[namelen - space]); - } else { - if (path == NULL) - mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1), - _("File: ")); - else - mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1), - _(" DIR: ")); - waddstr(topwin, what); - } + mvwaddnstr(topwin, 0, COLS / 2 - 6, _("New Buffer"), + COLS / 2 + COLS % 2 - 6); + else if (namelen > space) { + if (path == NULL) + waddstr(topwin, _(" File: ...")); + else + waddstr(topwin, _(" DIR: ...")); + waddstr(topwin, &what[namelen - space]); + } else { + if (path == NULL) + mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1), + _("File: ")); + else + mvwaddstr(topwin, 0, COLS / 2 - (namelen / 2 + 1), + _(" DIR: ")); + waddstr(topwin, what); } } /* If we don't have space, we shouldn't bother */ if (ISSET(MODIFIED)) - mvwaddstr(topwin, 0, COLS - 11, _(" Modified ")); + mvwaddnstr(topwin, 0, COLS - 11, _(" Modified "), 11); else if (ISSET(VIEW_MODE)) - mvwaddstr(topwin, 0, COLS - 11, _(" View ")); + mvwaddnstr(topwin, 0, COLS - 11, _(" View "), 11); -#ifdef ENABLE_COLOR - color_off(topwin, COLOR_TITLEBAR); -#else wattroff(topwin, A_REVERSE); -#endif wrefresh(topwin); reset_cursor(); @@ -620,7 +537,7 @@ static void onekey(const char *keystroke, const char *desc, int len) } } -void clear_bottomwin(void) +static void clear_bottomwin(void) { if (ISSET(NO_HELP)) return; @@ -644,13 +561,6 @@ void bottombars(const shortcut *s) } else slen = length_of_list(s); -#ifdef ENABLE_COLOR - color_on(bottomwin, COLOR_BOTTOMBARS); - if (!colors[COLOR_BOTTOMBARS - FIRST_COLORNUM].set || - colors[COLOR_BOTTOMBARS - FIRST_COLORNUM].fg != COLOR_BLACK) - wattroff(bottomwin, A_REVERSE); -#endif - /* There will be this many columns of shortcuts */ numcols = (slen + (slen % 2)) / 2; @@ -681,16 +591,12 @@ void bottombars(const shortcut *s) goto break_completely_out; } } -break_completely_out: - -#ifdef ENABLE_COLOR - color_off(bottomwin, COLOR_BOTTOMBARS); -#endif + break_completely_out: wrefresh(bottomwin); } -/* If modified is not already set, set it and update titlebar */ +/* If modified is not already set, set it and update titlebar. */ void set_modified(void) { if (!ISSET(MODIFIED)) { @@ -735,6 +641,13 @@ inline int get_page_end_virtual(int page) return get_page_start_virtual(page) + COLS - 1; } +static int get_page_start(int column) +{ + assert(COLS > 9); + return column < COLS - 1 ? 0 : column - 7 - (column - 8) % (COLS - 9); +} + + #ifndef NANO_SMALL /* This takes care of the case where there is a mark that covers only */ /* the current line. */ @@ -781,20 +694,11 @@ void add_marked_sameline(int begin, int end, filestruct * fileptr, int y, sel_data_len = end - begin; post_data_len = this_page_end - end; -#ifdef ENABLE_COLOR - color_on(edit, COLOR_MARKER); -#else wattron(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ - mvwaddnstr(edit, y, begin - this_page_start, &fileptr->data[begin], sel_data_len); -#ifdef ENABLE_COLOR - color_off(edit, COLOR_MARKER); -#else wattroff(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ } #endif @@ -809,7 +713,7 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, { #ifdef ENABLE_COLOR - colortype *tmpcolor = NULL; + const colortype *tmpcolor = NULL; int k, paintlen; filestruct *e, *s; regoff_t ematch, smatch; @@ -1011,19 +915,12 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, if (fileptr != mark_beginbuf && fileptr != current) { /* We are on a completely marked line, paint it all * inverse */ -#ifdef ENABLE_COLOR - color_on(edit, COLOR_MARKER); -#else + wattron(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ mvwaddnstr(edit, yval, 0, fileptr->data, COLS); -#ifdef ENABLE_COLOR - color_off(edit, COLOR_MARKER); -#else wattroff(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ } else if (fileptr == mark_beginbuf && fileptr == current) { /* Special case, we're still on the same line we started @@ -1051,11 +948,8 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, int target; if (mark_beginbuf->lineno > current->lineno) { -#ifdef ENABLE_COLOR - color_on(edit, COLOR_MARKER); -#else + wattron(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ target = (virt_mark_beginx < @@ -1063,22 +957,13 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, mvwaddnstr(edit, yval, 0, fileptr->data, target); -#ifdef ENABLE_COLOR - color_off(edit, COLOR_MARKER); -#else wattroff(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ - } if (mark_beginbuf->lineno < current->lineno) { -#ifdef ENABLE_COLOR - color_on(edit, COLOR_MARKER); -#else - wattron(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ + wattron(edit, A_REVERSE); target = (COLS - 1) - virt_mark_beginx; if (target < 0) @@ -1087,12 +972,7 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, mvwaddnstr(edit, yval, virt_mark_beginx, &fileptr->data[virt_mark_beginx], target); -#ifdef ENABLE_COLOR - color_off(edit, COLOR_MARKER); -#else wattroff(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ - } } else if (fileptr == current) { @@ -1103,11 +983,7 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, if (mark_beginbuf->lineno < current->lineno) { -#ifdef ENABLE_COLOR - color_on(edit, COLOR_MARKER); -#else wattron(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ if (virt_cur_x > COLS - 2) { mvwaddnstr(edit, yval, 0, @@ -1116,22 +992,13 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, } else mvwaddnstr(edit, yval, 0, fileptr->data, virt_cur_x); -#ifdef ENABLE_COLOR - color_off(edit, COLOR_MARKER); -#else wattroff(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ } if (mark_beginbuf->lineno > current->lineno) { -#ifdef ENABLE_COLOR - color_on(edit, COLOR_MARKER); -#else wattron(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ - if (virt_cur_x > COLS - 2) mvwaddnstr(edit, yval, virt_cur_x - this_page_start, &fileptr->data[virt_cur_x], @@ -1141,11 +1008,7 @@ void edit_add(filestruct * fileptr, int yval, int start, int virt_cur_x, &fileptr->data[virt_cur_x], COLS - virt_cur_x); -#ifdef ENABLE_COLOR - color_off(edit, COLOR_MARKER); -#else wattroff(edit, A_REVERSE); -#endif /* ENABLE_COLOR */ } } @@ -1267,47 +1130,48 @@ void center_cursor(void) wmove(edit, current_y, current_x); } -/* Refresh the screen without changing the position of lines */ +/* Refresh the screen without changing the position of lines. */ void edit_refresh(void) { static int noloop = 0; - int nlines = 0, i = 0, currentcheck = 0; - filestruct *temp, *hold = current; + int nlines = 0, currentcheck = 0; + /* Neither of these conditions should occur, but they do. edittop is + * NULL when you open an existing file on the command line, and + * ENABLE_COLOR is defined. Yuck. */ if (current == NULL) return; + if (edittop == NULL) + edittop = current; - temp = edittop; - - while (nlines <= editwinrows - 1 && nlines <= totlines && temp != NULL) { - hold = temp; - update_line(temp, current_x); - if (temp == current) + editbot = edittop; + while (nlines < editwinrows) { + update_line(editbot, current_x); + if (editbot == current) currentcheck = 1; - temp = temp->next; nlines++; + + if (editbot->next == NULL) + break; + editbot = editbot->next; } /* If noloop == 1, then we already did an edit_update without finishing this function. So we don't run edit_update again */ - if (!currentcheck && !noloop) { /* Then current has run off the screen... */ + if (!currentcheck && !noloop) { + /* Then current has run off the screen... */ edit_update(current, CENTER); noloop = 1; } else if (noloop) noloop = 0; - if (nlines <= editwinrows - 1) - while (nlines <= editwinrows - 1) { - mvwaddstr(edit, nlines, i, hblank); - nlines++; - } - if (temp == NULL) - editbot = hold; - else - editbot = temp; + while (nlines < editwinrows) { + mvwaddstr(edit, nlines, 0, hblank); + nlines++; + } /* What the hell are we expecting to update the screen if this isn't - here? luck?? */ + here? Luck?? */ wrefresh(edit); } @@ -1325,34 +1189,25 @@ void edit_refresh_clearok(void) * Nice generic routine to update the edit buffer, given a pointer to the * file struct =) */ -void edit_update(filestruct * fileptr, int topmidbotnone) +void edit_update(filestruct *fileptr, topmidbotnone location) { - int i = 0; - filestruct *temp; - if (fileptr == NULL) return; - temp = fileptr; - if (topmidbotnone == TOP); - else if (topmidbotnone == NONE) - for (i = 0; i <= current_y - 1 && temp->prev != NULL; i++) - temp = temp->prev; - else if (topmidbotnone == BOTTOM) - for (i = 0; i <= editwinrows - 1 && temp->prev != NULL; i++) - temp = temp->prev; - else - for (i = 0; i <= editwinrows / 2 && temp->prev != NULL; i++) - temp = temp->prev; + if (location != TOP) { + int goal = location == NONE ? current_y - 1 : editwinrows / 2; - edittop = temp; + for (; goal >= 0 && fileptr->prev != NULL; goal--) + fileptr = fileptr->prev; + } + edittop = fileptr; fix_editbot(); edit_refresh(); } -/* This function updates current, based on where current_y is; reset_cursor - does the opposite */ +/* This function updates current, based on where current_y is; + * reset_cursor() does the opposite. */ void update_cursor(void) { int i = 0; @@ -1363,7 +1218,7 @@ void update_cursor(void) #endif current = edittop; - while (i <= current_y - 1 && current->next != NULL) { + while (i < current_y && current->next != NULL) { current = current->next; i++; } @@ -1371,7 +1226,6 @@ void update_cursor(void) #ifdef DEBUG fprintf(stderr, _("current->data = \"%s\"\n"), current->data); #endif - } /* @@ -1382,12 +1236,12 @@ void update_cursor(void) * * New arg tabs tells whether or not to allow tab completion. */ -int statusq(int tabs, shortcut *s, char *def, char *msg, ...) +int statusq(int tabs, const shortcut *s, const char *def, + const char *msg, ...) { va_list ap; - char foo[133]; + char *foo = charalloc(COLS - 3); int ret; - #ifndef DISABLE_TABCOMP int list = 0; #endif @@ -1395,34 +1249,18 @@ int statusq(int tabs, shortcut *s, char *def, char *msg, ...) bottombars(s); va_start(ap, msg); - vsnprintf(foo, 132, msg, ap); + vsnprintf(foo, COLS - 4, msg, ap); va_end(ap); - strncat(foo, ": ", 132); - -#ifdef ENABLE_COLOR - color_on(bottomwin, COLOR_STATUSBAR); -#else - wattron(bottomwin, A_REVERSE); -#endif - + foo[COLS - 4] = '\0'; #ifndef DISABLE_TABCOMP - ret = nanogetstr(tabs, foo, def, s, (strlen(foo) + 3), list); + ret = nanogetstr(tabs, foo, def, s, &list); #else - /* if we've disabled tab completion, the value of list won't be - used at all, so it's safe to use 0 (NULL) as a placeholder */ - ret = nanogetstr(tabs, foo, def, s, (strlen(foo) + 3), 0); + ret = nanogetstr(tabs, foo, def, s); #endif - -#ifdef ENABLE_COLOR - color_off(bottomwin, COLOR_STATUSBAR); -#else - wattroff(bottomwin, A_REVERSE); -#endif - + free(foo); switch (ret) { - case NANO_FIRSTLINE_KEY: do_first_line(); break; @@ -1430,14 +1268,8 @@ int statusq(int tabs, shortcut *s, char *def, char *msg, ...) do_last_line(); break; case NANO_CANCEL_KEY: -#ifndef DISABLE_TABCOMP - /* if we've done tab completion, there might be a list of - filename matches on the edit window at this point; make sure - they're cleared off */ - if (list) - edit_refresh(); -#endif - return -1; + ret = -1; + break; default: blank_statusbar(); } @@ -1446,6 +1278,14 @@ int statusq(int tabs, shortcut *s, char *def, char *msg, ...) fprintf(stderr, _("I got \"%s\"\n"), answer); #endif +#ifndef DISABLE_TABCOMP + /* if we've done tab completion, there might be a list of + filename matches on the edit window at this point; make sure + they're cleared off */ + if (list) + edit_refresh(); +#endif + return ret; } @@ -1453,21 +1293,20 @@ int statusq(int tabs, shortcut *s, char *def, char *msg, ...) * 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, char *msg, ...) +int do_yesno(int all, int leavecursor, const char *msg, ...) { va_list ap; char foo[133]; int kbinput, ok = -1, i; - char *yesstr; /* String of yes characters accepted */ - char *nostr; /* Same for no */ - char *allstr; /* And all, surprise! */ + const char *yesstr; /* String of yes characters accepted */ + const char *nostr; /* Same for no */ + const char *allstr; /* And all, surprise! */ #ifndef DISABLE_MOUSE #ifdef NCURSES_MOUSE_VERSION MEVENT mevent; #endif #endif - /* Yes, no and all are strings of any length. Each string consists of all characters accepted as a valid character for that value. The first value will be the one displayed in the shortcuts. */ @@ -1478,10 +1317,6 @@ int do_yesno(int all, int leavecursor, char *msg, ...) /* Write the bottom of the screen */ clear_bottomwin(); -#ifdef ENABLE_COLOR - color_on(bottomwin, COLOR_BOTTOMBARS); -#endif - /* Remove gettext call for keybindings until we clear the thing up */ if (!ISSET(NO_HELP)) { char shortstr[3]; /* Temp string for Y, N, A */ @@ -1506,21 +1341,12 @@ int do_yesno(int all, int leavecursor, char *msg, ...) vsnprintf(foo, 132, msg, ap); va_end(ap); -#ifdef ENABLE_COLOR - color_off(bottomwin, COLOR_BOTTOMBARS); - color_on(bottomwin, COLOR_STATUSBAR); -#else wattron(bottomwin, A_REVERSE); -#endif /* ENABLE_COLOR */ blank_statusbar(); mvwaddstr(bottomwin, 0, 0, foo); -#ifdef ENABLE_COLOR - color_off(bottomwin, COLOR_STATUSBAR); -#else wattroff(bottomwin, A_REVERSE); -#endif wrefresh(bottomwin); @@ -1546,22 +1372,17 @@ int do_yesno(int all, int leavecursor, char *msg, ...) else { /* Rather than a bunch of if statements, set up a matrix - of possible return keystrokes based on the x and y values */ - if (all) { - char yesnosquare[2][2] = { - {yesstr[0], allstr[0]}, - {nostr[0], NANO_CONTROL_C} - }; - - ungetch(yesnosquare[mevent.y][mevent.x / (COLS / 6)]); - } else { - char yesnosquare[2][2] = { - {yesstr[0], '\0'}, - {nostr[0], NANO_CONTROL_C} - }; - - ungetch(yesnosquare[mevent.y][mevent.x / (COLS / 6)]); - } + of possible return keystrokes based on the x and y + values */ + char yesnosquare[2][2]; + yesnosquare[0][0] = yesstr[0]; + if (all) + yesnosquare[0][1] = allstr[0]; + else + yesnosquare[0][1] = '\0'; + yesnosquare[1][0] = nostr[0]; + yesnosquare[1][1] = NANO_CONTROL_C; + ungetch(yesnosquare[mevent.y][mevent.x / (COLS / 6)]); } break; #endif @@ -1603,38 +1424,37 @@ int do_yesno(int all, int leavecursor, char *msg, ...) return ok; } -void statusbar(char *msg, ...) +void statusbar(const char *msg, ...) { va_list ap; - char foo[133]; + char *foo; int start_x = 0; + size_t foo_len; + + assert(COLS >= 4); + foo = charalloc(COLS - 3); va_start(ap, msg); - vsnprintf(foo, 132, msg, ap); + vsnprintf(foo, COLS - 3, msg, ap); va_end(ap); - start_x = COLS / 2 - strlen(foo) / 2 - 1; + foo[COLS - 4] = '\0'; + foo_len = strlen(foo); + start_x = (COLS - foo_len - 4) / 2; /* Blank out line */ blank_statusbar(); wmove(bottomwin, 0, start_x); -#ifdef ENABLE_COLOR - color_on(bottomwin, COLOR_STATUSBAR); -#else wattron(bottomwin, A_REVERSE); -#endif waddstr(bottomwin, "[ "); waddstr(bottomwin, foo); + free(foo); waddstr(bottomwin, " ]"); -#ifdef ENABLE_COLOR - color_off(bottomwin, COLOR_STATUSBAR); -#else wattroff(bottomwin, A_REVERSE); -#endif wrefresh(bottomwin); @@ -1666,12 +1486,6 @@ int total_refresh(void) return 1; } -void previous_line(void) -{ - if (current_y > 0) - current_y--; -} - int do_cursorpos(int constant) { filestruct *fileptr; @@ -1688,7 +1502,7 @@ int do_cursorpos(int constant) if (old_totsize == -1) old_totsize = totsize; - colpct = 100 * (xplustabs() + 1) / (xpt(current, strlen(current->data)) + 1); + colpct = 100 * (xplustabs() + 1) / (strlenpt(current->data) + 1); for (fileptr = fileage; fileptr != current && fileptr != NULL; fileptr = fileptr->next) @@ -1719,7 +1533,7 @@ int do_cursorpos(int constant) statusbar(_ ("line %d/%d (%.0f%%), col %ld/%ld (%.0f%%), char %ld/%ld (%.0f%%)"), current->lineno, totlines, linepct, xplustabs() + 1, - xpt(current, strlen(current->data)) + 1, colpct, i, j, bytepct); + strlenpt(current->data) + 1, colpct, i, j, bytepct); } old_i = i; @@ -1741,7 +1555,7 @@ int do_help(void) #ifndef DISABLE_HELP int i, j, row = 0, page = 1, kbinput = 0, no_more = 0, kp, kp2; int no_help_flag = 0; - shortcut *oldshortcut; + const shortcut *oldshortcut; blank_edit(); curs_set(0); @@ -1889,24 +1703,21 @@ int do_help(void) kp = keypad_on(edit, kp); kp2 = keypad_on(bottomwin, kp2); -#elif defined(DISABLE_HELP) - nano_disabled_msg(); -#endif - /* The help_init() at the beginning allocated help_text, which has now been written to screen. */ free(help_text); help_text = NULL; +#elif defined(DISABLE_HELP) + nano_disabled_msg(); +#endif + return 1; } -/* Dump the current file structure to stderr */ -void dump_buffer(filestruct * inptr) -{ #ifdef DEBUG - filestruct *fileptr; - +/* 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) @@ -1914,54 +1725,60 @@ void dump_buffer(filestruct * inptr) else fprintf(stderr, _("Dumping a buffer to stderr...\n")); - fileptr = inptr; - while (fileptr != NULL) { - fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data); - fflush(stderr); - fileptr = fileptr->next; + while (inptr != NULL) { + fprintf(stderr, "(%d) %s\n", inptr->lineno, inptr->data); + inptr = inptr->next; } -#endif /* DEBUG */ } +#endif /* DEBUG */ -void dump_buffer_reverse(filestruct * inptr) -{ #ifdef DEBUG - filestruct *fileptr; +void dump_buffer_reverse(void) { + const filestruct *fileptr = filebot; - fileptr = filebot; while (fileptr != NULL) { fprintf(stderr, "(%d) %s\n", fileptr->lineno, fileptr->data); - fflush(stderr); fileptr = fileptr->prev; } -#endif /* DEBUG */ } +#endif /* DEBUG */ /* Fix editbot, based on the assumption that edittop is correct */ void fix_editbot(void) { int i; + editbot = edittop; - for (i = 0; (i <= editwinrows - 1) && (editbot->next != NULL) - && (editbot != filebot); i++, editbot = editbot->next); + for (i = 0; i < editwinrows && editbot->next != NULL; i++) + editbot = editbot->next; } /* highlight the current word being replaced or spell checked */ -void do_replace_highlight(int highlight_flag, char *word) +void do_replace_highlight(int highlight_flag, const char *word) { char *highlight_word = NULL; - int x, y; + int x, y, word_len; highlight_word = mallocstrcpy(highlight_word, ¤t->data[current_x]); - highlight_word[strlen(word)] = '\0'; + +#ifdef HAVE_REGEX_H + if (ISSET(USE_REGEXP)) + /* if we're using regexps, the highlight is the length of the + search result, not the length of the regexp string */ + word_len = regmatches[0].rm_eo - regmatches[0].rm_so; + else +#endif + word_len = strlen(word); + + highlight_word[word_len] = '\0'; /* adjust output when word extends beyond screen */ x = xplustabs(); - y = get_page_end_virtual(get_page_from_virtual(x)) + 1; + y = get_page_start(x) + COLS; - if ((COLS - (y - x) + strlen(word)) > COLS) { + if ((COLS - (y - x) + word_len) > COLS) { highlight_word[y - x - 1] = '$'; highlight_word[y - x] = '\0'; } @@ -1986,18 +1803,17 @@ void do_replace_highlight(int highlight_flag, char *word) void do_credits(void) { int i, j = 0, k, place = 0, start_x; - char *what; + const char *what; + const char *nanotext = _("The nano text editor"); + const char *version = _("version "); + const char *brought = _("Brought to you by:"); + const char *specialthx = _("Special thanks to:"); + const char *fsf = _("The Free Software Foundation"); + const char *ncurses = _("For ncurses:"); + const char *anyonelse = _("and anyone else we forgot..."); + const char *thankyou = _("Thank you for using nano!\n"); - char *nanotext = _("The nano text editor"); - char *version = _("version "); - char *brought = _("Brought to you by:"); - char *specialthx = _("Special thanks to:"); - char *fsf = _("The Free Software Foundation"); - char *ncurses = _("For ncurses:"); - char *anyonelse = _("and anyone else we forgot..."); - char *thankyou = _("Thank you for using nano!\n"); - - char *credits[CREDIT_LEN] = { nanotext, + const char *credits[CREDIT_LEN] = { nanotext, version, VERSION, "", @@ -2090,17 +1906,13 @@ void do_credits(void) int keypad_on(WINDOW * win, int newval) { - /* This is taken right from aumix. Don't sue me. */ #ifdef HAVE_USEKEYPAD - int old; - - old = win->_use_keypad; + int old = win->_use_keypad; keypad(win, newval); return old; #else keypad(win, newval); return 1; -#endif /* HAVE_USEKEYPAD */ - +#endif /* HAVE_USEKEYPAD */ }