From 8213850df6babfd7d30f4a0a6da608cec4717199 Mon Sep 17 00:00:00 2001 From: David Lawrence Ramsey Date: Wed, 24 Dec 2003 08:03:54 +0000 Subject: [PATCH] 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 --- ChangeLog | 22 +- src/cut.c | 317 +++++++++++++-------------- src/files.c | 593 +++++++++++++++++++++++++-------------------------- src/global.c | 4 +- src/nano.c | 18 +- src/nano.h | 119 ++++++----- src/proto.h | 5 +- src/winio.c | 16 +- 8 files changed, 541 insertions(+), 553 deletions(-) diff --git a/ChangeLog b/ChangeLog index a9eb0e12..98192dcb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,13 +29,26 @@ CVS code - shortcut display routines to handle them. Also modify the shortcut list code to not treat non-control character values 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 can be used in the edit window. New function do_verbatim_input(); changes to do_char(). (DLR) Additional minor tweaks to do_char() by David Benbennick. - 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: + 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() - Prompt the user if we're trying to save an existing file (and 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) main() - 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: findnextstr(), do_replace_loop() - 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 ungetch() the key equivalents of shortcuts we click on or not. (DLR) + nanogetstr() + - Properly interpret the Meta key value in misc if we hit it at + the statusbar prompt. (DLR) do_yesno() - Add a few efficiency/extensibility tweaks. (David Benbennick) - Convert to use the new low-level input functions, and remove diff --git a/src/cut.c b/src/cut.c index 61d88bf9..7652c17f 100644 --- a/src/cut.c +++ b/src/cut.c @@ -28,15 +28,17 @@ #include "proto.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 static int concatenate_cut; /* Should we add this cut string to the - end of the last one? */ + * end of the last one? */ #endif static filestruct *cutbottom = NULL; - /* Pointer to end of cutbuffer */ + /* Pointer to end of cutbuffer. */ filestruct *get_cutbottom(void) { @@ -46,29 +48,27 @@ filestruct *get_cutbottom(void) void add_to_cutbuffer(filestruct *inptr) { #ifdef DEBUG - fprintf(stderr, "add_to_cutbuffer() called with inptr->data = %s\n", - inptr->data); + fprintf(stderr, "add_to_cutbuffer(): inptr->data = %s\n", inptr->data); #endif - if (cutbuffer == NULL) { + if (cutbuffer == NULL) cutbuffer = inptr; - inptr->prev = NULL; #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, - unless we're backing up lines while justifying text. */ + * unless we're backing up lines while justifying text. */ cutbottom->data = charealloc(cutbottom->data, strlen(cutbottom->data) + strlen(inptr->data) + 1); strcat(cutbottom->data, inptr->data); return; + } #endif - } else { + else { cutbottom->next = inptr; inptr->prev = cutbottom; } - - inptr->next = NULL; cutbottom = inptr; + cutbottom->next = NULL; } #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 * bot->data[bot_x - 1]. * - * destructive is whether to actually modify the file structure, if not - * then just copy the buffer into cutbuffer and don't pull it from the - * file. + * We maintain totsize, totlines, filebot, the magicline, and line + * numbers. Also, we set current and current_x so the cursor will be on + * the first character after what was cut. We do not do any screen + * updates. * - * If destructive, then we maintain totsize, totlines, filebot, the - * magic line, and line numbers. Also, we set current and current_x so - * 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) + * Note cutbuffer might not be NULL if "cut to end" is used. */ +void cut_marked_segment(void) { - filestruct *tmp, *next; + filestruct *top; + filestruct *bot; + filestruct *tmp; + size_t top_x; + size_t bot_x; 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; - assert(top != NULL && bot != NULL); + assert(current != NULL && mark_beginbuf != NULL); - /* Make top be no later than bot. */ - if (top->lineno > bot->lineno) { - filestruct *swap = top; - int swap2 = top_x; + /* Set up the top and bottom lines and coordinates of the marked + * text. */ + mark_order((const filestruct **)&top, &top_x, + (const filestruct **)&bot, &bot_x); - top = bot; - bot = swap; - - 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. */ + /* Make the first cut line manually. Move the cut part of the top + * line into tmp, and set newsize to that partial line's length. */ tmp = copy_node(top); 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); - add_to_cutbuffer(tmp); - /* And make the remainder line manually too. */ - if (destructive) { - current_x = top_x; - totsize -= newsize; - totlines -= bot->lineno - top->lineno; - - newsize = top_x + strlen(bot->data + bot_x) + 1; - if (top == bot) { - /* In this case, the remainder line is shorter, so 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); - } 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); - } + /* Add the contents of tmp to the cutbuffer. Note that cutbuffer + * might be non-NULL if we have cut to end enabled. */ + if (cutbuffer == NULL) { + cutbuffer = tmp; + cutbottom = tmp; + } else { + cutbottom->next = tmp; + tmp->prev = cutbottom; + cutbottom = tmp; } + /* 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) { + /* 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 dump_buffer(cutbuffer); #endif return; } - tmp = top->next; - while (tmp != bot) { - next = tmp->next; - if (!destructive) - tmp = copy_node(tmp); - else - totsize -= strlen(tmp->data) + 1; - add_to_cutbuffer(tmp); - tmp = next; - } + /* Update totsize to account for the cut part of the last line. */ + totsize -= bot_x + 1; + + /* Here, the top remainder line might get longer (if the bottom + * remainder line is added to the end of it), so we realloc() it + * first. */ + top->data = charealloc(top->data, newsize); + charmove(top->data + top_x, bot->data + bot_x, newsize - top_x); + + 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. */ - tmp = copy_node(bot); - null_at(&tmp->data, bot_x); - add_to_cutbuffer(tmp); -#ifdef DEBUG - dump_buffer(cutbuffer); -#endif + null_at(&bot->data, bot_x); - if (destructive) { - top->next = bot->next; - if (top->next != NULL) - top->next->prev = top; - delete_node(bot); - renumber(top); - current = top; - if (bot == filebot) { - filebot = top; - assert(bot_x == 0); - if (top_x > 0) - new_magicline(); - } + /* Move the rest of the cut text (other than the cut part of the top + * line) from the buffer to the end of the cutbuffer, and fix the + * edit buffer to account for the cut text. */ + top->next = bot->next; + cutbottom = bot; + cutbottom->next = NULL; + if (top->next != NULL) + top->next->prev = top; + renumber(top); + current = top; + + /* 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 dump_buffer(cutbuffer); @@ -194,9 +197,6 @@ void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot, int do_cut_text(void) { filestruct *fileptr; -#ifndef NANO_SMALL - int dontupdate = 0; -#endif assert(current != NULL && current->data != NULL); @@ -214,8 +214,8 @@ int do_cut_text(void) #endif } - /* You can't cut the magic line except with the mark. But - trying does clear the cutbuffer if KEEP_CUTBUFFER is not set. */ + /* You can't cut the magicline except with the mark. But trying + * does clear the cutbuffer if KEEP_CUTBUFFER is not set. */ if (current == filebot #ifndef NANO_SMALL && !ISSET(MARK_ISSET) @@ -231,11 +231,12 @@ int do_cut_text(void) if (current->data[current_x] == '\0') { /* 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) { filestruct *junk = make_new_node(current); - junk->data = charalloc(1); + junk->data = charalloc(1); junk->data[0] = '\0'; add_to_cutbuffer(junk); #ifdef DEBUG @@ -251,33 +252,22 @@ int do_cut_text(void) mark_beginx = strlen(current->data); mark_beginbuf = current; - dontupdate = 1; } } if (ISSET(MARK_ISSET)) { - /* Don't do_update() and move the screen position if the marked - 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); + cut_marked_segment(); placewewant = xplustabs(); UNSET(MARK_ISSET); /* 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 - end of this cut, as Pico does. */ + * the first line of any cut done immediately afterward to the + * end of this cut, as Pico does. */ if (current == mark_beginbuf && current_x < strlen(current->data)) concatenate_cut = 1; marked_cut = 1; - if (dontupdate) - edit_refresh(); - else - edit_update(current, CENTER); + edit_refresh(); set_modified(); return 1; @@ -310,29 +300,26 @@ int do_cut_text(void) #ifndef NANO_SMALL concatenate_cut = 0; #endif - placewewant = 0; return 1; } int do_uncut_text(void) { - filestruct *tmp = current, *fileptr = current; - filestruct *newbuf = NULL, *newend = NULL; - char *tmpstr, *tmpstr2; - filestruct *hold = current; - int i; + filestruct *tmp = current; + filestruct *newbuf = NULL; + filestruct *newend = NULL; #ifndef DISABLE_WRAPPING wrap_reset(); #endif check_statblank(); - if (cutbuffer == NULL || fileptr == NULL) + if (cutbuffer == NULL || current == NULL) return 0; /* AIEEEEEEEEEEEE */ /* 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 - beginning of the line, set placewewant to 0. Pico does both of - these. */ + * 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 + * these. */ if (marked_cut == 0) { if (current_x != 0) 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 - magicline in advance. */ + * magicline in advance, as Pico does. */ if (current->next == NULL) new_magicline(); - if (marked_cut == 0 || cutbuffer->next != NULL) - { + if (marked_cut == 0 || cutbuffer->next != NULL) { newbuf = copy_filestruct(cutbuffer); for (newend = newbuf; newend->next != NULL && newend != NULL; newend = newend->next) totlines++; } - /* Hook newbuf into fileptr */ + /* Hook newbuf in at current. */ if (marked_cut != 0) { - int recenter_me = 0; - /* Should we eventually use edit_update(CENTER)? */ + filestruct *hold = current; - /* If there's only one line in the cutbuffer */ + /* If there's only one line in the cutbuffer... */ if (cutbuffer->next == NULL) { size_t buf_len = strlen(cutbuffer->data); size_t cur_len = strlen(current->data); @@ -367,22 +352,24 @@ int do_uncut_text(void) charmove(current->data + current_x + buf_len, current->data + current_x, cur_len - current_x + 1); 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; totsize += buf_len; placewewant = xplustabs(); - update_cursor(); - } else { /* yuck -- no kidding! */ + } else { /* Yuck -- no kidding! */ + char *tmpstr, *tmpstr2; + tmp = current->next; - /* New beginning */ + + /* New beginning. */ tmpstr = charalloc(current_x + strlen(newbuf->data) + 1); strncpy(tmpstr, current->data, current_x); strcpy(&tmpstr[current_x], newbuf->data); totsize += strlen(newbuf->data) + strlen(newend->data) + 1; - /* New end */ + /* New end. */ tmpstr2 = charalloc(strlen(newend->data) + strlen(¤t->data[current_x]) + 1); strcpy(tmpstr2, newend->data); @@ -401,35 +388,28 @@ int do_uncut_text(void) newend->next = tmp; - /* If tmp isn't null, we're in the middle: update the - prev pointer. If it IS null, we're at the end; update - the filebot pointer */ - + /* If tmp isn't NULL, we're in the middle: update the + * prev pointer. If it IS NULL, we're at the end; update + * the filebot pointer. */ if (tmp != NULL) tmp->prev = newend; else { - /* Fix the editbot pointer too */ - if (editbot == filebot) - editbot = newend; filebot = newend; 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) totsize += strlen(tmp->data) + 1; current = newend; - if (editbot->lineno < newend->lineno) - recenter_me = 1; } /* 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 - 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 - work night. */ - + * 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. + * There must be a better way to do this, but not at 1 AM on a + * work night. */ if (marked_cut == 2) { tmp = make_new_node(current); tmp->data = mallocstrcpy(NULL, current->data + current_x); @@ -439,7 +419,7 @@ int do_uncut_text(void) current_x = 0; placewewant = 0; - /* Extra line added, update stuff */ + /* Extra line added; update stuff. */ totlines++; totsize++; } @@ -451,41 +431,32 @@ int do_uncut_text(void) dump_buffer(cutbuffer); #endif set_modified(); - if (recenter_me) - edit_update(current, CENTER); - else - edit_refresh(); + edit_refresh(); return 0; } - if (fileptr != fileage) { - tmp = fileptr->prev; + if (current != fileage) { + tmp = current->prev; tmp->next = newbuf; newbuf->prev = tmp; } else 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 => */ if (current_y == 0) edittop = newbuf; - /* Connect the end of the buffer to the filestruct */ - newend->next = fileptr; - fileptr->prev = newend; + /* Connect the end of the buffer to the filestruct. */ + newend->next = current; + current->prev = newend; /* Recalculate size *sigh* */ - for (tmp = newbuf; tmp != fileptr; tmp = tmp->next) + for (tmp = newbuf; tmp != current; tmp = tmp->next) totsize += strlen(tmp->data) + 1; - i = editbot->lineno; renumber(newbuf); - /* Center the screen if we've moved beyond the line numbers of both - the old and new editbots */ - if (i < newend->lineno && editbot->lineno < newend->lineno) - edit_update(fileptr, CENTER); - else - edit_refresh(); + edit_refresh(); #ifdef DEBUG dump_buffer_reverse(); diff --git a/src/files.c b/src/files.c index 46422a10..3fc82332 100644 --- a/src/files.c +++ b/src/files.c @@ -522,7 +522,7 @@ int do_insertfile(int loading_file) #endif #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"), operating_dir); 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 * 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) { - /* The char *full_operating_dir is global for mem cleanup, and - therefore we only need to get it the first time this function - is called; also, a relative operating directory path will - only be handled properly if this is done */ + /* The char *full_operating_dir is global for mem cleanup. It + * should have already been initialized by init_operating_dir(). + * Also, a relative operating directory path will only be handled + * properly if this is done. */ char *fullpath; int retval = 0; 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) return 0; + assert(full_operating_dir != NULL); 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) - return 1; + return allow_tabcomp; whereami1 = strstr(fullpath, full_operating_dir); if (allow_tabcomp) whereami2 = strstr(full_operating_dir, fullpath); - /* if both searches failed, we're outside the operating directory */ - /* otherwise */ - /* check the search results; if the full operating directory path is - not at the beginning of the full current path (for normal usage) - and vice versa (for tab completion, if we're allowing it), we're - outside the operating directory */ + /* If both searches failed, we're outside the operating directory. + * Otherwise, check the search results; if the full operating + * directory path is not at the beginning of the full current path + * (for normal usage) and vice versa (for tab completion, if we're + * allowing it), we're outside the operating directory. */ if (whereami1 != fullpath && whereami2 != full_operating_dir) retval = 1; free(fullpath); - /* otherwise, we're still inside it */ + + /* Otherwise, we're still inside it. */ return retval; } #endif -/* - * Write a file out. If tmp is nonzero, we set the umask to 0600, - * we don't set the global variable filename to its name, and don't - * print out how many lines we wrote on the statusbar. +/* Read from inn, write to out. We assume inn is opened for reading, + * and out for writing. We return 0 on success, -1 on read error, -2 on + * write error. */ +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 - * it when spell checking or dumping the file on an error. + * tmp means we are writing a temporary file in a secure fashion. We + * use it when spell checking or dumping the file on an error. * * append == 1 means we are appending instead of overwriting. * append == 2 means we are prepending instead of overwriting. * * nonamechange means don't change the current filename, it is ignored * 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 retval = -1; /* Instead of returning in this function, you should always - * merely set retval then goto cleanup_and_exit. */ - long size; - int lineswritten = 0; - char *buf = NULL; - const filestruct *fileptr; - FILE *f; + * merely set retval and then goto cleanup_and_exit. */ + size_t lineswritten = 0; + const filestruct *fileptr = fileage; int fd; - int mask = 0, realexists, anyexists; - struct stat st, lst; - char *realname = NULL; + mode_t original_umask = 0; + /* Our umask, from when nano started. */ + 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') { statusbar(_("Cancelled")); return -1; } if (!tmp) titlebar(NULL); - fileptr = fileage; realname = real_dir_from_tilde(name); #ifndef DISABLE_OPERATINGDIR /* If we're writing a temporary file, we're probably going outside - the operating directory, so skip the operating directory test. */ - if (!tmp && operating_dir != NULL && check_operating_dir(realname, 0)) { + * the operating directory, so skip the operating directory test. */ + if (!tmp && check_operating_dir(realname, 0) != 0) { statusbar(_("Can't write outside of %s"), operating_dir); goto cleanup_and_exit; } #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 - one). */ - realexists = stat(realname, &st); + * one). */ + realexists = stat(realname, &st) != -1; #ifndef NANO_SMALL /* We backup only if the backup toggle is set, the file isn't - temporary, and the file already exists. Furthermore, if we aren't - appending, prepending, or writing a selection, we backup only if - the file has not been modified by someone else since nano opened - it. */ - if (ISSET(BACKUP_FILE) && !tmp && realexists == 0 && - (append != 0 || ISSET(MARK_ISSET) || - originalfilestat.st_mtime == st.st_mtime)) { - FILE *backup_file; - char *backupname = NULL; - char backupbuf[COPYFILEBLOCKSIZE]; - size_t bytesread; - struct utimbuf filetime; + * temporary, and the file already exists. Furthermore, if we + * aren't appending, prepending, or writing a selection, we backup + * only if the file has not been modified by someone else since nano + * opened it. */ + if (ISSET(BACKUP_FILE) && !tmp && realexists != 0 && + (append != 0 || ISSET(MARK_ISSET) || + originalfilestat.st_mtime == st.st_mtime)) { - /* 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.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"); if (f == NULL) { - statusbar(_("Could not read %s for backup: %s"), realname, + statusbar(_("Error reading %s: %s"), realname, strerror(errno)); - return -1; + goto cleanup_and_exit; } backupname = charalloc(strlen(realname) + 2); 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"); - if (backup_file == NULL) { - statusbar(_("Couldn't write backup: %s"), strerror(errno)); + if (backup_file == NULL || + chmod(backupname, originalfilestat.st_mode) == -1) { + statusbar(_("Error writing %s: %s"), backupname, strerror(errno)); free(backupname); - return -1; + if (backup_file != NULL) + fclose(backup_file); + fclose(f); + goto cleanup_and_exit; } #ifdef DEBUG fprintf(stderr, "Backing up %s to %s\n", realname, backupname); #endif - /* copy the file */ - while ((bytesread = fread(backupbuf, sizeof(char), - COPYFILEBLOCKSIZE, f)) > 0) - if (fwrite(backupbuf, sizeof(char), bytesread, backup_file) <= 0) - break; - fclose(backup_file); - fclose(f); - - if (chmod(backupname, originalfilestat.st_mode) == -1) - statusbar(_("Could not set permissions %o on backup %s: %s"), - originalfilestat.st_mode, backupname, + /* Copy the file. */ + copy_status = copy_file(f, backup_file); + /* And set metadata. */ + if (copy_status != 0 || chown(backupname, originalfilestat.st_uid, + originalfilestat.st_gid) == -1 || + utime(backupname, &filetime) == -1) { + free(backupname); + if (copy_status == -1) + statusbar(_("Error reading %s: %s"), realname, strerror(errno)); - - if (chown(backupname, originalfilestat.st_uid, - originalfilestat.st_gid) == -1) - statusbar(_("Could not set owner %d/group %d on backup %s: %s"), - 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)); - + else + statusbar(_("Error writing %s: %s"), backupname, + strerror(errno)); + goto cleanup_and_exit; + } free(backupname); } -#endif +#endif /* !NANO_SMALL */ - /* Stat the link itself for the check... */ - anyexists = lstat(realname, &lst); - - /* New case: if the file exists, just give up */ - if (tmp && anyexists != -1) + /* If NOFOLLOW_SYMLINKS and the file is a link, we aren't doing + * prepend or append. So we delete the link first, and just + * overwrite. */ + if (ISSET(NOFOLLOW_SYMLINKS) && anyexists && S_ISLNK(lst.st_mode) && + unlink(realname) == -1) { + statusbar(_("Error writing %s: %s"), realname, strerror(errno)); 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 */ - if (fd == -1) { - if (!tmp && ISSET(TEMP_OPT)) { - UNSET(TEMP_OPT); - retval = do_writeout(filename, 1, 0); - } else - statusbar(_("Could not open file for writing: %s"), - strerror(errno)); + original_umask = umask(0); + umask(original_umask); + /* If we create a temp file, we don't let anyone else access it. We + * create a temp file if tmp is nonzero or if we prepend. */ + if (tmp || append == 2) + umask(S_IRWXG | S_IRWXO); + + /* 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; } - } - /* Don't follow symlink. Create new file. */ - else { - buf = charalloc(strlen(realname) + 8); - strcpy(buf, realname); - strcat(buf, ".XXXXXX"); - if ((fd = mkstemp(buf)) == -1) { - if (ISSET(TEMP_OPT)) { - UNSET(TEMP_OPT); - retval = do_writeout(filename, 1, 0); - } else - statusbar(_("Could not open file for writing: %s"), - strerror(errno)); + fd_source = open(realname, O_RDONLY | O_CREAT); + if (fd_source != -1) { + f_source = fdopen(fd_source, "rb"); + if (f_source == NULL) + close(fd_source); + } + if (f_source == NULL) { + statusbar(_("Error reading %s: %s"), realname, strerror(errno)); + fclose(f); + unlink(tempname); + goto cleanup_and_exit; + } + + if (copy_file(f_source, f) != 0) { + statusbar(_("Error writing %s: %s"), tempname, strerror(errno)); + unlink(tempname); goto cleanup_and_exit; } } -#ifdef DEBUG - dump_buffer(fileage); -#endif + /* Now open the file in place. Use O_EXCL if tmp is nonzero. This + * is now copied from joe, because wiggy says so *shrug*. */ + 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"); 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; } - while (fileptr != NULL && fileptr->next != NULL) { - int data_len; + /* There might not be a magic line. There won't be when writing out + * 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" */ - if (filebot == fileptr && fileptr->data[0] == '\0') - break; - - data_len = strlen(fileptr->data); - - /* newlines to nulls, just before we write to disk */ + /* Newlines to nulls, just before we write to disk. */ 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); if (size < data_len) { - statusbar(_("Could not open file for writing: %s"), - strerror(errno)); + statusbar(_("Error writing %s: %s"), realname, strerror(errno)); fclose(f); goto cleanup_and_exit; } -#ifdef DEBUG - else - fprintf(stderr, "Wrote >%s\n", fileptr->data); -#endif #ifndef NANO_SMALL 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)) #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; lineswritten++; } - if (fileptr != NULL) { - int data_len = strlen(fileptr->data); + /* If we're prepending, open the temp file, and append it to f. */ + if (append == 2) { + int fd_source; + FILE *f_source = NULL; - /* newlines to nulls, just before we write to disk */ - sunder(fileptr->data); - - size = fwrite(fileptr->data, 1, data_len, f); - - /* nulls to newlines; data_len is the string's real length here */ - unsunder(fileptr->data, data_len); - - if (size < data_len) { - statusbar(_("Could not open file for writing: %s"), - 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++; - } + fd_source = open(tempname, O_RDONLY | O_CREAT); + if (fd_source != -1) { + f_source = fdopen(fd_source, "rb"); + if (f_source == NULL) + close(fd_source); + } + if (f_source == NULL) { + statusbar(_("Error reading %s: %s"), tempname, strerror(errno)); + fclose(f); + goto cleanup_and_exit; } - } - if (fclose(f) != 0) { - statusbar(_("Could not close %s: %s"), realname, strerror(errno)); - unlink(buf); + if (copy_file(f_source, f) == -1 + || unlink(tempname) == -1) { + 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; } - /* 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 (!nonamechange) { 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. */ stat(filename, &originalfilestat); #endif - statusbar(P_("Wrote %d line", "Wrote %d lines", lineswritten), - lineswritten); + statusbar(P_("Wrote %u line", "Wrote %u lines", lineswritten), + lineswritten); UNSET(MODIFIED); titlebar(NULL); } @@ -1699,7 +1665,7 @@ int write_file(const char *name, int tmp, int append, int nonamechange) cleanup_and_exit: free(realname); - free(buf); + free(tempname); return retval; } @@ -1850,31 +1816,52 @@ int do_writeout(const char *path, int exiting, int append) } #ifndef NANO_SMALL - /* Here's where we allow the selected text to be written to - a separate file. */ + /* Here's where we allow the selected text to be written to + * a separate file. */ if (ISSET(MARK_ISSET) && !exiting) { filestruct *fileagebak = fileage; filestruct *filebotbak = filebot; - filestruct *cutback = cutbuffer; int oldmod = ISSET(MODIFIED); /* 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; - - /* Put the marked text in the cutbuffer without changing - the open file. */ - cut_marked_segment(current, current_x, mark_beginbuf, - mark_beginx, 0); - - fileage = cutbuffer; - filebot = get_cutbottom(); + /* Set fileage as the top of the mark, and filebot as the + * bottom. */ + if (current->lineno > mark_beginbuf->lineno || + (current->lineno == mark_beginbuf->lineno && + current_x > mark_beginx)) { + fileage = mark_beginbuf; + topx = mark_beginx; + filebot = current; + 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); - /* Now restore everything */ - free_filestruct(cutbuffer); + /* Now restore everything. */ + fileage->data -= topx; + *origcharloc = origchar; fileage = fileagebak; filebot = filebotbak; - cutbuffer = cutback; if (oldmod) set_modified(); } else @@ -2110,7 +2097,7 @@ char **cwd_tab_completion(char *buf, int *num_matches) strcpy(tmp2, dirname); strcat(tmp2, "/"); strcat(tmp2, next->d_name); - if (check_operating_dir(tmp2, 1)) { + if (check_operating_dir(tmp2, 1) != 0) { free(tmp2); continue; } @@ -2631,7 +2618,7 @@ char *do_browser(const char *inpath) /* Note: the selected file can be outside the operating * directory if it is .. or if it is a symlink to * 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); beep(); break; @@ -2704,7 +2691,7 @@ char *do_browser(const char *inpath) } #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); free(new_path); break; @@ -2847,7 +2834,7 @@ char *do_browse_from(const char *inpath) #ifndef DISABLE_OPERATINGDIR /* 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); #endif diff --git a/src/global.c b/src/global.c index 78da209c..e2c8b11b 100644 --- a/src/global.c +++ b/src/global.c @@ -172,7 +172,7 @@ void sc_init_one(shortcut **shortcutage, int key, const char *desc, #ifndef DISABLE_HELP const char *help, #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; @@ -191,7 +191,7 @@ void sc_init_one(shortcut **shortcutage, int key, const char *desc, #ifndef DISABLE_HELP s->help = help; #endif - s->altval = alt; + s->metaval = meta; s->func_key = func_key; s->misc = misc; s->viewok = view; diff --git a/src/nano.c b/src/nano.c index cce32695..351125ee 100644 --- a/src/nano.c +++ b/src/nano.c @@ -403,7 +403,7 @@ void help_init(void) /* Now add our shortcut info */ 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; if (s->val != NANO_NO_KEY) { @@ -420,12 +420,12 @@ void help_init(void) ptr += sprintf(ptr, "^%c", s->val + 64); } #ifndef NANO_SMALL - else if (s->altval != NANO_NO_KEY) { + else if (s->metaval != NANO_NO_KEY) { meta_shortcut = 1; - if (s->altval == NANO_ALT_SPACE) + if (s->metaval == NANO_ALT_SPACE) ptr += snprintf(ptr, 8, "M-%.5s", _("Space")); else - ptr += sprintf(ptr, "M-%c", toupper(s->altval)); + ptr += sprintf(ptr, "M-%c", toupper(s->metaval)); } #endif @@ -436,8 +436,8 @@ void help_init(void) *(ptr++) = '\t'; - if (!meta_shortcut && s->altval != NANO_NO_KEY) - ptr += sprintf(ptr, "(M-%c)", toupper(s->altval)); + if (!meta_shortcut && s->metaval != NANO_NO_KEY) + ptr += sprintf(ptr, "(M-%c)", toupper(s->metaval)); else if (meta_shortcut && s->misc != NANO_NO_KEY) 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); #endif 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) - if ((s->altval > 0 && kbinput == s->altval) || - (s->misc > 0 && kbinput == s->misc)) { + if ((s->metaval != NANO_NO_KEY && kbinput == s->metaval) || + (s->misc != NANO_NO_KEY && kbinput == s->misc)) { if (ISSET(VIEW_MODE) && !s->viewok) print_view_warning(); else { diff --git a/src/nano.h b/src/nano.h index 44c23196..41561b82 100644 --- a/src/nano.h +++ b/src/nano.h @@ -46,13 +46,14 @@ #endif #ifdef USE_SLANG -/* Slang support enabled */ +/* Slang support enabled. Work around Slang's not defining KEY_DC or + * KEY_IC. */ #include #define KEY_DC SL_KEY_DELETE #define KEY_IC SL_KEY_IC #elif defined(HAVE_NCURSES_H) #include -#else /* Uh oh */ +#else /* Uh oh. */ #include #endif /* CURSES_H */ @@ -71,16 +72,18 @@ #include #include "config.h" +/* If no snprintf()/vsnprintf(), use the versions from glib. */ #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) #include # ifndef HAVE_SNPRINTF -# define snprintf g_snprintf +# define snprintf g_snprintf # endif # ifndef HAVE_VSNPRINTF -# define vsnprintf g_vsnprintf +# define vsnprintf g_vsnprintf # endif #endif +/* If no strcasecmp()/strncasecmp(), use the versions we have. */ #ifndef HAVE_STRCASECMP #define strcasecmp nstricmp #endif @@ -90,11 +93,11 @@ #endif /* 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 - all of the special sentinel values below, to different negative - values other than -1. */ + * some key definitions are missing, we have to set all of these, and + * all of the special sentinel values below, to different negative + * 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 #define KEY_HOME -2 #endif @@ -103,13 +106,13 @@ #define KEY_END -3 #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 #define KEY_RESIZE -4 #endif /* Slang does not seem to support KEY_SUSPEND, KEY_SLEFT, or - KEY_SRIGHT */ + * KEY_SRIGHT. */ #ifndef KEY_SUSPEND #define KEY_SUSPEND -5 #endif @@ -124,6 +127,8 @@ #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 #define DISABLE_MOUSE 1 #endif @@ -132,12 +137,12 @@ #define DISABLE_WRAPJUSTIFY 1 #endif -/* Structure types */ +/* Structure types. */ typedef struct filestruct { char *data; - struct filestruct *next; /* Next node */ - struct filestruct *prev; /* Previous node */ - int lineno; /* The line number */ + struct filestruct *next; /* Next node. */ + struct filestruct *prev; /* Previous node. */ + int lineno; /* The line number. */ } filestruct; #ifdef ENABLE_MULTIBUFFER @@ -146,51 +151,56 @@ typedef struct openfilestruct { #ifndef NANO_SMALL struct stat originalfilestat; #endif - struct openfilestruct *next; /* Next node */ - struct openfilestruct *prev; /* Previous node */ - struct filestruct *fileage; /* Current file */ - struct filestruct *filebot; /* Current file's last line */ + struct openfilestruct *next; /* Next node. */ + struct openfilestruct *prev; /* Previous node. */ + struct filestruct *fileage; /* Current file. */ + struct filestruct *filebot; /* Current file's last line. */ #ifndef NANO_SMALL struct filestruct *file_mark_beginbuf; - /* Current file's beginning marked line */ - int file_mark_beginx; /* Current file's beginning marked line's - x-coordinate position */ + /* Current file's beginning marked + * line. */ + int file_mark_beginx; /* Current file's beginning marked + * line's x-coordinate position. */ #endif - int file_current_x; /* Current file's x-coordinate position */ - int file_current_y; /* Current file's y-coordinate position */ + int file_current_x; /* Current file's x-coordinate + * position. */ + int file_current_y; /* Current file's y-coordinate + * position. */ int file_flags; /* Current file's flags: modification - status (and marking status, if - available) */ - int file_placewewant; /* Current file's place we want */ - int file_totlines; /* Current file's total number of lines */ - long file_totsize; /* Current file's total size */ - int file_lineno; /* Current file's line number */ + * status (and marking status, if + * available). */ + int file_placewewant; /* Current file's place we want. */ + int file_totlines; /* Current file's total number of + * lines. */ + long file_totsize; /* Current file's total size. */ + int file_lineno; /* Current file's line number. */ } openfilestruct; #endif 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 - * bound */ - int altval; /* Alt key we want bound */ - int func_key; /* Function key we want bound */ - int misc; /* Other Alt key we want bound */ + * bound. */ + int metaval; /* Meta key we want bound. */ + int func_key; /* Function key we want bound. */ + int misc; /* Other Meta key we want bound. */ int viewok; /* Is this function legal in view mode? */ - int (*func) (void); /* Function to call when we catch this key */ - const char *desc; /* Description, e.g. "Page Up" */ + int (*func) (void); /* Function to call when we catch this key. */ + const char *desc; /* Description, e.g. "Page Up". */ #ifndef DISABLE_HELP - const char *help; /* Help file entry text */ + const char *help; /* Help file entry text. */ #endif struct shortcut *next; } shortcut; #ifndef NANO_SMALL 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, e.g. "Pico Messages"; we'll append Enabled or - Disabled */ - int flag; /* What flag actually gets toggled */ + Disabled. */ + int flag; /* What flag actually gets toggled. */ struct toggle *next; } toggle; #endif /* !NANO_SMALL */ @@ -235,8 +245,10 @@ typedef struct historytype { char *data; } historytype; typedef struct historyheadtype { - struct historytype *next; /* keep *next and *prev members together */ - struct historytype *prev; /* and in same order as in historytype */ + struct historytype *next; /* Keep *next and *prev members + * together. */ + struct historytype *prev; /* And in same order as in + * historytype. */ struct historytype *tail; struct historytype *current; int count; @@ -244,8 +256,8 @@ typedef struct historyheadtype { } historyheadtype; #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 KEEP_CUTBUFFER (1<<1) #define CASE_SENSITIVE (1<<2) @@ -267,7 +279,7 @@ typedef struct historyheadtype { #define DOS_FILE (1<<18) #define MAC_FILE (1<<19) #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 NO_CONVERT (1<<23) #define BACKUP_FILE (1<<24) @@ -278,8 +290,7 @@ typedef struct historyheadtype { #define HISTORYLOG (1<<29) #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_A 1 #define NANO_CONTROL_B 2 @@ -347,13 +358,13 @@ typedef struct historyheadtype { #define NANO_ALT_RBRACKET ']' #define NANO_ALT_SPACE ' ' -/* Some semi-changeable keybindings; don't play with unless you're sure - you know what you're doing */ +/* Some semi-changeable keybindings; don't play with these unless you're + * sure you know what you're doing. */ /* No key at all. */ #define NANO_NO_KEY -8 -/* Special sentinel key. */ +/* Special sentinel key used for search string history. */ #define NANO_HISTORY_KEY -9 /* Normal keys. */ @@ -453,18 +464,18 @@ typedef enum { TOP, CENTER, NONE } 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 -/* 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 /* Default number of characters from end-of-line where text wrapping - occurs */ + * occurs. */ #define CHARS_FROM_EOL 8 /* Maximum number of search history strings saved, same value used for - replace history */ + * replace history. */ #define MAX_SEARCH_HISTORY 100 #endif /* !NANO_H */ diff --git a/src/proto.h b/src/proto.h index 097588a5..93accea3 100644 --- a/src/proto.h +++ b/src/proto.h @@ -140,8 +140,7 @@ void update_color(void); /* Public functions in cut.c */ filestruct *get_cutbottom(void); void add_to_cutbuffer(filestruct *inptr); -void cut_marked_segment(filestruct *top, size_t top_x, filestruct *bot, - size_t bot_x, int destructive); +void cut_marked_segment(void); int do_cut_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 const char *help, #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 void toggle_init_one(int val, const char *desc, int flag); void toggle_init(void); diff --git a/src/winio.c b/src/winio.c index 24fec70c..ec60a2fa 100644 --- a/src/winio.c +++ b/src/winio.c @@ -1090,12 +1090,12 @@ int nanogetstr(int allowtabs, const char *buf, const char *def, fprintf(stderr, "Aha! \'%c\' (%d)\n", kbinput, kbinput); #endif - if (meta == 1 && kbinput == t->altval) - /* We hit an Alt key. Do like above. We don't - just ungetch() the letter and let it get - caught above cause that screws the - keypad... */ - return t->altval; + if (meta == 1 && (kbinput == t->metaval || kbinput == t->misc)) + /* We hit a Meta key. Do like above. We don't + * just ungetch() the letter and let it get + * caught above cause that screws the + * keypad... */ + return kbinput; } if (is_cntrl_char(kbinput)) @@ -1223,8 +1223,8 @@ void bottombars(const shortcut *s) strcpy(keystr, "^?"); else sprintf(keystr, "^%c", s->val + 64); - } else if (s->altval != NANO_NO_KEY) - sprintf(keystr, "M-%c", toupper(s->altval)); + } else if (s->metaval != NANO_NO_KEY) + sprintf(keystr, "M-%c", toupper(s->metaval)); onekey(keystr, s->desc, COLS / numcols);