// Do a private, read-only map of the entirety of a history file with the given name. Returns true if successful. Returns the mapped memory region by reference. static bool map_file(const wcstring &name, const char **out_map_start, size_t *out_map_len) { bool result = false; wcstring filename = history_filename(name, L""); if (! filename.empty()) { int fd; if((fd = wopen_cloexec(filename, O_RDONLY)) > 0) { off_t len = lseek( fd, 0, SEEK_END ); if(len != (off_t)-1) { size_t mmap_length = (size_t)len; if(lseek(fd, 0, SEEK_SET) == 0) { char *mmap_start; if ((mmap_start = (char *)mmap(0, mmap_length, PROT_READ, MAP_PRIVATE, fd, 0)) != MAP_FAILED) { result = true; *out_map_start = mmap_start; *out_map_len = mmap_length; } } } close( fd ); } } return result; }
void history_t::clear(void) { scoped_lock locker(lock); new_items.clear(); deleted_items.clear(); unsaved_item_count = 0; old_item_offsets.clear(); wcstring filename = history_filename(name, L""); if (! filename.empty()) wunlink(filename); this->clear_file_state(); }
static void save_history (GtkWidget *combo) { gchar *host; GPtrArray *history; guint i; gchar *filename, *path; GString *content; GError *error = NULL; host = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (combo)); history = saved_history (); for (i=0; i<history->len; i++) if (!g_strcmp0 (g_ptr_array_index (history, i), host)) { g_ptr_array_remove_index (history, i); break; } g_ptr_array_add (history, host); content = g_string_new (NULL); for (i=0; i<history->len; i++) g_string_append_printf (content, "%s\n", (char *) g_ptr_array_index (history, i)); filename = history_filename (); path = g_path_get_dirname (filename); g_mkdir_with_parents (path, 0755); g_file_set_contents (filename, content->str, -1, &error); g_free (filename); g_free (path); g_ptr_array_free (history, TRUE); g_string_free (content, TRUE); if (error) { g_warning (_("Error while saving history file: %s"), error->message); g_error_free (error); } }
/** Load contents of the backing file to memory */ static void history_load( history_mode_t *m ) { int fd; int ok=0; void *context; wchar_t *filename; if( !m ) return; m->has_loaded=1; signal_block(); context = halloc( 0, 0 ); filename = history_filename( context, m->name, 0 ); if( filename ) { if( ( fd = wopen( filename, O_RDONLY ) ) > 0 ) { off_t len = lseek( fd, 0, SEEK_END ); if( len != (off_t)-1) { m->mmap_length = (size_t)len; if( lseek( fd, 0, SEEK_SET ) == 0 ) { if( (m->mmap_start = mmap( 0, m->mmap_length, PROT_READ, MAP_PRIVATE, fd, 0 )) != MAP_FAILED ) { ok = 1; history_populate_from_mmap( m ); } } } close( fd ); } } halloc_free( context ); signal_unblock(); }
static GPtrArray * saved_history (void) { gchar *filename, *file_contents = NULL; gchar **history_from_file = NULL, **list; gboolean success; gint len; GPtrArray *array; array = g_ptr_array_new_with_free_func (g_free); filename = history_filename (); success = g_file_get_contents (filename, &file_contents, NULL, NULL); if (success) { history_from_file = g_strsplit (file_contents, "\n", 0); len = g_strv_length (history_from_file); if (len > 0 && strlen (history_from_file[len-1]) == 0) { g_free (history_from_file[len-1]); history_from_file[len-1] = NULL; } list = history_from_file; while (*list != NULL) g_ptr_array_add (array, *list++); } g_free (filename); g_free (file_contents); g_free (history_from_file); return array; }
/** 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(); } }
/** Save the specified mode to file */ static void history_save_mode( void *n, history_mode_t *m ) { FILE *out; history_mode_t *on_disk; int i; int has_new=0; wchar_t *tmp_name; int ok = 1; /* First check if there are any new entries to save. If not, then we can just return */ for( i=0; i<al_get_count(&m->item); i++ ) { void *ptr = al_get( &m->item, i ); has_new = item_is_new( m, ptr ); if( has_new ) { break; } } if( !has_new ) { return; } signal_block(); /* Set up on_disk variable to describe the current contents of the history file */ on_disk = history_create_mode( m->name ); history_load( on_disk ); tmp_name = history_filename( on_disk, m->name, L".tmp" ); if( tmp_name ) { tmp_name = wcsdup(tmp_name ); if( (out=wfopen( tmp_name, "w" ) ) ) { hash_table_t mine; hash_init( &mine, &hash_item_func, &hash_item_cmp ); for( i=0; i<al_get_count(&m->item); i++ ) { void *ptr = al_get( &m->item, i ); int is_new = item_is_new( m, ptr ); if( is_new ) { hash_put( &mine, item_get( m, ptr ), L"" ); } } /* Re-save the old history */ for( i=0; ok && (i<al_get_count(&on_disk->item)); i++ ) { void *ptr = al_get( &on_disk->item, i ); item_t *i = item_get( on_disk, ptr ); if( !hash_get( &mine, i ) ) { if( item_write( out, on_disk, ptr ) == -1 ) { ok = 0; break; } } } hash_destroy( &mine ); /* Add our own items last */ for( i=0; ok && (i<al_get_count(&m->item)); i++ ) { void *ptr = al_get( &m->item, i ); int is_new = item_is_new( m, ptr ); if( is_new ) { if( item_write( out, m, ptr ) == -1 ) { ok = 0; } } } 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 { wrename( tmp_name, history_filename( on_disk, m->name, 0 ) ); } } free( tmp_name ); } halloc_free( on_disk); if( ok ) { /* Reset the history. The item_t entries created in this session are not lost or dropped, they are stored in the session_item hash table. On reload, they will be automatically inserted at the end of the history list. */ if( m->mmap_start && (m->mmap_start != MAP_FAILED ) ) { munmap( m->mmap_start, m->mmap_length ); } al_truncate( &m->item, 0 ); al_truncate( &m->used, 0 ); m->pos = 0; m->has_loaded = 0; m->mmap_start=0; m->mmap_length=0; m->save_timestamp=time(0); m->new_count = 0; } signal_unblock(); }
int repl() { #ifdef HAVE_READLINE history_truncate_file(history_filename(), 1000); read_history(history_filename()); #endif // HAVE_READLINE // Eingabeschleife als REPL(read-eval-print loop) -> Eingabe, Verarbeitung, Ausgabe do { char *user_input = NULL; ////////////////////// // read ////////////////////// #ifdef HAVE_READLINE user_input = readline("?: "); if (!user_input) break; strim(user_input); if (*user_input) add_history(user_input); #else // Eingabepuffer als "String" mit Maximallänge user_input = calloc(LINE_BUFFER_SIZE, sizeof(char)); printf("?: "); // Bei end-of-file oder Lesefehler gibt fgets NULL zurück // und das Programm soll beendet werden (analog zur Shell) // // break ist eigentlich nicht schön (=nicht im Struktogramm // abbildbar ;) ), dient aber hier der Klarheit, da einige // if entfallen können if (!fgets(user_input, LINE_BUFFER_SIZE, stdin)) break; strim(user_input); #endif // HAVE_READLINE ////////////////////// // eval ////////////////////// // Abbruch durch User? if (strcmp(user_input, "\\quit") == 0) break; // 1. Parsen parse_context *p_ctx = start_parse(user_input); // 2. Baum traversieren if (parse(p_ctx) == 0) { eval_context *e_ctx = eval(p_ctx->ast_root); if (e_ctx == NULL) fprintf(stderr, "Fatal evaluation error.\n"); if (e_ctx->success) { ////////////////////// // print ////////////////////// printf("-> %.50Lg\n", e_ctx->result); } else { print_eval_errors(e_ctx); } end_eval(e_ctx); } else { print_parse_errors(p_ctx); } // Aufräumen end_parse(p_ctx); free(user_input); } while (1); // loop.... #ifdef HAVE_READLINE write_history(history_filename()); #endif // HAVE_READLINE printf("End.\n"); return 0; }