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,
/* 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)