diff options
Diffstat (limited to 'src/text.c')
-rw-r--r-- | src/text.c | 1391 |
1 files changed, 899 insertions, 492 deletions
@@ -1,9 +1,9 @@ -/* $Id: text.c 4520 2010-11-12 06:23:14Z astyanax $ */ +/* $Id: text.c 5145 2015-03-21 21:13:03Z bens $ */ /************************************************************************** * text.c * * * * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * - * 2008, 2009 Free Software Foundation, Inc. * + * 2008, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 3, or (at your option) * @@ -64,12 +64,10 @@ void do_mark(void) #endif /* !NANO_TINY */ /* Delete the character under the cursor. */ -void do_delete(void) +void do_deletion(undo_type action) { - size_t orig_lenpt = 0; - #ifndef NANO_TINY - update_undo(DEL); + size_t orig_lenpt = 0; #endif assert(openfile->current != NULL && openfile->current->data != NULL && openfile->current_x <= strlen(openfile->current->data)); @@ -84,8 +82,12 @@ void do_delete(void) assert(openfile->current_x < strlen(openfile->current->data)); +#ifndef NANO_TINY + update_undo(action); + if (ISSET(SOFTWRAP)) orig_lenpt = strlenpt(openfile->current->data); +#endif /* Let's get dangerous. */ charmove(&openfile->current->data[openfile->current_x], @@ -106,15 +108,17 @@ void do_delete(void) assert(openfile->current_x == strlen(openfile->current->data)); +#ifndef NANO_TINY + add_undo(action); +#endif /* If we're deleting at the end of a line, we need to call * edit_refresh(). */ if (openfile->current->data[openfile->current_x] == '\0') edit_refresh_needed = TRUE; openfile->current->data = charealloc(openfile->current->data, - openfile->current_x + strlen(foo->data) + 1); - strcpy(openfile->current->data + openfile->current_x, - foo->data); + strlen(openfile->current->data) + strlen(foo->data) + 1); + strcat(openfile->current->data, foo->data); #ifndef NANO_TINY if (openfile->mark_set && openfile->mark_begin == openfile->current->next) { @@ -139,16 +143,23 @@ void do_delete(void) } else return; +#ifndef NANO_TINY if (ISSET(SOFTWRAP) && edit_refresh_needed == FALSE) if (strlenpt(openfile->current->data) / COLS != orig_lenpt / COLS) - edit_refresh_needed = TRUE; + edit_refresh_needed = TRUE; +#endif set_modified(); - if (edit_refresh_needed == FALSE) + if (edit_refresh_needed == FALSE) update_line(openfile->current, openfile->current_x); } +void do_delete(void) +{ + do_deletion(DEL); +} + /* Backspace over one character. That is, move the cursor left one * character, and then delete the character under the cursor. */ void do_backspace(void) @@ -156,7 +167,7 @@ void do_backspace(void) if (openfile->current != openfile->fileage || openfile->current_x > 0) { do_left(); - do_delete(); + do_deletion(BACK); } } @@ -216,8 +227,7 @@ void do_indent(ssize_t cols) if (cols == 0) return; - /* If cols is negative, make it positive and set unindent to - * TRUE. */ + /* If cols is negative, make it positive and set unindent to TRUE. */ if (cols < 0) { cols = -cols; unindent = TRUE; @@ -360,99 +370,79 @@ void do_unindent(void) do_indent(-tabsize); } -/* undo a cut, or re-do an uncut */ +#define redo_paste undo_cut +#define undo_paste redo_cut + +/* Undo a cut, or redo an uncut. */ void undo_cut(undo *u) { - /* If we cut the magicline may was well not crash :/ */ + /* If we cut the magicline, we may as well not crash. :/ */ if (!u->cutbuffer) return; - cutbuffer = copy_filestruct(u->cutbuffer); - - /* Compute cutbottom for the uncut using out copy */ - for (cutbottom = cutbuffer; cutbottom->next != NULL; cutbottom = cutbottom->next) - ; - - /* Get to where we need to uncut from */ - if (u->mark_set && u->mark_begin_lineno < u->lineno) - do_gotolinecolumn(u->mark_begin_lineno, u->mark_begin_x+1, FALSE, FALSE, FALSE, FALSE); + /* Get to where we need to uncut from. */ + if (u->xflags == UNcut_cutline) + goto_line_posx(u->mark_begin_lineno, 0); else - do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE); + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); - copy_from_filestruct(cutbuffer, cutbottom); - free_filestruct(cutbuffer); - cutbuffer = NULL; + copy_from_filestruct(u->cutbuffer); + if (u->xflags != UNcut_marked_forward && u->type != PASTE) + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); } -/* Re-do a cut, or undo an uncut */ -void redo_cut(undo *u) { - int i; - filestruct *t, *c; - - /* If we cut the magicline may was well not crash :/ */ +/* Redo a cut, or undo an uncut. */ +void redo_cut(undo *u) +{ + /* If we cut the magicline, we may as well not crash. :/ */ if (!u->cutbuffer) return; - do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE); - openfile->mark_set = u->mark_set; - if (cutbuffer) - free(cutbuffer); + filestruct *oldcutbuffer = cutbuffer, *oldcutbottom = cutbottom; cutbuffer = NULL; + cutbottom = NULL; - /* Move ahead the same # lines we had if a marked cut */ - if (u->mark_set) { - for (i = 1, t = openfile->fileage; i != u->mark_begin_lineno; i++) - t = t->next; - openfile->mark_begin = t; - } else if (!u->to_end) { - /* Here we have a regular old potentially multi-line ^K cut. We'll - need to trick nano into thinking it's a marked cut to cut more - than one line again */ - for (c = u->cutbuffer, t = openfile->current; c->next != NULL && t->next != NULL; ) { + goto_line_posx(u->lineno, u->begin); -#ifdef DEBUG - fprintf(stderr, "Advancing, lineno = %lu, data = \"%s\"\n", (unsigned long) t->lineno, t->data); -#endif - c = c->next; - t = t->next; - } - openfile->mark_begin = t; - openfile->mark_begin_x = 0; - openfile->mark_set = TRUE; + if (ISSET(NO_NEWLINES) && openfile->current->lineno != u->lineno) { + openfile->current_x = strlen(openfile->current->data); + openfile->placewewant = xplustabs(); } - openfile->mark_begin_x = u->mark_begin_x; - do_cut_text(FALSE, u->to_end, TRUE); + openfile->mark_set = TRUE; + openfile->mark_begin = fsfromline(u->mark_begin_lineno); + openfile->mark_begin_x = (u->xflags == UNcut_cutline) ? 0 : u->mark_begin_x; + + do_cut_text(FALSE, FALSE, TRUE); + openfile->mark_set = FALSE; openfile->mark_begin = NULL; openfile->mark_begin_x = 0; edit_refresh_needed = TRUE; + + if (cutbuffer != NULL) + free_filestruct(cutbuffer); + cutbuffer = oldcutbuffer; + cutbottom = oldcutbottom; } -/* Undo the last thing(s) we did */ +/* Undo the last thing(s) we did. */ void do_undo(void) { undo *u = openfile->current_undo; - filestruct *f = openfile->current, *t; - int len = 0; - char *undidmsg, *data; - filestruct *oldcutbuffer = cutbuffer, *oldcutbottom = cutbottom; + filestruct *t = 0; + size_t len = 0; + char *data, *undidmsg = NULL; if (!u) { statusbar(_("Nothing in undo buffer!")); return; } - - if (u->lineno <= f->lineno) - for (; f->prev != NULL && f->lineno != u->lineno; f = f->prev) - ; - else - for (; f->next != NULL && f->lineno != u->lineno; f = f->next) - ; - if (f->lineno != u->lineno) { - statusbar(_("Internal error: can't match line %d. Please save your work"), u->lineno); + filestruct *f = fsfromline(u->mark_begin_lineno); + if (!f) { + statusbar(_("Internal error: can't match line %d. Please save your work."), u->mark_begin_lineno); return; } #ifdef DEBUG @@ -461,85 +451,94 @@ void do_undo(void) #endif openfile->current_x = u->begin; - switch(u->type) { + switch (u->type) { case ADD: undidmsg = _("text add"); len = strlen(f->data) - strlen(u->strdata) + 1; - data = charalloc(len); - strncpy(data, f->data, u->begin); + data = charalloc(len); + strncpy(data, f->data, u->begin); strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]); free(f->data); f->data = data; + goto_line_posx(u->lineno, u->begin); break; + case BACK: case DEL: undidmsg = _("text delete"); len = strlen(f->data) + strlen(u->strdata) + 1; data = charalloc(len); - strncpy(data, f->data, u->begin); strcpy(&data[u->begin], u->strdata); strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]); free(f->data); f->data = data; - if (u->xflags == UNDO_DEL_BACKSPACE) - openfile->current_x += strlen(u->strdata); + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); break; #ifndef DISABLE_WRAPPING - case SPLIT: - undidmsg = _("line wrap"); - f->data = (char *) nrealloc(f->data, strlen(f->data) + strlen(u->strdata) + 1); - strcpy(&f->data[strlen(f->data) - 1], u->strdata); - if (u->strdata2 != NULL) - f->next->data = mallocstrcpy(f->next->data, u->strdata2); - else { - filestruct *foo = openfile->current->next; - unlink_node(foo); - delete_node(foo); - } - renumber(f); + case SPLIT_END: + goto_line_posx(u->lineno, u->begin); + openfile->current_undo = openfile->current_undo->next; + openfile->last_action = OTHER; + while (openfile->current_undo->type != SPLIT_BEGIN) + do_undo(); + u = openfile->current_undo; + f = openfile->current; + case SPLIT_BEGIN: + undidmsg = _("text add"); break; -#endif /* DISABLE_WRAPPING */ - case UNSPLIT: +#endif /* !DISABLE_WRAPPING */ + case JOIN: undidmsg = _("line join"); t = make_new_node(f); t->data = mallocstrcpy(NULL, u->strdata); - data = mallocstrncpy(NULL, f->data, u->begin); - data[u->begin] = '\0'; + data = mallocstrncpy(NULL, f->data, u->mark_begin_x + 1); + data[u->mark_begin_x] = '\0'; free(f->data); f->data = data; splice_node(f, t, f->next); - renumber(f); + if (f == openfile->filebot) + openfile->filebot = t; + goto_line_posx(u->lineno, u->begin); break; + case CUT_EOF: case CUT: undidmsg = _("text cut"); - undo_cut(u); + undo_cut(u); + f = fsfromline(u->lineno); break; - case UNCUT: + case PASTE: undidmsg = _("text uncut"); - redo_cut(u); + undo_paste(u); + f = fsfromline(u->lineno); break; case ENTER: undidmsg = _("line break"); if (f->next) { filestruct *foo = f->next; - f->data = (char *) nrealloc(f->data, strlen(f->data) + strlen(f->next->data) + 1); - strcat(f->data, f->next->data); + f->data = charealloc(f->data, strlen(f->data) + strlen(&f->next->data[u->mark_begin_x]) + 1); + strcat(f->data, &f->next->data[u->mark_begin_x]); + if (foo == openfile->filebot) + openfile->filebot = f; unlink_node(foo); delete_node(foo); } + goto_line_posx(u->lineno, u->begin); break; case INSERT: undidmsg = _("text insert"); + filestruct *oldcutbuffer = cutbuffer, *oldcutbottom = cutbottom; cutbuffer = NULL; cutbottom = NULL; - /* When we updated mark_begin_lineno in update_undo, it was effectively how many line - were inserted due to being partitioned before read_file was called. So we - add its value here */ + /* When we updated mark_begin_lineno in update_undo, it was effectively + * how many lines were inserted due to being partitioned before read_file + * was called. So we add its value here. */ openfile->mark_begin = fsfromline(u->lineno + u->mark_begin_lineno - 1); openfile->mark_begin_x = 0; openfile->mark_set = TRUE; - do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE); + goto_line_posx(u->lineno, u->begin); cut_marked(); + if (u->cutbuffer != NULL) + free_filestruct(u->cutbuffer); u->cutbuffer = cutbuffer; u->cutbottom = cutbottom; cutbuffer = oldcutbuffer; @@ -548,29 +547,32 @@ void do_undo(void) break; case REPLACE: undidmsg = _("text replace"); + goto_line_posx(u->lineno, u->begin); data = u->strdata; u->strdata = f->data; f->data = data; break; - default: - undidmsg = _("Internal error: unknown type. Please save your work"); + undidmsg = _("Internal error: unknown type. Please save your work."); break; - } + + if (undidmsg) + statusbar(_("Undid action (%s)"), undidmsg); + renumber(f); - do_gotolinecolumn(u->lineno, u->begin, FALSE, FALSE, FALSE, TRUE); - statusbar(_("Undid action (%s)"), undidmsg); openfile->current_undo = openfile->current_undo->next; openfile->last_action = OTHER; + openfile->placewewant = xplustabs(); + set_modified(); } +/* Redo the last thing(s) we undid. */ void do_redo(void) { undo *u = openfile->undotop; - filestruct *f = openfile->current; - int len = 0; - char *undidmsg, *data; + size_t len = 0; + char *data, *redidmsg = NULL; for (; u != NULL && u->next != openfile->current_undo; u = u->next) ; @@ -579,18 +581,13 @@ void do_redo(void) return; } if (u->next != openfile->current_undo) { - statusbar(_("Internal error: Redo setup failed. Please save your work")); + statusbar(_("Internal error: cannot set up redo. Please save your work.")); return; } - if (u->lineno <= f->lineno) - for (; f->prev != NULL && f->lineno != u->lineno; f = f->prev) - ; - else - for (; f->next != NULL && f->lineno != u->lineno; f = f->next) - ; - if (f->lineno != u->lineno) { - statusbar(_("Internal error: can't match line %d. Please save your work"), u->lineno); + filestruct *f = fsfromline(u->mark_begin_lineno); + if (!f) { + statusbar(_("Internal error: can't match line %d. Please save your work."), u->mark_begin_lineno); return; } #ifdef DEBUG @@ -598,86 +595,98 @@ void do_redo(void) fprintf(stderr, "Redo running for type %d\n", u->type); #endif - switch(u->type) { + switch (u->type) { case ADD: - undidmsg = _("text add"); + redidmsg = _("text add"); len = strlen(f->data) + strlen(u->strdata) + 1; - data = charalloc(len); + data = charalloc(len); strncpy(data, f->data, u->begin); strcpy(&data[u->begin], u->strdata); strcpy(&data[u->begin + strlen(u->strdata)], &f->data[u->begin]); free(f->data); f->data = data; + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); break; + case BACK: case DEL: - undidmsg = _("text delete"); + redidmsg = _("text delete"); len = strlen(f->data) + strlen(u->strdata) + 1; data = charalloc(len); - strncpy(data, f->data, u->begin); + strncpy(data, f->data, u->begin); strcpy(&data[u->begin], &f->data[u->begin + strlen(u->strdata)]); free(f->data); f->data = data; + openfile->current_x = u->begin; + goto_line_posx(u->lineno, u->begin); break; case ENTER: - undidmsg = _("line break"); - do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE); + redidmsg = _("line break"); + goto_line_posx(u->lineno, u->begin); do_enter(TRUE); break; #ifndef DISABLE_WRAPPING - case SPLIT: - undidmsg = _("line wrap"); - if (u->xflags & UNDO_SPLIT_MADENEW) - prepend_wrap = TRUE; - do_wrap(f, TRUE); - renumber(f); + case SPLIT_BEGIN: + goto_line_posx(u->lineno, u->begin); + openfile->current_undo = u; + openfile->last_action = OTHER; + while (openfile->current_undo->type != SPLIT_END) + do_redo(); + u = openfile->current_undo; + goto_line_posx(u->lineno, u->begin); + case SPLIT_END: + redidmsg = _("text add"); break; -#endif /* DISABLE_WRAPPING */ - case UNSPLIT: - undidmsg = _("line join"); - len = strlen(f->data) + strlen(u->strdata + 1); - data = charalloc(len); - strcpy(data, f->data); - strcat(data, u->strdata); - free(f->data); - f->data = data; +#endif /* !DISABLE_WRAPPING */ + case JOIN: + redidmsg = _("line join"); + len = strlen(f->data) + strlen(u->strdata) + 1; + f->data = charealloc(f->data, len); + strcat(f->data, u->strdata); if (f->next != NULL) { filestruct *tmp = f->next; + if (tmp == openfile->filebot) + openfile->filebot = f; unlink_node(tmp); delete_node(tmp); } renumber(f); + goto_line_posx(u->mark_begin_lineno, u->mark_begin_x); break; + case CUT_EOF: case CUT: - undidmsg = _("text cut"); + redidmsg = _("text cut"); redo_cut(u); break; - case UNCUT: - undidmsg = _("text uncut"); - undo_cut(u); + case PASTE: + redidmsg = _("text uncut"); + redo_paste(u); break; case REPLACE: - undidmsg = _("text replace"); + redidmsg = _("text replace"); data = u->strdata; u->strdata = f->data; f->data = data; + goto_line_posx(u->lineno, u->begin); break; case INSERT: - undidmsg = _("text insert"); - do_gotolinecolumn(u->lineno, u->begin+1, FALSE, FALSE, FALSE, FALSE); - copy_from_filestruct(u->cutbuffer, u->cutbottom); - openfile->placewewant = xplustabs(); + redidmsg = _("text insert"); + goto_line_posx(u->lineno, u->begin); + copy_from_filestruct(u->cutbuffer); + free_filestruct(u->cutbuffer); + u->cutbuffer = NULL; break; default: - undidmsg = _("Internal error: unknown type. Please save your work"); + redidmsg = _("Internal error: unknown type. Please save your work."); break; - } - do_gotolinecolumn(u->lineno, u->begin, FALSE, FALSE, FALSE, TRUE); - statusbar(_("Redid action (%s)"), undidmsg); + + if (redidmsg) + statusbar(_("Redid action (%s)"), redidmsg); openfile->current_undo = u; openfile->last_action = OTHER; - + openfile->placewewant = xplustabs(); + set_modified(); } #endif /* !NANO_TINY */ @@ -693,7 +702,6 @@ void do_enter(bool undoing) if (!undoing) add_undo(ENTER); - /* Do auto-indenting, like the neolithic Turbo Pascal editor. */ if (ISSET(AUTOINDENT)) { /* If we are breaking the line in the indentation, the new @@ -711,7 +719,7 @@ void do_enter(bool undoing) #ifndef NANO_TINY if (ISSET(AUTOINDENT)) { strncpy(newnode->data, openfile->current->data, extra); - openfile->totsize += mbstrlen(newnode->data); + openfile->totsize += extra; } #endif null_at(&openfile->current->data, openfile->current_x); @@ -738,9 +746,20 @@ void do_enter(bool undoing) openfile->placewewant = xplustabs(); +#ifndef NANO_TINY + if (!undoing) + update_undo(ENTER); +#endif + edit_refresh_needed = TRUE; } +/* Need this again... */ +void do_enter_void(void) +{ + do_enter(FALSE); +} + #ifndef NANO_TINY /* Send a SIGKILL (unconditional kill) to the forked process in * execute_command(). */ @@ -763,13 +782,12 @@ bool execute_command(const char *command) /* Make our pipes. */ if (pipe(fd) == -1) { - statusbar(_("Could not pipe")); + statusbar(_("Could not create pipe")); return FALSE; } - /* Check $SHELL for the shell to use. If it isn't set, use - * /bin/sh. Note that $SHELL should contain only a path, with no - * arguments. */ + /* Check $SHELL for the shell to use. If it isn't set, use /bin/sh. + * Note that $SHELL should contain only a path, with no arguments. */ shellenv = getenv("SHELL"); if (shellenv == NULL) shellenv = (char *) "/bin/sh"; @@ -835,26 +853,23 @@ bool execute_command(const char *command) return TRUE; } -/* Add a new undo struct to the top of the current pile */ +/* Add a new undo struct to the top of the current pile. */ void add_undo(undo_type current_action) { undo *u; char *data; openfilestruct *fs = openfile; - static undo *last_cutu = NULL; /* Last thing we cut to set up the undo for uncut */ - ssize_t wrap_loc; /* For calculating split beginning */ - - if (!ISSET(UNDOABLE)) - return; + /* Last thing we cut to set up the undo for uncut. */ - /* Ugh, if we were called while cutting not-to-end, non-marked and on the same lineno, - we need to abort here */ + /* Ugh, if we were called while cutting not-to-end, non-marked, and + * on the same lineno, we need to abort here. */ u = fs->current_undo; - if (current_action == CUT && u && u->type == CUT - && !u->mark_set && u->lineno == fs->current->lineno) + if (u && u->mark_begin_lineno == fs->current->lineno && + ((current_action == CUT && u->type == CUT && !u->mark_set && keeping_cutbuffer()) || + (current_action == ADD && u->type == ADD && u->mark_begin_x == fs->current_x))) return; - /* Blow away the old undo stack if we are starting from the middle */ + /* Blow away the old undo stack if we are starting from the middle. */ while (fs->undotop != NULL && fs->undotop != fs->current_undo) { undo *u2 = fs->undotop; fs->undotop = fs->undotop->next; @@ -865,83 +880,101 @@ void add_undo(undo_type current_action) free(u2); } - /* Allocate and initialize a new undo type */ + /* Allocate and initialize a new undo type. */ u = (undo *) nmalloc(sizeof(undo)); u->type = current_action; u->lineno = fs->current->lineno; u->begin = fs->current_x; - u->next = fs->undotop; - fs->undotop = u; - fs->current_undo = u; +#ifndef DISABLE_WRAPPING + if (u->type == SPLIT_BEGIN) { + /* Some action, most likely an ADD, was performed that invoked + * do_wrap(). Rearrange the undo order so that this previous + * action is after the SPLIT_BEGIN undo. */ + u->next = fs->undotop->next ; + fs->undotop->next = u; + } else +#endif + { + u->next = fs->undotop; + fs->undotop = u; + fs->current_undo = u; + } u->strdata = NULL; - u->strdata2 = NULL; u->cutbuffer = NULL; - u->cutbottom = NULL; - u->mark_set = 0; - u->mark_begin_lineno = 0; - u->mark_begin_x = 0; + u->cutbottom = NULL; + u->mark_set = FALSE; + u->mark_begin_lineno = fs->current->lineno; + u->mark_begin_x = fs->current_x; u->xflags = 0; - u->to_end = FALSE; switch (u->type) { - /* We need to start copying data into the undo buffer or we wont be able - to restore it later */ + /* We need to start copying data into the undo buffer + * or we won't be able to restore it later. */ case ADD: - data = charalloc(2); - data[0] = fs->current->data[fs->current_x]; - data[1] = '\0'; - u->strdata = data; break; + case BACK: case DEL: if (u->begin != strlen(fs->current->data)) { - data = mallocstrncpy(NULL, &fs->current->data[u->begin], 2); - data[1] = '\0'; - u->strdata = data; + char *char_buf = charalloc(mb_cur_max() + 1); + int char_buf_len = parse_mbchar(&fs->current->data[u->begin], char_buf, NULL); + null_at(&char_buf, char_buf_len); + u->strdata = char_buf; + if (u->type == BACK) + u->mark_begin_x += char_buf_len; break; } - /* Else purposely fall into unsplit code */ - current_action = u->type = UNSPLIT; - case UNSPLIT: + /* Else purposely fall into the line-joining code. */ + case JOIN: if (fs->current->next) { + if (u->type == BACK) { + u->lineno = fs->current->next->lineno; + u->begin = 0; + } data = mallocstrcpy(NULL, fs->current->next->data); u->strdata = data; } + current_action = u->type = JOIN; break; #ifndef DISABLE_WRAPPING - case SPLIT: - wrap_loc = break_line(openfile->current->data, fill -#ifndef DISABLE_HELP - , FALSE -#endif - ); - u->strdata = mallocstrcpy(NULL, &openfile->current->data[wrap_loc]); - /* Don't both saving the next line if we're not prepending as a new line - will be created */ - if (prepend_wrap) - u->strdata2 = mallocstrcpy(NULL, fs->current->next->data); - u->begin = wrap_loc; + case SPLIT_BEGIN: + current_action = fs->undotop->type; + break; + case SPLIT_END: break; -#endif /* DISABLE_WRAPPING */ +#endif /* !DISABLE_WRAPPING */ case INSERT: + break; case REPLACE: data = mallocstrcpy(NULL, fs->current->data); u->strdata = data; break; + case CUT_EOF: + cutbuffer_reset(); + break; case CUT: + cutbuffer_reset(); u->mark_set = openfile->mark_set; if (u->mark_set) { u->mark_begin_lineno = openfile->mark_begin->lineno; u->mark_begin_x = openfile->mark_begin_x; } - u->to_end = (ISSET(CUT_TO_END)) ? TRUE : FALSE; - last_cutu = u; + else if (!ISSET(CUT_TO_END)) { + /* The entire line is being cut regardless of the cursor position. */ + u->begin = 0; + u->xflags = UNcut_cutline; + } break; - case UNCUT: - if (!last_cutu) - statusbar(_("Internal error: can't setup uncut. Please save your work.")); - else if (last_cutu->type == CUT) { - u->cutbuffer = last_cutu->cutbuffer; - u->cutbottom = last_cutu->cutbottom; + case PASTE: + if (!cutbuffer) + statusbar(_("Internal error: cannot set up uncut. Please save your work.")); + else { + if (u->cutbuffer) + free_filestruct(u->cutbuffer); + u->cutbuffer = copy_filestruct(cutbuffer); + u->mark_begin_lineno = fs->current->lineno; + u->mark_begin_x = fs->current_x; + u->lineno = fs->current->lineno + cutbottom->lineno - cutbuffer->lineno; + u->mark_set = TRUE; } break; case ENTER: @@ -952,43 +985,37 @@ void add_undo(undo_type current_action) } #ifdef DEBUG - fprintf(stderr, "fs->current->data = \"%s\", current_x = %lu, u->begin = %d, type = %d\n", - fs->current->data, (unsigned long) fs->current_x, u->begin, current_action); + fprintf(stderr, "fs->current->data = \"%s\", current_x = %lu, u->begin = %lu, type = %d\n", + fs->current->data, (unsigned long)fs->current_x, (unsigned long)u->begin, current_action); fprintf(stderr, "left add_undo...\n"); #endif fs->last_action = current_action; } -/* Update an undo item, or determine whether a new one - is really needed and bounce the data to add_undo - instead. The latter functionality just feels - gimmicky and may just be more hassle than - it's worth, so it should be axed if needed. */ +/* Update an undo item, or determine whether a new one is really needed + * and bounce the data to add_undo instead. The latter functionality + * just feels gimmicky and may just be more hassle than it's worth, + * so it should be axed if needed. */ void update_undo(undo_type action) { undo *u; - char *data; - int len = 0; openfilestruct *fs = openfile; - if (!ISSET(UNDOABLE)) - return; - #ifdef DEBUG - fprintf(stderr, "action = %d, fs->last_action = %d, openfile->current->lineno = %lu", - action, fs->last_action, (unsigned long) openfile->current->lineno); +fprintf(stderr, "action = %d, fs->last_action = %d, openfile->current->lineno = %ld", + action, fs->last_action, (long)openfile->current->lineno); if (fs->current_undo) - fprintf(stderr, "fs->current_undo->lineno = %lu\n", (unsigned long) fs->current_undo->lineno); + fprintf(stderr, "fs->current_undo->lineno = %ld\n", (long)fs->current_undo->lineno); else fprintf(stderr, "\n"); #endif /* Change to an add if we're not using the same undo struct - that we should be using */ + * that we should be using. */ if (action != fs->last_action - || (action != CUT && action != INSERT && action != SPLIT + || (action != ENTER && action != CUT && action != INSERT && openfile->current->lineno != fs->current_undo->lineno)) { - add_undo(action); + add_undo(action); return; } @@ -996,103 +1023,103 @@ void update_undo(undo_type action) u = fs->undotop; switch (u->type) { - case ADD: + case ADD: { #ifdef DEBUG - fprintf(stderr, "fs->current->data = \"%s\", current_x = %lu, u->begin = %d\n", - fs->current->data, (unsigned long) fs->current_x, u->begin); -#endif - len = strlen(u->strdata) + 2; - data = (char *) nrealloc((void *) u->strdata, len * sizeof(char *)); - data[len-2] = fs->current->data[fs->current_x]; - data[len-1] = '\0'; - u->strdata = (char *) data; + fprintf(stderr, "fs->current->data = \"%s\", current_x = %lu, u->begin = %lu\n", + fs->current->data, (unsigned long)fs->current_x, (unsigned long)u->begin); +#endif + char *char_buf = charalloc(mb_cur_max()); + size_t char_buf_len = parse_mbchar(&fs->current->data[u->mark_begin_x], char_buf, NULL); + u->strdata = addstrings(u->strdata, u->strdata ? strlen(u->strdata) : 0, char_buf, char_buf_len); #ifdef DEBUG fprintf(stderr, "current undo data now \"%s\"\n", u->strdata); #endif + u->mark_begin_lineno = fs->current->lineno; + u->mark_begin_x = fs->current_x; break; - case DEL: - len = strlen(u->strdata) + 2; - assert(len > 2); - if (fs->current_x == u->begin) { - /* They're deleting */ - if (!u->xflags) - u->xflags = UNDO_DEL_DEL; - else if (u->xflags != UNDO_DEL_DEL) { - add_undo(action); - return; - } - data = charalloc(len); - strcpy(data, u->strdata); - data[len-2] = fs->current->data[fs->current_x];; - data[len-1] = '\0'; - free(u->strdata); - u->strdata = data; - } else if (fs->current_x == u->begin - 1) { - /* They're backspacing */ - if (!u->xflags) - u->xflags = UNDO_DEL_BACKSPACE; - else if (u->xflags != UNDO_DEL_BACKSPACE) { - add_undo(action); - return; - } - data = charalloc(len); - data[0] = fs->current->data[fs->current_x]; - strcpy(&data[1], u->strdata); - free(u->strdata); - u->strdata = data; - u->begin--; + } + case BACK: + case DEL: { + char *char_buf = charalloc(mb_cur_max()); + size_t char_buf_len = parse_mbchar(&fs->current->data[fs->current_x], char_buf, NULL); + if (fs->current_x == u->begin) { + /* They're deleting. */ + u->strdata = addstrings(u->strdata, strlen(u->strdata), char_buf, char_buf_len); + u->mark_begin_x = fs->current_x; + } else if (fs->current_x == u->begin - char_buf_len) { + /* They're backspacing. */ + u->strdata = addstrings(char_buf, char_buf_len, u->strdata, strlen(u->strdata)); + u->begin = fs->current_x; } else { - /* They deleted something else on the line */ - add_undo(DEL); + /* They deleted something else on the line. */ + free(char_buf); + add_undo(u->type); return; } #ifdef DEBUG - fprintf(stderr, "current undo data now \"%s\"\nu->begin = %d\n", u->strdata, u->begin); + fprintf(stderr, "current undo data now \"%s\"\nu->begin = %lu\n", u->strdata, (unsigned long)u->begin); #endif break; + } + case CUT_EOF: case CUT: if (!cutbuffer) break; if (u->cutbuffer) - free(u->cutbuffer); + free_filestruct(u->cutbuffer); u->cutbuffer = copy_filestruct(cutbuffer); - /* Compute cutbottom for the uncut using out copy */ - for (u->cutbottom = u->cutbuffer; u->cutbottom->next != NULL; u->cutbottom = u->cutbottom->next) - ; + if (u->mark_set) { + /* If the "marking" operation was from right-->left or + * bottom-->top, then swap the mark points. */ + if ((u->lineno == u->mark_begin_lineno && u->begin < u->mark_begin_x) + || u->lineno < u->mark_begin_lineno) { + size_t x_loc = u->begin; + u->begin = u->mark_begin_x; + u->mark_begin_x = x_loc; + + ssize_t line = u->lineno; + u->lineno = u->mark_begin_lineno; + u->mark_begin_lineno = line; + } else + u->xflags = UNcut_marked_forward; + } else { + /* Compute cutbottom for the uncut using our copy. */ + u->cutbottom = u->cutbuffer; + while (u->cutbottom->next != NULL) + u->cutbottom = u->cutbottom->next; + u->lineno = u->mark_begin_lineno + u->cutbottom->lineno - u->cutbuffer->lineno; + if (ISSET(CUT_TO_END) || u->type == CUT_EOF) { + u->begin = strlen(u->cutbottom->data); + if(u->lineno == u->mark_begin_lineno) + u->begin += u->mark_begin_x; + } + } break; case REPLACE: - case UNCUT: - add_undo(action); + case PASTE: + u->begin = fs->current_x; + u->lineno = openfile->current->lineno; break; case INSERT: u->mark_begin_lineno = openfile->current->lineno; break; -#ifndef DISABLE_WRAPPING - case SPLIT: - /* This will only be called if we made a completely new line, - and as such we should note that so we can destroy it later */ - u->xflags = UNDO_SPLIT_MADENEW; - break; -#endif /* DISABLE_WRAPPING */ - case UNSPLIT: - /* These cases are handled by the earlier check for a new line and action */ case ENTER: + u->mark_begin_x = fs->current_x; + break; +#ifndef DISABLE_WRAPPING + case SPLIT_BEGIN: + case SPLIT_END: +#endif /* !DISABLE_WRAPPING */ + case JOIN: + /* These cases are handled by the earlier check for a new line and action. */ case OTHER: break; } #ifdef DEBUG - fprintf(stderr, "Done in udpate_undo (type was %d)\n", action); -#endif - if (fs->last_action != action) { -#ifdef DEBUG - fprintf(stderr, "Starting add_undo for new action as it does not match last_action\n"); + fprintf(stderr, "Done in update_undo (type was %d)\n", action); #endif - add_undo(action); - } - fs->last_action = action; } - #endif /* !NANO_TINY */ #ifndef DISABLE_WRAPPING @@ -1103,10 +1130,8 @@ void wrap_reset(void) prepend_wrap = FALSE; } -/* We wrap the given line. Precondition: we assume the cursor has been - * moved forward since the last typed character. Return TRUE if we - * wrapped, and FALSE otherwise. */ -bool do_wrap(filestruct *line, bool undoing) +/* Try wrapping the given line. Return TRUE if wrapped, FALSE otherwise. */ +bool do_wrap(filestruct *line) { size_t line_len; /* The length of the line we wrap. */ @@ -1122,16 +1147,10 @@ bool do_wrap(filestruct *line, bool undoing) /* The text after the wrap point. */ size_t after_break_len; /* The length of after_break. */ - bool prepending = FALSE; - /* Do we prepend to the next line? */ const char *next_line = NULL; /* The next line, minus indentation. */ size_t next_line_len = 0; /* The length of next_line. */ - char *new_line = NULL; - /* The line we create. */ - size_t new_line_len = 0; - /* The eventual length of new_line. */ /* There are three steps. First, we decide where to wrap. Then, we * create the new wrap line. Finally, we clean up. */ @@ -1171,9 +1190,6 @@ bool do_wrap(filestruct *line, bool undoing) return FALSE; #ifndef NANO_TINY - if (!undoing) - add_undo(SPLIT); - /* If autoindent is turned on, and we're on the character just after * the indentation, we don't wrap. */ if (ISSET(AUTOINDENT)) { @@ -1184,8 +1200,14 @@ bool do_wrap(filestruct *line, bool undoing) if (wrap_loc == indent_len) return FALSE; } + + add_undo(SPLIT_BEGIN); #endif + size_t old_x = openfile->current_x; + filestruct * oldLine = openfile->current; + openfile->current = line; + /* Step 2, making the new wrap line. It will consist of indentation * followed by the text after the wrap point, optionally followed by * a space (if the text after the wrap point doesn't end in a blank) @@ -1205,9 +1227,15 @@ bool do_wrap(filestruct *line, bool undoing) const char *end = after_break + move_mbleft(after_break, after_break_len); + /* Go to the end of the line. */ + openfile->current_x = line_len; + /* If after_break doesn't end in a blank, make sure it ends in a * space. */ if (!is_blank_mbchar(end)) { +#ifndef NANO_TINY + add_undo(ADD); +#endif line_len++; line->data = charealloc(line->data, line_len + 1); line->data[line_len - 1] = ' '; @@ -1215,126 +1243,42 @@ bool do_wrap(filestruct *line, bool undoing) after_break = line->data + wrap_loc; after_break_len++; openfile->totsize++; + openfile->current_x++; +#ifndef NANO_TINY + update_undo(ADD); +#endif } next_line = line->next->data; next_line_len = strlen(next_line); if (after_break_len + next_line_len <= fill) { - prepending = TRUE; - new_line_len += next_line_len; + /* Delete the LF to join the two lines. */ + do_delete(); + /* Delete any leading blanks from the joined-on line. */ + while (is_blank_mbchar(&line->data[openfile->current_x])) + do_delete(); + renumber(line); } } - /* new_line_len is now the length of the text that will be wrapped - * to the next line, plus (if we're prepending to it) the length of - * the text of the next line. */ - new_line_len += after_break_len; + /* Go to the wrap location and split the line there. */ + openfile->current_x = wrap_loc; + do_enter(FALSE); -#ifndef NANO_TINY - if (ISSET(AUTOINDENT)) { - if (prepending) { - /* If we're prepending, the indentation will come from the - * next line. */ - indent_string = next_line; - indent_len = indent_length(indent_string); - next_line += indent_len; - } else { - /* Otherwise, it will come from this line, in which case - * we should increase new_line_len to make room for it. */ - new_line_len += indent_len; - openfile->totsize += mbstrnlen(indent_string, indent_len); - } - } -#endif - - /* Now we allocate the new line and copy the text into it. */ - new_line = charalloc(new_line_len + 1); - new_line[0] = '\0'; - -#ifndef NANO_TINY - if (ISSET(AUTOINDENT)) { - /* Copy the indentation. */ - strncpy(new_line, indent_string, indent_len); - new_line[indent_len] = '\0'; - new_line_len += indent_len; - } -#endif - - /* Copy all the text after the wrap point of the current line. */ - strcat(new_line, after_break); - - /* Break the current line at the wrap point. */ - null_at(&line->data, wrap_loc); - - if (prepending) { - if (!undoing) - update_undo(SPLIT); - /* If we're prepending, copy the text from the next line, minus - * the indentation that we already copied above. */ - strcat(new_line, next_line); - - free(line->next->data); - line->next->data = new_line; - - /* If the NO_NEWLINES flag isn't set, and text has been added to - * the magicline, make a new magicline. */ - if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0') - new_magicline(); + if (old_x < wrap_loc) { + openfile->current_x = old_x; + openfile->current = oldLine; + prepend_wrap = TRUE; } else { - /* Otherwise, make a new line and copy the text after where we - * broke this line to the beginning of the new line. */ - splice_node(openfile->current, make_new_node(openfile->current), - openfile->current->next); - - /* If the current line is the last line of the file, move the - * last line of the file down to the next line. */ - if (openfile->filebot == openfile->current) - openfile->filebot = openfile->current->next; - - openfile->current->next->data = new_line; - - openfile->totsize++; - } - - /* Step 3, clean up. Reposition the cursor and mark, and do some - * other sundry things. */ - - /* Set the prepend_wrap flag, so that later wraps of this line will - * be prepended to the next line. */ - prepend_wrap = TRUE; - - /* Each line knows its number. We recalculate these if we inserted - * a new line. */ - if (!prepending) - renumber(line); - - /* If the cursor was after the break point, we must move it. We - * also clear the prepend_wrap flag in this case. */ - if (openfile->current_x > wrap_loc) { + openfile->current_x += (old_x - wrap_loc); prepend_wrap = FALSE; - - openfile->current = openfile->current->next; - openfile->current_x -= wrap_loc -#ifndef NANO_TINY - - indent_len -#endif - ; - openfile->placewewant = xplustabs(); } + openfile->placewewant = xplustabs(); + #ifndef NANO_TINY - /* If the mark was on this line after the wrap point, we move it - * down. If it was on the next line and we prepended to that line, - * we move it right. */ - if (openfile->mark_set) { - if (openfile->mark_begin == line && openfile->mark_begin_x > - wrap_loc) { - openfile->mark_begin = line->next; - openfile->mark_begin_x -= wrap_loc - indent_len + 1; - } else if (prepending && openfile->mark_begin == line->next) - openfile->mark_begin_x += after_break_len; - } + add_undo(SPLIT_END); #endif return TRUE; @@ -1346,7 +1290,7 @@ bool do_wrap(filestruct *line, bool undoing) * that the display length to there is at most (goal + 1). If there is * no such blank, then we find the first blank. We then take the last * blank in that group of blanks. The terminating '\0' counts as a - * blank, as does a '\n' if newline is TRUE. */ + * blank, as does a '\n' if newln is TRUE. */ ssize_t break_line(const char *line, ssize_t goal #ifndef DISABLE_HELP , bool newln @@ -1355,17 +1299,18 @@ ssize_t break_line(const char *line, ssize_t goal { ssize_t blank_loc = -1; /* Current tentative return value. Index of the last blank we - * found with short enough display width. */ + * found with short enough display width. */ ssize_t cur_loc = 0; /* Current index in line. */ size_t cur_pos = 0; /* Current column position in line. */ - int line_len; + int char_len = 0; + /* Length of current character, in bytes. */ assert(line != NULL); while (*line != '\0' && goal >= cur_pos) { - line_len = parse_mbchar(line, NULL, &cur_pos); + char_len = parse_mbchar(line, NULL, &cur_pos); if (is_blank_mbchar(line) #ifndef DISABLE_HELP @@ -1380,8 +1325,8 @@ ssize_t break_line(const char *line, ssize_t goal #endif } - line += line_len; - cur_loc += line_len; + line += char_len; + cur_loc += char_len; } if (goal >= cur_pos) @@ -1390,34 +1335,34 @@ ssize_t break_line(const char *line, ssize_t goal #ifndef DISABLE_HELP if (newln && blank_loc <= 0) { - /* If blank was not found or was found only first character, - * force line break. */ - cur_loc -= line_len; - return cur_loc; + /* If no blank was found, or was found only as the first + * character, force a line break. */ + cur_loc -= char_len; + return cur_loc; } #endif if (blank_loc == -1) { - /* No blank was found that was short enough. */ + /* No blank was found within the goal width, + * so now try and find a blank beyond it. */ bool found_blank = FALSE; ssize_t found_blank_loc = 0; while (*line != '\0') { - line_len = parse_mbchar(line, NULL, NULL); + char_len = parse_mbchar(line, NULL, NULL); if (is_blank_mbchar(line) #ifndef DISABLE_HELP || (newln && *line == '\n') #endif ) { - if (!found_blank) - found_blank = TRUE; + found_blank = TRUE; found_blank_loc = cur_loc; } else if (found_blank) return found_blank_loc; - line += line_len; - cur_loc += line_len; + line += char_len; + cur_loc += char_len; } return -1; @@ -1426,23 +1371,14 @@ ssize_t break_line(const char *line, ssize_t goal /* Move to the last blank after blank_loc, if there is one. */ line -= cur_loc; line += blank_loc; - line_len = parse_mbchar(line, NULL, NULL); - line += line_len; - - while (*line != '\0' && (is_blank_mbchar(line) -#ifndef DISABLE_HELP - || (newln && *line == '\n') -#endif - )) { -#ifndef DISABLE_HELP - if (newln && *line == '\n') - break; -#endif + char_len = parse_mbchar(line, NULL, NULL); + line += char_len; - line_len = parse_mbchar(line, NULL, NULL); + while (*line != '\0' && is_blank_mbchar(line)) { + char_len = parse_mbchar(line, NULL, NULL); - line += line_len; - blank_loc += line_len; + line += char_len; + blank_loc += char_len; } return blank_loc; @@ -1804,7 +1740,7 @@ void backup_lines(filestruct *first_line, size_t par_len) /* Copy the paragraph back to the current buffer's filestruct from * the justify buffer. */ - copy_from_filestruct(jusbuffer, jusbottom); + copy_from_filestruct(jusbuffer); /* Move upward from the last line of the paragraph to the first * line, putting first_line, edittop, current, and mark_begin at the @@ -1947,6 +1883,10 @@ void do_justify(bool full_justify) bool filebot_inpar = FALSE; /* Whether the text at filebot is part of the current * paragraph. */ + int kbinput; + /* The first keystroke after a justification. */ + functionptrtype func; + /* The function associated with that keystroke. */ /* We save these variables to be restored if the user * unjustifies. */ @@ -1961,10 +1901,6 @@ void do_justify(bool full_justify) #endif bool modified_save = openfile->modified; - int kbinput; - bool meta_key, func_key, s_or_t, ran_func, finished; - const sc *s; - /* Move to the beginning of the current line, so that justifying at * the end of the last line of the file, if that line isn't blank, * will work the first time through. */ @@ -1974,6 +1910,10 @@ void do_justify(bool full_justify) if (full_justify) openfile->current = openfile->fileage; +#ifndef NANO_TINY + allow_pending_sigwinch(FALSE); +#endif + while (TRUE) { size_t i; /* Generic loop variable. */ @@ -2272,16 +2212,16 @@ void do_justify(bool full_justify) do_cursorpos(TRUE); /* Display the shortcut list with UnJustify. */ - shortcut_init(TRUE); + uncutfunc->desc = unjust_tag; + currmenu = MMAIN; display_main_list(); /* Now get a keystroke and see if it's unjustify. If not, put back * the keystroke and return. */ - kbinput = do_input(&meta_key, &func_key, &s_or_t, &ran_func, - &finished, FALSE); - s = get_shortcut(currmenu, &kbinput, &meta_key, &func_key); + kbinput = do_input(FALSE); + func = func_from_key(&kbinput); - if (s && s->scfunc == DO_UNCUT_TEXT) { + if (func == do_uncut_text) { /* Splice the justify buffer back into the file, but only if we * actually justified something. */ if (first_par_line != NULL) { @@ -2342,8 +2282,12 @@ void do_justify(bool full_justify) blank_statusbar(); /* Display the shortcut list with UnCut. */ - shortcut_init(FALSE); + uncutfunc->desc = uncut_tag; display_main_list(); + +#ifndef NANO_TINY + allow_pending_sigwinch(TRUE); +#endif } /* Justify the current paragraph. */ @@ -2367,7 +2311,6 @@ bool do_int_spell_fix(const char *word) char *save_search, *save_replace; size_t match_len, current_x_save = openfile->current_x; size_t pww_save = openfile->placewewant; - bool meta_key = FALSE, func_key = FALSE; filestruct *edittop_save = openfile->edittop; filestruct *current_save = openfile->current; /* Save where we are. */ @@ -2455,8 +2398,7 @@ bool do_int_spell_fix(const char *word) TRUE, #endif MSPELL, word, - &meta_key, &func_key, -#ifndef NANO_TINY +#ifndef DISABLE_HISTORIES NULL, #endif edit_refresh, _("Edit a replacement")) == -1); @@ -2692,7 +2634,7 @@ const char *do_int_speller(const char *tempfile_name) if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status)) return _("Error invoking \"spell\""); - if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status)) + if (WIFEXITED(sort_status) == 0 || WEXITSTATUS(sort_status)) return _("Error invoking \"sort -f\""); if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status)) @@ -2719,9 +2661,10 @@ const char *do_alt_speller(char *tempfile_name) { int alt_spell_status; size_t current_x_save = openfile->current_x; - size_t pww_save = openfile->placewewant; ssize_t current_y_save = openfile->current_y; ssize_t lineno_save = openfile->current->lineno; + struct stat spellfileinfo; + __time_t timestamp; pid_t pid_spell; char *ptr; static int arglen = 3; @@ -2730,9 +2673,6 @@ const char *do_alt_speller(char *tempfile_name) bool old_mark_set = openfile->mark_set; bool added_magicline = FALSE; /* Whether we added a magicline after filebot. */ - bool right_side_up = FALSE; - /* TRUE if (mark_begin, mark_begin_x) is the top of the mark, - * FALSE if (current, current_x) is. */ filestruct *top, *bot; size_t top_x, bot_x; ssize_t mb_lineno_save = 0; @@ -2757,6 +2697,10 @@ const char *do_alt_speller(char *tempfile_name) return NULL; } + /* Get the timestamp of the temporary file. */ + stat(tempfile_name, &spellfileinfo); + timestamp = spellfileinfo.st_mtime; + endwin(); /* Set up an argument list to pass execvp(). */ @@ -2834,7 +2778,7 @@ const char *do_alt_speller(char *tempfile_name) * added when we're done correcting misspelled words; and * turn the mark off. */ mark_order((const filestruct **)&top, &top_x, - (const filestruct **)&bot, &bot_x, &right_side_up); + (const filestruct **)&bot, &bot_x, NULL); filepart = partition_filestruct(top, top_x, bot, bot_x); if (!ISSET(NO_NEWLINES)) added_magicline = (openfile->filebot->data[0] != '\0'); @@ -2858,18 +2802,6 @@ const char *do_alt_speller(char *tempfile_name) if (!ISSET(NO_NEWLINES) && added_magicline) remove_magicline(); - /* Put the beginning and the end of the mark at the beginning - * and the end of the spell-checked text. */ - if (openfile->fileage == openfile->filebot) - bot_x += top_x; - if (right_side_up) { - openfile->mark_begin_x = top_x; - current_x_save = bot_x; - } else { - current_x_save = top_x; - openfile->mark_begin_x = bot_x; - } - /* Unpartition the filestruct so that it contains all the text * again. Note that we've replaced the marked text originally * in the partition with the spell-checked marked text in the @@ -2885,8 +2817,7 @@ const char *do_alt_speller(char *tempfile_name) openfile->totsize = totsize_save; /* Assign mark_begin to the line where the mark began before. */ - do_gotopos(mb_lineno_save, openfile->mark_begin_x, - current_y_save, 0); + goto_line_posx(mb_lineno_save, openfile->mark_begin_x); openfile->mark_begin = openfile->current; /* Assign mark_begin_x to the location in mark_begin where the @@ -2899,9 +2830,16 @@ const char *do_alt_speller(char *tempfile_name) } #endif - /* Go back to the old position, and mark the file as modified. */ - do_gotopos(lineno_save, current_x_save, current_y_save, pww_save); - set_modified(); + /* Go back to the old position. */ + goto_line_posx(lineno_save, current_x_save); + openfile->current_y = current_y_save; + edit_update(NONE); + + /* Stat the temporary file again, and mark the buffer as modified only + * if this file was changed since it was written. */ + stat(tempfile_name, &spellfileinfo); + if (spellfileinfo.st_mtime != timestamp) + set_modified(); #ifndef NANO_TINY /* Handle a pending SIGWINCH again. */ @@ -2921,7 +2859,7 @@ void do_spell(void) const char *spell_msg; if (ISSET(RESTRICTED)) { - nano_disabled_msg(); + nano_disabled_msg(); return; } @@ -2943,6 +2881,10 @@ void do_spell(void) return; } + blank_bottombars(); + statusbar(_("Invoking spell checker, please wait")); + doupdate(); + spell_msg = (alt_speller != NULL) ? do_alt_speller(temp) : do_int_speller(temp); unlink(temp); @@ -2966,6 +2908,472 @@ void do_spell(void) } #endif /* !DISABLE_SPELLER */ +#ifndef DISABLE_COLOR +/* Cleanup things to do after leaving the linter. */ +void lint_cleanup(void) +{ + currmenu = MMAIN; + display_main_list(); +} + + +/* Run linter. Based on alt-speller code. Return NULL for normal + * termination, and the error string otherwise. */ +void do_linter(void) +{ + char *read_buff, *read_buff_ptr, *read_buff_word, *ptr; + size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread; + size_t parsesuccess = 0; + int lint_fd[2]; + pid_t pid_lint; + int lint_status; + static int arglen = 3; + static char **lintargs = NULL; + char *lintcopy; + char *convendptr = NULL; + lintstruct *lints = NULL, *tmplint = NULL, *curlint = NULL; + + if (!openfile->syntax || !openfile->syntax->linter) { + statusbar(_("No linter defined for this type of file!")); + return; + } + + if (ISSET(RESTRICTED)) { + nano_disabled_msg(); + return; + } + + if (openfile->modified) { + int i = do_yesno_prompt(FALSE, _("Save modified buffer before linting?")); + if (i == -1) { + statusbar(_("Cancelled")); + lint_cleanup(); + return; + } else if (i == 1) { + if (do_writeout(FALSE) != TRUE) { + lint_cleanup(); + return; + } + } + } + + lintcopy = mallocstrcpy(NULL, openfile->syntax->linter); + /* Create pipe up front. */ + if (pipe(lint_fd) == -1) { + statusbar(_("Could not create pipe")); + lint_cleanup(); + return; + } + + blank_bottombars(); + statusbar(_("Invoking linter, please wait")); + doupdate(); + + /* Set up an argument list to pass execvp(). */ + if (lintargs == NULL) { + lintargs = (char **)nmalloc(arglen * sizeof(char *)); + + lintargs[0] = strtok(lintcopy, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { + arglen++; + lintargs = (char **)nrealloc(lintargs, arglen * + sizeof(char *)); + lintargs[arglen - 3] = ptr; + } + lintargs[arglen - 1] = NULL; + } + lintargs[arglen - 2] = openfile->filename; + + /* A new process to run linter. */ + if ((pid_lint = fork()) == 0) { + + /* Child continues (i.e. future spell process). */ + close(lint_fd[0]); + + /* Send spell's standard output/err to the pipe. */ + if (dup2(lint_fd[1], STDOUT_FILENO) != STDOUT_FILENO) + exit(1); + if (dup2(lint_fd[1], STDERR_FILENO) != STDERR_FILENO) + exit(1); + + close(lint_fd[1]); + + /* Start the linter program; we are using $PATH. */ + execvp(lintargs[0], lintargs); + + /* This should not be reached if linter is found. */ + exit(1); + } + + /* Parent continues here. */ + close(lint_fd[1]); + + /* The child process was not forked successfully. */ + if (pid_lint < 0) { + close(lint_fd[0]); + statusbar(_("Could not fork")); + lint_cleanup(); + return; + } + + /* Get the system pipe buffer size. */ + if ((pipe_buff_size = fpathconf(lint_fd[0], _PC_PIPE_BUF)) < 1) { + close(lint_fd[0]); + statusbar(_("Could not get size of pipe buffer")); + lint_cleanup(); + return; + } + + /* Read in the returned spelling errors. */ + read_buff_read = 0; + read_buff_size = pipe_buff_size + 1; + read_buff = read_buff_ptr = charalloc(read_buff_size); + + while ((bytesread = read(lint_fd[0], read_buff_ptr, + pipe_buff_size)) > 0) { +#ifdef DEBUG + fprintf(stderr, "text.c:do_linter:%ld bytes (%s)\n", (long)bytesread, read_buff_ptr); +#endif + read_buff_read += bytesread; + read_buff_size += pipe_buff_size; + read_buff = read_buff_ptr = charealloc(read_buff, + read_buff_size); + read_buff_ptr += read_buff_read; + } + + *read_buff_ptr = '\0'; + close(lint_fd[0]); + +#ifdef DEBUG + fprintf(stderr, "text.c:do_lint:Raw output: %s\n", read_buff); +#endif + + /* Process output. */ + read_buff_word = read_buff_ptr = read_buff; + + while (*read_buff_ptr != '\0') { + if ((*read_buff_ptr == '\r') || (*read_buff_ptr == '\n')) { + *read_buff_ptr = '\0'; + if (read_buff_word != read_buff_ptr) { + char *filename = NULL, *linestr = NULL, *maybecol = NULL; + char *message = mallocstrcpy(NULL, read_buff_word); + + /* At the moment we're assuming the following formats: + * + * filenameorcategory:line:column:message (e.g. splint) + * filenameorcategory:line:message (e.g. pyflakes) + * filenameorcategory:line,col:message (e.g. pylint) + * + * This could be turned into some scanf() based parser, + * but ugh. */ + if ((filename = strtok(read_buff_word, ":")) != NULL) { + if ((linestr = strtok(NULL, ":")) != NULL) { + if ((maybecol = strtok(NULL, ":")) != NULL) { + ssize_t tmplineno = 0, tmpcolno = 0; + char *tmplinecol; + + tmplineno = strtol(linestr, NULL, 10); + if (tmplineno <= 0) { + read_buff_ptr++; + free(message); + continue; + } + + tmpcolno = strtol(maybecol, &convendptr, 10); + if (*convendptr != '\0') { + /* Previous field might still be + * line,col format. */ + strtok(linestr, ","); + if ((tmplinecol = strtok(NULL, ",")) != NULL) + tmpcolno = strtol(tmplinecol, NULL, 10); + } + +#ifdef DEBUG + fprintf(stderr, "text.c:do_lint:Successful parse! %ld:%ld:%s\n", (long)tmplineno, (long)tmpcolno, message); +#endif + /* Nice. We have a lint message we can use. */ + parsesuccess++; + tmplint = curlint; + curlint = nmalloc(sizeof(lintstruct)); + curlint->next = NULL; + curlint->prev = tmplint; + if (curlint->prev != NULL) + curlint->prev->next = curlint; + curlint->msg = mallocstrcpy(NULL, message); + curlint->lineno = tmplineno; + curlint->colno = tmpcolno; + curlint->filename = mallocstrcpy(NULL, filename); + + if (lints == NULL) + lints = curlint; + } + } + } else + free(message); + } + read_buff_word = read_buff_ptr + 1; + } + read_buff_ptr++; + } + + /* Process the end of the lint process. */ + waitpid(pid_lint, &lint_status, 0); + + free(read_buff); + + if (parsesuccess == 0) { + statusbar(_("Got 0 parsable lines from command: %s"), openfile->syntax->linter); + lint_cleanup(); + return; + } + + currmenu = MLINTER; + bottombars(MLINTER); + tmplint = NULL; + curlint = lints; + while (TRUE) { + ssize_t tmpcol = 1; + int kbinput; + functionptrtype func; + + if (curlint->colno > 0) + tmpcol = curlint->colno; + + if (tmplint != curlint) { +#ifndef NANO_TINY + struct stat lintfileinfo; + + new_lint_loop: + if (stat(curlint->filename, &lintfileinfo) != -1) { + if (openfile->current_stat->st_ino != lintfileinfo.st_ino) { + openfilestruct *tmpof = openfile; + while (tmpof != openfile->next) { + if (tmpof->current_stat->st_ino == lintfileinfo.st_ino) + break; + tmpof = tmpof->next; + } + if (tmpof->current_stat->st_ino != lintfileinfo.st_ino) { + char *msg = charalloc(1024 + strlen(curlint->filename)); + int i; + + sprintf(msg, _("This message is for unopened file %s, open it in a new buffer?"), + curlint->filename); + i = do_yesno_prompt(FALSE, msg); + free(msg); + if (i == -1) { + statusbar(_("Cancelled")); + goto free_lints_and_return; + } else if (i == 1) { + SET(MULTIBUFFER); + open_buffer(curlint->filename, FALSE); + } else { + char *dontwantfile = curlint->filename; + + while (curlint != NULL && !strcmp(curlint->filename, dontwantfile)) + curlint = curlint->next; + if (curlint == NULL) { + statusbar("No more errors in un-opened filed, cancelling"); + break; + } else + goto new_lint_loop; + } + } else + openfile = tmpof; + } + } +#endif /* !NANO_TINY */ + do_gotolinecolumn(curlint->lineno, tmpcol, FALSE, FALSE, FALSE, FALSE); + titlebar(NULL); + edit_refresh(); + statusbar(curlint->msg); + bottombars(MLINTER); + } + + kbinput = get_kbinput(bottomwin); + func = func_from_key(&kbinput); + tmplint = curlint; + + if (func == do_cancel) + break; + else if (func == do_help_void) { + tmplint = NULL; + do_help_void(); + } else if (func == do_page_down) { + if (curlint->next != NULL) + curlint = curlint->next; + else + statusbar(_("At last message")); + } else if (func == do_page_up) { + if (curlint->prev != NULL) + curlint = curlint->prev; + else + statusbar(_("At first message")); + } + } + blank_statusbar(); +#ifndef NANO_TINY +free_lints_and_return: +#endif + for (tmplint = lints; tmplint != NULL; tmplint = tmplint->next) { + free(tmplint->msg); + free(tmplint->filename); + free(tmplint); + } + lint_cleanup(); +} + +/* Run a formatter for the given syntax. + * Expects the formatter to be non-interactive and + * operate on a file in-place, which we'll pass it + * on the command line. Another mashup of the speller + * and alt_speller routines. + */ +void do_formatter(void) +{ + bool status; + FILE *temp_file; + char *temp = safe_tempfile(&temp_file); + int format_status; + size_t current_x_save = openfile->current_x; + size_t pww_save = openfile->placewewant; + ssize_t current_y_save = openfile->current_y; + ssize_t lineno_save = openfile->current->lineno; + pid_t pid_format; + char *ptr; + static int arglen = 3; + static char **formatargs = NULL; + char *finalstatus = NULL; + + /* Check whether we're using syntax highlighting + * and the formatter option is set. */ + if (openfile->syntax == NULL || openfile->syntax->formatter == NULL) { + statusbar(_("Error: no formatter defined")); + return; + } + + if (ISSET(RESTRICTED)) { + nano_disabled_msg(); + return; + } + + if (temp == NULL) { + statusbar(_("Error writing temp file: %s"), strerror(errno)); + return; + } + + /* We're not supporting partial formatting, oi vey. */ + openfile->mark_set = FALSE; + status = write_file(temp, temp_file, TRUE, OVERWRITE, FALSE); + + if (!status) { + statusbar(_("Error writing temp file: %s"), strerror(errno)); + free(temp); + return; + } + + if (openfile->totsize == 0) { + statusbar(_("Finished")); + return; + } + + blank_bottombars(); + statusbar(_("Invoking formatter, please wait")); + doupdate(); + + endwin(); + + /* Set up an argument list to pass execvp(). */ + if (formatargs == NULL) { + formatargs = (char **)nmalloc(arglen * sizeof(char *)); + + formatargs[0] = strtok(openfile->syntax->formatter, " "); + while ((ptr = strtok(NULL, " ")) != NULL) { + arglen++; + formatargs = (char **)nrealloc(formatargs, arglen * + sizeof(char *)); + formatargs[arglen - 3] = ptr; + } + formatargs[arglen - 1] = NULL; + } + formatargs[arglen - 2] = temp; + + /* Start a new process for the formatter. */ + if ((pid_format = fork()) == 0) { + /* Start the formatting program; we are using $PATH. */ + execvp(formatargs[0], formatargs); + + /* Should not be reached, if the formatter is found! */ + exit(1); + } + + /* If we couldn't fork, get out. */ + if (pid_format < 0) { + statusbar(_("Could not fork")); + return; + } + +#ifndef NANO_TINY + /* Don't handle a pending SIGWINCH until the alternate format checker + * is finished and we've loaded the format-checked file back in. */ + allow_pending_sigwinch(FALSE); +#endif + + /* Wait for the formatter to finish. */ + wait(&format_status); + + /* Reenter curses mode. */ + doupdate(); + + /* Restore the terminal to its previous state. */ + terminal_init(); + + /* Turn the cursor back on for sure. */ + curs_set(1); + + /* The screen might have been resized. If it has, reinitialize all + * the windows based on the new screen dimensions. */ + window_init(); + + if (!WIFEXITED(format_status) || + WEXITSTATUS(format_status) != 0) { + char *format_error; + char *invoke_error = _("Error invoking \"%s\""); + + format_error = + charalloc(strlen(invoke_error) + + strlen(openfile->syntax->formatter) + 1); + sprintf(format_error, invoke_error, openfile->syntax->formatter); + finalstatus = format_error; + } else { + /* Replace the text of the current buffer with the formatted text. */ + replace_buffer(temp); + + /* Go back to the old position, and mark the file as modified. */ + do_gotopos(lineno_save, current_x_save, current_y_save, pww_save); + set_modified(); + +#ifndef NANO_TINY + /* Handle a pending SIGWINCH again. */ + allow_pending_sigwinch(TRUE); +#endif + + finalstatus = _("Finished formatting"); + } + + unlink(temp); + free(temp); + + currmenu = MMAIN; + + /* If the formatter printed any error messages onscreen, make + * sure that they're cleared off. */ + total_refresh(); + + statusbar(finalstatus); +} + +#endif /* !DISABLE_COLOR */ + #ifndef NANO_TINY /* Our own version of "wc". Note that its character counts are in * multibyte characters instead of single-byte characters. */ @@ -3071,4 +3479,3 @@ void do_verbatim_input(void) free(output); } - |