/* Null a string at a certain index and align it. */ void null_at(char **data, size_t index) { assert(data != NULL); *data = charealloc(*data, index + 1); (*data)[index] = '\0'; }
/* Fix the memory allocation for a string. */ void align(char **str) { assert(str != NULL); if (*str != NULL) *str = charealloc(*str, strlen(*str) + 1); }
/* This function is equivalent to getdelim(). */ ssize_t ngetdelim(char **lineptr, size_t *n, int delim, FILE *stream) { size_t indx = 0; int c; /* Sanity checks. */ if (lineptr == NULL || n == NULL || stream == NULL || fileno(stream) == -1) { errno = EINVAL; return -1; } /* Allocate the line the first time. */ if (*lineptr == NULL) { *n = MAX_BUF_SIZE; *lineptr = charalloc(*n); } while ((c = getc(stream)) != EOF) { /* Check if more memory is needed. */ if (indx >= *n) { *n += MAX_BUF_SIZE; *lineptr = charealloc(*lineptr, *n); } /* Put the result in the line. */ (*lineptr)[indx++] = (char)c; /* Bail out. */ if (c == delim) break; } /* Make room for the null character. */ if (indx >= *n) { *n += MAX_BUF_SIZE; *lineptr = charealloc(*lineptr, *n); } /* Null-terminate the buffer. */ null_at(lineptr, indx++); *n = indx; /* The last line may not have the delimiter. We have to return what * we got, and the error will be seen on the next iteration. */ return (c == EOF && (indx - 1) == 0) ? -1 : indx - 1; }
/* Concatenate two allocated strings. */ char* addstrings(char* str1, size_t len1, char* str2, size_t len2) { str1 = charealloc(str1, len1 + len2 + 1); str1[len1] = '\0'; strncat(&str1[len1], str2, len2); free(str2); return str1; }
/* The user typed output_len multibyte characters. Add them to the * statusbar prompt, setting got_enter to TRUE if we get a newline, and * filtering out all ASCII control characters if allow_cntrls is * TRUE. */ void do_statusbar_output(char *output, size_t output_len, bool *got_enter, bool allow_cntrls) { size_t answer_len, i = 0; char *char_buf = charalloc(mb_cur_max()); int char_buf_len; assert(answer != NULL); answer_len = strlen(answer); *got_enter = FALSE; while (i < output_len) { /* If allow_cntrls is TRUE, convert nulls and newlines * properly. */ if (allow_cntrls) { /* Null to newline, if needed. */ if (output[i] == '\0') output[i] = '\n'; /* Newline to Enter, if needed. */ else if (output[i] == '\n') { /* Set got_enter to TRUE to indicate that we got the * Enter key, put back the rest of the characters in * output so that they can be parsed and output again, * and get out. */ *got_enter = TRUE; unparse_kbinput(output + i, output_len - i); return; } } /* Interpret the next multibyte character. */ char_buf_len = parse_mbchar(output + i, char_buf, NULL); i += char_buf_len; /* If allow_cntrls is FALSE, filter out an ASCII control * character. */ if (!allow_cntrls && is_ascii_cntrl_char(*(output + i - char_buf_len))) continue; /* More dangerousness fun. =) */ answer = charealloc(answer, answer_len + (char_buf_len * 2)); assert(statusbar_x <= answer_len); charmove(answer + statusbar_x + char_buf_len, answer + statusbar_x, answer_len - statusbar_x + char_buf_len); strncpy(answer + statusbar_x, char_buf, char_buf_len); answer_len += char_buf_len; statusbar_x += char_buf_len; } free(char_buf); statusbar_pww = statusbar_xplustabs(); update_statusbar_line(answer, statusbar_x); }
/* The main rcfile function. It tries to open the system-wide rcfile, * followed by the current user's rcfile. */ void do_rcfile(void) { struct stat rcinfo; FILE *rcstream; nanorc = mallocstrcpy(nanorc, SYSCONFDIR "/nanorc"); /* Don't open directories, character files, or block files. */ if (stat(nanorc, &rcinfo) != -1) { if (S_ISDIR(rcinfo.st_mode) || S_ISCHR(rcinfo.st_mode) || S_ISBLK(rcinfo.st_mode)) rcfile_error(S_ISDIR(rcinfo.st_mode) ? _("\"%s\" is a directory") : _("\"%s\" is a device file"), nanorc); } #ifdef DEBUG fprintf(stderr, "Parsing file \"%s\"\n", nanorc); #endif /* Try to open the system-wide nanorc. */ rcstream = fopen(nanorc, "rb"); if (rcstream != NULL) parse_rcfile(rcstream #ifdef ENABLE_COLOR , FALSE #endif ); #ifdef DISABLE_ROOTWRAPPING /* We've already read SYSCONFDIR/nanorc, if it's there. If we're * root, and --disable-wrapping-as-root is used, turn wrapping off * now. */ if (geteuid() == NANO_ROOT_UID) SET(NO_WRAP); #endif get_homedir(); if (homedir == NULL) rcfile_error(N_("I can't find my home directory! Wah!")); else { #ifndef RCFILE_NAME #define RCFILE_NAME ".nanorc" #endif nanorc = charealloc(nanorc, strlen(homedir) + strlen(RCFILE_NAME) + 2); sprintf(nanorc, "%s/%s", homedir, RCFILE_NAME); /* Don't open directories, character files, or block files. */ if (stat(nanorc, &rcinfo) != -1) { if (S_ISDIR(rcinfo.st_mode) || S_ISCHR(rcinfo.st_mode) || S_ISBLK(rcinfo.st_mode)) rcfile_error(S_ISDIR(rcinfo.st_mode) ? _("\"%s\" is a directory") : _("\"%s\" is a device file"), nanorc); } /* Try to open the current user's nanorc. */ rcstream = fopen(nanorc, "rb"); if (rcstream == NULL) { /* Don't complain about the file's not existing. */ if (errno != ENOENT) rcfile_error(N_("Error reading %s: %s"), nanorc, strerror(errno)); } else parse_rcfile(rcstream #ifdef ENABLE_COLOR , FALSE #endif ); } free(nanorc); nanorc = NULL; if (errors && !ISSET(QUIET)) { errors = FALSE; fprintf(stderr, _("\nPress Enter to continue starting nano.\n")); while (getchar() != '\n') ; } #ifdef ENABLE_COLOR set_colorpairs(); #endif }
/* Our main file browser function. path is the tilde-expanded path we * start browsing from. */ char *do_browser(char *path, DIR *dir) { char *retval = NULL; int kbinput; bool old_const_update = ISSET(CONST_UPDATE); char *prev_dir = NULL; /* The directory we were in before backing up to "..". */ char *ans = NULL; /* The last answer the user typed at the statusbar prompt. */ size_t old_selected; /* The selected file we had before the current selected file. */ functionptrtype func; /* The function of the key the user typed in. */ curs_set(0); blank_statusbar(); bottombars(MBROWSER); wnoutrefresh(bottomwin); UNSET(CONST_UPDATE); ans = mallocstrcpy(NULL, ""); change_browser_directory: /* We go here after we select a new directory. */ /* Start with no key pressed. */ kbinput = ERR; path = mallocstrassn(path, get_full_path(path)); /* Save the current path in order to be used later. */ path_save = path; assert(path != NULL && path[strlen(path) - 1] == '/'); /* Get the file list, and set longest and width in the process. */ browser_init(path, dir); assert(filelist != NULL); /* Sort the file list. */ qsort(filelist, filelist_len, sizeof(char *), diralphasort); /* If prev_dir isn't NULL, select the directory saved in it, and * then blow it away. */ if (prev_dir != NULL) { browser_select_dirname(prev_dir); free(prev_dir); prev_dir = NULL; /* Otherwise, select the first file or directory in the list. */ } else selected = 0; old_selected = (size_t)-1; titlebar(path); while (TRUE) { struct stat st; int i; size_t fileline = selected / width; /* The line number the selected file is on. */ char *new_path; /* The path we switch to at the "Go to Directory" * prompt. */ /* Display the file list if we don't have a key, or if the * selected file has changed, and set width in the process. */ if (kbinput == ERR || old_selected != selected) browser_refresh(); old_selected = selected; kbinput = get_kbinput(edit); #ifndef NANO_TINY if (kbinput == KEY_WINCH) { kbinput = ERR; curs_set(0); continue; } #endif #ifndef DISABLE_MOUSE if (kbinput == KEY_MOUSE) { int mouse_x, mouse_y; /* We can click on the edit window to select a * filename. */ if (get_mouseinput(&mouse_x, &mouse_y, TRUE) == 0 && wmouse_trafo(edit, &mouse_y, &mouse_x, FALSE)) { /* longest is the width of each column. There * are two spaces between each column. */ selected = (fileline / editwinrows) * (editwinrows * width) + (mouse_y * width) + (mouse_x / (longest + 2)); /* If they clicked beyond the end of a row, * select the last filename in that row. */ if (mouse_x > width * (longest + 2)) selected--; /* If we're off the screen, select the last filename. */ if (selected > filelist_len - 1) selected = filelist_len - 1; /* If we selected the same filename as last time, * put back the Enter key so that it's read in. */ if (old_selected == selected) unget_kbinput(sc_seq_or(do_enter_void, 0), FALSE, FALSE); } } #endif /* !DISABLE_MOUSE */ func = parse_browser_input(&kbinput); if (func == total_refresh) { total_redraw(); } else if (func == do_help_void) { #ifndef DISABLE_HELP do_help_void(); /* Perhaps the window dimensions have changed. */ browser_refresh(); curs_set(0); #else nano_disabled_msg(); #endif } else if (func == do_search) { /* Search for a filename. */ curs_set(1); do_filesearch(); curs_set(0); } else if (func == do_research) { /* Search for another filename. */ do_fileresearch(); } else if (func == do_page_up) { if (selected >= (editwinrows + fileline % editwinrows) * width) selected -= (editwinrows + fileline % editwinrows) * width; else selected = 0; } else if (func == do_page_down) { selected += (editwinrows - fileline % editwinrows) * width; if (selected > filelist_len - 1) selected = filelist_len - 1; } else if (func == do_first_file) { selected = 0; } else if (func == do_last_file) { selected = filelist_len - 1; } else if (func == goto_dir_void) { /* Go to a specific directory. */ curs_set(1); i = do_prompt(TRUE, #ifndef DISABLE_TABCOMP FALSE, #endif MGOTODIR, ans, #ifndef DISABLE_HISTORIES NULL, #endif /* TRANSLATORS: This is a prompt. */ browser_refresh, _("Go To Directory")); curs_set(0); bottombars(MBROWSER); /* If the directory begins with a newline (i.e. an * encoded null), treat it as though it's blank. */ if (i < 0 || *answer == '\n') { /* We canceled. Indicate that on the statusbar, and * blank out ans, since we're done with it. */ statusbar(_("Cancelled")); ans = mallocstrcpy(ans, ""); continue; } else if (i != 0) { /* Put back the "Go to Directory" key and save * answer in ans, so that the file list is displayed * again, the prompt is displayed again, and what we * typed before at the prompt is displayed again. */ unget_kbinput(sc_seq_or(do_gotolinecolumn_void, 0), FALSE, FALSE); ans = mallocstrcpy(ans, answer); continue; } /* We have a directory. Blank out ans, since we're done * with it. */ ans = mallocstrcpy(ans, ""); /* Convert newlines to nulls, just before we go to the * directory. */ sunder(answer); align(&answer); new_path = real_dir_from_tilde(answer); if (new_path[0] != '/') { new_path = charealloc(new_path, strlen(path) + strlen(answer) + 1); sprintf(new_path, "%s%s", path, answer); } #ifndef DISABLE_OPERATINGDIR if (check_operating_dir(new_path, FALSE)) { statusbar(_("Can't go outside of %s in restricted mode"), operating_dir); free(new_path); continue; } #endif dir = opendir(new_path); if (dir == NULL) { /* We can't open this directory for some reason. * Complain. */ statusbar(_("Error reading %s: %s"), answer, strerror(errno)); beep(); free(new_path); continue; } /* Start over again with the new path value. */ free(path); path = new_path; goto change_browser_directory; } else if (func == do_up_void) { if (selected >= width) selected -= width; } else if (func == do_down_void) { if (selected + width <= filelist_len - 1) selected += width; } else if (func == do_left) { if (selected > 0) selected--; } else if (func == do_right) { if (selected < filelist_len - 1) selected++; } else if (func == do_enter_void) { /* We can't move up from "/". */ if (strcmp(filelist[selected], "/..") == 0) { statusbar(_("Can't move up a directory")); beep(); continue; } #ifndef DISABLE_OPERATINGDIR /* Note: The selected file can be outside the operating * directory if it's ".." or if it's a symlink to a * directory outside the operating directory. */ if (check_operating_dir(filelist[selected], FALSE)) { statusbar(_("Can't go outside of %s in restricted mode"), operating_dir); beep(); continue; } #endif if (stat(filelist[selected], &st) == -1) { /* We can't open this file for some reason. * Complain. */ statusbar(_("Error reading %s: %s"), filelist[selected], strerror(errno)); beep(); continue; } if (!S_ISDIR(st.st_mode)) { /* We've successfully opened a file, we're done, so * get out. */ retval = mallocstrcpy(NULL, filelist[selected]); break; } else if (strcmp(tail(filelist[selected]), "..") == 0) /* We've successfully opened the parent directory, * save the current directory in prev_dir, so that * we can easily return to it by hitting Enter. */ prev_dir = mallocstrcpy(NULL, striponedir(filelist[selected])); dir = opendir(filelist[selected]); if (dir == NULL) { /* We can't open this directory for some reason. * Complain. */ statusbar(_("Error reading %s: %s"), filelist[selected], strerror(errno)); beep(); continue; } path = mallocstrcpy(path, filelist[selected]); /* Start over again with the new path value. */ goto change_browser_directory; } else if (func == do_exit) { /* Exit from the file browser. */ break; } } titlebar(NULL); edit_refresh(); curs_set(1); if (old_const_update) SET(CONST_UPDATE); free(path); free(ans); free_chararray(filelist, filelist_len); filelist = NULL; filelist_len = 0; return retval; }
char *display_string(const char *buf, size_t start_col, size_t len, bool dollars) { size_t start_index; /* Index in buf of the first character shown. */ size_t column; /* Screen column that start_index corresponds to. */ size_t alloc_len; /* The length of memory allocated for converted. */ char *converted; /* The string we return. */ size_t index; /* Current position in converted. */ char *buf_mb; int buf_mb_len; /* If dollars is true, make room for the "$" at the end of the * line. */ if (dollars && len > 0 && strlenpt(buf) > start_col + len) { len--; } if (len == 0) { return mallocstrcpy(NULL, ""); } buf_mb = charalloc(mb_cur_max()); start_index = actual_x(buf, start_col); column = strnlenpt(buf, start_index); assert(column <= start_col); /* Make sure there's enough room for the initial character, whether * it's a multibyte control character, a non-control multibyte * character, a tab character, or a null terminator. Rationale: * * multibyte control character followed by a null terminator: * 1 byte ('^') + mb_cur_max() bytes + 1 byte ('\0') * multibyte non-control character followed by a null terminator: * mb_cur_max() bytes + 1 byte ('\0') * tab character followed by a null terminator: * mb_cur_max() bytes + (tabsize - 1) bytes + 1 byte ('\0') * * Since tabsize has a minimum value of 1, it can substitute for 1 * byte above. */ alloc_len = (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE; converted = charalloc(alloc_len); index = 0; if (buf[start_index] != '\0' && buf[start_index] != '\t' && (column < start_col || (dollars && column > 0))) { /* We don't display all of buf[start_index] since it starts to * the left of the screen. */ buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL); if (is_cntrl_mbchar(buf_mb)) { if (column < start_col) { char *ctrl_buf_mb = charalloc(mb_cur_max()); int ctrl_buf_mb_len, i; ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb, &ctrl_buf_mb_len); for (i = 0; i < ctrl_buf_mb_len; i++) { converted[index++] = ctrl_buf_mb[i]; } start_col += mbwidth(ctrl_buf_mb); free(ctrl_buf_mb); start_index += buf_mb_len; } } else if (using_utf8() && mbwidth(buf_mb) == 2) { if (column >= start_col) { converted[index++] = ' '; start_col++; } converted[index++] = ' '; start_col++; start_index += buf_mb_len; } } while (buf[start_index] != '\0') { buf_mb_len = parse_mbchar(buf + start_index, buf_mb, NULL); /* Make sure there's enough room for the next character, whether * it's a multibyte control character, a non-control multibyte * character, a tab character, or a null terminator. */ if (index + mb_cur_max() + tabsize + 1 >= alloc_len - 1) { alloc_len += (mb_cur_max() + tabsize + 1) * MAX_BUF_SIZE; converted = charealloc(converted, alloc_len); } /* If buf contains a tab character, interpret it. */ if (*buf_mb == '\t') { if (ISSET(WHITESPACE_DISPLAY)) { int i; for (i = 0; i < whitespace_len[0]; i++) { converted[index++] = whitespace[i]; } } else { converted[index++] = ' '; } start_col++; while (start_col % tabsize != 0) { converted[index++] = ' '; start_col++; } } else if (is_cntrl_mbchar(buf_mb)) { /* If buf contains a control character, interpret it. */ char *ctrl_buf_mb = charalloc(mb_cur_max()); int ctrl_buf_mb_len, i; converted[index++] = '^'; start_col++; ctrl_buf_mb = control_mbrep(buf_mb, ctrl_buf_mb, &ctrl_buf_mb_len); for (i = 0; i < ctrl_buf_mb_len; i++) { converted[index++] = ctrl_buf_mb[i]; } start_col += mbwidth(ctrl_buf_mb); free(ctrl_buf_mb); /* If buf contains a space character, interpret it. */ } else if (*buf_mb == ' ') { if (ISSET(WHITESPACE_DISPLAY)) { int i; for (i = whitespace_len[0]; i < whitespace_len[0] + whitespace_len[1]; i++) { converted[index++] = whitespace[i]; } } else { converted[index++] = ' '; } start_col++; } else { /* If buf contains a non-control character, interpret it. If buf * contains an invalid multibyte sequence, display it as such. */ char *nctrl_buf_mb = charalloc(mb_cur_max()); int nctrl_buf_mb_len, i; /* Make sure an invalid sequence-starter byte is properly * terminated, so that it doesn't pick up lingering bytes * of any previous content. */ null_at(&buf_mb, buf_mb_len); nctrl_buf_mb = mbrep(buf_mb, nctrl_buf_mb, &nctrl_buf_mb_len); for (i = 0; i < nctrl_buf_mb_len; i++) { converted[index++] = nctrl_buf_mb[i]; } start_col += mbwidth(nctrl_buf_mb); free(nctrl_buf_mb); } start_index += buf_mb_len; } free(buf_mb); assert(alloc_len >= index + 1); /* Null-terminate converted. */ converted[index] = '\0'; /* Make sure converted takes up no more than len columns. */ index = actual_x(converted, len); null_at(&converted, index); return converted; }