/* Update a history list. h should be the current position in the * list. */ void update_history(linestruct **h, const char *s) { linestruct **hage = NULL, **hbot = NULL, *p; assert(h != NULL && s != NULL); if (*h == search_history) { hage = &searchage; hbot = &searchbot; } else if (*h == replace_history) { hage = &replaceage; hbot = &replacebot; } assert(hage != NULL && hbot != NULL); /* If this string is already in the history, delete it. */ p = find_history(*hage, *hbot, s, strlen(s)); if (p != NULL) { linestruct *foo, *bar; /* If the string is at the beginning, move the beginning down to * the next string. */ if (p == *hage) *hage = (*hage)->next; /* Delete the string. */ foo = p; bar = p->next; unlink_node(foo); delete_node(foo); renumber(bar); } /* If the history is full, delete the beginning entry to make room * for the new entry at the end. We assume that MAX_SEARCH_HISTORY * is greater than zero. */ if ((*hbot)->lineno == MAX_SEARCH_HISTORY + 1) { linestruct *foo = *hage; *hage = (*hage)->next; unlink_node(foo); delete_node(foo); renumber(*hage); } /* Add the new entry to the end. */ (*hbot)->data = mallocstrcpy((*hbot)->data, s); splice_node(*hbot, make_new_node(*hbot), (*hbot)->next); *hbot = (*hbot)->next; (*hbot)->data = mallocstrcpy(NULL, ""); /* Indicate that the history's been changed. */ history_changed = TRUE; /* Set the current position in the list to the bottom. */ *h = *hbot; }
/* Search for a string. */ void do_search(void) { linestruct *fileptr = openfile->current; size_t fileptr_x = openfile->current_x; size_t pww_save = openfile->placewewant; int i; bool didfind; i = search_init(FALSE, FALSE); if (i == -1) /* Cancel, Go to Line, blank search string, or regcomp() failed. */ search_replace_abort(); else if (i == -2) /* Replace. */ do_replace(); #if !defined(NANO_TINY) || defined(HAVE_REGEX_H) else if (i == 1) /* Case Sensitive, Backwards, or Regexp search toggle. */ do_search(); #endif if (i != 0) return; /* If answer is now "", copy last_search into answer. */ if (*answer == '\0') answer = mallocstrcpy(answer, last_search); else last_search = mallocstrcpy(last_search, answer); #ifndef DISABLE_HISTORIES /* If answer is not "", add this search string to the search history * list. */ if (answer[0] != '\0') update_history(&search_history, answer); #endif findnextstr_wrap_reset(); didfind = findnextstr( #ifndef DISABLE_SPELLER FALSE, #endif openfile->current, openfile->current_x, answer, NULL); /* If we found something, and we're back at the exact same spot where * we started searching, then this is the only occurrence. */ if (fileptr == openfile->current && fileptr_x == openfile->current_x && didfind) { statusbar(_("This is the only occurrence")); } openfile->placewewant = xplustabs(); edit_redraw(fileptr, pww_save); search_replace_abort(); }
/* Initialize the search and replace history lists. */ void history_init(void) { search_history = make_new_node(NULL); search_history->data = mallocstrcpy(NULL, ""); searchage = search_history; searchbot = search_history; replace_history = make_new_node(NULL); replace_history->data = mallocstrcpy(NULL, ""); replaceage = replace_history; replacebot = replace_history; }
/* Update a history list (the one in which item is the current position) * with a fresh string text. That is: add text, or move it to the end. */ void update_history(linestruct **item, const char *text) { linestruct **htop = NULL, **hbot = NULL, *thesame; if (*item == search_history) { htop = &searchtop; hbot = &searchbot; } else if (*item == replace_history) { htop = &replacetop; hbot = &replacebot; } else if (*item == execute_history) { htop = &executetop; hbot = &executebot; } /* See if the string is already in the history. */ thesame = find_history(*hbot, *htop, text, HIGHEST_POSITIVE); /* If an identical string was found, delete that item. */ if (thesame != NULL) { linestruct *after = thesame->next; /* If the string is at the head of the list, move the head. */ if (thesame == *htop) *htop = after; unlink_node(thesame); renumber_from(after); } /* If the history is full, delete the oldest item (the one at the * head of the list), to make room for a new item at the end. */ if ((*hbot)->lineno == MAX_SEARCH_HISTORY + 1) { linestruct *oldest = *htop; *htop = (*htop)->next; unlink_node(oldest); renumber_from(*htop); } /* Store the fresh string in the last item, then create a new item. */ (*hbot)->data = mallocstrcpy((*hbot)->data, text); splice_node(*hbot, make_new_node(*hbot)); *hbot = (*hbot)->next; (*hbot)->data = mallocstrcpy(NULL, ""); /* Indicate that the history needs to be saved on exit. */ history_changed = TRUE; /* Set the current position in the list to the bottom. */ *item = *hbot; }
void f() { if (buf[0] == '~' && strchr(tmp, '/') == NULL) { buf = mallocstrcpy(buf, tmp); matches = username_tab_completion(tmp, &num_matches); } /* If we're in the middle of the original line, copy the string only up to the cursor position into buf, so tab completion will result in buf's containing only the tab-completed path/filename. */ else if (strlen(buf) > strlen(tmp)) buf = mallocstrcpy(buf, tmp); }
/* Fix the regex if we're on platforms which require an adjustment * from GNU-style to BSD-style word boundaries. */ const char *fixbounds(const char *r) { #ifndef GNU_WORDBOUNDS int i, j = 0; char *r2 = charalloc(strlen(r) * 5); char *r3; #ifdef DEBUG fprintf(stderr, "fixbounds(): Start string = \"%s\"\n", r); #endif for (i = 0; i < strlen(r); i++) { if (r[i] != '\0' && r[i] == '\\' && (r[i + 1] == '>' || r[i + 1] == '<')) { strcpy(&r2[j], "[[:"); r2[j + 3] = r[i + 1]; strcpy(&r2[j + 4], ":]]"); i++; j += 6; } else r2[j] = r[i]; j++; } r2[j] = '\0'; r3 = mallocstrcpy(NULL, r2); free(r2); #ifdef DEBUG fprintf(stderr, "fixbounds(): Ending string = \"%s\"\n", r3); #endif return (const char *) r3; #endif /* !GNU_WORDBOUNDS */ return r; }
/* Initialize the lists of historical search and replace strings * and the list of historical executed commands. */ void history_init(void) { search_history = make_new_node(NULL); search_history->data = mallocstrcpy(NULL, ""); searchtop = search_history; searchbot = search_history; replace_history = make_new_node(NULL); replace_history->data = mallocstrcpy(NULL, ""); replacetop = replace_history; replacebot = replace_history; execute_history = make_new_node(NULL); execute_history->data = mallocstrcpy(NULL, ""); executetop = execute_history; executebot = execute_history; }
/* Move h to the next string that's a tab completion of the string s, * looking at only the first len characters of s, and return that * string. If there isn't one, or if len is 0, don't move h and return * s. */ char *get_history_completion(linestruct **h, char *s, size_t len) { if (len > 0) { linestruct *htop = NULL, *hbot = NULL, *p; if (*h == search_history) { htop = searchtop; hbot = searchbot; } else if (*h == replace_history) { htop = replacetop; hbot = replacebot; } else if (*h == execute_history) { htop = executetop; hbot = executebot; } /* Search the history list from the current position to the top * for a match of len characters. Skip over an exact match. */ p = find_history((*h)->prev, htop, s, len); while (p != NULL && strcmp(p->data, s) == 0) p = find_history(p->prev, htop, s, len); if (p != NULL) { *h = p; return mallocstrcpy(s, (*h)->data); } /* Search the history list from the bottom to the current position * for a match of len characters. Skip over an exact match. */ p = find_history(hbot, *h, s, len); while (p != NULL && strcmp(p->data, s) == 0) p = find_history(p->prev, *h, s, len); if (p != NULL) { *h = p; return mallocstrcpy(s, (*h)->data); } } /* If we're here, we didn't find a match, we didn't find an inexact * match, or len is 0. Return s. */ return (char *)s; }
/* Parse and store the name given after a linter/formatter command. */ void pick_up_name(const char *kind, char *ptr, char **storage) { assert(ptr != NULL); if (!opensyntax) { rcfile_error( N_("A '%s' command requires a preceding 'syntax' command"), kind); return; } if (*ptr == '\0') { rcfile_error(N_("Missing command after '%s'"), kind); return; } free(*storage); /* Allow unsetting the command by using an empty string. */ if (!strcmp(ptr, "\"\"")) *storage = NULL; else if (*ptr == '"') { *storage = mallocstrcpy(NULL, ++ptr); char* q = *storage; char* p = *storage; /* Snip out the backslashes of escaped characters. */ while (*p != '"') { if (*p == '\0') { rcfile_error(N_("Argument of '%s' lacks closing \""), kind); free(*storage); *storage = NULL; return; } else if (*p == '\\' && *(p + 1) != '\0') { p++; } *q++ = *p++; } *q = '\0'; } else *storage = mallocstrcpy(NULL, ptr); }
int execvp(const std::string& file, const std::vector<std::string>& argv, char ***buf) { *buf = (char **)malloc((argv.size() + 1) * sizeof(char *)); char **args = *buf; size_t pos = 0; for (auto arg : argv) { args[pos++] = mallocstrcpy(NULL, arg.c_str()); } args[pos] = NULL; return execvp(file.c_str(), args); }
/* Parse the header-line regexes that may influence the choice of syntax. */ void parse_header_exp(char *ptr) { regexlisttype *endheader = NULL; assert(ptr != NULL); if (syntaxes == NULL) { rcfile_error( N_("Cannot add a header regex without a syntax command")); return; } if (*ptr == '\0') { rcfile_error(N_("Missing regex string")); return; } while (*ptr != '\0') { const char *regexstring; regexlisttype *newheader; if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); ptr = parse_next_regex(ptr); continue; } ptr++; regexstring = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; newheader = (regexlisttype *)nmalloc(sizeof(regexlisttype)); /* Save the regex string if it's valid. */ if (nregcomp(regexstring, 0)) { newheader->ext_regex = mallocstrcpy(NULL, regexstring); newheader->ext = NULL; if (endheader == NULL) endsyntax->headers = newheader; else endheader->next = newheader; endheader = newheader; endheader->next = NULL; } else free(newheader); } }
/* Append a new magicline to filebot. */ void new_magicline(void) { openfile->filebot->next = (filestruct *)nmalloc(sizeof(filestruct)); openfile->filebot->next->data = mallocstrcpy(NULL, ""); openfile->filebot->next->prev = openfile->filebot; openfile->filebot->next->next = NULL; openfile->filebot->next->lineno = openfile->filebot->lineno + 1; #ifndef DISABLE_COLOR openfile->filebot->next->multidata = NULL; #endif openfile->filebot = openfile->filebot->next; openfile->totsize++; }
/* The file browser front end. We check to see if inpath has a * directory in it. If it does, we start do_browser() from there. * Otherwise, we start do_browser() from the current directory. */ char *do_browse_from(const char *inpath) { struct stat st; char *path; /* This holds the tilde-expanded version of inpath. */ DIR *dir = NULL; assert(inpath != NULL); path = real_dir_from_tilde(inpath); /* Perhaps path is a directory. If so, we'll pass it to * do_browser(). Or perhaps path is a directory / a file. If so, * we'll try stripping off the last path element and passing it to * do_browser(). Or perhaps path doesn't have a directory portion * at all. If so, we'll just pass the current directory to * do_browser(). */ if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { path = mallocstrassn(path, striponedir(path)); if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { free(path); path = charalloc(PATH_MAX + 1); path = getcwd(path, PATH_MAX + 1); if (path != NULL) align(&path); } } #ifndef DISABLE_OPERATINGDIR /* If the resulting path isn't in the operating directory, use * the operating directory instead. */ if (check_operating_dir(path, FALSE)) path = mallocstrcpy(path, operating_dir); #endif if (path != NULL) dir = opendir(path); /* If we can't open the path, get out. */ if (dir == NULL) { free(path); beep(); return NULL; } return do_browser(path, dir); }
/* Return the user's home directory. We use $HOME, and if that fails, * we fall back on the home directory of the effective user ID. */ void get_homedir(void) { if (homedir == NULL) { const char *homenv = getenv("HOME"); if (homenv == NULL) { const struct passwd *userage = getpwuid(geteuid()); if (userage != NULL) homenv = userage->pw_dir; } homedir = mallocstrcpy(NULL, homenv); } }
/* Return the user's home directory. We use $HOME, and if that fails, * we fall back on the home directory of the effective user ID. */ void get_homedir(void) { if (homedir == NULL) { const char *homenv = getenv("HOME"); if (homenv == NULL) { const struct passwd *userage = getpwuid(geteuid()); if (userage != NULL) homenv = userage->pw_dir; } /* Only set homedir if some home directory could be determined, * otherwise keep homedir NULL. */ if (homenv != NULL && strcmp(homenv, "") != 0) homedir = mallocstrcpy(NULL, homenv); } }
/* Search for the last string without prompting. */ void do_research(void) { linestruct *fileptr = openfile->current; size_t fileptr_x = openfile->current_x; size_t pww_save = openfile->placewewant; bool didfind; focusing = TRUE; #ifndef DISABLE_HISTORIES /* If nothing was searched for yet during this run of nano, but * there is a search history, take the most recent item. */ if (last_search[0] == '\0' && searchbot->prev != NULL) last_search = mallocstrcpy(last_search, searchbot->prev->data); #endif if (last_search[0] != '\0') { #ifdef HAVE_REGEX_H /* Since answer is "", use last_search! */ if (ISSET(USE_REGEXP) && !regexp_init(last_search)) return; #endif findnextstr_wrap_reset(); didfind = findnextstr( #ifndef DISABLE_SPELLER FALSE, #endif openfile->current, openfile->current_x, last_search, NULL); /* If we found something, and we're back at the exact same spot * where we started searching, then this is the only occurrence. */ if (fileptr == openfile->current && fileptr_x == openfile->current_x && didfind) { statusbar(_("This is the only occurrence")); } } else statusbar(_("No current search pattern")); openfile->placewewant = xplustabs(); edit_redraw(fileptr, pww_save); search_replace_abort(); }
/* Parse the linter requested for this syntax. */ void parse_linter(char *ptr) { assert(ptr != NULL); if (syntaxes == NULL) { rcfile_error( N_("Cannot add a linter without a syntax command")); return; } if (*ptr == '\0') { rcfile_error(N_("Missing linter command")); return; } free(endsyntax->linter); /* Let them unset the linter by using "". */ if (!strcmp(ptr, "\"\"")) endsyntax->linter = NULL; else endsyntax->linter = mallocstrcpy(NULL, ptr); }
/* Get a string of input at the statusbar prompt. This should only be * called from do_prompt(). */ functionptrtype get_prompt_string(int *actual, bool allow_tabs, #ifndef DISABLE_TABCOMP bool allow_files, bool *list, #endif const char *curranswer, #ifndef DISABLE_HISTORIES linestruct **history_list, #endif void (*refresh_func)(void)) { int kbinput = ERR; bool ran_func, finished; size_t curranswer_len; functionptrtype func; #ifndef DISABLE_TABCOMP bool tabbed = FALSE; /* Whether we've pressed Tab. */ #endif #ifndef DISABLE_HISTORIES char *history = NULL; /* The current history string. */ char *magichistory = NULL; /* The temporary string typed at the bottom of the history, if * any. */ #ifndef DISABLE_TABCOMP int last_kbinput = ERR; /* The key we pressed before the current key. */ size_t complete_len = 0; /* The length of the original string that we're trying to * tab complete, if any. */ #endif #endif /* !DISABLE_HISTORIES */ answer = mallocstrcpy(answer, curranswer); curranswer_len = strlen(answer); /* If reset_statusbar_x is TRUE, restore statusbar_x and * statusbar_pww to what they were before this prompt. Then, if * statusbar_x is uninitialized or past the end of curranswer, put * statusbar_x at the end of the string and update statusbar_pww * based on it. We do these things so that the cursor position * stays at the right place if a prompt-changing toggle is pressed, * or if this prompt was started from another prompt and we cancel * out of it. */ if (reset_statusbar_x) { statusbar_x = old_statusbar_x; statusbar_pww = old_pww; } if (statusbar_x == (size_t)-1 || statusbar_x > curranswer_len) { statusbar_x = curranswer_len; statusbar_pww = statusbar_xplustabs(); } #ifdef DEBUG fprintf(stderr, "get_prompt_string: answer = \"%s\", statusbar_x = %lu\n", answer, (unsigned long) statusbar_x); #endif update_statusbar_line(answer, statusbar_x); /* Refresh the edit window and the statusbar before getting * input. */ wnoutrefresh(edit); wnoutrefresh(bottomwin); /* If we're using restricted mode, we aren't allowed to change the * name of the current file once it has one, because that would * allow writing to files not specified on the command line. In * this case, disable all keys that would change the text if the * filename isn't blank and we're at the "Write File" prompt. */ while (TRUE) { kbinput = do_statusbar_input(&ran_func, &finished, refresh_func); assert(statusbar_x <= strlen(answer)); #ifndef NANO_TINY if (kbinput == KEY_WINCH) { refresh_func(); update_statusbar_line(answer, statusbar_x); continue; } #endif func = func_from_key(&kbinput); if (func == do_cancel || func == do_enter_void) break; #ifndef DISABLE_TABCOMP if (func != do_tab) tabbed = FALSE; if (func == do_tab) { #ifndef DISABLE_HISTORIES if (history_list != NULL) { if (last_kbinput != sc_seq_or(do_tab, NANO_CONTROL_I)) complete_len = strlen(answer); if (complete_len > 0) { answer = mallocstrcpy(answer, get_history_completion(history_list, answer, complete_len)); statusbar_x = strlen(answer); } } else #endif if (allow_tabs) answer = input_tab(answer, allow_files, &statusbar_x, &tabbed, refresh_func, list); update_statusbar_line(answer, statusbar_x); } else #endif /* !DISABLE_TABCOMP */ #ifndef DISABLE_HISTORIES if (func == get_history_older_void) { if (history_list != NULL) { /* If we're scrolling up at the bottom of the history list * and answer isn't blank, save answer in magichistory. */ if ((*history_list)->next == NULL && answer[0] != '\0') magichistory = mallocstrcpy(magichistory, answer); /* Get the older search from the history list and save it in * answer. If there is no older search, don't do anything. */ if ((history = get_history_older(history_list)) != NULL) { answer = mallocstrcpy(answer, history); statusbar_x = strlen(answer); } update_statusbar_line(answer, statusbar_x); /* This key has a shortcut-list entry when it's used to * move to an older search, which means that finished has * been set to TRUE. Set it back to FALSE here, so that * we aren't kicked out of the statusbar prompt. */ finished = FALSE; } } else if (func == get_history_newer_void) { if (history_list != NULL) { /* Get the newer search from the history list and save it in * answer. If there is no newer search, don't do anything. */ if ((history = get_history_newer(history_list)) != NULL) { answer = mallocstrcpy(answer, history); statusbar_x = strlen(answer); } /* If, after scrolling down, we're at the bottom of the * history list, answer is blank, and magichistory is set, * save magichistory in answer. */ if ((*history_list)->next == NULL && *answer == '\0' && magichistory != NULL) { answer = mallocstrcpy(answer, magichistory); statusbar_x = strlen(answer); } update_statusbar_line(answer, statusbar_x); /* This key has a shortcut-list entry when it's used to * move to a newer search, which means that finished has * been set to TRUE. Set it back to FALSE here, so that * we aren't kicked out of the statusbar prompt. */ finished = FALSE; } } else #endif /* !DISABLE_HISTORIES */ if (func == do_help_void) { update_statusbar_line(answer, statusbar_x); /* This key has a shortcut-list entry when it's used to go to * the help browser or display a message indicating that help * is disabled, which means that finished has been set to TRUE. * Set it back to FALSE here, so that we aren't kicked out of * the statusbar prompt. */ finished = FALSE; } /* If we have a shortcut with an associated function, break out if * we're finished after running or trying to run the function. */ if (finished) break; #if !defined(DISABLE_HISTORIES) && !defined(DISABLE_TABCOMP) last_kbinput = kbinput; #endif reset_statusbar_cursor(); wnoutrefresh(bottomwin); } #ifndef DISABLE_HISTORIES /* Set the current position in the history list to the bottom, * and free magichistory if we need to. */ if (history_list != NULL) { history_reset(*history_list); free(magichistory); } #endif /* We've finished putting in an answer or run a normal shortcut's * associated function, so reset statusbar_x and statusbar_pww. If * we've finished putting in an answer, reset the statusbar cursor * position too. */ if (func) { if (func == do_cancel || func == do_enter_void || ran_func) { statusbar_x = old_statusbar_x; statusbar_pww = old_pww; if (!ran_func) reset_statusbar_x = TRUE; /* Otherwise, we're still putting in an answer or a shortcut with * an associated function, so leave the statusbar cursor position * alone. */ } else reset_statusbar_x = FALSE; } *actual = kbinput; return func; }
/* Set up the system variables for a search or replace. If use_answer * is TRUE, only set backupstring to answer. Return -2 to run the * opposite program (search -> replace, replace -> search), return -1 if * the search should be canceled (due to Cancel, a blank search string, * Go to Line, or a failed regcomp()), return 0 on success, and return 1 * on rerun calling program. * * replacing is TRUE if we call from do_replace(), and FALSE if called * from do_search(). */ int search_init(bool replacing, bool use_answer) { int i = 0; char *buf; static char *backupstring = NULL; /* The search string we'll be using. */ /* If backupstring doesn't exist, initialize it to "". */ if (backupstring == NULL) backupstring = mallocstrcpy(NULL, ""); /* If use_answer is TRUE, set backupstring to answer and get out. */ if (use_answer) { backupstring = mallocstrcpy(backupstring, answer); return 0; } /* We display the search prompt below. If the user types a partial * search string and then Replace or a toggle, we will return to * do_search() or do_replace() and be called again. In that case, * we should put the same search string back up. */ focusing = TRUE; if (last_search[0] != '\0') { char *disp = display_string(last_search, 0, COLS / 3, FALSE); buf = charalloc(strlen(disp) + 7); /* We use (COLS / 3) here because we need to see more on the line. */ sprintf(buf, " [%s%s]", disp, (strlenpt(last_search) > COLS / 3) ? "..." : ""); free(disp); } else buf = mallocstrcpy(NULL, ""); /* This is now one simple call. It just does a lot. */ i = do_prompt(FALSE, #ifndef DISABLE_TABCOMP TRUE, #endif replacing ? MREPLACE : MWHEREIS, backupstring, #ifndef DISABLE_HISTORIES &search_history, #endif /* TRANSLATORS: This is the main search prompt. */ edit_refresh, "%s%s%s%s%s%s", _("Search"), #ifndef NANO_TINY /* TRANSLATORS: The next three strings are modifiers of the search prompt. */ ISSET(CASE_SENSITIVE) ? _(" [Case Sensitive]") : #endif "", #ifdef HAVE_REGEX_H ISSET(USE_REGEXP) ? _(" [Regexp]") : #endif "", #ifndef NANO_TINY ISSET(BACKWARDS_SEARCH) ? _(" [Backwards]") : #endif "", replacing ? #ifndef NANO_TINY /* TRANSLATORS: The next two strings are modifiers of the search prompt. */ openfile->mark_set ? _(" (to replace) in selection") : #endif _(" (to replace)") : "", buf); fflush(stderr); /* Release buf now that we don't need it anymore. */ free(buf); free(backupstring); backupstring = NULL; /* Cancel any search, or just return with no previous search. */ if (i == -1 || (i < 0 && *last_search == '\0') || (!replacing && i == 0 && *answer == '\0')) { statusbar(_("Cancelled")); return -1; } else { functionptrtype func = func_from_key(&i); if (i == -2 || i == 0 ) { #ifdef HAVE_REGEX_H /* Use last_search if answer is an empty string, or * answer if it isn't. */ if (ISSET(USE_REGEXP) && !regexp_init((i == -2) ? last_search : answer)) return -1; #endif ; #ifndef NANO_TINY } else if (func == case_sens_void) { TOGGLE(CASE_SENSITIVE); backupstring = mallocstrcpy(backupstring, answer); return 1; } else if (func == backwards_void) { TOGGLE(BACKWARDS_SEARCH); backupstring = mallocstrcpy(backupstring, answer); return 1; #endif #ifdef HAVE_REGEX_H } else if (func == regexp_void) { TOGGLE(USE_REGEXP); backupstring = mallocstrcpy(backupstring, answer); return 1; #endif } else if (func == do_replace || func == flip_replace_void) { backupstring = mallocstrcpy(backupstring, answer); return -2; /* Call the opposite search function. */ } else if (func == do_gotolinecolumn_void) { do_gotolinecolumn(openfile->current->lineno, openfile->placewewant + 1, TRUE, TRUE); /* Put answer up on the statusbar and * fall through. */ return 3; } else { return -1; } } return 0; }
/* Parse the headers (1st line) of the file which may influence the regex used. */ void parse_headers(char *ptr) { char *regstr; assert(ptr != NULL); if (syntaxes == NULL) { rcfile_error( N_("Cannot add a header regex without a syntax command")); return; } if (*ptr == '\0') { rcfile_error(N_("Missing regex string")); return; } /* Now for the fun part. Start adding regexes to individual strings * in the colorstrings array, woo! */ while (ptr != NULL && *ptr != '\0') { exttype *newheader; /* The new color structure. */ if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); ptr = parse_next_regex(ptr); continue; } ptr++; regstr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; newheader = (exttype *)nmalloc(sizeof(exttype)); /* Save the regex string if it's valid */ if (nregcomp(regstr, 0)) { newheader->ext_regex = mallocstrcpy(NULL, regstr); newheader->ext = NULL; newheader->next = NULL; #ifdef DEBUG fprintf(stderr, "Starting a new header entry: %s\n", newheader->ext_regex); #endif if (endheader == NULL) { endsyntax->headers = newheader; } else { endheader->next = newheader; } endheader = newheader; } else free(newheader); } }
/* Parse the rcfile, once it has been opened successfully at rcstream, * and close it afterwards. If syntax_only is TRUE, only allow the file * to contain color syntax commands: syntax, color, and icolor. */ void parse_rcfile(FILE *rcstream #ifdef ENABLE_COLOR , bool syntax_only #endif ) { char *buf = NULL; ssize_t len; size_t n; while ((len = getline(&buf, &n, rcstream)) > 0) { char *ptr, *keyword, *option; int set = 0; size_t i; /* Ignore the newline. */ if (buf[len - 1] == '\n') buf[len - 1] = '\0'; lineno++; ptr = buf; while (isblank(*ptr)) ptr++; /* If we have a blank line or a comment, skip to the next * line. */ if (*ptr == '\0' || *ptr == '#') continue; /* Otherwise, skip to the next space. */ keyword = ptr; ptr = parse_next_word(ptr); /* Try to parse the keyword. */ if (strcasecmp(keyword, "set") == 0) { #ifdef ENABLE_COLOR if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else #endif set = 1; } else if (strcasecmp(keyword, "unset") == 0) { #ifdef ENABLE_COLOR if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else #endif set = -1; } #ifdef ENABLE_COLOR else if (strcasecmp(keyword, "include") == 0) { if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else parse_include(ptr); } else if (strcasecmp(keyword, "syntax") == 0) { if (endsyntax != NULL && endcolor == NULL) rcfile_error(N_("Syntax \"%s\" has no color commands"), endsyntax->desc); parse_syntax(ptr); } else if (strcasecmp(keyword, "header") == 0) parse_headers(ptr); else if (strcasecmp(keyword, "color") == 0) parse_colors(ptr, FALSE); else if (strcasecmp(keyword, "icolor") == 0) parse_colors(ptr, TRUE); else if (strcasecmp(keyword, "bind") == 0) parse_keybinding(ptr); #endif /* ENABLE_COLOR */ else rcfile_error(N_("Command \"%s\" not understood"), keyword); if (set == 0) continue; if (*ptr == '\0') { rcfile_error(N_("Missing flag")); continue; } option = ptr; ptr = parse_next_word(ptr); for (i = 0; rcopts[i].name != NULL; i++) { if (strcasecmp(option, rcopts[i].name) == 0) { #ifdef DEBUG fprintf(stderr, "parse_rcfile(): name = \"%s\"\n", rcopts[i].name); #endif if (set == 1) { if (rcopts[i].flag != 0) /* This option has a flag, so it doesn't take an * argument. */ SET(rcopts[i].flag); else { /* This option doesn't have a flag, so it takes * an argument. */ if (*ptr == '\0') { rcfile_error( N_("Option \"%s\" requires an argument"), rcopts[i].name); break; } option = ptr; if (*option == '"') option++; ptr = parse_argument(ptr); option = mallocstrcpy(NULL, option); #ifdef DEBUG fprintf(stderr, "option = \"%s\"\n", option); #endif /* Make sure option is a valid multibyte * string. */ if (!is_valid_mbstring(option)) { rcfile_error( N_("Option is not a valid multibyte string")); break; } #ifndef DISABLE_OPERATINGDIR if (strcasecmp(rcopts[i].name, "operatingdir") == 0) operating_dir = option; else #endif #ifndef DISABLE_WRAPJUSTIFY if (strcasecmp(rcopts[i].name, "fill") == 0) { if (!parse_num(option, &wrap_at)) { rcfile_error( N_("Requested fill size \"%s\" is invalid"), option); wrap_at = -CHARS_FROM_EOL; } else free(option); } else #endif #ifndef NANO_TINY if (strcasecmp(rcopts[i].name, "matchbrackets") == 0) { matchbrackets = option; if (has_blank_mbchars(matchbrackets)) { rcfile_error( N_("Non-blank characters required")); free(matchbrackets); matchbrackets = NULL; } } else if (strcasecmp(rcopts[i].name, "whitespace") == 0) { whitespace = option; if (mbstrlen(whitespace) != 2 || strlenpt(whitespace) != 2) { rcfile_error( N_("Two single-column characters required")); free(whitespace); whitespace = NULL; } else { whitespace_len[0] = parse_mbchar(whitespace, NULL, NULL); whitespace_len[1] = parse_mbchar(whitespace + whitespace_len[0], NULL, NULL); } } else #endif #ifndef DISABLE_JUSTIFY if (strcasecmp(rcopts[i].name, "punct") == 0) { punct = option; if (has_blank_mbchars(punct)) { rcfile_error( N_("Non-blank characters required")); free(punct); punct = NULL; } } else if (strcasecmp(rcopts[i].name, "brackets") == 0) { brackets = option; if (has_blank_mbchars(brackets)) { rcfile_error( N_("Non-blank characters required")); free(brackets); brackets = NULL; } } else if (strcasecmp(rcopts[i].name, "quotestr") == 0) quotestr = option; else #endif #ifndef NANO_TINY if (strcasecmp(rcopts[i].name, "backupdir") == 0) backup_dir = option; else #endif #ifndef DISABLE_SPELLER if (strcasecmp(rcopts[i].name, "speller") == 0) alt_speller = option; else #endif if (strcasecmp(rcopts[i].name, "tabsize") == 0) { if (!parse_num(option, &tabsize) || tabsize <= 0) { rcfile_error( N_("Requested tab size \"%s\" is invalid"), option); tabsize = -1; } else free(option); } else assert(FALSE); } #ifdef DEBUG fprintf(stderr, "flag = %ld\n", rcopts[i].flag); #endif } else if (rcopts[i].flag != 0) UNSET(rcopts[i].flag); else rcfile_error(N_("Cannot unset flag \"%s\""), rcopts[i].name); break; } } if (rcopts[i].name == NULL) rcfile_error(N_("Unknown flag \"%s\""), option); } #ifdef ENABLE_COLOR if (endsyntax != NULL && endcolor == NULL) rcfile_error(N_("Syntax \"%s\" has no color commands"), endsyntax->desc); #endif free(buf); fclose(rcstream); lineno = 0; check_vitals_mapped(); return; }
void parse_keybinding(char *ptr) { char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL; sc *s, *newsc; int i, menu; assert(ptr != NULL); if (*ptr == '\0') { rcfile_error(N_("Missing key name")); return; } keyptr = ptr; ptr = parse_next_word(ptr); keycopy = mallocstrcpy(NULL, keyptr); for (i = 0; i < strlen(keycopy); i++) keycopy[i] = toupper(keycopy[i]); if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') { rcfile_error( N_("keybindings must begin with \"^\", \"M\", or \"F\"")); return; } funcptr = ptr; ptr = parse_next_word(ptr); if (!strcmp(funcptr, "")) { rcfile_error( N_("Must specify function to bind key to")); return; } menuptr = ptr; ptr = parse_next_word(ptr); if (!strcmp(menuptr, "")) { rcfile_error( /* Note to translators, do not translate the word "all" in the sentence below, everything else is fine */ N_("Must specify menu to bind key to (or \"all\")")); return; } newsc = strtosc(menu, funcptr); if (newsc == NULL) { rcfile_error( N_("Could not map name \"%s\" to a function"), funcptr); return; } menu = strtomenu(menuptr); if (menu < 1) { rcfile_error( N_("Could not map name \"%s\" to a menu"), menuptr); return; } #ifdef DEBUG fprintf(stderr, "newsc now address %d, menu func assigned = %d, menu = %d\n", &newsc, newsc->scfunc, menu); #endif newsc->keystr = keycopy; newsc->menu = menu; newsc->type = strtokeytype(newsc->keystr); assign_keyinfo(newsc); #ifdef DEBUG fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr); fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq); #endif if (check_bad_binding(newsc)) { rcfile_error( N_("Sorry, keystr \"%s\" is an illegal binding"), newsc->keystr); return; } /* now let's have some fun. Try and delete the other entries we found for the same menu, then make this new new beginning */ for (s = sclist; s != NULL; s = s->next) { if (((s->menu & newsc->menu)) && s->seq == newsc->seq) { s->menu &= ~newsc->menu; #ifdef DEBUG fprintf(stderr, "replaced menu entry %d\n", s->menu); #endif } } newsc->next = sclist; sclist = newsc; }
/* Parse the color string in the line at ptr, and add it to the current * file's associated colors. If icase is TRUE, treat the color string * as case insensitive. */ void parse_colors(char *ptr, bool icase) { short fg, bg; bool bright = FALSE, no_fgcolor = FALSE; char *fgstr; assert(ptr != NULL); if (syntaxes == NULL) { rcfile_error( N_("Cannot add a color command without a syntax command")); return; } if (*ptr == '\0') { rcfile_error(N_("Missing color name")); return; } fgstr = ptr; ptr = parse_next_word(ptr); if (strchr(fgstr, ',') != NULL) { char *bgcolorname; strtok(fgstr, ","); bgcolorname = strtok(NULL, ","); if (bgcolorname == NULL) { /* If we have a background color without a foreground color, * parse it properly. */ bgcolorname = fgstr + 1; no_fgcolor = TRUE; } if (strncasecmp(bgcolorname, "bright", 6) == 0) { rcfile_error( N_("Background color \"%s\" cannot be bright"), bgcolorname); return; } bg = color_to_short(bgcolorname, &bright); } else bg = -1; if (!no_fgcolor) { fg = color_to_short(fgstr, &bright); /* Don't try to parse screwed-up foreground colors. */ if (fg == -1) return; } else fg = -1; if (*ptr == '\0') { rcfile_error(N_("Missing regex string")); return; } /* Now for the fun part. Start adding regexes to individual strings * in the colorstrings array, woo! */ while (ptr != NULL && *ptr != '\0') { colortype *newcolor; /* The new color structure. */ bool cancelled = FALSE; /* The start expression was bad. */ bool expectend = FALSE; /* Do we expect an end= line? */ if (strncasecmp(ptr, "start=", 6) == 0) { ptr += 6; expectend = TRUE; } if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); ptr = parse_next_regex(ptr); continue; } ptr++; fgstr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; newcolor = (colortype *)nmalloc(sizeof(colortype)); /* Save the starting regex string if it's valid, and set up the * color information. */ if (nregcomp(fgstr, icase ? REG_ICASE : 0)) { newcolor->fg = fg; newcolor->bg = bg; newcolor->bright = bright; newcolor->icase = icase; newcolor->start_regex = mallocstrcpy(NULL, fgstr); newcolor->start = NULL; newcolor->end_regex = NULL; newcolor->end = NULL; newcolor->next = NULL; if (endcolor == NULL) { endsyntax->color = newcolor; #ifdef DEBUG fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg); #endif } else { #ifdef DEBUG fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg); #endif endcolor->next = newcolor; } endcolor = newcolor; } else { free(newcolor); cancelled = TRUE; } if (expectend) { if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) { rcfile_error( N_("\"start=\" requires a corresponding \"end=\"")); return; } ptr += 4; if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); continue; } ptr++; fgstr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; /* If the start regex was invalid, skip past the end regex to * stay in sync. */ if (cancelled) continue; /* Save the ending regex string if it's valid. */ newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE : 0)) ? mallocstrcpy(NULL, fgstr) : NULL; /* Lame way to skip another static counter */ newcolor->id = endsyntax->nmultis; endsyntax->nmultis++; } } }
/* 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 }
/* Parse the next syntax string from the line at ptr, and add it to the * global list of color syntaxes. */ void parse_syntax(char *ptr) { const char *fileregptr = NULL, *nameptr = NULL; syntaxtype *tmpsyntax; exttype *endext = NULL; /* The end of the extensions list for this syntax. */ assert(ptr != NULL); if (*ptr == '\0') { rcfile_error(N_("Missing syntax name")); return; } if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); return; } ptr++; nameptr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) return; /* Search for a duplicate syntax name. If we find one, free it, so * that we always use the last syntax with a given name. */ for (tmpsyntax = syntaxes; tmpsyntax != NULL; tmpsyntax = tmpsyntax->next) { if (strcmp(nameptr, tmpsyntax->desc) == 0) { syntaxtype *prev_syntax = tmpsyntax; tmpsyntax = tmpsyntax->next; free(prev_syntax); break; } } if (syntaxes == NULL) { syntaxes = (syntaxtype *)nmalloc(sizeof(syntaxtype)); endsyntax = syntaxes; } else { endsyntax->next = (syntaxtype *)nmalloc(sizeof(syntaxtype)); endsyntax = endsyntax->next; #ifdef DEBUG fprintf(stderr, "Adding new syntax after first one\n"); #endif } endsyntax->desc = mallocstrcpy(NULL, nameptr); endsyntax->color = NULL; endcolor = NULL; endheader = NULL; endsyntax->extensions = NULL; endsyntax->headers = NULL; endsyntax->next = NULL; endsyntax->nmultis = 0; #ifdef DEBUG fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr); #endif /* The "none" syntax is the same as not having a syntax at all, so * we can't assign any extensions or colors to it. */ if (strcmp(endsyntax->desc, "none") == 0) { rcfile_error(N_("The \"none\" syntax is reserved")); return; } /* The default syntax should have no associated extensions. */ if (strcmp(endsyntax->desc, "default") == 0 && *ptr != '\0') { rcfile_error( N_("The \"default\" syntax must take no extensions")); return; } /* Now load the extensions into their part of the struct. */ while (*ptr != '\0') { exttype *newext; /* The new extension structure. */ while (*ptr != '"' && *ptr != '\0') ptr++; if (*ptr == '\0') return; ptr++; fileregptr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; newext = (exttype *)nmalloc(sizeof(exttype)); /* Save the extension regex if it's valid. */ if (nregcomp(fileregptr, REG_NOSUB)) { newext->ext_regex = mallocstrcpy(NULL, fileregptr); newext->ext = NULL; if (endext == NULL) endsyntax->extensions = newext; else endext->next = newext; endext = newext; endext->next = NULL; } else free(newext); } }
/* Set width to the number of files that we can display per line, if * necessary, and display the list of files. */ void browser_refresh(void) { size_t i; int line = 0, col = 0; /* The current line and column while the list is getting displayed. */ char *foo; /* The additional information that we'll display about a file. */ /* Perhaps window dimensions have changed; reinitialize the browser. */ browser_init(path_save, opendir(path_save)); qsort(filelist, filelist_len, sizeof(char *), diralphasort); /* Make sure the selected file is within range. */ if (selected >= filelist_len) selected = filelist_len - 1; titlebar(path_save); blank_edit(); wmove(edit, 0, 0); i = width * editwinrows * ((selected / width) / editwinrows); for (; i < filelist_len && line < editwinrows; i++) { struct stat st; const char *filetail = tail(filelist[i]); /* The filename we display, minus the path. */ size_t filetaillen = strlenpt(filetail); /* The length of the filename in columns. */ size_t foolen; /* The length of the file information in columns. */ int foomaxlen = 7; /* The maximum length of the file information in * columns: seven for "--", "(dir)", or the file size, * and 12 for "(parent dir)". */ bool dots = (COLS >= 15 && filetaillen >= longest - foomaxlen); /* Do we put an ellipsis before the filename? Don't set * this to TRUE if we have fewer than 15 columns (i.e. * one column for padding, plus seven columns for a * filename other than ".."). */ char *disp = display_string(filetail, dots ? filetaillen - longest + foomaxlen + 4 : 0, longest, FALSE); /* If we put an ellipsis before the filename, reserve * one column for padding, plus seven columns for "--", * "(dir)", or the file size, plus three columns for the * ellipsis. */ /* Start highlighting the currently selected file or directory. */ if (i == selected) wattron(edit, hilite_attribute); blank_line(edit, line, col, longest); /* If dots is TRUE, we will display something like "...ename". */ if (dots) mvwaddstr(edit, line, col, "..."); mvwaddstr(edit, line, dots ? col + 3 : col, disp); free(disp); col += longest; /* Show information about the file. We don't want to report * file sizes for links, so we use lstat(). */ if (lstat(filelist[i], &st) == -1 || S_ISLNK(st.st_mode)) { /* If the file doesn't exist (i.e. it's been deleted while * the file browser is open), or it's a symlink that doesn't * point to a directory, display "--". */ if (stat(filelist[i], &st) == -1 || !S_ISDIR(st.st_mode)) foo = mallocstrcpy(NULL, "--"); /* If the file is a symlink that points to a directory, * display it as a directory. */ else /* TRANSLATORS: Try to keep this at most 7 characters. */ foo = mallocstrcpy(NULL, _("(dir)")); } else if (S_ISDIR(st.st_mode)) { /* If the file is a directory, display it as such. */ if (strcmp(filetail, "..") == 0) { /* TRANSLATORS: Try to keep this at most 12 characters. */ foo = mallocstrcpy(NULL, _("(parent dir)")); foomaxlen = 12; } else foo = mallocstrcpy(NULL, _("(dir)")); } else { unsigned long result = st.st_size; char modifier; foo = charalloc(foomaxlen + 1); if (st.st_size < (1 << 10)) modifier = ' '; /* bytes */ else if (st.st_size < (1 << 20)) { result >>= 10; modifier = 'K'; /* kilobytes */ } else if (st.st_size < (1 << 30)) { result >>= 20; modifier = 'M'; /* megabytes */ } else {
/* 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; }
/* Let user unbind a sequence from a given (or all) menus */ void parse_unbinding(char *ptr) { char *keyptr = NULL, *keycopy = NULL, *menuptr = NULL; sc *s; int i, menu; assert(ptr != NULL); if (*ptr == '\0') { rcfile_error(N_("Missing key name")); return; } keyptr = ptr; ptr = parse_next_word(ptr); keycopy = mallocstrcpy(NULL, keyptr); for (i = 0; i < strlen(keycopy); i++) keycopy[i] = toupper(keycopy[i]); #ifdef DEBUG fprintf(stderr, "Starting unbinding code"); #endif if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') { rcfile_error( N_("keybindings must begin with \"^\", \"M\", or \"F\"")); return; } menuptr = ptr; ptr = parse_next_word(ptr); if (!strcmp(menuptr, "")) { rcfile_error( /* Note to translators, do not translate the word "all" in the sentence below, everything else is fine */ N_("Must specify menu to bind key to (or \"all\")")); return; } menu = strtomenu(menuptr); if (menu < 1) { rcfile_error( N_("Could not map name \"%s\" to a menu"), menuptr); return; } #ifdef DEBUG fprintf(stderr, "unbinding \"%s\" from menu = %d\n", keycopy, menu); #endif /* Now find the apropriate entries in the menu to delete */ for (s = sclist; s != NULL; s = s->next) { if (((s->menu & menu)) && !strcmp(s->keystr,keycopy)) { s->menu &= ~menu; #ifdef DEBUG fprintf(stderr, "deleted menu entry %d\n", s->menu); #endif } } }
/* Go to the specified line and column, or ask for them if interactive * is TRUE. Save the x-coordinate and y-coordinate if save_pos is TRUE. * Update the screen afterwards if allow_update is TRUE. Note that both * the line and column numbers should be one-based. */ void do_gotolinecolumn(ssize_t line, ssize_t column, bool use_answer, bool interactive, bool save_pos, bool allow_update) { if (interactive) { char *ans = mallocstrcpy(NULL, answer); functionptrtype func; /* Ask for the line and column. */ int i = do_prompt(FALSE, #ifndef DISABLE_TABCOMP TRUE, #endif MGOTOLINE, use_answer ? ans : "", #ifndef DISABLE_HISTORIES NULL, #endif /* TRANSLATORS: This is a prompt. */ edit_refresh, _("Enter line number, column number")); free(ans); /* Cancel, or Enter with blank string. */ if (i < 0) { statusbar(_("Cancelled")); display_main_list(); return; } func = func_from_key(&i); if (func == gototext_void) { /* Keep answer up on the statusbar. */ search_init(TRUE, TRUE); do_search(); return; } /* Do a bounds check. Display a warning on an out-of-bounds * line or column number only if we hit Enter at the statusbar * prompt. */ if (!parse_line_column(answer, &line, &column) || line < 1 || column < 1) { if (i == 0) statusbar(_("Invalid line or column number")); display_main_list(); return; } } else { if (line < 1) line = openfile->current->lineno; if (column < 1) column = openfile->placewewant + 1; } for (openfile->current = openfile->fileage; openfile->current != openfile->filebot && line > 1; line--) openfile->current = openfile->current->next; openfile->current_x = actual_x(openfile->current->data, column - 1); openfile->placewewant = column - 1; /* Put the top line of the edit window in range of the current line. * If save_pos is TRUE, don't change the cursor position when doing * it. */ edit_update(save_pos ? NONE : CENTER); /* If allow_update is TRUE, update the screen. */ if (allow_update) { edit_refresh(); display_main_list(); } }
/* Replace a string. */ void do_replace(void) { linestruct *edittop_save, *begin; size_t begin_x, pww_save; ssize_t numreplaced; int i; if (ISSET(VIEW_MODE)) { print_view_warning(); search_replace_abort(); return; } i = search_init(TRUE, FALSE); if (i == -1) { /* Cancel, Go to Line, blank search string, or regcomp() failed. */ search_replace_abort(); return; } else if (i == -2) { /* No Replace. */ do_search(); return; } else if (i == 1) /* Case Sensitive, Backwards, or Regexp search toggle. */ do_replace(); if (i != 0) return; /* If answer is not "", add answer to the search history list and * copy answer into last_search. */ if (answer[0] != '\0') { #ifndef DISABLE_HISTORIES update_history(&search_history, answer); #endif last_search = mallocstrcpy(last_search, answer); } last_replace = mallocstrcpy(last_replace, ""); i = do_prompt(FALSE, #ifndef DISABLE_TABCOMP TRUE, #endif MREPLACEWITH, last_replace, #ifndef DISABLE_HISTORIES &replace_history, #endif /* TRANSLATORS: This is a prompt. */ edit_refresh, _("Replace with")); #ifndef DISABLE_HISTORIES /* If the replace string is not "", add it to the replace history list. */ if (i == 0) update_history(&replace_history, answer); #endif if (i != 0 && i != -2) { if (i == -1) { /* Cancel. */ if (last_replace[0] != '\0') answer = mallocstrcpy(answer, last_replace); statusbar(_("Cancelled")); } search_replace_abort(); return; } last_replace = mallocstrcpy(last_replace, answer); /* Save where we are. */ edittop_save = openfile->edittop; begin = openfile->current; begin_x = openfile->current_x; pww_save = openfile->placewewant; numreplaced = do_replace_loop( #ifndef DISABLE_SPELLER FALSE, #endif NULL, begin, &begin_x, last_search); /* Restore where we were. */ openfile->edittop = edittop_save; openfile->current = begin; openfile->current_x = begin_x; openfile->placewewant = pww_save; edit_refresh(); if (numreplaced >= 0) statusbar(P_("Replaced %lu occurrence", "Replaced %lu occurrences", (unsigned long)numreplaced), (unsigned long)numreplaced); search_replace_abort(); }