Beispiel #1
0
/* Look for needle, starting at (current, current_x).  begin is the line
 * where we first started searching, at column begin_x.  The return
 * value specifies whether we found anything.  If we did, set needle_len
 * to the length of the string we found if it isn't NULL. */
bool findnextstr(
#ifndef DISABLE_SPELLER
	bool whole_word_only,
#endif
	const filestruct *begin, size_t begin_x,
	const char *needle, size_t *needle_len)
{
    size_t found_len;
	/* The length of the match we find. */
    size_t current_x_find = 0;
	/* The location in the current line of the match we find. */
    ssize_t current_y_find = openfile->current_y;
    filestruct *fileptr = openfile->current;
    const char *rev_start = fileptr->data, *found = NULL;
    time_t lastkbcheck = time(NULL);

    /* rev_start might end up 1 character before the start or after the
     * end of the line.  This won't be a problem because strstrwrapper()
     * will return immediately and say that no match was found, and
     * rev_start will be properly set when the search continues on the
     * previous or next line. */
    rev_start +=
#ifndef NANO_TINY
	ISSET(BACKWARDS_SEARCH) ?
	((openfile->current_x == 0) ? -1 : move_mbleft(fileptr->data, openfile->current_x)) :
#endif
	move_mbright(fileptr->data, openfile->current_x);

    /* Look for needle in the current line we're searching. */
    enable_nodelay();
    while (TRUE) {
	if (time(NULL) - lastkbcheck > 1) {
	    int input = parse_kbinput(edit);

	    lastkbcheck = time(NULL);

	    if (input && func_from_key(&input) == do_cancel) {
		statusbar(_("Cancelled"));
		return FALSE;
	    }
	}

	found = strstrwrapper(fileptr->data, needle, rev_start);

	/* We've found a potential match. */
	if (found != NULL) {
#ifndef DISABLE_SPELLER
	    bool found_whole = FALSE;
		/* Is this potential match a whole word? */
#endif

	    /* Set found_len to the length of the potential match. */
	    found_len =
#ifdef HAVE_REGEX_H
		ISSET(USE_REGEXP) ?
		regmatches[0].rm_eo - regmatches[0].rm_so :
#endif
		strlen(needle);

#ifndef DISABLE_SPELLER
	    /* If we're searching for whole words, see if this potential
	     * match is a whole word. */
	    if (whole_word_only) {
		char *word = mallocstrncpy(NULL, found, found_len + 1);
		word[found_len] = '\0';

		found_whole = is_whole_word(found - fileptr->data,
			fileptr->data, word);
		free(word);
	    }

	    /* If we're searching for whole words and this potential
	     * match isn't a whole word, continue searching. */
	    if (!whole_word_only || found_whole)
#endif
		break;
	}

	if (search_last_line) {
	    /* We've finished processing the file, so get out. */
	    not_found_msg(needle);
	    disable_nodelay();
	    return FALSE;
	}

	/* Move to the previous or next line in the file. */
#ifndef NANO_TINY
	if (ISSET(BACKWARDS_SEARCH)) {
	    fileptr = fileptr->prev;
	    current_y_find--;
	} else {
#endif
	    fileptr = fileptr->next;
	    current_y_find++;
#ifndef NANO_TINY
	}
#endif

	if (fileptr == NULL) {
	    /* We've reached the start or end of the buffer, so wrap around. */
#ifndef NANO_TINY
	    if (ISSET(BACKWARDS_SEARCH)) {
		fileptr = openfile->filebot;
		current_y_find = editwinrows - 1;
	    } else {
#endif
		fileptr = openfile->fileage;
		current_y_find = 0;
#ifndef NANO_TINY
	    }
#endif
	    statusbar(_("Search Wrapped"));
	}

	if (fileptr == begin)
	    /* We've reached the original starting line. */
	    search_last_line = TRUE;

	rev_start = fileptr->data;
#ifndef NANO_TINY
	if (ISSET(BACKWARDS_SEARCH))
	    rev_start += strlen(fileptr->data);
#endif
    }

    /* We found an instance. */
    current_x_find = found - fileptr->data;

    /* Ensure we haven't wrapped around again! */
    if (search_last_line &&
#ifndef NANO_TINY
	((!ISSET(BACKWARDS_SEARCH) && current_x_find > begin_x) ||
	(ISSET(BACKWARDS_SEARCH) && current_x_find < begin_x))
#else
	current_x_find > begin_x
#endif
	) {
	not_found_msg(needle);
	disable_nodelay();
	return FALSE;
    }

    disable_nodelay();
    /* We've definitely found something. */
    openfile->current = fileptr;
    openfile->current_x = current_x_find;
    openfile->placewewant = xplustabs();
    openfile->current_y = current_y_find;

    /* needle_len holds the length of needle. */
    if (needle_len != NULL)
	*needle_len = found_len;

    return TRUE;
}
Beispiel #2
0
/* 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();
    }
}
Beispiel #3
0
/* 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;
}
Beispiel #4
0
/* Our main file browser function.  path is the tilde-expanded path we
 * start browsing from. */
std::string do_browser(std::string path, DIR *dir)
{
	std::string retval;
	bool old_const_update = ISSET(CONST_UPDATE);
	std::string prev_dir;
	/* The directory we were in before backing up to "..". */
	std::string ans;
	/* The last answer the user typed at the statusbar prompt. */
	size_t old_selected;
	/* The selected file we had before the current selected file. */

	curs_set(0);
	blank_statusbar();
	bottombars(MBROWSER);
	wnoutrefresh(bottomwin);

	UNSET(CONST_UPDATE);

change_browser_directory:
	/* We go here after we select a new directory. */

	path = get_full_path(path);

	assert(path.length() > 0 && path.back() == '/');

	/* Get the file list, and set longest and width in the process. */
	browser_init(path, dir);

	/* Sort the file list. */
	std::sort(filelist.begin(), filelist.end(), sort_directories);

	/* If prev_dir isn't empty, select the directory saved in it, and then blow it away. */
	if (prev_dir != "") {
		browser_select_filename(prev_dir);

		prev_dir = "";
		/* Otherwise, select the first file or directory in the list. */
	} else {
		selected = 0;
	}

	old_selected = (size_t)-1;

	titlebar(path);

	Key *kbinput = nullptr;

	while (true) {
		struct stat st;
		size_t fileline = selected / width;
		/* The line number the selected file is on. */
		std::string new_path;
		/* The path we switch to at the "Go to Directory" prompt. */

		if (kbinput) {
			delete kbinput;
			kbinput = nullptr;
		}

		/* 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 (old_selected != selected) {
			browser_refresh();
		}

		old_selected = selected;

		// Deal with the keyboard input
		kbinput = new Key(get_kbinput(edit));

		auto func = func_from_key(*kbinput);

		if (func == total_refresh) {
			total_redraw();
		} else if (func == do_help_void) {
			do_help_void();
			curs_set(0);
			/* Search for a filename. */
		} else if (func == do_search) {
			curs_set(1);
			do_filesearch();
			curs_set(0);
			/* Search for another filename. */
		} else if (func == do_research) {
			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.size() - 1) {
				selected = filelist.size() - 1;
			}
		} else if (func == do_first_file) {
			selected = 0;
		} else if (func == do_last_file) {
			selected = filelist.size() - 1;
		} else if (func == goto_dir_void) {
			/* Go to a specific directory. */
			curs_set(1);

			std::shared_ptr<Key> key;
			PromptResult i = do_prompt(true,
			              false,
			              MGOTODIR, key, ans.c_str(),
			              NULL,
			              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 == PROMPT_ABORTED || i == PROMPT_BLANK_STRING || answer.front() == '\n') {
				/* We canceled.  Indicate that on the statusbar, and
				 * blank out ans, since we're done with it. */
				statusbar(_("Cancelled"));
				ans = "";
				func = nullptr;
				continue;
			} else if (i != PROMPT_ENTER_PRESSED) {
				/* 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. */
				ans = answer;
				func = goto_dir_void;
				continue;
			}

			/* We have a directory.  Blank out ans, since we're done with it. */
			ans = "";

			/* Convert newlines to nulls, just before we go to the directory. */
			sunder(answer);

			new_path = real_dir_from_tilde(answer);

			if (new_path == "") {
				new_path = path + answer;
			}

			dir = opendir(new_path);
			if (dir == NULL) {
				/* We can't open this directory for some reason. Complain. */
				statusbar(_("Error reading %s: %s"), answer.c_str(), strerror(errno));
				beep();
				func = nullptr;
				continue;
			}

			/* Start over again with the new path value. */
			path = new_path;
			goto change_browser_directory;
		} else if (func == do_up_void) {
			if (selected >= width) {
				selected -= width;
			}
		} else if (func == do_left) {
			if (selected > 0) {
				selected--;
			}
		} else if (func == do_down_void) {
			if (selected + width <= filelist.size() - 1) {
				selected += width;
			}
		} else if (func == do_right) {
			if (selected < filelist.size() - 1) {
				selected++;
			}
		} else if (func == do_enter_void) {
			/* We can't move up from "/". */
			if (filelist[selected] == "/..") {
				statusbar(_("Can't move up a directory"));
				beep();
				func = nullptr;
				continue;
			}

			if (stat(filelist[selected], &st) == -1) {
				/* We can't open this file for some reason. Complain. */
				statusbar(_("Error reading %s: %s"), filelist[selected].c_str(), strerror(errno));
				beep();
				func = nullptr;
				continue;
			}

			if (!S_ISDIR(st.st_mode)) {
				/* We've successfully opened a file, we're done, so get out. */
				retval = filelist[selected];
				func = nullptr;
				break;
			} else if (tail(filelist[selected]) == "..") {
				/* 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 = striponedir(filelist[selected]);
			}

			dir = opendir(filelist[selected].c_str());
			if (dir == NULL) {
				/* We can't open this directory for some reason. Complain. */
				statusbar(_("Error reading %s: %s"), filelist[selected].c_str(), strerror(errno));
				beep();
				func = nullptr;
				continue;
			}

			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;
		}
		func = nullptr;
	}
	if (kbinput) {
		delete kbinput;
	}
	titlebar(NULL);
	edit_refresh();
	curs_set(1);
	if (old_const_update) {
		SET(CONST_UPDATE);
	}

	filelist.clear();

	return retval;
}