history_item_t history_t::item_at_index(size_t idx) { scoped_lock locker(lock); /* 0 is considered an invalid index */ assert(idx > 0); idx--; /* idx=0 corresponds to last item in new_items */ size_t new_item_count = new_items.size(); if (idx < new_item_count) { return new_items.at(new_item_count - idx - 1); } /* Now look in our old items */ idx -= new_item_count; load_old_if_needed(); size_t old_item_count = old_item_offsets.size(); if (idx < old_item_count) { /* idx=0 corresponds to last item in old_item_offsets */ size_t offset = old_item_offsets.at(old_item_count - idx - 1); return history_t::decode_item(mmap_start + offset, mmap_length - offset); } /* Index past the valid range, so return an empty history item */ return history_item_t(wcstring(), 0); }
history_item_t history_t::decode_item(const char *base, size_t len, history_file_type_t type) { switch (type) { case history_type_fish_1_x: return history_t::decode_item_fish_1_x(base, len); case history_type_fish_2_0: return history_t::decode_item_fish_2_0(base, len); default: return history_item_t(L""); } }
history_item_t history_t::decode_item(const char *base, size_t len) { wcstring cmd; time_t when = 0; path_list_t paths; size_t indent = 0, cursor = 0; std::string key, value, line; /* Read the "- cmd:" line */ size_t advance = read_line(base, cursor, len, line); trim_leading_spaces(line); if (! extract_prefix(key, value, line) || key != "- cmd") goto done; cursor += advance; cmd = str2wcstring(value); /* Read the remaining lines */ for (;;) { /* Read a line */ size_t advance = read_line(base, cursor, len, line); /* Count and trim leading spaces */ size_t this_indent = trim_leading_spaces(line); if (indent == 0) indent = this_indent; if (this_indent == 0 || indent != this_indent) break; if (! extract_prefix(key, value, line)) break; /* We are definitely going to consume this line */ unescape_yaml(value); cursor += advance; if (key == "when") { /* Parse an int from the timestamp */ long tmp = 0; if (sscanf(value.c_str(), "%ld", &tmp) > 0) { when = tmp; } } else if (key == "paths") { /* Read lines starting with " - " until we can't read any more */ for (;;) { size_t advance = read_line(base, cursor, len, line); if (trim_leading_spaces(line) <= indent) break; if (strncmp(line.c_str(), "- ", 2)) break; /* We're going to consume this line */ cursor += advance; /* Skip the leading dash-space and then store this path it */ line.erase(0, 2); unescape_yaml(line); paths.push_front(str2wcstring(line)); } } } /* Reverse the paths, since we pushed them to the front each time */ done: paths.reverse(); return history_item_t(cmd, when, paths); }
void history_t::add(const wcstring &str, const path_list_t &valid_paths) { this->add(history_item_t(str, time(NULL), valid_paths)); }
/* Decode an item via the fish 1.x format. Adapted from fish 1.x's item_get(). */ history_item_t history_t::decode_item_fish_1_x(const char *begin, size_t length) { const char *end = begin + length; const char *pos=begin; bool was_backslash = 0; wcstring out; bool first_char = true; bool timestamp_mode = false; time_t timestamp = 0; while( 1 ) { wchar_t c; mbstate_t state; size_t res; memset( &state, 0, sizeof(state) ); res = mbrtowc( &c, pos, end-pos, &state ); if( res == (size_t)-1 ) { pos++; continue; } else if( res == (size_t)-2 ) { break; } else if( res == (size_t)0 ) { pos++; continue; } pos += res; if( c == L'\n' ) { if( timestamp_mode ) { const wchar_t *time_string = out.c_str(); while( *time_string && !iswdigit(*time_string)) time_string++; errno=0; if( *time_string ) { time_t tm; wchar_t *end; errno = 0; tm = (time_t)wcstol( time_string, &end, 10 ); if( tm && !errno && !*end ) { timestamp = tm; } } out.clear(); timestamp_mode = false; continue; } if( !was_backslash ) break; } if( first_char ) { if( c == L'#' ) timestamp_mode = true; } first_char = false; out.push_back(c); was_backslash = ( (c == L'\\') && !was_backslash); } out = history_unescape_newlines_fish_1_x(out); return history_item_t(out, timestamp); }