1 /* $Id: text.c 4520 2010-11-12 06:23:14Z astyanax $ */
2 /**************************************************************************
5 * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
6 * 2008, 2009 Free Software Foundation, Inc. *
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 3, or (at your option) *
10 * any later version. *
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. *
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 *
22 **************************************************************************/
35 static pid_t pid
= -1;
36 /* The PID of the forked process in execute_command(), for use
37 * with the cancel_command() signal handler. */
39 #ifndef DISABLE_WRAPPING
40 static bool prepend_wrap
= FALSE
;
41 /* Should we prepend wrapped text to the next line? */
43 #ifndef DISABLE_JUSTIFY
44 static filestruct
*jusbottom
= NULL
;
45 /* Pointer to the end of the justify buffer. */
49 /* Toggle the mark. */
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
;
58 statusbar(_("Mark Unset"));
59 openfile
->mark_begin
= NULL
;
60 openfile
->mark_begin_x
= 0;
64 #endif /* !NANO_TINY */
66 /* Delete the character under the cursor. */
69 size_t orig_lenpt
= 0;
75 assert(openfile
->current
!= NULL
&& openfile
->current
->data
!= NULL
&& openfile
->current_x
<= strlen(openfile
->current
->data
));
77 openfile
->placewewant
= xplustabs();
79 if (openfile
->current
->data
[openfile
->current_x
] != '\0') {
80 int char_buf_len
= parse_mbchar(openfile
->current
->data
+
81 openfile
->current_x
, NULL
, NULL
);
82 size_t line_len
= strlen(openfile
->current
->data
+
85 assert(openfile
->current_x
< strlen(openfile
->current
->data
));
88 orig_lenpt
= strlenpt(openfile
->current
->data
);
90 /* Let's get dangerous. */
91 charmove(&openfile
->current
->data
[openfile
->current_x
],
92 &openfile
->current
->data
[openfile
->current_x
+
93 char_buf_len
], line_len
- char_buf_len
+ 1);
95 null_at(&openfile
->current
->data
, openfile
->current_x
+
96 line_len
- char_buf_len
);
98 if (openfile
->mark_set
&& openfile
->mark_begin
==
99 openfile
->current
&& openfile
->current_x
<
100 openfile
->mark_begin_x
)
101 openfile
->mark_begin_x
-= char_buf_len
;
104 } else if (openfile
->current
!= openfile
->filebot
) {
105 filestruct
*foo
= openfile
->current
->next
;
107 assert(openfile
->current_x
== strlen(openfile
->current
->data
));
109 /* If we're deleting at the end of a line, we need to call
111 if (openfile
->current
->data
[openfile
->current_x
] == '\0')
112 edit_refresh_needed
= TRUE
;
114 openfile
->current
->data
= charealloc(openfile
->current
->data
,
115 openfile
->current_x
+ strlen(foo
->data
) + 1);
116 strcpy(openfile
->current
->data
+ openfile
->current_x
,
119 if (openfile
->mark_set
&& openfile
->mark_begin
==
120 openfile
->current
->next
) {
121 openfile
->mark_begin
= openfile
->current
;
122 openfile
->mark_begin_x
+= openfile
->current_x
;
125 if (openfile
->filebot
== foo
)
126 openfile
->filebot
= openfile
->current
;
130 renumber(openfile
->current
);
133 /* If the NO_NEWLINES flag isn't set, and text has been added to
134 * the magicline as a result of deleting at the end of the line
135 * before filebot, add a new magicline. */
136 if (!ISSET(NO_NEWLINES
) && openfile
->current
==
137 openfile
->filebot
&& openfile
->current
->data
[0] != '\0')
142 if (ISSET(SOFTWRAP
) && edit_refresh_needed
== FALSE
)
143 if (strlenpt(openfile
->current
->data
) / COLS
!= orig_lenpt
/ COLS
)
144 edit_refresh_needed
= TRUE
;
148 if (edit_refresh_needed
== FALSE
)
149 update_line(openfile
->current
, openfile
->current_x
);
152 /* Backspace over one character. That is, move the cursor left one
153 * character, and then delete the character under the cursor. */
154 void do_backspace(void)
156 if (openfile
->current
!= openfile
->fileage
||
157 openfile
->current_x
> 0) {
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. */
168 if (ISSET(TABS_TO_SPACES
)) {
170 size_t output_len
= 0, new_pww
= xplustabs();
175 } while (new_pww
% tabsize
!= 0);
177 output
= charalloc(output_len
+ 1);
179 charset(output
, ' ', output_len
);
180 output
[output_len
] = '\0';
182 do_output(output
, output_len
, TRUE
);
187 do_output((char *) "\t", 1, TRUE
);
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. */
199 void do_indent(ssize_t cols
)
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
210 filestruct
*top
, *bot
, *f
;
213 assert(openfile
->current
!= NULL
&& openfile
->current
->data
!= NULL
);
215 /* If cols is zero, get out. */
219 /* If cols is negative, make it positive and set unindent to
224 /* Otherwise, we're indenting, in which case the file will always be
225 * modified, so set indent_changed to TRUE. */
227 indent_changed
= TRUE
;
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. */
235 top
= openfile
->current
;
240 /* Set up the text we'll be using as indentation. */
241 line_indent
= charalloc(cols
+ 1);
243 if (ISSET(TABS_TO_SPACES
)) {
244 /* Set the indentation to cols spaces. */
245 charset(line_indent
, ' ', cols
);
246 line_indent_len
= cols
;
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
;
253 charset(line_indent
, '\t', num_tabs
);
254 charset(line_indent
+ num_tabs
, ' ', num_spaces
);
256 line_indent_len
= num_tabs
+ num_spaces
;
259 line_indent
[line_indent_len
] = '\0';
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
);
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
;
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
;
282 if (f
== openfile
->current
&& openfile
->current_x
>=
284 openfile
->current_x
+= line_indent_len
;
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
)
291 size_t indent_col
= strnlenpt(f
->data
, indent_len
);
292 /* The length in columns of the indentation on this
295 if (cols
<= indent_col
) {
296 size_t indent_new
= actual_x(f
->data
, indent_col
-
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. */
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
;
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
;
318 openfile
->mark_begin_x
-= indent_shift
;
321 if (f
== openfile
->current
&& openfile
->current_x
>
323 if (openfile
->current_x
<= indent_len
)
324 openfile
->current_x
= indent_new
;
326 openfile
->current_x
-= indent_shift
;
329 /* We've unindented, so set indent_changed to TRUE. */
331 indent_changed
= TRUE
;
340 if (indent_changed
) {
341 /* Mark the file as modified. */
344 /* Update the screen. */
345 edit_refresh_needed
= TRUE
;
349 /* Indent the current line, or all lines covered by the mark if the mark
350 * is on, tabsize columns. */
351 void do_indent_void(void)
356 /* Unindent the current line, or all lines covered by the mark if the
357 * mark is on, tabsize columns. */
358 void do_unindent(void)
363 /* undo a cut, or re-do an uncut */
364 void undo_cut(undo
*u
)
366 /* If we cut the magicline may was well not crash :/ */
370 cutbuffer
= copy_filestruct(u
->cutbuffer
);
372 /* Compute cutbottom for the uncut using out copy */
373 for (cutbottom
= cutbuffer
; cutbottom
->next
!= NULL
; cutbottom
= cutbottom
->next
)
376 /* Get to where we need to uncut from */
377 if (u
->mark_set
&& u
->mark_begin_lineno
< u
->lineno
)
378 do_gotolinecolumn(u
->mark_begin_lineno
, u
->mark_begin_x
+1, FALSE
, FALSE
, FALSE
, FALSE
);
380 do_gotolinecolumn(u
->lineno
, u
->begin
+1, FALSE
, FALSE
, FALSE
, FALSE
);
382 copy_from_filestruct(cutbuffer
, cutbottom
);
383 free_filestruct(cutbuffer
);
388 /* Re-do a cut, or undo an uncut */
389 void redo_cut(undo
*u
) {
393 /* If we cut the magicline may was well not crash :/ */
397 do_gotolinecolumn(u
->lineno
, u
->begin
+1, FALSE
, FALSE
, FALSE
, FALSE
);
398 openfile
->mark_set
= u
->mark_set
;
403 /* Move ahead the same # lines we had if a marked cut */
405 for (i
= 1, t
= openfile
->fileage
; i
!= u
->mark_begin_lineno
; i
++)
407 openfile
->mark_begin
= t
;
408 } else if (!u
->to_end
) {
409 /* Here we have a regular old potentially multi-line ^K cut. We'll
410 need to trick nano into thinking it's a marked cut to cut more
411 than one line again */
412 for (c
= u
->cutbuffer
, t
= openfile
->current
; c
->next
!= NULL
&& t
->next
!= NULL
; ) {
415 fprintf(stderr
, "Advancing, lineno = %lu, data = \"%s\"\n", (unsigned long) t
->lineno
, t
->data
);
420 openfile
->mark_begin
= t
;
421 openfile
->mark_begin_x
= 0;
422 openfile
->mark_set
= TRUE
;
425 openfile
->mark_begin_x
= u
->mark_begin_x
;
426 do_cut_text(FALSE
, u
->to_end
, TRUE
);
427 openfile
->mark_set
= FALSE
;
428 openfile
->mark_begin
= NULL
;
429 openfile
->mark_begin_x
= 0;
430 edit_refresh_needed
= TRUE
;
433 /* Undo the last thing(s) we did */
436 undo
*u
= openfile
->current_undo
;
437 filestruct
*f
= openfile
->current
, *t
;
439 char *undidmsg
, *data
;
440 filestruct
*oldcutbuffer
= cutbuffer
, *oldcutbottom
= cutbottom
;
443 statusbar(_("Nothing in undo buffer!"));
448 if (u
->lineno
<= f
->lineno
)
449 for (; f
->prev
!= NULL
&& f
->lineno
!= u
->lineno
; f
= f
->prev
)
452 for (; f
->next
!= NULL
&& f
->lineno
!= u
->lineno
; f
= f
->next
)
454 if (f
->lineno
!= u
->lineno
) {
455 statusbar(_("Internal error: can't match line %d. Please save your work"), u
->lineno
);
459 fprintf(stderr
, "data we're about to undo = \"%s\"\n", f
->data
);
460 fprintf(stderr
, "Undo running for type %d\n", u
->type
);
463 openfile
->current_x
= u
->begin
;
466 undidmsg
= _("text add");
467 len
= strlen(f
->data
) - strlen(u
->strdata
) + 1;
468 data
= charalloc(len
);
469 strncpy(data
, f
->data
, u
->begin
);
470 strcpy(&data
[u
->begin
], &f
->data
[u
->begin
+ strlen(u
->strdata
)]);
475 undidmsg
= _("text delete");
476 len
= strlen(f
->data
) + strlen(u
->strdata
) + 1;
477 data
= charalloc(len
);
479 strncpy(data
, f
->data
, u
->begin
);
480 strcpy(&data
[u
->begin
], u
->strdata
);
481 strcpy(&data
[u
->begin
+ strlen(u
->strdata
)], &f
->data
[u
->begin
]);
484 if (u
->xflags
== UNDO_DEL_BACKSPACE
)
485 openfile
->current_x
+= strlen(u
->strdata
);
487 #ifndef DISABLE_WRAPPING
489 undidmsg
= _("line wrap");
490 f
->data
= (char *) nrealloc(f
->data
, strlen(f
->data
) + strlen(u
->strdata
) + 1);
491 strcpy(&f
->data
[strlen(f
->data
) - 1], u
->strdata
);
492 if (u
->strdata2
!= NULL
)
493 f
->next
->data
= mallocstrcpy(f
->next
->data
, u
->strdata2
);
495 filestruct
*foo
= openfile
->current
->next
;
501 #endif /* DISABLE_WRAPPING */
503 undidmsg
= _("line join");
504 t
= make_new_node(f
);
505 t
->data
= mallocstrcpy(NULL
, u
->strdata
);
506 data
= mallocstrncpy(NULL
, f
->data
, u
->begin
);
507 data
[u
->begin
] = '\0';
510 splice_node(f
, t
, f
->next
);
514 undidmsg
= _("text cut");
518 undidmsg
= _("text uncut");
522 undidmsg
= _("line break");
524 filestruct
*foo
= f
->next
;
525 f
->data
= (char *) nrealloc(f
->data
, strlen(f
->data
) + strlen(f
->next
->data
) + 1);
526 strcat(f
->data
, f
->next
->data
);
532 undidmsg
= _("text insert");
535 /* When we updated mark_begin_lineno in update_undo, it was effectively how many line
536 were inserted due to being partitioned before read_file was called. So we
537 add its value here */
538 openfile
->mark_begin
= fsfromline(u
->lineno
+ u
->mark_begin_lineno
- 1);
539 openfile
->mark_begin_x
= 0;
540 openfile
->mark_set
= TRUE
;
541 do_gotolinecolumn(u
->lineno
, u
->begin
+1, FALSE
, FALSE
, FALSE
, FALSE
);
543 u
->cutbuffer
= cutbuffer
;
544 u
->cutbottom
= cutbottom
;
545 cutbuffer
= oldcutbuffer
;
546 cutbottom
= oldcutbottom
;
547 openfile
->mark_set
= FALSE
;
550 undidmsg
= _("text replace");
552 u
->strdata
= f
->data
;
557 undidmsg
= _("Internal error: unknown type. Please save your work");
562 do_gotolinecolumn(u
->lineno
, u
->begin
, FALSE
, FALSE
, FALSE
, TRUE
);
563 statusbar(_("Undid action (%s)"), undidmsg
);
564 openfile
->current_undo
= openfile
->current_undo
->next
;
565 openfile
->last_action
= OTHER
;
570 undo
*u
= openfile
->undotop
;
571 filestruct
*f
= openfile
->current
;
573 char *undidmsg
, *data
;
575 for (; u
!= NULL
&& u
->next
!= openfile
->current_undo
; u
= u
->next
)
578 statusbar(_("Nothing to re-do!"));
581 if (u
->next
!= openfile
->current_undo
) {
582 statusbar(_("Internal error: Redo setup failed. Please save your work"));
586 if (u
->lineno
<= f
->lineno
)
587 for (; f
->prev
!= NULL
&& f
->lineno
!= u
->lineno
; f
= f
->prev
)
590 for (; f
->next
!= NULL
&& f
->lineno
!= u
->lineno
; f
= f
->next
)
592 if (f
->lineno
!= u
->lineno
) {
593 statusbar(_("Internal error: can't match line %d. Please save your work"), u
->lineno
);
597 fprintf(stderr
, "data we're about to redo = \"%s\"\n", f
->data
);
598 fprintf(stderr
, "Redo running for type %d\n", u
->type
);
603 undidmsg
= _("text add");
604 len
= strlen(f
->data
) + strlen(u
->strdata
) + 1;
605 data
= charalloc(len
);
606 strncpy(data
, f
->data
, u
->begin
);
607 strcpy(&data
[u
->begin
], u
->strdata
);
608 strcpy(&data
[u
->begin
+ strlen(u
->strdata
)], &f
->data
[u
->begin
]);
613 undidmsg
= _("text delete");
614 len
= strlen(f
->data
) + strlen(u
->strdata
) + 1;
615 data
= charalloc(len
);
616 strncpy(data
, f
->data
, u
->begin
);
617 strcpy(&data
[u
->begin
], &f
->data
[u
->begin
+ strlen(u
->strdata
)]);
622 undidmsg
= _("line break");
623 do_gotolinecolumn(u
->lineno
, u
->begin
+1, FALSE
, FALSE
, FALSE
, FALSE
);
626 #ifndef DISABLE_WRAPPING
628 undidmsg
= _("line wrap");
629 if (u
->xflags
& UNDO_SPLIT_MADENEW
)
634 #endif /* DISABLE_WRAPPING */
636 undidmsg
= _("line join");
637 len
= strlen(f
->data
) + strlen(u
->strdata
+ 1);
638 data
= charalloc(len
);
639 strcpy(data
, f
->data
);
640 strcat(data
, u
->strdata
);
643 if (f
->next
!= NULL
) {
644 filestruct
*tmp
= f
->next
;
651 undidmsg
= _("text cut");
655 undidmsg
= _("text uncut");
659 undidmsg
= _("text replace");
661 u
->strdata
= f
->data
;
665 undidmsg
= _("text insert");
666 do_gotolinecolumn(u
->lineno
, u
->begin
+1, FALSE
, FALSE
, FALSE
, FALSE
);
667 copy_from_filestruct(u
->cutbuffer
, u
->cutbottom
);
668 openfile
->placewewant
= xplustabs();
671 undidmsg
= _("Internal error: unknown type. Please save your work");
675 do_gotolinecolumn(u
->lineno
, u
->begin
, FALSE
, FALSE
, FALSE
, TRUE
);
676 statusbar(_("Redid action (%s)"), undidmsg
);
678 openfile
->current_undo
= u
;
679 openfile
->last_action
= OTHER
;
682 #endif /* !NANO_TINY */
684 /* Someone hits Enter *gasp!* */
685 void do_enter(bool undoing
)
687 filestruct
*newnode
= make_new_node(openfile
->current
);
690 assert(openfile
->current
!= NULL
&& openfile
->current
->data
!= NULL
);
697 /* Do auto-indenting, like the neolithic Turbo Pascal editor. */
698 if (ISSET(AUTOINDENT
)) {
699 /* If we are breaking the line in the indentation, the new
700 * indentation should have only current_x characters, and
701 * current_x should not change. */
702 extra
= indent_length(openfile
->current
->data
);
703 if (extra
> openfile
->current_x
)
704 extra
= openfile
->current_x
;
707 newnode
->data
= charalloc(strlen(openfile
->current
->data
+
708 openfile
->current_x
) + extra
+ 1);
709 strcpy(&newnode
->data
[extra
], openfile
->current
->data
+
710 openfile
->current_x
);
712 if (ISSET(AUTOINDENT
)) {
713 strncpy(newnode
->data
, openfile
->current
->data
, extra
);
714 openfile
->totsize
+= mbstrlen(newnode
->data
);
717 null_at(&openfile
->current
->data
, openfile
->current_x
);
719 if (openfile
->mark_set
&& openfile
->current
==
720 openfile
->mark_begin
&& openfile
->current_x
<
721 openfile
->mark_begin_x
) {
722 openfile
->mark_begin
= newnode
;
723 openfile
->mark_begin_x
+= extra
- openfile
->current_x
;
726 openfile
->current_x
= extra
;
728 if (openfile
->current
== openfile
->filebot
)
729 openfile
->filebot
= newnode
;
730 splice_node(openfile
->current
, newnode
,
731 openfile
->current
->next
);
733 renumber(openfile
->current
);
734 openfile
->current
= newnode
;
739 openfile
->placewewant
= xplustabs();
741 edit_refresh_needed
= TRUE
;
745 /* Send a SIGKILL (unconditional kill) to the forked process in
746 * execute_command(). */
747 RETSIGTYPE
cancel_command(int signal
)
749 if (kill(pid
, SIGKILL
) == -1)
753 /* Execute command in a shell. Return TRUE on success. */
754 bool execute_command(const char *command
)
759 struct sigaction oldaction
, newaction
;
760 /* Original and temporary handlers for SIGINT. */
761 bool sig_failed
= FALSE
;
762 /* Did sigaction() fail without changing the signal handlers? */
764 /* Make our pipes. */
765 if (pipe(fd
) == -1) {
766 statusbar(_("Could not pipe"));
770 /* Check $SHELL for the shell to use. If it isn't set, use
771 * /bin/sh. Note that $SHELL should contain only a path, with no
773 shellenv
= getenv("SHELL");
774 if (shellenv
== NULL
)
775 shellenv
= (char *) "/bin/sh";
778 if ((pid
= fork()) == 0) {
780 dup2(fd
[1], fileno(stdout
));
781 dup2(fd
[1], fileno(stderr
));
783 /* If execl() returns at all, there was an error. */
784 execl(shellenv
, tail(shellenv
), "-c", command
, NULL
);
788 /* Continue as parent. */
793 statusbar(_("Could not fork"));
797 /* Before we start reading the forked command's output, we set
798 * things up so that Ctrl-C will cancel the new process. */
800 /* Enable interpretation of the special control keys so that we get
801 * SIGINT when Ctrl-C is pressed. */
804 if (sigaction(SIGINT
, NULL
, &newaction
) == -1) {
806 nperror("sigaction");
808 newaction
.sa_handler
= cancel_command
;
809 if (sigaction(SIGINT
, &newaction
, &oldaction
) == -1) {
811 nperror("sigaction");
815 /* Note that now oldaction is the previous SIGINT signal handler,
816 * to be restored later. */
818 f
= fdopen(fd
[0], "rb");
822 read_file(f
, 0, "stdin", TRUE
, FALSE
);
824 if (wait(NULL
) == -1)
827 if (!sig_failed
&& sigaction(SIGINT
, &oldaction
, NULL
) == -1)
828 nperror("sigaction");
830 /* Restore the terminal to its previous state. In the process,
831 * disable interpretation of the special control keys so that we can
832 * use Ctrl-C for other things. */
838 /* Add a new undo struct to the top of the current pile */
839 void add_undo(undo_type current_action
)
843 openfilestruct
*fs
= openfile
;
844 static undo
*last_cutu
= NULL
; /* Last thing we cut to set up the undo for uncut */
845 ssize_t wrap_loc
; /* For calculating split beginning */
847 if (!ISSET(UNDOABLE
))
850 /* Ugh, if we were called while cutting not-to-end, non-marked and on the same lineno,
851 we need to abort here */
852 u
= fs
->current_undo
;
853 if (current_action
== CUT
&& u
&& u
->type
== CUT
854 && !u
->mark_set
&& u
->lineno
== fs
->current
->lineno
)
857 /* Blow away the old undo stack if we are starting from the middle */
858 while (fs
->undotop
!= NULL
&& fs
->undotop
!= fs
->current_undo
) {
859 undo
*u2
= fs
->undotop
;
860 fs
->undotop
= fs
->undotop
->next
;
861 if (u2
->strdata
!= NULL
)
864 free_filestruct(u2
->cutbuffer
);
868 /* Allocate and initialize a new undo type */
869 u
= (undo
*) nmalloc(sizeof(undo
));
870 u
->type
= current_action
;
871 u
->lineno
= fs
->current
->lineno
;
872 u
->begin
= fs
->current_x
;
873 u
->next
= fs
->undotop
;
875 fs
->current_undo
= u
;
881 u
->mark_begin_lineno
= 0;
887 /* We need to start copying data into the undo buffer or we wont be able
888 to restore it later */
891 data
[0] = fs
->current
->data
[fs
->current_x
];
896 if (u
->begin
!= strlen(fs
->current
->data
)) {
897 data
= mallocstrncpy(NULL
, &fs
->current
->data
[u
->begin
], 2);
902 /* Else purposely fall into unsplit code */
903 current_action
= u
->type
= UNSPLIT
;
905 if (fs
->current
->next
) {
906 data
= mallocstrcpy(NULL
, fs
->current
->next
->data
);
910 #ifndef DISABLE_WRAPPING
912 wrap_loc
= break_line(openfile
->current
->data
, fill
917 u
->strdata
= mallocstrcpy(NULL
, &openfile
->current
->data
[wrap_loc
]);
918 /* Don't both saving the next line if we're not prepending as a new line
921 u
->strdata2
= mallocstrcpy(NULL
, fs
->current
->next
->data
);
924 #endif /* DISABLE_WRAPPING */
927 data
= mallocstrcpy(NULL
, fs
->current
->data
);
931 u
->mark_set
= openfile
->mark_set
;
933 u
->mark_begin_lineno
= openfile
->mark_begin
->lineno
;
934 u
->mark_begin_x
= openfile
->mark_begin_x
;
936 u
->to_end
= (ISSET(CUT_TO_END
)) ? TRUE
: FALSE
;
941 statusbar(_("Internal error: can't setup uncut. Please save your work."));
942 else if (last_cutu
->type
== CUT
) {
943 u
->cutbuffer
= last_cutu
->cutbuffer
;
944 u
->cutbottom
= last_cutu
->cutbottom
;
950 statusbar(_("Internal error: unknown type. Please save your work."));
955 fprintf(stderr
, "fs->current->data = \"%s\", current_x = %lu, u->begin = %d, type = %d\n",
956 fs
->current
->data
, (unsigned long) fs
->current_x
, u
->begin
, current_action
);
957 fprintf(stderr
, "left add_undo...\n");
959 fs
->last_action
= current_action
;
962 /* Update an undo item, or determine whether a new one
963 is really needed and bounce the data to add_undo
964 instead. The latter functionality just feels
965 gimmicky and may just be more hassle than
966 it's worth, so it should be axed if needed. */
967 void update_undo(undo_type action
)
972 openfilestruct
*fs
= openfile
;
974 if (!ISSET(UNDOABLE
))
978 fprintf(stderr
, "action = %d, fs->last_action = %d, openfile->current->lineno = %lu",
979 action
, fs
->last_action
, (unsigned long) openfile
->current
->lineno
);
980 if (fs
->current_undo
)
981 fprintf(stderr
, "fs->current_undo->lineno = %lu\n", (unsigned long) fs
->current_undo
->lineno
);
983 fprintf(stderr
, "\n");
986 /* Change to an add if we're not using the same undo struct
987 that we should be using */
988 if (action
!= fs
->last_action
989 || (action
!= CUT
&& action
!= INSERT
&& action
!= SPLIT
990 && openfile
->current
->lineno
!= fs
->current_undo
->lineno
)) {
995 assert(fs
->undotop
!= NULL
);
1001 fprintf(stderr
, "fs->current->data = \"%s\", current_x = %lu, u->begin = %d\n",
1002 fs
->current
->data
, (unsigned long) fs
->current_x
, u
->begin
);
1004 len
= strlen(u
->strdata
) + 2;
1005 data
= (char *) nrealloc((void *) u
->strdata
, len
* sizeof(char *));
1006 data
[len
-2] = fs
->current
->data
[fs
->current_x
];
1008 u
->strdata
= (char *) data
;
1010 fprintf(stderr
, "current undo data now \"%s\"\n", u
->strdata
);
1014 len
= strlen(u
->strdata
) + 2;
1016 if (fs
->current_x
== u
->begin
) {
1017 /* They're deleting */
1019 u
->xflags
= UNDO_DEL_DEL
;
1020 else if (u
->xflags
!= UNDO_DEL_DEL
) {
1024 data
= charalloc(len
);
1025 strcpy(data
, u
->strdata
);
1026 data
[len
-2] = fs
->current
->data
[fs
->current_x
];;
1030 } else if (fs
->current_x
== u
->begin
- 1) {
1031 /* They're backspacing */
1033 u
->xflags
= UNDO_DEL_BACKSPACE
;
1034 else if (u
->xflags
!= UNDO_DEL_BACKSPACE
) {
1038 data
= charalloc(len
);
1039 data
[0] = fs
->current
->data
[fs
->current_x
];
1040 strcpy(&data
[1], u
->strdata
);
1045 /* They deleted something else on the line */
1050 fprintf(stderr
, "current undo data now \"%s\"\nu->begin = %d\n", u
->strdata
, u
->begin
);
1058 u
->cutbuffer
= copy_filestruct(cutbuffer
);
1059 /* Compute cutbottom for the uncut using out copy */
1060 for (u
->cutbottom
= u
->cutbuffer
; u
->cutbottom
->next
!= NULL
; u
->cutbottom
= u
->cutbottom
->next
)
1068 u
->mark_begin_lineno
= openfile
->current
->lineno
;
1070 #ifndef DISABLE_WRAPPING
1072 /* This will only be called if we made a completely new line,
1073 and as such we should note that so we can destroy it later */
1074 u
->xflags
= UNDO_SPLIT_MADENEW
;
1076 #endif /* DISABLE_WRAPPING */
1078 /* These cases are handled by the earlier check for a new line and action */
1085 fprintf(stderr
, "Done in udpate_undo (type was %d)\n", action
);
1087 if (fs
->last_action
!= action
) {
1089 fprintf(stderr
, "Starting add_undo for new action as it does not match last_action\n");
1093 fs
->last_action
= action
;
1096 #endif /* !NANO_TINY */
1098 #ifndef DISABLE_WRAPPING
1099 /* Unset the prepend_wrap flag. We need to do this as soon as we do
1100 * something other than type text. */
1101 void wrap_reset(void)
1103 prepend_wrap
= FALSE
;
1106 /* We wrap the given line. Precondition: we assume the cursor has been
1107 * moved forward since the last typed character. Return TRUE if we
1108 * wrapped, and FALSE otherwise. */
1109 bool do_wrap(filestruct
*line
, bool undoing
)
1112 /* The length of the line we wrap. */
1114 /* The index of line->data where we wrap. */
1116 const char *indent_string
= NULL
;
1117 /* Indentation to prepend to the new line. */
1118 size_t indent_len
= 0;
1119 /* The length of indent_string. */
1121 const char *after_break
;
1122 /* The text after the wrap point. */
1123 size_t after_break_len
;
1124 /* The length of after_break. */
1125 bool prepending
= FALSE
;
1126 /* Do we prepend to the next line? */
1127 const char *next_line
= NULL
;
1128 /* The next line, minus indentation. */
1129 size_t next_line_len
= 0;
1130 /* The length of next_line. */
1131 char *new_line
= NULL
;
1132 /* The line we create. */
1133 size_t new_line_len
= 0;
1134 /* The eventual length of new_line. */
1136 /* There are three steps. First, we decide where to wrap. Then, we
1137 * create the new wrap line. Finally, we clean up. */
1139 /* Step 1, finding where to wrap. We are going to add a new line
1140 * after a blank character. In this step, we call break_line() to
1141 * get the location of the last blank we can break the line at, and
1142 * set wrap_loc to the location of the character after it, so that
1143 * the blank is preserved at the end of the line.
1145 * If there is no legal wrap point, or we reach the last character
1146 * of the line while trying to find one, we should return without
1147 * wrapping. Note that if autoindent is turned on, we don't break
1148 * at the end of it! */
1149 assert(line
!= NULL
&& line
->data
!= NULL
);
1151 /* Save the length of the line. */
1152 line_len
= strlen(line
->data
);
1154 /* Find the last blank where we can break the line. */
1155 wrap_loc
= break_line(line
->data
, fill
1156 #ifndef DISABLE_HELP
1161 /* If we couldn't break the line, or we've reached the end of it, we
1163 if (wrap_loc
== -1 || line
->data
[wrap_loc
] == '\0')
1166 /* Otherwise, move forward to the character just after the blank. */
1167 wrap_loc
+= move_mbright(line
->data
+ wrap_loc
, 0);
1169 /* If we've reached the end of the line, we don't wrap. */
1170 if (line
->data
[wrap_loc
] == '\0')
1177 /* If autoindent is turned on, and we're on the character just after
1178 * the indentation, we don't wrap. */
1179 if (ISSET(AUTOINDENT
)) {
1180 /* Get the indentation of this line. */
1181 indent_string
= line
->data
;
1182 indent_len
= indent_length(indent_string
);
1184 if (wrap_loc
== indent_len
)
1189 /* Step 2, making the new wrap line. It will consist of indentation
1190 * followed by the text after the wrap point, optionally followed by
1191 * a space (if the text after the wrap point doesn't end in a blank)
1192 * and the text of the next line, if they can fit without wrapping,
1193 * the next line exists, and the prepend_wrap flag is set. */
1195 /* after_break is the text that will be wrapped to the next line. */
1196 after_break
= line
->data
+ wrap_loc
;
1197 after_break_len
= line_len
- wrap_loc
;
1199 assert(strlen(after_break
) == after_break_len
);
1201 /* We prepend the wrapped text to the next line, if the prepend_wrap
1202 * flag is set, there is a next line, and prepending would not make
1203 * the line too long. */
1204 if (prepend_wrap
&& line
!= openfile
->filebot
) {
1205 const char *end
= after_break
+ move_mbleft(after_break
,
1208 /* If after_break doesn't end in a blank, make sure it ends in a
1210 if (!is_blank_mbchar(end
)) {
1212 line
->data
= charealloc(line
->data
, line_len
+ 1);
1213 line
->data
[line_len
- 1] = ' ';
1214 line
->data
[line_len
] = '\0';
1215 after_break
= line
->data
+ wrap_loc
;
1217 openfile
->totsize
++;
1220 next_line
= line
->next
->data
;
1221 next_line_len
= strlen(next_line
);
1223 if (after_break_len
+ next_line_len
<= fill
) {
1225 new_line_len
+= next_line_len
;
1229 /* new_line_len is now the length of the text that will be wrapped
1230 * to the next line, plus (if we're prepending to it) the length of
1231 * the text of the next line. */
1232 new_line_len
+= after_break_len
;
1235 if (ISSET(AUTOINDENT
)) {
1237 /* If we're prepending, the indentation will come from the
1239 indent_string
= next_line
;
1240 indent_len
= indent_length(indent_string
);
1241 next_line
+= indent_len
;
1243 /* Otherwise, it will come from this line, in which case
1244 * we should increase new_line_len to make room for it. */
1245 new_line_len
+= indent_len
;
1246 openfile
->totsize
+= mbstrnlen(indent_string
, indent_len
);
1251 /* Now we allocate the new line and copy the text into it. */
1252 new_line
= charalloc(new_line_len
+ 1);
1256 if (ISSET(AUTOINDENT
)) {
1257 /* Copy the indentation. */
1258 strncpy(new_line
, indent_string
, indent_len
);
1259 new_line
[indent_len
] = '\0';
1260 new_line_len
+= indent_len
;
1264 /* Copy all the text after the wrap point of the current line. */
1265 strcat(new_line
, after_break
);
1267 /* Break the current line at the wrap point. */
1268 null_at(&line
->data
, wrap_loc
);
1273 /* If we're prepending, copy the text from the next line, minus
1274 * the indentation that we already copied above. */
1275 strcat(new_line
, next_line
);
1277 free(line
->next
->data
);
1278 line
->next
->data
= new_line
;
1280 /* If the NO_NEWLINES flag isn't set, and text has been added to
1281 * the magicline, make a new magicline. */
1282 if (!ISSET(NO_NEWLINES
) && openfile
->filebot
->data
[0] != '\0')
1285 /* Otherwise, make a new line and copy the text after where we
1286 * broke this line to the beginning of the new line. */
1287 splice_node(openfile
->current
, make_new_node(openfile
->current
),
1288 openfile
->current
->next
);
1290 /* If the current line is the last line of the file, move the
1291 * last line of the file down to the next line. */
1292 if (openfile
->filebot
== openfile
->current
)
1293 openfile
->filebot
= openfile
->current
->next
;
1295 openfile
->current
->next
->data
= new_line
;
1297 openfile
->totsize
++;
1300 /* Step 3, clean up. Reposition the cursor and mark, and do some
1301 * other sundry things. */
1303 /* Set the prepend_wrap flag, so that later wraps of this line will
1304 * be prepended to the next line. */
1305 prepend_wrap
= TRUE
;
1307 /* Each line knows its number. We recalculate these if we inserted
1312 /* If the cursor was after the break point, we must move it. We
1313 * also clear the prepend_wrap flag in this case. */
1314 if (openfile
->current_x
> wrap_loc
) {
1315 prepend_wrap
= FALSE
;
1317 openfile
->current
= openfile
->current
->next
;
1318 openfile
->current_x
-= wrap_loc
1323 openfile
->placewewant
= xplustabs();
1327 /* If the mark was on this line after the wrap point, we move it
1328 * down. If it was on the next line and we prepended to that line,
1329 * we move it right. */
1330 if (openfile
->mark_set
) {
1331 if (openfile
->mark_begin
== line
&& openfile
->mark_begin_x
>
1333 openfile
->mark_begin
= line
->next
;
1334 openfile
->mark_begin_x
-= wrap_loc
- indent_len
+ 1;
1335 } else if (prepending
&& openfile
->mark_begin
== line
->next
)
1336 openfile
->mark_begin_x
+= after_break_len
;
1342 #endif /* !DISABLE_WRAPPING */
1344 #if !defined(DISABLE_HELP) || !defined(DISABLE_WRAPJUSTIFY)
1345 /* We are trying to break a chunk off line. We find the last blank such
1346 * that the display length to there is at most (goal + 1). If there is
1347 * no such blank, then we find the first blank. We then take the last
1348 * blank in that group of blanks. The terminating '\0' counts as a
1349 * blank, as does a '\n' if newline is TRUE. */
1350 ssize_t
break_line(const char *line
, ssize_t goal
1351 #ifndef DISABLE_HELP
1356 ssize_t blank_loc
= -1;
1357 /* Current tentative return value. Index of the last blank we
1358 * found with short enough display width. */
1359 ssize_t cur_loc
= 0;
1360 /* Current index in line. */
1362 /* Current column position in line. */
1365 assert(line
!= NULL
);
1367 while (*line
!= '\0' && goal
>= cur_pos
) {
1368 line_len
= parse_mbchar(line
, NULL
, &cur_pos
);
1370 if (is_blank_mbchar(line
)
1371 #ifndef DISABLE_HELP
1372 || (newln
&& *line
== '\n')
1375 blank_loc
= cur_loc
;
1377 #ifndef DISABLE_HELP
1378 if (newln
&& *line
== '\n')
1384 cur_loc
+= line_len
;
1387 if (goal
>= cur_pos
)
1388 /* In fact, the whole line displays shorter than goal. */
1391 #ifndef DISABLE_HELP
1392 if (newln
&& blank_loc
<= 0) {
1393 /* If blank was not found or was found only first character,
1394 * force line break. */
1395 cur_loc
-= line_len
;
1400 if (blank_loc
== -1) {
1401 /* No blank was found that was short enough. */
1402 bool found_blank
= FALSE
;
1403 ssize_t found_blank_loc
= 0;
1405 while (*line
!= '\0') {
1406 line_len
= parse_mbchar(line
, NULL
, NULL
);
1408 if (is_blank_mbchar(line
)
1409 #ifndef DISABLE_HELP
1410 || (newln
&& *line
== '\n')
1415 found_blank_loc
= cur_loc
;
1416 } else if (found_blank
)
1417 return found_blank_loc
;
1420 cur_loc
+= line_len
;
1426 /* Move to the last blank after blank_loc, if there is one. */
1429 line_len
= parse_mbchar(line
, NULL
, NULL
);
1432 while (*line
!= '\0' && (is_blank_mbchar(line
)
1433 #ifndef DISABLE_HELP
1434 || (newln
&& *line
== '\n')
1437 #ifndef DISABLE_HELP
1438 if (newln
&& *line
== '\n')
1442 line_len
= parse_mbchar(line
, NULL
, NULL
);
1445 blank_loc
+= line_len
;
1450 #endif /* !DISABLE_HELP || !DISABLE_WRAPJUSTIFY */
1452 #if !defined(NANO_TINY) || !defined(DISABLE_JUSTIFY)
1453 /* The "indentation" of a line is the whitespace between the quote part
1454 * and the non-whitespace of the line. */
1455 size_t indent_length(const char *line
)
1461 assert(line
!= NULL
);
1463 blank_mb
= charalloc(mb_cur_max());
1465 while (*line
!= '\0') {
1466 blank_mb_len
= parse_mbchar(line
, blank_mb
, NULL
);
1468 if (!is_blank_mbchar(blank_mb
))
1471 line
+= blank_mb_len
;
1472 len
+= blank_mb_len
;
1479 #endif /* !NANO_TINY || !DISABLE_JUSTIFY */
1481 #ifndef DISABLE_JUSTIFY
1482 /* justify_format() replaces blanks with spaces and multiple spaces by 1
1483 * (except it maintains up to 2 after a character in punct optionally
1484 * followed by a character in brackets, and removes all from the end).
1486 * justify_format() might make paragraph->data shorter, and change the
1487 * actual pointer with null_at().
1489 * justify_format() will not look at the first skip characters of
1490 * paragraph. skip should be at most strlen(paragraph->data). The
1491 * character at paragraph[skip + 1] must not be blank. */
1492 void justify_format(filestruct
*paragraph
, size_t skip
)
1494 char *end
, *new_end
, *new_paragraph_data
;
1497 size_t mark_shift
= 0;
1500 /* These four asserts are assumptions about the input data. */
1501 assert(paragraph
!= NULL
);
1502 assert(paragraph
->data
!= NULL
);
1503 assert(skip
< strlen(paragraph
->data
));
1504 assert(!is_blank_mbchar(paragraph
->data
+ skip
));
1506 end
= paragraph
->data
+ skip
;
1507 new_paragraph_data
= charalloc(strlen(paragraph
->data
) + 1);
1508 strncpy(new_paragraph_data
, paragraph
->data
, skip
);
1509 new_end
= new_paragraph_data
+ skip
;
1511 while (*end
!= '\0') {
1514 /* If this character is blank, change it to a space if
1515 * necessary, and skip over all blanks after it. */
1516 if (is_blank_mbchar(end
)) {
1517 end_len
= parse_mbchar(end
, NULL
, NULL
);
1523 while (*end
!= '\0' && is_blank_mbchar(end
)) {
1524 end_len
= parse_mbchar(end
, NULL
, NULL
);
1530 /* Keep track of the change in the current line. */
1531 if (openfile
->mark_set
&& openfile
->mark_begin
==
1532 paragraph
&& openfile
->mark_begin_x
>= end
-
1534 mark_shift
+= end_len
;
1537 /* If this character is punctuation optionally followed by a
1538 * bracket and then followed by blanks, change no more than two
1539 * of the blanks to spaces if necessary, and skip over all
1540 * blanks after them. */
1541 } else if (mbstrchr(punct
, end
) != NULL
) {
1542 end_len
= parse_mbchar(end
, NULL
, NULL
);
1544 while (end_len
> 0) {
1551 if (*end
!= '\0' && mbstrchr(brackets
, end
) != NULL
) {
1552 end_len
= parse_mbchar(end
, NULL
, NULL
);
1554 while (end_len
> 0) {
1562 if (*end
!= '\0' && is_blank_mbchar(end
)) {
1563 end_len
= parse_mbchar(end
, NULL
, NULL
);
1570 if (*end
!= '\0' && is_blank_mbchar(end
)) {
1571 end_len
= parse_mbchar(end
, NULL
, NULL
);
1578 while (*end
!= '\0' && is_blank_mbchar(end
)) {
1579 end_len
= parse_mbchar(end
, NULL
, NULL
);
1585 /* Keep track of the change in the current line. */
1586 if (openfile
->mark_set
&& openfile
->mark_begin
==
1587 paragraph
&& openfile
->mark_begin_x
>= end
-
1589 mark_shift
+= end_len
;
1592 /* If this character is neither blank nor punctuation, leave it
1595 end_len
= parse_mbchar(end
, NULL
, NULL
);
1597 while (end_len
> 0) {
1606 assert(*end
== '\0');
1610 /* If there are spaces at the end of the line, remove them. */
1611 while (new_end
> new_paragraph_data
+ skip
&&
1612 *(new_end
- 1) == ' ') {
1618 openfile
->totsize
-= shift
;
1619 null_at(&new_paragraph_data
, new_end
- new_paragraph_data
);
1620 free(paragraph
->data
);
1621 paragraph
->data
= new_paragraph_data
;
1624 /* Adjust the mark coordinates to compensate for the change in
1625 * the current line. */
1626 if (openfile
->mark_set
&& openfile
->mark_begin
== paragraph
) {
1627 openfile
->mark_begin_x
-= mark_shift
;
1628 if (openfile
->mark_begin_x
> new_end
- new_paragraph_data
)
1629 openfile
->mark_begin_x
= new_end
- new_paragraph_data
;
1633 free(new_paragraph_data
);
1636 /* The "quote part" of a line is the largest initial substring matching
1637 * the quote string. This function returns the length of the quote part
1638 * of the given line.
1640 * Note that if !HAVE_REGEX_H then we match concatenated copies of
1642 size_t quote_length(const char *line
)
1646 int rc
= regexec("ereg
, line
, 1, &matches
, 0);
1648 if (rc
== REG_NOMATCH
|| matches
.rm_so
== (regoff_t
)-1)
1650 /* matches.rm_so should be 0, since the quote string should start
1651 * with the caret ^. */
1652 return matches
.rm_eo
;
1653 #else /* !HAVE_REGEX_H */
1656 /* Compute quote depth level. */
1657 while (strncmp(line
+ qdepth
, quotestr
, quotelen
) == 0)
1660 #endif /* !HAVE_REGEX_H */
1663 /* a_line and b_line are lines of text. The quotation part of a_line is
1664 * the first a_quote characters. Check that the quotation part of
1665 * b_line is the same. */
1666 bool quotes_match(const char *a_line
, size_t a_quote
, const char
1669 /* Here is the assumption about a_quote. */
1670 assert(a_quote
== quote_length(a_line
));
1672 return (a_quote
== quote_length(b_line
) &&
1673 strncmp(a_line
, b_line
, a_quote
) == 0);
1676 /* We assume a_line and b_line have no quote part. Then, we return
1677 * whether b_line could follow a_line in a paragraph. */
1678 bool indents_match(const char *a_line
, size_t a_indent
, const char
1679 *b_line
, size_t b_indent
)
1681 assert(a_indent
== indent_length(a_line
));
1682 assert(b_indent
== indent_length(b_line
));
1684 return (b_indent
<= a_indent
&&
1685 strncmp(a_line
, b_line
, b_indent
) == 0);
1688 /* Is foo the beginning of a paragraph?
1690 * A line of text consists of a "quote part", followed by an
1691 * "indentation part", followed by text. The functions quote_length()
1692 * and indent_length() calculate these parts.
1694 * A line is "part of a paragraph" if it has a part not in the quote
1695 * part or the indentation.
1697 * A line is "the beginning of a paragraph" if it is part of a
1699 * 1) it is the top line of the file, or
1700 * 2) the line above it is not part of a paragraph, or
1701 * 3) the line above it does not have precisely the same quote
1703 * 4) the indentation of this line is not an initial substring of
1704 * the indentation of the previous line, or
1705 * 5) this line has no quote part and some indentation, and
1706 * autoindent isn't turned on.
1707 * The reason for number 5) is that if autoindent isn't turned on,
1708 * then an indented line is expected to start a paragraph, as in
1709 * books. Thus, nano can justify an indented paragraph only if
1710 * autoindent is turned on. */
1711 bool begpar(const filestruct
*const foo
)
1713 size_t quote_len
, indent_len
, temp_id_len
;
1719 if (foo
== openfile
->fileage
)
1722 quote_len
= quote_length(foo
->data
);
1723 indent_len
= indent_length(foo
->data
+ quote_len
);
1725 /* Not part of a paragraph. */
1726 if (foo
->data
[quote_len
+ indent_len
] == '\0')
1730 if (!quotes_match(foo
->data
, quote_len
, foo
->prev
->data
))
1733 temp_id_len
= indent_length(foo
->prev
->data
+ quote_len
);
1735 /* Case 2) or 5) or 4). */
1736 if (foo
->prev
->data
[quote_len
+ temp_id_len
] == '\0' ||
1737 (quote_len
== 0 && indent_len
> 0
1739 && !ISSET(AUTOINDENT
)
1741 ) || !indents_match(foo
->prev
->data
+ quote_len
, temp_id_len
,
1742 foo
->data
+ quote_len
, indent_len
))
1748 /* Is foo inside a paragraph? */
1749 bool inpar(const filestruct
*const foo
)
1756 quote_len
= quote_length(foo
->data
);
1758 return (foo
->data
[quote_len
+ indent_length(foo
->data
+
1759 quote_len
)] != '\0');
1762 /* Move the next par_len lines, starting with first_line, into the
1763 * justify buffer, leaving copies of those lines in place. Assume that
1764 * par_len is greater than zero, and that there are enough lines after
1766 void backup_lines(filestruct
*first_line
, size_t par_len
)
1768 filestruct
*top
= first_line
;
1769 /* The top of the paragraph we're backing up. */
1770 filestruct
*bot
= first_line
;
1771 /* The bottom of the paragraph we're backing up. */
1773 /* Generic loop variable. */
1774 size_t current_x_save
= openfile
->current_x
;
1775 ssize_t fl_lineno_save
= first_line
->lineno
;
1776 ssize_t edittop_lineno_save
= openfile
->edittop
->lineno
;
1777 ssize_t current_lineno_save
= openfile
->current
->lineno
;
1779 bool old_mark_set
= openfile
->mark_set
;
1780 ssize_t mb_lineno_save
= 0;
1781 size_t mark_begin_x_save
= 0;
1784 mb_lineno_save
= openfile
->mark_begin
->lineno
;
1785 mark_begin_x_save
= openfile
->mark_begin_x
;
1789 /* par_len will be one greater than the number of lines between
1790 * current and filebot if filebot is the last line in the
1792 assert(par_len
> 0 && openfile
->current
->lineno
+ par_len
<=
1793 openfile
->filebot
->lineno
+ 1);
1795 /* Move bot down par_len lines to the line after the last line of
1796 * the paragraph, if there is one. */
1797 for (i
= par_len
; i
> 0 && bot
!= openfile
->filebot
; i
--)
1800 /* Move the paragraph from the current buffer's filestruct to the
1801 * justify buffer. */
1802 move_to_filestruct(&jusbuffer
, &jusbottom
, top
, 0, bot
,
1803 (i
== 1 && bot
== openfile
->filebot
) ? strlen(bot
->data
) : 0);
1805 /* Copy the paragraph back to the current buffer's filestruct from
1806 * the justify buffer. */
1807 copy_from_filestruct(jusbuffer
, jusbottom
);
1809 /* Move upward from the last line of the paragraph to the first
1810 * line, putting first_line, edittop, current, and mark_begin at the
1811 * same lines in the copied paragraph that they had in the original
1813 if (openfile
->current
!= openfile
->fileage
) {
1814 top
= openfile
->current
->prev
;
1817 openfile
->current
->lineno
== mb_lineno_save
) {
1818 openfile
->mark_begin
= openfile
->current
;
1819 openfile
->mark_begin_x
= mark_begin_x_save
;
1823 top
= openfile
->current
;
1824 for (i
= par_len
; i
> 0 && top
!= NULL
; i
--) {
1825 if (top
->lineno
== fl_lineno_save
)
1827 if (top
->lineno
== edittop_lineno_save
)
1828 openfile
->edittop
= top
;
1829 if (top
->lineno
== current_lineno_save
)
1830 openfile
->current
= top
;
1832 if (old_mark_set
&& top
->lineno
== mb_lineno_save
) {
1833 openfile
->mark_begin
= top
;
1834 openfile
->mark_begin_x
= mark_begin_x_save
;
1840 /* Put current_x at the same place in the copied paragraph that it
1841 * had in the original paragraph. */
1842 openfile
->current_x
= current_x_save
;
1847 /* Find the beginning of the current paragraph if we're in one, or the
1848 * beginning of the next paragraph if we're not. Afterwards, save the
1849 * quote length and paragraph length in *quote and *par. Return TRUE if
1850 * we found a paragraph, and FALSE if there was an error or we didn't
1853 * See the comment at begpar() for more about when a line is the
1854 * beginning of a paragraph. */
1855 bool find_paragraph(size_t *const quote
, size_t *const par
)
1858 /* Length of the initial quotation of the paragraph we search
1861 /* Number of lines in the paragraph we search for. */
1862 filestruct
*current_save
;
1863 /* The line at the beginning of the paragraph we search for. */
1864 ssize_t current_y_save
;
1865 /* The y-coordinate at the beginning of the paragraph we search
1870 statusbar(_("Bad quote string %s: %s"), quotestr
, quoteerr
);
1875 assert(openfile
->current
!= NULL
);
1877 /* If we're at the end of the last line of the file, it means that
1878 * there aren't any paragraphs left, so get out. */
1879 if (openfile
->current
== openfile
->filebot
&& openfile
->current_x
==
1880 strlen(openfile
->filebot
->data
))
1883 /* If the current line isn't in a paragraph, move forward to the
1884 * last line of the next paragraph, if any. */
1885 if (!inpar(openfile
->current
)) {
1888 /* If we end up past the beginning of the line, it means that
1889 * we're at the end of the last line of the file, and the line
1890 * isn't blank, in which case the last line of the file is the
1891 * last line of the next paragraph.
1893 * Otherwise, if we end up on a line that's in a paragraph, it
1894 * means that we're on the line after the last line of the next
1895 * paragraph, in which case we should move back to the last line
1896 * of the next paragraph. */
1897 if (openfile
->current_x
== 0) {
1898 if (!inpar(openfile
->current
->prev
))
1900 if (openfile
->current
!= openfile
->fileage
)
1901 openfile
->current
= openfile
->current
->prev
;
1905 /* If the current line isn't the first line of the paragraph, move
1906 * back to the first line of the paragraph. */
1907 if (!begpar(openfile
->current
))
1908 do_para_begin(FALSE
);
1910 /* Now current is the first line of the paragraph. Set quote_len to
1911 * the quotation length of that line, and set par_len to the number
1912 * of lines in this paragraph. */
1913 quote_len
= quote_length(openfile
->current
->data
);
1914 current_save
= openfile
->current
;
1915 current_y_save
= openfile
->current_y
;
1917 par_len
= openfile
->current
->lineno
- current_save
->lineno
;
1919 /* If we end up past the beginning of the line, it means that we're
1920 * at the end of the last line of the file, and the line isn't
1921 * blank, in which case the last line of the file is part of the
1923 if (openfile
->current_x
> 0)
1925 openfile
->current
= current_save
;
1926 openfile
->current_y
= current_y_save
;
1928 /* Save the values of quote_len and par_len. */
1929 assert(quote
!= NULL
&& par
!= NULL
);
1937 /* If full_justify is TRUE, justify the entire file. Otherwise, justify
1938 * the current paragraph. */
1939 void do_justify(bool full_justify
)
1941 filestruct
*first_par_line
= NULL
;
1942 /* Will be the first line of the justified paragraph(s), if any.
1943 * For restoring after unjustify. */
1944 filestruct
*last_par_line
= NULL
;
1945 /* Will be the line after the last line of the justified
1946 * paragraph(s), if any. Also for restoring after unjustify. */
1947 bool filebot_inpar
= FALSE
;
1948 /* Whether the text at filebot is part of the current
1951 /* We save these variables to be restored if the user
1953 filestruct
*edittop_save
= openfile
->edittop
;
1954 filestruct
*current_save
= openfile
->current
;
1955 size_t current_x_save
= openfile
->current_x
;
1956 size_t pww_save
= openfile
->placewewant
;
1957 size_t totsize_save
= openfile
->totsize
;
1959 filestruct
*mark_begin_save
= openfile
->mark_begin
;
1960 size_t mark_begin_x_save
= openfile
->mark_begin_x
;
1962 bool modified_save
= openfile
->modified
;
1965 bool meta_key
, func_key
, s_or_t
, ran_func
, finished
;
1968 /* Move to the beginning of the current line, so that justifying at
1969 * the end of the last line of the file, if that line isn't blank,
1970 * will work the first time through. */
1971 openfile
->current_x
= 0;
1973 /* If we're justifying the entire file, start at the beginning. */
1975 openfile
->current
= openfile
->fileage
;
1979 /* Generic loop variable. */
1980 filestruct
*curr_first_par_line
;
1981 /* The first line of the current paragraph. */
1983 /* Length of the initial quotation of the current
1986 /* Length of the initial indentation of the current
1989 /* Number of lines in the current paragraph. */
1991 /* Where we will break lines. */
1992 char *indent_string
;
1993 /* The first indentation that doesn't match the initial
1994 * indentation of the current paragraph. This is put at the
1995 * beginning of every line broken off the first justified
1996 * line of the paragraph. Note that this works because a
1997 * paragraph can only contain two indentations at most: the
1998 * initial one, and a different one starting on a line after
1999 * the first. See the comment at begpar() for more about
2000 * when a line is part of a paragraph. */
2002 /* Find the first line of the paragraph to be justified. That
2003 * is the start of this paragraph if we're in one, or the start
2004 * of the next otherwise. Save the quote length and paragraph
2005 * length (number of lines). Don't refresh the screen yet,
2006 * since we'll do that after we justify.
2008 * If the search failed, we do one of two things. If we're
2009 * justifying the whole file, and we've found at least one
2010 * paragraph, it means that we should justify all the way to the
2011 * last line of the file, so set the last line of the text to be
2012 * justified to the last line of the file and break out of the
2013 * loop. Otherwise, it means that there are no paragraph(s) to
2014 * justify, so refresh the screen and get out. */
2015 if (!find_paragraph("e_len
, &par_len
)) {
2016 if (full_justify
&& first_par_line
!= NULL
) {
2017 last_par_line
= openfile
->filebot
;
2020 edit_refresh_needed
= TRUE
;
2025 /* par_len will be one greater than the number of lines between
2026 * current and filebot if filebot is the last line in the
2027 * paragraph. Set filebot_inpar to TRUE if this is the case. */
2028 filebot_inpar
= (openfile
->current
->lineno
+ par_len
==
2029 openfile
->filebot
->lineno
+ 1);
2031 /* If we haven't already done it, move the original paragraph(s)
2032 * to the justify buffer, splice a copy of the original
2033 * paragraph(s) into the file in the same place, and set
2034 * first_par_line to the first line of the copy. */
2035 if (first_par_line
== NULL
) {
2036 backup_lines(openfile
->current
, full_justify
?
2037 openfile
->filebot
->lineno
- openfile
->current
->lineno
+
2038 ((openfile
->filebot
->data
[0] != '\0') ? 1 : 0) :
2040 first_par_line
= openfile
->current
;
2043 /* Set curr_first_par_line to the first line of the current
2045 curr_first_par_line
= openfile
->current
;
2047 /* Initialize indent_string to a blank string. */
2048 indent_string
= mallocstrcpy(NULL
, "");
2050 /* Find the first indentation in the paragraph that doesn't
2051 * match the indentation of the first line, and save it in
2052 * indent_string. If all the indentations are the same, save
2053 * the indentation of the first line in indent_string. */
2055 const filestruct
*indent_line
= openfile
->current
;
2056 bool past_first_line
= FALSE
;
2058 for (i
= 0; i
< par_len
; i
++) {
2059 indent_len
= quote_len
+
2060 indent_length(indent_line
->data
+ quote_len
);
2062 if (indent_len
!= strlen(indent_string
)) {
2063 indent_string
= mallocstrncpy(indent_string
,
2064 indent_line
->data
, indent_len
+ 1);
2065 indent_string
[indent_len
] = '\0';
2067 if (past_first_line
)
2071 if (indent_line
== openfile
->current
)
2072 past_first_line
= TRUE
;
2074 indent_line
= indent_line
->next
;
2078 /* Now tack all the lines of the paragraph together, skipping
2079 * the quoting and indentation on all lines after the first. */
2080 for (i
= 0; i
< par_len
- 1; i
++) {
2081 filestruct
*next_line
= openfile
->current
->next
;
2082 size_t line_len
= strlen(openfile
->current
->data
);
2083 size_t next_line_len
=
2084 strlen(openfile
->current
->next
->data
);
2086 indent_len
= quote_len
+
2087 indent_length(openfile
->current
->next
->data
+
2090 next_line_len
-= indent_len
;
2091 openfile
->totsize
-= indent_len
;
2093 /* We're just about to tack the next line onto this one. If
2094 * this line isn't empty, make sure it ends in a space. */
2096 openfile
->current
->data
[line_len
- 1] != ' ') {
2098 openfile
->current
->data
=
2099 charealloc(openfile
->current
->data
,
2101 openfile
->current
->data
[line_len
- 1] = ' ';
2102 openfile
->current
->data
[line_len
] = '\0';
2103 openfile
->totsize
++;
2106 openfile
->current
->data
=
2107 charealloc(openfile
->current
->data
, line_len
+
2109 strcat(openfile
->current
->data
, next_line
->data
+
2112 /* Don't destroy edittop or filebot! */
2113 if (next_line
== openfile
->edittop
)
2114 openfile
->edittop
= openfile
->current
;
2115 if (next_line
== openfile
->filebot
)
2116 openfile
->filebot
= openfile
->current
;
2119 /* Adjust the mark coordinates to compensate for the change
2120 * in the next line. */
2121 if (openfile
->mark_set
&& openfile
->mark_begin
==
2123 openfile
->mark_begin
= openfile
->current
;
2124 openfile
->mark_begin_x
+= line_len
- indent_len
;
2128 unlink_node(next_line
);
2129 delete_node(next_line
);
2131 /* If we've removed the next line, we need to go through
2132 * this line again. */
2136 openfile
->totsize
--;
2139 /* Call justify_format() on the paragraph, which will remove
2140 * excess spaces from it and change all blank characters to
2142 justify_format(openfile
->current
, quote_len
+
2143 indent_length(openfile
->current
->data
+ quote_len
));
2145 while (par_len
> 0 && strlenpt(openfile
->current
->data
) >
2147 size_t line_len
= strlen(openfile
->current
->data
);
2149 indent_len
= strlen(indent_string
);
2151 /* If this line is too long, try to wrap it to the next line
2152 * to make it short enough. */
2153 break_pos
= break_line(openfile
->current
->data
+ indent_len
,
2154 fill
- strnlenpt(openfile
->current
->data
, indent_len
)
2155 #ifndef DISABLE_HELP
2160 /* We can't break the line, or don't need to, so get out. */
2161 if (break_pos
== -1 || break_pos
+ indent_len
== line_len
)
2164 /* Move forward to the character after the indentation and
2165 * just after the space. */
2166 break_pos
+= indent_len
+ 1;
2168 assert(break_pos
<= line_len
);
2170 /* Make a new line, and copy the text after where we're
2171 * going to break this line to the beginning of the new
2173 splice_node(openfile
->current
,
2174 make_new_node(openfile
->current
),
2175 openfile
->current
->next
);
2177 /* If this paragraph is non-quoted, and autoindent isn't
2178 * turned on, set the indentation length to zero so that the
2179 * indentation is treated as part of the line. */
2182 && !ISSET(AUTOINDENT
)
2187 /* Copy the text after where we're going to break the
2188 * current line to the next line. */
2189 openfile
->current
->next
->data
= charalloc(indent_len
+ 1 +
2190 line_len
- break_pos
);
2191 strncpy(openfile
->current
->next
->data
, indent_string
,
2193 strcpy(openfile
->current
->next
->data
+ indent_len
,
2194 openfile
->current
->data
+ break_pos
);
2197 openfile
->totsize
+= indent_len
+ 1;
2200 /* Adjust the mark coordinates to compensate for the change
2201 * in the current line. */
2202 if (openfile
->mark_set
&& openfile
->mark_begin
==
2203 openfile
->current
&& openfile
->mark_begin_x
>
2205 openfile
->mark_begin
= openfile
->current
->next
;
2206 openfile
->mark_begin_x
-= break_pos
- indent_len
;
2210 /* Break the current line. */
2211 null_at(&openfile
->current
->data
, break_pos
);
2213 /* If the current line is the last line of the file, move
2214 * the last line of the file down to the next line. */
2215 if (openfile
->filebot
== openfile
->current
)
2216 openfile
->filebot
= openfile
->filebot
->next
;
2218 /* Go to the next line. */
2220 openfile
->current_y
++;
2221 openfile
->current
= openfile
->current
->next
;
2224 /* We're done breaking lines, so we don't need indent_string
2226 free(indent_string
);
2228 /* Go to the next line, if possible. If there is no next line,
2229 * move to the end of the current line. */
2230 if (openfile
->current
!= openfile
->filebot
) {
2231 openfile
->current_y
++;
2232 openfile
->current
= openfile
->current
->next
;
2234 openfile
->current_x
= strlen(openfile
->current
->data
);
2236 /* Renumber the lines of the now-justified current paragraph,
2237 * since both find_paragraph() and edit_refresh() need the line
2238 * numbers to be right. */
2239 renumber(curr_first_par_line
);
2241 /* We've just finished justifying the paragraph. If we're not
2242 * justifying the entire file, break out of the loop.
2243 * Otherwise, continue the loop so that we justify all the
2244 * paragraphs in the file. */
2249 /* We are now done justifying the paragraph or the file, so clean
2250 * up. current_y and totsize have been maintained above. If we
2251 * actually justified something, set last_par_line to the new end of
2253 if (first_par_line
!= NULL
)
2254 last_par_line
= openfile
->current
;
2259 /* We're going to set jump_buf so that we return here after a
2260 * SIGWINCH instead of to main(). Indicate this. */
2261 jump_buf_main
= FALSE
;
2263 /* Return here after a SIGWINCH. */
2264 sigsetjmp(jump_buf
, 1);
2267 statusbar(_("Can now UnJustify!"));
2269 /* If constant cursor position display is on, make sure the current
2270 * cursor position will be properly displayed on the statusbar. */
2271 if (ISSET(CONST_UPDATE
))
2274 /* Display the shortcut list with UnJustify. */
2275 shortcut_init(TRUE
);
2276 display_main_list();
2278 /* Now get a keystroke and see if it's unjustify. If not, put back
2279 * the keystroke and return. */
2280 kbinput
= do_input(&meta_key
, &func_key
, &s_or_t
, &ran_func
,
2282 s
= get_shortcut(currmenu
, &kbinput
, &meta_key
, &func_key
);
2284 if (s
&& s
->scfunc
== DO_UNCUT_TEXT
) {
2285 /* Splice the justify buffer back into the file, but only if we
2286 * actually justified something. */
2287 if (first_par_line
!= NULL
) {
2288 filestruct
*top_save
;
2290 /* Partition the filestruct so that it contains only the
2291 * text of the justified paragraph. */
2292 filepart
= partition_filestruct(first_par_line
, 0,
2293 last_par_line
, filebot_inpar
?
2294 strlen(last_par_line
->data
) : 0);
2296 /* Remove the text of the justified paragraph, and
2297 * replace it with the text in the justify buffer. */
2298 free_filestruct(openfile
->fileage
);
2299 openfile
->fileage
= jusbuffer
;
2300 openfile
->filebot
= jusbottom
;
2302 top_save
= openfile
->fileage
;
2304 /* Unpartition the filestruct so that it contains all the
2305 * text again. Note that the justified paragraph has been
2306 * replaced with the unjustified paragraph. */
2307 unpartition_filestruct(&filepart
);
2309 /* Renumber starting with the beginning line of the old
2313 /* Restore the justify we just did (ungrateful user!). */
2314 openfile
->edittop
= edittop_save
;
2315 openfile
->current
= current_save
;
2316 openfile
->current_x
= current_x_save
;
2317 openfile
->placewewant
= pww_save
;
2318 openfile
->totsize
= totsize_save
;
2320 if (openfile
->mark_set
) {
2321 openfile
->mark_begin
= mark_begin_save
;
2322 openfile
->mark_begin_x
= mark_begin_x_save
;
2325 openfile
->modified
= modified_save
;
2327 /* Clear the justify buffer. */
2330 if (!openfile
->modified
)
2332 edit_refresh_needed
= TRUE
;
2335 unget_kbinput(kbinput
, meta_key
, func_key
);
2337 /* Blow away the text in the justify buffer. */
2338 free_filestruct(jusbuffer
);
2344 /* Display the shortcut list with UnCut. */
2345 shortcut_init(FALSE
);
2346 display_main_list();
2349 /* Justify the current paragraph. */
2350 void do_justify_void(void)
2355 /* Justify the entire file. */
2356 void do_full_justify(void)
2360 #endif /* !DISABLE_JUSTIFY */
2362 #ifndef DISABLE_SPELLER
2363 /* A word is misspelled in the file. Let the user replace it. We
2364 * return FALSE if the user cancels. */
2365 bool do_int_spell_fix(const char *word
)
2367 char *save_search
, *save_replace
;
2368 size_t match_len
, current_x_save
= openfile
->current_x
;
2369 size_t pww_save
= openfile
->placewewant
;
2370 bool meta_key
= FALSE
, func_key
= FALSE
;
2371 filestruct
*edittop_save
= openfile
->edittop
;
2372 filestruct
*current_save
= openfile
->current
;
2373 /* Save where we are. */
2374 bool canceled
= FALSE
;
2375 /* The return value. */
2376 bool case_sens_set
= ISSET(CASE_SENSITIVE
);
2378 bool backwards_search_set
= ISSET(BACKWARDS_SEARCH
);
2381 bool regexp_set
= ISSET(USE_REGEXP
);
2384 bool old_mark_set
= openfile
->mark_set
;
2385 bool added_magicline
= FALSE
;
2386 /* Whether we added a magicline after filebot. */
2387 bool right_side_up
= FALSE
;
2388 /* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
2389 * FALSE if (current, current_x) is. */
2390 filestruct
*top
, *bot
;
2391 size_t top_x
, bot_x
;
2394 /* Make sure spell-check is case sensitive. */
2395 SET(CASE_SENSITIVE
);
2398 /* Make sure spell-check goes forward only. */
2399 UNSET(BACKWARDS_SEARCH
);
2402 /* Make sure spell-check doesn't use regular expressions. */
2406 /* Save the current search/replace strings. */
2407 search_init_globals();
2408 save_search
= last_search
;
2409 save_replace
= last_replace
;
2411 /* Set the search/replace strings to the misspelled word. */
2412 last_search
= mallocstrcpy(NULL
, word
);
2413 last_replace
= mallocstrcpy(NULL
, word
);
2417 /* If the mark is on, partition the filestruct so that it
2418 * contains only the marked text; if the NO_NEWLINES flag isn't
2419 * set, keep track of whether the text will have a magicline
2420 * added when we're done correcting misspelled words; and
2421 * turn the mark off. */
2422 mark_order((const filestruct
**)&top
, &top_x
,
2423 (const filestruct
**)&bot
, &bot_x
, &right_side_up
);
2424 filepart
= partition_filestruct(top
, top_x
, bot
, bot_x
);
2425 if (!ISSET(NO_NEWLINES
))
2426 added_magicline
= (openfile
->filebot
->data
[0] != '\0');
2427 openfile
->mark_set
= FALSE
;
2431 /* Start from the top of the file. */
2432 openfile
->edittop
= openfile
->fileage
;
2433 openfile
->current
= openfile
->fileage
;
2434 openfile
->current_x
= (size_t)-1;
2435 openfile
->placewewant
= 0;
2437 /* Find the first whole occurrence of word. */
2438 findnextstr_wrap_reset();
2439 while (findnextstr(TRUE
, FALSE
, openfile
->fileage
, 0, word
,
2441 if (is_whole_word(openfile
->current_x
, openfile
->current
->data
,
2443 size_t xpt
= xplustabs();
2444 char *exp_word
= display_string(openfile
->current
->data
,
2445 xpt
, strnlenpt(openfile
->current
->data
,
2446 openfile
->current_x
+ match_len
) - xpt
, FALSE
);
2450 do_replace_highlight(TRUE
, exp_word
);
2452 /* Allow all instances of the word to be corrected. */
2453 canceled
= (do_prompt(FALSE
,
2454 #ifndef DISABLE_TABCOMP
2458 &meta_key
, &func_key
,
2462 edit_refresh
, _("Edit a replacement")) == -1);
2464 do_replace_highlight(FALSE
, exp_word
);
2468 if (!canceled
&& strcmp(word
, answer
) != 0) {
2469 openfile
->current_x
--;
2470 do_replace_loop(TRUE
, &canceled
, openfile
->current
,
2471 &openfile
->current_x
, word
);
2480 /* If the mark was on, the NO_NEWLINES flag isn't set, and we
2481 * added a magicline, remove it now. */
2482 if (!ISSET(NO_NEWLINES
) && added_magicline
)
2485 /* Put the beginning and the end of the mark at the beginning
2486 * and the end of the spell-checked text. */
2487 if (openfile
->fileage
== openfile
->filebot
)
2489 if (right_side_up
) {
2490 openfile
->mark_begin_x
= top_x
;
2491 current_x_save
= bot_x
;
2493 current_x_save
= top_x
;
2494 openfile
->mark_begin_x
= bot_x
;
2497 /* Unpartition the filestruct so that it contains all the text
2498 * again, and turn the mark back on. */
2499 unpartition_filestruct(&filepart
);
2500 openfile
->mark_set
= TRUE
;
2504 /* Restore the search/replace strings. */
2506 last_search
= save_search
;
2508 last_replace
= save_replace
;
2510 /* Restore where we were. */
2511 openfile
->edittop
= edittop_save
;
2512 openfile
->current
= current_save
;
2513 openfile
->current_x
= current_x_save
;
2514 openfile
->placewewant
= pww_save
;
2516 /* Restore case sensitivity setting. */
2518 UNSET(CASE_SENSITIVE
);
2521 /* Restore search/replace direction. */
2522 if (backwards_search_set
)
2523 SET(BACKWARDS_SEARCH
);
2526 /* Restore regular expression usage setting. */
2534 /* Internal (integrated) spell checking using the spell program,
2535 * filtered through the sort and uniq programs. Return NULL for normal
2536 * termination, and the error string otherwise. */
2537 const char *do_int_speller(const char *tempfile_name
)
2539 char *read_buff
, *read_buff_ptr
, *read_buff_word
;
2540 size_t pipe_buff_size
, read_buff_size
, read_buff_read
, bytesread
;
2541 int spell_fd
[2], sort_fd
[2], uniq_fd
[2], tempfile_fd
= -1;
2542 pid_t pid_spell
, pid_sort
, pid_uniq
;
2543 int spell_status
, sort_status
, uniq_status
;
2545 /* Create all three pipes up front. */
2546 if (pipe(spell_fd
) == -1 || pipe(sort_fd
) == -1 ||
2547 pipe(uniq_fd
) == -1)
2548 return _("Could not create pipe");
2550 statusbar(_("Creating misspelled word list, please wait..."));
2552 /* A new process to run spell in. */
2553 if ((pid_spell
= fork()) == 0) {
2554 /* Child continues (i.e. future spell process). */
2557 /* Replace the standard input with the temp file. */
2558 if ((tempfile_fd
= open(tempfile_name
, O_RDONLY
)) == -1)
2559 goto close_pipes_and_exit
;
2561 if (dup2(tempfile_fd
, STDIN_FILENO
) != STDIN_FILENO
)
2562 goto close_pipes_and_exit
;
2566 /* Send spell's standard output to the pipe. */
2567 if (dup2(spell_fd
[1], STDOUT_FILENO
) != STDOUT_FILENO
)
2568 goto close_pipes_and_exit
;
2572 /* Start the spell program; we are using $PATH. */
2573 execlp("spell", "spell", NULL
);
2575 /* This should not be reached if spell is found. */
2579 /* Parent continues here. */
2582 /* A new process to run sort in. */
2583 if ((pid_sort
= fork()) == 0) {
2584 /* Child continues (i.e. future spell process). Replace the
2585 * standard input with the standard output of the old pipe. */
2586 if (dup2(spell_fd
[0], STDIN_FILENO
) != STDIN_FILENO
)
2587 goto close_pipes_and_exit
;
2591 /* Send sort's standard output to the new pipe. */
2592 if (dup2(sort_fd
[1], STDOUT_FILENO
) != STDOUT_FILENO
)
2593 goto close_pipes_and_exit
;
2597 /* Start the sort program. Use -f to remove mixed case. If
2598 * this isn't portable, let me know. */
2599 execlp("sort", "sort", "-f", NULL
);
2601 /* This should not be reached if sort is found. */
2608 /* A new process to run uniq in. */
2609 if ((pid_uniq
= fork()) == 0) {
2610 /* Child continues (i.e. future uniq process). Replace the
2611 * standard input with the standard output of the old pipe. */
2612 if (dup2(sort_fd
[0], STDIN_FILENO
) != STDIN_FILENO
)
2613 goto close_pipes_and_exit
;
2617 /* Send uniq's standard output to the new pipe. */
2618 if (dup2(uniq_fd
[1], STDOUT_FILENO
) != STDOUT_FILENO
)
2619 goto close_pipes_and_exit
;
2623 /* Start the uniq program; we are using PATH. */
2624 execlp("uniq", "uniq", NULL
);
2626 /* This should not be reached if uniq is found. */
2633 /* The child process was not forked successfully. */
2634 if (pid_spell
< 0 || pid_sort
< 0 || pid_uniq
< 0) {
2636 return _("Could not fork");
2639 /* Get the system pipe buffer size. */
2640 if ((pipe_buff_size
= fpathconf(uniq_fd
[0], _PC_PIPE_BUF
)) < 1) {
2642 return _("Could not get size of pipe buffer");
2645 /* Read in the returned spelling errors. */
2647 read_buff_size
= pipe_buff_size
+ 1;
2648 read_buff
= read_buff_ptr
= charalloc(read_buff_size
);
2650 while ((bytesread
= read(uniq_fd
[0], read_buff_ptr
,
2651 pipe_buff_size
)) > 0) {
2652 read_buff_read
+= bytesread
;
2653 read_buff_size
+= pipe_buff_size
;
2654 read_buff
= read_buff_ptr
= charealloc(read_buff
,
2656 read_buff_ptr
+= read_buff_read
;
2659 *read_buff_ptr
= '\0';
2662 /* Process the spelling errors. */
2663 read_buff_word
= read_buff_ptr
= read_buff
;
2665 while (*read_buff_ptr
!= '\0') {
2666 if ((*read_buff_ptr
== '\r') || (*read_buff_ptr
== '\n')) {
2667 *read_buff_ptr
= '\0';
2668 if (read_buff_word
!= read_buff_ptr
) {
2669 if (!do_int_spell_fix(read_buff_word
)) {
2670 read_buff_word
= read_buff_ptr
;
2674 read_buff_word
= read_buff_ptr
+ 1;
2679 /* Special case: the last word doesn't end with '\r' or '\n'. */
2680 if (read_buff_word
!= read_buff_ptr
)
2681 do_int_spell_fix(read_buff_word
);
2684 search_replace_abort();
2685 edit_refresh_needed
= TRUE
;
2687 /* Process the end of the spell process. */
2688 waitpid(pid_spell
, &spell_status
, 0);
2689 waitpid(pid_sort
, &sort_status
, 0);
2690 waitpid(pid_uniq
, &uniq_status
, 0);
2692 if (WIFEXITED(spell_status
) == 0 || WEXITSTATUS(spell_status
))
2693 return _("Error invoking \"spell\"");
2695 if (WIFEXITED(sort_status
) == 0 || WEXITSTATUS(sort_status
))
2696 return _("Error invoking \"sort -f\"");
2698 if (WIFEXITED(uniq_status
) == 0 || WEXITSTATUS(uniq_status
))
2699 return _("Error invoking \"uniq\"");
2704 close_pipes_and_exit
:
2705 /* Don't leak any handles. */
2716 /* External (alternate) spell checking. Return NULL for normal
2717 * termination, and the error string otherwise. */
2718 const char *do_alt_speller(char *tempfile_name
)
2720 int alt_spell_status
;
2721 size_t current_x_save
= openfile
->current_x
;
2722 size_t pww_save
= openfile
->placewewant
;
2723 ssize_t current_y_save
= openfile
->current_y
;
2724 ssize_t lineno_save
= openfile
->current
->lineno
;
2727 static int arglen
= 3;
2728 static char **spellargs
= NULL
;
2730 bool old_mark_set
= openfile
->mark_set
;
2731 bool added_magicline
= FALSE
;
2732 /* Whether we added a magicline after filebot. */
2733 bool right_side_up
= FALSE
;
2734 /* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
2735 * FALSE if (current, current_x) is. */
2736 filestruct
*top
, *bot
;
2737 size_t top_x
, bot_x
;
2738 ssize_t mb_lineno_save
= 0;
2739 /* We're going to close the current file, and open the output of
2740 * the alternate spell command. The line that mark_begin points
2741 * to will be freed, so we save the line number and restore it
2743 size_t totsize_save
= openfile
->totsize
;
2744 /* Our saved value of totsize, used when we spell-check a marked
2748 /* If the mark is on, save the number of the line it starts on,
2749 * and then turn the mark off. */
2750 mb_lineno_save
= openfile
->mark_begin
->lineno
;
2751 openfile
->mark_set
= FALSE
;
2755 if (openfile
->totsize
== 0) {
2756 statusbar(_("Finished checking spelling"));
2762 /* Set up an argument list to pass execvp(). */
2763 if (spellargs
== NULL
) {
2764 spellargs
= (char **)nmalloc(arglen
* sizeof(char *));
2766 spellargs
[0] = strtok(alt_speller
, " ");
2767 while ((ptr
= strtok(NULL
, " ")) != NULL
) {
2769 spellargs
= (char **)nrealloc(spellargs
, arglen
*
2771 spellargs
[arglen
- 3] = ptr
;
2773 spellargs
[arglen
- 1] = NULL
;
2775 spellargs
[arglen
- 2] = tempfile_name
;
2777 /* Start a new process for the alternate speller. */
2778 if ((pid_spell
= fork()) == 0) {
2779 /* Start alternate spell program; we are using $PATH. */
2780 execvp(spellargs
[0], spellargs
);
2782 /* Should not be reached, if alternate speller is found!!! */
2786 /* If we couldn't fork, get out. */
2788 return _("Could not fork");
2791 /* Don't handle a pending SIGWINCH until the alternate spell checker
2792 * is finished and we've loaded the spell-checked file back in. */
2793 allow_pending_sigwinch(FALSE
);
2796 /* Wait for the alternate spell checker to finish. */
2797 wait(&alt_spell_status
);
2799 /* Reenter curses mode. */
2802 /* Restore the terminal to its previous state. */
2805 /* Turn the cursor back on for sure. */
2808 /* The screen might have been resized. If it has, reinitialize all
2809 * the windows based on the new screen dimensions. */
2812 if (!WIFEXITED(alt_spell_status
) ||
2813 WEXITSTATUS(alt_spell_status
) != 0) {
2814 char *alt_spell_error
;
2815 char *invoke_error
= _("Error invoking \"%s\"");
2818 /* Turn the mark back on if it was on before. */
2819 openfile
->mark_set
= old_mark_set
;
2823 charalloc(strlen(invoke_error
) +
2824 strlen(alt_speller
) + 1);
2825 sprintf(alt_spell_error
, invoke_error
, alt_speller
);
2826 return alt_spell_error
;
2831 /* If the mark is on, partition the filestruct so that it
2832 * contains only the marked text; if the NO_NEWLINES flag isn't
2833 * set, keep track of whether the text will have a magicline
2834 * added when we're done correcting misspelled words; and
2835 * turn the mark off. */
2836 mark_order((const filestruct
**)&top
, &top_x
,
2837 (const filestruct
**)&bot
, &bot_x
, &right_side_up
);
2838 filepart
= partition_filestruct(top
, top_x
, bot
, bot_x
);
2839 if (!ISSET(NO_NEWLINES
))
2840 added_magicline
= (openfile
->filebot
->data
[0] != '\0');
2842 /* Get the number of characters in the marked text, and subtract
2843 * it from the saved value of totsize. */
2844 totsize_save
-= get_totsize(top
, bot
);
2848 /* Replace the text of the current buffer with the spell-checked
2850 replace_buffer(tempfile_name
);
2854 filestruct
*top_save
= openfile
->fileage
;
2856 /* If the mark was on, the NO_NEWLINES flag isn't set, and we
2857 * added a magicline, remove it now. */
2858 if (!ISSET(NO_NEWLINES
) && added_magicline
)
2861 /* Put the beginning and the end of the mark at the beginning
2862 * and the end of the spell-checked text. */
2863 if (openfile
->fileage
== openfile
->filebot
)
2865 if (right_side_up
) {
2866 openfile
->mark_begin_x
= top_x
;
2867 current_x_save
= bot_x
;
2869 current_x_save
= top_x
;
2870 openfile
->mark_begin_x
= bot_x
;
2873 /* Unpartition the filestruct so that it contains all the text
2874 * again. Note that we've replaced the marked text originally
2875 * in the partition with the spell-checked marked text in the
2877 unpartition_filestruct(&filepart
);
2879 /* Renumber starting with the beginning line of the old
2880 * partition. Also add the number of characters in the
2881 * spell-checked marked text to the saved value of totsize, and
2882 * then make that saved value the actual value. */
2884 totsize_save
+= openfile
->totsize
;
2885 openfile
->totsize
= totsize_save
;
2887 /* Assign mark_begin to the line where the mark began before. */
2888 do_gotopos(mb_lineno_save
, openfile
->mark_begin_x
,
2890 openfile
->mark_begin
= openfile
->current
;
2892 /* Assign mark_begin_x to the location in mark_begin where the
2893 * mark began before, adjusted for any shortening of the
2895 openfile
->mark_begin_x
= openfile
->current_x
;
2897 /* Turn the mark back on. */
2898 openfile
->mark_set
= TRUE
;
2902 /* Go back to the old position, and mark the file as modified. */
2903 do_gotopos(lineno_save
, current_x_save
, current_y_save
, pww_save
);
2907 /* Handle a pending SIGWINCH again. */
2908 allow_pending_sigwinch(TRUE
);
2914 /* Spell check the current file. If an alternate spell checker is
2915 * specified, use it. Otherwise, use the internal spell checker. */
2920 char *temp
= safe_tempfile(&temp_file
);
2921 const char *spell_msg
;
2923 if (ISSET(RESTRICTED
)) {
2924 nano_disabled_msg();
2929 statusbar(_("Error writing temp file: %s"), strerror(errno
));
2935 openfile
->mark_set
? write_marked_file(temp
, temp_file
, TRUE
,
2938 write_file(temp
, temp_file
, TRUE
, OVERWRITE
, FALSE
);
2941 statusbar(_("Error writing temp file: %s"), strerror(errno
));
2946 spell_msg
= (alt_speller
!= NULL
) ? do_alt_speller(temp
) :
2947 do_int_speller(temp
);
2953 /* If the spell-checker printed any error messages onscreen, make
2954 * sure that they're cleared off. */
2957 if (spell_msg
!= NULL
) {
2959 /* Don't display an error message of "Success". */
2960 statusbar(_("Spell checking failed: %s"), spell_msg
);
2962 statusbar(_("Spell checking failed: %s: %s"), spell_msg
,
2965 statusbar(_("Finished checking spelling"));
2967 #endif /* !DISABLE_SPELLER */
2970 /* Our own version of "wc". Note that its character counts are in
2971 * multibyte characters instead of single-byte characters. */
2972 void do_wordlinechar_count(void)
2974 size_t words
= 0, chars
= 0;
2976 size_t current_x_save
= openfile
->current_x
;
2977 size_t pww_save
= openfile
->placewewant
;
2978 filestruct
*current_save
= openfile
->current
;
2979 bool old_mark_set
= openfile
->mark_set
;
2980 filestruct
*top
, *bot
;
2981 size_t top_x
, bot_x
;
2984 /* If the mark is on, partition the filestruct so that it
2985 * contains only the marked text, and turn the mark off. */
2986 mark_order((const filestruct
**)&top
, &top_x
,
2987 (const filestruct
**)&bot
, &bot_x
, NULL
);
2988 filepart
= partition_filestruct(top
, top_x
, bot
, bot_x
);
2989 openfile
->mark_set
= FALSE
;
2992 /* Start at the top of the file. */
2993 openfile
->current
= openfile
->fileage
;
2994 openfile
->current_x
= 0;
2995 openfile
->placewewant
= 0;
2997 /* Keep moving to the next word (counting punctuation characters as
2998 * part of a word, as "wc -w" does), without updating the screen,
2999 * until we reach the end of the file, incrementing the total word
3000 * count whenever we're on a word just before moving. */
3001 while (openfile
->current
!= openfile
->filebot
||
3002 openfile
->current
->data
[openfile
->current_x
] != '\0') {
3003 if (do_next_word(TRUE
, FALSE
))
3007 /* Get the total line and character counts, as "wc -l" and "wc -c"
3008 * do, but get the latter in multibyte characters. */
3010 nlines
= openfile
->filebot
->lineno
-
3011 openfile
->fileage
->lineno
+ 1;
3012 chars
= get_totsize(openfile
->fileage
, openfile
->filebot
);
3014 /* Unpartition the filestruct so that it contains all the text
3015 * again, and turn the mark back on. */
3016 unpartition_filestruct(&filepart
);
3017 openfile
->mark_set
= TRUE
;
3019 nlines
= openfile
->filebot
->lineno
;
3020 chars
= openfile
->totsize
;
3023 /* Restore where we were. */
3024 openfile
->current
= current_save
;
3025 openfile
->current_x
= current_x_save
;
3026 openfile
->placewewant
= pww_save
;
3028 /* Display the total word, line, and character counts on the
3030 statusbar(_("%sWords: %lu Lines: %ld Chars: %lu"), old_mark_set
?
3031 _("In Selection: ") : "", (unsigned long)words
, (long)nlines
,
3032 (unsigned long)chars
);
3034 #endif /* !NANO_TINY */
3036 /* Get verbatim input. */
3037 void do_verbatim_input(void)
3040 size_t kbinput_len
, i
;
3043 /* TRANSLATORS: This is displayed when the next keystroke will be
3044 * inserted verbatim. */
3045 statusbar(_("Verbatim Input"));
3047 /* Read in all the verbatim characters. */
3048 kbinput
= get_verbatim_kbinput(edit
, &kbinput_len
);
3050 /* If constant cursor position display is on, make sure the current
3051 * cursor position will be properly displayed on the statusbar.
3052 * Otherwise, blank the statusbar. */
3053 if (ISSET(CONST_UPDATE
))
3057 wnoutrefresh(bottomwin
);
3060 /* Display all the verbatim characters at once, not filtering out
3061 * control characters. */
3062 output
= charalloc(kbinput_len
+ 1);
3064 for (i
= 0; i
< kbinput_len
; i
++)
3065 output
[i
] = (char)kbinput
[i
];
3070 do_output(output
, kbinput_len
, TRUE
);