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