add DB's overhaul of the cutting code and related file-writing code, his

fixes to check_operating_dir(), and a few minor cleanups and fixes of
mine


git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@1600 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
master
David Lawrence Ramsey 2003-12-24 08:03:54 +00:00
parent f427694400
commit 8213850df6
8 changed files with 541 additions and 553 deletions

View File

@ -29,13 +29,26 @@ CVS code -
shortcut display routines to handle them. Also modify the shortcut display routines to handle them. Also modify the
shortcut list code to not treat non-control character values shortcut list code to not treat non-control character values
of val as Meta-sequences, and fix dependencies on that of val as Meta-sequences, and fix dependencies on that
behavior. (DLR) behavior. Also rename several variables: "alt" -> "meta",
"altval" -> "metaval". (DLR)
- Hook up the verbatim input functions so that verbatim input - Hook up the verbatim input functions so that verbatim input
can be used in the edit window. New function can be used in the edit window. New function
do_verbatim_input(); changes to do_char(). (DLR) Additional do_verbatim_input(); changes to do_char(). (DLR) Additional
minor tweaks to do_char() by David Benbennick. minor tweaks to do_char() by David Benbennick.
- Clarify the description of the --rebinddelete option. (DLR) - Clarify the description of the --rebinddelete option. (DLR)
- cut.c:
- Overhaul to increase efficiency and add various cleanups.
Changes to add_to_cutbuffer(), cut_marked_segment(), and
do_uncut_text(). (David Benbennick)
- files.c: - files.c:
check_operating_dir()
- Add an assert to ensure that full_operatingdir isn't NULL,
a fix for reporting nonexistent (incomplete) directory names
as being outside the operating directory when tab completion
is being used, and cosmetic cleanups. (David Benbennick)
copy_file()
- New function containing part of the functionality formerly in
do_writeout. (David Benbennick)
do_writeout() do_writeout()
- Prompt the user if we're trying to save an existing file (and - Prompt the user if we're trying to save an existing file (and
not just a selection of it) under a different name. (DLR; not just a selection of it) under a different name. (DLR;
@ -56,6 +69,10 @@ CVS code -
- Convert to use the new low-level input functions. (DLR) - Convert to use the new low-level input functions. (DLR)
main() main()
- Remove unused variable option_index. (DLR) - Remove unused variable option_index. (DLR)
- Fix omission of NANO_NO_KEY in the shortcut list scanning
code. (DLR)
- nano.h:
- Comment additions and cosmetic tweaks. (DLR)
- search.c: - search.c:
findnextstr(), do_replace_loop() findnextstr(), do_replace_loop()
- Fix potential infinite loops and other misbehavior when doing - Fix potential infinite loops and other misbehavior when doing
@ -95,6 +112,9 @@ CVS code -
- Modify to take an extra parameter indicating if we should - Modify to take an extra parameter indicating if we should
ungetch() the key equivalents of shortcuts we click on or not. ungetch() the key equivalents of shortcuts we click on or not.
(DLR) (DLR)
nanogetstr()
- Properly interpret the Meta key value in misc if we hit it at
the statusbar prompt. (DLR)
do_yesno() do_yesno()
- Add a few efficiency/extensibility tweaks. (David Benbennick) - Add a few efficiency/extensibility tweaks. (David Benbennick)
- Convert to use the new low-level input functions, and remove - Convert to use the new low-level input functions, and remove

317
src/cut.c
View File

@ -28,15 +28,17 @@
#include "proto.h" #include "proto.h"
#include "nano.h" #include "nano.h"
static int marked_cut; /* Is the cutbuffer from a mark? */ static int marked_cut; /* Is the cutbuffer from a mark?
* 0 means whole-line cut, 1 means mark,
* 2 means cut-from-cursor. */
#ifndef NANO_SMALL #ifndef NANO_SMALL
static int concatenate_cut; /* Should we add this cut string to the static int concatenate_cut; /* Should we add this cut string to the
end of the last one? */ * end of the last one? */
#endif #endif
static filestruct *cutbottom = NULL; static filestruct *cutbottom = NULL;
/* Pointer to end of cutbuffer */ /* Pointer to end of cutbuffer. */
filestruct *get_cutbottom(void) filestruct *get_cutbottom(void)
{ {
@ -46,29 +48,27 @@ filestruct *get_cutbottom(void)
void add_to_cutbuffer(filestruct *inptr) void add_to_cutbuffer(filestruct *inptr)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "add_to_cutbuffer() called with inptr->data = %s\n", fprintf(stderr, "add_to_cutbuffer(): inptr->data = %s\n", inptr->data);
inptr->data);
#endif #endif
if (cutbuffer == NULL) { if (cutbuffer == NULL)
cutbuffer = inptr; cutbuffer = inptr;
inptr->prev = NULL;
#ifndef NANO_SMALL #ifndef NANO_SMALL
} else if (concatenate_cut && !ISSET(JUSTIFY_MODE)) { else if (concatenate_cut && !ISSET(JUSTIFY_MODE)) {
/* Just tack the text in inptr onto the text in cutbottom, /* Just tack the text in inptr onto the text in cutbottom,
unless we're backing up lines while justifying text. */ * unless we're backing up lines while justifying text. */
cutbottom->data = charealloc(cutbottom->data, cutbottom->data = charealloc(cutbottom->data,
strlen(cutbottom->data) + strlen(inptr->data) + 1); strlen(cutbottom->data) + strlen(inptr->data) + 1);
strcat(cutbottom->data, inptr->data); strcat(cutbottom->data, inptr->data);
return; return;
}
#endif #endif
} else { else {
cutbottom->next = inptr; cutbottom->next = inptr;
inptr->prev = cutbottom; inptr->prev = cutbottom;
} }
inptr->next = NULL;
cutbottom = inptr; cutbottom = inptr;
cutbottom->next = NULL;
} }
#ifndef NANO_SMALL #ifndef NANO_SMALL
@ -78,112 +78,115 @@ void add_to_cutbuffer(filestruct *inptr)
* last cut line has length bot_x. That is, if bot_x > 0 then we cut to * last cut line has length bot_x. That is, if bot_x > 0 then we cut to
* bot->data[bot_x - 1]. * bot->data[bot_x - 1].
* *
* destructive is whether to actually modify the file structure, if not * We maintain totsize, totlines, filebot, the magicline, and line
* then just copy the buffer into cutbuffer and don't pull it from the * numbers. Also, we set current and current_x so the cursor will be on
* file. * the first character after what was cut. We do not do any screen
* updates.
* *
* If destructive, then we maintain totsize, totlines, filebot, the * Note cutbuffer might not be NULL if "cut to end" is used. */
* magic line, and line numbers. Also, we set current and current_x so void cut_marked_segment(void)
* the cursor will be on the first character after what was cut. We do
* not do any screen updates. */
void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot,
size_t bot_x, int destructive)
{ {
filestruct *tmp, *next; filestruct *top;
filestruct *bot;
filestruct *tmp;
size_t top_x;
size_t bot_x;
size_t newsize; size_t newsize;
if (top == bot && top_x == bot_x) /* If the mark doesn't cover any text, get out. */
if (current == mark_beginbuf && current_x == mark_beginx)
return; return;
assert(top != NULL && bot != NULL); assert(current != NULL && mark_beginbuf != NULL);
/* Make top be no later than bot. */ /* Set up the top and bottom lines and coordinates of the marked
if (top->lineno > bot->lineno) { * text. */
filestruct *swap = top; mark_order((const filestruct **)&top, &top_x,
int swap2 = top_x; (const filestruct **)&bot, &bot_x);
top = bot; /* Make the first cut line manually. Move the cut part of the top
bot = swap; * line into tmp, and set newsize to that partial line's length. */
top_x = bot_x;
bot_x = swap2;
} else if (top == bot && top_x > bot_x) {
/* And bot_x can't be an earlier character than top_x. */
int swap = top_x;
top_x = bot_x;
bot_x = swap;
}
/* Make the first cut line manually. */
tmp = copy_node(top); tmp = copy_node(top);
newsize = (top == bot ? bot_x - top_x : strlen(top->data + top_x)); newsize = (top == bot ? bot_x - top_x : strlen(top->data + top_x));
charmove(tmp->data, top->data + top_x, newsize); charmove(tmp->data, tmp->data + top_x, newsize);
null_at(&tmp->data, newsize); null_at(&tmp->data, newsize);
add_to_cutbuffer(tmp);
/* And make the remainder line manually too. */ /* Add the contents of tmp to the cutbuffer. Note that cutbuffer
if (destructive) { * might be non-NULL if we have cut to end enabled. */
current_x = top_x; if (cutbuffer == NULL) {
totsize -= newsize; cutbuffer = tmp;
totlines -= bot->lineno - top->lineno; cutbottom = tmp;
} else {
newsize = top_x + strlen(bot->data + bot_x) + 1; cutbottom->next = tmp;
if (top == bot) { tmp->prev = cutbottom;
/* In this case, the remainder line is shorter, so we must cutbottom = tmp;
move text from the end forward first. */
charmove(top->data + top_x, bot->data + bot_x,
newsize - top_x);
top->data = charealloc(top->data, newsize);
} else {
totsize -= bot_x + 1;
/* Here, the remainder line might get longer, so we
realloc() it first. */
top->data = charealloc(top->data, newsize);
charmove(top->data + top_x, bot->data + bot_x,
newsize - top_x);
}
} }
/* And make the top remainder line manually too. Update current_x
* and totlines to account for all the cut text, and update totsize
* to account for the length of the cut part of the first line. */
current_x = top_x;
totsize -= newsize;
totlines -= bot->lineno - top->lineno;
/* Now set newsize to be the length of the top remainder line plus
* the bottom remainder line, plus one for the null terminator. */
newsize = top_x + strlen(bot->data + bot_x) + 1;
if (top == bot) { if (top == bot) {
/* In this case, we're only cutting one line or part of one
* line, so the remainder line is shorter. This means that we
* must move text from the end forward first. */
charmove(top->data + top_x, bot->data + bot_x, newsize - top_x);
top->data = charealloc(top->data, newsize);
cutbottom->next = NULL;
#ifdef DEBUG #ifdef DEBUG
dump_buffer(cutbuffer); dump_buffer(cutbuffer);
#endif #endif
return; return;
} }
tmp = top->next; /* Update totsize to account for the cut part of the last line. */
while (tmp != bot) { totsize -= bot_x + 1;
next = tmp->next;
if (!destructive) /* Here, the top remainder line might get longer (if the bottom
tmp = copy_node(tmp); * remainder line is added to the end of it), so we realloc() it
else * first. */
totsize -= strlen(tmp->data) + 1; top->data = charealloc(top->data, newsize);
add_to_cutbuffer(tmp); charmove(top->data + top_x, bot->data + bot_x, newsize - top_x);
tmp = next;
} assert(cutbottom != NULL && cutbottom->next != NULL);
/* We're cutting multiple lines, so in particular the next line is
* cut too. */
cutbottom->next->prev = cutbottom;
/* Update totsize to account for all the complete lines that have
* been cut. After this, totsize is fully up to date. */
for (tmp = top->next; tmp != bot; tmp = tmp->next)
totsize -= strlen(tmp->data) + 1;
/* Make the last cut line manually. */ /* Make the last cut line manually. */
tmp = copy_node(bot); null_at(&bot->data, bot_x);
null_at(&tmp->data, bot_x);
add_to_cutbuffer(tmp);
#ifdef DEBUG
dump_buffer(cutbuffer);
#endif
if (destructive) { /* Move the rest of the cut text (other than the cut part of the top
top->next = bot->next; * line) from the buffer to the end of the cutbuffer, and fix the
if (top->next != NULL) * edit buffer to account for the cut text. */
top->next->prev = top; top->next = bot->next;
delete_node(bot); cutbottom = bot;
renumber(top); cutbottom->next = NULL;
current = top; if (top->next != NULL)
if (bot == filebot) { top->next->prev = top;
filebot = top; renumber(top);
assert(bot_x == 0); current = top;
if (top_x > 0)
new_magicline(); /* If the bottom line of the cut was the magicline, set filebot
} * properly, and add a new magicline if the top remainder line
* (which is now the new bottom line) is non-blank. */
if (bot == filebot) {
filebot = top;
assert(bot_x == 0);
if (top_x > 0)
new_magicline();
} }
#ifdef DEBUG #ifdef DEBUG
dump_buffer(cutbuffer); dump_buffer(cutbuffer);
@ -194,9 +197,6 @@ void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot,
int do_cut_text(void) int do_cut_text(void)
{ {
filestruct *fileptr; filestruct *fileptr;
#ifndef NANO_SMALL
int dontupdate = 0;
#endif
assert(current != NULL && current->data != NULL); assert(current != NULL && current->data != NULL);
@ -214,8 +214,8 @@ int do_cut_text(void)
#endif #endif
} }
/* You can't cut the magic line except with the mark. But /* You can't cut the magicline except with the mark. But trying
trying does clear the cutbuffer if KEEP_CUTBUFFER is not set. */ * does clear the cutbuffer if KEEP_CUTBUFFER is not set. */
if (current == filebot if (current == filebot
#ifndef NANO_SMALL #ifndef NANO_SMALL
&& !ISSET(MARK_ISSET) && !ISSET(MARK_ISSET)
@ -231,11 +231,12 @@ int do_cut_text(void)
if (current->data[current_x] == '\0') { if (current->data[current_x] == '\0') {
/* If the line is empty and we didn't just cut a non-blank /* If the line is empty and we didn't just cut a non-blank
line, create a dummy line and add it to the cutbuffer */ * line, create a dummy blank line and add it to the
* cutbuffer. */
if (marked_cut != 1 && current->next != filebot) { if (marked_cut != 1 && current->next != filebot) {
filestruct *junk = make_new_node(current); filestruct *junk = make_new_node(current);
junk->data = charalloc(1); junk->data = charalloc(1);
junk->data[0] = '\0'; junk->data[0] = '\0';
add_to_cutbuffer(junk); add_to_cutbuffer(junk);
#ifdef DEBUG #ifdef DEBUG
@ -251,33 +252,22 @@ int do_cut_text(void)
mark_beginx = strlen(current->data); mark_beginx = strlen(current->data);
mark_beginbuf = current; mark_beginbuf = current;
dontupdate = 1;
} }
} }
if (ISSET(MARK_ISSET)) { if (ISSET(MARK_ISSET)) {
/* Don't do_update() and move the screen position if the marked cut_marked_segment();
area lies entirely within the screen buffer */
dontupdate |= current->lineno >= edittop->lineno &&
current->lineno <= editbot->lineno &&
mark_beginbuf->lineno >= edittop->lineno &&
mark_beginbuf->lineno <= editbot->lineno;
cut_marked_segment(current, current_x, mark_beginbuf,
mark_beginx, 1);
placewewant = xplustabs(); placewewant = xplustabs();
UNSET(MARK_ISSET); UNSET(MARK_ISSET);
/* If we just did a marked cut of part of a line, we should add /* If we just did a marked cut of part of a line, we should add
the first line of any cut done immediately afterward to the * the first line of any cut done immediately afterward to the
end of this cut, as Pico does. */ * end of this cut, as Pico does. */
if (current == mark_beginbuf && current_x < strlen(current->data)) if (current == mark_beginbuf && current_x < strlen(current->data))
concatenate_cut = 1; concatenate_cut = 1;
marked_cut = 1; marked_cut = 1;
if (dontupdate) edit_refresh();
edit_refresh();
else
edit_update(current, CENTER);
set_modified(); set_modified();
return 1; return 1;
@ -310,29 +300,26 @@ int do_cut_text(void)
#ifndef NANO_SMALL #ifndef NANO_SMALL
concatenate_cut = 0; concatenate_cut = 0;
#endif #endif
placewewant = 0;
return 1; return 1;
} }
int do_uncut_text(void) int do_uncut_text(void)
{ {
filestruct *tmp = current, *fileptr = current; filestruct *tmp = current;
filestruct *newbuf = NULL, *newend = NULL; filestruct *newbuf = NULL;
char *tmpstr, *tmpstr2; filestruct *newend = NULL;
filestruct *hold = current;
int i;
#ifndef DISABLE_WRAPPING #ifndef DISABLE_WRAPPING
wrap_reset(); wrap_reset();
#endif #endif
check_statblank(); check_statblank();
if (cutbuffer == NULL || fileptr == NULL) if (cutbuffer == NULL || current == NULL)
return 0; /* AIEEEEEEEEEEEE */ return 0; /* AIEEEEEEEEEEEE */
/* If we're uncutting a previously non-marked block, uncut to end if /* If we're uncutting a previously non-marked block, uncut to end if
we're not at the beginning of the line. If we are at the * we're not at the beginning of the line. If we are at the
beginning of the line, set placewewant to 0. Pico does both of * beginning of the line, set placewewant to 0. Pico does both of
these. */ * these. */
if (marked_cut == 0) { if (marked_cut == 0) {
if (current_x != 0) if (current_x != 0)
marked_cut = 2; marked_cut = 2;
@ -341,24 +328,22 @@ int do_uncut_text(void)
} }
/* If we're going to uncut on the magicline, always make a new /* If we're going to uncut on the magicline, always make a new
magicline in advance. */ * magicline in advance, as Pico does. */
if (current->next == NULL) if (current->next == NULL)
new_magicline(); new_magicline();
if (marked_cut == 0 || cutbuffer->next != NULL) if (marked_cut == 0 || cutbuffer->next != NULL) {
{
newbuf = copy_filestruct(cutbuffer); newbuf = copy_filestruct(cutbuffer);
for (newend = newbuf; newend->next != NULL && newend != NULL; for (newend = newbuf; newend->next != NULL && newend != NULL;
newend = newend->next) newend = newend->next)
totlines++; totlines++;
} }
/* Hook newbuf into fileptr */ /* Hook newbuf in at current. */
if (marked_cut != 0) { if (marked_cut != 0) {
int recenter_me = 0; filestruct *hold = current;
/* Should we eventually use edit_update(CENTER)? */
/* If there's only one line in the cutbuffer */ /* If there's only one line in the cutbuffer... */
if (cutbuffer->next == NULL) { if (cutbuffer->next == NULL) {
size_t buf_len = strlen(cutbuffer->data); size_t buf_len = strlen(cutbuffer->data);
size_t cur_len = strlen(current->data); size_t cur_len = strlen(current->data);
@ -367,22 +352,24 @@ int do_uncut_text(void)
charmove(current->data + current_x + buf_len, charmove(current->data + current_x + buf_len,
current->data + current_x, cur_len - current_x + 1); current->data + current_x, cur_len - current_x + 1);
strncpy(current->data + current_x, cutbuffer->data, buf_len); strncpy(current->data + current_x, cutbuffer->data, buf_len);
/* Use strncpy() to not copy the terminal '\0'. */ /* Use strncpy() to not copy the null terminator. */
current_x += buf_len; current_x += buf_len;
totsize += buf_len; totsize += buf_len;
placewewant = xplustabs(); placewewant = xplustabs();
update_cursor(); } else { /* Yuck -- no kidding! */
} else { /* yuck -- no kidding! */ char *tmpstr, *tmpstr2;
tmp = current->next; tmp = current->next;
/* New beginning */
/* New beginning. */
tmpstr = charalloc(current_x + strlen(newbuf->data) + 1); tmpstr = charalloc(current_x + strlen(newbuf->data) + 1);
strncpy(tmpstr, current->data, current_x); strncpy(tmpstr, current->data, current_x);
strcpy(&tmpstr[current_x], newbuf->data); strcpy(&tmpstr[current_x], newbuf->data);
totsize += strlen(newbuf->data) + strlen(newend->data) + 1; totsize += strlen(newbuf->data) + strlen(newend->data) + 1;
/* New end */ /* New end. */
tmpstr2 = charalloc(strlen(newend->data) + tmpstr2 = charalloc(strlen(newend->data) +
strlen(&current->data[current_x]) + 1); strlen(&current->data[current_x]) + 1);
strcpy(tmpstr2, newend->data); strcpy(tmpstr2, newend->data);
@ -401,35 +388,28 @@ int do_uncut_text(void)
newend->next = tmp; newend->next = tmp;
/* If tmp isn't null, we're in the middle: update the /* If tmp isn't NULL, we're in the middle: update the
prev pointer. If it IS null, we're at the end; update * prev pointer. If it IS NULL, we're at the end; update
the filebot pointer */ * the filebot pointer. */
if (tmp != NULL) if (tmp != NULL)
tmp->prev = newend; tmp->prev = newend;
else { else {
/* Fix the editbot pointer too */
if (editbot == filebot)
editbot = newend;
filebot = newend; filebot = newend;
new_magicline(); new_magicline();
} }
/* Now why don't we update the totsize also */ /* Now why don't we update the totsize also? */
for (tmp = current->next; tmp != newend; tmp = tmp->next) for (tmp = current->next; tmp != newend; tmp = tmp->next)
totsize += strlen(tmp->data) + 1; totsize += strlen(tmp->data) + 1;
current = newend; current = newend;
if (editbot->lineno < newend->lineno)
recenter_me = 1;
} }
/* If marked cut == 2, that means that we're doing a cut to end /* If marked cut == 2, that means that we're doing a cut to end
and we don't want anything else on the line, so we have to * and we don't want anything else on the line, so we have to
screw up all the work we just did and separate the line. * screw up all the work we just did and separate the line.
There must be a better way to do this, but not at 1AM on a * There must be a better way to do this, but not at 1 AM on a
work night. */ * work night. */
if (marked_cut == 2) { if (marked_cut == 2) {
tmp = make_new_node(current); tmp = make_new_node(current);
tmp->data = mallocstrcpy(NULL, current->data + current_x); tmp->data = mallocstrcpy(NULL, current->data + current_x);
@ -439,7 +419,7 @@ int do_uncut_text(void)
current_x = 0; current_x = 0;
placewewant = 0; placewewant = 0;
/* Extra line added, update stuff */ /* Extra line added; update stuff. */
totlines++; totlines++;
totsize++; totsize++;
} }
@ -451,41 +431,32 @@ int do_uncut_text(void)
dump_buffer(cutbuffer); dump_buffer(cutbuffer);
#endif #endif
set_modified(); set_modified();
if (recenter_me) edit_refresh();
edit_update(current, CENTER);
else
edit_refresh();
return 0; return 0;
} }
if (fileptr != fileage) { if (current != fileage) {
tmp = fileptr->prev; tmp = current->prev;
tmp->next = newbuf; tmp->next = newbuf;
newbuf->prev = tmp; newbuf->prev = tmp;
} else } else
fileage = newbuf; fileage = newbuf;
totlines++; /* Unmarked uncuts don't split lines */ totlines++; /* Unmarked uncuts don't split lines. */
/* This is so uncutting at the top of the buffer will work => */ /* This is so uncutting at the top of the buffer will work => */
if (current_y == 0) if (current_y == 0)
edittop = newbuf; edittop = newbuf;
/* Connect the end of the buffer to the filestruct */ /* Connect the end of the buffer to the filestruct. */
newend->next = fileptr; newend->next = current;
fileptr->prev = newend; current->prev = newend;
/* Recalculate size *sigh* */ /* Recalculate size *sigh* */
for (tmp = newbuf; tmp != fileptr; tmp = tmp->next) for (tmp = newbuf; tmp != current; tmp = tmp->next)
totsize += strlen(tmp->data) + 1; totsize += strlen(tmp->data) + 1;
i = editbot->lineno;
renumber(newbuf); renumber(newbuf);
/* Center the screen if we've moved beyond the line numbers of both edit_refresh();
the old and new editbots */
if (i < newend->lineno && editbot->lineno < newend->lineno)
edit_update(fileptr, CENTER);
else
edit_refresh();
#ifdef DEBUG #ifdef DEBUG
dump_buffer_reverse(); dump_buffer_reverse();

View File

@ -522,7 +522,7 @@ int do_insertfile(int loading_file)
#endif #endif
#ifndef DISABLE_OPERATINGDIR #ifndef DISABLE_OPERATINGDIR
if (i != NANO_EXTCMD_KEY && check_operating_dir(answer, FALSE)) { if (i != NANO_EXTCMD_KEY && check_operating_dir(answer, 0) != 0) {
statusbar(_("Can't insert file from outside of %s"), statusbar(_("Can't insert file from outside of %s"),
operating_dir); operating_dir);
return 0; return 0;
@ -1272,410 +1272,376 @@ void init_operating_dir(void)
} }
} }
/* /* Check to see if we're inside the operating directory. Return 0 if we
* Check to see if we're inside the operating directory. Return 0 if we
* are, or 1 otherwise. If allow_tabcomp is nonzero, allow incomplete * are, or 1 otherwise. If allow_tabcomp is nonzero, allow incomplete
* names that would be matches for the operating directory, so that tab * names that would be matches for the operating directory, so that tab
* completion will work. * completion will work. */
*/
int check_operating_dir(const char *currpath, int allow_tabcomp) int check_operating_dir(const char *currpath, int allow_tabcomp)
{ {
/* The char *full_operating_dir is global for mem cleanup, and /* The char *full_operating_dir is global for mem cleanup. It
therefore we only need to get it the first time this function * should have already been initialized by init_operating_dir().
is called; also, a relative operating directory path will * Also, a relative operating directory path will only be handled
only be handled properly if this is done */ * properly if this is done. */
char *fullpath; char *fullpath;
int retval = 0; int retval = 0;
const char *whereami1, *whereami2 = NULL; const char *whereami1, *whereami2 = NULL;
/* if no operating directory is set, don't bother doing anything */ /* If no operating directory is set, don't bother doing anything. */
if (operating_dir == NULL) if (operating_dir == NULL)
return 0; return 0;
assert(full_operating_dir != NULL);
fullpath = get_full_path(currpath); fullpath = get_full_path(currpath);
/* fullpath == NULL means some directory in the path doesn't exist
* or is unreadable. If allow_tabcomp is zero, then currpath is
* what the user typed somewhere. We don't want to report a
* non-existent directory as being outside the operating directory,
* so we return 0. If allow_tabcomp is nonzero, then currpath
* exists, but is not executable. So we say it isn't in the
* operating directory. */
if (fullpath == NULL) if (fullpath == NULL)
return 1; return allow_tabcomp;
whereami1 = strstr(fullpath, full_operating_dir); whereami1 = strstr(fullpath, full_operating_dir);
if (allow_tabcomp) if (allow_tabcomp)
whereami2 = strstr(full_operating_dir, fullpath); whereami2 = strstr(full_operating_dir, fullpath);
/* if both searches failed, we're outside the operating directory */ /* If both searches failed, we're outside the operating directory.
/* otherwise */ * Otherwise, check the search results; if the full operating
/* check the search results; if the full operating directory path is * directory path is not at the beginning of the full current path
not at the beginning of the full current path (for normal usage) * (for normal usage) and vice versa (for tab completion, if we're
and vice versa (for tab completion, if we're allowing it), we're * allowing it), we're outside the operating directory. */
outside the operating directory */
if (whereami1 != fullpath && whereami2 != full_operating_dir) if (whereami1 != fullpath && whereami2 != full_operating_dir)
retval = 1; retval = 1;
free(fullpath); free(fullpath);
/* otherwise, we're still inside it */
/* Otherwise, we're still inside it. */
return retval; return retval;
} }
#endif #endif
/* /* Read from inn, write to out. We assume inn is opened for reading,
* Write a file out. If tmp is nonzero, we set the umask to 0600, * and out for writing. We return 0 on success, -1 on read error, -2 on
* we don't set the global variable filename to its name, and don't * write error. */
* print out how many lines we wrote on the statusbar. int copy_file(FILE *inn, FILE *out)
{
char buf[BUFSIZ];
size_t charsread;
int retval = 0;
assert(inn != NULL && out != NULL);
do {
charsread = fread(buf, sizeof(char), BUFSIZ, inn);
if (charsread == 0 && ferror(inn)) {
retval = -1;
break;
}
if (fwrite(buf, sizeof(char), charsread, out) < charsread) {
retval = -2;
break;
}
} while (charsread > 0);
if (fclose(inn) == EOF)
retval = -1;
if (fclose(out) == EOF)
retval = -2;
return retval;
}
/* Write a file out. If tmp is nonzero, we set the umask to disallow
* anyone else from accessing the file, we don't set the global variable
* filename to its name, and we don't print out how many lines we wrote
* on the statusbar.
* *
* tmp means we are writing a tmp file in a secure fashion. We use * tmp means we are writing a temporary file in a secure fashion. We
* it when spell checking or dumping the file on an error. * use it when spell checking or dumping the file on an error.
* *
* append == 1 means we are appending instead of overwriting. * append == 1 means we are appending instead of overwriting.
* append == 2 means we are prepending instead of overwriting. * append == 2 means we are prepending instead of overwriting.
* *
* nonamechange means don't change the current filename, it is ignored * nonamechange means don't change the current filename, it is ignored
* if tmp is nonzero or if we're appending/prepending. * if tmp is nonzero or if we're appending/prepending.
*/ *
* Return -1 on error, 1 on success. */
int write_file(const char *name, int tmp, int append, int nonamechange) int write_file(const char *name, int tmp, int append, int nonamechange)
{ {
int retval = -1; int retval = -1;
/* Instead of returning in this function, you should always /* Instead of returning in this function, you should always
* merely set retval then goto cleanup_and_exit. */ * merely set retval and then goto cleanup_and_exit. */
long size; size_t lineswritten = 0;
int lineswritten = 0; const filestruct *fileptr = fileage;
char *buf = NULL;
const filestruct *fileptr;
FILE *f;
int fd; int fd;
int mask = 0, realexists, anyexists; mode_t original_umask = 0;
struct stat st, lst; /* Our umask, from when nano started. */
char *realname = NULL; int realexists;
/* The result of stat(). True if the file exists, false
* otherwise. If name is a link that points nowhere, realexists
* is false. */
struct stat st;
/* The status fields filled in by stat(). */
int anyexists;
/* Result of lstat(). Same as realexists unless name is a
* link. */
struct stat lst;
/* The status fields filled in by lstat(). */
char *realname;
/* name after ~ expansion. */
FILE *f;
/* The actual file, realname, we are writing to. */
char *tempname = NULL;
/* The temp file name we write to on prepend. */
assert(name != NULL);
if (name[0] == '\0') { if (name[0] == '\0') {
statusbar(_("Cancelled")); statusbar(_("Cancelled"));
return -1; return -1;
} }
if (!tmp) if (!tmp)
titlebar(NULL); titlebar(NULL);
fileptr = fileage;
realname = real_dir_from_tilde(name); realname = real_dir_from_tilde(name);
#ifndef DISABLE_OPERATINGDIR #ifndef DISABLE_OPERATINGDIR
/* If we're writing a temporary file, we're probably going outside /* If we're writing a temporary file, we're probably going outside
the operating directory, so skip the operating directory test. */ * the operating directory, so skip the operating directory test. */
if (!tmp && operating_dir != NULL && check_operating_dir(realname, 0)) { if (!tmp && check_operating_dir(realname, 0) != 0) {
statusbar(_("Can't write outside of %s"), operating_dir); statusbar(_("Can't write outside of %s"), operating_dir);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
#endif #endif
anyexists = lstat(realname, &lst) != -1;
/* New case: if the file exists, just give up. */
if (tmp && anyexists)
goto cleanup_and_exit;
/* If NOFOLLOW_SYMLINKS is set, it doesn't make sense to prepend or
* append to a symlink. Here we warn about the contradiction. */
if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode)) {
statusbar(_("Cannot prepend or append to a symlink with --nofollow set."));
goto cleanup_and_exit;
}
/* Save the state of file at the end of the symlink (if there is /* Save the state of file at the end of the symlink (if there is
one). */ * one). */
realexists = stat(realname, &st); realexists = stat(realname, &st) != -1;
#ifndef NANO_SMALL #ifndef NANO_SMALL
/* We backup only if the backup toggle is set, the file isn't /* We backup only if the backup toggle is set, the file isn't
temporary, and the file already exists. Furthermore, if we aren't * temporary, and the file already exists. Furthermore, if we
appending, prepending, or writing a selection, we backup only if * aren't appending, prepending, or writing a selection, we backup
the file has not been modified by someone else since nano opened * only if the file has not been modified by someone else since nano
it. */ * opened it. */
if (ISSET(BACKUP_FILE) && !tmp && realexists == 0 && if (ISSET(BACKUP_FILE) && !tmp && realexists != 0 &&
(append != 0 || ISSET(MARK_ISSET) || (append != 0 || ISSET(MARK_ISSET) ||
originalfilestat.st_mtime == st.st_mtime)) { originalfilestat.st_mtime == st.st_mtime)) {
FILE *backup_file;
char *backupname = NULL;
char backupbuf[COPYFILEBLOCKSIZE];
size_t bytesread;
struct utimbuf filetime;
/* save the original file's access and modification times */ FILE *backup_file;
char *backupname;
struct utimbuf filetime;
int copy_status;
/* Save the original file's access and modification times. */
filetime.actime = originalfilestat.st_atime; filetime.actime = originalfilestat.st_atime;
filetime.modtime = originalfilestat.st_mtime; filetime.modtime = originalfilestat.st_mtime;
/* open the original file to copy to the backup */ /* Open the original file to copy to the backup. */
f = fopen(realname, "rb"); f = fopen(realname, "rb");
if (f == NULL) { if (f == NULL) {
statusbar(_("Could not read %s for backup: %s"), realname, statusbar(_("Error reading %s: %s"), realname,
strerror(errno)); strerror(errno));
return -1; goto cleanup_and_exit;
} }
backupname = charalloc(strlen(realname) + 2); backupname = charalloc(strlen(realname) + 2);
sprintf(backupname, "%s~", realname); sprintf(backupname, "%s~", realname);
/* get a file descriptor for the destination backup file */ /* Open the destination backup file. Before we write to it, we
* set its permissions, so no unauthorized person can read it as
* we write. */
backup_file = fopen(backupname, "wb"); backup_file = fopen(backupname, "wb");
if (backup_file == NULL) { if (backup_file == NULL ||
statusbar(_("Couldn't write backup: %s"), strerror(errno)); chmod(backupname, originalfilestat.st_mode) == -1) {
statusbar(_("Error writing %s: %s"), backupname, strerror(errno));
free(backupname); free(backupname);
return -1; if (backup_file != NULL)
fclose(backup_file);
fclose(f);
goto cleanup_and_exit;
} }
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "Backing up %s to %s\n", realname, backupname); fprintf(stderr, "Backing up %s to %s\n", realname, backupname);
#endif #endif
/* copy the file */ /* Copy the file. */
while ((bytesread = fread(backupbuf, sizeof(char), copy_status = copy_file(f, backup_file);
COPYFILEBLOCKSIZE, f)) > 0) /* And set metadata. */
if (fwrite(backupbuf, sizeof(char), bytesread, backup_file) <= 0) if (copy_status != 0 || chown(backupname, originalfilestat.st_uid,
break; originalfilestat.st_gid) == -1 ||
fclose(backup_file); utime(backupname, &filetime) == -1) {
fclose(f); free(backupname);
if (copy_status == -1)
if (chmod(backupname, originalfilestat.st_mode) == -1) statusbar(_("Error reading %s: %s"), realname,
statusbar(_("Could not set permissions %o on backup %s: %s"),
originalfilestat.st_mode, backupname,
strerror(errno)); strerror(errno));
else
if (chown(backupname, originalfilestat.st_uid, statusbar(_("Error writing %s: %s"), backupname,
originalfilestat.st_gid) == -1) strerror(errno));
statusbar(_("Could not set owner %d/group %d on backup %s: %s"), goto cleanup_and_exit;
originalfilestat.st_uid, originalfilestat.st_gid, }
backupname, strerror(errno));
if (utime(backupname, &filetime) == -1)
statusbar(_("Could not set access/modification time on backup %s: %s"),
backupname, strerror(errno));
free(backupname); free(backupname);
} }
#endif #endif /* !NANO_SMALL */
/* Stat the link itself for the check... */ /* If NOFOLLOW_SYMLINKS and the file is a link, we aren't doing
anyexists = lstat(realname, &lst); * prepend or append. So we delete the link first, and just
* overwrite. */
/* New case: if the file exists, just give up */ if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode) &&
if (tmp && anyexists != -1) unlink(realname) == -1) {
statusbar(_("Error writing %s: %s"), realname, strerror(errno));
goto cleanup_and_exit; goto cleanup_and_exit;
/* NOTE: If you change this statement, you MUST CHANGE the if }
statement below (that says:
if (realexists == -1 || tmp || (ISSET(NOFOLLOW_SYMLINKS) &&
S_ISLNK(lst.st_mode))) {
to reflect whether or not to link/unlink/rename the file */
else if (append != 2 && (!ISSET(NOFOLLOW_SYMLINKS) || !S_ISLNK(lst.st_mode)
|| tmp)) {
/* Use O_EXCL if tmp is nonzero. This is now copied from joe,
because wiggy says so *shrug*. */
if (append != 0)
fd = open(realname, O_WRONLY | O_CREAT | O_APPEND, (S_IRUSR | S_IWUSR));
else if (tmp)
fd = open(realname, O_WRONLY | O_CREAT | O_EXCL, (S_IRUSR | S_IWUSR));
else
fd = open(realname, O_WRONLY | O_CREAT | O_TRUNC, (S_IRUSR | S_IWUSR));
/* First, just give up if we couldn't even open the file */ original_umask = umask(0);
if (fd == -1) { umask(original_umask);
if (!tmp && ISSET(TEMP_OPT)) { /* If we create a temp file, we don't let anyone else access it. We
UNSET(TEMP_OPT); * create a temp file if tmp is nonzero or if we prepend. */
retval = do_writeout(filename, 1, 0); if (tmp || append == 2)
} else umask(S_IRWXG | S_IRWXO);
statusbar(_("Could not open file for writing: %s"),
strerror(errno)); /* If we are prepending, copy the file to a temp file. */
if (append == 2) {
int fd_source;
FILE *f_source = NULL;
tempname = charalloc(strlen(realname) + 8);
strcpy(tempname, realname);
strcat(tempname, ".XXXXXX");
fd = mkstemp(tempname);
f = NULL;
if (fd != -1) {
f = fdopen(fd, "wb");
if (f == NULL)
close(fd);
}
if (f == NULL) {
statusbar(_("Error writing %s: %s"), tempname, strerror(errno));
unlink(tempname);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
} fd_source = open(realname, O_RDONLY | O_CREAT);
/* Don't follow symlink. Create new file. */ if (fd_source != -1) {
else { f_source = fdopen(fd_source, "rb");
buf = charalloc(strlen(realname) + 8); if (f_source == NULL)
strcpy(buf, realname); close(fd_source);
strcat(buf, ".XXXXXX"); }
if ((fd = mkstemp(buf)) == -1) { if (f_source == NULL) {
if (ISSET(TEMP_OPT)) { statusbar(_("Error reading %s: %s"), realname, strerror(errno));
UNSET(TEMP_OPT); fclose(f);
retval = do_writeout(filename, 1, 0); unlink(tempname);
} else goto cleanup_and_exit;
statusbar(_("Could not open file for writing: %s"), }
strerror(errno));
if (copy_file(f_source, f) != 0) {
statusbar(_("Error writing %s: %s"), tempname, strerror(errno));
unlink(tempname);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
} }
#ifdef DEBUG /* Now open the file in place. Use O_EXCL if tmp is nonzero. This
dump_buffer(fileage); * is now copied from joe, because wiggy says so *shrug*. */
#endif fd = open(realname, O_WRONLY | O_CREAT |
(append == 1 ? O_APPEND : (tmp ? O_EXCL : O_TRUNC)),
S_IRUSR | S_IWUSR);
/* Put the umask back to the user's original value. */
umask(original_umask);
/* First, just give up if we couldn't even open the file. */
if (fd == -1) {
statusbar(_("Error writing %s: %s"), realname, strerror(errno));
unlink(tempname);
goto cleanup_and_exit;
}
f = fdopen(fd, append == 1 ? "ab" : "wb"); f = fdopen(fd, append == 1 ? "ab" : "wb");
if (f == NULL) { if (f == NULL) {
statusbar(_("Could not open file for writing: %s"), strerror(errno)); statusbar(_("Error writing %s: %s"), realname, strerror(errno));
close(fd);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
while (fileptr != NULL && fileptr->next != NULL) { /* There might not be a magic line. There won't be when writing out
int data_len; * a selection. */
assert(fileage != NULL && filebot != NULL);
while (fileptr != filebot) {
size_t data_len = strlen(fileptr->data);
size_t size;
/* Next line is so we discount the "magic line" */ /* Newlines to nulls, just before we write to disk. */
if (filebot == fileptr && fileptr->data[0] == '\0')
break;
data_len = strlen(fileptr->data);
/* newlines to nulls, just before we write to disk */
sunder(fileptr->data); sunder(fileptr->data);
size = fwrite(fileptr->data, 1, data_len, f); size = fwrite(fileptr->data, sizeof(char), data_len, f);
/* nulls to newlines; data_len is the string's real length here */ /* Nulls to newlines; data_len is the string's real length. */
unsunder(fileptr->data, data_len); unsunder(fileptr->data, data_len);
if (size < data_len) { if (size < data_len) {
statusbar(_("Could not open file for writing: %s"), statusbar(_("Error writing %s: %s"), realname, strerror(errno));
strerror(errno));
fclose(f); fclose(f);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
#ifdef DEBUG
else
fprintf(stderr, "Wrote >%s\n", fileptr->data);
#endif
#ifndef NANO_SMALL #ifndef NANO_SMALL
if (ISSET(DOS_FILE) || ISSET(MAC_FILE)) if (ISSET(DOS_FILE) || ISSET(MAC_FILE))
putc('\r', f); if (putc('\r', f) == EOF) {
statusbar(_("Error writing %s: %s"), realname, strerror(errno));
fclose(f);
goto cleanup_and_exit;
}
if (!ISSET(MAC_FILE)) if (!ISSET(MAC_FILE))
#endif #endif
putc('\n', f); if (putc('\n', f) == EOF) {
statusbar(_("Error writing %s: %s"), realname, strerror(errno));
fclose(f);
goto cleanup_and_exit;
}
fileptr = fileptr->next; fileptr = fileptr->next;
lineswritten++; lineswritten++;
} }
if (fileptr != NULL) { /* If we're prepending, open the temp file, and append it to f. */
int data_len = strlen(fileptr->data); if (append == 2) {
int fd_source;
FILE *f_source = NULL;
/* newlines to nulls, just before we write to disk */ fd_source = open(tempname, O_RDONLY | O_CREAT);
sunder(fileptr->data); if (fd_source != -1) {
f_source = fdopen(fd_source, "rb");
size = fwrite(fileptr->data, 1, data_len, f); if (f_source == NULL)
close(fd_source);
/* nulls to newlines; data_len is the string's real length here */ }
unsunder(fileptr->data, data_len); if (f_source == NULL) {
statusbar(_("Error reading %s: %s"), tempname, strerror(errno));
if (size < data_len) { fclose(f);
statusbar(_("Could not open file for writing: %s"), goto cleanup_and_exit;
strerror(errno));
goto cleanup_and_exit;
} else if (data_len > 0) {
#ifndef NANO_SMALL
if (ISSET(DOS_FILE) || ISSET(MAC_FILE)) {
if (putc('\r', f) == EOF) {
statusbar(_("Could not open file for writing: %s"),
strerror(errno));
fclose(f);
goto cleanup_and_exit;
}
lineswritten++;
}
if (!ISSET(MAC_FILE))
#endif
{
if (putc('\n', f) == EOF) {
statusbar(_("Could not open file for writing: %s"),
strerror(errno));
fclose(f);
goto cleanup_and_exit;
}
lineswritten++;
}
} }
}
if (fclose(f) != 0) { if (copy_file(f_source, f) == -1
statusbar(_("Could not close %s: %s"), realname, strerror(errno)); || unlink(tempname) == -1) {
unlink(buf); statusbar(_("Error writing %s: %s"), realname, strerror(errno));
goto cleanup_and_exit;
}
} else if (fclose(f) == EOF) {
statusbar(_("Error writing %s: %s"), realname, strerror(errno));
unlink(tempname);
goto cleanup_and_exit; goto cleanup_and_exit;
} }
/* if we're prepending, open the real file, and append it here */
if (append == 2) {
int fd_source, fd_dest;
FILE *f_source, *f_dest;
int prechar;
if ((fd_dest = open(buf, O_WRONLY | O_APPEND, (S_IRUSR | S_IWUSR))) == -1) {
statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
goto cleanup_and_exit;
}
f_dest = fdopen(fd_dest, "wb");
if (f_dest == NULL) {
statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
close(fd_dest);
goto cleanup_and_exit;
}
if ((fd_source = open(realname, O_RDONLY | O_CREAT)) == -1) {
statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
fclose(f_dest);
goto cleanup_and_exit;
}
f_source = fdopen(fd_source, "rb");
if (f_source == NULL) {
statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
fclose(f_dest);
close(fd_source);
goto cleanup_and_exit;
}
/* Doing this in blocks is an exercise left to some other reader. */
while ((prechar = getc(f_source)) != EOF) {
if (putc(prechar, f_dest) == EOF) {
statusbar(_("Could not open %s for prepend: %s"), realname, strerror(errno));
fclose(f_source);
fclose(f_dest);
goto cleanup_and_exit;
}
}
if (ferror(f_source)) {
statusbar(_("Could not reopen %s: %s"), buf, strerror(errno));
fclose(f_source);
fclose(f_dest);
goto cleanup_and_exit;
}
fclose(f_source);
fclose(f_dest);
}
if (realexists == -1 || tmp ||
(ISSET(NOFOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode))) {
/* Use default umask as file permissions if file is a new file. */
mask = umask(0);
umask(mask);
if (tmp) /* We don't want anyone reading our temporary file! */
mask = S_IRUSR | S_IWUSR;
else
mask = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
S_IWOTH) & ~mask;
} else
/* Use permissions from file we are overwriting. */
mask = st.st_mode;
if (append == 2 ||
(!tmp && (ISSET(NOFOLLOW_SYMLINKS) && S_ISLNK(lst.st_mode)))) {
if (unlink(realname) == -1) {
if (errno != ENOENT) {
statusbar(_("Could not open %s for writing: %s"),
realname, strerror(errno));
unlink(buf);
goto cleanup_and_exit;
}
}
if (link(buf, realname) != -1)
unlink(buf);
else if (errno != EPERM) {
statusbar(_("Could not open %s for writing: %s"),
name, strerror(errno));
unlink(buf);
goto cleanup_and_exit;
} else if (rename(buf, realname) == -1) { /* Try a rename?? */
statusbar(_("Could not open %s for writing: %s"),
realname, strerror(errno));
unlink(buf);
goto cleanup_and_exit;
}
}
if (chmod(realname, mask) == -1)
statusbar(_("Could not set permissions %o on %s: %s"),
mask, realname, strerror(errno));
if (!tmp && append == 0) { if (!tmp && append == 0) {
if (!nonamechange) { if (!nonamechange) {
filename = mallocstrcpy(filename, realname); filename = mallocstrcpy(filename, realname);
@ -1689,8 +1655,8 @@ int write_file(const char *name, int tmp, int append, int nonamechange)
/* Update originalfilestat to reference the file as it is now. */ /* Update originalfilestat to reference the file as it is now. */
stat(filename, &originalfilestat); stat(filename, &originalfilestat);
#endif #endif
statusbar(P_("Wrote %d line", "Wrote %d lines", lineswritten), statusbar(P_("Wrote %u line", "Wrote %u lines", lineswritten),
lineswritten); lineswritten);
UNSET(MODIFIED); UNSET(MODIFIED);
titlebar(NULL); titlebar(NULL);
} }
@ -1699,7 +1665,7 @@ int write_file(const char *name, int tmp, int append, int nonamechange)
cleanup_and_exit: cleanup_and_exit:
free(realname); free(realname);
free(buf); free(tempname);
return retval; return retval;
} }
@ -1850,31 +1816,52 @@ int do_writeout(const char *path, int exiting, int append)
} }
#ifndef NANO_SMALL #ifndef NANO_SMALL
/* Here's where we allow the selected text to be written to /* Here's where we allow the selected text to be written to
a separate file. */ * a separate file. */
if (ISSET(MARK_ISSET) && !exiting) { if (ISSET(MARK_ISSET) && !exiting) {
filestruct *fileagebak = fileage; filestruct *fileagebak = fileage;
filestruct *filebotbak = filebot; filestruct *filebotbak = filebot;
filestruct *cutback = cutbuffer;
int oldmod = ISSET(MODIFIED); int oldmod = ISSET(MODIFIED);
/* write_file() unsets the MODIFIED flag. */ /* write_file() unsets the MODIFIED flag. */
size_t topx;
/* The column of the beginning of the mark. */
char origchar;
/* We replace the character at the end of the mark with
* '\0'. We save the original character, to restore
* it. */
char *origcharloc;
/* The location of the character we nulled. */
cutbuffer = NULL; /* Set fileage as the top of the mark, and filebot as the
* bottom. */
/* Put the marked text in the cutbuffer without changing if (current->lineno > mark_beginbuf->lineno ||
the open file. */ (current->lineno == mark_beginbuf->lineno &&
cut_marked_segment(current, current_x, mark_beginbuf, current_x > mark_beginx)) {
mark_beginx, 0); fileage = mark_beginbuf;
topx = mark_beginx;
fileage = cutbuffer; filebot = current;
filebot = get_cutbottom(); origcharloc = current->data + current_x;
} else {
fileage = current;
topx = current_x;
filebot = mark_beginbuf;
origcharloc = mark_beginbuf->data + mark_beginx;
}
origchar = *origcharloc;
*origcharloc = '\0';
fileage->data += topx;
/* If the line at filebot is blank, treat it as the
* magicline and hence the end of the file. Otherwise,
* treat the line after filebot as the end of the file. */
if (filebot->data[0] != '\0' && filebot->next != NULL)
filebot = filebot->next;
i = write_file(answer, 0, append, 1); i = write_file(answer, 0, append, 1);
/* Now restore everything */ /* Now restore everything. */
free_filestruct(cutbuffer); fileage->data -= topx;
*origcharloc = origchar;
fileage = fileagebak; fileage = fileagebak;
filebot = filebotbak; filebot = filebotbak;
cutbuffer = cutback;
if (oldmod) if (oldmod)
set_modified(); set_modified();
} else } else
@ -2110,7 +2097,7 @@ char **cwd_tab_completion(char *buf, int *num_matches)
strcpy(tmp2, dirname); strcpy(tmp2, dirname);
strcat(tmp2, "/"); strcat(tmp2, "/");
strcat(tmp2, next->d_name); strcat(tmp2, next->d_name);
if (check_operating_dir(tmp2, 1)) { if (check_operating_dir(tmp2, 1) != 0) {
free(tmp2); free(tmp2);
continue; continue;
} }
@ -2631,7 +2618,7 @@ char *do_browser(const char *inpath)
/* Note: the selected file can be outside the operating /* Note: the selected file can be outside the operating
* directory if it is .. or if it is a symlink to * directory if it is .. or if it is a symlink to
* directory outside the operating directory. */ * directory outside the operating directory. */
if (check_operating_dir(filelist[selected], FALSE)) { if (check_operating_dir(filelist[selected], 0) != 0) {
statusbar(_("Can't go outside of %s in restricted mode"), operating_dir); statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
beep(); beep();
break; break;
@ -2704,7 +2691,7 @@ char *do_browser(const char *inpath)
} }
#ifndef DISABLE_OPERATINGDIR #ifndef DISABLE_OPERATINGDIR
if (check_operating_dir(new_path, FALSE)) { if (check_operating_dir(new_path, 0) != 0) {
statusbar(_("Can't go outside of %s in restricted mode"), operating_dir); statusbar(_("Can't go outside of %s in restricted mode"), operating_dir);
free(new_path); free(new_path);
break; break;
@ -2847,7 +2834,7 @@ char *do_browse_from(const char *inpath)
#ifndef DISABLE_OPERATINGDIR #ifndef DISABLE_OPERATINGDIR
/* If the resulting path isn't in the operating directory, use that. */ /* If the resulting path isn't in the operating directory, use that. */
if (check_operating_dir(path, FALSE)) if (check_operating_dir(path, 0) != 0)
path = mallocstrcpy(path, operating_dir); path = mallocstrcpy(path, operating_dir);
#endif #endif

View File

@ -172,7 +172,7 @@ void sc_init_one(shortcut **shortcutage, int key, const char *desc,
#ifndef DISABLE_HELP #ifndef DISABLE_HELP
const char *help, const char *help,
#endif #endif
int alt, int func_key, int misc, int view, int (*func) (void)) int meta, int func_key, int misc, int view, int (*func) (void))
{ {
shortcut *s; shortcut *s;
@ -191,7 +191,7 @@ void sc_init_one(shortcut **shortcutage, int key, const char *desc,
#ifndef DISABLE_HELP #ifndef DISABLE_HELP
s->help = help; s->help = help;
#endif #endif
s->altval = alt; s->metaval = meta;
s->func_key = func_key; s->func_key = func_key;
s->misc = misc; s->misc = misc;
s->viewok = view; s->viewok = view;

View File

@ -403,7 +403,7 @@ void help_init(void)
/* Now add our shortcut info */ /* Now add our shortcut info */
for (s = currshortcut; s != NULL; s = s->next) { for (s = currshortcut; s != NULL; s = s->next) {
/* true if the character in s->altval is shown in first column */ /* true if the character in s->metaval is shown in first column */
int meta_shortcut = 0; int meta_shortcut = 0;
if (s->val != NANO_NO_KEY) { if (s->val != NANO_NO_KEY) {
@ -420,12 +420,12 @@ void help_init(void)
ptr += sprintf(ptr, "^%c", s->val + 64); ptr += sprintf(ptr, "^%c", s->val + 64);
} }
#ifndef NANO_SMALL #ifndef NANO_SMALL
else if (s->altval != NANO_NO_KEY) { else if (s->metaval != NANO_NO_KEY) {
meta_shortcut = 1; meta_shortcut = 1;
if (s->altval == NANO_ALT_SPACE) if (s->metaval == NANO_ALT_SPACE)
ptr += snprintf(ptr, 8, "M-%.5s", _("Space")); ptr += snprintf(ptr, 8, "M-%.5s", _("Space"));
else else
ptr += sprintf(ptr, "M-%c", toupper(s->altval)); ptr += sprintf(ptr, "M-%c", toupper(s->metaval));
} }
#endif #endif
@ -436,8 +436,8 @@ void help_init(void)
*(ptr++) = '\t'; *(ptr++) = '\t';
if (!meta_shortcut && s->altval != NANO_NO_KEY) if (!meta_shortcut && s->metaval != NANO_NO_KEY)
ptr += sprintf(ptr, "(M-%c)", toupper(s->altval)); ptr += sprintf(ptr, "(M-%c)", toupper(s->metaval));
else if (meta_shortcut && s->misc != NANO_NO_KEY) else if (meta_shortcut && s->misc != NANO_NO_KEY)
ptr += sprintf(ptr, "(M-%c)", toupper(s->misc)); ptr += sprintf(ptr, "(M-%c)", toupper(s->misc));
@ -3516,10 +3516,10 @@ int main(int argc, char *argv[])
fprintf(stderr, "AHA! %c (%d)\n", kbinput, kbinput); fprintf(stderr, "AHA! %c (%d)\n", kbinput, kbinput);
#endif #endif
if (meta == 1) { if (meta == 1) {
/* Check for the altkey and misc defs... */ /* Check for the metaval and misc defs... */
for (s = main_list; s != NULL; s = s->next) for (s = main_list; s != NULL; s = s->next)
if ((s->altval > 0 && kbinput == s->altval) || if ((s->metaval != NANO_NO_KEY && kbinput == s->metaval) ||
(s->misc > 0 && kbinput == s->misc)) { (s->misc != NANO_NO_KEY && kbinput == s->misc)) {
if (ISSET(VIEW_MODE) && !s->viewok) if (ISSET(VIEW_MODE) && !s->viewok)
print_view_warning(); print_view_warning();
else { else {

View File

@ -46,13 +46,14 @@
#endif #endif
#ifdef USE_SLANG #ifdef USE_SLANG
/* Slang support enabled */ /* Slang support enabled. Work around Slang's not defining KEY_DC or
* KEY_IC. */
#include <slcurses.h> #include <slcurses.h>
#define KEY_DC SL_KEY_DELETE #define KEY_DC SL_KEY_DELETE
#define KEY_IC SL_KEY_IC #define KEY_IC SL_KEY_IC
#elif defined(HAVE_NCURSES_H) #elif defined(HAVE_NCURSES_H)
#include <ncurses.h> #include <ncurses.h>
#else /* Uh oh */ #else /* Uh oh. */
#include <curses.h> #include <curses.h>
#endif /* CURSES_H */ #endif /* CURSES_H */
@ -71,16 +72,18 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "config.h" #include "config.h"
/* If no snprintf()/vsnprintf(), use the versions from glib. */
#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
#include <glib.h> #include <glib.h>
# ifndef HAVE_SNPRINTF # ifndef HAVE_SNPRINTF
# define snprintf g_snprintf # define snprintf g_snprintf
# endif # endif
# ifndef HAVE_VSNPRINTF # ifndef HAVE_VSNPRINTF
# define vsnprintf g_vsnprintf # define vsnprintf g_vsnprintf
# endif # endif
#endif #endif
/* If no strcasecmp()/strncasecmp(), use the versions we have. */
#ifndef HAVE_STRCASECMP #ifndef HAVE_STRCASECMP
#define strcasecmp nstricmp #define strcasecmp nstricmp
#endif #endif
@ -90,11 +93,11 @@
#endif #endif
/* Assume ERR is defined as -1. To avoid duplicate case values when /* Assume ERR is defined as -1. To avoid duplicate case values when
some key definitions are missing, we have to set all of these, and * some key definitions are missing, we have to set all of these, and
all of the special sentinel values below, to different negative * all of the special sentinel values below, to different negative
values other than -1. */ * values other than -1. */
/* HP-UX 10 & 11 do not seem to support KEY_HOME and KEY_END */ /* HP-UX 10 & 11 do not seem to support KEY_HOME and KEY_END. */
#ifndef KEY_HOME #ifndef KEY_HOME
#define KEY_HOME -2 #define KEY_HOME -2
#endif #endif
@ -103,13 +106,13 @@
#define KEY_END -3 #define KEY_END -3
#endif #endif
/* Slang and SunOS 5.7-5.9 do not seem to support KEY_RESIZE */ /* Slang and SunOS 5.7-5.9 do not seem to support KEY_RESIZE. */
#ifndef KEY_RESIZE #ifndef KEY_RESIZE
#define KEY_RESIZE -4 #define KEY_RESIZE -4
#endif #endif
/* Slang does not seem to support KEY_SUSPEND, KEY_SLEFT, or /* Slang does not seem to support KEY_SUSPEND, KEY_SLEFT, or
KEY_SRIGHT */ * KEY_SRIGHT. */
#ifndef KEY_SUSPEND #ifndef KEY_SUSPEND
#define KEY_SUSPEND -5 #define KEY_SUSPEND -5
#endif #endif
@ -124,6 +127,8 @@
#define VERMSG "GNU nano " VERSION #define VERMSG "GNU nano " VERSION
/* If we aren't using ncurses, turn the mouse support off, as it's
* ncurses-specific. */
#ifndef NCURSES_MOUSE_VERSION #ifndef NCURSES_MOUSE_VERSION
#define DISABLE_MOUSE 1 #define DISABLE_MOUSE 1
#endif #endif
@ -132,12 +137,12 @@
#define DISABLE_WRAPJUSTIFY 1 #define DISABLE_WRAPJUSTIFY 1
#endif #endif
/* Structure types */ /* Structure types. */
typedef struct filestruct { typedef struct filestruct {
char *data; char *data;
struct filestruct *next; /* Next node */ struct filestruct *next; /* Next node. */
struct filestruct *prev; /* Previous node */ struct filestruct *prev; /* Previous node. */
int lineno; /* The line number */ int lineno; /* The line number. */
} filestruct; } filestruct;
#ifdef ENABLE_MULTIBUFFER #ifdef ENABLE_MULTIBUFFER
@ -146,51 +151,56 @@ typedef struct openfilestruct {
#ifndef NANO_SMALL #ifndef NANO_SMALL
struct stat originalfilestat; struct stat originalfilestat;
#endif #endif
struct openfilestruct *next; /* Next node */ struct openfilestruct *next; /* Next node. */
struct openfilestruct *prev; /* Previous node */ struct openfilestruct *prev; /* Previous node. */
struct filestruct *fileage; /* Current file */ struct filestruct *fileage; /* Current file. */
struct filestruct *filebot; /* Current file's last line */ struct filestruct *filebot; /* Current file's last line. */
#ifndef NANO_SMALL #ifndef NANO_SMALL
struct filestruct *file_mark_beginbuf; struct filestruct *file_mark_beginbuf;
/* Current file's beginning marked line */ /* Current file's beginning marked
int file_mark_beginx; /* Current file's beginning marked line's * line. */
x-coordinate position */ int file_mark_beginx; /* Current file's beginning marked
* line's x-coordinate position. */
#endif #endif
int file_current_x; /* Current file's x-coordinate position */ int file_current_x; /* Current file's x-coordinate
int file_current_y; /* Current file's y-coordinate position */ * position. */
int file_current_y; /* Current file's y-coordinate
* position. */
int file_flags; /* Current file's flags: modification int file_flags; /* Current file's flags: modification
status (and marking status, if * status (and marking status, if
available) */ * available). */
int file_placewewant; /* Current file's place we want */ int file_placewewant; /* Current file's place we want. */
int file_totlines; /* Current file's total number of lines */ int file_totlines; /* Current file's total number of
long file_totsize; /* Current file's total size */ * lines. */
int file_lineno; /* Current file's line number */ long file_totsize; /* Current file's total size. */
int file_lineno; /* Current file's line number. */
} openfilestruct; } openfilestruct;
#endif #endif
typedef struct shortcut { typedef struct shortcut {
/* Key values that aren't used should be set to NANO_NO_KEY */ /* Key values that aren't used should be set to NANO_NO_KEY. */
int val; /* Special sentinel key or control key we want int val; /* Special sentinel key or control key we want
* bound */ * bound. */
int altval; /* Alt key we want bound */ int metaval; /* Meta key we want bound. */
int func_key; /* Function key we want bound */ int func_key; /* Function key we want bound. */
int misc; /* Other Alt key we want bound */ int misc; /* Other Meta key we want bound. */
int viewok; /* Is this function legal in view mode? */ int viewok; /* Is this function legal in view mode? */
int (*func) (void); /* Function to call when we catch this key */ int (*func) (void); /* Function to call when we catch this key. */
const char *desc; /* Description, e.g. "Page Up" */ const char *desc; /* Description, e.g. "Page Up". */
#ifndef DISABLE_HELP #ifndef DISABLE_HELP
const char *help; /* Help file entry text */ const char *help; /* Help file entry text. */
#endif #endif
struct shortcut *next; struct shortcut *next;
} shortcut; } shortcut;
#ifndef NANO_SMALL #ifndef NANO_SMALL
typedef struct toggle { typedef struct toggle {
int val; /* Sequence to toggle the key. Should only need 1 */ int val; /* Sequence to toggle the key. Should only need
* one. */
const char *desc; /* Description for when toggle is, uh, toggled, const char *desc; /* Description for when toggle is, uh, toggled,
e.g. "Pico Messages"; we'll append Enabled or e.g. "Pico Messages"; we'll append Enabled or
Disabled */ Disabled. */
int flag; /* What flag actually gets toggled */ int flag; /* What flag actually gets toggled. */
struct toggle *next; struct toggle *next;
} toggle; } toggle;
#endif /* !NANO_SMALL */ #endif /* !NANO_SMALL */
@ -235,8 +245,10 @@ typedef struct historytype {
char *data; char *data;
} historytype; } historytype;
typedef struct historyheadtype { typedef struct historyheadtype {
struct historytype *next; /* keep *next and *prev members together */ struct historytype *next; /* Keep *next and *prev members
struct historytype *prev; /* and in same order as in historytype */ * together. */
struct historytype *prev; /* And in same order as in
* historytype. */
struct historytype *tail; struct historytype *tail;
struct historytype *current; struct historytype *current;
int count; int count;
@ -244,8 +256,8 @@ typedef struct historyheadtype {
} historyheadtype; } historyheadtype;
#endif /* !NANO_SMALL */ #endif /* !NANO_SMALL */
/* Bitwise flags so we can save space (or more correctly, not waste it) */ /* Bitwise flags so we can save space (or more correctly, not waste
* it). */
#define MODIFIED (1<<0) #define MODIFIED (1<<0)
#define KEEP_CUTBUFFER (1<<1) #define KEEP_CUTBUFFER (1<<1)
#define CASE_SENSITIVE (1<<2) #define CASE_SENSITIVE (1<<2)
@ -267,7 +279,7 @@ typedef struct historyheadtype {
#define DOS_FILE (1<<18) #define DOS_FILE (1<<18)
#define MAC_FILE (1<<19) #define MAC_FILE (1<<19)
#define SMOOTHSCROLL (1<<20) #define SMOOTHSCROLL (1<<20)
#define DISABLE_CURPOS (1<<21) /* Damn, we still need it */ #define DISABLE_CURPOS (1<<21) /* Damn, we still need it. */
#define REBIND_DELETE (1<<22) #define REBIND_DELETE (1<<22)
#define NO_CONVERT (1<<23) #define NO_CONVERT (1<<23)
#define BACKUP_FILE (1<<24) #define BACKUP_FILE (1<<24)
@ -278,8 +290,7 @@ typedef struct historyheadtype {
#define HISTORYLOG (1<<29) #define HISTORYLOG (1<<29)
#define JUSTIFY_MODE (1<<30) #define JUSTIFY_MODE (1<<30)
/* Control key sequences, changing these would be very very bad */ /* Control key sequences, changing these would be very very bad. */
#define NANO_CONTROL_SPACE 0 #define NANO_CONTROL_SPACE 0
#define NANO_CONTROL_A 1 #define NANO_CONTROL_A 1
#define NANO_CONTROL_B 2 #define NANO_CONTROL_B 2
@ -347,13 +358,13 @@ typedef struct historyheadtype {
#define NANO_ALT_RBRACKET ']' #define NANO_ALT_RBRACKET ']'
#define NANO_ALT_SPACE ' ' #define NANO_ALT_SPACE ' '
/* Some semi-changeable keybindings; don't play with unless you're sure /* Some semi-changeable keybindings; don't play with these unless you're
you know what you're doing */ * sure you know what you're doing. */
/* No key at all. */ /* No key at all. */
#define NANO_NO_KEY -8 #define NANO_NO_KEY -8
/* Special sentinel key. */ /* Special sentinel key used for search string history. */
#define NANO_HISTORY_KEY -9 #define NANO_HISTORY_KEY -9
/* Normal keys. */ /* Normal keys. */
@ -453,18 +464,18 @@ typedef enum {
TOP, CENTER, NONE TOP, CENTER, NONE
} topmidnone; } topmidnone;
/* Minimum editor window rows required for nano to work correctly */ /* Minimum editor window rows required for nano to work correctly. */
#define MIN_EDITOR_ROWS 3 #define MIN_EDITOR_ROWS 3
/* Minimum editor window cols required for nano to work correctly */ /* Minimum editor window cols required for nano to work correctly. */
#define MIN_EDITOR_COLS 10 #define MIN_EDITOR_COLS 10
/* Default number of characters from end-of-line where text wrapping /* Default number of characters from end-of-line where text wrapping
occurs */ * occurs. */
#define CHARS_FROM_EOL 8 #define CHARS_FROM_EOL 8
/* Maximum number of search history strings saved, same value used for /* Maximum number of search history strings saved, same value used for
replace history */ * replace history. */
#define MAX_SEARCH_HISTORY 100 #define MAX_SEARCH_HISTORY 100
#endif /* !NANO_H */ #endif /* !NANO_H */

View File

@ -140,8 +140,7 @@ void update_color(void);
/* Public functions in cut.c */ /* Public functions in cut.c */
filestruct *get_cutbottom(void); filestruct *get_cutbottom(void);
void add_to_cutbuffer(filestruct *inptr); void add_to_cutbuffer(filestruct *inptr);
void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot, void cut_marked_segment(void);
size_t bot_x, int destructive);
int do_cut_text(void); int do_cut_text(void);
int do_uncut_text(void); int do_uncut_text(void);
@ -207,7 +206,7 @@ void sc_init_one(shortcut **shortcutage, int key, const char *desc,
#ifndef DISABLE_HELP #ifndef DISABLE_HELP
const char *help, const char *help,
#endif #endif
int alt, int func_key, int misc, int view, int (*func) (void)); int meta, int func_key, int misc, int view, int (*func) (void));
#ifndef NANO_SMALL #ifndef NANO_SMALL
void toggle_init_one(int val, const char *desc, int flag); void toggle_init_one(int val, const char *desc, int flag);
void toggle_init(void); void toggle_init(void);

View File

@ -1090,12 +1090,12 @@ int nanogetstr(int allowtabs, const char *buf, const char *def,
fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput,
kbinput); kbinput);
#endif #endif
if (meta == 1 && kbinput == t->altval) if (meta == 1 && (kbinput == t->metaval || kbinput == t->misc))
/* We hit an Alt key. Do like above. We don't /* We hit a Meta key. Do like above. We don't
just ungetch() the letter and let it get * just ungetch() the letter and let it get
caught above cause that screws the * caught above cause that screws the
keypad... */ * keypad... */
return t->altval; return kbinput;
} }
if (is_cntrl_char(kbinput)) if (is_cntrl_char(kbinput))
@ -1223,8 +1223,8 @@ void bottombars(const shortcut *s)
strcpy(keystr, "^?"); strcpy(keystr, "^?");
else else
sprintf(keystr, "^%c", s->val + 64); sprintf(keystr, "^%c", s->val + 64);
} else if (s->altval != NANO_NO_KEY) } else if (s->metaval != NANO_NO_KEY)
sprintf(keystr, "M-%c", toupper(s->altval)); sprintf(keystr, "M-%c", toupper(s->metaval));
onekey(keystr, s->desc, COLS / numcols); onekey(keystr, s->desc, COLS / numcols);