"Fossies" - the Fresh Open Source Software Archive

Member "nano-4.5/src/text.c" (4 Oct 2019, 95759 Bytes) of package /linux/misc/nano-4.5.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "text.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.4_vs_4.5.

    1 /**************************************************************************
    2  *   text.c  --  This file is part of GNU nano.                           *
    3  *                                                                        *
    4  *   Copyright (C) 1999-2011, 2013-2019 Free Software Foundation, Inc.    *
    5  *   Copyright (C) 2014-2015 Mark Majeres                                 *
    6  *   Copyright (C) 2016 Mike Scalora                                      *
    7  *   Copyright (C) 2016 Sumedh Pendurkar                                  *
    8  *   Copyright (C) 2018 Marco Diego Aurélio Mesquita                      *
    9  *   Copyright (C) 2015-2019 Benno Schulenberg                            *
   10  *                                                                        *
   11  *   GNU nano is free software: you can redistribute it and/or modify     *
   12  *   it under the terms of the GNU General Public License as published    *
   13  *   by the Free Software Foundation, either version 3 of the License,    *
   14  *   or (at your option) any later version.                               *
   15  *                                                                        *
   16  *   GNU nano is distributed in the hope that it will be useful,          *
   17  *   but WITHOUT ANY WARRANTY; without even the implied warranty          *
   18  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.              *
   19  *   See the GNU General Public License for more details.                 *
   20  *                                                                        *
   21  *   You should have received a copy of the GNU General Public License    *
   22  *   along with this program.  If not, see http://www.gnu.org/licenses/.  *
   23  *                                                                        *
   24  **************************************************************************/
   25 
   26 #include "proto.h"
   27 
   28 #include <errno.h>
   29 #include <fcntl.h>
   30 #include <string.h>
   31 #include <unistd.h>
   32 #include <sys/wait.h>
   33 
   34 #ifndef NANO_TINY
   35 static pid_t pid_of_command = -1;
   36         /* The PID of the forked process -- needed when wanting to abort it. */
   37 #endif
   38 #ifdef ENABLE_WORDCOMPLETION
   39 static int pletion_x = 0;
   40         /* The x position in pletion_line of the last found completion. */
   41 static completion_word *list_of_completions;
   42         /* A linked list of the completions that have been attempted. */
   43 #endif
   44 
   45 #ifndef NANO_TINY
   46 /* Toggle the mark. */
   47 void do_mark(void)
   48 {
   49     if (!openfile->mark) {
   50         openfile->mark = openfile->current;
   51         openfile->mark_x = openfile->current_x;
   52         statusbar(_("Mark Set"));
   53         openfile->kind_of_mark = HARDMARK;
   54     } else {
   55         openfile->mark = NULL;
   56         statusbar(_("Mark Unset"));
   57         refresh_needed = TRUE;
   58     }
   59 }
   60 #endif /* !NANO_TINY */
   61 
   62 #if defined(ENABLE_COLOR) || defined(ENABLE_SPELLER)
   63 /* Return an error message about invoking the given name.  The message
   64  * should not be freed; this leak is not worth the trouble avoiding. */
   65 const char *invocation_error(const char *name)
   66 {
   67     char *message, *invoke_error = _("Error invoking \"%s\"");
   68 
   69     message = charalloc(strlen(invoke_error) + strlen(name) + 1);
   70     sprintf(message, invoke_error, name);
   71     return message;
   72 }
   73 #endif
   74 
   75 /* Insert a tab.  If the TABS_TO_SPACES flag is set, insert the number
   76  * of spaces that a tab would normally take up. */
   77 void do_tab(void)
   78 {
   79 #ifdef ENABLE_COLOR
   80     if (openfile->syntax && openfile->syntax->tab)
   81         do_output(openfile->syntax->tab, strlen(openfile->syntax->tab), TRUE);
   82     else
   83 #endif
   84 #ifndef NANO_TINY
   85     if (ISSET(TABS_TO_SPACES)) {
   86         char *spaces = charalloc(tabsize + 1);
   87         size_t length = tabsize - (xplustabs() % tabsize);
   88 
   89         memset(spaces, ' ', length);
   90         spaces[length] = '\0';
   91 
   92         do_output(spaces, length, TRUE);
   93 
   94         free(spaces);
   95     } else
   96 #endif
   97         do_output((char *)"\t", 1, TRUE);
   98 }
   99 
  100 #ifndef NANO_TINY
  101 /* Add an indent to the given line. */
  102 void indent_a_line(linestruct *line, char *indentation)
  103 {
  104     size_t length = strlen(line->data);
  105     size_t indent_len = strlen(indentation);
  106 
  107     /* If the requested indentation is empty, don't change the line. */
  108     if (indent_len == 0)
  109         return;
  110 
  111     /* Add the fabricated indentation to the beginning of the line. */
  112     line->data = charealloc(line->data, length + indent_len + 1);
  113     memmove(line->data + indent_len, line->data, length + 1);
  114     strncpy(line->data, indentation, indent_len);
  115 
  116     openfile->totsize += indent_len;
  117 
  118     /* Compensate for the change in the current line. */
  119     if (line == openfile->mark && openfile->mark_x > 0)
  120         openfile->mark_x += indent_len;
  121     if (line == openfile->current && openfile->current_x > 0) {
  122         openfile->current_x += indent_len;
  123         openfile->placewewant = xplustabs();
  124     }
  125 }
  126 
  127 /* Indent the current line (or the marked lines) by tabsize columns.
  128  * This inserts either a tab character or a tab's worth of spaces,
  129  * depending on whether --tabstospaces is in effect. */
  130 void do_indent(void)
  131 {
  132     char *indentation;
  133     linestruct *top, *bot, *line;
  134 
  135     /* Use either all the marked lines or just the current line. */
  136     get_range((const linestruct **)&top, (const linestruct **)&bot);
  137 
  138     /* Skip any leading empty lines. */
  139     while (top != bot->next && top->data[0] == '\0')
  140         top = top->next;
  141 
  142     /* If all lines are empty, there is nothing to do. */
  143     if (top == bot->next)
  144         return;
  145 
  146     indentation = charalloc(tabsize + 1);
  147 
  148     /* Set the indentation to either a bunch of spaces or a single tab. */
  149 #ifdef ENABLE_COLOR
  150     if (openfile->syntax && openfile->syntax->tab)
  151         indentation = mallocstrcpy(indentation, openfile->syntax->tab);
  152     else
  153 #endif
  154     if (ISSET(TABS_TO_SPACES)) {
  155         memset(indentation, ' ', tabsize);
  156         indentation[tabsize] = '\0';
  157     } else {
  158         indentation[0] = '\t';
  159         indentation[1] = '\0';
  160     }
  161 
  162     add_undo(INDENT);
  163 
  164     /* Go through each of the lines, adding an indent to the non-empty ones,
  165      * and recording whatever was added in the undo item. */
  166     for (line = top; line != bot->next; line = line->next) {
  167         char *real_indent = (line->data[0] == '\0') ? "" : indentation;
  168 
  169         indent_a_line(line, real_indent);
  170         update_multiline_undo(line->lineno, real_indent);
  171     }
  172 
  173     free(indentation);
  174 
  175     set_modified();
  176     ensure_firstcolumn_is_aligned();
  177     refresh_needed = TRUE;
  178     shift_held = TRUE;
  179 }
  180 
  181 /* Return the number of bytes of whitespace at the start of the given text,
  182  * but at most a tab's worth. */
  183 size_t length_of_white(const char *text)
  184 {
  185     size_t white_count = 0;
  186 
  187 #ifdef ENABLE_COLOR
  188     if (openfile->syntax && openfile->syntax->tab) {
  189         size_t thelength = strlen(openfile->syntax->tab);
  190 
  191         while (text[white_count] == openfile->syntax->tab[white_count])
  192             if (++white_count == thelength)
  193                 return thelength;
  194 
  195         white_count = 0;
  196     }
  197 #endif
  198 
  199     while (TRUE) {
  200         if (*text == '\t')
  201             return ++white_count;
  202 
  203         if (*text != ' ')
  204             return white_count;
  205 
  206         if (++white_count == tabsize)
  207             return tabsize;
  208 
  209         text++;
  210     }
  211 }
  212 
  213 /* Adjust the positions of mark and cursor when they are on the given line. */
  214 void compensate_leftward(linestruct *line, size_t leftshift)
  215 {
  216     if (line == openfile->mark) {
  217         if (openfile->mark_x < leftshift)
  218             openfile->mark_x = 0;
  219         else
  220             openfile->mark_x -= leftshift;
  221     }
  222 
  223     if (line == openfile->current) {
  224         if (openfile->current_x < leftshift)
  225             openfile->current_x = 0;
  226         else
  227             openfile->current_x -= leftshift;
  228         openfile->placewewant = xplustabs();
  229     }
  230 }
  231 
  232 /* Remove an indent from the given line. */
  233 void unindent_a_line(linestruct *line, size_t indent_len)
  234 {
  235     size_t length = strlen(line->data);
  236 
  237     /* If the indent is empty, don't change the line. */
  238     if (indent_len == 0)
  239         return;
  240 
  241     /* Remove the first tab's worth of whitespace from this line. */
  242     memmove(line->data, line->data + indent_len, length - indent_len + 1);
  243 
  244     openfile->totsize -= indent_len;
  245 
  246     /* Adjust the positions of mark and cursor, when they are affected. */
  247     compensate_leftward(line, indent_len);
  248 }
  249 
  250 /* Unindent the current line (or the marked lines) by tabsize columns.
  251  * The removed indent can be a mixture of spaces plus at most one tab. */
  252 void do_unindent(void)
  253 {
  254     linestruct *top, *bot, *line;
  255 
  256     /* Use either all the marked lines or just the current line. */
  257     get_range((const linestruct **)&top, (const linestruct **)&bot);
  258 
  259     /* Skip any leading lines that cannot be unindented. */
  260     while (top != bot->next && length_of_white(top->data) == 0)
  261         top = top->next;
  262 
  263     /* If none of the lines can be unindented, there is nothing to do. */
  264     if (top == bot->next)
  265         return;
  266 
  267     add_undo(UNINDENT);
  268 
  269     /* Go through each of the lines, removing their leading indent where
  270      * possible, and saving the removed whitespace in the undo item. */
  271     for (line = top; line != bot->next; line = line->next) {
  272         size_t indent_len = length_of_white(line->data);
  273         char *indentation = mallocstrncpy(NULL, line->data, indent_len + 1);
  274 
  275         indentation[indent_len] = '\0';
  276 
  277         unindent_a_line(line, indent_len);
  278         update_multiline_undo(line->lineno, indentation);
  279 
  280         free(indentation);
  281     }
  282 
  283     set_modified();
  284     ensure_firstcolumn_is_aligned();
  285     refresh_needed = TRUE;
  286     shift_held = TRUE;
  287 }
  288 
  289 /* Perform an undo or redo for an indent or unindent action. */
  290 void handle_indent_action(undostruct *u, bool undoing, bool add_indent)
  291 {
  292     groupstruct *group = u->grouping;
  293     linestruct *line = line_from_number(group->top_line);
  294 
  295     if (group->next != NULL)
  296         statusline(ALERT, "Multiple groups -- please report a bug");
  297 
  298     /* When redoing, reposition the cursor and let the indenter adjust it. */
  299     if (!undoing)
  300         goto_line_posx(u->lineno, u->begin);
  301 
  302     /* For each line in the group, add or remove the individual indent. */
  303     while (line && line->lineno <= group->bottom_line) {
  304         char *blanks = group->indentations[line->lineno - group->top_line];
  305 
  306         if (undoing ^ add_indent)
  307             indent_a_line(line, blanks);
  308         else
  309             unindent_a_line(line, strlen(blanks));
  310 
  311         line = line->next;
  312     }
  313 
  314     /* When undoing, reposition the cursor to the recorded location. */
  315     if (undoing)
  316         goto_line_posx(u->lineno, u->begin);
  317 
  318     refresh_needed = TRUE;
  319 }
  320 #endif /* !NANO_TINY */
  321 
  322 #ifdef ENABLE_COMMENT
  323 /* Test whether the given line can be uncommented, or add or remove a comment,
  324  * depending on action.  Return TRUE if the line is uncommentable, or when
  325  * anything was added or removed; FALSE otherwise. */
  326 bool comment_line(undo_type action, linestruct *line, const char *comment_seq)
  327 {
  328     size_t comment_seq_len = strlen(comment_seq);
  329     const char *post_seq = strchr(comment_seq, '|');
  330         /* The postfix, if this is a bracketing type comment sequence. */
  331     size_t pre_len = post_seq ? post_seq++ - comment_seq : comment_seq_len;
  332         /* Length of prefix. */
  333     size_t post_len = post_seq ? comment_seq_len - pre_len - 1 : 0;
  334         /* Length of postfix. */
  335     size_t line_len = strlen(line->data);
  336 
  337     if (!ISSET(NO_NEWLINES) && line == openfile->filebot)
  338         return FALSE;
  339 
  340     if (action == COMMENT) {
  341         /* Make room for the comment sequence(s), move the text right and
  342          * copy them in. */
  343         line->data = charealloc(line->data, line_len + pre_len + post_len + 1);
  344         memmove(line->data + pre_len, line->data, line_len + 1);
  345         memmove(line->data, comment_seq, pre_len);
  346         if (post_len > 0)
  347             memmove(line->data + pre_len + line_len, post_seq, post_len + 1);
  348 
  349         openfile->totsize += pre_len + post_len;
  350 
  351         /* If needed, adjust the position of the mark and of the cursor. */
  352         if (line == openfile->mark && openfile->mark_x > 0)
  353             openfile->mark_x += pre_len;
  354         if (line == openfile->current && openfile->current_x > 0) {
  355             openfile->current_x += pre_len;
  356             openfile->placewewant = xplustabs();
  357         }
  358 
  359         return TRUE;
  360     }
  361 
  362     /* If the line is commented, report it as uncommentable, or uncomment it. */
  363     if (strncmp(line->data, comment_seq, pre_len) == 0 && (post_len == 0 ||
  364                 strcmp(line->data + line_len - post_len, post_seq) == 0)) {
  365 
  366         if (action == PREFLIGHT)
  367             return TRUE;
  368 
  369         /* Erase the comment prefix by moving the non-comment part. */
  370         memmove(line->data, line->data + pre_len, line_len - pre_len);
  371         /* Truncate the postfix if there was one. */
  372         line->data[line_len - pre_len - post_len] = '\0';
  373 
  374         openfile->totsize -= pre_len + post_len;
  375 
  376         /* Adjust the positions of mark and cursor, when needed. */
  377         compensate_leftward(line, pre_len);
  378 
  379         return TRUE;
  380     }
  381 
  382     return FALSE;
  383 }
  384 
  385 /* Comment or uncomment the current line or the marked lines. */
  386 void do_comment(void)
  387 {
  388     const char *comment_seq = GENERAL_COMMENT_CHARACTER;
  389     undo_type action = UNCOMMENT;
  390     linestruct *top, *bot, *line;
  391     bool empty, all_empty = TRUE;
  392 
  393 #ifdef ENABLE_COLOR
  394     if (openfile->syntax)
  395         comment_seq = openfile->syntax->comment;
  396 
  397     if (*comment_seq == '\0') {
  398         statusbar(_("Commenting is not supported for this file type"));
  399         return;
  400     }
  401 #endif
  402 
  403     /* Determine which lines to work on. */
  404     get_range((const linestruct **)&top, (const linestruct **)&bot);
  405 
  406     /* If only the magic line is selected, don't do anything. */
  407     if (top == bot && bot == openfile->filebot && !ISSET(NO_NEWLINES)) {
  408         statusbar(_("Cannot comment past end of file"));
  409         return;
  410     }
  411 
  412     /* Figure out whether to comment or uncomment the selected line or lines. */
  413     for (line = top; line != bot->next; line = line->next) {
  414         empty = white_string(line->data);
  415 
  416         /* If this line is not blank and not commented, we comment all. */
  417         if (!empty && !comment_line(PREFLIGHT, line, comment_seq)) {
  418             action = COMMENT;
  419             break;
  420         }
  421         all_empty = all_empty && empty;
  422     }
  423 
  424     /* If all selected lines are blank, we comment them. */
  425     action = all_empty ? COMMENT : action;
  426 
  427     add_undo(action);
  428 
  429     /* Store the comment sequence used for the operation, because it could
  430      * change when the file name changes; we need to know what it was. */
  431     openfile->current_undo->strdata = mallocstrcpy(NULL, comment_seq);
  432 
  433     /* Comment/uncomment each of the selected lines when possible, and
  434      * store undo data when a line changed. */
  435     for (line = top; line != bot->next; line = line->next) {
  436         if (comment_line(action, line, comment_seq))
  437             update_multiline_undo(line->lineno, "");
  438     }
  439 
  440     set_modified();
  441     ensure_firstcolumn_is_aligned();
  442     refresh_needed = TRUE;
  443     shift_held = TRUE;
  444 }
  445 
  446 /* Perform an undo or redo for a comment or uncomment action. */
  447 void handle_comment_action(undostruct *u, bool undoing, bool add_comment)
  448 {
  449     groupstruct *group = u->grouping;
  450 
  451     /* When redoing, reposition the cursor and let the commenter adjust it. */
  452     if (!undoing)
  453         goto_line_posx(u->lineno, u->begin);
  454 
  455     while (group) {
  456         linestruct *f = line_from_number(group->top_line);
  457 
  458         while (f && f->lineno <= group->bottom_line) {
  459             comment_line(undoing ^ add_comment ?
  460                                 COMMENT : UNCOMMENT, f, u->strdata);
  461             f = f->next;
  462         }
  463 
  464         group = group->next;
  465     }
  466 
  467     /* When undoing, reposition the cursor to the recorded location. */
  468     if (undoing)
  469         goto_line_posx(u->lineno, u->begin);
  470 
  471     refresh_needed = TRUE;
  472 }
  473 #endif /* ENABLE_COMMENT */
  474 
  475 #ifndef NANO_TINY
  476 #define redo_paste undo_cut
  477 #define undo_paste redo_cut
  478 
  479 /* Undo a cut, or redo a paste. */
  480 void undo_cut(undostruct *u)
  481 {
  482     if (u->xflags & WAS_WHOLE_LINE)
  483         goto_line_posx(u->mark_begin_lineno, 0);
  484     else
  485         goto_line_posx(u->mark_begin_lineno, u->mark_begin_x);
  486 
  487     /* If nothing was actually cut, positioning the cursor was enough. */
  488     if (!u->cutbuffer)
  489         return;
  490 
  491     copy_from_buffer(u->cutbuffer);
  492 
  493     /* If the final line was originally cut, remove the extra magic line. */
  494     if ((u->xflags & WAS_FINAL_LINE) && !ISSET(NO_NEWLINES) &&
  495             openfile->current != openfile->filebot)
  496         remove_magicline();
  497 
  498     if (!(u->xflags & WAS_MARKED_FORWARD) && u->type != PASTE)
  499         goto_line_posx(u->mark_begin_lineno, u->mark_begin_x);
  500 }
  501 
  502 /* Redo a cut, or undo a paste. */
  503 void redo_cut(undostruct *u)
  504 {
  505     linestruct *oldcutbuffer = cutbuffer;
  506 
  507     goto_line_posx(u->lineno, u->begin);
  508 
  509     /* If nothing was actually cut, positioning the cursor was enough. */
  510     if (!u->cutbuffer)
  511         return;
  512 
  513     cutbuffer = NULL;
  514 
  515     openfile->mark = line_from_number(u->mark_begin_lineno);
  516     openfile->mark_x = (u->xflags & WAS_WHOLE_LINE) ? 0 : u->mark_begin_x;
  517 
  518     do_snip(FALSE, TRUE, FALSE, u->type == ZAP);
  519 
  520     free_lines(cutbuffer);
  521     cutbuffer = oldcutbuffer;
  522 }
  523 
  524 /* Undo the last thing(s) we did. */
  525 void do_undo(void)
  526 {
  527     undostruct *u = openfile->current_undo;
  528     linestruct *f = NULL, *t = NULL;
  529     linestruct *oldcutbuffer;
  530     char *data, *undidmsg = NULL;
  531     size_t from_x, to_x;
  532 
  533     if (u == NULL) {
  534         statusbar(_("Nothing to undo"));
  535         return;
  536     }
  537 
  538     if (u->type <= REPLACE)
  539         f = line_from_number(u->mark_begin_lineno);
  540 
  541     openfile->current_x = u->begin;
  542 
  543     switch (u->type) {
  544     case ADD:
  545         /* TRANSLATORS: The next thirteen strings describe actions
  546          * that are undone or redone.  They are all nouns, not verbs. */
  547         undidmsg = _("addition");
  548         if ((u->xflags & WAS_FINAL_LINE) && !ISSET(NO_NEWLINES))
  549             remove_magicline();
  550         data = charalloc(strlen(f->data) - strlen(u->strdata) + 1);
  551         strncpy(data, f->data, u->begin);
  552         strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]);
  553         free(f->data);
  554         f->data = data;
  555         goto_line_posx(u->lineno, u->begin);
  556         break;
  557     case ENTER:
  558         undidmsg = _("line break");
  559         from_x = (u->begin == 0) ? 0 : u->mark_begin_x;
  560         to_x = (u->begin == 0) ? u->mark_begin_x : u->begin;
  561         f->data = charealloc(f->data, strlen(f->data) +
  562                                 strlen(&u->strdata[from_x]) + 1);
  563         strcat(f->data, &u->strdata[from_x]);
  564         unlink_node(f->next);
  565         renumber_from(f);
  566         goto_line_posx(u->lineno, to_x);
  567         break;
  568     case BACK:
  569     case DEL:
  570         undidmsg = _("deletion");
  571         data = charalloc(strlen(f->data) + strlen(u->strdata) + 1);
  572         strncpy(data, f->data, u->begin);
  573         strcpy(&data[u->begin], u->strdata);
  574         strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]);
  575         free(f->data);
  576         f->data = data;
  577         goto_line_posx(u->mark_begin_lineno, u->mark_begin_x);
  578         break;
  579     case JOIN:
  580         undidmsg = _("line join");
  581         /* When the join was done by a Backspace at the tail of the file,
  582          * and the nonewlines flag isn't set, do not re-add a newline that
  583          * wasn't actually deleted; just position the cursor. */
  584         if ((u->xflags & WAS_FINAL_BACKSPACE) && !ISSET(NO_NEWLINES)) {
  585             goto_line_posx(openfile->filebot->lineno, 0);
  586             break;
  587         }
  588         t = make_new_node(f);
  589         t->data = mallocstrcpy(NULL, u->strdata);
  590         data = mallocstrncpy(NULL, f->data, u->mark_begin_x + 1);
  591         data[u->mark_begin_x] = '\0';
  592         free(f->data);
  593         f->data = data;
  594         splice_node(f, t);
  595         renumber_from(t);
  596         goto_line_posx(u->lineno, u->begin);
  597         break;
  598     case REPLACE:
  599         undidmsg = _("replacement");
  600         goto_line_posx(u->lineno, u->begin);
  601         data = u->strdata;
  602         u->strdata = f->data;
  603         f->data = data;
  604         break;
  605 #ifdef ENABLE_WRAPPING
  606     case SPLIT_END:
  607         goto_line_posx(u->lineno, u->begin);
  608         openfile->current_undo = openfile->current_undo->next;
  609         while (openfile->current_undo->type != SPLIT_BEGIN)
  610             do_undo();
  611         u = openfile->current_undo;
  612     case SPLIT_BEGIN:
  613         undidmsg = _("addition");
  614         break;
  615 #endif
  616     case ZAP:
  617         undidmsg = _("erasure");
  618         undo_cut(u);
  619         break;
  620     case CUT_TO_EOF:
  621     case CUT:
  622         /* TRANSLATORS: Remember: these are nouns, NOT verbs. */
  623         undidmsg = _("cut");
  624         undo_cut(u);
  625         break;
  626     case PASTE:
  627         undidmsg = _("paste");
  628         undo_paste(u);
  629         break;
  630     case INSERT:
  631         undidmsg = _("insertion");
  632         oldcutbuffer = cutbuffer;
  633         cutbuffer = NULL;
  634         openfile->mark = line_from_number(u->mark_begin_lineno);
  635         openfile->mark_x = u->mark_begin_x;
  636         goto_line_posx(u->lineno, u->begin);
  637         cut_marked(NULL);
  638         free_lines(u->cutbuffer);
  639         u->cutbuffer = cutbuffer;
  640         cutbuffer = oldcutbuffer;
  641         break;
  642     case COUPLE_BEGIN:
  643         undidmsg = u->strdata;
  644         goto_line_posx(u->lineno, u->begin);
  645         break;
  646     case COUPLE_END:
  647         openfile->current_undo = openfile->current_undo->next;
  648         do_undo();
  649         do_undo();
  650         do_undo();
  651         return;
  652     case INDENT:
  653         handle_indent_action(u, TRUE, TRUE);
  654         undidmsg = _("indent");
  655         break;
  656     case UNINDENT:
  657         handle_indent_action(u, TRUE, FALSE);
  658         undidmsg = _("unindent");
  659         break;
  660 #ifdef ENABLE_COMMENT
  661     case COMMENT:
  662         handle_comment_action(u, TRUE, TRUE);
  663         undidmsg = _("comment");
  664         break;
  665     case UNCOMMENT:
  666         handle_comment_action(u, TRUE, FALSE);
  667         undidmsg = _("uncomment");
  668         break;
  669 #endif
  670     default:
  671         break;
  672     }
  673 
  674     if (undidmsg && !pletion_line)
  675         statusline(HUSH, _("Undid %s"), undidmsg);
  676 
  677     openfile->current_undo = openfile->current_undo->next;
  678     openfile->last_action = OTHER;
  679     openfile->mark = NULL;
  680     openfile->placewewant = xplustabs();
  681 
  682     openfile->totsize = u->wassize;
  683 
  684     /* When at the point where the file was last saved, unset "Modified". */
  685     if (openfile->current_undo == openfile->last_saved) {
  686         openfile->modified = FALSE;
  687         titlebar(NULL);
  688     } else
  689         set_modified();
  690 }
  691 
  692 /* Redo the last thing(s) we undid. */
  693 void do_redo(void)
  694 {
  695     linestruct *f = NULL, *shoveline;
  696     char *data, *redidmsg = NULL;
  697     undostruct *u = openfile->undotop;
  698 
  699     if (u == NULL || u == openfile->current_undo) {
  700         statusbar(_("Nothing to redo"));
  701         return;
  702     }
  703 
  704     /* Find the item before the current one in the undo stack. */
  705     while (u->next != openfile->current_undo)
  706         u = u->next;
  707 
  708     if (u->type <= REPLACE)
  709         f = line_from_number(u->mark_begin_lineno);
  710 
  711     switch (u->type) {
  712     case ADD:
  713         redidmsg = _("addition");
  714         if ((u->xflags & WAS_FINAL_LINE) && !ISSET(NO_NEWLINES))
  715             new_magicline();
  716         data = charalloc(strlen(f->data) + strlen(u->strdata) + 1);
  717         strncpy(data, f->data, u->begin);
  718         strcpy(&data[u->begin], u->strdata);
  719         strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]);
  720         free(f->data);
  721         f->data = data;
  722         goto_line_posx(u->mark_begin_lineno, u->mark_begin_x);
  723         break;
  724     case ENTER:
  725         redidmsg = _("line break");
  726         shoveline = make_new_node(f);
  727         shoveline->data = mallocstrcpy(NULL, u->strdata);
  728         data = mallocstrncpy(NULL, f->data, u->begin + 1);
  729         data[u->begin] = '\0';
  730         free(f->data);
  731         f->data = data;
  732         splice_node(f, shoveline);
  733         renumber_from(shoveline);
  734         goto_line_posx(u->lineno + 1, u->mark_begin_x);
  735         break;
  736     case BACK:
  737     case DEL:
  738         redidmsg = _("deletion");
  739         data = charalloc(strlen(f->data) + strlen(u->strdata) + 1);
  740         strncpy(data, f->data, u->begin);
  741         strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]);
  742         free(f->data);
  743         f->data = data;
  744         goto_line_posx(u->lineno, u->begin);
  745         break;
  746     case JOIN:
  747         redidmsg = _("line join");
  748         /* When the join was done by a Backspace at the tail of the file,
  749          * and the nonewlines flag isn't set, do not join anything, as
  750          * nothing was actually deleted; just position the cursor. */
  751         if ((u->xflags & WAS_FINAL_BACKSPACE) && !ISSET(NO_NEWLINES)) {
  752             goto_line_posx(u->mark_begin_lineno, u->mark_begin_x);
  753             break;
  754         }
  755         f->data = charealloc(f->data, strlen(f->data) + strlen(u->strdata) + 1);
  756         strcat(f->data, u->strdata);
  757         unlink_node(f->next);
  758         renumber_from(f);
  759         goto_line_posx(u->mark_begin_lineno, u->mark_begin_x);
  760         break;
  761     case REPLACE:
  762         redidmsg = _("replacement");
  763         data = u->strdata;
  764         u->strdata = f->data;
  765         f->data = data;
  766         goto_line_posx(u->lineno, u->begin);
  767         break;
  768 #ifdef ENABLE_WRAPPING
  769     case SPLIT_BEGIN:
  770         goto_line_posx(u->lineno, u->begin);
  771         openfile->current_undo = u;
  772         while (openfile->current_undo->type != SPLIT_END)
  773             do_redo();
  774         u = openfile->current_undo;
  775         goto_line_posx(u->lineno, u->begin);
  776     case SPLIT_END:
  777         redidmsg = _("addition");
  778         break;
  779 #endif
  780     case ZAP:
  781         redidmsg = _("erasure");
  782         redo_cut(u);
  783         break;
  784     case CUT_TO_EOF:
  785     case CUT:
  786         redidmsg = _("cut");
  787         redo_cut(u);
  788         break;
  789     case PASTE:
  790         redidmsg = _("paste");
  791         redo_paste(u);
  792         break;
  793     case INSERT:
  794         redidmsg = _("insertion");
  795         goto_line_posx(u->lineno, u->begin);
  796         copy_from_buffer(u->cutbuffer);
  797         free_lines(u->cutbuffer);
  798         u->cutbuffer = NULL;
  799         break;
  800     case COUPLE_BEGIN:
  801         openfile->current_undo = u;
  802         do_redo();
  803         do_redo();
  804         do_redo();
  805         return;
  806     case COUPLE_END:
  807         redidmsg = u->strdata;
  808         goto_line_posx(u->lineno, u->begin);
  809         break;
  810     case INDENT:
  811         handle_indent_action(u, FALSE, TRUE);
  812         redidmsg = _("indent");
  813         break;
  814     case UNINDENT:
  815         handle_indent_action(u, FALSE, FALSE);
  816         redidmsg = _("unindent");
  817         break;
  818 #ifdef ENABLE_COMMENT
  819     case COMMENT:
  820         handle_comment_action(u, FALSE, TRUE);
  821         redidmsg = _("comment");
  822         break;
  823     case UNCOMMENT:
  824         handle_comment_action(u, FALSE, FALSE);
  825         redidmsg = _("uncomment");
  826         break;
  827 #endif
  828     default:
  829         break;
  830     }
  831 
  832     if (redidmsg)
  833         statusline(HUSH, _("Redid %s"), redidmsg);
  834 
  835     openfile->current_undo = u;
  836     openfile->last_action = OTHER;
  837     openfile->mark = NULL;
  838     openfile->placewewant = xplustabs();
  839 
  840     openfile->totsize = u->newsize;
  841 
  842     /* When at the point where the file was last saved, unset "Modified". */
  843     if (openfile->current_undo == openfile->last_saved) {
  844         openfile->modified = FALSE;
  845         titlebar(NULL);
  846     } else
  847         set_modified();
  848 }
  849 #endif /* !NANO_TINY */
  850 
  851 /* Break the current line at the cursor position. */
  852 void do_enter(void)
  853 {
  854     linestruct *newnode = make_new_node(openfile->current);
  855     size_t extra = 0;
  856 #ifndef NANO_TINY
  857     linestruct *sampleline = openfile->current;
  858     bool allblanks = FALSE;
  859 
  860     if (ISSET(AUTOINDENT)) {
  861 #ifdef ENABLE_JUSTIFY
  862         /* When doing automatic long-line wrapping and the next line is
  863          * in this same paragraph, use its indentation as the model. */
  864         if (ISSET(BREAK_LONG_LINES) && sampleline->next != NULL &&
  865                     inpar(sampleline->next) && !begpar(sampleline->next, 0))
  866             sampleline = sampleline->next;
  867 #endif
  868         extra = indent_length(sampleline->data);
  869 
  870         /* When breaking in the indentation, limit the automatic one. */
  871         if (extra > openfile->current_x)
  872             extra = openfile->current_x;
  873         else if (extra == openfile->current_x)
  874             allblanks = TRUE;
  875     }
  876 #endif /* NANO_TINY */
  877     newnode->data = charalloc(strlen(openfile->current->data +
  878                                         openfile->current_x) + extra + 1);
  879     strcpy(&newnode->data[extra], openfile->current->data +
  880                                         openfile->current_x);
  881 #ifndef NANO_TINY
  882     if (ISSET(AUTOINDENT)) {
  883         /* Copy the whitespace from the sample line to the new one. */
  884         strncpy(newnode->data, sampleline->data, extra);
  885         /* If there were only blanks before the cursor, trim them. */
  886         if (allblanks)
  887             openfile->current_x = 0;
  888     }
  889 #endif
  890 
  891     /* Make the current line end at the cursor position. */
  892     openfile->current->data[openfile->current_x] = '\0';
  893 
  894 #ifndef NANO_TINY
  895     add_undo(ENTER);
  896 
  897     /* Adjust the mark if it was on the current line after the cursor. */
  898     if (openfile->mark == openfile->current &&
  899                 openfile->mark_x > openfile->current_x) {
  900         openfile->mark = newnode;
  901         openfile->mark_x += extra - openfile->current_x;
  902     }
  903 #endif
  904 
  905     /* Insert the newly created line after the current one and renumber. */
  906     splice_node(openfile->current, newnode);
  907     renumber_from(newnode);
  908 
  909     /* Put the cursor on the new line, after any automatic whitespace. */
  910     openfile->current = newnode;
  911     openfile->current_x = extra;
  912     openfile->placewewant = xplustabs();
  913 
  914     openfile->totsize++;
  915     set_modified();
  916 
  917 #ifndef NANO_TINY
  918     if (ISSET(AUTOINDENT) && !allblanks)
  919         openfile->totsize += extra;
  920     update_undo(ENTER);
  921 #endif
  922 
  923     refresh_needed = TRUE;
  924     focusing = FALSE;
  925 }
  926 
  927 #ifndef NANO_TINY
  928 /* Send an unconditional kill signal to the running external command. */
  929 RETSIGTYPE cancel_the_command(int signal)
  930 {
  931     kill(pid_of_command, SIGKILL);
  932 }
  933 
  934 /* Send the text that starts at the given line to file descriptor fd. */
  935 void send_data(const linestruct *line, int fd)
  936 {
  937     FILE *tube = fdopen(fd, "w");
  938 
  939     if (tube == NULL)
  940         return;
  941 
  942     /* Send each line, except a final empty line. */
  943     while (line != NULL && (line->next != NULL || line->data[0] != '\0')) {
  944         fprintf(tube, "%s%s", line->data, line->next == NULL ? "" : "\n");
  945         line = line->next;
  946     }
  947 
  948     fclose(tube);
  949 }
  950 
  951 /* Execute the given command in a shell.  Return TRUE on success. */
  952 bool execute_command(const char *command)
  953 {
  954     int from_fd[2], to_fd[2];
  955         /* The pipes through which text will written and read. */
  956     const bool should_pipe = (command[0] == '|');
  957     FILE *stream;
  958     const char *shellenv;
  959     struct sigaction oldaction, newaction;
  960         /* Original and temporary handlers for SIGINT. */
  961 
  962     /* Create a pipe to read the command's output from, and, if needed,
  963      * a pipe to feed the command's input through. */
  964     if (pipe(from_fd) == -1 || (should_pipe && pipe(to_fd) == -1)) {
  965         statusline(ALERT, _("Could not create pipe"));
  966         return FALSE;
  967     }
  968 
  969     /* Check which shell to use.  If none is specified, use /bin/sh. */
  970     shellenv = getenv("SHELL");
  971     if (shellenv == NULL)
  972         shellenv = (char *)"/bin/sh";
  973 
  974     /* Fork a child process to run the command in. */
  975     if ((pid_of_command = fork()) == 0) {
  976         /* Child: close the unused read end of the output pipe. */
  977         close(from_fd[0]);
  978 
  979         /* Connect the write end of the output pipe to the process' output streams. */
  980         dup2(from_fd[1], fileno(stdout));
  981         dup2(from_fd[1], fileno(stderr));
  982 
  983         /* If the parent sends text, connect the read end of the
  984          * feeding pipe to the child's input stream. */
  985         if (should_pipe) {
  986             dup2(to_fd[0], fileno(stdin));
  987             close(to_fd[1]);
  988         }
  989 
  990         /* Run the given command inside the preferred shell. */
  991         execl(shellenv, tail(shellenv), "-c", should_pipe ? &command[1] : command, NULL);
  992 
  993         /* If the exec call returns, there was an error. */
  994         exit(1);
  995     }
  996 
  997     /* Parent: close the unused write end of the pipe. */
  998     close(from_fd[1]);
  999 
 1000     if (pid_of_command == -1) {
 1001         statusline(ALERT, _("Could not fork"));
 1002         close(from_fd[0]);
 1003         return FALSE;
 1004     }
 1005 
 1006     statusbar(_("Executing..."));
 1007 
 1008     /* If the command starts with "|", pipe buffer or region to the command. */
 1009     if (should_pipe) {
 1010         linestruct *was_cutbuffer = cutbuffer;
 1011         cutbuffer = NULL;
 1012 
 1013 #ifdef ENABLE_MULTIBUFFER
 1014         if (ISSET(MULTIBUFFER)) {
 1015             openfile = openfile->prev;
 1016             if (openfile->mark)
 1017                 do_snip(TRUE, TRUE, FALSE, FALSE);
 1018         } else
 1019 #endif
 1020         {
 1021             add_undo(COUPLE_BEGIN);
 1022             openfile->undotop->strdata = mallocstrcpy(NULL, _("filtering"));
 1023             if (openfile->mark == NULL) {
 1024                 openfile->current = openfile->filetop;
 1025                 openfile->current_x = 0;
 1026             }
 1027             add_undo(CUT);
 1028             do_snip(FALSE, openfile->mark != NULL, openfile->mark == NULL, FALSE);
 1029             update_undo(CUT);
 1030         }
 1031 
 1032         if (fork() == 0) {
 1033             close(to_fd[0]);
 1034             send_data(cutbuffer != NULL ? cutbuffer : openfile->filetop, to_fd[1]);
 1035             close(to_fd[1]);
 1036             exit(0);
 1037         }
 1038 
 1039         close(to_fd[0]);
 1040         close(to_fd[1]);
 1041 
 1042 #ifdef ENABLE_MULTIBUFFER
 1043         if (ISSET(MULTIBUFFER))
 1044             openfile = openfile->next;
 1045 #endif
 1046         free_lines(cutbuffer);
 1047         cutbuffer = was_cutbuffer;
 1048     }
 1049 
 1050     /* Re-enable interpretation of the special control keys so that we get
 1051      * SIGINT when Ctrl-C is pressed. */
 1052     enable_kb_interrupt();
 1053 
 1054     /* Set up a signal handler so that ^C will terminate the forked process. */
 1055     newaction.sa_handler = cancel_the_command;
 1056     newaction.sa_flags = 0;
 1057     sigaction(SIGINT, &newaction, &oldaction);
 1058 
 1059     stream = fdopen(from_fd[0], "rb");
 1060     if (stream == NULL)
 1061         statusline(ALERT, _("Failed to open pipe: %s"), strerror(errno));
 1062     else
 1063         read_file(stream, 0, "pipe", TRUE);
 1064 
 1065     if (should_pipe && !ISSET(MULTIBUFFER)) {
 1066         add_undo(COUPLE_END);
 1067         openfile->undotop->strdata = mallocstrcpy(NULL, _("filtering"));
 1068     }
 1069 
 1070     /* Wait for the external command (and possibly data sender) to terminate. */
 1071     wait(NULL);
 1072     if (should_pipe)
 1073         wait(NULL);
 1074 
 1075     /* Restore the original handler for SIGINT. */
 1076     sigaction(SIGINT, &oldaction, NULL);
 1077 
 1078     /* Restore the terminal to its desired state, and disable
 1079      * interpretation of the special control keys again. */
 1080     terminal_init();
 1081 
 1082     return TRUE;
 1083 }
 1084 
 1085 /* Discard undo items that are newer than the given one, or all if NULL.
 1086  * When keep is TRUE, do not touch the last_saved pointer. */
 1087 void discard_until(const undostruct *thisitem, openfilestruct *thefile, bool keep)
 1088 {
 1089     undostruct *dropit = thefile->undotop;
 1090     groupstruct *group;
 1091 
 1092     while (dropit != NULL && dropit != thisitem) {
 1093         thefile->undotop = dropit->next;
 1094         free(dropit->strdata);
 1095         free_lines(dropit->cutbuffer);
 1096         group = dropit->grouping;
 1097         while (group != NULL) {
 1098             groupstruct *next = group->next;
 1099             free_chararray(group->indentations,
 1100                                 group->bottom_line - group->top_line);
 1101             free(group);
 1102             group = next;
 1103         }
 1104         free(dropit);
 1105         dropit = thefile->undotop;
 1106     }
 1107 
 1108     /* Adjust the pointer to the top of the undo stack. */
 1109     thefile->current_undo = (undostruct *)thisitem;
 1110 
 1111     /* Prevent a chain of editing actions from continuing. */
 1112     thefile->last_action = OTHER;
 1113 
 1114     /* When requested, record that the undo stack was chopped, and
 1115      * that thus there is no point at which the file was last saved. */
 1116     if (!keep)
 1117         thefile->last_saved = (undostruct *)0xbeeb;
 1118 }
 1119 
 1120 /* Add a new undo item of the given type to the top of the current pile. */
 1121 void add_undo(undo_type action)
 1122 {
 1123     undostruct *u = nmalloc(sizeof(undostruct));
 1124 
 1125     /* Initialize the newly allocated undo item. */
 1126     u->type = action;
 1127     u->strdata = NULL;
 1128     u->cutbuffer = NULL;
 1129     u->lineno = openfile->current->lineno;
 1130     u->begin = openfile->current_x;
 1131     u->mark_begin_lineno = openfile->current->lineno;
 1132     u->mark_begin_x = openfile->current_x;
 1133     u->wassize = openfile->totsize;
 1134     u->newsize = openfile->totsize;
 1135     u->grouping = NULL;
 1136     u->xflags = 0;
 1137 
 1138     /* Blow away any undone items. */
 1139     discard_until(openfile->current_undo, openfile, TRUE);
 1140 
 1141 #ifdef ENABLE_WRAPPING
 1142     /* If some action caused automatic long-line wrapping, insert the
 1143      * SPLIT_BEGIN item underneath that action's undo item.  Otherwise,
 1144      * just add the new item to the top of the undo stack. */
 1145     if (u->type == SPLIT_BEGIN) {
 1146         u->next = openfile->undotop->next;
 1147         openfile->undotop->next = u;
 1148     } else
 1149 #endif
 1150     {
 1151         u->next = openfile->undotop;
 1152         openfile->undotop = u;
 1153         openfile->current_undo = u;
 1154     }
 1155 
 1156     /* Record the info needed to be able to undo each possible action. */
 1157     switch (u->type) {
 1158     case ADD:
 1159         /* If a new magic line will be added, an undo should remove it. */
 1160         if (openfile->current == openfile->filebot)
 1161             u->xflags |= WAS_FINAL_LINE;
 1162         u->wassize--;
 1163         break;
 1164     case ENTER:
 1165         break;
 1166     case BACK:
 1167         /* If the next line is the magic line, don't ever undo this
 1168          * backspace, as it won't actually have deleted anything. */
 1169         if (openfile->current->next == openfile->filebot &&
 1170                         openfile->current->data[0] != '\0')
 1171             u->xflags |= WAS_FINAL_BACKSPACE;
 1172     case DEL:
 1173         /* When not at the end of a line, store the deleted character,
 1174          * else purposely fall into the line-joining code. */
 1175         if (openfile->current->data[openfile->current_x] != '\0') {
 1176             char *char_buf = charalloc(MAXCHARLEN + 1);
 1177             int charlen = parse_mbchar(&openfile->current->data[u->begin],
 1178                                                 char_buf, NULL);
 1179             char_buf[charlen] = '\0';
 1180             u->strdata = char_buf;
 1181             if (u->type == BACK)
 1182                 u->mark_begin_x += charlen;
 1183             break;
 1184         }
 1185     case JOIN:
 1186         if (openfile->current->next) {
 1187             if (u->type == BACK) {
 1188                 u->lineno = openfile->current->next->lineno;
 1189                 u->begin = 0;
 1190             }
 1191             u->strdata = mallocstrcpy(NULL, openfile->current->next->data);
 1192         }
 1193         action = u->type = JOIN;
 1194         break;
 1195     case REPLACE:
 1196         u->strdata = mallocstrcpy(NULL, openfile->current->data);
 1197         break;
 1198 #ifdef ENABLE_WRAPPING
 1199     case SPLIT_BEGIN:
 1200         action = openfile->undotop->type;
 1201         break;
 1202     case SPLIT_END:
 1203         break;
 1204 #endif
 1205     case CUT_TO_EOF:
 1206         u->xflags |= WAS_FINAL_LINE;
 1207         break;
 1208     case ZAP:
 1209     case CUT:
 1210         if (openfile->mark) {
 1211             u->mark_begin_lineno = openfile->mark->lineno;
 1212             u->mark_begin_x = openfile->mark_x;
 1213             u->xflags |= MARK_WAS_SET;
 1214             if (openfile->current == openfile->filebot ||
 1215                         openfile->mark == openfile->filebot)
 1216                 u->xflags |= WAS_FINAL_LINE;
 1217         } else if (!ISSET(CUT_FROM_CURSOR)) {
 1218             /* The entire line is being cut regardless of the cursor position. */
 1219             u->begin = 0;
 1220             u->xflags |= WAS_WHOLE_LINE;
 1221         }
 1222         break;
 1223     case PASTE:
 1224         u->cutbuffer = copy_buffer(cutbuffer);
 1225         u->lineno += cutbottom->lineno - cutbuffer->lineno;
 1226         break;
 1227     case INSERT:
 1228     case COUPLE_BEGIN:
 1229     case COUPLE_END:
 1230         break;
 1231     case INDENT:
 1232     case UNINDENT:
 1233         break;
 1234 #ifdef ENABLE_COMMENT
 1235     case COMMENT:
 1236     case UNCOMMENT:
 1237         break;
 1238 #endif
 1239     default:
 1240         break;
 1241     }
 1242 
 1243     openfile->last_action = action;
 1244 }
 1245 
 1246 /* Update a multiline undo item.  This should be called once for each line
 1247  * affected by a multiple-line-altering feature.  The indentation that is
 1248  * added or removed is saved separately for each line in the undo item. */
 1249 void update_multiline_undo(ssize_t lineno, char *indentation)
 1250 {
 1251     undostruct *u = openfile->current_undo;
 1252 
 1253     /* If there already is a group and the current line is contiguous with it,
 1254      * extend the group; otherwise, create a new group. */
 1255     if (u->grouping && u->grouping->bottom_line + 1 == lineno) {
 1256         size_t number_of_lines;
 1257 
 1258         u->grouping->bottom_line++;
 1259 
 1260         number_of_lines = u->grouping->bottom_line - u->grouping->top_line + 1;
 1261         u->grouping->indentations = (char **)nrealloc(u->grouping->indentations,
 1262                                         number_of_lines * sizeof(char *));
 1263         u->grouping->indentations[number_of_lines - 1] = mallocstrcpy(NULL,
 1264                                                                 indentation);
 1265     } else {
 1266         groupstruct *born = nmalloc(sizeof(groupstruct));
 1267 
 1268         born->next = u->grouping;
 1269         u->grouping = born;
 1270         born->top_line = lineno;
 1271         born->bottom_line = lineno;
 1272 
 1273         u->grouping->indentations = (char **)nmalloc(sizeof(char *));
 1274         u->grouping->indentations[0] = mallocstrcpy(NULL, indentation);
 1275     }
 1276 
 1277     /* Store the file size after the change, to be used when redoing. */
 1278     u->newsize = openfile->totsize;
 1279 }
 1280 
 1281 /* Update an undo item with (among other things) the file size and
 1282  * cursor position after the given action. */
 1283 void update_undo(undo_type action)
 1284 {
 1285     undostruct *u = openfile->undotop;
 1286     char *char_buf;
 1287     int charlen;
 1288 
 1289     if (u->type != action)
 1290         statusline(ALERT, "Mismatching undo type -- please report a bug");
 1291 
 1292     u->newsize = openfile->totsize;
 1293 
 1294     switch (u->type) {
 1295     case ADD:
 1296         char_buf = charalloc(MAXCHARLEN);
 1297         charlen = parse_mbchar(&openfile->current->data[u->mark_begin_x],
 1298                                 char_buf, NULL);
 1299         u->strdata = addstrings(u->strdata, u->strdata ? strlen(u->strdata) : 0,
 1300                                 char_buf, charlen);
 1301         u->mark_begin_lineno = openfile->current->lineno;
 1302         u->mark_begin_x = openfile->current_x;
 1303         break;
 1304     case ENTER:
 1305         u->strdata = mallocstrcpy(NULL, openfile->current->data);
 1306         u->mark_begin_x = openfile->current_x;
 1307         break;
 1308     case BACK:
 1309     case DEL:
 1310         char_buf = charalloc(MAXCHARLEN);
 1311         charlen = parse_mbchar(&openfile->current->data[openfile->current_x],
 1312                                 char_buf, NULL);
 1313         if (openfile->current_x == u->begin) {
 1314             /* They deleted more: add removed character after earlier stuff. */
 1315             u->strdata = addstrings(u->strdata, strlen(u->strdata), char_buf, charlen);
 1316             u->mark_begin_x = openfile->current_x;
 1317         } else if (openfile->current_x == u->begin - charlen) {
 1318             /* They backspaced further: add removed character before earlier. */
 1319             u->strdata = addstrings(char_buf, charlen, u->strdata, strlen(u->strdata));
 1320             u->begin = openfile->current_x;
 1321         } else {
 1322             /* They deleted *elsewhere* on the line: start a new undo item. */
 1323             free(char_buf);
 1324             add_undo(u->type);
 1325             return;
 1326         }
 1327         break;
 1328     case JOIN:
 1329         break;
 1330     case REPLACE:
 1331     case PASTE:
 1332         u->lineno = openfile->current->lineno;
 1333         u->begin = openfile->current_x;
 1334         break;
 1335 #ifdef ENABLE_WRAPPING
 1336     case SPLIT_BEGIN:
 1337     case SPLIT_END:
 1338         break;
 1339 #endif
 1340     case ZAP:
 1341     case CUT_TO_EOF:
 1342     case CUT:
 1343         if (!cutbuffer)
 1344             break;
 1345         if (u->type == ZAP)
 1346             u->cutbuffer = cutbuffer;
 1347         else {
 1348             free_lines(u->cutbuffer);
 1349             u->cutbuffer = copy_buffer(cutbuffer);
 1350         }
 1351         if (u->xflags & MARK_WAS_SET) {
 1352             /* If the region was marked backwards, swap the end points. */
 1353             if (u->lineno < u->mark_begin_lineno ||
 1354                         (u->lineno == u->mark_begin_lineno &&
 1355                         u->begin < u->mark_begin_x)) {
 1356                 ssize_t number = u->lineno;
 1357                 size_t position = u->begin;
 1358 
 1359                 u->lineno = u->mark_begin_lineno;
 1360                 u->begin = u->mark_begin_x;
 1361 
 1362                 u->mark_begin_lineno = number;
 1363                 u->mark_begin_x = position;
 1364             } else
 1365                 u->xflags |= WAS_MARKED_FORWARD;
 1366         } else {
 1367             linestruct *bottomline = u->cutbuffer;
 1368             size_t count = 0;
 1369 
 1370             /* Find the end of the cut for the undo/redo, using our copy. */
 1371             while (bottomline->next != NULL) {
 1372                 bottomline = bottomline->next;
 1373                 count++;
 1374             }
 1375             u->lineno = u->mark_begin_lineno + count;
 1376             if (ISSET(CUT_FROM_CURSOR) || u->type == CUT_TO_EOF) {
 1377                 u->begin = strlen(bottomline->data);
 1378                 if (u->lineno == u->mark_begin_lineno)
 1379                     u->begin += u->mark_begin_x;
 1380             } else if (openfile->current == openfile->filebot &&
 1381                         ISSET(NO_NEWLINES))
 1382                 u->begin = strlen(bottomline->data);
 1383         }
 1384         break;
 1385     case INSERT:
 1386         u->mark_begin_lineno = openfile->current->lineno;
 1387         u->mark_begin_x = openfile->current_x;
 1388     case COUPLE_BEGIN:
 1389         break;
 1390     case COUPLE_END:
 1391         u->lineno = openfile->current->lineno;
 1392         u->begin = openfile->current_x;
 1393         break;
 1394     default:
 1395         break;
 1396     }
 1397 }
 1398 #endif /* !NANO_TINY */
 1399 
 1400 #ifdef ENABLE_WRAPPING
 1401 /* When the current line is overlong, hard-wrap it at the furthest possible
 1402  * whitespace character, and (if possible) prepend the remainder of the line
 1403  * to the next line.  Return TRUE if wrapping occurred, and FALSE otherwise. */
 1404 bool do_wrap(void)
 1405 {
 1406     linestruct *line = openfile->current;
 1407         /* The line to be wrapped, if needed and possible. */
 1408     size_t line_len = strlen(line->data);
 1409         /* The length of this line. */
 1410     size_t cursor_x = openfile->current_x;
 1411         /* The current cursor position, for comparison with the wrap point. */
 1412     ssize_t wrap_loc;
 1413         /* The position in the line's text where we wrap. */
 1414     const char *remainder;
 1415         /* The text after the wrap point. */
 1416     size_t rest_length;
 1417         /* The length of the remainder. */
 1418 
 1419     /* First find the last blank character where we can break the line. */
 1420     wrap_loc = break_line(line->data, wrap_at, FALSE);
 1421 
 1422     /* If no wrapping point was found before end-of-line, we don't wrap. */
 1423     if (wrap_loc == -1 || line->data[wrap_loc] == '\0')
 1424         return FALSE;
 1425 
 1426     /* Step forward to the character just after the blank. */
 1427     wrap_loc = step_right(line->data, wrap_loc);
 1428 
 1429     /* When now at end-of-line, no need to wrap. */
 1430     if (line->data[wrap_loc] == '\0')
 1431         return FALSE;
 1432 
 1433 #ifndef NANO_TINY
 1434     /* When autoindenting, we don't wrap right after the indentation. */
 1435     if (ISSET(AUTOINDENT) && wrap_loc == indent_length(line->data))
 1436         return FALSE;
 1437 
 1438     add_undo(SPLIT_BEGIN);
 1439 #endif
 1440 #ifdef ENABLE_JUSTIFY
 1441     bool autowhite = ISSET(AUTOINDENT);
 1442     size_t lead_len = quote_length(line->data);
 1443 
 1444     if (lead_len > 0)
 1445         UNSET(AUTOINDENT);
 1446 #endif
 1447 
 1448     /* The remainder is the text that will be wrapped to the next line. */
 1449     remainder = line->data + wrap_loc;
 1450     rest_length = line_len - wrap_loc;
 1451 
 1452     /* When prepending and the remainder of this line will not make the next
 1453      * line too long, then join the two lines, so that, after the line wrap,
 1454      * the remainder will effectively have been prefixed to the next line. */
 1455     if (openfile->spillage_line && openfile->spillage_line == line->next &&
 1456                 rest_length + breadth(line->next->data) <= wrap_at) {
 1457         /* Go to the end of this line. */
 1458         openfile->current_x = line_len;
 1459 
 1460         /* If the remainder doesn't end in a blank, add a space. */
 1461         if (!is_blank_mbchar(remainder + step_left(remainder, rest_length))) {
 1462 #ifndef NANO_TINY
 1463             add_undo(ADD);
 1464 #endif
 1465             line->data = charealloc(line->data, line_len + 2);
 1466             line->data[line_len] = ' ';
 1467             line->data[line_len + 1] = '\0';
 1468             rest_length++;
 1469             openfile->totsize++;
 1470             openfile->current_x++;
 1471 #ifndef NANO_TINY
 1472             update_undo(ADD);
 1473 #endif
 1474         }
 1475 
 1476         /* Join the next line to this one. */
 1477         do_delete();
 1478 
 1479 #ifdef ENABLE_JUSTIFY
 1480         /* If the quoting part of the current line equals the quoting part of
 1481          * what was the next line, then strip this second quoting part. */
 1482         if (strncmp(line->data, line->data + openfile->current_x, lead_len) == 0)
 1483             for (size_t i = lead_len; i > 0; i--)
 1484                 do_delete();
 1485 #endif
 1486         /* Remove any extra blanks. */
 1487         while (is_blank_mbchar(&line->data[openfile->current_x]))
 1488             do_delete();
 1489     }
 1490 
 1491     /* Go to the wrap location. */
 1492     openfile->current_x = wrap_loc;
 1493 
 1494     /* When requested, snip trailing blanks off the wrapped line. */
 1495     if (ISSET(TRIM_BLANKS)) {
 1496         size_t tail_x = step_left(line->data, wrap_loc);
 1497         size_t typed_x = step_left(line->data, cursor_x);
 1498 
 1499         while ((tail_x != typed_x || cursor_x >= wrap_loc) &&
 1500                         is_blank_mbchar(line->data + tail_x)) {
 1501             openfile->current_x = tail_x;
 1502             do_delete();
 1503             tail_x = step_left(line->data, tail_x);
 1504         }
 1505     }
 1506 
 1507     /* Now split the line. */
 1508     do_enter();
 1509 
 1510 #ifdef ENABLE_JUSTIFY
 1511     /* If the original line has quoting, copy it to the spillage line. */
 1512     if (lead_len > 0) {
 1513         lead_len += indent_length(line->data + lead_len);
 1514 
 1515         line = line->next;
 1516         line_len = strlen(line->data);
 1517         line->data = charealloc(line->data, lead_len + line_len + 1);
 1518 
 1519         memmove(line->data + lead_len, line->data, line_len + 1);
 1520         strncpy(line->data, line->prev->data, lead_len);
 1521 
 1522         openfile->current_x += lead_len;
 1523 #ifndef NANO_TINY
 1524         update_undo(ENTER);
 1525 #endif
 1526         if (autowhite)
 1527             SET(AUTOINDENT);
 1528     }
 1529 #endif
 1530 
 1531     openfile->spillage_line = openfile->current;
 1532 
 1533     if (cursor_x < wrap_loc) {
 1534         openfile->current = openfile->current->prev;
 1535         openfile->current_x = cursor_x;
 1536     } else
 1537         openfile->current_x += (cursor_x - wrap_loc);
 1538 
 1539     openfile->placewewant = xplustabs();
 1540 
 1541 #ifndef NANO_TINY
 1542     add_undo(SPLIT_END);
 1543 #endif
 1544 
 1545     return TRUE;
 1546 }
 1547 #endif /* ENABLE_WRAPPING */
 1548 
 1549 #if defined(ENABLE_HELP) || defined(ENABLED_WRAPORJUSTIFY)
 1550 /* We are trying to break a chunk off line.  We find the last blank such
 1551  * that the display length to there is at most (goal + 1).  If there is
 1552  * no such blank, then we find the first blank.  We then take the last
 1553  * blank in that group of blanks.  The terminating '\0' counts as a
 1554  * blank, as does a '\n' if snap_at_nl is TRUE. */
 1555 ssize_t break_line(const char *line, ssize_t goal, bool snap_at_nl)
 1556 {
 1557     ssize_t lastblank = -1;
 1558         /* The index of the last blank we found. */
 1559     ssize_t index = 0;
 1560         /* The index of the character we are looking at. */
 1561     size_t column = 0;
 1562         /* The column position that corresponds with index. */
 1563     int charlen = 0;
 1564         /* The length of the current character, in bytes. */
 1565 
 1566     /* Find the last blank that does not overshoot the target column. */
 1567     while (*line != '\0' && ((ssize_t)column <= goal)) {
 1568         if (is_blank_mbchar(line) || (snap_at_nl && *line == '\n')) {
 1569             lastblank = index;
 1570 
 1571             if (*line == '\n')
 1572                 break;
 1573         }
 1574 
 1575         charlen = parse_mbchar(line, NULL, &column);
 1576         line += charlen;
 1577         index += charlen;
 1578     }
 1579 
 1580     /* If the whole line displays shorter than goal, we're done. */
 1581     if ((ssize_t)column <= goal)
 1582         return index;
 1583 
 1584 #ifdef ENABLE_HELP
 1585     /* If we're wrapping a help text and no blank was found, or was
 1586      * found only as the first character, force a line break. */
 1587     if (snap_at_nl && lastblank < 1)
 1588         return (index - charlen);
 1589 #endif
 1590 
 1591     /* If no blank was found within the goal width, seek one after it. */
 1592     if (lastblank < 0) {
 1593         while (*line != '\0') {
 1594             if (is_blank_mbchar(line))
 1595                 lastblank = index;
 1596             else if (lastblank > 0)
 1597                 return lastblank;
 1598 
 1599             charlen = char_length(line);
 1600             line += charlen;
 1601             index += charlen;
 1602         }
 1603 
 1604         return -1;
 1605     }
 1606 
 1607     /* Move the pointer back to the last blank, and then step beyond it. */
 1608     line = line - index + lastblank;
 1609     charlen = char_length(line);
 1610     line += charlen;
 1611 
 1612     /* Skip any consecutive blanks after the last blank. */
 1613     while (*line != '\0' && is_blank_mbchar(line)) {
 1614         lastblank += charlen;
 1615         charlen = char_length(line);
 1616         line += charlen;
 1617     }
 1618 
 1619     return lastblank;
 1620 }
 1621 #endif /* ENABLE_HELP || ENABLED_WRAPORJUSTIFY */
 1622 
 1623 #if !defined(NANO_TINY) || defined(ENABLE_JUSTIFY)
 1624 /* Return the length of the indentation part of the given line.  The
 1625  * "indentation" of a line is the leading consecutive whitespace. */
 1626 size_t indent_length(const char *line)
 1627 {
 1628     size_t len = 0;
 1629     char onechar[MAXCHARLEN];
 1630     int charlen;
 1631 
 1632     while (*line != '\0') {
 1633         charlen = parse_mbchar(line, onechar, NULL);
 1634 
 1635         if (!is_blank_mbchar(onechar))
 1636             break;
 1637 
 1638         line += charlen;
 1639         len += charlen;
 1640     }
 1641 
 1642     return len;
 1643 }
 1644 #endif /* !NANO_TINY || ENABLE_JUSTIFY */
 1645 
 1646 #ifdef ENABLE_JUSTIFY
 1647 /* Copy a character from one place to another. */
 1648 void copy_character(char **from, char **to)
 1649 {
 1650     int charlen = char_length(*from);
 1651 
 1652     if (*from == *to) {
 1653         *from += charlen;
 1654         *to += charlen;
 1655     } else
 1656         while (--charlen >= 0)
 1657             *((*to)++) = *((*from)++);
 1658 }
 1659 
 1660 /* In the given line, replace any series of blanks with a single space,
 1661  * but keep two spaces (if there are two) after any closing punctuation,
 1662  * and remove all blanks from the end of the line.  Leave the first skip
 1663  * number of characters untreated. */
 1664 void squeeze(linestruct *line, size_t skip)
 1665 {
 1666     char *start = line->data + skip;
 1667     char *from = start, *to = start;
 1668 
 1669     /* For each character, 1) when a blank, change it to a space, and pass over
 1670      * all blanks after it; 2) if it is punctuation, copy it plus a possible
 1671      * tailing bracket, and change at most two subsequent blanks to spaces, and
 1672      * pass over all blanks after these; 3) leave anything else unchanged. */
 1673     while (*from != '\0') {
 1674         if (is_blank_mbchar(from)) {
 1675             from += char_length(from);
 1676             *(to++) = ' ';
 1677 
 1678             while (*from != '\0' && is_blank_mbchar(from))
 1679                 from += char_length(from);
 1680         } else if (mbstrchr(punct, from) != NULL) {
 1681             copy_character(&from, &to);
 1682 
 1683             if (*from != '\0' && mbstrchr(brackets, from) != NULL)
 1684                 copy_character(&from, &to);
 1685 
 1686             if (*from != '\0' && is_blank_mbchar(from)) {
 1687                 from += char_length(from);
 1688                 *(to++) = ' ';
 1689             }
 1690             if (*from != '\0' && is_blank_mbchar(from)) {
 1691                 from += char_length(from);
 1692                 *(to++) = ' ';
 1693             }
 1694 
 1695             while (*from != '\0' && is_blank_mbchar(from))
 1696                 from += char_length(from);
 1697         } else
 1698             copy_character(&from, &to);
 1699     }
 1700 
 1701     /* If there are spaces at the end of the line, remove them. */
 1702     while (to > start && *(to - 1) == ' ')
 1703         to--;
 1704 
 1705     *to = '\0';
 1706 }
 1707 
 1708 /* Return the length of the quote part of the given line.  The "quote part"
 1709  * of a line is the largest initial substring matching the quoting regex. */
 1710 size_t quote_length(const char *line)
 1711 {
 1712     regmatch_t matches;
 1713     int rc = regexec(&quotereg, line, 1, &matches, 0);
 1714 
 1715     if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1)
 1716         return 0;
 1717 
 1718     return matches.rm_eo;
 1719 }
 1720 
 1721 /* The maximum depth of recursion.  This must be an even number. */
 1722 #define RECURSION_LIMIT 222
 1723 
 1724 /* Return TRUE when the given line is the beginning of a paragraph (BOP). */
 1725 bool begpar(const linestruct *const line, int depth)
 1726 {
 1727     size_t quote_len, indent_len, prev_dent_len;
 1728 
 1729     /* If this is the very first line of the buffer, it counts as a BOP
 1730      * even when it contains no text. */
 1731     if (line == openfile->filetop)
 1732         return TRUE;
 1733 
 1734     /* If recursion is going too deep, just say it's not a BOP. */
 1735     if (depth > RECURSION_LIMIT)
 1736         return FALSE;
 1737 
 1738     quote_len = quote_length(line->data);
 1739     indent_len = indent_length(line->data + quote_len);
 1740 
 1741     /* If this line contains no text, it is not a BOP. */
 1742     if (line->data[quote_len + indent_len] == '\0')
 1743         return FALSE;
 1744 
 1745     /* If the quote part of the preceding line differs, this is a BOP. */
 1746     if (quote_len != quote_length(line->prev->data) ||
 1747                     strncmp(line->data, line->prev->data, quote_len) != 0)
 1748         return TRUE;
 1749 
 1750     prev_dent_len = indent_length(line->prev->data + quote_len);
 1751 
 1752     /* If the preceding line contains no text, this is a BOP. */
 1753     if (line->prev->data[quote_len + prev_dent_len] == '\0')
 1754         return TRUE;
 1755 
 1756     /* If the indentation of the preceding line equals the indentation
 1757      * of this line, this is not a BOP. */
 1758     if (prev_dent_len == indent_len && strncmp(line->prev->data + quote_len,
 1759                                     line->data + quote_len, indent_len) == 0)
 1760         return FALSE;
 1761 
 1762     /* Otherwise, this is a BOP if the preceding line is not. */
 1763     return !begpar(line->prev, depth + 1);
 1764 }
 1765 
 1766 /* Return TRUE when the given line is part of a paragraph: when it
 1767  * contains something more than quoting and leading whitespace. */
 1768 bool inpar(const linestruct *const line)
 1769 {
 1770     size_t quote_len = quote_length(line->data);
 1771     size_t indent_len = indent_length(line->data + quote_len);
 1772 
 1773     return (line->data[quote_len + indent_len] != '\0');
 1774 }
 1775 
 1776 /* Find the first occurring paragraph in the forward direction.  Return TRUE
 1777  * when a paragraph was found, and FALSE otherwise.  Furthermore, return the
 1778  * first line and the length (number of lines) of the paragraph. */
 1779 bool find_paragraph(linestruct **firstline, size_t *const parlen)
 1780 {
 1781     linestruct *line = *firstline;
 1782 
 1783     /* When not currently in a paragraph, move forward to a line that is. */
 1784     while (!inpar(line) && line->next != NULL)
 1785         line = line->next;
 1786 
 1787     *firstline = line;
 1788 
 1789     /* Move down to the last line of the paragraph (if any). */
 1790     do_para_end(&line);
 1791 
 1792     /* When not in a paragraph now, there aren't any paragraphs left. */
 1793     if (!inpar(line))
 1794         return FALSE;
 1795 
 1796     /* We found a paragraph; determine its number of lines. */
 1797     *parlen = line->lineno - (*firstline)->lineno + 1;
 1798 
 1799     return TRUE;
 1800 }
 1801 
 1802 /* Concatenate into a single line all the lines of the paragraph that starts at
 1803  * *line and consists of par_len lines, skipping the quoting and indentation on
 1804  * all lines after the first. */
 1805 void concat_paragraph(linestruct **line, size_t par_len)
 1806 {
 1807     while (par_len > 1) {
 1808         linestruct *next_line = (*line)->next;
 1809         size_t line_len = strlen((*line)->data);
 1810         size_t next_line_len = strlen(next_line->data);
 1811         size_t next_quote_len = quote_length(next_line->data);
 1812         size_t next_lead_len = next_quote_len +
 1813                             indent_length(next_line->data + next_quote_len);
 1814 
 1815         /* We're just about to tack the next line onto this one.  If
 1816          * this line isn't empty, make sure it ends in a space. */
 1817         if (line_len > 0 && (*line)->data[line_len - 1] != ' ') {
 1818             (*line)->data = charealloc((*line)->data, line_len + 2);
 1819             (*line)->data[line_len++] = ' ';
 1820             (*line)->data[line_len] = '\0';
 1821         }
 1822 
 1823         (*line)->data = charealloc((*line)->data,
 1824                                 line_len + next_line_len - next_lead_len + 1);
 1825         strcat((*line)->data, next_line->data + next_lead_len);
 1826 
 1827         unlink_node(next_line);
 1828         par_len--;
 1829     }
 1830 }
 1831 
 1832 /* Rewrap the given line (that starts with the given lead string which is of
 1833  * the given length), into lines that fit within the target width (wrap_at). */
 1834 void rewrap_paragraph(linestruct **line, char *lead_string, size_t lead_len)
 1835 {
 1836     ssize_t break_pos;
 1837         /* The x-coordinate where the current line is to be broken. */
 1838 
 1839     while (breadth((*line)->data) > wrap_at) {
 1840         size_t line_len = strlen((*line)->data);
 1841 
 1842         /* Find a point in the line where it can be broken. */
 1843         break_pos = break_line((*line)->data + lead_len,
 1844                         wrap_at - wideness((*line)->data, lead_len), FALSE);
 1845 
 1846         /* If we can't break the line, or don't need to, we're done. */
 1847         if (break_pos == -1 || break_pos + lead_len == line_len)
 1848             break;
 1849 
 1850         /* Adjust the breaking position for the leading part and
 1851          * move it beyond the found whitespace character. */
 1852         break_pos += lead_len + 1;
 1853 
 1854         /* Insert a new line after the current one, and copy the leading part
 1855          * plus the text after the breaking point into it. */
 1856         splice_node(*line, make_new_node(*line));
 1857         (*line)->next->data = charalloc(lead_len + line_len - break_pos + 1);
 1858         strncpy((*line)->next->data, lead_string, lead_len);
 1859         strcpy((*line)->next->data + lead_len, (*line)->data + break_pos);
 1860 
 1861         /* When requested, snip all trailing blanks. */
 1862         if (ISSET(TRIM_BLANKS)) {
 1863             while (break_pos > 0 &&
 1864                         is_blank_mbchar(&(*line)->data[break_pos - 1]))
 1865                 break_pos--;
 1866         }
 1867 
 1868         /* Now actually break the current line, and go to the next. */
 1869         (*line)->data[break_pos] = '\0';
 1870         *line = (*line)->next;
 1871     }
 1872 
 1873     /* When possible, go to the line after the rewrapped paragraph. */
 1874     if ((*line)->next != NULL)
 1875         *line = (*line)->next;
 1876 }
 1877 
 1878 /* Justify the lines of the given paragraph (that starts at *line, and consists
 1879  * of par_len lines) so they all fit within the target width (wrap_at) and have
 1880  * their whitespace normalized. */
 1881 void justify_paragraph(linestruct **line, size_t par_len)
 1882 {
 1883     linestruct *sampleline;
 1884         /* The line from which the indentation is copied. */
 1885     size_t quote_len;
 1886         /* Length of the quote part. */
 1887     size_t lead_len;
 1888         /* Length of the quote part plus the indentation part. */
 1889     char *lead_string;
 1890         /* The quote+indent stuff that is copied from the sample line. */
 1891 
 1892     /* The sample line is either the only line or the second line. */
 1893     sampleline = (par_len == 1 ? *line : (*line)->next);
 1894 
 1895     /* Copy the leading part (quoting + indentation) of the sample line. */
 1896     quote_len = quote_length(sampleline->data);
 1897     lead_len = quote_len + indent_length(sampleline->data + quote_len);
 1898     lead_string = mallocstrncpy(NULL, sampleline->data, lead_len + 1);
 1899     lead_string[lead_len] = '\0';
 1900 
 1901     /* Concatenate all lines of the paragraph into a single line. */
 1902     concat_paragraph(line, par_len);
 1903 
 1904     /* Change all blank characters to spaces and remove excess spaces. */
 1905     squeeze(*line, quote_len + indent_length((*line)->data + quote_len));
 1906 
 1907     /* Rewrap the line into multiple lines, accounting for the leading part. */
 1908     rewrap_paragraph(line, lead_string, lead_len);
 1909 
 1910     free(lead_string);
 1911 }
 1912 
 1913 /* Justify the current paragraph, or the entire buffer when full_justify is
 1914  * TRUE.  But if the mark is on, justify only the marked text instead. */
 1915 void do_justify(bool full_justify)
 1916 {
 1917     size_t par_len;
 1918         /* The number of lines in the original paragraph. */
 1919     linestruct *first_par_line;
 1920         /* The first line of the paragraph. */
 1921     linestruct *last_par_line;
 1922         /* The line after the last line of the paragraph. */
 1923     size_t top_x;
 1924         /* The top x-coordinate of the paragraph we justify. */
 1925     size_t bot_x;
 1926         /* The bottom x-coordinate of the paragraph we justify. */
 1927     linestruct *was_cutbuffer = cutbuffer;
 1928         /* The old cutbuffer, so we can justify in the current cutbuffer. */
 1929     linestruct *jusline;
 1930         /* The line that we're justifying in the current cutbuffer. */
 1931 
 1932 #ifndef NANO_TINY
 1933     /* Stash the cursor position, to be stored in the undo item. */
 1934     ssize_t was_lineno = openfile->current->lineno;
 1935     size_t was_current_x = openfile->current_x;
 1936 
 1937     /* We need these to restore the coordinates of the mark after justifying
 1938      * marked text. */
 1939     ssize_t was_top_lineno = 0;
 1940     size_t was_top_x = 0;
 1941     bool right_side_up = FALSE;
 1942 
 1943     /* Whether the bottom of the mark is at the end of its line, in which case
 1944      * we don't need to add a new line after it. */
 1945     bool ends_at_eol = FALSE;
 1946 
 1947     /* We need these to hold the leading part (quoting + indentation) of the
 1948      * line where the marked text begins, whether or not that part is covered
 1949      * by the mark. */
 1950     char *the_lead = NULL;
 1951     size_t lead_len = 0;
 1952 
 1953     /* We need these to hold the leading part of the line after the line where
 1954      * the marked text begins (if any). */
 1955     char *the_second_lead = NULL;
 1956     size_t second_lead_len = 0;
 1957 #endif
 1958 
 1959 #ifndef NANO_TINY
 1960     /* If the mark is on, do as Pico: treat all marked text as one paragraph. */
 1961     if (openfile->mark) {
 1962         size_t quote_len;
 1963 
 1964         get_region((const linestruct **)&first_par_line, &top_x,
 1965                     (const linestruct **)&last_par_line, &bot_x, &right_side_up);
 1966 
 1967         /* Save the starting point of the marked region. */
 1968         was_top_lineno = first_par_line->lineno;
 1969         was_top_x = top_x;
 1970 
 1971         par_len = last_par_line->lineno - first_par_line->lineno +
 1972                                             (bot_x > 0 ? 1 : 0);
 1973 
 1974         /* Remember whether the end of the region was at the end of a line. */
 1975         ends_at_eol = last_par_line->data[bot_x] == '\0';
 1976 
 1977         /* Copy the leading part that is to be used for the new paragraph. */
 1978         quote_len = quote_length(first_par_line->data);
 1979         lead_len = quote_len + indent_length(first_par_line->data + quote_len);
 1980         the_lead = mallocstrncpy(the_lead, first_par_line->data, lead_len + 1);
 1981         the_lead[lead_len] = '\0';
 1982 
 1983         /* Copy the leading part that is to be used for the new paragraph after
 1984          * its first line (if any): the quoting of the first line, plus the
 1985          * indentation of the second line. */
 1986         if (first_par_line != last_par_line) {
 1987             size_t sample_quote_len = quote_length(first_par_line->next->data);
 1988             size_t sample_indent_len = indent_length(first_par_line->next->data +
 1989                                                         sample_quote_len);
 1990 
 1991             second_lead_len = quote_len + sample_indent_len;
 1992             the_second_lead = charalloc(second_lead_len + 1);
 1993             strncpy(the_second_lead, first_par_line->data, quote_len);
 1994             strncpy(the_second_lead + quote_len, first_par_line->next->data +
 1995                     sample_quote_len, sample_indent_len);
 1996             the_second_lead[second_lead_len] = '\0';
 1997         }
 1998     } else
 1999 #endif
 2000     {
 2001         size_t jus_len;
 2002             /* The number of lines we're storing in the current cutbuffer. */
 2003 
 2004         /* When justifying the entire buffer, start at the top.  Otherwise, when
 2005          * in a paragraph but not at its beginning, move back to its first line. */
 2006         if (full_justify)
 2007             openfile->current = openfile->filetop;
 2008         else if (inpar(openfile->current) && !begpar(openfile->current, 0))
 2009             do_para_begin(&openfile->current);
 2010 
 2011         /* Find the first line of the paragraph(s) to be justified.  If the
 2012          * search fails, there is nothing to justify, and we will be on the
 2013          * last line of the file, so put the cursor at the end of it. */
 2014         if (!find_paragraph(&openfile->current, &par_len)) {
 2015             openfile->current_x = strlen(openfile->filebot->data);
 2016             refresh_needed = TRUE;
 2017             return;
 2018         }
 2019 
 2020         first_par_line = openfile->current;
 2021         top_x = 0;
 2022 
 2023         /* Set the number of lines to be pulled into the cutbuffer. */
 2024         if (full_justify)
 2025             jus_len = openfile->filebot->lineno - first_par_line->lineno + 1;
 2026         else
 2027             jus_len = par_len;
 2028 
 2029         /* Move down to the last line to be extracted. */
 2030         for (last_par_line = openfile->current; jus_len > 1; jus_len--)
 2031             last_par_line = last_par_line->next;
 2032 
 2033         /* When possible, step one line further; otherwise, to line's end. */
 2034         if (last_par_line->next != NULL) {
 2035             last_par_line = last_par_line->next;
 2036             bot_x = 0;
 2037         } else
 2038             bot_x = strlen(last_par_line->data);
 2039     }
 2040 
 2041 #ifndef NANO_TINY
 2042     add_undo(COUPLE_BEGIN);
 2043     openfile->undotop->strdata = mallocstrcpy(NULL, _("justification"));
 2044 
 2045     /* Store the original cursor position, in case we unjustify. */
 2046     openfile->undotop->lineno = was_lineno;
 2047     openfile->undotop->begin = was_current_x;
 2048 
 2049     add_undo(CUT);
 2050 #endif
 2051 
 2052     /* Do the equivalent of a marked cut into an empty cutbuffer. */
 2053     cutbuffer = NULL;
 2054     extract(first_par_line, top_x, last_par_line, bot_x);
 2055 #ifndef NANO_TINY
 2056     update_undo(CUT);
 2057 
 2058     if (openfile->mark) {
 2059         size_t line_len = strlen(cutbuffer->data), indent_len;
 2060         size_t needed_top_extra = (top_x < lead_len ? top_x : lead_len);
 2061         size_t needed_bot_extra = (bot_x < lead_len ? lead_len - bot_x : 0);
 2062         linestruct *line;
 2063 
 2064         /* If the marked region starts in the middle of a line, and this line
 2065          * has a leading part, prepend any missing portion of this leading part
 2066          * to the first line of the extracted region. */
 2067         if (needed_top_extra > 0) {
 2068             cutbuffer->data = charealloc(cutbuffer->data,
 2069                                     line_len + needed_top_extra + 1);
 2070             memmove(cutbuffer->data + needed_top_extra, cutbuffer->data,
 2071                                     line_len + 1);
 2072             strncpy(cutbuffer->data, the_lead, needed_top_extra);
 2073             line_len += needed_top_extra;
 2074 
 2075             /* When no portion was missing, nothing needs removing later. */
 2076             if (top_x > lead_len)
 2077                 needed_top_extra = 0;
 2078         }
 2079 
 2080         indent_len = indent_length(cutbuffer->data + lead_len);
 2081 
 2082         /* Remove extra whitespace after the leading part. */
 2083         if (indent_len > 0)
 2084             memmove(cutbuffer->data + lead_len,
 2085                         cutbuffer->data + lead_len + indent_len,
 2086                         line_len - indent_len + 1);
 2087 
 2088         /* If the marked region ends in the middle of a line, and this line
 2089          * has a leading part, check if the last line of the extracted region
 2090          * contains a missing portion of this leading part.  If it has no
 2091          * missing portion, we don't need to append anything below. */
 2092         if (strncmp(cutbottom->data, the_lead, lead_len - needed_bot_extra) != 0)
 2093             needed_bot_extra = 0;
 2094 
 2095         /* Now justify the extracted region. */
 2096         concat_paragraph(&cutbuffer, par_len);
 2097         squeeze(cutbuffer, lead_len);
 2098         line = cutbuffer;
 2099         if (the_second_lead != NULL) {
 2100             rewrap_paragraph(&line, the_second_lead, second_lead_len);
 2101             free(the_second_lead);
 2102         } else
 2103             rewrap_paragraph(&line, the_lead, lead_len);
 2104 
 2105         /* If the marked region started after the beginning of a line, insert
 2106          * a new line before the new paragraph.  But if the region started in
 2107          * the middle of the line's leading part, no new line is needed: just
 2108          * remove the (now-redundant) addition we made earlier. */
 2109         if (top_x > 0) {
 2110             if (needed_top_extra > 0)
 2111                 memmove(cutbuffer->data, cutbuffer->data + needed_top_extra,
 2112                             strlen(cutbuffer->data) - needed_top_extra + 1);
 2113             else {
 2114                 cutbuffer->prev = make_new_node(NULL);
 2115                 cutbuffer->prev->data = mallocstrcpy(NULL, "");
 2116                 cutbuffer->prev->next = cutbuffer;
 2117                 cutbuffer = cutbuffer->prev;
 2118             }
 2119         }
 2120 
 2121         /* If the marked region ended in the middle of a line, insert a new
 2122          * line after the new paragraph.  If the region ended in the middle
 2123          * of a line's leading part, make the new line start with the missing
 2124          * portion, so it will become a full leading part when the justified
 2125          * region is "pasted" back. */
 2126         if (bot_x > 0 && !ends_at_eol) {
 2127             line->next = make_new_node(line);
 2128             line->next->data = mallocstrcpy(NULL, the_lead + needed_bot_extra);
 2129         }
 2130 
 2131         free(the_lead);
 2132     } else
 2133 #endif
 2134     {
 2135         /* Prepare to justify the text we just put in the cutbuffer. */
 2136         jusline = cutbuffer;
 2137 
 2138         /* Justify the current paragraph. */
 2139         justify_paragraph(&jusline, par_len);
 2140 
 2141         /* When justifying the entire buffer, find and justify all paragraphs. */
 2142         if (full_justify) {
 2143             while (find_paragraph(&jusline, &par_len)) {
 2144                 justify_paragraph(&jusline, par_len);
 2145 
 2146                 if (jusline->next == NULL)
 2147                     break;
 2148             }
 2149         }
 2150     }
 2151 
 2152 #ifndef NANO_TINY
 2153     add_undo(PASTE);
 2154 #endif
 2155     /* Do the equivalent of a paste of the justified text. */
 2156     ingraft_buffer(cutbuffer);
 2157 #ifndef NANO_TINY
 2158     update_undo(PASTE);
 2159 
 2160     add_undo(COUPLE_END);
 2161     openfile->undotop->strdata = mallocstrcpy(NULL, _("justification"));
 2162 
 2163     /* If we justified marked text, restore mark or cursor position. */
 2164     if (openfile->mark) {
 2165         if (right_side_up) {
 2166             openfile->mark = line_from_number(was_top_lineno);
 2167             openfile->mark_x = was_top_x;
 2168         } else {
 2169             openfile->current = line_from_number(was_top_lineno);
 2170             openfile->current_x = was_top_x;
 2171         }
 2172         update_undo(COUPLE_END);
 2173     }
 2174 #endif
 2175 
 2176     /* We're done justifying.  Restore the old cutbuffer. */
 2177     cutbuffer = was_cutbuffer;
 2178 
 2179     /* Show what we justified on the status bar. */
 2180 #ifndef NANO_TINY
 2181     if (openfile->mark)
 2182         statusbar(_("Justified selection"));
 2183     else
 2184 #endif
 2185     if (full_justify)
 2186         statusbar(_("Justified file"));
 2187     else
 2188         statusbar(_("Justified paragraph"));
 2189 
 2190     /* Set the desired screen column (always zero, except at EOF). */
 2191     openfile->placewewant = xplustabs();
 2192 
 2193     set_modified();
 2194     refresh_needed = TRUE;
 2195     shift_held = TRUE;
 2196 }
 2197 
 2198 /* Justify the current paragraph. */
 2199 void do_justify_void(void)
 2200 {
 2201     do_justify(FALSE);
 2202 }
 2203 
 2204 /* Justify the entire file. */
 2205 void do_full_justify(void)
 2206 {
 2207     do_justify(TRUE);
 2208 }
 2209 #endif /* ENABLE_JUSTIFY */
 2210 
 2211 #if defined(ENABLE_SPELLER) || defined (ENABLE_COLOR)
 2212 /* Set up an argument list for executing the given command. */
 2213 void construct_argument_list(char ***arguments, char *command, char *filename)
 2214 {
 2215     char *copy_of_command = mallocstrcpy(NULL, command);
 2216     char *element = strtok(copy_of_command, " ");
 2217     int count = 2;
 2218 
 2219     while (element != NULL) {
 2220         *arguments = (char **)nrealloc(*arguments, ++count * sizeof(char *));
 2221         (*arguments)[count - 3] = element;
 2222         element = strtok(NULL, " ");
 2223     }
 2224 
 2225     (*arguments)[count - 2] = filename;
 2226     (*arguments)[count - 1] = NULL;
 2227 }
 2228 #endif
 2229 
 2230 #ifdef ENABLE_SPELLER
 2231 /* Let the user edit the misspelled word.  Return FALSE if the user cancels. */
 2232 bool fix_spello(const char *word)
 2233 {
 2234     char *save_search;
 2235     size_t firstcolumn_save = openfile->firstcolumn;
 2236     size_t current_x_save = openfile->current_x;
 2237     linestruct *edittop_save = openfile->edittop;
 2238     linestruct *current_save = openfile->current;
 2239         /* Save where we are. */
 2240     bool proceed = FALSE;
 2241         /* The return value of this function. */
 2242     bool result;
 2243         /* The return value of searching for a misspelled word. */
 2244 #ifndef NANO_TINY
 2245     bool right_side_up = FALSE;
 2246         /* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
 2247          * FALSE if (current, current_x) is. */
 2248     linestruct *top, *bot;
 2249     size_t top_x, bot_x;
 2250 #endif
 2251 
 2252     /* Save the current search string, then set it to the misspelled word. */
 2253     save_search = last_search;
 2254     last_search = mallocstrcpy(NULL, word);
 2255 
 2256 #ifndef NANO_TINY
 2257     /* If the mark is on, start at the beginning of the marked region. */
 2258     if (openfile->mark) {
 2259         get_region((const linestruct **)&top, &top_x,
 2260                     (const linestruct **)&bot, &bot_x, &right_side_up);
 2261         /* If the region is marked normally, swap the end points, so that
 2262          * (current, current_x) (where searching starts) is at the top. */
 2263         if (right_side_up) {
 2264             openfile->current = top;
 2265             openfile->current_x = top_x;
 2266             openfile->mark = bot;
 2267             openfile->mark_x = bot_x;
 2268         }
 2269     } else
 2270 #endif
 2271     /* Otherwise, start from the top of the file. */
 2272     {
 2273         openfile->current = openfile->filetop;
 2274         openfile->current_x = 0;
 2275     }
 2276 
 2277     /* Find the first whole occurrence of word. */
 2278     result = findnextstr(word, TRUE, INREGION, NULL, FALSE, NULL, 0);
 2279 
 2280     /* If the word isn't found, alert the user; if it is, allow correction. */
 2281     if (result == 0) {
 2282         statusline(ALERT, _("Unfindable word: %s"), word);
 2283         lastmessage = HUSH;
 2284         proceed = TRUE;
 2285         napms(2800);
 2286     } else if (result == 1) {
 2287         spotlighted = TRUE;
 2288         light_from_col = xplustabs();
 2289         light_to_col = light_from_col + breadth(word);
 2290 #ifndef NANO_TINY
 2291         linestruct *saved_mark = openfile->mark;
 2292         openfile->mark = NULL;
 2293 #endif
 2294         edit_refresh();
 2295 
 2296         /* Let the user supply a correctly spelled alternative. */
 2297         proceed = (do_prompt(FALSE, FALSE, MSPELL, word, NULL,
 2298                                 edit_refresh, _("Edit a replacement")) != -1);
 2299 
 2300         spotlighted = FALSE;
 2301 
 2302 #ifndef NANO_TINY
 2303         openfile->mark = saved_mark;
 2304 #endif
 2305 
 2306         /* If a replacement was given, go through all occurrences. */
 2307         if (proceed && strcmp(word, answer) != 0) {
 2308             do_replace_loop(word, TRUE, current_save, &current_x_save);
 2309 
 2310             /* TRANSLATORS: Shown after fixing misspellings in one word. */
 2311             statusbar(_("Next word..."));
 2312             napms(400);
 2313         }
 2314     }
 2315 
 2316 #ifndef NANO_TINY
 2317     if (openfile->mark) {
 2318         /* Restore the (compensated) end points of the marked region. */
 2319         if (right_side_up) {
 2320             openfile->current = openfile->mark;
 2321             openfile->current_x = openfile->mark_x;
 2322             openfile->mark = top;
 2323             openfile->mark_x = top_x;
 2324         } else {
 2325             openfile->current = top;
 2326             openfile->current_x = top_x;
 2327         }
 2328     } else
 2329 #endif
 2330     {
 2331         /* Restore the (compensated) cursor position. */
 2332         openfile->current = current_save;
 2333         openfile->current_x = current_x_save;
 2334     }
 2335 
 2336     /* Restore the string that was last searched for. */
 2337     free(last_search);
 2338     last_search = save_search;
 2339 
 2340     /* Restore the viewport to where it was. */
 2341     openfile->edittop = edittop_save;
 2342     openfile->firstcolumn = firstcolumn_save;
 2343 
 2344     return proceed;
 2345 }
 2346 
 2347 /* Internal (integrated) spell checking using the spell program,
 2348  * filtered through the sort and uniq programs.  Return NULL for normal
 2349  * termination, and the error string otherwise. */
 2350 const char *do_int_speller(const char *tempfile_name)
 2351 {
 2352     char *misspellings, *pointer, *oneword;
 2353     long pipesize;
 2354     size_t buffersize, bytesread, totalread;
 2355     int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
 2356     pid_t pid_spell, pid_sort, pid_uniq;
 2357     int spell_status, sort_status, uniq_status;
 2358 
 2359     /* Create all three pipes up front. */
 2360     if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 || pipe(uniq_fd) == -1)
 2361         return _("Could not create pipe");
 2362 
 2363     statusbar(_("Creating misspelled word list, please wait..."));
 2364 
 2365     /* A new process to run spell in. */
 2366     if ((pid_spell = fork()) == 0) {
 2367         /* Child continues (i.e. future spell process). */
 2368         close(spell_fd[0]);
 2369 
 2370         /* Replace the standard input with the temp file. */
 2371         if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
 2372             goto close_pipes_and_exit;
 2373 
 2374         if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) {
 2375             close(tempfile_fd);
 2376             goto close_pipes_and_exit;
 2377         }
 2378 
 2379         close(tempfile_fd);
 2380 
 2381         /* Send spell's standard output to the pipe. */
 2382         if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
 2383             goto close_pipes_and_exit;
 2384 
 2385         close(spell_fd[1]);
 2386 
 2387         /* Start the spell program; we are using $PATH. */
 2388         execlp("spell", "spell", NULL);
 2389 
 2390         /* This should not be reached if spell is found. */
 2391         exit(1);
 2392     }
 2393 
 2394     /* Parent continues here. */
 2395     close(spell_fd[1]);
 2396 
 2397     /* A new process to run sort in. */
 2398     if ((pid_sort = fork()) == 0) {
 2399         /* Child continues (i.e. future sort process).  Replace the
 2400          * standard input with the standard output of the old pipe. */
 2401         if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
 2402             goto close_pipes_and_exit;
 2403 
 2404         close(spell_fd[0]);
 2405 
 2406         /* Send sort's standard output to the new pipe. */
 2407         if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
 2408             goto close_pipes_and_exit;
 2409 
 2410         close(sort_fd[1]);
 2411 
 2412         /* Start the sort program.  Use -f to ignore case. */
 2413         execlp("sort", "sort", "-f", NULL);
 2414 
 2415         /* This should not be reached if sort is found. */
 2416         exit(1);
 2417     }
 2418 
 2419     close(spell_fd[0]);
 2420     close(sort_fd[1]);
 2421 
 2422     /* A new process to run uniq in. */
 2423     if ((pid_uniq = fork()) == 0) {
 2424         /* Child continues (i.e. future uniq process).  Replace the
 2425          * standard input with the standard output of the old pipe. */
 2426         if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
 2427             goto close_pipes_and_exit;
 2428 
 2429         close(sort_fd[0]);
 2430 
 2431         /* Send uniq's standard output to the new pipe. */
 2432         if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
 2433             goto close_pipes_and_exit;
 2434 
 2435         close(uniq_fd[1]);
 2436 
 2437         /* Start the uniq program; we are using PATH. */
 2438         execlp("uniq", "uniq", NULL);
 2439 
 2440         /* This should not be reached if uniq is found. */
 2441         exit(1);
 2442     }
 2443 
 2444     close(sort_fd[0]);
 2445     close(uniq_fd[1]);
 2446 
 2447     /* The child process was not forked successfully. */
 2448     if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
 2449         close(uniq_fd[0]);
 2450         return _("Could not fork");
 2451     }
 2452 
 2453     /* Get the system pipe buffer size. */
 2454     pipesize = fpathconf(uniq_fd[0], _PC_PIPE_BUF);
 2455 
 2456     if (pipesize < 1) {
 2457         close(uniq_fd[0]);
 2458         return _("Could not get size of pipe buffer");
 2459     }
 2460 
 2461     /* Block SIGWINCHes while reading misspelled words from the pipe. */
 2462     block_sigwinch(TRUE);
 2463 
 2464     totalread = 0;
 2465     buffersize = pipesize + 1;
 2466     misspellings = charalloc(buffersize);
 2467     pointer = misspellings;
 2468 
 2469     while ((bytesread = read(uniq_fd[0], pointer, pipesize)) > 0) {
 2470         totalread += bytesread;
 2471         buffersize += pipesize;
 2472         misspellings = charealloc(misspellings, buffersize);
 2473         pointer = misspellings + totalread;
 2474     }
 2475 
 2476     *pointer = '\0';
 2477     close(uniq_fd[0]);
 2478 
 2479     block_sigwinch(FALSE);
 2480 
 2481     /* Do any replacements case sensitive, forward, and without regexes. */
 2482     SET(CASE_SENSITIVE);
 2483     UNSET(BACKWARDS_SEARCH);
 2484     UNSET(USE_REGEXP);
 2485 
 2486     pointer = misspellings;
 2487     oneword = misspellings;
 2488 
 2489     /* Process each of the misspelled words. */
 2490     while (*pointer != '\0') {
 2491         if ((*pointer == '\r') || (*pointer == '\n')) {
 2492             *pointer = '\0';
 2493             if (oneword != pointer) {
 2494                 if (!fix_spello(oneword)) {
 2495                     oneword = pointer;
 2496                     break;
 2497                 }
 2498             }
 2499             oneword = pointer + 1;
 2500         }
 2501         pointer++;
 2502     }
 2503 
 2504     /* Special case: the last word doesn't end with '\r' or '\n'. */
 2505     if (oneword != pointer)
 2506         fix_spello(oneword);
 2507 
 2508     free(misspellings);
 2509     refresh_needed = TRUE;
 2510 
 2511     /* Process the end of the three processes. */
 2512     waitpid(pid_spell, &spell_status, 0);
 2513     waitpid(pid_sort, &sort_status, 0);
 2514     waitpid(pid_uniq, &uniq_status, 0);
 2515 
 2516     if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
 2517         return _("Error invoking \"uniq\"");
 2518 
 2519     if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status))
 2520         return _("Error invoking \"sort -f\"");
 2521 
 2522     if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
 2523         return _("Error invoking \"spell\"");
 2524 
 2525     /* When all went okay. */
 2526     return NULL;
 2527 
 2528   close_pipes_and_exit:
 2529     /* Don't leak any handles. */
 2530     close(spell_fd[0]);
 2531     close(spell_fd[1]);
 2532     close(sort_fd[0]);
 2533     close(sort_fd[1]);
 2534     close(uniq_fd[0]);
 2535     close(uniq_fd[1]);
 2536     exit(1);
 2537 }
 2538 
 2539 /* External (alternate) spell checking.  Return NULL for normal
 2540  * termination, and the error string otherwise. */
 2541 const char *do_alt_speller(char *tempfile_name)
 2542 {
 2543     int alt_spell_status;
 2544     size_t current_x_save = openfile->current_x;
 2545     size_t pww_save = openfile->placewewant;
 2546     ssize_t lineno_save = openfile->current->lineno;
 2547     bool was_at_eol = (openfile->current->data[openfile->current_x] == '\0');
 2548     bool replaced = FALSE;
 2549     struct stat spellfileinfo;
 2550     time_t timestamp;
 2551     pid_t pid_spell;
 2552     static char **spellargs = NULL;
 2553 
 2554     /* Get the timestamp and the size of the temporary file. */
 2555     stat(tempfile_name, &spellfileinfo);
 2556     timestamp = spellfileinfo.st_mtime;
 2557 
 2558     /* If the number of bytes to check is zero, get out. */
 2559     if (spellfileinfo.st_size == 0)
 2560         return NULL;
 2561 
 2562     /* Exit from curses mode. */
 2563     endwin();
 2564 
 2565     construct_argument_list(&spellargs, alt_speller, tempfile_name);
 2566 
 2567     /* Fork a child process and run the alternate spell program in it. */
 2568     if ((pid_spell = fork()) == 0) {
 2569         execvp(spellargs[0], spellargs);
 2570 
 2571         /* Terminate the child process if no alternate speller is found. */
 2572         exit(1);
 2573     } else if (pid_spell < 0)
 2574         return _("Could not fork");
 2575 
 2576     /* Block SIGWINCHes while waiting for the alternate spell checker's end,
 2577      * so nano doesn't get pushed past the wait(). */
 2578     block_sigwinch(TRUE);
 2579     wait(&alt_spell_status);
 2580     block_sigwinch(FALSE);
 2581 
 2582     /* Set the desired terminal state again, and reenter curses mode. */
 2583     terminal_init();
 2584     doupdate();
 2585 
 2586     if (!WIFEXITED(alt_spell_status) || WEXITSTATUS(alt_spell_status) != 0)
 2587         return invocation_error(alt_speller);
 2588 
 2589     /* Stat the temporary file again. */
 2590     stat(tempfile_name, &spellfileinfo);
 2591 
 2592     /* Use the spell-checked file only when it changed. */
 2593     if (spellfileinfo.st_mtime != timestamp) {
 2594 #ifndef NANO_TINY
 2595         /* Replace the marked text (or entire text) with the corrected text. */
 2596         if (openfile->mark) {
 2597             bool upright = (openfile->mark->lineno < openfile->current->lineno ||
 2598                                     (openfile->mark == openfile->current &&
 2599                                     openfile->mark_x < openfile->current_x));
 2600             ssize_t was_mark_lineno = openfile->mark->lineno;
 2601 
 2602             replaced = replace_buffer(tempfile_name, CUT, TRUE);
 2603 
 2604             /* Adjust the end point of the marked region for any change in
 2605              * length of the region's last line. */
 2606             if (upright)
 2607                 current_x_save = openfile->current_x;
 2608             else
 2609                 openfile->mark_x = openfile->current_x;
 2610 
 2611             /* Restore the mark. */
 2612             openfile->mark = line_from_number(was_mark_lineno);
 2613         } else
 2614 #endif
 2615             replaced = replace_buffer(tempfile_name, CUT_TO_EOF, FALSE);
 2616 
 2617         /* Go back to the old position. */
 2618         goto_line_posx(lineno_save, current_x_save);
 2619         if (was_at_eol || openfile->current_x > strlen(openfile->current->data))
 2620             openfile->current_x = strlen(openfile->current->data);
 2621 #ifndef NANO_TINY
 2622         if (replaced)
 2623             update_undo(COUPLE_END);
 2624 #endif
 2625         openfile->placewewant = pww_save;
 2626         adjust_viewport(STATIONARY);
 2627     }
 2628 
 2629     return NULL;
 2630 }
 2631 
 2632 /* Spell check the current file.  If an alternate spell checker is
 2633  * specified, use it.  Otherwise, use the internal spell checker. */
 2634 void do_spell(void)
 2635 {
 2636     bool status;
 2637     FILE *temp_file;
 2638     char *temp;
 2639     unsigned stash[sizeof(flags) / sizeof(flags[0])];
 2640         /* A storage place for the current flag settings. */
 2641     const char *result_msg;
 2642 
 2643     if (ISSET(RESTRICTED)) {
 2644         show_restricted_warning();
 2645         return;
 2646     }
 2647 
 2648     temp = safe_tempfile(&temp_file);
 2649 
 2650     if (temp == NULL) {
 2651         statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
 2652         return;
 2653     }
 2654 
 2655     /* Save the settings of the global flags. */
 2656     memcpy(stash, flags, sizeof(flags));
 2657 
 2658     /* Don't add an extra newline when writing out the (selected) text. */
 2659     SET(NO_NEWLINES);
 2660 
 2661 #ifndef NANO_TINY
 2662     if (openfile->mark)
 2663         status = write_marked_file(temp, temp_file, TRUE, OVERWRITE);
 2664     else
 2665 #endif
 2666         status = write_file(temp, temp_file, TRUE, OVERWRITE, TRUE);
 2667 
 2668     if (!status) {
 2669         statusline(ALERT, _("Error writing temp file: %s"), strerror(errno));
 2670         free(temp);
 2671         return;
 2672     }
 2673 
 2674     blank_bottombars();
 2675 
 2676     result_msg = (alt_speller ? do_alt_speller(temp) : do_int_speller(temp));
 2677 
 2678     unlink(temp);
 2679     free(temp);
 2680 
 2681     /* Restore the settings of the global flags. */
 2682     memcpy(flags, stash, sizeof(flags));
 2683 
 2684     /* Ensure the help lines will be redrawn and a selection is retained. */
 2685     currmenu = MMOST;
 2686     shift_held = TRUE;
 2687 
 2688     if (result_msg != NULL) {
 2689         /* Avoid giving a failure reason of "Success". */
 2690         if (errno == 0)
 2691             statusline(ALERT, result_msg);
 2692         else
 2693             statusline(ALERT, _("%s: %s"), result_msg, strerror(errno));
 2694     } else
 2695         statusbar(_("Finished checking spelling"));
 2696 }
 2697 #endif /* ENABLE_SPELLER */
 2698 
 2699 #ifdef ENABLE_COLOR
 2700 /* Run a linting program on the current buffer.  Return NULL for normal
 2701  * termination, and the error string otherwise. */
 2702 void do_linter(void)
 2703 {
 2704     char *lintings, *pointer, *onelint;
 2705     long pipesize;
 2706     size_t buffersize, bytesread, totalread;
 2707     size_t parsesuccess = 0;
 2708     int lint_status, lint_fd[2];
 2709     pid_t pid_lint;
 2710     bool helpless = ISSET(NO_HELP);
 2711     static char **lintargs = NULL;
 2712     lintstruct *lints = NULL, *tmplint = NULL, *curlint = NULL;
 2713     time_t last_wait = 0;
 2714 
 2715     if (ISSET(RESTRICTED)) {
 2716         show_restricted_warning();
 2717         return;
 2718     }
 2719 
 2720     if (!openfile->syntax || !openfile->syntax->linter) {
 2721         statusbar(_("No linter defined for this type of file!"));
 2722         return;
 2723     }
 2724 
 2725 #ifndef NANO_TINY
 2726     openfile->mark = NULL;
 2727 #endif
 2728     edit_refresh();
 2729 
 2730     if (openfile->modified) {
 2731         int choice = do_yesno_prompt(FALSE, _("Save modified buffer before linting?"));
 2732 
 2733         if (choice == -1) {
 2734             statusbar(_("Cancelled"));
 2735             return;
 2736         } else if (choice == 1 && (do_writeout(FALSE, FALSE) != 1))
 2737             return;
 2738     }
 2739 
 2740     /* Create a pipe up front. */
 2741     if (pipe(lint_fd) == -1) {
 2742         statusline(ALERT, _("Could not create pipe"));
 2743         return;
 2744     }
 2745 
 2746     blank_bottombars();
 2747     currmenu = MLINTER;
 2748     statusbar(_("Invoking linter, please wait"));
 2749 
 2750     construct_argument_list(&lintargs, openfile->syntax->linter, openfile->filename);
 2751 
 2752     /* Start a new process to run the linter in. */
 2753     if ((pid_lint = fork()) == 0) {
 2754 
 2755         /* Child continues here (i.e. the future linting process). */
 2756         close(lint_fd[0]);
 2757 
 2758         /* Send the linter's standard output + err to the pipe. */
 2759         if (dup2(lint_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
 2760             exit(9);
 2761         if (dup2(lint_fd[1], STDERR_FILENO) != STDERR_FILENO)
 2762             exit(9);
 2763 
 2764         close(lint_fd[1]);
 2765 
 2766         /* Start the linter program; we are using $PATH. */
 2767         execvp(lintargs[0], lintargs);
 2768 
 2769         /* This is only reached when the linter is not found. */
 2770         exit(9);
 2771     }
 2772 
 2773     /* Parent continues here. */
 2774     close(lint_fd[1]);
 2775 
 2776     /* If the child process was not forked successfully... */
 2777     if (pid_lint < 0) {
 2778         close(lint_fd[0]);
 2779         statusline(ALERT, _("Could not fork"));
 2780         return;
 2781     }
 2782 
 2783     /* Get the system pipe buffer size. */
 2784     pipesize = fpathconf(lint_fd[0], _PC_PIPE_BUF);
 2785 
 2786     if (pipesize < 1) {
 2787         close(lint_fd[0]);
 2788         statusline(ALERT, _("Could not get size of pipe buffer"));
 2789         return;
 2790     }
 2791 
 2792     /* Read in the returned syntax errors. */
 2793     totalread = 0;
 2794     buffersize = pipesize + 1;
 2795     lintings = charalloc(buffersize);
 2796     pointer = lintings;
 2797 
 2798     while ((bytesread = read(lint_fd[0], pointer, pipesize)) > 0) {
 2799         totalread += bytesread;
 2800         buffersize += pipesize;
 2801         lintings = charealloc(lintings, buffersize);
 2802         pointer = lintings + totalread;
 2803     }
 2804 
 2805     *pointer = '\0';
 2806     close(lint_fd[0]);
 2807 
 2808     /* Process the linter output. */
 2809     pointer = lintings;
 2810     onelint = lintings;
 2811 
 2812     while (*pointer != '\0') {
 2813         if ((*pointer == '\r') || (*pointer == '\n')) {
 2814             *pointer = '\0';
 2815             if (onelint != pointer) {
 2816                 char *filename = NULL, *linestr = NULL, *maybecol = NULL;
 2817                 char *message = mallocstrcpy(NULL, onelint);
 2818 
 2819                 /* At the moment we handle the following formats:
 2820                  *
 2821                  * filenameorcategory:line:column:message (e.g. splint)
 2822                  * filenameorcategory:line,column:message (e.g. pylint)
 2823                  * filenameorcategory:line:message        (e.g. pyflakes) */
 2824                 if (strstr(message, ": ") != NULL) {
 2825                     filename = strtok(onelint, ":");
 2826                     if ((linestr = strtok(NULL, ":")) != NULL) {
 2827                         if ((maybecol = strtok(NULL, ":")) != NULL) {
 2828                             ssize_t tmplineno = 0, tmpcolno = 0;
 2829                             char *tmplinecol;
 2830 
 2831                             tmplineno = strtol(linestr, NULL, 10);
 2832                             if (tmplineno <= 0) {
 2833                                 pointer++;
 2834                                 free(message);
 2835                                 continue;
 2836                             }
 2837 
 2838                             tmpcolno = strtol(maybecol, NULL, 10);
 2839                             /* Check if the middle field is in comma format. */
 2840                             if (tmpcolno <= 0) {
 2841                                 strtok(linestr, ",");
 2842                                 if ((tmplinecol = strtok(NULL, ",")) != NULL)
 2843                                     tmpcolno = strtol(tmplinecol, NULL, 10);
 2844                                 else
 2845                                     tmpcolno = 1;
 2846                             }
 2847 
 2848                             /* Nice.  We have a lint message we can use. */
 2849                             parsesuccess++;
 2850                             tmplint = curlint;
 2851                             curlint = nmalloc(sizeof(lintstruct));
 2852                             curlint->next = NULL;
 2853                             curlint->prev = tmplint;
 2854                             if (curlint->prev != NULL)
 2855                                 curlint->prev->next = curlint;
 2856                             curlint->msg = mallocstrcpy(NULL, message);
 2857                             curlint->lineno = tmplineno;
 2858                             curlint->colno = tmpcolno;
 2859                             curlint->filename = mallocstrcpy(NULL, filename);
 2860 
 2861                             if (lints == NULL)
 2862                                 lints = curlint;
 2863                         }
 2864                     }
 2865                 }
 2866                 free(message);
 2867             }
 2868             onelint = pointer + 1;
 2869         }
 2870         pointer++;
 2871     }
 2872 
 2873     free(lintings);
 2874 
 2875     /* Process the end of the linting process. */
 2876     waitpid(pid_lint, &lint_status, 0);
 2877 
 2878     if (!WIFEXITED(lint_status) || WEXITSTATUS(lint_status) > 2) {
 2879         statusbar(invocation_error(openfile->syntax->linter));
 2880         return;
 2881     }
 2882 
 2883     if (parsesuccess == 0) {
 2884         statusline(HUSH, _("Got 0 parsable lines from command: %s"),
 2885                         openfile->syntax->linter);
 2886         return;
 2887     }
 2888 
 2889     if (helpless && LINES > 4) {
 2890         UNSET(NO_HELP);
 2891         window_init();
 2892     }
 2893 
 2894     /* Show that we are in the linter now. */
 2895     titlebar(NULL);
 2896     bottombars(MLINTER);
 2897 
 2898     tmplint = NULL;
 2899     curlint = lints;
 2900 
 2901     while (TRUE) {
 2902         int kbinput;
 2903         functionptrtype func;
 2904         struct stat lintfileinfo;
 2905 
 2906         if (stat(curlint->filename, &lintfileinfo) != -1 &&
 2907                     (openfile->current_stat == NULL ||
 2908                     openfile->current_stat->st_ino != lintfileinfo.st_ino)) {
 2909 #ifdef ENABLE_MULTIBUFFER
 2910             const openfilestruct *started_at = openfile;
 2911 
 2912             openfile = openfile->next;
 2913             while (openfile != started_at && (openfile->current_stat == NULL ||
 2914                         openfile->current_stat->st_ino != lintfileinfo.st_ino))
 2915                 openfile = openfile->next;
 2916 
 2917             if (openfile->current_stat == NULL ||
 2918                         openfile->current_stat->st_ino != lintfileinfo.st_ino) {
 2919                 char *msg = charalloc(1024 + strlen(curlint->filename));
 2920                 int choice;
 2921 
 2922                 sprintf(msg, _("This message is for unopened file %s,"
 2923                             " open it in a new buffer?"), curlint->filename);
 2924                 choice = do_yesno_prompt(FALSE, msg);
 2925                 currmenu = MLINTER;
 2926                 free(msg);
 2927 
 2928                 if (choice == -1) {
 2929                     statusbar(_("Cancelled"));
 2930                     break;
 2931                 } else if (choice == 1) {
 2932                     open_buffer(curlint->filename, TRUE);
 2933                 } else {
 2934 #endif
 2935                     char *dontwantfile = mallocstrcpy(NULL, curlint->filename);
 2936                     lintstruct *restlint = NULL;
 2937 
 2938                     while (curlint != NULL) {
 2939                         if (strcmp(curlint->filename, dontwantfile) == 0) {
 2940                             if (curlint == lints)
 2941                                 lints = curlint->next;
 2942                             else
 2943                                 curlint->prev->next = curlint->next;
 2944                             if (curlint->next != NULL)
 2945                                 curlint->next->prev = curlint->prev;
 2946                             tmplint = curlint;
 2947                             curlint = curlint->next;
 2948                             free(tmplint->msg);
 2949                             free(tmplint->filename);
 2950                             free(tmplint);
 2951                         } else {
 2952                             if (restlint == NULL)
 2953                                 restlint = curlint;
 2954                             curlint = curlint->next;
 2955                         }
 2956                     }
 2957 
 2958                     free(dontwantfile);
 2959 
 2960                     if (restlint == NULL) {
 2961                         statusbar(_("No messages for this file"));
 2962                         break;
 2963                     } else {
 2964                         curlint = restlint;
 2965                         continue;
 2966                     }
 2967 #ifdef ENABLE_MULTIBUFFER
 2968                 }
 2969             }
 2970 #endif
 2971         }
 2972 
 2973         if (tmplint != curlint) {
 2974             goto_line_posx(curlint->lineno, curlint->colno - 1);
 2975             titlebar(NULL);
 2976             adjust_viewport(CENTERING);
 2977 #ifdef ENABLE_LINENUMBERS
 2978             confirm_margin();
 2979 #endif
 2980             edit_refresh();
 2981             statusline(NOTICE, curlint->msg);
 2982             bottombars(MLINTER);
 2983         }
 2984 
 2985         /* Place the cursor to indicate the affected line. */
 2986         place_the_cursor();
 2987         wnoutrefresh(edit);
 2988 
 2989         kbinput = get_kbinput(bottomwin, VISIBLE);
 2990 
 2991 #ifndef NANO_TINY
 2992         if (kbinput == KEY_WINCH)
 2993             continue;
 2994 #endif
 2995         func = func_from_key(&kbinput);
 2996         tmplint = curlint;
 2997 
 2998         if (func == do_cancel || func == do_enter) {
 2999             wipe_statusbar();
 3000             break;
 3001         } else if (func == do_help_void) {
 3002             tmplint = NULL;
 3003             do_help_void();
 3004         } else if (func == do_page_up || func == do_prev_block) {
 3005             if (curlint->prev != NULL)
 3006                 curlint = curlint->prev;
 3007             else if (last_wait != time(NULL)) {
 3008                 statusbar(_("At first message"));
 3009                 napms(600);
 3010                 last_wait = time(NULL);
 3011                 statusline(NOTICE, curlint->msg);
 3012             }
 3013         } else if (func == do_page_down || func == do_next_block) {
 3014             if (curlint->next != NULL)
 3015                 curlint = curlint->next;
 3016             else if (last_wait != time(NULL)) {
 3017                 statusbar(_("At last message"));
 3018                 napms(600);
 3019                 last_wait = time(NULL);
 3020                 statusline(NOTICE, curlint->msg);
 3021             }
 3022         }
 3023     }
 3024 
 3025     for (curlint = lints; curlint != NULL;) {
 3026         tmplint = curlint;
 3027         curlint = curlint->next;
 3028         free(tmplint->msg);
 3029         free(tmplint->filename);
 3030         free(tmplint);
 3031     }
 3032 
 3033     if (helpless) {
 3034         SET(NO_HELP);
 3035         window_init();
 3036         refresh_needed = TRUE;
 3037     }
 3038 
 3039     currmenu = MMOST;
 3040     titlebar(NULL);
 3041 }
 3042 #endif /* ENABLE_COLOR */
 3043 
 3044 #ifndef NANO_TINY
 3045 /* Our own version of "wc".  Note that its character counts are in
 3046  * multibyte characters instead of single-byte characters. */
 3047 void do_wordlinechar_count(void)
 3048 {
 3049     linestruct *was_current = openfile->current;
 3050     size_t was_x = openfile->current_x;
 3051     size_t words = 0, chars = 0;
 3052     ssize_t lines = 0;
 3053     linestruct *top, *bot;
 3054     size_t top_x, bot_x;
 3055 
 3056     /* If the mark is on, partition the buffer so that it
 3057      * contains only the marked text, and turn the mark off. */
 3058     if (openfile->mark) {
 3059         get_region((const linestruct **)&top, &top_x,
 3060                     (const linestruct **)&bot, &bot_x, NULL);
 3061         partition_buffer(top, top_x, bot, bot_x);
 3062     }
 3063 
 3064     /* Start at the top of the file. */
 3065     openfile->current = openfile->filetop;
 3066     openfile->current_x = 0;
 3067 
 3068     /* Keep moving to the next word (counting punctuation characters as
 3069      * part of a word, as "wc -w" does), without updating the screen,
 3070      * until we reach the end of the file, incrementing the total word
 3071      * count whenever we're on a word just before moving. */
 3072     while (openfile->current != openfile->filebot ||
 3073         openfile->current->data[openfile->current_x] != '\0') {
 3074         if (do_next_word(FALSE, TRUE))
 3075             words++;
 3076     }
 3077 
 3078     /* Get the number of lines, similar to what "wc -l" gives. */
 3079     lines = openfile->filebot->lineno - openfile->filetop->lineno +
 3080                     ((openfile->filebot->data[0] == '\0') ? 0 : 1);
 3081 
 3082     /* Get the number of multibyte characters, similar to "wc -c". */
 3083     if (openfile->mark) {
 3084         chars = get_totsize(openfile->filetop, openfile->filebot);
 3085         unpartition_buffer();
 3086     } else
 3087         chars = openfile->totsize;
 3088 
 3089     /* Restore where we were. */
 3090     openfile->current = was_current;
 3091     openfile->current_x = was_x;
 3092 
 3093     /* Display the total word, line, and character counts on the statusbar. */
 3094     statusline(HUSH, _("%sWords: %zu  Lines: %zd  Chars: %zu"), openfile->mark ?
 3095                         _("In Selection:  ") : "", words, lines, chars);
 3096 }
 3097 #endif /* !NANO_TINY */
 3098 
 3099 /* Get verbatim input. */
 3100 void do_verbatim_input(void)
 3101 {
 3102     int *kbinput;
 3103     size_t kbinput_len, i;
 3104     char *output;
 3105 
 3106     /* TRANSLATORS: This is displayed when the next keystroke will be
 3107      * inserted verbatim. */
 3108     statusbar(_("Verbatim Input"));
 3109     place_the_cursor();
 3110 
 3111     /* Read in all the verbatim characters. */
 3112     kbinput = get_verbatim_kbinput(edit, &kbinput_len);
 3113 
 3114     /* Unsuppress cursor-position display or blank the statusbar. */
 3115     if (ISSET(CONSTANT_SHOW))
 3116         suppress_cursorpos = FALSE;
 3117     else
 3118         wipe_statusbar();
 3119 
 3120     /* Display all the verbatim characters at once, not filtering out
 3121      * control characters. */
 3122     output = charalloc(kbinput_len + 1);
 3123 
 3124     for (i = 0; i < kbinput_len; i++)
 3125         output[i] = (char)kbinput[i];
 3126     output[i] = '\0';
 3127 
 3128     free(kbinput);
 3129 
 3130     do_output(output, kbinput_len, TRUE);
 3131 
 3132     free(output);
 3133 }
 3134 
 3135 #ifdef ENABLE_WORDCOMPLETION
 3136 /* Return a copy of the found completion candidate. */
 3137 char *copy_completion(char *text)
 3138 {
 3139     char *word;
 3140     size_t length = 0, index = 0;
 3141 
 3142     /* Find the end of the candidate word to get its length. */
 3143     while (is_word_mbchar(&text[length], FALSE))
 3144         length = step_right(text, length);
 3145 
 3146     /* Now copy this candidate to a new string. */
 3147     word = charalloc(length + 1);
 3148     while (index < length)
 3149         word[index++] = *(text++);
 3150     word[index] = '\0';
 3151 
 3152     return word;
 3153 }
 3154 
 3155 /* Look at the fragment the user has typed, then search the current buffer for
 3156  * the first word that starts with this fragment, and tentatively complete the
 3157  * fragment.  If the user types 'Complete' again, search and paste the next
 3158  * possible completion. */
 3159 void complete_a_word(void)
 3160 {
 3161     char *shard, *completion = NULL;
 3162     size_t start_of_shard, shard_length = 0;
 3163     size_t i = 0, j = 0;
 3164     completion_word *some_word;
 3165 #ifdef ENABLE_WRAPPING
 3166     bool was_set_wrapping = ISSET(BREAK_LONG_LINES);
 3167 #endif
 3168 
 3169     /* If this is a fresh completion attempt... */
 3170     if (pletion_line == NULL) {
 3171         /* Clear the list of words of a previous completion run. */
 3172         while (list_of_completions != NULL) {
 3173             completion_word *dropit = list_of_completions;
 3174             list_of_completions = list_of_completions->next;
 3175             free(dropit->word);
 3176             free(dropit);
 3177         }
 3178 
 3179         /* Prevent a completion from being merged with typed text. */
 3180         openfile->last_action = OTHER;
 3181 
 3182         /* Initialize the starting point for searching. */
 3183         pletion_line = openfile->filetop;
 3184         pletion_x = 0;
 3185 
 3186         /* Wipe the "No further matches" message. */
 3187         wipe_statusbar();
 3188     } else {
 3189         /* Remove the attempted completion from the buffer. */
 3190         do_undo();
 3191     }
 3192 
 3193     /* Find the start of the fragment that the user typed. */
 3194     start_of_shard = openfile->current_x;
 3195     while (start_of_shard > 0) {
 3196         size_t oneleft = step_left(openfile->current->data, start_of_shard);
 3197 
 3198         if (!is_word_mbchar(&openfile->current->data[oneleft], FALSE))
 3199             break;
 3200         start_of_shard = oneleft;
 3201     }
 3202 
 3203     /* If there is no word fragment before the cursor, do nothing. */
 3204     if (start_of_shard == openfile->current_x) {
 3205         /* TRANSLATORS: Shown when no text is directly left of the cursor. */
 3206         statusbar(_("No word fragment"));
 3207         pletion_line = NULL;
 3208         return;
 3209     }
 3210 
 3211     shard = charalloc(openfile->current_x - start_of_shard + 1);
 3212 
 3213     /* Copy the fragment that has to be searched for. */
 3214     while (start_of_shard < openfile->current_x)
 3215         shard[shard_length++] = openfile->current->data[start_of_shard++];
 3216     shard[shard_length] = '\0';
 3217 
 3218     /* Run through all of the lines in the buffer, looking for shard. */
 3219     while (pletion_line != NULL) {
 3220         ssize_t threshold = strlen(pletion_line->data) - shard_length - 1;
 3221                 /* The point where we can stop searching for shard. */
 3222 
 3223         /* Traverse the whole line, looking for shard. */
 3224         for (i = pletion_x; (ssize_t)i < threshold; i++) {
 3225             /* If the first byte doesn't match, run on. */
 3226             if (pletion_line->data[i] != shard[0])
 3227                 continue;
 3228 
 3229             /* Compare the rest of the bytes in shard. */
 3230             for (j = 1; j < shard_length; j++)
 3231                 if (pletion_line->data[i + j] != shard[j])
 3232                     break;
 3233 
 3234             /* If not all of the bytes matched, continue searching. */
 3235             if (j < shard_length)
 3236                 continue;
 3237 
 3238             /* If the found match is not /longer/ than shard, skip it. */
 3239             if (!is_word_mbchar(&pletion_line->data[i + j], FALSE))
 3240                 continue;
 3241 
 3242             /* If the match is not a separate word, skip it. */
 3243             if (i > 0 && is_word_mbchar(&pletion_line->data[
 3244                                 step_left(pletion_line->data, i)], FALSE))
 3245                 continue;
 3246 
 3247             /* If this match is the shard itself, ignore it. */
 3248             if (pletion_line == openfile->current &&
 3249                         i == openfile->current_x - shard_length)
 3250                 continue;
 3251 
 3252             completion = copy_completion(pletion_line->data + i);
 3253 
 3254             /* Look among earlier attempted completions for a duplicate. */
 3255             some_word = list_of_completions;
 3256             while (some_word && strcmp(some_word->word, completion) != 0)
 3257                 some_word = some_word->next;
 3258 
 3259             /* If we've already tried this word, skip it. */
 3260             if (some_word != NULL) {
 3261                 free(completion);
 3262                 continue;
 3263             }
 3264 
 3265             /* Add the found word to the list of completions. */
 3266             some_word = (completion_word *)nmalloc(sizeof(completion_word));
 3267             some_word->word = completion;
 3268             some_word->next = list_of_completions;
 3269             list_of_completions = some_word;
 3270 
 3271 #ifdef ENABLE_WRAPPING
 3272             /* Temporarily disable wrapping so only one undo item is added. */
 3273             UNSET(BREAK_LONG_LINES);
 3274 #endif
 3275             /* Inject the completion into the buffer. */
 3276             do_output(&completion[shard_length],
 3277                         strlen(completion) - shard_length, FALSE);
 3278 #ifdef ENABLE_WRAPPING
 3279             /* If needed, reenable wrapping and wrap the current line. */
 3280             if (was_set_wrapping) {
 3281                 SET(BREAK_LONG_LINES);
 3282                 do_wrap();
 3283             }
 3284 #endif
 3285             /* Set the position for a possible next search attempt. */
 3286             pletion_x = ++i;
 3287 
 3288             free(shard);
 3289             return;
 3290         }
 3291 
 3292         pletion_line = pletion_line->next;
 3293         pletion_x = 0;
 3294     }
 3295 
 3296     /* The search has reached the end of the file. */
 3297     if (list_of_completions != NULL) {
 3298         statusline(ALERT, _("No further matches"));
 3299         refresh_needed = TRUE;
 3300     } else
 3301         /* TRANSLATORS: Shown when there are zero possible completions. */
 3302         statusline(ALERT, _("No matches"));
 3303 
 3304     free(shard);
 3305 }
 3306 #endif /* ENABLE_WORDCOMPLETION */