From: David Lawrence Ramsey Date: Fri, 8 Jul 2005 02:47:05 +0000 (+0000) Subject: reorder some functions for consistency X-Git-Tag: v1.3.9~216 X-Git-Url: https://git.wh0rd.org/?a=commitdiff_plain;h=90573296ea2541a79684c890c55f093d770932da;p=nano.git reorder some functions for consistency git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2830 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- diff --git a/ChangeLog b/ChangeLog index 7eac5d27..dc6b1559 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ CVS code - saved as an int instead of a ssize_t. Changes to renumber_all(), renumber(), do_alt_speller(), and backup_lines(). (DLR) + - Reorder some functions for consistency. (DLR) - global.c: shortcut_init() - Simplify wording of nano_gotoline_msg. (Jordi) diff --git a/src/files.c b/src/files.c index c4bd3265..2c5a79d7 100644 --- a/src/files.c +++ b/src/files.c @@ -40,950 +40,950 @@ static file_format fmt = NIX_FILE; /* The format of the current file. */ -/* What happens when there is no file to open? aiee! */ -void new_file(void) +#ifdef ENABLE_MULTIBUFFER +/* Create a new openfilestruct node. */ +openfilestruct *make_new_opennode(void) { - fileage = make_new_node(NULL); - fileage->data = mallocstrcpy(NULL, ""); - filebot = fileage; - edittop = fileage; - current = fileage; - current_x = 0; - totlines = 1; - totsize = 0; + openfilestruct *newnode = + (openfilestruct *)nmalloc(sizeof(openfilestruct)); + newnode->filename = NULL; -#ifdef ENABLE_COLOR - update_color(); - if (!ISSET(NO_COLOR_SYNTAX)) - edit_refresh(); -#endif + return newnode; } -/* We make a new line of text from buf. buf is length buf_len. If - * first_line_ins is TRUE, then we put the new line at the top of the - * file. Otherwise, we assume prevnode is the last line of the file, - * and put our line after prevnode. */ -filestruct *read_line(char *buf, filestruct *prevnode, bool - *first_line_ins, size_t buf_len) +/* Splice a node into an existing openfilestruct. */ +void splice_opennode(openfilestruct *begin, openfilestruct *newnode, + openfilestruct *end) { - filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct)); - - /* Convert nulls to newlines. buf_len is the string's real length - * here. */ - unsunder(buf, buf_len); - - assert(strlen(buf) == buf_len); - - fileptr->data = mallocstrcpy(NULL, buf); - -#ifndef NANO_SMALL - /* If it's a DOS file ("\r\n"), and file conversion isn't disabled, - * strip the '\r' part from fileptr->data. */ - if (!ISSET(NO_CONVERT) && buf_len > 0 && buf[buf_len - 1] == '\r') - fileptr->data[buf_len - 1] = '\0'; -#endif + assert(newnode != NULL && begin != NULL); - if (*first_line_ins == TRUE || fileage == NULL) { - /* Special case: We're inserting with the cursor on the first - * line. */ - fileptr->prev = NULL; - fileptr->next = fileage; - fileptr->lineno = 1; - if (*first_line_ins == TRUE) { - *first_line_ins = FALSE; - /* If we're inserting into the first line of the file, then - * we want to make sure that our edit buffer stays on the - * first line and that fileage stays up to date. */ - edittop = fileptr; - } else - filebot = fileptr; - fileage = fileptr; - } else { - assert(prevnode != NULL); + newnode->next = end; + newnode->prev = begin; + begin->next = newnode; + if (end != NULL) + end->prev = newnode; +} - fileptr->prev = prevnode; - fileptr->next = NULL; - fileptr->lineno = prevnode->lineno + 1; - prevnode->next = fileptr; - } +/* Unlink a node from the rest of the openfilestruct, and delete it. */ +void unlink_opennode(openfilestruct *fileptr) +{ + assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next); - return fileptr; + fileptr->prev->next = fileptr->next; + fileptr->next->prev = fileptr->prev; + delete_opennode(fileptr); } -/* Load a file into the edit buffer. This takes data from the file - * struct. */ -void load_file(void) +/* Delete a node from the openfilestruct. */ +void delete_opennode(openfilestruct *fileptr) { - current = fileage; - -#ifdef ENABLE_MULTIBUFFER - /* Add a new entry to the open_files structure. */ - add_open_file(FALSE); + assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL); - /* Reinitialize the shortcut list. */ - shortcut_init(FALSE); -#endif + free(fileptr->filename); + free_filestruct(fileptr->fileage); + free(fileptr); } -void read_file(FILE *f, const char *filename) +#ifdef DEBUG +/* Deallocate all memory associated with this and later files, including + * the lines of text. */ +void free_openfilestruct(openfilestruct *src) { - size_t num_lines = 0; - /* The number of lines in the file. */ - size_t num_chars; - /* The number of characters in the file. */ - size_t len = 0; - /* The length of the current line of the file. */ - size_t i = 0; - /* The position in the current line of the file. */ - size_t bufx = MAX_BUF_SIZE; - /* The size of each chunk of the file that we read. */ - char input = '\0'; - /* The current input character. */ - char *buf; - /* The buffer where we store chunks of the file. */ - filestruct *fileptr = current; - /* The current line of the file. */ - bool first_line_ins = FALSE; - /* Whether we're inserting with the cursor on the first line. */ - int input_int; - /* The current value we read from the file, whether an input - * character or EOF. */ -#ifndef NANO_SMALL - int format = 0; - /* 0 = *nix, 1 = DOS, 2 = Mac, 3 = both DOS and Mac. */ + assert(src != NULL); + + while (src != src->next) { + src = src->next; + delete_opennode(src->prev); + } + delete_opennode(src); +} #endif - buf = charalloc(bufx); - buf[0] = '\0'; +/* Add/update an entry to the open_files openfilestruct. If update is + * FALSE, a new entry is created; otherwise, the current entry is + * updated. */ +void add_open_file(bool update) +{ + if (update && open_files == NULL) + return; - if (current != NULL) { - if (current == fileage) - first_line_ins = TRUE; - else - fileptr = current->prev; + /* If there are no entries in open_files, make the first one. */ + if (open_files == NULL) { + open_files = make_new_opennode(); + splice_opennode(open_files, open_files, open_files); + /* Otherwise, if we're not updating, make a new entry for + * open_files and splice it in after the current entry. */ + } else if (!update) { + splice_opennode(open_files, make_new_opennode(), + open_files->next); + open_files = open_files->next; } - /* For the assertion in read_line(), it must be true that if current - * is NULL, then so is fileage. */ - assert(current != NULL || fileage == NULL); + /* Save the current filename. */ + open_files->filename = mallocstrcpy(open_files->filename, filename); #ifndef NANO_SMALL - /* We don't know which file format we have yet, so assume it's a - * *nix file for now. */ - fmt = NIX_FILE; + /* Save the current file's stat. */ + open_files->originalfilestat = originalfilestat; #endif - /* Read the entire file into the file struct. */ - while ((input_int = getc(f)) != EOF) { - input = (char)input_int; + /* Save the current file buffer. */ + open_files->fileage = fileage; + open_files->filebot = filebot; - /* If it's a *nix file ("\n") or a DOS file ("\r\n"), and file - * conversion isn't disabled, handle it! */ - if (input == '\n') { -#ifndef NANO_SMALL - /* If there's a '\r' before the '\n', set format to DOS if - * we currently think this is a *nix file, or to both if we - * currently think it's a Mac file. */ - if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r' && - (format == 0 || format == 2)) - format++; -#endif + /* Save the current top of the edit window. */ + open_files->edittop = edittop; - /* Read in the line properly. */ - fileptr = read_line(buf, fileptr, &first_line_ins, len); + /* Save the current line. */ + open_files->current = current; - /* Reset the line length in preparation for the next - * line. */ - len = 0; + /* Save the current cursor position. */ + open_files->current_x = current_x; - num_lines++; - buf[0] = '\0'; - i = 0; -#ifndef NANO_SMALL - /* If it's a Mac file ('\r' without '\n'), and file conversion - * isn't disabled, handle it! */ - } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') { + /* Save the current place we want. */ + open_files->placewewant = placewewant; - /* If we currently think the file is a *nix file, set format - * to Mac. If we currently think the file is a DOS file, - * set format to both DOS and Mac. */ - if (format == 0 || format == 1) - format += 2; + /* Save the current total number of lines. */ + open_files->totlines = totlines; - /* Read in the line properly. */ - fileptr = read_line(buf, fileptr, &first_line_ins, len); + /* Save the current total size. */ + open_files->totsize = totsize; - /* Reset the line length in preparation for the next line. - * Since we've already read in the next character, reset it - * to 1 instead of 0. */ - len = 1; + /* Start with no flags saved. */ + open_files->flags = 0; - num_lines++; - buf[0] = input; - buf[1] = '\0'; - i = 1; -#endif - } else { - /* Calculate the total length of the line. It might have - * nulls in it, so we can't just use strlen() here. */ - len++; + /* Save the current modification status. */ + if (ISSET(MODIFIED)) + open_files->flags |= MODIFIED; - /* Now we allocate a bigger buffer MAX_BUF_SIZE characters - * at a time. If we allocate a lot of space for one line, - * we may indeed have to use a buffer this big later on, so - * we don't decrease it at all. We do free it at the end, - * though. */ - if (i >= bufx - 1) { - bufx += MAX_BUF_SIZE; - buf = charealloc(buf, bufx); - } +#ifndef NANO_SMALL + /* Save the current marking status and mark, if applicable. */ + if (ISSET(MARK_ISSET)) { + open_files->flags |= MARK_ISSET; + open_files->mark_beginbuf = mark_beginbuf; + open_files->mark_beginx = mark_beginx; + } - buf[i] = input; - buf[i + 1] = '\0'; - i++; - } - } + /* Save the current file format. */ + open_files->fmt = fmt; +#endif - /* Perhaps this could use some better handling. */ - if (ferror(f)) - nperror(filename); - fclose(f); +#ifdef DEBUG + fprintf(stderr, "filename is %s\n", open_files->filename); +#endif +} -#ifndef NANO_SMALL - /* If file conversion isn't disabled and the last character in this - * file is '\r', read it in properly as a Mac format line. */ - if (len == 0 && !ISSET(NO_CONVERT) && input == '\r') { - len = 1; +/* Read the current entry in the open_files structure and set up the + * currently open file buffer using that entry's information. */ +void load_open_file(void) +{ + assert(open_files != NULL); - buf[0] = input; - buf[1] = '\0'; - } -#endif + /* Restore the current filename. */ + filename = mallocstrcpy(filename, open_files->filename); - /* Did we not get a newline and still have stuff to do? */ - if (len > 0) { #ifndef NANO_SMALL - /* If file conversion isn't disabled and the last character in - * this file is '\r', set format to Mac if we currently think - * the file is a *nix file, or to both DOS and Mac if we - * currently think the file is a DOS file. */ - if (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' && - (format == 0 || format == 1)) - format += 2; + /* Restore the current file's stat. */ + originalfilestat = open_files->originalfilestat; #endif - /* Read in the last line properly. */ - fileptr = read_line(buf, fileptr, &first_line_ins, len); - num_lines++; - } + /* Restore the current file buffer. */ + fileage = open_files->fileage; + filebot = open_files->filebot; - free(buf); + /* Restore the current top of the edit window. */ + edittop = open_files->edittop; - /* If we didn't get a file and we don't already have one, make a new - * file. */ - if (fileptr == NULL) - new_file(); + /* Restore the current line. */ + current = open_files->current; - /* Did we try to insert a file of 0 bytes? */ - if (num_lines != 0) { - if (current != NULL) { - fileptr->next = current; - current->prev = fileptr; - renumber(current); - current_x = 0; - placewewant = 0; - } else if (fileptr->next == NULL) { - filebot = fileptr; - new_magicline(); - totsize--; - } - } + /* Restore the current cursor position. */ + current_x = open_files->current_x; - get_totals(fileage, filebot, NULL, &num_chars); - totsize += num_chars; + /* Restore the current place we want. */ + placewewant = open_files->placewewant; + + /* Restore the current total number of lines. */ + totlines = open_files->totlines; + + /* Restore the current total size. */ + totsize = open_files->totsize; + + /* Restore the current modification status. */ + if (open_files->flags & MODIFIED) + SET(MODIFIED); + else + UNSET(MODIFIED); #ifndef NANO_SMALL - if (format == 3) - statusbar( - P_("Read %lu line (Converted from DOS and Mac format)", - "Read %lu lines (Converted from DOS and Mac format)", - (unsigned long)num_lines), (unsigned long)num_lines); - else if (format == 2) { - fmt = MAC_FILE; - statusbar(P_("Read %lu line (Converted from Mac format)", - "Read %lu lines (Converted from Mac format)", - (unsigned long)num_lines), (unsigned long)num_lines); - } else if (format == 1) { - fmt = DOS_FILE; - statusbar(P_("Read %lu line (Converted from DOS format)", - "Read %lu lines (Converted from DOS format)", - (unsigned long)num_lines), (unsigned long)num_lines); + /* Restore the current marking status and mark, if applicable. */ + if (open_files->flags & MARK_ISSET) { + mark_beginbuf = open_files->mark_beginbuf; + mark_beginx = open_files->mark_beginx; + SET(MARK_ISSET); } else + UNSET(MARK_ISSET); + + /* Restore the current file format. */ + fmt = open_files->fmt; #endif - statusbar(P_("Read %lu line", "Read %lu lines", - (unsigned long)num_lines), (unsigned long)num_lines); - totlines += num_lines; +#ifdef ENABLE_COLOR + update_color(); +#endif + edit_refresh(); + + /* Update the titlebar. */ + titlebar(NULL); } -/* Open the file (and decide if it exists). If newfie is TRUE, display - * "New File" if the file is missing. Otherwise, say "[filename] not - * found". - * - * Return -2 if we say "New File". Otherwise, -1 if the file isn't - * opened, 0 otherwise. The file might still have an error while - * reading with a 0 return value. *f is set to the opened file. */ -int open_file(const char *filename, bool newfie, FILE **f) +/* Open either the next or previous file buffer. */ +void open_prevnext_file(bool next_file) { - int fd; - struct stat fileinfo; - - assert(f != NULL); + assert(open_files != NULL); - if (filename == NULL || filename[0] == '\0' || - stat(filename, &fileinfo) == -1) { - if (newfie) { - statusbar(_("New File")); - return -2; - } - statusbar(_("\"%s\" not found"), filename); - return -1; - } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) || - S_ISBLK(fileinfo.st_mode)) { - /* Don't open character or block files. Sorry, /dev/sndstat! */ - statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory") - : _("File \"%s\" is a device file"), filename); - return -1; - } else if ((fd = open(filename, O_RDONLY)) == -1) { - statusbar(_("Error reading %s: %s"), filename, strerror(errno)); - return -1; - } else { - /* File is A-OK. Open it in binary mode for our own end-of-line - * character munging. */ - *f = fdopen(fd, "rb"); + add_open_file(TRUE); - if (*f == NULL) { - statusbar(_("Error reading %s: %s"), filename, - strerror(errno)); - close(fd); - } else - statusbar(_("Reading File")); + /* If only one file buffer is open, indicate it on the statusbar and + * get out. */ + if (open_files == open_files->next) { + statusbar(_("No more open file buffers")); + return; } - return 0; + + /* Switch to the next or previous file, depending on the value of + * next. */ + open_files = next_file ? open_files->next : open_files->prev; + +#ifdef DEBUG + fprintf(stderr, "filename is %s\n", open_files->filename); +#endif + + /* Load the file we switched to. */ + load_open_file(); + + /* And indicate the switch on the statusbar. */ + statusbar(_("Switched to %s"), + ((open_files->filename[0] == '\0') ? _("New Buffer") : + open_files->filename)); + +#ifdef DEBUG + dump_buffer(current); +#endif } -/* This function will return the name of the first available extension - * of a filename (starting with [name][suffix], then [name][suffix].1, - * etc.). Memory is allocated for the return value. If no writable - * extension exists, we return "". */ -char *get_next_filename(const char *name, const char *suffix) +/* Open the previous entry in the open_files structure. This function + * is used by the shortcut list. */ +void open_prevfile_void(void) { - unsigned long i = 0; - char *buf; - size_t namelen, suffixlen; - - assert(name != NULL && suffix != NULL); + open_prevnext_file(FALSE); +} - namelen = strlen(name); - suffixlen = strlen(suffix); +/* Open the next entry in the open_files structure. This function is + * used by the shortcut list. */ +void open_nextfile_void(void) +{ + open_prevnext_file(TRUE); +} - buf = charalloc(namelen + suffixlen + digits(ULONG_MAX) + 2); - sprintf(buf, "%s%s", name, suffix); +/* Delete an entry from the open_files filestruct. After deletion of an + * entry, the next entry is opened. Return TRUE on success or FALSE if + * there are no more open file buffers. */ +bool close_open_file(void) +{ + assert(open_files != NULL); - while (TRUE) { - struct stat fs; + /* If only one file is open, get out. */ + if (open_files == open_files->next) + return FALSE; - if (stat(buf, &fs) == -1) - return buf; - if (i == ULONG_MAX) - break; + /* Open the next file. */ + open_nextfile_void(); - i++; - sprintf(buf + namelen + suffixlen, ".%lu", i); - } + /* Close the file we had open before. */ + unlink_opennode(open_files->prev); - /* We get here only if there is no possible save file. Blank out - * the filename to indicate this. */ - null_at(&buf, 0); + /* Reinitialize the shortcut list. */ + shortcut_init(FALSE); + display_main_list(); - return buf; + return TRUE; } +#endif /* ENABLE_MULTIBUFFER */ -#ifndef NANO_SMALL -void execute_command(const char *command) +/* What happens when there is no file to open? aiee! */ +void new_file(void) { -#ifdef ENABLE_MULTIBUFFER - if (ISSET(MULTIBUFFER)) { - /* Update the current entry in the open_files structure. */ - add_open_file(TRUE); - new_file(); - UNSET(MODIFIED); - UNSET(MARK_ISSET); - } -#endif /* ENABLE_MULTIBUFFER */ - open_pipe(command); -#ifdef ENABLE_MULTIBUFFER - /* Add this new entry to the open_files structure. */ - if (ISSET(MULTIBUFFER)) - load_file(); -#endif /* ENABLE_MULTIBUFFER */ + fileage = make_new_node(NULL); + fileage->data = mallocstrcpy(NULL, ""); + filebot = fileage; + edittop = fileage; + current = fileage; + current_x = 0; + totlines = 1; + totsize = 0; + +#ifdef ENABLE_COLOR + update_color(); + if (!ISSET(NO_COLOR_SYNTAX)) + edit_refresh(); +#endif } -#endif /* !NANO_SMALL */ -/* name is a file name to open. We make a new buffer if necessary, then - * open and read the file. */ -void load_buffer(const char *name) +/* We make a new line of text from buf. buf is length buf_len. If + * first_line_ins is TRUE, then we put the new line at the top of the + * file. Otherwise, we assume prevnode is the last line of the file, + * and put our line after prevnode. */ +filestruct *read_line(char *buf, filestruct *prevnode, bool + *first_line_ins, size_t buf_len) { - bool new_buffer = (fileage == NULL -#ifdef ENABLE_MULTIBUFFER - || ISSET(MULTIBUFFER) -#endif - ); - /* new_buffer says whether we load into this buffer or a new - * one. If new_buffer is TRUE, we display "New File" if the - * file is not found, and if it is found we set filename and add - * a new open_files entry. */ - FILE *f; - int rc; - /* rc == -2 means that the statusbar displayed "New File". -1 - * means that the open failed. 0 means success. */ + filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct)); -#ifndef DISABLE_OPERATINGDIR - if (check_operating_dir(name, FALSE)) { - statusbar(_("Can't insert file from outside of %s"), - operating_dir); - return; - } -#endif + /* Convert nulls to newlines. buf_len is the string's real length + * here. */ + unsunder(buf, buf_len); -#ifdef ENABLE_MULTIBUFFER - /* Update the current entry in the open_files structure. */ - add_open_file(TRUE); -#endif + assert(strlen(buf) == buf_len); - rc = open_file(name, new_buffer, &f); + fileptr->data = mallocstrcpy(NULL, buf); -#ifdef ENABLE_MULTIBUFFER - if (rc != -1 && ISSET(MULTIBUFFER)) { - UNSET(MODIFIED); #ifndef NANO_SMALL - UNSET(MARK_ISSET); -#endif - } + /* If it's a DOS file ("\r\n"), and file conversion isn't disabled, + * strip the '\r' part from fileptr->data. */ + if (!ISSET(NO_CONVERT) && buf_len > 0 && buf[buf_len - 1] == '\r') + fileptr->data[buf_len - 1] = '\0'; #endif - if (rc != -1 && new_buffer) { - filename = mallocstrcpy(filename, name); - new_file(); + if (*first_line_ins == TRUE || fileage == NULL) { + /* Special case: We're inserting with the cursor on the first + * line. */ + fileptr->prev = NULL; + fileptr->next = fileage; + fileptr->lineno = 1; + if (*first_line_ins == TRUE) { + *first_line_ins = FALSE; + /* If we're inserting into the first line of the file, then + * we want to make sure that our edit buffer stays on the + * first line and that fileage stays up to date. */ + edittop = fileptr; + } else + filebot = fileptr; + fileage = fileptr; + } else { + assert(prevnode != NULL); + + fileptr->prev = prevnode; + fileptr->next = NULL; + fileptr->lineno = prevnode->lineno + 1; + prevnode->next = fileptr; } - if (rc == 0) { - file_format fmt_save = fmt; + return fileptr; +} - read_file(f, filename); +/* Load a file into the edit buffer. This takes data from the file + * struct. */ +void load_file(void) +{ + current = fileage; - /* If we're not loading into a new buffer, preserve the file - * format. */ - if (!new_buffer) - fmt = fmt_save; +#ifdef ENABLE_MULTIBUFFER + /* Add a new entry to the open_files structure. */ + add_open_file(FALSE); -#ifndef NANO_SMALL - stat(filename, &originalfilestat); + /* Reinitialize the shortcut list. */ + shortcut_init(FALSE); #endif - } - - /* Add this new entry to the open_files structure if we have - * multibuffer support, or to the main filestruct if we don't. */ - if (rc != -1 && new_buffer) - load_file(); } -void do_insertfile( +void read_file(FILE *f, const char *filename) +{ + size_t num_lines = 0; + /* The number of lines in the file. */ + size_t num_chars; + /* The number of characters in the file. */ + size_t len = 0; + /* The length of the current line of the file. */ + size_t i = 0; + /* The position in the current line of the file. */ + size_t bufx = MAX_BUF_SIZE; + /* The size of each chunk of the file that we read. */ + char input = '\0'; + /* The current input character. */ + char *buf; + /* The buffer where we store chunks of the file. */ + filestruct *fileptr = current; + /* The current line of the file. */ + bool first_line_ins = FALSE; + /* Whether we're inserting with the cursor on the first line. */ + int input_int; + /* The current value we read from the file, whether an input + * character or EOF. */ #ifndef NANO_SMALL - bool execute -#else - void + int format = 0; + /* 0 = *nix, 1 = DOS, 2 = Mac, 3 = both DOS and Mac. */ #endif - ) -{ - int i; - const char *msg; - char *ans = mallocstrcpy(NULL, ""); - /* The last answer the user typed on the statusbar. */ - filestruct *edittop_save = edittop; - ssize_t current_y_save = current_y; - bool at_edittop = FALSE; - /* Whether we're at the top of the edit window. */ -#ifndef DISABLE_WRAPPING - wrap_reset(); -#endif + buf = charalloc(bufx); + buf[0] = '\0'; - while (TRUE) { -#ifndef NANO_SMALL - if (execute) { -#ifdef ENABLE_MULTIBUFFER - if (ISSET(MULTIBUFFER)) - msg = N_("Command to execute in new buffer [from %s] "); - else -#endif - msg = N_("Command to execute [from %s] "); - } else { -#endif -#ifdef ENABLE_MULTIBUFFER - if (ISSET(MULTIBUFFER)) { - msg = N_("File to insert into new buffer [from %s] "); - } else -#endif - msg = N_("File to insert [from %s] "); -#ifndef NANO_SMALL - } -#endif + if (current != NULL) { + if (current == fileage) + first_line_ins = TRUE; + else + fileptr = current->prev; + } + + /* For the assertion in read_line(), it must be true that if current + * is NULL, then so is fileage. */ + assert(current != NULL || fileage == NULL); - i = statusq(TRUE, #ifndef NANO_SMALL - execute ? extcmd_list : + /* We don't know which file format we have yet, so assume it's a + * *nix file for now. */ + fmt = NIX_FILE; #endif - insertfile_list, ans, + + /* Read the entire file into the file struct. */ + while ((input_int = getc(f)) != EOF) { + input = (char)input_int; + + /* If it's a *nix file ("\n") or a DOS file ("\r\n"), and file + * conversion isn't disabled, handle it! */ + if (input == '\n') { #ifndef NANO_SMALL - NULL, -#endif - _(msg), -#ifndef DISABLE_OPERATINGDIR - operating_dir != NULL && strcmp(operating_dir, ".") != 0 ? - operating_dir : + /* If there's a '\r' before the '\n', set format to DOS if + * we currently think this is a *nix file, or to both if we + * currently think it's a Mac file. */ + if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r' && + (format == 0 || format == 2)) + format++; #endif - "./"); - /* If we're in multibuffer mode and the filename or command is - * blank, open a new buffer instead of canceling. */ - if (i == -1 || (i == -2 -#ifdef ENABLE_MULTIBUFFER - && !ISSET(MULTIBUFFER) -#endif - )) { - statusbar(_("Cancelled")); - break; - } else { - size_t pww_save = placewewant; + /* Read in the line properly. */ + fileptr = read_line(buf, fileptr, &first_line_ins, len); - ans = mallocstrcpy(ans, answer); + /* Reset the line length in preparation for the next + * line. */ + len = 0; + num_lines++; + buf[0] = '\0'; + i = 0; #ifndef NANO_SMALL -#ifdef ENABLE_MULTIBUFFER - if (i == TOGGLE_MULTIBUFFER_KEY) { - /* Don't allow toggling if we're in view mode. */ - if (!ISSET(VIEW_MODE)) - TOGGLE(MULTIBUFFER); - continue; - } else -#endif - if (i == NANO_TOOTHERINSERT_KEY) { - execute = !execute; - continue; - } -#ifndef DISABLE_BROWSER - else -#endif -#endif /* !NANO_SMALL */ - -#ifndef DISABLE_BROWSER - if (i == NANO_TOFILES_KEY) { - char *tmp = do_browse_from(answer); + /* If it's a Mac file ('\r' without '\n'), and file conversion + * isn't disabled, handle it! */ + } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') { - if (tmp == NULL) - continue; + /* If we currently think the file is a *nix file, set format + * to Mac. If we currently think the file is a DOS file, + * set format to both DOS and Mac. */ + if (format == 0 || format == 1) + format += 2; - free(answer); - answer = tmp; + /* Read in the line properly. */ + fileptr = read_line(buf, fileptr, &first_line_ins, len); - /* We have a file now. Indicate this and get out of the - * statusbar prompt cleanly. */ - i = 0; - statusq_abort(); - } -#endif + /* Reset the line length in preparation for the next line. + * Since we've already read in the next character, reset it + * to 1 instead of 0. */ + len = 1; - /* If we don't have a file yet, go back to the statusbar - * prompt. */ - if (i != 0 -#ifdef ENABLE_MULTIBUFFER - && (i != -2 || !ISSET(MULTIBUFFER)) + num_lines++; + buf[0] = input; + buf[1] = '\0'; + i = 1; #endif - ) - continue; + } else { + /* Calculate the total length of the line. It might have + * nulls in it, so we can't just use strlen() here. */ + len++; -#ifdef ENABLE_MULTIBUFFER - if (!ISSET(MULTIBUFFER)) { -#endif - /* If we're not inserting into a new buffer, partition - * the filestruct so that it contains no text and hence - * looks like a new buffer, and keep track of whether - * the top of the partition is the top of the edit - * window. */ - filepart = partition_filestruct(current, current_x, - current, current_x); - at_edittop = (fileage == edittop); -#ifdef ENABLE_MULTIBUFFER + /* Now we allocate a bigger buffer MAX_BUF_SIZE characters + * at a time. If we allocate a lot of space for one line, + * we may indeed have to use a buffer this big later on, so + * we don't decrease it at all. We do free it at the end, + * though. */ + if (i >= bufx - 1) { + bufx += MAX_BUF_SIZE; + buf = charealloc(buf, bufx); } -#endif + + buf[i] = input; + buf[i + 1] = '\0'; + i++; + } + } + + /* Perhaps this could use some better handling. */ + if (ferror(f)) + nperror(filename); + fclose(f); #ifndef NANO_SMALL - if (execute) - execute_command(answer); - else { -#endif - answer = mallocstrassn(answer, - real_dir_from_tilde(answer)); - load_buffer(answer); -#ifndef NANO_SMALL - } + /* If file conversion isn't disabled and the last character in this + * file is '\r', read it in properly as a Mac format line. */ + if (len == 0 && !ISSET(NO_CONVERT) && input == '\r') { + len = 1; + + buf[0] = input; + buf[1] = '\0'; + } #endif -#ifdef ENABLE_MULTIBUFFER - if (!ISSET(MULTIBUFFER)) + /* Did we not get a newline and still have stuff to do? */ + if (len > 0) { +#ifndef NANO_SMALL + /* If file conversion isn't disabled and the last character in + * this file is '\r', set format to Mac if we currently think + * the file is a *nix file, or to both DOS and Mac if we + * currently think the file is a DOS file. */ + if (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' && + (format == 0 || format == 1)) + format += 2; #endif - { - filestruct *top_save = fileage; - /* If we didn't insert into a new buffer, and we were at - * the top of the edit window before, set the saved - * value of edittop to the new top of the edit window, - * and update the current y-coordinate to account for - * the number of lines inserted. */ - if (at_edittop) - edittop_save = fileage; - current_y += current_y_save; + /* Read in the last line properly. */ + fileptr = read_line(buf, fileptr, &first_line_ins, len); + num_lines++; + } - /* If we didn't insert into a new buffer, unpartition - * the filestruct so that it contains all the text - * again. Note that we've replaced the non-text - * originally in the partition with the text in the - * inserted file/executed command output. */ - unpartition_filestruct(&filepart); + free(buf); - /* Renumber starting with the beginning line of the old - * partition. */ - renumber(top_save); + /* If we didn't get a file and we don't already have one, make a new + * file. */ + if (fileptr == NULL) + new_file(); - /* Set edittop back to what it was before. */ - edittop = edittop_save; - } + /* Did we try to insert a file of 0 bytes? */ + if (num_lines != 0) { + if (current != NULL) { + fileptr->next = current; + current->prev = fileptr; + renumber(current); + current_x = 0; + placewewant = 0; + } else if (fileptr->next == NULL) { + filebot = fileptr; + new_magicline(); + totsize--; + } + } -#ifdef ENABLE_MULTIBUFFER - if (ISSET(MULTIBUFFER)) { - /* Update the titlebar. */ - titlebar(NULL); + get_totals(fileage, filebot, NULL, &num_chars); + totsize += num_chars; - /* Reinitialize the shortcut list. */ - shortcut_init(FALSE); - } else { +#ifndef NANO_SMALL + if (format == 3) + statusbar( + P_("Read %lu line (Converted from DOS and Mac format)", + "Read %lu lines (Converted from DOS and Mac format)", + (unsigned long)num_lines), (unsigned long)num_lines); + else if (format == 2) { + fmt = MAC_FILE; + statusbar(P_("Read %lu line (Converted from Mac format)", + "Read %lu lines (Converted from Mac format)", + (unsigned long)num_lines), (unsigned long)num_lines); + } else if (format == 1) { + fmt = DOS_FILE; + statusbar(P_("Read %lu line (Converted from DOS format)", + "Read %lu lines (Converted from DOS format)", + (unsigned long)num_lines), (unsigned long)num_lines); + } else #endif - /* Mark the file as modified. */ - set_modified(); + statusbar(P_("Read %lu line", "Read %lu lines", + (unsigned long)num_lines), (unsigned long)num_lines); - /* Restore the old place we want. */ - placewewant = pww_save; -#ifdef ENABLE_MULTIBUFFER - } -#endif + totlines += num_lines; +} - /* Refresh the screen. */ - edit_refresh(); +/* Open the file (and decide if it exists). If newfie is TRUE, display + * "New File" if the file is missing. Otherwise, say "[filename] not + * found". + * + * Return -2 if we say "New File". Otherwise, -1 if the file isn't + * opened, 0 otherwise. The file might still have an error while + * reading with a 0 return value. *f is set to the opened file. */ +int open_file(const char *filename, bool newfie, FILE **f) +{ + int fd; + struct stat fileinfo; + + assert(f != NULL); + + if (filename == NULL || filename[0] == '\0' || + stat(filename, &fileinfo) == -1) { + if (newfie) { + statusbar(_("New File")); + return -2; + } + statusbar(_("\"%s\" not found"), filename); + return -1; + } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) || + S_ISBLK(fileinfo.st_mode)) { + /* Don't open character or block files. Sorry, /dev/sndstat! */ + statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory") + : _("File \"%s\" is a device file"), filename); + return -1; + } else if ((fd = open(filename, O_RDONLY)) == -1) { + statusbar(_("Error reading %s: %s"), filename, strerror(errno)); + return -1; + } else { + /* File is A-OK. Open it in binary mode for our own end-of-line + * character munging. */ + *f = fdopen(fd, "rb"); - break; - } + if (*f == NULL) { + statusbar(_("Error reading %s: %s"), filename, + strerror(errno)); + close(fd); + } else + statusbar(_("Reading File")); } - - free(ans); + return 0; } -void do_insertfile_void(void) +/* This function will return the name of the first available extension + * of a filename (starting with [name][suffix], then [name][suffix].1, + * etc.). Memory is allocated for the return value. If no writable + * extension exists, we return "". */ +char *get_next_filename(const char *name, const char *suffix) { -#ifdef ENABLE_MULTIBUFFER - if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER)) - statusbar(_("Key illegal in non-multibuffer mode")); - else -#endif - do_insertfile( -#ifndef NANO_SMALL - FALSE -#endif - ); - - display_main_list(); -} + unsigned long i = 0; + char *buf; + size_t namelen, suffixlen; -#ifdef ENABLE_MULTIBUFFER -/* Create a new openfilestruct node. */ -openfilestruct *make_new_opennode(void) -{ - openfilestruct *newnode = - (openfilestruct *)nmalloc(sizeof(openfilestruct)); - newnode->filename = NULL; + assert(name != NULL && suffix != NULL); - return newnode; -} + namelen = strlen(name); + suffixlen = strlen(suffix); -/* Splice a node into an existing openfilestruct. */ -void splice_opennode(openfilestruct *begin, openfilestruct *newnode, - openfilestruct *end) -{ - assert(newnode != NULL && begin != NULL); + buf = charalloc(namelen + suffixlen + digits(ULONG_MAX) + 2); + sprintf(buf, "%s%s", name, suffix); - newnode->next = end; - newnode->prev = begin; - begin->next = newnode; - if (end != NULL) - end->prev = newnode; -} + while (TRUE) { + struct stat fs; -/* Unlink a node from the rest of the openfilestruct, and delete it. */ -void unlink_opennode(openfilestruct *fileptr) -{ - assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next); + if (stat(buf, &fs) == -1) + return buf; + if (i == ULONG_MAX) + break; - fileptr->prev->next = fileptr->next; - fileptr->next->prev = fileptr->prev; - delete_opennode(fileptr); -} + i++; + sprintf(buf + namelen + suffixlen, ".%lu", i); + } -/* Delete a node from the openfilestruct. */ -void delete_opennode(openfilestruct *fileptr) -{ - assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL); + /* We get here only if there is no possible save file. Blank out + * the filename to indicate this. */ + null_at(&buf, 0); - free(fileptr->filename); - free_filestruct(fileptr->fileage); - free(fileptr); + return buf; } -#ifdef DEBUG -/* Deallocate all memory associated with this and later files, including - * the lines of text. */ -void free_openfilestruct(openfilestruct *src) +#ifndef NANO_SMALL +void execute_command(const char *command) { - assert(src != NULL); - - while (src != src->next) { - src = src->next; - delete_opennode(src->prev); +#ifdef ENABLE_MULTIBUFFER + if (ISSET(MULTIBUFFER)) { + /* Update the current entry in the open_files structure. */ + add_open_file(TRUE); + new_file(); + UNSET(MODIFIED); + UNSET(MARK_ISSET); } - delete_opennode(src); +#endif /* ENABLE_MULTIBUFFER */ + open_pipe(command); +#ifdef ENABLE_MULTIBUFFER + /* Add this new entry to the open_files structure. */ + if (ISSET(MULTIBUFFER)) + load_file(); +#endif /* ENABLE_MULTIBUFFER */ } -#endif +#endif /* !NANO_SMALL */ -/* Add/update an entry to the open_files openfilestruct. If update is - * FALSE, a new entry is created; otherwise, the current entry is - * updated. */ -void add_open_file(bool update) +/* name is a file name to open. We make a new buffer if necessary, then + * open and read the file. */ +void load_buffer(const char *name) { - if (update && open_files == NULL) - return; + bool new_buffer = (fileage == NULL +#ifdef ENABLE_MULTIBUFFER + || ISSET(MULTIBUFFER) +#endif + ); + /* new_buffer says whether we load into this buffer or a new + * one. If new_buffer is TRUE, we display "New File" if the + * file is not found, and if it is found we set filename and add + * a new open_files entry. */ + FILE *f; + int rc; + /* rc == -2 means that the statusbar displayed "New File". -1 + * means that the open failed. 0 means success. */ - /* If there are no entries in open_files, make the first one. */ - if (open_files == NULL) { - open_files = make_new_opennode(); - splice_opennode(open_files, open_files, open_files); - /* Otherwise, if we're not updating, make a new entry for - * open_files and splice it in after the current entry. */ - } else if (!update) { - splice_opennode(open_files, make_new_opennode(), - open_files->next); - open_files = open_files->next; +#ifndef DISABLE_OPERATINGDIR + if (check_operating_dir(name, FALSE)) { + statusbar(_("Can't insert file from outside of %s"), + operating_dir); + return; } +#endif - /* Save the current filename. */ - open_files->filename = mallocstrcpy(open_files->filename, filename); +#ifdef ENABLE_MULTIBUFFER + /* Update the current entry in the open_files structure. */ + add_open_file(TRUE); +#endif + rc = open_file(name, new_buffer, &f); + +#ifdef ENABLE_MULTIBUFFER + if (rc != -1 && ISSET(MULTIBUFFER)) { + UNSET(MODIFIED); #ifndef NANO_SMALL - /* Save the current file's stat. */ - open_files->originalfilestat = originalfilestat; + UNSET(MARK_ISSET); +#endif + } #endif - /* Save the current file buffer. */ - open_files->fileage = fileage; - open_files->filebot = filebot; + if (rc != -1 && new_buffer) { + filename = mallocstrcpy(filename, name); + new_file(); + } - /* Save the current top of the edit window. */ - open_files->edittop = edittop; + if (rc == 0) { + file_format fmt_save = fmt; - /* Save the current line. */ - open_files->current = current; + read_file(f, filename); - /* Save the current cursor position. */ - open_files->current_x = current_x; + /* If we're not loading into a new buffer, preserve the file + * format. */ + if (!new_buffer) + fmt = fmt_save; - /* Save the current place we want. */ - open_files->placewewant = placewewant; +#ifndef NANO_SMALL + stat(filename, &originalfilestat); +#endif + } - /* Save the current total number of lines. */ - open_files->totlines = totlines; + /* Add this new entry to the open_files structure if we have + * multibuffer support, or to the main filestruct if we don't. */ + if (rc != -1 && new_buffer) + load_file(); +} - /* Save the current total size. */ - open_files->totsize = totsize; +void do_insertfile( +#ifndef NANO_SMALL + bool execute +#else + void +#endif + ) +{ + int i; + const char *msg; + char *ans = mallocstrcpy(NULL, ""); + /* The last answer the user typed on the statusbar. */ + filestruct *edittop_save = edittop; + ssize_t current_y_save = current_y; + bool at_edittop = FALSE; + /* Whether we're at the top of the edit window. */ - /* Start with no flags saved. */ - open_files->flags = 0; +#ifndef DISABLE_WRAPPING + wrap_reset(); +#endif - /* Save the current modification status. */ - if (ISSET(MODIFIED)) - open_files->flags |= MODIFIED; + while (TRUE) { +#ifndef NANO_SMALL + if (execute) { +#ifdef ENABLE_MULTIBUFFER + if (ISSET(MULTIBUFFER)) + msg = N_("Command to execute in new buffer [from %s] "); + else +#endif + msg = N_("Command to execute [from %s] "); + } else { +#endif +#ifdef ENABLE_MULTIBUFFER + if (ISSET(MULTIBUFFER)) { + msg = N_("File to insert into new buffer [from %s] "); + } else +#endif + msg = N_("File to insert [from %s] "); +#ifndef NANO_SMALL + } +#endif + i = statusq(TRUE, #ifndef NANO_SMALL - /* Save the current marking status and mark, if applicable. */ - if (ISSET(MARK_ISSET)) { - open_files->flags |= MARK_ISSET; - open_files->mark_beginbuf = mark_beginbuf; - open_files->mark_beginx = mark_beginx; - } - - /* Save the current file format. */ - open_files->fmt = fmt; + execute ? extcmd_list : #endif - -#ifdef DEBUG - fprintf(stderr, "filename is %s\n", open_files->filename); + insertfile_list, ans, +#ifndef NANO_SMALL + NULL, #endif -} + _(msg), +#ifndef DISABLE_OPERATINGDIR + operating_dir != NULL && strcmp(operating_dir, ".") != 0 ? + operating_dir : +#endif + "./"); -/* Read the current entry in the open_files structure and set up the - * currently open file buffer using that entry's information. */ -void load_open_file(void) -{ - assert(open_files != NULL); + /* If we're in multibuffer mode and the filename or command is + * blank, open a new buffer instead of canceling. */ + if (i == -1 || (i == -2 +#ifdef ENABLE_MULTIBUFFER + && !ISSET(MULTIBUFFER) +#endif + )) { + statusbar(_("Cancelled")); + break; + } else { + size_t pww_save = placewewant; - /* Restore the current filename. */ - filename = mallocstrcpy(filename, open_files->filename); + ans = mallocstrcpy(ans, answer); #ifndef NANO_SMALL - /* Restore the current file's stat. */ - originalfilestat = open_files->originalfilestat; +#ifdef ENABLE_MULTIBUFFER + if (i == TOGGLE_MULTIBUFFER_KEY) { + /* Don't allow toggling if we're in view mode. */ + if (!ISSET(VIEW_MODE)) + TOGGLE(MULTIBUFFER); + continue; + } else #endif + if (i == NANO_TOOTHERINSERT_KEY) { + execute = !execute; + continue; + } +#ifndef DISABLE_BROWSER + else +#endif +#endif /* !NANO_SMALL */ - /* Restore the current file buffer. */ - fileage = open_files->fileage; - filebot = open_files->filebot; - - /* Restore the current top of the edit window. */ - edittop = open_files->edittop; - - /* Restore the current line. */ - current = open_files->current; +#ifndef DISABLE_BROWSER + if (i == NANO_TOFILES_KEY) { + char *tmp = do_browse_from(answer); - /* Restore the current cursor position. */ - current_x = open_files->current_x; + if (tmp == NULL) + continue; - /* Restore the current place we want. */ - placewewant = open_files->placewewant; + free(answer); + answer = tmp; - /* Restore the current total number of lines. */ - totlines = open_files->totlines; + /* We have a file now. Indicate this and get out of the + * statusbar prompt cleanly. */ + i = 0; + statusq_abort(); + } +#endif - /* Restore the current total size. */ - totsize = open_files->totsize; + /* If we don't have a file yet, go back to the statusbar + * prompt. */ + if (i != 0 +#ifdef ENABLE_MULTIBUFFER + && (i != -2 || !ISSET(MULTIBUFFER)) +#endif + ) + continue; - /* Restore the current modification status. */ - if (open_files->flags & MODIFIED) - SET(MODIFIED); - else - UNSET(MODIFIED); +#ifdef ENABLE_MULTIBUFFER + if (!ISSET(MULTIBUFFER)) { +#endif + /* If we're not inserting into a new buffer, partition + * the filestruct so that it contains no text and hence + * looks like a new buffer, and keep track of whether + * the top of the partition is the top of the edit + * window. */ + filepart = partition_filestruct(current, current_x, + current, current_x); + at_edittop = (fileage == edittop); +#ifdef ENABLE_MULTIBUFFER + } +#endif #ifndef NANO_SMALL - /* Restore the current marking status and mark, if applicable. */ - if (open_files->flags & MARK_ISSET) { - mark_beginbuf = open_files->mark_beginbuf; - mark_beginx = open_files->mark_beginx; - SET(MARK_ISSET); - } else - UNSET(MARK_ISSET); - - /* Restore the current file format. */ - fmt = open_files->fmt; + if (execute) + execute_command(answer); + else { +#endif + answer = mallocstrassn(answer, + real_dir_from_tilde(answer)); + load_buffer(answer); +#ifndef NANO_SMALL + } #endif -#ifdef ENABLE_COLOR - update_color(); +#ifdef ENABLE_MULTIBUFFER + if (!ISSET(MULTIBUFFER)) #endif - edit_refresh(); + { + filestruct *top_save = fileage; - /* Update the titlebar. */ - titlebar(NULL); -} + /* If we didn't insert into a new buffer, and we were at + * the top of the edit window before, set the saved + * value of edittop to the new top of the edit window, + * and update the current y-coordinate to account for + * the number of lines inserted. */ + if (at_edittop) + edittop_save = fileage; + current_y += current_y_save; -/* Open either the next or previous file buffer. */ -void open_prevnext_file(bool next_file) -{ - assert(open_files != NULL); + /* If we didn't insert into a new buffer, unpartition + * the filestruct so that it contains all the text + * again. Note that we've replaced the non-text + * originally in the partition with the text in the + * inserted file/executed command output. */ + unpartition_filestruct(&filepart); - add_open_file(TRUE); + /* Renumber starting with the beginning line of the old + * partition. */ + renumber(top_save); - /* If only one file buffer is open, indicate it on the statusbar and - * get out. */ - if (open_files == open_files->next) { - statusbar(_("No more open file buffers")); - return; - } + /* Set edittop back to what it was before. */ + edittop = edittop_save; + } - /* Switch to the next or previous file, depending on the value of - * next. */ - open_files = next_file ? open_files->next : open_files->prev; +#ifdef ENABLE_MULTIBUFFER + if (ISSET(MULTIBUFFER)) { + /* Update the titlebar. */ + titlebar(NULL); -#ifdef DEBUG - fprintf(stderr, "filename is %s\n", open_files->filename); + /* Reinitialize the shortcut list. */ + shortcut_init(FALSE); + } else { #endif + /* Mark the file as modified. */ + set_modified(); - /* Load the file we switched to. */ - load_open_file(); - - /* And indicate the switch on the statusbar. */ - statusbar(_("Switched to %s"), - ((open_files->filename[0] == '\0') ? _("New Buffer") : - open_files->filename)); - -#ifdef DEBUG - dump_buffer(current); + /* Restore the old place we want. */ + placewewant = pww_save; +#ifdef ENABLE_MULTIBUFFER + } #endif -} -/* Open the previous entry in the open_files structure. This function - * is used by the shortcut list. */ -void open_prevfile_void(void) -{ - open_prevnext_file(FALSE); -} + /* Refresh the screen. */ + edit_refresh(); -/* Open the next entry in the open_files structure. This function is - * used by the shortcut list. */ -void open_nextfile_void(void) -{ - open_prevnext_file(TRUE); + break; + } + } + + free(ans); } -/* Delete an entry from the open_files filestruct. After deletion of an - * entry, the next entry is opened. Return TRUE on success or FALSE if - * there are no more open file buffers. */ -bool close_open_file(void) +void do_insertfile_void(void) { - assert(open_files != NULL); - - /* If only one file is open, get out. */ - if (open_files == open_files->next) - return FALSE; - - /* Open the next file. */ - open_nextfile_void(); - - /* Close the file we had open before. */ - unlink_opennode(open_files->prev); +#ifdef ENABLE_MULTIBUFFER + if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER)) + statusbar(_("Key illegal in non-multibuffer mode")); + else +#endif + do_insertfile( +#ifndef NANO_SMALL + FALSE +#endif + ); - /* Reinitialize the shortcut list. */ - shortcut_init(FALSE); display_main_list(); - - return TRUE; } -#endif /* ENABLE_MULTIBUFFER */ /* When passed "[relative path]" or "[relative path][filename]" in * origpath, return "[full path]" or "[full path][filename]" on success, diff --git a/src/nano.c b/src/nano.c index e20c18ad..c6257e84 100644 --- a/src/nano.c +++ b/src/nano.c @@ -78,903 +78,903 @@ static filestruct *jusbottom = NULL; /* Pointer to end of justify buffer. */ #endif -void print_view_warning(void) +/* Create a new filestruct node. Note that we specifically do not set + * prevnode->next equal to the new line. */ +filestruct *make_new_node(filestruct *prevnode) { - statusbar(_("Key illegal in VIEW mode")); + filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct)); + + newnode->data = NULL; + newnode->prev = prevnode; + newnode->next = NULL; + newnode->lineno = (prevnode != NULL) ? prevnode->lineno + 1 : 1; + + return newnode; } -/* What we do when we're all set to exit. */ -void finish(void) +/* Make a copy of a filestruct node. */ +filestruct *copy_node(const filestruct *src) { - if (!ISSET(NO_HELP)) - blank_bottombars(); - else - blank_statusbar(); - - wrefresh(bottomwin); - endwin(); + filestruct *dst; - /* Restore the old terminal settings. */ - tcsetattr(0, TCSANOW, &oldterm); + assert(src != NULL); -#if !defined(NANO_SMALL) && defined(ENABLE_NANORC) - if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG)) - save_history(); -#endif + dst = (filestruct *)nmalloc(sizeof(filestruct)); -#ifdef DEBUG - thanks_for_all_the_fish(); -#endif + dst->data = mallocstrcpy(NULL, src->data); + dst->next = src->next; + dst->prev = src->prev; + dst->lineno = src->lineno; - exit(0); + return dst; } -/* Die (gracefully?). */ -void die(const char *msg, ...) +/* Splice a node into an existing filestruct. */ +void splice_node(filestruct *begin, filestruct *newnode, filestruct + *end) { - va_list ap; - - endwin(); - curses_ended = TRUE; - - /* Restore the old terminal settings. */ - tcsetattr(0, TCSANOW, &oldterm); - - va_start(ap, msg); - vfprintf(stderr, msg, ap); - va_end(ap); - - /* Save the current file buffer if it's been modified. */ - if (ISSET(MODIFIED)) { - /* If we've partitioned the filestruct, unpartition it now. */ - if (filepart != NULL) - unpartition_filestruct(&filepart); + assert(newnode != NULL && begin != NULL); - die_save_file(filename); - } + newnode->next = end; + newnode->prev = begin; + begin->next = newnode; + if (end != NULL) + end->prev = newnode; +} -#ifdef ENABLE_MULTIBUFFER - /* Save all of the other modified file buffers, if any. */ - if (open_files != NULL) { - openfilestruct *tmp = open_files; +/* Unlink a node from the rest of the filestruct. */ +void unlink_node(const filestruct *fileptr) +{ + assert(fileptr != NULL); - while (tmp != open_files->next) { - open_files = open_files->next; + if (fileptr->prev != NULL) + fileptr->prev->next = fileptr->next; + if (fileptr->next != NULL) + fileptr->next->prev = fileptr->prev; +} - /* Save the current file buffer if it's been modified. */ - if (open_files->flags & MODIFIED) { - /* Set fileage and filebot to match the current file - * buffer, and then write it to disk. */ - fileage = open_files->fileage; - filebot = open_files->filebot; - die_save_file(open_files->filename); - } - } - } -#endif +/* Delete a node from the filestruct. */ +void delete_node(filestruct *fileptr) +{ + assert(fileptr != NULL && fileptr->data != NULL); - /* Get out. */ - exit(1); + if (fileptr->data != NULL) + free(fileptr->data); + free(fileptr); } -void die_save_file(const char *die_filename) +/* Duplicate a whole filestruct. */ +filestruct *copy_filestruct(const filestruct *src) { - char *retval; - bool failed = TRUE; + filestruct *head, *copy; - /* If we're using restricted mode, don't write any emergency backup - * files, since that would allow reading from or writing to files - * not specified on the command line. */ - if (ISSET(RESTRICTED)) - return; + assert(src != NULL); - /* If we can't save, we have REAL bad problems, but we might as well - * TRY. */ - if (die_filename[0] == '\0') - die_filename = "nano"; + copy = copy_node(src); + copy->prev = NULL; + head = copy; + src = src->next; - retval = get_next_filename(die_filename, ".save"); - if (retval[0] != '\0') - failed = (write_file(retval, NULL, TRUE, FALSE, TRUE) == -1); + while (src != NULL) { + copy->next = copy_node(src); + copy->next->prev = copy; + copy = copy->next; - if (!failed) - fprintf(stderr, _("\nBuffer written to %s\n"), retval); - else if (retval[0] != '\0') - fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval, - strerror(errno)); - else - fprintf(stderr, _("\nBuffer not written: %s\n"), - _("Too many backup files?")); + src = src->next; + } + copy->next = NULL; - free(retval); + return head; } -/* Die with an error message that the screen was too small if, well, the - * screen is too small. */ -void check_die_too_small(void) +/* Frees a filestruct. */ +void free_filestruct(filestruct *src) { - editwinrows = LINES - 5 + no_more_space() + no_help(); - if (editwinrows < MIN_EDITOR_ROWS) - die(_("Window size is too small for nano...\n")); + assert(src != NULL); + + while (src->next != NULL) { + src = src->next; + delete_node(src->prev); + } + delete_node(src); } -/* Reinitialize the variables that depend on the window size. That is, - * fill and hblank. */ -void resize_variables(void) +void renumber_all(void) { -#ifndef DISABLE_WRAPJUSTIFY - fill = wrap_at; - if (fill <= 0) - fill += COLS; - if (fill < 0) - fill = 0; -#endif + filestruct *temp; + ssize_t line = 1; - hblank = charealloc(hblank, COLS + 1); - charset(hblank, ' ', COLS); - hblank[COLS] = '\0'; + assert(fileage == NULL || fileage != fileage->next); + + for (temp = fileage; temp != NULL; temp = temp->next) + temp->lineno = line++; } -/* Initialize global variables -- no better way for now. If - * save_cutbuffer is TRUE, don't set cutbuffer to NULL. */ -void global_init(bool save_cutbuffer) +void renumber(filestruct *fileptr) { - check_die_too_small(); - resize_variables(); + if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage) + renumber_all(); + else { + ssize_t line = fileptr->prev->lineno; - fileage = NULL; - edittop = NULL; - current = NULL; - if (!save_cutbuffer) - cutbuffer = NULL; - current_x = 0; - placewewant = 0; - current_y = 0; - totlines = 0; - totsize = 0; + assert(fileptr != fileptr->next); + + for (; fileptr != NULL; fileptr = fileptr->next) + fileptr->lineno = ++line; + } } -void window_init(void) +/* Partition a filestruct so it begins at (top, top_x) and ends at (bot, + * bot_x). */ +partition *partition_filestruct(filestruct *top, size_t top_x, + filestruct *bot, size_t bot_x) { - check_die_too_small(); - - if (topwin != NULL) - delwin(topwin); - if (edit != NULL) - delwin(edit); - if (bottomwin != NULL) - delwin(bottomwin); + partition *p; - /* Set up the windows. */ - topwin = newwin(2 - no_more_space(), COLS, 0, 0); - edit = newwin(editwinrows, COLS, 2 - no_more_space(), 0); - bottomwin = newwin(3 - no_help(), COLS, editwinrows + - (2 - no_more_space()), 0); + assert(top != NULL && bot != NULL && fileage != NULL && filebot != NULL); - /* Turn the keypad back on. */ - keypad(edit, TRUE); - keypad(bottomwin, TRUE); -} + /* Initialize the partition. */ + p = (partition *)nmalloc(sizeof(partition)); -#ifndef DISABLE_MOUSE -void mouse_init(void) -{ - if (ISSET(USE_MOUSE)) { - mousemask(BUTTON1_RELEASED, NULL); - mouseinterval(50); + /* If the top and bottom of the partition are different from the top + * and bottom of the filestruct, save the latter and then set them + * to top and bot. */ + if (top != fileage) { + p->fileage = fileage; + fileage = top; } else - mousemask(0, NULL); -} -#endif + p->fileage = NULL; + if (bot != filebot) { + p->filebot = filebot; + filebot = bot; + } else + p->filebot = NULL; -#ifndef DISABLE_HELP -/* This function allocates help_text, and stores the help string in it. - * help_text should be NULL initially. */ -void help_init(void) -{ - size_t allocsize = 0; /* Space needed for help_text. */ - const char *htx[3]; /* Untranslated help message. We break - * it up into three chunks in case the - * full string is too long for the - * compiler to handle. */ - char *ptr; - const shortcut *s; -#ifndef NANO_SMALL - const toggle *t; -#ifdef ENABLE_NANORC - bool old_whitespace = ISSET(WHITESPACE_DISPLAY); - - UNSET(WHITESPACE_DISPLAY); -#endif -#endif - - /* First, set up the initial help text for the current function. */ - if (currshortcut == whereis_list || currshortcut == replace_list - || currshortcut == replace_list_2) { - htx[0] = N_("Search Command Help Text\n\n " - "Enter the words or characters you would like to " - "search for, and then press Enter. If there is a " - "match for the text you entered, the screen will be " - "updated to the location of the nearest match for the " - "search string.\n\n The previous search string will be " - "shown in brackets after the search prompt. Hitting " - "Enter without entering any text will perform the " - "previous search. "); - htx[1] = N_("If you have selected text with the mark and then " - "search to replace, only matches in the selected text " - "will be replaced.\n\n The following function keys are " - "available in Search mode:\n\n"); - htx[2] = NULL; - } else if (currshortcut == gotoline_list) { - htx[0] = N_("Go To Line Help Text\n\n " - "Enter the line number that you wish to go to and hit " - "Enter. If there are fewer lines of text than the " - "number you entered, you will be brought to the last " - "line of the file.\n\n The following function keys are " - "available in Go To Line mode:\n\n"); - htx[1] = NULL; - htx[2] = NULL; - } else if (currshortcut == insertfile_list) { - htx[0] = N_("Insert File Help Text\n\n " - "Type in the name of a file to be inserted into the " - "current file buffer at the current cursor " - "location.\n\n If you have compiled nano with multiple " - "file buffer support, and enable multiple file buffers " - "with the -F or --multibuffer command line flags, the " - "Meta-F toggle, or a nanorc file, inserting a file " - "will cause it to be loaded into a separate buffer " - "(use Meta-< and > to switch between file buffers). "); - htx[1] = N_("If you need another blank buffer, do not enter " - "any filename, or type in a nonexistent filename at " - "the prompt and press Enter.\n\n The following " - "function keys are available in Insert File mode:\n\n"); - htx[2] = NULL; - } else if (currshortcut == writefile_list) { - htx[0] = N_("Write File Help Text\n\n " - "Type the name that you wish to save the current file " - "as and press Enter to save the file.\n\n If you have " - "selected text with the mark, you will be prompted to " - "save only the selected portion to a separate file. To " - "reduce the chance of overwriting the current file with " - "just a portion of it, the current filename is not the " - "default in this mode.\n\n The following function keys " - "are available in Write File mode:\n\n"); - htx[1] = NULL; - htx[2] = NULL; - } -#ifndef DISABLE_BROWSER - else if (currshortcut == browser_list) { - htx[0] = N_("File Browser Help Text\n\n " - "The file browser is used to visually browse the " - "directory structure to select a file for reading " - "or writing. You may use the arrow keys or Page Up/" - "Down to browse through the files, and S or Enter to " - "choose the selected file or enter the selected " - "directory. To move up one level, select the " - "directory called \"..\" at the top of the file " - "list.\n\n The following function keys are available " - "in the file browser:\n\n"); - htx[1] = NULL; - htx[2] = NULL; - } else if (currshortcut == gotodir_list) { - htx[0] = N_("Browser Go To Directory Help Text\n\n " - "Enter the name of the directory you would like to " - "browse to.\n\n If tab completion has not been " - "disabled, you can use the Tab key to (attempt to) " - "automatically complete the directory name.\n\n The " - "following function keys are available in Browser Go " - "To Directory mode:\n\n"); - htx[1] = NULL; - htx[2] = NULL; - } -#endif -#ifndef DISABLE_SPELLER - else if (currshortcut == spell_list) { - htx[0] = N_("Spell Check Help Text\n\n " - "The spell checker checks the spelling of all text in " - "the current file. When an unknown word is " - "encountered, it is highlighted and a replacement can " - "be edited. It will then prompt to replace every " - "instance of the given misspelled word in the current " - "file, or, if you have selected text with the mark, in " - "the selected text.\n\n The following other functions " - "are available in Spell Check mode:\n\n"); - htx[1] = NULL; - htx[2] = NULL; - } -#endif -#ifndef NANO_SMALL - else if (currshortcut == extcmd_list) { - htx[0] = N_("Execute Command Help Text\n\n " - "This menu allows you to insert the output of a " - "command run by the shell into the current buffer (or " - "a new buffer in multiple file buffer mode). If you " - "need another blank buffer, do not enter any " - "command.\n\n The following keys are available in " - "Execute Command mode:\n\n"); - htx[1] = NULL; - htx[2] = NULL; - } -#endif - else { - /* Default to the main help list. */ - htx[0] = N_(" nano help text\n\n " - "The nano editor is designed to emulate the " - "functionality and ease-of-use of the UW Pico text " - "editor. There are four main sections of the editor. " - "The top line shows the program version, the current " - "filename being edited, and whether or not the file " - "has been modified. Next is the main editor window " - "showing the file being edited. The status line is " - "the third line from the bottom and shows important " - "messages. The bottom two lines show the most " - "commonly used shortcuts in the editor.\n\n "); - htx[1] = N_("The notation for shortcuts is as follows: " - "Control-key sequences are notated with a caret (^) " - "symbol and can be entered either by using the Control " - "(Ctrl) key or pressing the Escape (Esc) key twice. " - "Escape-key sequences are notated with the Meta (M) " - "symbol and can be entered using either the Esc, Alt, " - "or Meta key depending on your keyboard setup. "); - htx[2] = N_("Also, pressing Esc twice and then typing a " - "three-digit decimal number from 000 to 255 will enter " - "the character with the corresponding value. The " - "following keystrokes are available in the main editor " - "window. Alternative keys are shown in " - "parentheses:\n\n"); - } - - htx[0] = _(htx[0]); - if (htx[1] != NULL) - htx[1] = _(htx[1]); - if (htx[2] != NULL) - htx[2] = _(htx[2]); + /* Save the line above the top of the partition, detach the top of + * the partition from it, and save the text before top_x in + * top_data. */ + p->top_prev = top->prev; + top->prev = NULL; + p->top_data = mallocstrncpy(NULL, top->data, top_x + 1); + p->top_data[top_x] = '\0'; - allocsize += strlen(htx[0]); - if (htx[1] != NULL) - allocsize += strlen(htx[1]); - if (htx[2] != NULL) - allocsize += strlen(htx[2]); + /* Save the line below the bottom of the partition, detach the + * bottom of the partition from it, and save the text after bot_x in + * bot_data. */ + p->bot_next = bot->next; + bot->next = NULL; + p->bot_data = mallocstrcpy(NULL, bot->data + bot_x); - /* The space needed for the shortcut lists, at most COLS characters, - * plus '\n'. */ - allocsize += (COLS < 24 ? (24 * mb_cur_max()) : - ((COLS + 1) * mb_cur_max())) * length_of_list(currshortcut); + /* Remove all text after bot_x at the bottom of the partition. */ + null_at(&bot->data, bot_x); -#ifndef NANO_SMALL - /* If we're on the main list, we also count the toggle help text. - * Each line has "M-%c\t\t\t", which fills 24 columns, plus a space, - * plus translated text, plus '\n'. */ - if (currshortcut == main_list) { - size_t endis_len = strlen(_("enable/disable")); + /* Remove all text before top_x at the top of the partition. */ + charmove(top->data, top->data + top_x, strlen(top->data) - + top_x + 1); + align(&top->data); - for (t = toggles; t != NULL; t = t->next) - allocsize += 8 + strlen(t->desc) + endis_len; - } -#endif + /* Return the partition. */ + return p; +} - /* help_text has been freed and set to NULL unless the user resized - * while in the help screen. */ - free(help_text); +/* Unpartition a filestruct so it begins at (fileage, 0) and ends at + * (filebot, strlen(filebot)) again. */ +void unpartition_filestruct(partition **p) +{ + char *tmp; - /* Allocate space for the help text. */ - help_text = charalloc(allocsize + 1); + assert(p != NULL && fileage != NULL && filebot != NULL); - /* Now add the text we want. */ - strcpy(help_text, htx[0]); - if (htx[1] != NULL) - strcat(help_text, htx[1]); - if (htx[2] != NULL) - strcat(help_text, htx[2]); + /* Reattach the line above the top of the partition, and restore the + * text before top_x from top_data. Free top_data when we're done + * with it. */ + tmp = mallocstrcpy(NULL, fileage->data); + fileage->prev = (*p)->top_prev; + if (fileage->prev != NULL) + fileage->prev->next = fileage; + fileage->data = charealloc(fileage->data, strlen((*p)->top_data) + + strlen(fileage->data) + 1); + strcpy(fileage->data, (*p)->top_data); + free((*p)->top_data); + strcat(fileage->data, tmp); + free(tmp); - ptr = help_text + strlen(help_text); + /* Reattach the line below the bottom of the partition, and restore + * the text after bot_x from bot_data. Free bot_data when we're + * done with it. */ + filebot->next = (*p)->bot_next; + if (filebot->next != NULL) + filebot->next->prev = filebot; + filebot->data = charealloc(filebot->data, strlen(filebot->data) + + strlen((*p)->bot_data) + 1); + strcat(filebot->data, (*p)->bot_data); + free((*p)->bot_data); - /* Now add our shortcut info. Assume that each shortcut has, at the - * very least, an equivalent control key, an equivalent primary meta - * key sequence, or both. Also assume that the meta key values are - * not control characters. We can display a maximum of 3 shortcut - * entries. */ - for (s = currshortcut; s != NULL; s = s->next) { - int entries = 0; + /* Restore the top and bottom of the filestruct, if they were + * different from the top and bottom of the partition. */ + if ((*p)->fileage != NULL) + fileage = (*p)->fileage; + if ((*p)->filebot != NULL) + filebot = (*p)->filebot; - /* Control key. */ - if (s->ctrlval != NANO_NO_KEY) { - entries++; - /* Yucky sentinel values that we can't handle a better - * way. */ - if (s->ctrlval == NANO_CONTROL_SPACE) { - char *space_ptr = display_string(_("Space"), 0, 6, - FALSE); + /* Uninitialize the partition. */ + free(*p); + *p = NULL; +} - ptr += sprintf(ptr, "^%s", space_ptr); +/* Move all the text between (top, top_x) and (bot, bot_x) in the + * current filestruct to a filestruct beginning with file_top and ending + * with file_bot. If no text is between (top, top_x) and (bot, bot_x), + * don't do anything. */ +void move_to_filestruct(filestruct **file_top, filestruct **file_bot, + filestruct *top, size_t top_x, filestruct *bot, size_t bot_x) +{ + filestruct *top_save; + size_t part_totsize; + bool at_edittop; +#ifndef NANO_SMALL + bool mark_inside = FALSE; +#endif - free(space_ptr); - } else if (s->ctrlval == NANO_CONTROL_8) - ptr += sprintf(ptr, "^?"); - /* Normal values. */ - else - ptr += sprintf(ptr, "^%c", s->ctrlval + 64); - *(ptr++) = '\t'; - } + assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL); - /* Function key. */ - if (s->funcval != NANO_NO_KEY) { - entries++; - /* If this is the first entry, put it in the middle. */ - if (entries == 1) { - entries++; - *(ptr++) = '\t'; - } - ptr += sprintf(ptr, "(F%d)", s->funcval - KEY_F0); - *(ptr++) = '\t'; - } + /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */ + if (top == bot && top_x == bot_x) + return; - /* Primary meta key sequence. If it's the first entry, don't - * put parentheses around it. */ - if (s->metaval != NANO_NO_KEY) { - entries++; - /* If this is the last entry, put it at the end. */ - if (entries == 2 && s->miscval == NANO_NO_KEY) { - entries++; - *(ptr++) = '\t'; - } - /* Yucky sentinel values that we can't handle a better - * way. */ - if (s->metaval == NANO_ALT_SPACE && entries == 1) { - char *space_ptr = display_string(_("Space"), 0, 5, - FALSE); + /* Partition the filestruct so that it contains only the text from + * (top, top_x) to (bot, bot_x), keep track of whether the top of + * the partition is the top of the edit window, and keep track of + * whether the mark begins inside the partition. */ + filepart = partition_filestruct(top, top_x, bot, bot_x); + at_edittop = (fileage == edittop); +#ifndef NANO_SMALL + if (ISSET(MARK_ISSET)) + mark_inside = (mark_beginbuf->lineno >= fileage->lineno && + mark_beginbuf->lineno <= filebot->lineno && + (mark_beginbuf != fileage || mark_beginx >= top_x) && + (mark_beginbuf != filebot || mark_beginx <= bot_x)); +#endif - ptr += sprintf(ptr, "M-%s", space_ptr); + /* Get the number of characters in the text, and subtract it from + * totsize. */ + get_totals(top, bot, NULL, &part_totsize); + totsize -= part_totsize; - free(space_ptr); - } else - /* Normal values. */ - ptr += sprintf(ptr, (entries == 1) ? "M-%c" : "(M-%c)", - toupper(s->metaval)); - *(ptr++) = '\t'; - } + if (*file_top == NULL) { + /* If file_top is empty, just move all the text directly into + * it. This is equivalent to tacking the text in top onto the + * (lack of) text at the end of file_top. */ + *file_top = fileage; + *file_bot = filebot; + } else { + /* Otherwise, tack the text in top onto the text at the end of + * file_bot. */ + (*file_bot)->data = charealloc((*file_bot)->data, + strlen((*file_bot)->data) + strlen(fileage->data) + 1); + strcat((*file_bot)->data, fileage->data); - /* Miscellaneous meta key sequence. */ - if (entries < 3 && s->miscval != NANO_NO_KEY) { - entries++; - /* If this is the last entry, put it at the end. */ - if (entries == 2) { - entries++; - *(ptr++) = '\t'; - } - ptr += sprintf(ptr, "(M-%c)", toupper(s->miscval)); - *(ptr++) = '\t'; + /* Attach the line after top to the line after file_bot. Then, + * if there's more than one line after top, move file_bot down + * to bot. */ + (*file_bot)->next = fileage->next; + if ((*file_bot)->next != NULL) { + (*file_bot)->next->prev = *file_bot; + *file_bot = filebot; } + } - /* Make sure all the help text starts at the same place. */ - while (entries < 3) { - entries++; - *(ptr++) = '\t'; - } + /* Since the text has now been saved, remove it from the filestruct. + * If the top of the partition was the top of the edit window, set + * edittop to where the text used to start. If the mark began + * inside the partition, set the beginning of the mark to where the + * text used to start. */ + fileage = (filestruct *)nmalloc(sizeof(filestruct)); + fileage->data = mallocstrcpy(NULL, ""); + filebot = fileage; + if (at_edittop) + edittop = fileage; +#ifndef NANO_SMALL + if (mark_inside) { + mark_beginbuf = fileage; + mark_beginx = top_x; + } +#endif - assert(s->help != NULL); + /* Restore the current line and cursor position. */ + current = fileage; + current_x = top_x; - if (COLS > 24) { - char *help_ptr = display_string(s->help, 0, COLS - 24, - FALSE); + top_save = fileage; - ptr += sprintf(ptr, help_ptr); + /* Unpartition the filestruct so that it contains all the text + * again, minus the saved text. */ + unpartition_filestruct(&filepart); - free(help_ptr); - } + /* Renumber starting with the beginning line of the old + * partition. */ + renumber(top_save); - ptr += sprintf(ptr, "\n"); - } + if (filebot->data[0] != '\0') + new_magicline(); -#ifndef NANO_SMALL - /* And the toggles... */ - if (currshortcut == main_list) { - for (t = toggles; t != NULL; t = t->next) { + /* Set totlines to the new number of lines in the file. */ + totlines = filebot->lineno; +} - assert(t->desc != NULL); +/* Copy all the text from the filestruct beginning with file_top and + * ending with file_bot to the current filestruct at the current cursor + * position. */ +void copy_from_filestruct(filestruct *file_top, filestruct *file_bot) +{ + filestruct *top_save; + size_t part_totlines, part_totsize; + bool at_edittop; - ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", toupper(t->val), - t->desc, _("enable/disable")); - } - } + assert(file_top != NULL && file_bot != NULL); -#ifdef ENABLE_NANORC - if (old_whitespace) - SET(WHITESPACE_DISPLAY); -#endif -#endif + /* Partition the filestruct so that it contains no text, and keep + * track of whether the top of the partition is the top of the edit + * window. */ + filepart = partition_filestruct(current, current_x, current, + current_x); + at_edittop = (fileage == edittop); - /* If all went well, we didn't overwrite the allocated space for - * help_text. */ - assert(strlen(help_text) <= allocsize + 1); -} -#endif + /* Put the top and bottom of the filestruct at copies of file_top + * and file_bot. */ + fileage = copy_filestruct(file_top); + filebot = fileage; + while (filebot->next != NULL) + filebot = filebot->next; -/* Create a new filestruct node. Note that we specifically do not set - * prevnode->next equal to the new line. */ -filestruct *make_new_node(filestruct *prevnode) -{ - filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct)); + /* Restore the current line and cursor position. */ + current = filebot; + current_x = strlen(filebot->data); + if (fileage == filebot) + current_x += strlen(filepart->top_data); - newnode->data = NULL; - newnode->prev = prevnode; - newnode->next = NULL; - newnode->lineno = (prevnode != NULL) ? prevnode->lineno + 1 : 1; + /* Get the number of lines and the number of characters in the saved + * text, and add the latter to totsize. */ + get_totals(fileage, filebot, &part_totlines, &part_totsize); + totsize += part_totsize; - return newnode; -} + /* If the top of the partition was the top of the edit window, set + * edittop to where the saved text now starts, and update the + * current y-coordinate to account for the number of lines it + * has, less one since the first line will be tacked onto the + * current line. */ + if (at_edittop) + edittop = fileage; + current_y += part_totlines - 1; -/* Make a copy of a filestruct node. */ -filestruct *copy_node(const filestruct *src) -{ - filestruct *dst; + top_save = fileage; - assert(src != NULL); + /* Unpartition the filestruct so that it contains all the text + * again, minus the saved text. */ + unpartition_filestruct(&filepart); - dst = (filestruct *)nmalloc(sizeof(filestruct)); + /* Renumber starting with the beginning line of the old + * partition. */ + renumber(top_save); - dst->data = mallocstrcpy(NULL, src->data); - dst->next = src->next; - dst->prev = src->prev; - dst->lineno = src->lineno; + if (filebot->data[0] != '\0') + new_magicline(); - return dst; + /* Set totlines to the new number of lines in the file. */ + totlines = filebot->lineno; } -/* Splice a node into an existing filestruct. */ -void splice_node(filestruct *begin, filestruct *newnode, filestruct - *end) +void print_view_warning(void) { - assert(newnode != NULL && begin != NULL); - - newnode->next = end; - newnode->prev = begin; - begin->next = newnode; - if (end != NULL) - end->prev = newnode; + statusbar(_("Key illegal in VIEW mode")); } -/* Unlink a node from the rest of the filestruct. */ -void unlink_node(const filestruct *fileptr) +/* What we do when we're all set to exit. */ +void finish(void) { - assert(fileptr != NULL); + if (!ISSET(NO_HELP)) + blank_bottombars(); + else + blank_statusbar(); - if (fileptr->prev != NULL) - fileptr->prev->next = fileptr->next; - if (fileptr->next != NULL) - fileptr->next->prev = fileptr->prev; -} + wrefresh(bottomwin); + endwin(); -/* Delete a node from the filestruct. */ -void delete_node(filestruct *fileptr) -{ - assert(fileptr != NULL && fileptr->data != NULL); + /* Restore the old terminal settings. */ + tcsetattr(0, TCSANOW, &oldterm); - if (fileptr->data != NULL) - free(fileptr->data); - free(fileptr); +#if !defined(NANO_SMALL) && defined(ENABLE_NANORC) + if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG)) + save_history(); +#endif + +#ifdef DEBUG + thanks_for_all_the_fish(); +#endif + + exit(0); } -/* Duplicate a whole filestruct. */ -filestruct *copy_filestruct(const filestruct *src) +/* Die (gracefully?). */ +void die(const char *msg, ...) { - filestruct *head, *copy; + va_list ap; - assert(src != NULL); + endwin(); + curses_ended = TRUE; - copy = copy_node(src); - copy->prev = NULL; - head = copy; - src = src->next; + /* Restore the old terminal settings. */ + tcsetattr(0, TCSANOW, &oldterm); - while (src != NULL) { - copy->next = copy_node(src); - copy->next->prev = copy; - copy = copy->next; + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); - src = src->next; + /* Save the current file buffer if it's been modified. */ + if (ISSET(MODIFIED)) { + /* If we've partitioned the filestruct, unpartition it now. */ + if (filepart != NULL) + unpartition_filestruct(&filepart); + + die_save_file(filename); } - copy->next = NULL; - return head; -} +#ifdef ENABLE_MULTIBUFFER + /* Save all of the other modified file buffers, if any. */ + if (open_files != NULL) { + openfilestruct *tmp = open_files; -/* Frees a filestruct. */ -void free_filestruct(filestruct *src) -{ - assert(src != NULL); + while (tmp != open_files->next) { + open_files = open_files->next; - while (src->next != NULL) { - src = src->next; - delete_node(src->prev); + /* Save the current file buffer if it's been modified. */ + if (open_files->flags & MODIFIED) { + /* Set fileage and filebot to match the current file + * buffer, and then write it to disk. */ + fileage = open_files->fileage; + filebot = open_files->filebot; + die_save_file(open_files->filename); + } + } } - delete_node(src); +#endif + + /* Get out. */ + exit(1); } -/* Partition a filestruct so it begins at (top, top_x) and ends at (bot, - * bot_x). */ -partition *partition_filestruct(filestruct *top, size_t top_x, - filestruct *bot, size_t bot_x) +void die_save_file(const char *die_filename) { - partition *p; + char *retval; + bool failed = TRUE; - assert(top != NULL && bot != NULL && fileage != NULL && filebot != NULL); + /* If we're using restricted mode, don't write any emergency backup + * files, since that would allow reading from or writing to files + * not specified on the command line. */ + if (ISSET(RESTRICTED)) + return; - /* Initialize the partition. */ - p = (partition *)nmalloc(sizeof(partition)); + /* If we can't save, we have REAL bad problems, but we might as well + * TRY. */ + if (die_filename[0] == '\0') + die_filename = "nano"; - /* If the top and bottom of the partition are different from the top - * and bottom of the filestruct, save the latter and then set them - * to top and bot. */ - if (top != fileage) { - p->fileage = fileage; - fileage = top; - } else - p->fileage = NULL; - if (bot != filebot) { - p->filebot = filebot; - filebot = bot; - } else - p->filebot = NULL; + retval = get_next_filename(die_filename, ".save"); + if (retval[0] != '\0') + failed = (write_file(retval, NULL, TRUE, FALSE, TRUE) == -1); - /* Save the line above the top of the partition, detach the top of - * the partition from it, and save the text before top_x in - * top_data. */ - p->top_prev = top->prev; - top->prev = NULL; - p->top_data = mallocstrncpy(NULL, top->data, top_x + 1); - p->top_data[top_x] = '\0'; + if (!failed) + fprintf(stderr, _("\nBuffer written to %s\n"), retval); + else if (retval[0] != '\0') + fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval, + strerror(errno)); + else + fprintf(stderr, _("\nBuffer not written: %s\n"), + _("Too many backup files?")); - /* Save the line below the bottom of the partition, detach the - * bottom of the partition from it, and save the text after bot_x in - * bot_data. */ - p->bot_next = bot->next; - bot->next = NULL; - p->bot_data = mallocstrcpy(NULL, bot->data + bot_x); + free(retval); +} - /* Remove all text after bot_x at the bottom of the partition. */ - null_at(&bot->data, bot_x); +/* Die with an error message that the screen was too small if, well, the + * screen is too small. */ +void check_die_too_small(void) +{ + editwinrows = LINES - 5 + no_more_space() + no_help(); + if (editwinrows < MIN_EDITOR_ROWS) + die(_("Window size is too small for nano...\n")); +} - /* Remove all text before top_x at the top of the partition. */ - charmove(top->data, top->data + top_x, strlen(top->data) - - top_x + 1); - align(&top->data); +/* Reinitialize the variables that depend on the window size. That is, + * fill and hblank. */ +void resize_variables(void) +{ +#ifndef DISABLE_WRAPJUSTIFY + fill = wrap_at; + if (fill <= 0) + fill += COLS; + if (fill < 0) + fill = 0; +#endif - /* Return the partition. */ - return p; + hblank = charealloc(hblank, COLS + 1); + charset(hblank, ' ', COLS); + hblank[COLS] = '\0'; } -/* Unpartition a filestruct so it begins at (fileage, 0) and ends at - * (filebot, strlen(filebot)) again. */ -void unpartition_filestruct(partition **p) +/* Initialize global variables -- no better way for now. If + * save_cutbuffer is TRUE, don't set cutbuffer to NULL. */ +void global_init(bool save_cutbuffer) { - char *tmp; + check_die_too_small(); + resize_variables(); - assert(p != NULL && fileage != NULL && filebot != NULL); + fileage = NULL; + edittop = NULL; + current = NULL; + if (!save_cutbuffer) + cutbuffer = NULL; + current_x = 0; + placewewant = 0; + current_y = 0; + totlines = 0; + totsize = 0; +} - /* Reattach the line above the top of the partition, and restore the - * text before top_x from top_data. Free top_data when we're done - * with it. */ - tmp = mallocstrcpy(NULL, fileage->data); - fileage->prev = (*p)->top_prev; - if (fileage->prev != NULL) - fileage->prev->next = fileage; - fileage->data = charealloc(fileage->data, strlen((*p)->top_data) + - strlen(fileage->data) + 1); - strcpy(fileage->data, (*p)->top_data); - free((*p)->top_data); - strcat(fileage->data, tmp); - free(tmp); +void window_init(void) +{ + check_die_too_small(); - /* Reattach the line below the bottom of the partition, and restore - * the text after bot_x from bot_data. Free bot_data when we're - * done with it. */ - filebot->next = (*p)->bot_next; - if (filebot->next != NULL) - filebot->next->prev = filebot; - filebot->data = charealloc(filebot->data, strlen(filebot->data) + - strlen((*p)->bot_data) + 1); - strcat(filebot->data, (*p)->bot_data); - free((*p)->bot_data); + if (topwin != NULL) + delwin(topwin); + if (edit != NULL) + delwin(edit); + if (bottomwin != NULL) + delwin(bottomwin); - /* Restore the top and bottom of the filestruct, if they were - * different from the top and bottom of the partition. */ - if ((*p)->fileage != NULL) - fileage = (*p)->fileage; - if ((*p)->filebot != NULL) - filebot = (*p)->filebot; + /* Set up the windows. */ + topwin = newwin(2 - no_more_space(), COLS, 0, 0); + edit = newwin(editwinrows, COLS, 2 - no_more_space(), 0); + bottomwin = newwin(3 - no_help(), COLS, editwinrows + + (2 - no_more_space()), 0); - /* Uninitialize the partition. */ - free(*p); - *p = NULL; + /* Turn the keypad back on. */ + keypad(edit, TRUE); + keypad(bottomwin, TRUE); } -/* Move all the text between (top, top_x) and (bot, bot_x) in the - * current filestruct to a filestruct beginning with file_top and ending - * with file_bot. If no text is between (top, top_x) and (bot, bot_x), - * don't do anything. */ -void move_to_filestruct(filestruct **file_top, filestruct **file_bot, - filestruct *top, size_t top_x, filestruct *bot, size_t bot_x) +#ifndef DISABLE_MOUSE +void mouse_init(void) { - filestruct *top_save; - size_t part_totsize; - bool at_edittop; -#ifndef NANO_SMALL - bool mark_inside = FALSE; + if (ISSET(USE_MOUSE)) { + mousemask(BUTTON1_RELEASED, NULL); + mouseinterval(50); + } else + mousemask(0, NULL); +} #endif - assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL); +#ifndef DISABLE_HELP +/* This function allocates help_text, and stores the help string in it. + * help_text should be NULL initially. */ +void help_init(void) +{ + size_t allocsize = 0; /* Space needed for help_text. */ + const char *htx[3]; /* Untranslated help message. We break + * it up into three chunks in case the + * full string is too long for the + * compiler to handle. */ + char *ptr; + const shortcut *s; +#ifndef NANO_SMALL + const toggle *t; +#ifdef ENABLE_NANORC + bool old_whitespace = ISSET(WHITESPACE_DISPLAY); - /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */ - if (top == bot && top_x == bot_x) - return; + UNSET(WHITESPACE_DISPLAY); +#endif +#endif - /* Partition the filestruct so that it contains only the text from - * (top, top_x) to (bot, bot_x), keep track of whether the top of - * the partition is the top of the edit window, and keep track of - * whether the mark begins inside the partition. */ - filepart = partition_filestruct(top, top_x, bot, bot_x); - at_edittop = (fileage == edittop); + /* First, set up the initial help text for the current function. */ + if (currshortcut == whereis_list || currshortcut == replace_list + || currshortcut == replace_list_2) { + htx[0] = N_("Search Command Help Text\n\n " + "Enter the words or characters you would like to " + "search for, and then press Enter. If there is a " + "match for the text you entered, the screen will be " + "updated to the location of the nearest match for the " + "search string.\n\n The previous search string will be " + "shown in brackets after the search prompt. Hitting " + "Enter without entering any text will perform the " + "previous search. "); + htx[1] = N_("If you have selected text with the mark and then " + "search to replace, only matches in the selected text " + "will be replaced.\n\n The following function keys are " + "available in Search mode:\n\n"); + htx[2] = NULL; + } else if (currshortcut == gotoline_list) { + htx[0] = N_("Go To Line Help Text\n\n " + "Enter the line number that you wish to go to and hit " + "Enter. If there are fewer lines of text than the " + "number you entered, you will be brought to the last " + "line of the file.\n\n The following function keys are " + "available in Go To Line mode:\n\n"); + htx[1] = NULL; + htx[2] = NULL; + } else if (currshortcut == insertfile_list) { + htx[0] = N_("Insert File Help Text\n\n " + "Type in the name of a file to be inserted into the " + "current file buffer at the current cursor " + "location.\n\n If you have compiled nano with multiple " + "file buffer support, and enable multiple file buffers " + "with the -F or --multibuffer command line flags, the " + "Meta-F toggle, or a nanorc file, inserting a file " + "will cause it to be loaded into a separate buffer " + "(use Meta-< and > to switch between file buffers). "); + htx[1] = N_("If you need another blank buffer, do not enter " + "any filename, or type in a nonexistent filename at " + "the prompt and press Enter.\n\n The following " + "function keys are available in Insert File mode:\n\n"); + htx[2] = NULL; + } else if (currshortcut == writefile_list) { + htx[0] = N_("Write File Help Text\n\n " + "Type the name that you wish to save the current file " + "as and press Enter to save the file.\n\n If you have " + "selected text with the mark, you will be prompted to " + "save only the selected portion to a separate file. To " + "reduce the chance of overwriting the current file with " + "just a portion of it, the current filename is not the " + "default in this mode.\n\n The following function keys " + "are available in Write File mode:\n\n"); + htx[1] = NULL; + htx[2] = NULL; + } +#ifndef DISABLE_BROWSER + else if (currshortcut == browser_list) { + htx[0] = N_("File Browser Help Text\n\n " + "The file browser is used to visually browse the " + "directory structure to select a file for reading " + "or writing. You may use the arrow keys or Page Up/" + "Down to browse through the files, and S or Enter to " + "choose the selected file or enter the selected " + "directory. To move up one level, select the " + "directory called \"..\" at the top of the file " + "list.\n\n The following function keys are available " + "in the file browser:\n\n"); + htx[1] = NULL; + htx[2] = NULL; + } else if (currshortcut == gotodir_list) { + htx[0] = N_("Browser Go To Directory Help Text\n\n " + "Enter the name of the directory you would like to " + "browse to.\n\n If tab completion has not been " + "disabled, you can use the Tab key to (attempt to) " + "automatically complete the directory name.\n\n The " + "following function keys are available in Browser Go " + "To Directory mode:\n\n"); + htx[1] = NULL; + htx[2] = NULL; + } +#endif +#ifndef DISABLE_SPELLER + else if (currshortcut == spell_list) { + htx[0] = N_("Spell Check Help Text\n\n " + "The spell checker checks the spelling of all text in " + "the current file. When an unknown word is " + "encountered, it is highlighted and a replacement can " + "be edited. It will then prompt to replace every " + "instance of the given misspelled word in the current " + "file, or, if you have selected text with the mark, in " + "the selected text.\n\n The following other functions " + "are available in Spell Check mode:\n\n"); + htx[1] = NULL; + htx[2] = NULL; + } +#endif #ifndef NANO_SMALL - if (ISSET(MARK_ISSET)) - mark_inside = (mark_beginbuf->lineno >= fileage->lineno && - mark_beginbuf->lineno <= filebot->lineno && - (mark_beginbuf != fileage || mark_beginx >= top_x) && - (mark_beginbuf != filebot || mark_beginx <= bot_x)); + else if (currshortcut == extcmd_list) { + htx[0] = N_("Execute Command Help Text\n\n " + "This menu allows you to insert the output of a " + "command run by the shell into the current buffer (or " + "a new buffer in multiple file buffer mode). If you " + "need another blank buffer, do not enter any " + "command.\n\n The following keys are available in " + "Execute Command mode:\n\n"); + htx[1] = NULL; + htx[2] = NULL; + } #endif + else { + /* Default to the main help list. */ + htx[0] = N_(" nano help text\n\n " + "The nano editor is designed to emulate the " + "functionality and ease-of-use of the UW Pico text " + "editor. There are four main sections of the editor. " + "The top line shows the program version, the current " + "filename being edited, and whether or not the file " + "has been modified. Next is the main editor window " + "showing the file being edited. The status line is " + "the third line from the bottom and shows important " + "messages. The bottom two lines show the most " + "commonly used shortcuts in the editor.\n\n "); + htx[1] = N_("The notation for shortcuts is as follows: " + "Control-key sequences are notated with a caret (^) " + "symbol and can be entered either by using the Control " + "(Ctrl) key or pressing the Escape (Esc) key twice. " + "Escape-key sequences are notated with the Meta (M) " + "symbol and can be entered using either the Esc, Alt, " + "or Meta key depending on your keyboard setup. "); + htx[2] = N_("Also, pressing Esc twice and then typing a " + "three-digit decimal number from 000 to 255 will enter " + "the character with the corresponding value. The " + "following keystrokes are available in the main editor " + "window. Alternative keys are shown in " + "parentheses:\n\n"); + } - /* Get the number of characters in the text, and subtract it from - * totsize. */ - get_totals(top, bot, NULL, &part_totsize); - totsize -= part_totsize; + htx[0] = _(htx[0]); + if (htx[1] != NULL) + htx[1] = _(htx[1]); + if (htx[2] != NULL) + htx[2] = _(htx[2]); - if (*file_top == NULL) { - /* If file_top is empty, just move all the text directly into - * it. This is equivalent to tacking the text in top onto the - * (lack of) text at the end of file_top. */ - *file_top = fileage; - *file_bot = filebot; - } else { - /* Otherwise, tack the text in top onto the text at the end of - * file_bot. */ - (*file_bot)->data = charealloc((*file_bot)->data, - strlen((*file_bot)->data) + strlen(fileage->data) + 1); - strcat((*file_bot)->data, fileage->data); + allocsize += strlen(htx[0]); + if (htx[1] != NULL) + allocsize += strlen(htx[1]); + if (htx[2] != NULL) + allocsize += strlen(htx[2]); - /* Attach the line after top to the line after file_bot. Then, - * if there's more than one line after top, move file_bot down - * to bot. */ - (*file_bot)->next = fileage->next; - if ((*file_bot)->next != NULL) { - (*file_bot)->next->prev = *file_bot; - *file_bot = filebot; - } - } + /* The space needed for the shortcut lists, at most COLS characters, + * plus '\n'. */ + allocsize += (COLS < 24 ? (24 * mb_cur_max()) : + ((COLS + 1) * mb_cur_max())) * length_of_list(currshortcut); - /* Since the text has now been saved, remove it from the filestruct. - * If the top of the partition was the top of the edit window, set - * edittop to where the text used to start. If the mark began - * inside the partition, set the beginning of the mark to where the - * text used to start. */ - fileage = (filestruct *)nmalloc(sizeof(filestruct)); - fileage->data = mallocstrcpy(NULL, ""); - filebot = fileage; - if (at_edittop) - edittop = fileage; #ifndef NANO_SMALL - if (mark_inside) { - mark_beginbuf = fileage; - mark_beginx = top_x; + /* If we're on the main list, we also count the toggle help text. + * Each line has "M-%c\t\t\t", which fills 24 columns, plus a space, + * plus translated text, plus '\n'. */ + if (currshortcut == main_list) { + size_t endis_len = strlen(_("enable/disable")); + + for (t = toggles; t != NULL; t = t->next) + allocsize += 8 + strlen(t->desc) + endis_len; } #endif - /* Restore the current line and cursor position. */ - current = fileage; - current_x = top_x; + /* help_text has been freed and set to NULL unless the user resized + * while in the help screen. */ + free(help_text); - top_save = fileage; + /* Allocate space for the help text. */ + help_text = charalloc(allocsize + 1); - /* Unpartition the filestruct so that it contains all the text - * again, minus the saved text. */ - unpartition_filestruct(&filepart); + /* Now add the text we want. */ + strcpy(help_text, htx[0]); + if (htx[1] != NULL) + strcat(help_text, htx[1]); + if (htx[2] != NULL) + strcat(help_text, htx[2]); - /* Renumber starting with the beginning line of the old - * partition. */ - renumber(top_save); + ptr = help_text + strlen(help_text); - if (filebot->data[0] != '\0') - new_magicline(); + /* Now add our shortcut info. Assume that each shortcut has, at the + * very least, an equivalent control key, an equivalent primary meta + * key sequence, or both. Also assume that the meta key values are + * not control characters. We can display a maximum of 3 shortcut + * entries. */ + for (s = currshortcut; s != NULL; s = s->next) { + int entries = 0; - /* Set totlines to the new number of lines in the file. */ - totlines = filebot->lineno; -} + /* Control key. */ + if (s->ctrlval != NANO_NO_KEY) { + entries++; + /* Yucky sentinel values that we can't handle a better + * way. */ + if (s->ctrlval == NANO_CONTROL_SPACE) { + char *space_ptr = display_string(_("Space"), 0, 6, + FALSE); -/* Copy all the text from the filestruct beginning with file_top and - * ending with file_bot to the current filestruct at the current cursor - * position. */ -void copy_from_filestruct(filestruct *file_top, filestruct *file_bot) -{ - filestruct *top_save; - size_t part_totlines, part_totsize; - bool at_edittop; + ptr += sprintf(ptr, "^%s", space_ptr); - assert(file_top != NULL && file_bot != NULL); + free(space_ptr); + } else if (s->ctrlval == NANO_CONTROL_8) + ptr += sprintf(ptr, "^?"); + /* Normal values. */ + else + ptr += sprintf(ptr, "^%c", s->ctrlval + 64); + *(ptr++) = '\t'; + } - /* Partition the filestruct so that it contains no text, and keep - * track of whether the top of the partition is the top of the edit - * window. */ - filepart = partition_filestruct(current, current_x, current, - current_x); - at_edittop = (fileage == edittop); + /* Function key. */ + if (s->funcval != NANO_NO_KEY) { + entries++; + /* If this is the first entry, put it in the middle. */ + if (entries == 1) { + entries++; + *(ptr++) = '\t'; + } + ptr += sprintf(ptr, "(F%d)", s->funcval - KEY_F0); + *(ptr++) = '\t'; + } - /* Put the top and bottom of the filestruct at copies of file_top - * and file_bot. */ - fileage = copy_filestruct(file_top); - filebot = fileage; - while (filebot->next != NULL) - filebot = filebot->next; + /* Primary meta key sequence. If it's the first entry, don't + * put parentheses around it. */ + if (s->metaval != NANO_NO_KEY) { + entries++; + /* If this is the last entry, put it at the end. */ + if (entries == 2 && s->miscval == NANO_NO_KEY) { + entries++; + *(ptr++) = '\t'; + } + /* Yucky sentinel values that we can't handle a better + * way. */ + if (s->metaval == NANO_ALT_SPACE && entries == 1) { + char *space_ptr = display_string(_("Space"), 0, 5, + FALSE); - /* Restore the current line and cursor position. */ - current = filebot; - current_x = strlen(filebot->data); - if (fileage == filebot) - current_x += strlen(filepart->top_data); + ptr += sprintf(ptr, "M-%s", space_ptr); - /* Get the number of lines and the number of characters in the saved - * text, and add the latter to totsize. */ - get_totals(fileage, filebot, &part_totlines, &part_totsize); - totsize += part_totsize; + free(space_ptr); + } else + /* Normal values. */ + ptr += sprintf(ptr, (entries == 1) ? "M-%c" : "(M-%c)", + toupper(s->metaval)); + *(ptr++) = '\t'; + } - /* If the top of the partition was the top of the edit window, set - * edittop to where the saved text now starts, and update the - * current y-coordinate to account for the number of lines it - * has, less one since the first line will be tacked onto the - * current line. */ - if (at_edittop) - edittop = fileage; - current_y += part_totlines - 1; + /* Miscellaneous meta key sequence. */ + if (entries < 3 && s->miscval != NANO_NO_KEY) { + entries++; + /* If this is the last entry, put it at the end. */ + if (entries == 2) { + entries++; + *(ptr++) = '\t'; + } + ptr += sprintf(ptr, "(M-%c)", toupper(s->miscval)); + *(ptr++) = '\t'; + } - top_save = fileage; + /* Make sure all the help text starts at the same place. */ + while (entries < 3) { + entries++; + *(ptr++) = '\t'; + } - /* Unpartition the filestruct so that it contains all the text - * again, minus the saved text. */ - unpartition_filestruct(&filepart); + assert(s->help != NULL); - /* Renumber starting with the beginning line of the old - * partition. */ - renumber(top_save); + if (COLS > 24) { + char *help_ptr = display_string(s->help, 0, COLS - 24, + FALSE); - if (filebot->data[0] != '\0') - new_magicline(); + ptr += sprintf(ptr, help_ptr); - /* Set totlines to the new number of lines in the file. */ - totlines = filebot->lineno; -} + free(help_ptr); + } -void renumber_all(void) -{ - filestruct *temp; - ssize_t line = 1; + ptr += sprintf(ptr, "\n"); + } - assert(fileage == NULL || fileage != fileage->next); +#ifndef NANO_SMALL + /* And the toggles... */ + if (currshortcut == main_list) { + for (t = toggles; t != NULL; t = t->next) { - for (temp = fileage; temp != NULL; temp = temp->next) - temp->lineno = line++; -} + assert(t->desc != NULL); -void renumber(filestruct *fileptr) -{ - if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage) - renumber_all(); - else { - ssize_t line = fileptr->prev->lineno; + ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", toupper(t->val), + t->desc, _("enable/disable")); + } + } - assert(fileptr != fileptr->next); +#ifdef ENABLE_NANORC + if (old_whitespace) + SET(WHITESPACE_DISPLAY); +#endif +#endif - for (; fileptr != NULL; fileptr = fileptr->next) - fileptr->lineno = ++line; - } + /* If all went well, we didn't overwrite the allocated space for + * help_text. */ + assert(strlen(help_text) <= allocsize + 1); } +#endif #ifdef HAVE_GETOPT_LONG #define print1opt(shortflag, longflag, desc) print1opt_full(shortflag, longflag, desc) diff --git a/src/proto.h b/src/proto.h index 33c26f4b..53d7ab50 100644 --- a/src/proto.h +++ b/src/proto.h @@ -246,6 +246,22 @@ void do_cut_till_end(void); void do_uncut_text(void); /* Public functions in files.c. */ +#ifdef ENABLE_MULTIBUFFER +openfilestruct *make_new_opennode(void); +void splice_opennode(openfilestruct *begin, openfilestruct *newnode, + openfilestruct *end); +void unlink_opennode(openfilestruct *fileptr); +void delete_opennode(openfilestruct *fileptr); +#ifdef DEBUG +void free_openfilestruct(openfilestruct *src); +#endif +void add_open_file(bool update); +void load_open_file(void); +void open_prevnext_file(bool next_file); +void open_prevfile_void(void); +void open_nextfile_void(void); +bool close_open_file(void); +#endif void new_file(void); filestruct *read_line(char *buf, filestruct *prevnode, bool *first_line_ins, size_t buf_len); @@ -265,22 +281,6 @@ void do_insertfile( #endif ); void do_insertfile_void(void); -#ifdef ENABLE_MULTIBUFFER -openfilestruct *make_new_opennode(void); -void splice_opennode(openfilestruct *begin, openfilestruct *newnode, - openfilestruct *end); -void unlink_opennode(openfilestruct *fileptr); -void delete_opennode(openfilestruct *fileptr); -#ifdef DEBUG -void free_openfilestruct(openfilestruct *src); -#endif -void add_open_file(bool update); -void load_open_file(void); -void open_prevnext_file(bool next_file); -void open_prevfile_void(void); -void open_nextfile_void(void); -bool close_open_file(void); -#endif char *get_full_path(const char *origpath); char *check_writable_directory(const char *path); char *safe_tempfile(FILE **f); @@ -359,20 +359,6 @@ void do_right(bool allow_update); void do_right_void(void); /* Public functions in nano.c. */ -void print_view_warning(void); -void finish(void); -void die(const char *msg, ...); -void die_save_file(const char *die_filename); -void check_die_too_small(void); -void resize_variables(void); -void global_init(bool save_cutbuffer); -void window_init(void); -#ifndef DISABLE_MOUSE -void mouse_init(void); -#endif -#ifndef DISABLE_HELP -void help_init(void); -#endif filestruct *make_new_node(filestruct *prevnode); filestruct *copy_node(const filestruct *src); void splice_node(filestruct *begin, filestruct *newnode, filestruct @@ -381,14 +367,28 @@ void unlink_node(const filestruct *fileptr); void delete_node(filestruct *fileptr); filestruct *copy_filestruct(const filestruct *src); void free_filestruct(filestruct *src); +void renumber_all(void); +void renumber(filestruct *fileptr); partition *partition_filestruct(filestruct *top, size_t top_x, filestruct *bot, size_t bot_x); void unpartition_filestruct(partition **p); void move_to_filestruct(filestruct **file_top, filestruct **file_bot, filestruct *top, size_t top_x, filestruct *bot, size_t bot_x); void copy_from_filestruct(filestruct *file_top, filestruct *file_bot); -void renumber_all(void); -void renumber(filestruct *fileptr); +void print_view_warning(void); +void finish(void); +void die(const char *msg, ...); +void die_save_file(const char *die_filename); +void check_die_too_small(void); +void resize_variables(void); +void global_init(bool save_cutbuffer); +void window_init(void); +#ifndef DISABLE_MOUSE +void mouse_init(void); +#endif +#ifndef DISABLE_HELP +void help_init(void); +#endif void print1opt_full(const char *shortflag #ifdef HAVE_GETOPT_LONG , const char *longflag