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