bool history_search_t::go_backwards() { /* Backwards means increasing our index */ const size_t max_idx = (size_t)(-1); size_t idx = 0; if (! prev_matches.empty()) idx = prev_matches.back().first; if (idx == max_idx) return false; while (++idx < max_idx) { const history_item_t item = history->item_at_index(idx); /* We're done if it's empty */ if (item.empty()) { return false; } /* Look for a term that matches and that we haven't seen before */ const wcstring &str = item.str(); if (item.matches_search(term, search_type) && ! match_already_made(str) && ! should_skip_match(str)) { prev_matches.push_back(prev_match_t(idx, item)); return true; } } return false; }
void history_t::get_string_representation(wcstring &result, const wcstring &separator) { scoped_lock locker(lock); bool first = true; /* Append new items */ for (std::vector<history_item_t>::const_reverse_iterator iter=new_items.rbegin(); iter < new_items.rend(); ++iter) { if (! first) result.append(separator); result.append(iter->str()); first = false; } /* Append old items */ load_old_if_needed(); for (std::deque<size_t>::const_reverse_iterator iter = old_item_offsets.rbegin(); iter != old_item_offsets.rend(); ++iter) { size_t offset = *iter; const history_item_t item = history_t::decode_item(mmap_start + offset, mmap_length - offset); if (! first) result.append(separator); result.append(item.str()); first = false; } }
/* Function to add a history item */ void add_item(const history_item_t &item) { /* Skip empty items */ if (item.empty()) return; /* See if it's in the cache. If it is, update the timestamp. If not, we create a new node and add it. Note that calling get_node promotes the node to the front. */ history_lru_node_t *node = this->get_node(item.str()); if (node != NULL) { node->timestamp = std::max(node->timestamp, item.timestamp()); /* What to do about paths here? Let's just ignore them */ } else { node = new history_lru_node_t(item); this->add_node(node); } }
history_lru_node_t(const history_item_t &item) : lru_node_t(item.str()), timestamp(item.timestamp()), required_paths(item.required_paths) {}
/** Save the specified mode to file */ void history_t::save_internal() { /* This must be called while locked */ ASSERT_IS_LOCKED(lock); /* Nothing to do if there's no new items */ if (new_items.empty() && deleted_items.empty()) return; /* Compact our new items so we don't have duplicates */ this->compact_new_items(); bool ok = true; wcstring tmp_name = history_filename(name, L".tmp"); if( ! tmp_name.empty() ) { /* Make an LRU cache to save only the last N elements */ history_lru_cache_t lru(HISTORY_SAVE_MAX); /* Insert old items in, from old to new. Merge them with our new items, inserting items with earlier timestamps first. */ std::vector<history_item_t>::const_iterator new_item_iter = new_items.begin(); /* Map in existing items (which may have changed out from underneath us, so don't trust our old mmap'd data) */ const char *local_mmap_start = NULL; size_t local_mmap_size = 0; if (map_file(name, &local_mmap_start, &local_mmap_size)) { size_t cursor = 0; for (;;) { size_t offset = offset_of_next_item(local_mmap_start, local_mmap_size, &cursor, 0); /* If we get back -1, we're done */ if (offset == (size_t)(-1)) break; /* Try decoding an old item */ const history_item_t old_item = history_t::decode_item(local_mmap_start + offset, local_mmap_size - offset); if (old_item.empty() || is_deleted(old_item)) { // debug(0, L"Item is deleted : %s\n", old_item.str().c_str()); continue; } /* The old item may actually be more recent than our new item, if it came from another session. Insert all new items at the given index with an earlier timestamp. */ for (; new_item_iter != new_items.end(); ++new_item_iter) { if (new_item_iter->timestamp() < old_item.timestamp()) { /* This "new item" is in fact older. */ lru.add_item(*new_item_iter); } else { /* The new item is not older. */ break; } } /* Now add this old item */ lru.add_item(old_item); } munmap((void *)local_mmap_start, local_mmap_size); } /* Insert any remaining new items */ for (; new_item_iter != new_items.end(); ++new_item_iter) { lru.add_item(*new_item_iter); } signal_block(); FILE *out; if( (out=wfopen( tmp_name, "w" ) ) ) { /* Write them out */ for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter) { const history_lru_node_t *node = *iter; if (! node->write_yaml_to_file(out)) { ok = false; break; } } if( fclose( out ) || !ok ) { /* This message does not have high enough priority to be shown by default. */ debug( 2, L"Error when writing history file" ); } else { wcstring new_name = history_filename(name, wcstring()); wrename(tmp_name, new_name); } } signal_unblock(); /* Make sure we clear all nodes, since this doesn't happen automatically */ lru.evict_all_nodes(); /* We've saved everything, so we have no more unsaved items */ unsaved_item_count = 0; } if( ok ) { /* Our history has been written to the file, so clear our state so we can re-reference the file. */ this->clear_file_state(); } }
bool autosuggest_validate_from_history(const history_item_t &item, file_detection_context_t &detector, const wcstring &working_directory, const env_vars_snapshot_t &vars) { ASSERT_IS_BACKGROUND_THREAD(); bool handled = false, suggestionOK = false; /* Parse the string */ wcstring parsed_command; wcstring_list_t parsed_arguments; int parsed_last_arg_pos = -1; if (! autosuggest_parse_command(item.str(), &parsed_command, &parsed_arguments, &parsed_last_arg_pos)) return false; if (parsed_command == L"cd" && ! parsed_arguments.empty()) { /* We can possibly handle this specially */ wcstring dir = parsed_arguments.back(); if (expand_one(dir, EXPAND_SKIP_CMDSUBST)) { handled = true; bool is_help = string_prefixes_string(dir, L"--help") || string_prefixes_string(dir, L"-h"); if (is_help) { suggestionOK = false; } else { wcstring path; bool can_cd = path_get_cdpath(dir, &path, working_directory.c_str(), vars); if (! can_cd) { suggestionOK = false; } else if (paths_are_same_file(working_directory, path)) { /* Don't suggest the working directory as the path! */ suggestionOK = false; } else { suggestionOK = true; } } } } /* If not handled specially, handle it here */ if (! handled) { bool cmd_ok = false; if (path_get_path(parsed_command, NULL)) { cmd_ok = true; } else if (builtin_exists(parsed_command) || function_exists_no_autoload(parsed_command, vars)) { cmd_ok = true; } if (cmd_ok) { const path_list_t &paths = item.get_required_paths(); if (paths.empty()) { suggestionOK= true; } else { detector.potential_paths = paths; suggestionOK = detector.paths_are_valid(paths); } } } return suggestionOK; }
bool history_t::is_deleted(const history_item_t &item) const { return deleted_items.count(item.str()) > 0; }