1/* $Id: text.c,v 1.163.2.1 2007/04/22 15:04:05 dolorous Exp $ */ 2/************************************************************************** 3 * text.c * 4 * * 5 * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 Chris Allegretta * 6 * Copyright (C) 2005, 2006, 2007 David Lawrence Ramsey * 7 * This program is free software; you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published by * 9 * the Free Software Foundation; either version 2, or (at your option) * 10 * any later version. * 11 * * 12 * This program is distributed in the hope that it will be useful, but * 13 * WITHOUT ANY WARRANTY; without even the implied warranty of * 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 15 * General Public License for more details. * 16 * * 17 * You should have received a copy of the GNU General Public License * 18 * along with this program; if not, write to the Free Software * 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 20 * 02110-1301, USA. * 21 * * 22 **************************************************************************/ 23 24#include "proto.h" 25 26#include <stdio.h> 27#include <signal.h> 28#include <unistd.h> 29#include <string.h> 30#include <fcntl.h> 31#include <sys/wait.h> 32#include <errno.h> 33 34#ifndef NANO_TINY 35static pid_t pid = -1; 36 /* The PID of the forked process in execute_command(), for use 37 * with the cancel_command() signal handler. */ 38#endif 39#ifndef DISABLE_WRAPPING 40static bool prepend_wrap = FALSE; 41 /* Should we prepend wrapped text to the next line? */ 42#endif 43#ifndef DISABLE_JUSTIFY 44static filestruct *jusbottom = NULL; 45 /* Pointer to the end of the justify buffer. */ 46#endif 47 48#ifndef NANO_TINY 49/* Toggle the mark. */ 50void do_mark(void) 51{ 52 openfile->mark_set = !openfile->mark_set; 53 if (openfile->mark_set) { 54 statusbar(_("Mark Set")); 55 openfile->mark_begin = openfile->current; 56 openfile->mark_begin_x = openfile->current_x; 57 } else { 58 statusbar(_("Mark Unset")); 59 openfile->mark_begin = NULL; 60 openfile->mark_begin_x = 0; 61 edit_refresh(); 62 } 63} 64#endif /* !NANO_TINY */ 65 66/* Delete the character under the cursor. */ 67void do_delete(void) 68{ 69 bool do_refresh = FALSE; 70 /* Do we have to call edit_refresh(), or can we get away with 71 * just update_line()? */ 72 73 assert(openfile->current != NULL && openfile->current->data != NULL && openfile->current_x <= strlen(openfile->current->data)); 74 75 openfile->placewewant = xplustabs(); 76 77 if (openfile->current->data[openfile->current_x] != '\0') { 78 int char_buf_len = parse_mbchar(openfile->current->data + 79 openfile->current_x, NULL, NULL); 80 size_t line_len = strlen(openfile->current->data + 81 openfile->current_x); 82 83 assert(openfile->current_x < strlen(openfile->current->data)); 84 85 /* Let's get dangerous. */ 86 charmove(&openfile->current->data[openfile->current_x], 87 &openfile->current->data[openfile->current_x + 88 char_buf_len], line_len - char_buf_len + 1); 89 90 null_at(&openfile->current->data, openfile->current_x + 91 line_len - char_buf_len); 92#ifndef NANO_TINY 93 if (openfile->mark_set && openfile->mark_begin == 94 openfile->current && openfile->current_x < 95 openfile->mark_begin_x) 96 openfile->mark_begin_x -= char_buf_len; 97#endif 98 openfile->totsize--; 99 } else if (openfile->current != openfile->filebot) { 100 filestruct *foo = openfile->current->next; 101 102 assert(openfile->current_x == strlen(openfile->current->data)); 103 104 /* If we're deleting at the end of a line, we need to call 105 * edit_refresh(). */ 106 if (openfile->current->data[openfile->current_x] == '\0') 107 do_refresh = TRUE; 108 109 openfile->current->data = charealloc(openfile->current->data, 110 openfile->current_x + strlen(foo->data) + 1); 111 strcpy(openfile->current->data + openfile->current_x, 112 foo->data); 113#ifndef NANO_TINY 114 if (openfile->mark_set && openfile->mark_begin == 115 openfile->current->next) { 116 openfile->mark_begin = openfile->current; 117 openfile->mark_begin_x += openfile->current_x; 118 } 119#endif 120 if (openfile->filebot == foo) 121 openfile->filebot = openfile->current; 122 123 unlink_node(foo); 124 delete_node(foo); 125 renumber(openfile->current); 126 openfile->totsize--; 127 128 /* If the NO_NEWLINES flag isn't set, and text has been added to 129 * the magicline as a result of deleting at the end of the line 130 * before filebot, add a new magicline. */ 131 if (!ISSET(NO_NEWLINES) && openfile->current == 132 openfile->filebot && openfile->current->data[0] != '\0') 133 new_magicline(); 134 } else 135 return; 136 137 set_modified(); 138 139#ifdef ENABLE_COLOR 140 /* If color syntaxes are available and turned on, we need to call 141 * edit_refresh(). */ 142 if (openfile->colorstrings != NULL && !ISSET(NO_COLOR_SYNTAX)) 143 do_refresh = TRUE; 144#endif 145 146 if (do_refresh) 147 edit_refresh(); 148 else 149 update_line(openfile->current, openfile->current_x); 150} 151 152/* Backspace over one character. That is, move the cursor left one 153 * character, and then delete the character under the cursor. */ 154void do_backspace(void) 155{ 156 if (openfile->current != openfile->fileage || 157 openfile->current_x > 0) { 158 do_left(); 159 do_delete(); 160 } 161} 162 163/* Insert a tab. If the TABS_TO_SPACES flag is set, insert the number 164 * of spaces that a tab would normally take up. */ 165void do_tab(void) 166{ 167#ifndef NANO_TINY 168 if (ISSET(TABS_TO_SPACES)) { 169 char *output; 170 size_t output_len = 0, new_pww = xplustabs(); 171 172 do { 173 new_pww++; 174 output_len++; 175 } while (new_pww % tabsize != 0); 176 177 output = charalloc(output_len + 1); 178 179 charset(output, ' ', output_len); 180 output[output_len] = '\0'; 181 182 do_output(output, output_len, TRUE); 183 184 free(output); 185 } else { 186#endif 187 do_output("\t", 1, TRUE); 188#ifndef NANO_TINY 189 } 190#endif 191} 192 193#ifndef NANO_TINY 194/* Indent or unindent the current line (or, if the mark is on, all lines 195 * covered by the mark) len columns, depending on whether len is 196 * positive or negative. If the TABS_TO_SPACES flag is set, indent or 197 * unindent by len spaces. Otherwise, indent or unindent by (len / 198 * tabsize) tabs and (len % tabsize) spaces. */ 199void do_indent(ssize_t cols) 200{ 201 bool indent_changed = FALSE; 202 /* Whether any indenting or unindenting was done. */ 203 bool unindent = FALSE; 204 /* Whether we're unindenting text. */ 205 char *line_indent = NULL; 206 /* The text added to each line in order to indent it. */ 207 size_t line_indent_len = 0; 208 /* The length of the text added to each line in order to indent 209 * it. */ 210 filestruct *top, *bot, *f; 211 size_t top_x, bot_x; 212 213 assert(openfile->current != NULL && openfile->current->data != NULL); 214 215 /* If cols is zero, get out. */ 216 if (cols == 0) 217 return; 218 219 /* If cols is negative, make it positive and set unindent to 220 * TRUE. */ 221 if (cols < 0) { 222 cols = -cols; 223 unindent = TRUE; 224 /* Otherwise, we're indenting, in which case the file will always be 225 * modified, so set indent_changed to TRUE. */ 226 } else 227 indent_changed = TRUE; 228 229 /* If the mark is on, use all lines covered by the mark. */ 230 if (openfile->mark_set) 231 mark_order((const filestruct **)&top, &top_x, 232 (const filestruct **)&bot, &bot_x, NULL); 233 /* Otherwise, use the current line. */ 234 else { 235 top = openfile->current; 236 bot = top; 237 } 238 239 if (!unindent) { 240 /* Set up the text we'll be using as indentation. */ 241 line_indent = charalloc(cols + 1); 242 243 if (ISSET(TABS_TO_SPACES)) { 244 /* Set the indentation to cols spaces. */ 245 charset(line_indent, ' ', cols); 246 line_indent_len = cols; 247 } else { 248 /* Set the indentation to (cols / tabsize) tabs and (cols % 249 * tabsize) spaces. */ 250 size_t num_tabs = cols / tabsize; 251 size_t num_spaces = cols % tabsize; 252 253 charset(line_indent, '\t', num_tabs); 254 charset(line_indent + num_tabs, ' ', num_spaces); 255 256 line_indent_len = num_tabs + num_spaces; 257 } 258 259 line_indent[line_indent_len] = '\0'; 260 } 261 262 /* Go through each line of the text. */ 263 for (f = top; f != bot->next; f = f->next) { 264 size_t line_len = strlen(f->data); 265 size_t indent_len = indent_length(f->data); 266 267 if (!unindent) { 268 /* If we're indenting, add the characters in line_indent to 269 * the beginning of the non-whitespace text of this line. */ 270 f->data = charealloc(f->data, line_len + 271 line_indent_len + 1); 272 charmove(&f->data[indent_len + line_indent_len], 273 &f->data[indent_len], line_len - indent_len + 1); 274 strncpy(f->data + indent_len, line_indent, line_indent_len); 275 openfile->totsize += line_indent_len; 276 277 /* Keep track of the change in the current line. */ 278 if (openfile->mark_set && f == openfile->mark_begin && 279 openfile->mark_begin_x >= indent_len) 280 openfile->mark_begin_x += line_indent_len; 281 282 if (f == openfile->current && openfile->current_x >= 283 indent_len) 284 openfile->current_x += line_indent_len; 285 286 /* If the NO_NEWLINES flag isn't set, and this is the 287 * magicline, add a new magicline. */ 288 if (!ISSET(NO_NEWLINES) && f == openfile->filebot) 289 new_magicline(); 290 } else { 291 size_t indent_col = strnlenpt(f->data, indent_len); 292 /* The length in columns of the indentation on this 293 * line. */ 294 295 if (cols <= indent_col) { 296 size_t indent_new = actual_x(f->data, indent_col - 297 cols); 298 /* The length of the indentation remaining on 299 * this line after we unindent. */ 300 size_t indent_shift = indent_len - indent_new; 301 /* The change in the indentation on this line 302 * after we unindent. */ 303 304 /* If we're unindenting, and there's at least cols 305 * columns' worth of indentation at the beginning of the 306 * non-whitespace text of this line, remove it. */ 307 charmove(&f->data[indent_new], &f->data[indent_len], 308 line_len - indent_shift - indent_new + 1); 309 null_at(&f->data, line_len - indent_shift + 1); 310 openfile->totsize -= indent_shift; 311 312 /* Keep track of the change in the current line. */ 313 if (openfile->mark_set && f == openfile->mark_begin && 314 openfile->mark_begin_x > indent_new) { 315 if (openfile->mark_begin_x <= indent_len) 316 openfile->mark_begin_x = indent_new; 317 else 318 openfile->mark_begin_x -= indent_shift; 319 } 320 321 if (f == openfile->current && openfile->current_x > 322 indent_new) { 323 if (openfile->current_x <= indent_len) 324 openfile->current_x = indent_new; 325 else 326 openfile->current_x -= indent_shift; 327 } 328 329 /* We've unindented, so set indent_changed to TRUE. */ 330 if (!indent_changed) 331 indent_changed = TRUE; 332 } 333 } 334 } 335 336 if (!unindent) 337 /* Clean up. */ 338 free(line_indent); 339 340 if (indent_changed) { 341 /* Mark the file as modified. */ 342 set_modified(); 343 344 /* Update the screen. */ 345 edit_refresh(); 346 } 347} 348 349/* Indent the current line, or all lines covered by the mark if the mark 350 * is on, tabsize columns. */ 351void do_indent_void(void) 352{ 353 do_indent(tabsize); 354} 355 356/* Unindent the current line, or all lines covered by the mark if the 357 * mark is on, tabsize columns. */ 358void do_unindent(void) 359{ 360 do_indent(-tabsize); 361} 362#endif /* !NANO_TINY */ 363 364/* Someone hits Enter *gasp!* */ 365void do_enter(void) 366{ 367 filestruct *newnode = make_new_node(openfile->current); 368 size_t extra = 0; 369 370 assert(openfile->current != NULL && openfile->current->data != NULL); 371 372#ifndef NANO_TINY 373 /* Do auto-indenting, like the neolithic Turbo Pascal editor. */ 374 if (ISSET(AUTOINDENT)) { 375 /* If we are breaking the line in the indentation, the new 376 * indentation should have only current_x characters, and 377 * current_x should not change. */ 378 extra = indent_length(openfile->current->data); 379 if (extra > openfile->current_x) 380 extra = openfile->current_x; 381 } 382#endif 383 newnode->data = charalloc(strlen(openfile->current->data + 384 openfile->current_x) + extra + 1); 385 strcpy(&newnode->data[extra], openfile->current->data + 386 openfile->current_x); 387#ifndef NANO_TINY 388 if (ISSET(AUTOINDENT)) { 389 strncpy(newnode->data, openfile->current->data, extra); 390 openfile->totsize += mbstrlen(newnode->data); 391 } 392#endif 393 null_at(&openfile->current->data, openfile->current_x); 394#ifndef NANO_TINY 395 if (openfile->mark_set && openfile->current == 396 openfile->mark_begin && openfile->current_x < 397 openfile->mark_begin_x) { 398 openfile->mark_begin = newnode; 399 openfile->mark_begin_x += extra - openfile->current_x; 400 } 401#endif 402 openfile->current_x = extra; 403 404 if (openfile->current == openfile->filebot) 405 openfile->filebot = newnode; 406 splice_node(openfile->current, newnode, 407 openfile->current->next); 408 409 renumber(openfile->current); 410 openfile->current = newnode; 411 412 openfile->totsize++; 413 set_modified(); 414 415 openfile->placewewant = xplustabs(); 416 417 edit_refresh(); 418} 419 420#ifndef NANO_TINY 421/* Send a SIGKILL (unconditional kill) to the forked process in 422 * execute_command(). */ 423RETSIGTYPE cancel_command(int signal) 424{ 425 if (kill(pid, SIGKILL) == -1) 426 nperror("kill"); 427} 428 429/* Execute command in a shell. Return TRUE on success. */ 430bool execute_command(const char *command) 431{ 432 int fd[2]; 433 FILE *f; 434 char *shellenv; 435 struct sigaction oldaction, newaction; 436 /* Original and temporary handlers for SIGINT. */ 437 bool sig_failed = FALSE; 438 /* Did sigaction() fail without changing the signal handlers? */ 439 440 /* Make our pipes. */ 441 if (pipe(fd) == -1) { 442 statusbar(_("Could not pipe")); 443 return FALSE; 444 } 445 446 /* Check $SHELL for the shell to use. If it isn't set, use 447 * /bin/sh. Note that $SHELL should contain only a path, with no 448 * arguments. */ 449 shellenv = getenv("SHELL"); 450 if (shellenv == NULL) 451 shellenv = "/bin/sh"; 452 453 /* Fork a child. */ 454 if ((pid = fork()) == 0) { 455 close(fd[0]); 456 dup2(fd[1], fileno(stdout)); 457 dup2(fd[1], fileno(stderr)); 458 459 /* If execl() returns at all, there was an error. */ 460 execl(shellenv, tail(shellenv), "-c", command, NULL); 461 exit(0); 462 } 463 464 /* Continue as parent. */ 465 close(fd[1]); 466 467 if (pid == -1) { 468 close(fd[0]); 469 statusbar(_("Could not fork")); 470 return FALSE; 471 } 472 473 /* Before we start reading the forked command's output, we set 474 * things up so that Ctrl-C will cancel the new process. */ 475 476 /* Enable interpretation of the special control keys so that we get 477 * SIGINT when Ctrl-C is pressed. */ 478 enable_signals(); 479 480 if (sigaction(SIGINT, NULL, &newaction) == -1) { 481 sig_failed = TRUE; 482 nperror("sigaction"); 483 } else { 484 newaction.sa_handler = cancel_command; 485 if (sigaction(SIGINT, &newaction, &oldaction) == -1) { 486 sig_failed = TRUE; 487 nperror("sigaction"); 488 } 489 } 490 491 /* Note that now oldaction is the previous SIGINT signal handler, 492 * to be restored later. */ 493 494 f = fdopen(fd[0], "rb"); 495 if (f == NULL) 496 nperror("fdopen"); 497 498 read_file(f, "stdin"); 499 500 if (wait(NULL) == -1) 501 nperror("wait"); 502 503 if (!sig_failed && sigaction(SIGINT, &oldaction, NULL) == -1) 504 nperror("sigaction"); 505 506 /* Disable interpretation of the special control keys so that we can 507 * use Ctrl-C for other things. */ 508 disable_signals(); 509 510 return TRUE; 511} 512#endif /* !NANO_TINY */ 513 514#ifndef DISABLE_WRAPPING 515/* Unset the prepend_wrap flag. We need to do this as soon as we do 516 * something other than type text. */ 517void wrap_reset(void) 518{ 519 prepend_wrap = FALSE; 520} 521 522/* We wrap the given line. Precondition: we assume the cursor has been 523 * moved forward since the last typed character. Return TRUE if we 524 * wrapped, and FALSE otherwise. */ 525bool do_wrap(filestruct *line) 526{ 527 size_t line_len; 528 /* The length of the line we wrap. */ 529 ssize_t wrap_loc; 530 /* The index of line->data where we wrap. */ 531#ifndef NANO_TINY 532 const char *indent_string = NULL; 533 /* Indentation to prepend to the new line. */ 534 size_t indent_len = 0; 535 /* The length of indent_string. */ 536#endif 537 const char *after_break; 538 /* The text after the wrap point. */ 539 size_t after_break_len; 540 /* The length of after_break. */ 541 bool prepending = FALSE; 542 /* Do we prepend to the next line? */ 543 const char *next_line = NULL; 544 /* The next line, minus indentation. */ 545 size_t next_line_len = 0; 546 /* The length of next_line. */ 547 char *new_line = NULL; 548 /* The line we create. */ 549 size_t new_line_len = 0; 550 /* The eventual length of new_line. */ 551 552 /* There are three steps. First, we decide where to wrap. Then, we 553 * create the new wrap line. Finally, we clean up. */ 554 555 /* Step 1, finding where to wrap. We are going to add a new line 556 * after a blank character. In this step, we call break_line() to 557 * get the location of the last blank we can break the line at, and 558 * set wrap_loc to the location of the character after it, so that 559 * the blank is preserved at the end of the line. 560 * 561 * If there is no legal wrap point, or we reach the last character 562 * of the line while trying to find one, we should return without 563 * wrapping. Note that if autoindent is turned on, we don't break 564 * at the end of it! */ 565 assert(line != NULL && line->data != NULL); 566 567 /* Save the length of the line. */ 568 line_len = strlen(line->data); 569 570 /* Find the last blank where we can break the line. */ 571 wrap_loc = break_line(line->data, fill 572#ifndef DISABLE_HELP 573 , FALSE 574#endif 575 ); 576 577 /* If we couldn't break the line, or we've reached the end of it, we 578 * don't wrap. */ 579 if (wrap_loc == -1 || line->data[wrap_loc] == '\0') 580 return FALSE; 581 582 /* Otherwise, move forward to the character just after the blank. */ 583 wrap_loc += move_mbright(line->data + wrap_loc, 0); 584 585 /* If we've reached the end of the line, we don't wrap. */ 586 if (line->data[wrap_loc] == '\0') 587 return FALSE; 588 589#ifndef NANO_TINY 590 /* If autoindent is turned on, and we're on the character just after 591 * the indentation, we don't wrap. */ 592 if (ISSET(AUTOINDENT)) { 593 /* Get the indentation of this line. */ 594 indent_string = line->data; 595 indent_len = indent_length(indent_string); 596 597 if (wrap_loc == indent_len) 598 return FALSE; 599 } 600#endif 601 602 /* Step 2, making the new wrap line. It will consist of indentation 603 * followed by the text after the wrap point, optionally followed by 604 * a space (if the text after the wrap point doesn't end in a blank) 605 * and the text of the next line, if they can fit without wrapping, 606 * the next line exists, and the prepend_wrap flag is set. */ 607 608 /* after_break is the text that will be wrapped to the next line. */ 609 after_break = line->data + wrap_loc; 610 after_break_len = line_len - wrap_loc; 611 612 assert(strlen(after_break) == after_break_len); 613 614 /* We prepend the wrapped text to the next line, if the prepend_wrap 615 * flag is set, there is a next line, and prepending would not make 616 * the line too long. */ 617 if (prepend_wrap && line != openfile->filebot) { 618 const char *end = after_break + move_mbleft(after_break, 619 after_break_len); 620 621 /* If after_break doesn't end in a blank, make sure it ends in a 622 * space. */ 623 if (!is_blank_mbchar(end)) { 624 line_len++; 625 line->data = charealloc(line->data, line_len + 1); 626 line->data[line_len - 1] = ' '; 627 line->data[line_len] = '\0'; 628 after_break = line->data + wrap_loc; 629 after_break_len++; 630 openfile->totsize++; 631 } 632 633 next_line = line->next->data; 634 next_line_len = strlen(next_line); 635 636 if (after_break_len + next_line_len <= fill) { 637 prepending = TRUE; 638 new_line_len += next_line_len; 639 } 640 } 641 642 /* new_line_len is now the length of the text that will be wrapped 643 * to the next line, plus (if we're prepending to it) the length of 644 * the text of the next line. */ 645 new_line_len += after_break_len; 646 647#ifndef NANO_TINY 648 if (ISSET(AUTOINDENT)) { 649 if (prepending) { 650 /* If we're prepending, the indentation will come from the 651 * next line. */ 652 indent_string = next_line; 653 indent_len = indent_length(indent_string); 654 next_line += indent_len; 655 } else { 656 /* Otherwise, it will come from this line, in which case 657 * we should increase new_line_len to make room for it. */ 658 new_line_len += indent_len; 659 openfile->totsize += mbstrnlen(indent_string, indent_len); 660 } 661 } 662#endif 663 664 /* Now we allocate the new line and copy the text into it. */ 665 new_line = charalloc(new_line_len + 1); 666 new_line[0] = '\0'; 667 668#ifndef NANO_TINY 669 if (ISSET(AUTOINDENT)) { 670 /* Copy the indentation. */ 671 strncpy(new_line, indent_string, indent_len); 672 new_line[indent_len] = '\0'; 673 new_line_len += indent_len; 674 } 675#endif 676 677 /* Copy all the text after the wrap point of the current line. */ 678 strcat(new_line, after_break); 679 680 /* Break the current line at the wrap point. */ 681 null_at(&line->data, wrap_loc); 682 683 if (prepending) { 684 /* If we're prepending, copy the text from the next line, minus 685 * the indentation that we already copied above. */ 686 strcat(new_line, next_line); 687 688 free(line->next->data); 689 line->next->data = new_line; 690 691 /* If the NO_NEWLINES flag isn't set, and text has been added to 692 * the magicline, make a new magicline. */ 693 if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0') 694 new_magicline(); 695 } else { 696 /* Otherwise, make a new line and copy the text after where we 697 * broke this line to the beginning of the new line. */ 698 splice_node(openfile->current, make_new_node(openfile->current), 699 openfile->current->next); 700 701 /* If the current line is the last line of the file, move the 702 * last line of the file down to the next line. */ 703 if (openfile->filebot == openfile->current) 704 openfile->filebot = openfile->current->next; 705 706 openfile->current->next->data = new_line; 707 708 openfile->totsize++; 709 } 710 711 /* Step 3, clean up. Reposition the cursor and mark, and do some 712 * other sundry things. */ 713 714 /* Set the prepend_wrap flag, so that later wraps of this line will 715 * be prepended to the next line. */ 716 prepend_wrap = TRUE; 717 718 /* Each line knows its number. We recalculate these if we inserted 719 * a new line. */ 720 if (!prepending) 721 renumber(line); 722 723 /* If the cursor was after the break point, we must move it. We 724 * also clear the prepend_wrap flag in this case. */ 725 if (openfile->current_x > wrap_loc) { 726 prepend_wrap = FALSE; 727 728 openfile->current = openfile->current->next; 729 openfile->current_x -= wrap_loc 730#ifndef NANO_TINY 731 - indent_len 732#endif 733 ; 734 openfile->placewewant = xplustabs(); 735 } 736 737#ifndef NANO_TINY 738 /* If the mark was on this line after the wrap point, we move it 739 * down. If it was on the next line and we prepended to that line, 740 * we move it right. */ 741 if (openfile->mark_set) { 742 if (openfile->mark_begin == line && openfile->mark_begin_x > 743 wrap_loc) { 744 openfile->mark_begin = line->next; 745 openfile->mark_begin_x -= wrap_loc - indent_len + 1; 746 } else if (prepending && openfile->mark_begin == line->next) 747 openfile->mark_begin_x += after_break_len; 748 } 749#endif 750 751 return TRUE; 752} 753#endif /* !DISABLE_WRAPPING */ 754 755#if !defined(DISABLE_HELP) || !defined(DISABLE_WRAPJUSTIFY) 756/* We are trying to break a chunk off line. We find the last blank such 757 * that the display length to there is at most (goal + 1). If there is 758 * no such blank, then we find the first blank. We then take the last 759 * blank in that group of blanks. The terminating '\0' counts as a 760 * blank, as does a '\n' if newline is TRUE. */ 761ssize_t break_line(const char *line, ssize_t goal 762#ifndef DISABLE_HELP 763 , bool newline 764#endif 765 ) 766{ 767 ssize_t blank_loc = -1; 768 /* Current tentative return value. Index of the last blank we 769 * found with short enough display width. */ 770 ssize_t cur_loc = 0; 771 /* Current index in line. */ 772 size_t cur_pos = 0; 773 /* Current column position in line. */ 774 int line_len; 775 776 assert(line != NULL); 777 778 while (*line != '\0' && goal >= cur_pos) { 779 line_len = parse_mbchar(line, NULL, &cur_pos); 780 781 if (is_blank_mbchar(line) 782#ifndef DISABLE_HELP 783 || (newline && *line == '\n') 784#endif 785 ) { 786 blank_loc = cur_loc; 787 788#ifndef DISABLE_HELP 789 if (newline && *line == '\n') 790 break; 791#endif 792 } 793 794 line += line_len; 795 cur_loc += line_len; 796 } 797 798 if (goal >= cur_pos) 799 /* In fact, the whole line displays shorter than goal. */ 800 return cur_loc; 801 802 if (blank_loc == -1) { 803 /* No blank was found that was short enough. */ 804 bool found_blank = FALSE; 805 ssize_t found_blank_loc = 0; 806 807 while (*line != '\0') { 808 line_len = parse_mbchar(line, NULL, NULL); 809 810 if (is_blank_mbchar(line) 811#ifndef DISABLE_HELP 812 || (newline && *line == '\n') 813#endif 814 ) { 815 if (!found_blank) 816 found_blank = TRUE; 817 found_blank_loc = cur_loc; 818 } else if (found_blank) 819 return found_blank_loc; 820 821 line += line_len; 822 cur_loc += line_len; 823 } 824 825 return -1; 826 } 827 828 /* Move to the last blank after blank_loc, if there is one. */ 829 line -= cur_loc; 830 line += blank_loc; 831 line_len = parse_mbchar(line, NULL, NULL); 832 line += line_len; 833 834 while (*line != '\0' && (is_blank_mbchar(line) 835#ifndef DISABLE_HELP 836 || (newline && *line == '\n') 837#endif 838 )) { 839#ifndef DISABLE_HELP 840 if (newline && *line == '\n') 841 break; 842#endif 843 844 line_len = parse_mbchar(line, NULL, NULL); 845 846 line += line_len; 847 blank_loc += line_len; 848 } 849 850 return blank_loc; 851} 852#endif /* !DISABLE_HELP || !DISABLE_WRAPJUSTIFY */ 853 854#if !defined(NANO_TINY) || !defined(DISABLE_JUSTIFY) 855/* The "indentation" of a line is the whitespace between the quote part 856 * and the non-whitespace of the line. */ 857size_t indent_length(const char *line) 858{ 859 size_t len = 0; 860 char *blank_mb; 861 int blank_mb_len; 862 863 assert(line != NULL); 864 865 blank_mb = charalloc(mb_cur_max()); 866 867 while (*line != '\0') { 868 blank_mb_len = parse_mbchar(line, blank_mb, NULL); 869 870 if (!is_blank_mbchar(blank_mb)) 871 break; 872 873 line += blank_mb_len; 874 len += blank_mb_len; 875 } 876 877 free(blank_mb); 878 879 return len; 880} 881#endif /* !NANO_TINY || !DISABLE_JUSTIFY */ 882 883#ifndef DISABLE_JUSTIFY 884/* justify_format() replaces blanks with spaces and multiple spaces by 1 885 * (except it maintains up to 2 after a character in punct optionally 886 * followed by a character in brackets, and removes all from the end). 887 * 888 * justify_format() might make paragraph->data shorter, and change the 889 * actual pointer with null_at(). 890 * 891 * justify_format() will not look at the first skip characters of 892 * paragraph. skip should be at most strlen(paragraph->data). The 893 * character at paragraph[skip + 1] must not be blank. */ 894void justify_format(filestruct *paragraph, size_t skip) 895{ 896 char *end, *new_end, *new_paragraph_data; 897 size_t shift = 0; 898#ifndef NANO_TINY 899 size_t mark_shift = 0; 900#endif 901 902 /* These four asserts are assumptions about the input data. */ 903 assert(paragraph != NULL); 904 assert(paragraph->data != NULL); 905 assert(skip < strlen(paragraph->data)); 906 assert(!is_blank_mbchar(paragraph->data + skip)); 907 908 end = paragraph->data + skip; 909 new_paragraph_data = charalloc(strlen(paragraph->data) + 1); 910 strncpy(new_paragraph_data, paragraph->data, skip); 911 new_end = new_paragraph_data + skip; 912 913 while (*end != '\0') { 914 int end_len; 915 916 /* If this character is blank, change it to a space if 917 * necessary, and skip over all blanks after it. */ 918 if (is_blank_mbchar(end)) { 919 end_len = parse_mbchar(end, NULL, NULL); 920 921 *new_end = ' '; 922 new_end++; 923 end += end_len; 924 925 while (*end != '\0' && is_blank_mbchar(end)) { 926 end_len = parse_mbchar(end, NULL, NULL); 927 928 end += end_len; 929 shift += end_len; 930 931#ifndef NANO_TINY 932 /* Keep track of the change in the current line. */ 933 if (openfile->mark_set && openfile->mark_begin == 934 paragraph && openfile->mark_begin_x >= end - 935 paragraph->data) 936 mark_shift += end_len; 937#endif 938 } 939 /* If this character is punctuation optionally followed by a 940 * bracket and then followed by blanks, change no more than two 941 * of the blanks to spaces if necessary, and skip over all 942 * blanks after them. */ 943 } else if (mbstrchr(punct, end) != NULL) { 944 end_len = parse_mbchar(end, NULL, NULL); 945 946 while (end_len > 0) { 947 *new_end = *end; 948 new_end++; 949 end++; 950 end_len--; 951 } 952 953 if (*end != '\0' && mbstrchr(brackets, end) != NULL) { 954 end_len = parse_mbchar(end, NULL, NULL); 955 956 while (end_len > 0) { 957 *new_end = *end; 958 new_end++; 959 end++; 960 end_len--; 961 } 962 } 963 964 if (*end != '\0' && is_blank_mbchar(end)) { 965 end_len = parse_mbchar(end, NULL, NULL); 966 967 *new_end = ' '; 968 new_end++; 969 end += end_len; 970 } 971 972 if (*end != '\0' && is_blank_mbchar(end)) { 973 end_len = parse_mbchar(end, NULL, NULL); 974 975 *new_end = ' '; 976 new_end++; 977 end += end_len; 978 } 979 980 while (*end != '\0' && is_blank_mbchar(end)) { 981 end_len = parse_mbchar(end, NULL, NULL); 982 983 end += end_len; 984 shift += end_len; 985 986#ifndef NANO_TINY 987 /* Keep track of the change in the current line. */ 988 if (openfile->mark_set && openfile->mark_begin == 989 paragraph && openfile->mark_begin_x >= end - 990 paragraph->data) 991 mark_shift += end_len; 992#endif 993 } 994 /* If this character is neither blank nor punctuation, leave it 995 * unchanged. */ 996 } else { 997 end_len = parse_mbchar(end, NULL, NULL); 998 999 while (end_len > 0) { 1000 *new_end = *end; 1001 new_end++; 1002 end++; 1003 end_len--; 1004 } 1005 } 1006 } 1007 1008 assert(*end == '\0'); 1009 1010 *new_end = *end; 1011 1012 /* If there are spaces at the end of the line, remove them. */ 1013 while (new_end > new_paragraph_data + skip && 1014 *(new_end - 1) == ' ') { 1015 new_end--; 1016 shift++; 1017 } 1018 1019 if (shift > 0) { 1020 openfile->totsize -= shift; 1021 null_at(&new_paragraph_data, new_end - new_paragraph_data); 1022 free(paragraph->data); 1023 paragraph->data = new_paragraph_data; 1024 1025#ifndef NANO_TINY 1026 /* Adjust the mark coordinates to compensate for the change in 1027 * the current line. */ 1028 if (openfile->mark_set && openfile->mark_begin == paragraph) { 1029 openfile->mark_begin_x -= mark_shift; 1030 if (openfile->mark_begin_x > new_end - new_paragraph_data) 1031 openfile->mark_begin_x = new_end - new_paragraph_data; 1032 } 1033#endif 1034 } else 1035 free(new_paragraph_data); 1036} 1037 1038/* The "quote part" of a line is the largest initial substring matching 1039 * the quote string. This function returns the length of the quote part 1040 * of the given line. 1041 * 1042 * Note that if !HAVE_REGEX_H then we match concatenated copies of 1043 * quotestr. */ 1044size_t quote_length(const char *line) 1045{ 1046#ifdef HAVE_REGEX_H 1047 regmatch_t matches; 1048 int rc = regexec("ereg, line, 1, &matches, 0); 1049 1050 if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1) 1051 return 0; 1052 /* matches.rm_so should be 0, since the quote string should start 1053 * with the caret ^. */ 1054 return matches.rm_eo; 1055#else /* !HAVE_REGEX_H */ 1056 size_t qdepth = 0; 1057 1058 /* Compute quote depth level. */ 1059 while (strncmp(line + qdepth, quotestr, quotelen) == 0) 1060 qdepth += quotelen; 1061 return qdepth; 1062#endif /* !HAVE_REGEX_H */ 1063} 1064 1065/* a_line and b_line are lines of text. The quotation part of a_line is 1066 * the first a_quote characters. Check that the quotation part of 1067 * b_line is the same. */ 1068bool quotes_match(const char *a_line, size_t a_quote, const char 1069 *b_line) 1070{ 1071 /* Here is the assumption about a_quote. */ 1072 assert(a_quote == quote_length(a_line)); 1073 1074 return (a_quote == quote_length(b_line) && 1075 strncmp(a_line, b_line, a_quote) == 0); 1076} 1077 1078/* We assume a_line and b_line have no quote part. Then, we return 1079 * whether b_line could follow a_line in a paragraph. */ 1080bool indents_match(const char *a_line, size_t a_indent, const char 1081 *b_line, size_t b_indent) 1082{ 1083 assert(a_indent == indent_length(a_line)); 1084 assert(b_indent == indent_length(b_line)); 1085 1086 return (b_indent <= a_indent && 1087 strncmp(a_line, b_line, b_indent) == 0); 1088} 1089 1090/* Is foo the beginning of a paragraph? 1091 * 1092 * A line of text consists of a "quote part", followed by an 1093 * "indentation part", followed by text. The functions quote_length() 1094 * and indent_length() calculate these parts. 1095 * 1096 * A line is "part of a paragraph" if it has a part not in the quote 1097 * part or the indentation. 1098 * 1099 * A line is "the beginning of a paragraph" if it is part of a 1100 * paragraph and 1101 * 1) it is the top line of the file, or 1102 * 2) the line above it is not part of a paragraph, or 1103 * 3) the line above it does not have precisely the same quote 1104 * part, or 1105 * 4) the indentation of this line is not an initial substring of 1106 * the indentation of the previous line, or 1107 * 5) this line has no quote part and some indentation, and 1108 * autoindent isn't turned on. 1109 * The reason for number 5) is that if autoindent isn't turned on, 1110 * then an indented line is expected to start a paragraph, as in 1111 * books. Thus, nano can justify an indented paragraph only if 1112 * autoindent is turned on. */ 1113bool begpar(const filestruct *const foo) 1114{ 1115 size_t quote_len, indent_len, temp_id_len; 1116 1117 if (foo == NULL) 1118 return FALSE; 1119 1120 /* Case 1). */ 1121 if (foo == openfile->fileage) 1122 return TRUE; 1123 1124 quote_len = quote_length(foo->data); 1125 indent_len = indent_length(foo->data + quote_len); 1126 1127 /* Not part of a paragraph. */ 1128 if (foo->data[quote_len + indent_len] == '\0') 1129 return FALSE; 1130 1131 /* Case 3). */ 1132 if (!quotes_match(foo->data, quote_len, foo->prev->data)) 1133 return TRUE; 1134 1135 temp_id_len = indent_length(foo->prev->data + quote_len); 1136 1137 /* Case 2) or 5) or 4). */ 1138 if (foo->prev->data[quote_len + temp_id_len] == '\0' || 1139 (quote_len == 0 && indent_len > 0 1140#ifndef NANO_TINY 1141 && !ISSET(AUTOINDENT) 1142#endif 1143 ) || !indents_match(foo->prev->data + quote_len, temp_id_len, 1144 foo->data + quote_len, indent_len)) 1145 return TRUE; 1146 1147 return FALSE; 1148} 1149 1150/* Is foo inside a paragraph? */ 1151bool inpar(const filestruct *const foo) 1152{ 1153 size_t quote_len; 1154 1155 if (foo == NULL) 1156 return FALSE; 1157 1158 quote_len = quote_length(foo->data); 1159 1160 return (foo->data[quote_len + indent_length(foo->data + 1161 quote_len)] != '\0'); 1162} 1163 1164/* Move the next par_len lines, starting with first_line, into the 1165 * justify buffer, leaving copies of those lines in place. Assume that 1166 * par_len is greater than zero, and that there are enough lines after 1167 * first_line. */ 1168void backup_lines(filestruct *first_line, size_t par_len) 1169{ 1170 filestruct *top = first_line; 1171 /* The top of the paragraph we're backing up. */ 1172 filestruct *bot = first_line; 1173 /* The bottom of the paragraph we're backing up. */ 1174 size_t i; 1175 /* Generic loop variable. */ 1176 size_t current_x_save = openfile->current_x; 1177 ssize_t fl_lineno_save = first_line->lineno; 1178 ssize_t edittop_lineno_save = openfile->edittop->lineno; 1179 ssize_t current_lineno_save = openfile->current->lineno; 1180#ifndef NANO_TINY 1181 bool old_mark_set = openfile->mark_set; 1182 ssize_t mb_lineno_save = 0; 1183 size_t mark_begin_x_save = 0; 1184 1185 if (old_mark_set) { 1186 mb_lineno_save = openfile->mark_begin->lineno; 1187 mark_begin_x_save = openfile->mark_begin_x; 1188 } 1189#endif 1190 1191 /* par_len will be one greater than the number of lines between 1192 * current and filebot if filebot is the last line in the 1193 * paragraph. */ 1194 assert(par_len > 0 && openfile->current->lineno + par_len <= 1195 openfile->filebot->lineno + 1); 1196 1197 /* Move bot down par_len lines to the line after the last line of 1198 * the paragraph, if there is one. */ 1199 for (i = par_len; i > 0 && bot != openfile->filebot; i--) 1200 bot = bot->next; 1201 1202 /* Move the paragraph from the current buffer's filestruct to the 1203 * justify buffer. */ 1204 move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot, 1205 (i == 1 && bot == openfile->filebot) ? strlen(bot->data) : 0); 1206 1207 /* Copy the paragraph back to the current buffer's filestruct from 1208 * the justify buffer. */ 1209 copy_from_filestruct(jusbuffer, jusbottom); 1210 1211 /* Move upward from the last line of the paragraph to the first 1212 * line, putting first_line, edittop, current, and mark_begin at the 1213 * same lines in the copied paragraph that they had in the original 1214 * paragraph. */ 1215 if (openfile->current != openfile->fileage) { 1216 top = openfile->current->prev; 1217#ifndef NANO_TINY 1218 if (old_mark_set && 1219 openfile->current->lineno == mb_lineno_save) { 1220 openfile->mark_begin = openfile->current; 1221 openfile->mark_begin_x = mark_begin_x_save; 1222 } 1223#endif 1224 } else 1225 top = openfile->current; 1226 for (i = par_len; i > 0 && top != NULL; i--) { 1227 if (top->lineno == fl_lineno_save) 1228 first_line = top; 1229 if (top->lineno == edittop_lineno_save) 1230 openfile->edittop = top; 1231 if (top->lineno == current_lineno_save) 1232 openfile->current = top; 1233#ifndef NANO_TINY 1234 if (old_mark_set && top->lineno == mb_lineno_save) { 1235 openfile->mark_begin = top; 1236 openfile->mark_begin_x = mark_begin_x_save; 1237 } 1238#endif 1239 top = top->prev; 1240 } 1241 1242 /* Put current_x at the same place in the copied paragraph that it 1243 * had in the original paragraph. */ 1244 openfile->current_x = current_x_save; 1245 1246 set_modified(); 1247} 1248 1249/* Find the beginning of the current paragraph if we're in one, or the 1250 * beginning of the next paragraph if we're not. Afterwards, save the 1251 * quote length and paragraph length in *quote and *par. Return TRUE if 1252 * we found a paragraph, and FALSE if there was an error or we didn't 1253 * find a paragraph. 1254 * 1255 * See the comment at begpar() for more about when a line is the 1256 * beginning of a paragraph. */ 1257bool find_paragraph(size_t *const quote, size_t *const par) 1258{ 1259 size_t quote_len; 1260 /* Length of the initial quotation of the paragraph we search 1261 * for. */ 1262 size_t par_len; 1263 /* Number of lines in the paragraph we search for. */ 1264 filestruct *current_save; 1265 /* The line at the beginning of the paragraph we search for. */ 1266 ssize_t current_y_save; 1267 /* The y-coordinate at the beginning of the paragraph we search 1268 * for. */ 1269 1270#ifdef HAVE_REGEX_H 1271 if (quoterc != 0) { 1272 statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr); 1273 return FALSE; 1274 } 1275#endif 1276 1277 assert(openfile->current != NULL); 1278 1279 /* If we're at the end of the last line of the file, it means that 1280 * there aren't any paragraphs left, so get out. */ 1281 if (openfile->current == openfile->filebot && openfile->current_x == 1282 strlen(openfile->filebot->data)) 1283 return FALSE; 1284 1285 /* If the current line isn't in a paragraph, move forward to the 1286 * last line of the next paragraph, if any. */ 1287 if (!inpar(openfile->current)) { 1288 do_para_end(FALSE); 1289 1290 /* If we end up past the beginning of the line, it means that 1291 * we're at the end of the last line of the file, and the line 1292 * isn't blank, in which case the last line of the file is the 1293 * last line of the next paragraph. 1294 * 1295 * Otherwise, if we end up on a line that's in a paragraph, it 1296 * means that we're on the line after the last line of the next 1297 * paragraph, in which case we should move back to the last line 1298 * of the next paragraph. */ 1299 if (openfile->current_x == 0) { 1300 if (!inpar(openfile->current->prev)) 1301 return FALSE; 1302 if (openfile->current != openfile->fileage) 1303 openfile->current = openfile->current->prev; 1304 } 1305 } 1306 1307 /* If the current line isn't the first line of the paragraph, move 1308 * back to the first line of the paragraph. */ 1309 if (!begpar(openfile->current)) 1310 do_para_begin(FALSE); 1311 1312 /* Now current is the first line of the paragraph. Set quote_len to 1313 * the quotation length of that line, and set par_len to the number 1314 * of lines in this paragraph. */ 1315 quote_len = quote_length(openfile->current->data); 1316 current_save = openfile->current; 1317 current_y_save = openfile->current_y; 1318 do_para_end(FALSE); 1319 par_len = openfile->current->lineno - current_save->lineno; 1320 1321 /* If we end up past the beginning of the line, it means that we're 1322 * at the end of the last line of the file, and the line isn't 1323 * blank, in which case the last line of the file is part of the 1324 * paragraph. */ 1325 if (openfile->current_x > 0) 1326 par_len++; 1327 openfile->current = current_save; 1328 openfile->current_y = current_y_save; 1329 1330 /* Save the values of quote_len and par_len. */ 1331 assert(quote != NULL && par != NULL); 1332 1333 *quote = quote_len; 1334 *par = par_len; 1335 1336 return TRUE; 1337} 1338 1339/* If full_justify is TRUE, justify the entire file. Otherwise, justify 1340 * the current paragraph. */ 1341void do_justify(bool full_justify) 1342{ 1343 filestruct *first_par_line = NULL; 1344 /* Will be the first line of the justified paragraph(s), if any. 1345 * For restoring after unjustify. */ 1346 filestruct *last_par_line = NULL; 1347 /* Will be the line after the last line of the justified 1348 * paragraph(s), if any. Also for restoring after unjustify. */ 1349 bool filebot_inpar = FALSE; 1350 /* Whether the text at filebot is part of the current 1351 * paragraph. */ 1352 1353 /* We save these variables to be restored if the user 1354 * unjustifies. */ 1355 filestruct *edittop_save = openfile->edittop; 1356 filestruct *current_save = openfile->current; 1357 size_t current_x_save = openfile->current_x; 1358 size_t pww_save = openfile->placewewant; 1359 size_t totsize_save = openfile->totsize; 1360#ifndef NANO_TINY 1361 filestruct *mark_begin_save = openfile->mark_begin; 1362 size_t mark_begin_x_save = openfile->mark_begin_x; 1363#endif 1364 bool modified_save = openfile->modified; 1365 1366 int kbinput; 1367 bool meta_key, func_key, s_or_t, ran_func, finished; 1368 1369 /* Move to the beginning of the current line, so that justifying at 1370 * the end of the last line of the file, if that line isn't blank, 1371 * will work the first time through. */ 1372 openfile->current_x = 0; 1373 1374 /* If we're justifying the entire file, start at the beginning. */ 1375 if (full_justify) 1376 openfile->current = openfile->fileage; 1377 1378 while (TRUE) { 1379 size_t i; 1380 /* Generic loop variable. */ 1381 filestruct *curr_first_par_line; 1382 /* The first line of the current paragraph. */ 1383 size_t quote_len; 1384 /* Length of the initial quotation of the current 1385 * paragraph. */ 1386 size_t indent_len; 1387 /* Length of the initial indentation of the current 1388 * paragraph. */ 1389 size_t par_len; 1390 /* Number of lines in the current paragraph. */ 1391 ssize_t break_pos; 1392 /* Where we will break lines. */ 1393 char *indent_string; 1394 /* The first indentation that doesn't match the initial 1395 * indentation of the current paragraph. This is put at the 1396 * beginning of every line broken off the first justified 1397 * line of the paragraph. Note that this works because a 1398 * paragraph can only contain two indentations at most: the 1399 * initial one, and a different one starting on a line after 1400 * the first. See the comment at begpar() for more about 1401 * when a line is part of a paragraph. */ 1402 1403 /* Find the first line of the paragraph to be justified. That 1404 * is the start of this paragraph if we're in one, or the start 1405 * of the next otherwise. Save the quote length and paragraph 1406 * length (number of lines). Don't refresh the screen yet, 1407 * since we'll do that after we justify. 1408 * 1409 * If the search failed, we do one of two things. If we're 1410 * justifying the whole file, and we've found at least one 1411 * paragraph, it means that we should justify all the way to the 1412 * last line of the file, so set the last line of the text to be 1413 * justified to the last line of the file and break out of the 1414 * loop. Otherwise, it means that there are no paragraph(s) to 1415 * justify, so refresh the screen and get out. */ 1416 if (!find_paragraph("e_len, &par_len)) { 1417 if (full_justify && first_par_line != NULL) { 1418 last_par_line = openfile->filebot; 1419 break; 1420 } else { 1421 edit_refresh(); 1422 return; 1423 } 1424 } 1425 1426 /* par_len will be one greater than the number of lines between 1427 * current and filebot if filebot is the last line in the 1428 * paragraph. Set filebot_inpar to TRUE if this is the case. */ 1429 filebot_inpar = (openfile->current->lineno + par_len == 1430 openfile->filebot->lineno + 1); 1431 1432 /* If we haven't already done it, move the original paragraph(s) 1433 * to the justify buffer, splice a copy of the original 1434 * paragraph(s) into the file in the same place, and set 1435 * first_par_line to the first line of the copy. */ 1436 if (first_par_line == NULL) { 1437 backup_lines(openfile->current, full_justify ? 1438 openfile->filebot->lineno - openfile->current->lineno + 1439 ((openfile->filebot->data[0] != '\0') ? 1 : 0) : 1440 par_len); 1441 first_par_line = openfile->current; 1442 } 1443 1444 /* Set curr_first_par_line to the first line of the current 1445 * paragraph. */ 1446 curr_first_par_line = openfile->current; 1447 1448 /* Initialize indent_string to a blank string. */ 1449 indent_string = mallocstrcpy(NULL, ""); 1450 1451 /* Find the first indentation in the paragraph that doesn't 1452 * match the indentation of the first line, and save it in 1453 * indent_string. If all the indentations are the same, save 1454 * the indentation of the first line in indent_string. */ 1455 { 1456 const filestruct *indent_line = openfile->current; 1457 bool past_first_line = FALSE; 1458 1459 for (i = 0; i < par_len; i++) { 1460 indent_len = quote_len + 1461 indent_length(indent_line->data + quote_len); 1462 1463 if (indent_len != strlen(indent_string)) { 1464 indent_string = mallocstrncpy(indent_string, 1465 indent_line->data, indent_len + 1); 1466 indent_string[indent_len] = '\0'; 1467 1468 if (past_first_line) 1469 break; 1470 } 1471 1472 if (indent_line == openfile->current) 1473 past_first_line = TRUE; 1474 1475 indent_line = indent_line->next; 1476 } 1477 } 1478 1479 /* Now tack all the lines of the paragraph together, skipping 1480 * the quoting and indentation on all lines after the first. */ 1481 for (i = 0; i < par_len - 1; i++) { 1482 filestruct *next_line = openfile->current->next; 1483 size_t line_len = strlen(openfile->current->data); 1484 size_t next_line_len = 1485 strlen(openfile->current->next->data); 1486 1487 indent_len = quote_len + 1488 indent_length(openfile->current->next->data + 1489 quote_len); 1490 1491 next_line_len -= indent_len; 1492 openfile->totsize -= indent_len; 1493 1494 /* We're just about to tack the next line onto this one. If 1495 * this line isn't empty, make sure it ends in a space. */ 1496 if (line_len > 0 && 1497 openfile->current->data[line_len - 1] != ' ') { 1498 line_len++; 1499 openfile->current->data = 1500 charealloc(openfile->current->data, 1501 line_len + 1); 1502 openfile->current->data[line_len - 1] = ' '; 1503 openfile->current->data[line_len] = '\0'; 1504 openfile->totsize++; 1505 } 1506 1507 openfile->current->data = 1508 charealloc(openfile->current->data, line_len + 1509 next_line_len + 1); 1510 strcat(openfile->current->data, next_line->data + 1511 indent_len); 1512 1513 /* Don't destroy edittop or filebot! */ 1514 if (next_line == openfile->edittop) 1515 openfile->edittop = openfile->current; 1516 if (next_line == openfile->filebot) 1517 openfile->filebot = openfile->current; 1518 1519#ifndef NANO_TINY 1520 /* Adjust the mark coordinates to compensate for the change 1521 * in the next line. */ 1522 if (openfile->mark_set && openfile->mark_begin == 1523 next_line) { 1524 openfile->mark_begin = openfile->current; 1525 openfile->mark_begin_x += line_len - indent_len; 1526 } 1527#endif 1528 1529 unlink_node(next_line); 1530 delete_node(next_line); 1531 1532 /* If we've removed the next line, we need to go through 1533 * this line again. */ 1534 i--; 1535 1536 par_len--; 1537 openfile->totsize--; 1538 } 1539 1540 /* Call justify_format() on the paragraph, which will remove 1541 * excess spaces from it and change all blank characters to 1542 * spaces. */ 1543 justify_format(openfile->current, quote_len + 1544 indent_length(openfile->current->data + quote_len)); 1545 1546 while (par_len > 0 && strlenpt(openfile->current->data) > 1547 fill) { 1548 size_t line_len = strlen(openfile->current->data); 1549 1550 indent_len = strlen(indent_string); 1551 1552 /* If this line is too long, try to wrap it to the next line 1553 * to make it short enough. */ 1554 break_pos = break_line(openfile->current->data + indent_len, 1555 fill - strnlenpt(openfile->current->data, indent_len) 1556#ifndef DISABLE_HELP 1557 , FALSE 1558#endif 1559 ); 1560 1561 /* We can't break the line, or don't need to, so get out. */ 1562 if (break_pos == -1 || break_pos + indent_len == line_len) 1563 break; 1564 1565 /* Move forward to the character after the indentation and 1566 * just after the space. */ 1567 break_pos += indent_len + 1; 1568 1569 assert(break_pos <= line_len); 1570 1571 /* Make a new line, and copy the text after where we're 1572 * going to break this line to the beginning of the new 1573 * line. */ 1574 splice_node(openfile->current, 1575 make_new_node(openfile->current), 1576 openfile->current->next); 1577 1578 /* If this paragraph is non-quoted, and autoindent isn't 1579 * turned on, set the indentation length to zero so that the 1580 * indentation is treated as part of the line. */ 1581 if (quote_len == 0 1582#ifndef NANO_TINY 1583 && !ISSET(AUTOINDENT) 1584#endif 1585 ) 1586 indent_len = 0; 1587 1588 /* Copy the text after where we're going to break the 1589 * current line to the next line. */ 1590 openfile->current->next->data = charalloc(indent_len + 1 + 1591 line_len - break_pos); 1592 strncpy(openfile->current->next->data, indent_string, 1593 indent_len); 1594 strcpy(openfile->current->next->data + indent_len, 1595 openfile->current->data + break_pos); 1596 1597 par_len++; 1598 openfile->totsize += indent_len + 1; 1599 1600#ifndef NANO_TINY 1601 /* Adjust the mark coordinates to compensate for the change 1602 * in the current line. */ 1603 if (openfile->mark_set && openfile->mark_begin == 1604 openfile->current && openfile->mark_begin_x > 1605 break_pos) { 1606 openfile->mark_begin = openfile->current->next; 1607 openfile->mark_begin_x -= break_pos - indent_len; 1608 } 1609#endif 1610 1611 /* Break the current line. */ 1612 null_at(&openfile->current->data, break_pos); 1613 1614 /* If the current line is the last line of the file, move 1615 * the last line of the file down to the next line. */ 1616 if (openfile->filebot == openfile->current) 1617 openfile->filebot = openfile->filebot->next; 1618 1619 /* Go to the next line. */ 1620 par_len--; 1621 openfile->current_y++; 1622 openfile->current = openfile->current->next; 1623 } 1624 1625 /* We're done breaking lines, so we don't need indent_string 1626 * anymore. */ 1627 free(indent_string); 1628 1629 /* Go to the next line, if possible. If there is no next line, 1630 * move to the end of the current line. */ 1631 if (openfile->current != openfile->filebot) { 1632 openfile->current_y++; 1633 openfile->current = openfile->current->next; 1634 } else 1635 openfile->current_x = strlen(openfile->current->data); 1636 1637 /* Renumber the lines of the now-justified current paragraph, 1638 * since both find_paragraph() and edit_refresh() need the line 1639 * numbers to be right. */ 1640 renumber(curr_first_par_line); 1641 1642 /* We've just finished justifying the paragraph. If we're not 1643 * justifying the entire file, break out of the loop. 1644 * Otherwise, continue the loop so that we justify all the 1645 * paragraphs in the file. */ 1646 if (!full_justify) 1647 break; 1648 } 1649 1650 /* We are now done justifying the paragraph or the file, so clean 1651 * up. current_y and totsize have been maintained above. If we 1652 * actually justified something, set last_par_line to the new end of 1653 * the paragraph. */ 1654 if (first_par_line != NULL) 1655 last_par_line = openfile->current; 1656 1657 edit_refresh(); 1658 1659#ifndef NANO_TINY 1660 /* We're going to set jump_buf so that we return here after a 1661 * SIGWINCH instead of to main(). Indicate this. */ 1662 jump_buf_main = FALSE; 1663 1664 /* Return here after a SIGWINCH. */ 1665 sigsetjmp(jump_buf, 1); 1666#endif 1667 1668 statusbar(_("Can now UnJustify!")); 1669 1670 /* If constant cursor position display is on, make sure the current 1671 * cursor position will be properly displayed on the statusbar. */ 1672 if (ISSET(CONST_UPDATE)) 1673 do_cursorpos(TRUE); 1674 1675 /* Display the shortcut list with UnJustify. */ 1676 shortcut_init(TRUE); 1677 display_main_list(); 1678 1679 /* Now get a keystroke and see if it's unjustify. If not, put back 1680 * the keystroke and return. */ 1681 kbinput = do_input(&meta_key, &func_key, &s_or_t, &ran_func, 1682 &finished, FALSE); 1683 1684 if (s_or_t && kbinput == NANO_UNJUSTIFY_KEY) { 1685 /* Splice the justify buffer back into the file, but only if we 1686 * actually justified something. */ 1687 if (first_par_line != NULL) { 1688 filestruct *top_save; 1689 1690 /* Partition the filestruct so that it contains only the 1691 * text of the justified paragraph. */ 1692 filepart = partition_filestruct(first_par_line, 0, 1693 last_par_line, filebot_inpar ? 1694 strlen(last_par_line->data) : 0); 1695 1696 /* Remove the text of the justified paragraph, and 1697 * replace it with the text in the justify buffer. */ 1698 free_filestruct(openfile->fileage); 1699 openfile->fileage = jusbuffer; 1700 openfile->filebot = jusbottom; 1701 1702 top_save = openfile->fileage; 1703 1704 /* Unpartition the filestruct so that it contains all the 1705 * text again. Note that the justified paragraph has been 1706 * replaced with the unjustified paragraph. */ 1707 unpartition_filestruct(&filepart); 1708 1709 /* Renumber starting with the beginning line of the old 1710 * partition. */ 1711 renumber(top_save); 1712 1713 /* Restore the justify we just did (ungrateful user!). */ 1714 openfile->edittop = edittop_save; 1715 openfile->current = current_save; 1716 openfile->current_x = current_x_save; 1717 openfile->placewewant = pww_save; 1718 openfile->totsize = totsize_save; 1719#ifndef NANO_TINY 1720 if (openfile->mark_set) { 1721 openfile->mark_begin = mark_begin_save; 1722 openfile->mark_begin_x = mark_begin_x_save; 1723 } 1724#endif 1725 openfile->modified = modified_save; 1726 1727 /* Clear the justify buffer. */ 1728 jusbuffer = NULL; 1729 1730 if (!openfile->modified) 1731 titlebar(NULL); 1732 edit_refresh(); 1733 } 1734 } else { 1735 unget_kbinput(kbinput, meta_key, func_key); 1736 1737 /* Blow away the text in the justify buffer. */ 1738 free_filestruct(jusbuffer); 1739 jusbuffer = NULL; 1740 } 1741 1742 blank_statusbar(); 1743 1744 /* Display the shortcut list with UnCut. */ 1745 shortcut_init(FALSE); 1746 display_main_list(); 1747} 1748 1749/* Justify the current paragraph. */ 1750void do_justify_void(void) 1751{ 1752 do_justify(FALSE); 1753} 1754 1755/* Justify the entire file. */ 1756void do_full_justify(void) 1757{ 1758 do_justify(TRUE); 1759} 1760#endif /* !DISABLE_JUSTIFY */ 1761 1762#ifndef DISABLE_SPELLER 1763/* A word is misspelled in the file. Let the user replace it. We 1764 * return FALSE if the user cancels. */ 1765bool do_int_spell_fix(const char *word) 1766{ 1767 char *save_search, *save_replace; 1768 size_t match_len, current_x_save = openfile->current_x; 1769 size_t pww_save = openfile->placewewant; 1770 filestruct *edittop_save = openfile->edittop; 1771 filestruct *current_save = openfile->current; 1772 /* Save where we are. */ 1773 bool canceled = FALSE; 1774 /* The return value. */ 1775 bool case_sens_set = ISSET(CASE_SENSITIVE); 1776#ifndef NANO_TINY 1777 bool backwards_search_set = ISSET(BACKWARDS_SEARCH); 1778#endif 1779#ifdef HAVE_REGEX_H 1780 bool regexp_set = ISSET(USE_REGEXP); 1781#endif 1782#ifndef NANO_TINY 1783 bool old_mark_set = openfile->mark_set; 1784 bool added_magicline = FALSE; 1785 /* Whether we added a magicline after filebot. */ 1786 bool right_side_up = FALSE; 1787 /* TRUE if (mark_begin, mark_begin_x) is the top of the mark, 1788 * FALSE if (current, current_x) is. */ 1789 filestruct *top, *bot; 1790 size_t top_x, bot_x; 1791#endif 1792 1793 /* Make sure spell-check is case sensitive. */ 1794 SET(CASE_SENSITIVE); 1795 1796#ifndef NANO_TINY 1797 /* Make sure spell-check goes forward only. */ 1798 UNSET(BACKWARDS_SEARCH); 1799#endif 1800#ifdef HAVE_REGEX_H 1801 /* Make sure spell-check doesn't use regular expressions. */ 1802 UNSET(USE_REGEXP); 1803#endif 1804 1805 /* Save the current search/replace strings. */ 1806 search_init_globals(); 1807 save_search = last_search; 1808 save_replace = last_replace; 1809 1810 /* Set the search/replace strings to the misspelled word. */ 1811 last_search = mallocstrcpy(NULL, word); 1812 last_replace = mallocstrcpy(NULL, word); 1813 1814#ifndef NANO_TINY 1815 if (old_mark_set) { 1816 /* If the mark is on, partition the filestruct so that it 1817 * contains only the marked text; if the NO_NEWLINES flag isn't 1818 * set, keep track of whether the text will have a magicline 1819 * added when we're done correcting misspelled words; and 1820 * turn the mark off. */ 1821 mark_order((const filestruct **)&top, &top_x, 1822 (const filestruct **)&bot, &bot_x, &right_side_up); 1823 filepart = partition_filestruct(top, top_x, bot, bot_x); 1824 if (!ISSET(NO_NEWLINES)) 1825 added_magicline = (openfile->filebot->data[0] != '\0'); 1826 openfile->mark_set = FALSE; 1827 } 1828#endif 1829 1830 /* Start from the top of the file. */ 1831 openfile->edittop = openfile->fileage; 1832 openfile->current = openfile->fileage; 1833 openfile->current_x = (size_t)-1; 1834 openfile->placewewant = 0; 1835 1836 /* Find the first whole occurrence of word. */ 1837 findnextstr_wrap_reset(); 1838 while (findnextstr(TRUE, FALSE, openfile->fileage, 0, word, 1839 &match_len)) { 1840 if (is_whole_word(openfile->current_x, openfile->current->data, 1841 word)) { 1842 size_t xpt = xplustabs(); 1843 char *exp_word = display_string(openfile->current->data, 1844 xpt, strnlenpt(openfile->current->data, 1845 openfile->current_x + match_len) - xpt, FALSE); 1846 1847 edit_refresh(); 1848 1849 do_replace_highlight(TRUE, exp_word); 1850 1851 /* Allow all instances of the word to be corrected. */ 1852 canceled = (do_prompt(FALSE, 1853#ifndef DISABLE_TABCOMP 1854 TRUE, 1855#endif 1856 spell_list, word, 1857#ifndef NANO_TINY 1858 NULL, 1859#endif 1860 edit_refresh, _("Edit a replacement")) == -1); 1861 1862 do_replace_highlight(FALSE, exp_word); 1863 1864 free(exp_word); 1865 1866 if (!canceled && strcmp(word, answer) != 0) { 1867 openfile->current_x--; 1868 do_replace_loop(TRUE, &canceled, openfile->current, 1869 &openfile->current_x, word); 1870 } 1871 1872 break; 1873 } 1874 } 1875 1876#ifndef NANO_TINY 1877 if (old_mark_set) { 1878 /* If the mark was on, the NO_NEWLINES flag isn't set, and we 1879 * added a magicline, remove it now. */ 1880 if (!ISSET(NO_NEWLINES) && added_magicline) 1881 remove_magicline(); 1882 1883 /* Put the beginning and the end of the mark at the beginning 1884 * and the end of the spell-checked text. */ 1885 if (openfile->fileage == openfile->filebot) 1886 bot_x += top_x; 1887 if (right_side_up) { 1888 openfile->mark_begin_x = top_x; 1889 current_x_save = bot_x; 1890 } else { 1891 current_x_save = top_x; 1892 openfile->mark_begin_x = bot_x; 1893 } 1894 1895 /* Unpartition the filestruct so that it contains all the text 1896 * again, and turn the mark back on. */ 1897 unpartition_filestruct(&filepart); 1898 openfile->mark_set = TRUE; 1899 } 1900#endif 1901 1902 /* Restore the search/replace strings. */ 1903 free(last_search); 1904 last_search = save_search; 1905 free(last_replace); 1906 last_replace = save_replace; 1907 1908 /* Restore where we were. */ 1909 openfile->edittop = edittop_save; 1910 openfile->current = current_save; 1911 openfile->current_x = current_x_save; 1912 openfile->placewewant = pww_save; 1913 1914 /* Restore case sensitivity setting. */ 1915 if (!case_sens_set) 1916 UNSET(CASE_SENSITIVE); 1917 1918#ifndef NANO_TINY 1919 /* Restore search/replace direction. */ 1920 if (backwards_search_set) 1921 SET(BACKWARDS_SEARCH); 1922#endif 1923#ifdef HAVE_REGEX_H 1924 /* Restore regular expression usage setting. */ 1925 if (regexp_set) 1926 SET(USE_REGEXP); 1927#endif 1928 1929 return !canceled; 1930} 1931 1932/* Internal (integrated) spell checking using the spell program, 1933 * filtered through the sort and uniq programs. Return NULL for normal 1934 * termination, and the error string otherwise. */ 1935const char *do_int_speller(const char *tempfile_name) 1936{ 1937 char *read_buff, *read_buff_ptr, *read_buff_word; 1938 size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread; 1939 int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1; 1940 pid_t pid_spell, pid_sort, pid_uniq; 1941 int spell_status, sort_status, uniq_status; 1942 1943 /* Create all three pipes up front. */ 1944 if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 || 1945 pipe(uniq_fd) == -1) 1946 return _("Could not create pipe"); 1947 1948 statusbar(_("Creating misspelled word list, please wait...")); 1949 1950 /* A new process to run spell in. */ 1951 if ((pid_spell = fork()) == 0) { 1952 /* Child continues (i.e. future spell process). */ 1953 close(spell_fd[0]); 1954 1955 /* Replace the standard input with the temp file. */ 1956 if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1) 1957 goto close_pipes_and_exit; 1958 1959 if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO) 1960 goto close_pipes_and_exit; 1961 1962 close(tempfile_fd); 1963 1964 /* Send spell's standard output to the pipe. */ 1965 if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO) 1966 goto close_pipes_and_exit; 1967 1968 close(spell_fd[1]); 1969 1970 /* Start the spell program; we are using $PATH. */ 1971 execlp("spell", "spell", NULL); 1972 1973 /* This should not be reached if spell is found. */ 1974 exit(1); 1975 } 1976 1977 /* Parent continues here. */ 1978 close(spell_fd[1]); 1979 1980 /* A new process to run sort in. */ 1981 if ((pid_sort = fork()) == 0) { 1982 /* Child continues (i.e. future spell process). Replace the 1983 * standard input with the standard output of the old pipe. */ 1984 if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO) 1985 goto close_pipes_and_exit; 1986 1987 close(spell_fd[0]); 1988 1989 /* Send sort's standard output to the new pipe. */ 1990 if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO) 1991 goto close_pipes_and_exit; 1992 1993 close(sort_fd[1]); 1994 1995 /* Start the sort program. Use -f to remove mixed case. If 1996 * this isn't portable, let me know. */ 1997 execlp("sort", "sort", "-f", NULL); 1998 1999 /* This should not be reached if sort is found. */ 2000 exit(1); 2001 } 2002 2003 close(spell_fd[0]); 2004 close(sort_fd[1]); 2005 2006 /* A new process to run uniq in. */ 2007 if ((pid_uniq = fork()) == 0) { 2008 /* Child continues (i.e. future uniq process). Replace the 2009 * standard input with the standard output of the old pipe. */ 2010 if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO) 2011 goto close_pipes_and_exit; 2012 2013 close(sort_fd[0]); 2014 2015 /* Send uniq's standard output to the new pipe. */ 2016 if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO) 2017 goto close_pipes_and_exit; 2018 2019 close(uniq_fd[1]); 2020 2021 /* Start the uniq program; we are using PATH. */ 2022 execlp("uniq", "uniq", NULL); 2023 2024 /* This should not be reached if uniq is found. */ 2025 exit(1); 2026 } 2027 2028 close(sort_fd[0]); 2029 close(uniq_fd[1]); 2030 2031 /* The child process was not forked successfully. */ 2032 if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) { 2033 close(uniq_fd[0]); 2034 return _("Could not fork"); 2035 } 2036 2037 /* Get the system pipe buffer size. */ 2038 if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) { 2039 close(uniq_fd[0]); 2040 return _("Could not get size of pipe buffer"); 2041 } 2042 2043 /* Read in the returned spelling errors. */ 2044 read_buff_read = 0; 2045 read_buff_size = pipe_buff_size + 1; 2046 read_buff = read_buff_ptr = charalloc(read_buff_size); 2047 2048 while ((bytesread = read(uniq_fd[0], read_buff_ptr, 2049 pipe_buff_size)) > 0) { 2050 read_buff_read += bytesread; 2051 read_buff_size += pipe_buff_size; 2052 read_buff = read_buff_ptr = charealloc(read_buff, 2053 read_buff_size); 2054 read_buff_ptr += read_buff_read; 2055 } 2056 2057 *read_buff_ptr = '\0'; 2058 close(uniq_fd[0]); 2059 2060 /* Process the spelling errors. */ 2061 read_buff_word = read_buff_ptr = read_buff; 2062 2063 while (*read_buff_ptr != '\0') { 2064 if ((*read_buff_ptr == '\r') || (*read_buff_ptr == '\n')) { 2065 *read_buff_ptr = '\0'; 2066 if (read_buff_word != read_buff_ptr) { 2067 if (!do_int_spell_fix(read_buff_word)) { 2068 read_buff_word = read_buff_ptr; 2069 break; 2070 } 2071 } 2072 read_buff_word = read_buff_ptr + 1; 2073 } 2074 read_buff_ptr++; 2075 } 2076 2077 /* Special case: the last word doesn't end with '\r' or '\n'. */ 2078 if (read_buff_word != read_buff_ptr) 2079 do_int_spell_fix(read_buff_word); 2080 2081 free(read_buff); 2082 search_replace_abort(); 2083 edit_refresh(); 2084 2085 /* Process the end of the spell process. */ 2086 waitpid(pid_spell, &spell_status, 0); 2087 waitpid(pid_sort, &sort_status, 0); 2088 waitpid(pid_uniq, &uniq_status, 0); 2089 2090 if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status)) 2091 return _("Error invoking \"spell\""); 2092 2093 if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status)) 2094 return _("Error invoking \"sort -f\""); 2095 2096 if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status)) 2097 return _("Error invoking \"uniq\""); 2098 2099 /* Otherwise... */ 2100 return NULL; 2101 2102 close_pipes_and_exit: 2103 /* Don't leak any handles. */ 2104 close(tempfile_fd); 2105 close(spell_fd[0]); 2106 close(spell_fd[1]); 2107 close(sort_fd[0]); 2108 close(sort_fd[1]); 2109 close(uniq_fd[0]); 2110 close(uniq_fd[1]); 2111 exit(1); 2112} 2113 2114/* External (alternate) spell checking. Return NULL for normal 2115 * termination, and the error string otherwise. */ 2116const char *do_alt_speller(char *tempfile_name) 2117{ 2118 int alt_spell_status; 2119 size_t current_x_save = openfile->current_x; 2120 size_t pww_save = openfile->placewewant; 2121 ssize_t current_y_save = openfile->current_y; 2122 ssize_t lineno_save = openfile->current->lineno; 2123 pid_t pid_spell; 2124 char *ptr; 2125 static int arglen = 3; 2126 static char **spellargs = NULL; 2127#ifndef NANO_TINY 2128 bool old_mark_set = openfile->mark_set; 2129 bool added_magicline = FALSE; 2130 /* Whether we added a magicline after filebot. */ 2131 bool right_side_up = FALSE; 2132 /* TRUE if (mark_begin, mark_begin_x) is the top of the mark, 2133 * FALSE if (current, current_x) is. */ 2134 filestruct *top, *bot; 2135 size_t top_x, bot_x; 2136 ssize_t mb_lineno_save = 0; 2137 /* We're going to close the current file, and open the output of 2138 * the alternate spell command. The line that mark_begin points 2139 * to will be freed, so we save the line number and restore it 2140 * afterwards. */ 2141 size_t totsize_save = openfile->totsize; 2142 /* Our saved value of totsize, used when we spell-check a marked 2143 * selection. */ 2144 2145 if (old_mark_set) { 2146 /* If the mark is on, save the number of the line it starts on, 2147 * and then turn the mark off. */ 2148 mb_lineno_save = openfile->mark_begin->lineno; 2149 openfile->mark_set = FALSE; 2150 } 2151#endif 2152 2153 endwin(); 2154 2155 /* Set up an argument list to pass execvp(). */ 2156 if (spellargs == NULL) { 2157 spellargs = (char **)nmalloc(arglen * sizeof(char *)); 2158 2159 spellargs[0] = strtok(alt_speller, " "); 2160 while ((ptr = strtok(NULL, " ")) != NULL) { 2161 arglen++; 2162 spellargs = (char **)nrealloc(spellargs, arglen * 2163 sizeof(char *)); 2164 spellargs[arglen - 3] = ptr; 2165 } 2166 spellargs[arglen - 1] = NULL; 2167 } 2168 spellargs[arglen - 2] = tempfile_name; 2169 2170 /* Start a new process for the alternate speller. */ 2171 if ((pid_spell = fork()) == 0) { 2172 /* Start alternate spell program; we are using $PATH. */ 2173 execvp(spellargs[0], spellargs); 2174 2175 /* Should not be reached, if alternate speller is found!!! */ 2176 exit(1); 2177 } 2178 2179 /* If we couldn't fork, get out. */ 2180 if (pid_spell < 0) 2181 return _("Could not fork"); 2182 2183#ifndef NANO_TINY 2184 /* Don't handle a pending SIGWINCH until the alternate spell checker 2185 * is finished and we've loaded the spell-checked file back in. */ 2186 allow_pending_sigwinch(FALSE); 2187#endif 2188 2189 /* Wait for the alternate spell checker to finish. */ 2190 wait(&alt_spell_status); 2191 2192 /* Reenter curses mode. */ 2193 doupdate(); 2194 2195 /* Restore the terminal to its previous state. */ 2196 terminal_init(); 2197 2198 /* Turn the cursor back on for sure. */ 2199 curs_set(1); 2200 2201 /* The screen might have been resized. If it has, reinitialize all 2202 * the windows based on the new screen dimensions. */ 2203 window_init(); 2204 2205 if (!WIFEXITED(alt_spell_status) || 2206 WEXITSTATUS(alt_spell_status) != 0) { 2207 char *alt_spell_error; 2208 char *invoke_error = _("Error invoking \"%s\""); 2209 2210#ifndef NANO_TINY 2211 /* Turn the mark back on if it was on before. */ 2212 openfile->mark_set = old_mark_set; 2213#endif 2214 2215 alt_spell_error = 2216 charalloc(strlen(invoke_error) + 2217 strlen(alt_speller) + 1); 2218 sprintf(alt_spell_error, invoke_error, alt_speller); 2219 return alt_spell_error; 2220 } 2221 2222#ifndef NANO_TINY 2223 if (old_mark_set) { 2224 /* If the mark is on, partition the filestruct so that it 2225 * contains only the marked text; if the NO_NEWLINES flag isn't 2226 * set, keep track of whether the text will have a magicline 2227 * added when we're done correcting misspelled words; and 2228 * turn the mark off. */ 2229 mark_order((const filestruct **)&top, &top_x, 2230 (const filestruct **)&bot, &bot_x, &right_side_up); 2231 filepart = partition_filestruct(top, top_x, bot, bot_x); 2232 if (!ISSET(NO_NEWLINES)) 2233 added_magicline = (openfile->filebot->data[0] != '\0'); 2234 2235 /* Get the number of characters in the marked text, and subtract 2236 * it from the saved value of totsize. */ 2237 totsize_save -= get_totsize(top, bot); 2238 } 2239#endif 2240 2241 /* Replace the text of the current buffer with the spell-checked 2242 * text. */ 2243 replace_buffer(tempfile_name); 2244 2245#ifndef NANO_TINY 2246 if (old_mark_set) { 2247 filestruct *top_save = openfile->fileage; 2248 2249 /* If the mark was on, the NO_NEWLINES flag isn't set, and we 2250 * added a magicline, remove it now. */ 2251 if (!ISSET(NO_NEWLINES) && added_magicline) 2252 remove_magicline(); 2253 2254 /* Put the beginning and the end of the mark at the beginning 2255 * and the end of the spell-checked text. */ 2256 if (openfile->fileage == openfile->filebot) 2257 bot_x += top_x; 2258 if (right_side_up) { 2259 openfile->mark_begin_x = top_x; 2260 current_x_save = bot_x; 2261 } else { 2262 current_x_save = top_x; 2263 openfile->mark_begin_x = bot_x; 2264 } 2265 2266 /* Unpartition the filestruct so that it contains all the text 2267 * again. Note that we've replaced the marked text originally 2268 * in the partition with the spell-checked marked text in the 2269 * temp file. */ 2270 unpartition_filestruct(&filepart); 2271 2272 /* Renumber starting with the beginning line of the old 2273 * partition. Also add the number of characters in the 2274 * spell-checked marked text to the saved value of totsize, and 2275 * then make that saved value the actual value. */ 2276 renumber(top_save); 2277 totsize_save += openfile->totsize; 2278 openfile->totsize = totsize_save; 2279 2280 /* Assign mark_begin to the line where the mark began before. */ 2281 do_gotopos(mb_lineno_save, openfile->mark_begin_x, 2282 current_y_save, 0); 2283 openfile->mark_begin = openfile->current; 2284 2285 /* Assign mark_begin_x to the location in mark_begin where the 2286 * mark began before, adjusted for any shortening of the 2287 * line. */ 2288 openfile->mark_begin_x = openfile->current_x; 2289 2290 /* Turn the mark back on. */ 2291 openfile->mark_set = TRUE; 2292 } 2293#endif 2294 2295 /* Go back to the old position, and mark the file as modified. */ 2296 do_gotopos(lineno_save, current_x_save, current_y_save, pww_save); 2297 set_modified(); 2298 2299#ifndef NANO_TINY 2300 /* Handle a pending SIGWINCH again. */ 2301 allow_pending_sigwinch(TRUE); 2302#endif 2303 2304 return NULL; 2305} 2306 2307/* Spell check the current file. If an alternate spell checker is 2308 * specified, use it. Otherwise, use the internal spell checker. */ 2309void do_spell(void) 2310{ 2311 bool status; 2312 FILE *temp_file; 2313 char *temp = safe_tempfile(&temp_file); 2314 const char *spell_msg; 2315 2316 if (temp == NULL) { 2317 statusbar(_("Error writing temp file: %s"), strerror(errno)); 2318 return; 2319 } 2320 2321 status = 2322#ifndef NANO_TINY 2323 openfile->mark_set ? write_marked_file(temp, temp_file, TRUE, 2324 OVERWRITE) : 2325#endif 2326 write_file(temp, temp_file, TRUE, OVERWRITE, FALSE); 2327 2328 if (!status) { 2329 statusbar(_("Error writing temp file: %s"), strerror(errno)); 2330 free(temp); 2331 return; 2332 } 2333 2334 spell_msg = (alt_speller != NULL) ? do_alt_speller(temp) : 2335 do_int_speller(temp); 2336 unlink(temp); 2337 free(temp); 2338 2339 currshortcut = main_list; 2340 2341 /* If the spell-checker printed any error messages onscreen, make 2342 * sure that they're cleared off. */ 2343 total_refresh(); 2344 2345 if (spell_msg != NULL) { 2346 if (errno == 0) 2347 /* Don't display an error message of "Success". */ 2348 statusbar(_("Spell checking failed: %s"), spell_msg); 2349 else 2350 statusbar(_("Spell checking failed: %s: %s"), spell_msg, 2351 strerror(errno)); 2352 } else 2353 statusbar(_("Finished checking spelling")); 2354} 2355#endif /* !DISABLE_SPELLER */ 2356 2357#ifndef NANO_TINY 2358/* Our own version of "wc". Note that its character counts are in 2359 * multibyte characters instead of single-byte characters. */ 2360void do_wordlinechar_count(void) 2361{ 2362 size_t words = 0, chars = 0; 2363 ssize_t lines = 0; 2364 size_t current_x_save = openfile->current_x; 2365 size_t pww_save = openfile->placewewant; 2366 filestruct *current_save = openfile->current; 2367 bool old_mark_set = openfile->mark_set; 2368 filestruct *top, *bot; 2369 size_t top_x, bot_x; 2370 2371 if (old_mark_set) { 2372 /* If the mark is on, partition the filestruct so that it 2373 * contains only the marked text, and turn the mark off. */ 2374 mark_order((const filestruct **)&top, &top_x, 2375 (const filestruct **)&bot, &bot_x, NULL); 2376 filepart = partition_filestruct(top, top_x, bot, bot_x); 2377 openfile->mark_set = FALSE; 2378 } 2379 2380 /* Start at the top of the file. */ 2381 openfile->current = openfile->fileage; 2382 openfile->current_x = 0; 2383 openfile->placewewant = 0; 2384 2385 /* Keep moving to the next word (counting punctuation characters as 2386 * part of a word, as "wc -w" does), without updating the screen, 2387 * until we reach the end of the file, incrementing the total word 2388 * count whenever we're on a word just before moving. */ 2389 while (openfile->current != openfile->filebot || 2390 openfile->current->data[openfile->current_x] != '\0') { 2391 if (do_next_word(TRUE, FALSE)) 2392 words++; 2393 } 2394 2395 /* Get the total line and character counts, as "wc -l" and "wc -c" 2396 * do, but get the latter in multibyte characters. */ 2397 if (old_mark_set) { 2398 lines = openfile->filebot->lineno - 2399 openfile->fileage->lineno + 1; 2400 chars = get_totsize(openfile->fileage, openfile->filebot); 2401 2402 /* Unpartition the filestruct so that it contains all the text 2403 * again, and turn the mark back on. */ 2404 unpartition_filestruct(&filepart); 2405 openfile->mark_set = TRUE; 2406 } else { 2407 lines = openfile->filebot->lineno; 2408 chars = openfile->totsize; 2409 } 2410 2411 /* Restore where we were. */ 2412 openfile->current = current_save; 2413 openfile->current_x = current_x_save; 2414 openfile->placewewant = pww_save; 2415 2416 /* Display the total word, line, and character counts on the 2417 * statusbar. */ 2418 statusbar(_("%sWords: %lu Lines: %ld Chars: %lu"), old_mark_set ? 2419 _("In Selection: ") : "", (unsigned long)words, (long)lines, 2420 (unsigned long)chars); 2421} 2422#endif /* !NANO_TINY */ 2423 2424/* Get verbatim input. */ 2425void do_verbatim_input(void) 2426{ 2427 int *kbinput; 2428 size_t kbinput_len, i; 2429 char *output; 2430 2431 /* TRANSLATORS: This is displayed when the next keystroke will be 2432 * inserted verbatim. */ 2433 statusbar(_("Verbatim Input")); 2434 2435 /* Read in all the verbatim characters. */ 2436 kbinput = get_verbatim_kbinput(edit, &kbinput_len); 2437 2438 /* If constant cursor position display is on, make sure the current 2439 * cursor position will be properly displayed on the statusbar. 2440 * Otherwise, blank the statusbar. */ 2441 if (ISSET(CONST_UPDATE)) 2442 do_cursorpos(TRUE); 2443 else { 2444 blank_statusbar(); 2445 wnoutrefresh(bottomwin); 2446 } 2447 2448 /* Display all the verbatim characters at once, not filtering out 2449 * control characters. */ 2450 output = charalloc(kbinput_len + 1); 2451 2452 for (i = 0; i < kbinput_len; i++) 2453 output[i] = (char)kbinput[i]; 2454 output[i] = '\0'; 2455 2456 free(kbinput); 2457 2458 do_output(output, kbinput_len, TRUE); 2459 2460 free(output); 2461} 2462