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