X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fprocedure.c;h=a45f497afd1268012a77064a5d49b33839c974f8;hb=9ade26c8349;hp=91e185a45a43f3d78802338d6b26536f0a9dc6b0;hpb=a1efcf97ca2f75f4be6a0389ff2372c03ed2d4e1;p=pspp-builds.git diff --git a/src/data/procedure.c b/src/data/procedure.c index 91e185a4..a45f497a 100644 --- a/src/data/procedure.c +++ b/src/data/procedure.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2007, 2009 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,28 +16,32 @@ #include +#include "data/procedure.h" + #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xalloc.h" +#include "data/case.h" +#include "data/case-map.h" +#include "data/caseinit.h" +#include "data/casereader.h" +#include "data/casereader-provider.h" +#include "data/casereader-shim.h" +#include "data/casewriter.h" +#include "data/dictionary.h" +#include "data/file-handle-def.h" +#include "data/transformations.h" +#include "data/variable.h" +#include "libpspp/deque.h" +#include "libpspp/misc.h" +#include "libpspp/str.h" +#include "libpspp/taint.h" +#include "libpspp/i18n.h" + +#include "gl/minmax.h" +#include "gl/xalloc.h" struct dataset { /* Cases are read from source, @@ -90,8 +94,15 @@ struct dataset { but proc_commit not yet called. */ } proc_state; - casenumber cases_written; /* Cases output so far. */ - bool ok; /* Error status. */ + casenumber cases_written; /* Cases output so far. */ + bool ok; /* Error status. */ + struct casereader_shim *shim; /* Shim on proc_open() casereader. */ + + void (*callback) (void *); /* Callback for when the dataset changes */ + void *cb_data; + + /* Default encoding for reading syntax files. */ + char *syntax_encoding; }; /* struct dataset */ @@ -99,9 +110,36 @@ static void add_case_limit_trns (struct dataset *ds); static void add_filter_trns (struct dataset *ds); static void update_last_proc_invocation (struct dataset *ds); + +static void +dataset_set_unsaved (const struct dataset *ds) +{ + if (ds->callback) ds->callback (ds->cb_data); +} + /* Public functions. */ +void +dataset_set_callback (struct dataset *ds, void (*cb) (void *), void *cb_data) +{ + ds->callback = cb; + ds->cb_data = cb_data; +} + +void +dataset_set_default_syntax_encoding (struct dataset *ds, const char *encoding) +{ + free (ds->syntax_encoding); + ds->syntax_encoding = xstrdup (encoding); +} + +const char * +dataset_get_default_syntax_encoding (const struct dataset *ds) +{ + return ds->syntax_encoding; +} + /* Returns the last time the data was read. */ time_t time_of_last_procedure (struct dataset *ds) @@ -139,11 +177,17 @@ proc_execute (struct dataset *ds) static const struct casereader_class proc_casereader_class; -/* Opens dataset DS for reading cases with proc_read. +/* Opens dataset DS for reading cases with proc_read. If FILTER is true, then + cases filtered out with FILTER BY will not be included in the casereader + (which is usually desirable). If FILTER is false, all cases will be + included regardless of FILTER BY settings. + proc_commit must be called when done. */ struct casereader * -proc_open (struct dataset *ds) +proc_open_filtering (struct dataset *ds, bool filter) { + struct casereader *reader; + assert (ds->source != NULL); assert (ds->proc_state == PROC_COMMITTED); @@ -153,7 +197,8 @@ proc_open (struct dataset *ds) /* Finish up the collection of transformations. */ add_case_limit_trns (ds); - add_filter_trns (ds); + if (filter) + add_filter_trns (ds); trns_chain_finalize (ds->cur_trns_chain); /* Make permanent_dict refer to the dictionary right before @@ -166,11 +211,19 @@ proc_open (struct dataset *ds) { struct dictionary *pd = ds->permanent_dict; size_t compacted_value_cnt = dict_count_values (pd, 1u << DC_SCRATCH); - bool should_compact = compacted_value_cnt < dict_get_next_value_idx (pd); - ds->compactor = (should_compact - ? case_map_to_compact_dict (pd, 1u << DC_SCRATCH) - : NULL); - ds->sink = autopaging_writer_create (compacted_value_cnt); + if (compacted_value_cnt < dict_get_next_value_idx (pd)) + { + struct caseproto *compacted_proto; + compacted_proto = dict_get_compacted_proto (pd, 1u << DC_SCRATCH); + ds->compactor = case_map_to_compact_dict (pd, 1u << DC_SCRATCH); + ds->sink = autopaging_writer_create (compacted_proto); + caseproto_unref (compacted_proto); + } + else + { + ds->compactor = NULL; + ds->sink = autopaging_writer_create (dict_get_proto (pd)); + } } else { @@ -188,10 +241,27 @@ proc_open (struct dataset *ds) /* FIXME: use taint in dataset in place of `ok'? */ /* FIXME: for trivial cases we can just return a clone of ds->source? */ - return casereader_create_sequential (NULL, - dict_get_next_value_idx (ds->dict), - CASENUMBER_MAX, - &proc_casereader_class, ds); + + /* Create casereader and insert a shim on top. The shim allows us to + arbitrarily extend the casereader's lifetime, by slurping the cases into + the shim's buffer in proc_commit(). That is especially useful when output + table_items are generated directly from the procedure casereader (e.g. by + the LIST procedure) when we are using an output driver that keeps a + reference to the output items passed to it (e.g. the GUI output driver in + PSPPIRE). */ + reader = casereader_create_sequential (NULL, dict_get_proto (ds->dict), + CASENUMBER_MAX, + &proc_casereader_class, ds); + ds->shim = casereader_shim_insert (reader); + return reader; +} + +/* Opens dataset DS for reading cases with proc_read. + proc_commit must be called when done. */ +struct casereader * +proc_open (struct dataset *ds) +{ + return proc_open_filtering (ds, true); } /* Returns true if a procedure is in progress, that is, if @@ -225,7 +295,7 @@ proc_casereader_read (struct casereader *reader UNUSED, void *ds_) c = casereader_read (ds->source); if (c == NULL) return NULL; - c = case_unshare_and_resize (c, dict_get_next_value_idx (ds->dict)); + c = case_unshare_and_resize (c, dict_get_proto (ds->dict)); caseinit_init_vars (ds->caseinit, c); /* Execute permanent transformations. */ @@ -270,6 +340,11 @@ proc_casereader_destroy (struct casereader *reader, void *ds_) struct dataset *ds = ds_; struct ccase *c; + /* We are always the subreader for a casereader_buffer, so if we're being + destroyed then it's because the casereader_buffer has read all the cases + that it ever will. */ + ds->shim = NULL; + /* Make sure transformations happen for every input case, in case they have side effects, and ensure that the replacement active file gets all the cases it should. */ @@ -290,9 +365,14 @@ proc_casereader_destroy (struct casereader *reader, void *ds_) bool proc_commit (struct dataset *ds) { + if (ds->shim != NULL) + casereader_shim_slurp (ds->shim); + assert (ds->proc_state == PROC_CLOSED); ds->proc_state = PROC_COMMITTED; + dataset_set_unsaved (ds); + /* Free memory for lagged cases. */ while (!deque_is_empty (&ds->lag)) case_unref (ds->lag_cases[deque_pop_back (&ds->lag)]); @@ -510,14 +590,30 @@ proc_cancel_all_transformations (struct dataset *ds) return ok; } + +static void +dict_callback (struct dictionary *d UNUSED, void *ds_) +{ + struct dataset *ds = ds_; + dataset_set_unsaved (ds); +} + /* Initializes procedure handling. */ struct dataset * create_dataset (void) { struct dataset *ds = xzalloc (sizeof(*ds)); ds->dict = dict_create (); + + dict_set_change_callback (ds->dict, dict_callback, ds); + + dict_set_encoding (ds->dict, get_default_encoding ()); + ds->caseinit = caseinit_create (); proc_cancel_all_transformations (ds); + + ds->syntax_encoding = xstrdup ("Auto"); + return ds; } @@ -542,6 +638,8 @@ destroy_dataset (struct dataset *ds) if ( ds->xform_callback) ds->xform_callback (false, ds->xform_callback_aux); + + free (ds->syntax_encoding); free (ds); } @@ -585,6 +683,7 @@ proc_set_active_file (struct dataset *ds, dict_destroy (ds->dict); ds->dict = dict; + dict_set_change_callback (ds->dict, dict_callback, ds); proc_set_active_file_data (ds, source); } @@ -638,7 +737,7 @@ dataset_end_of_command (struct dataset *ds) else { const struct taint *taint = casereader_get_taint (ds->source); - taint_reset_successor_taint ((struct taint *) taint); + taint_reset_successor_taint (CONST_CAST (struct taint *, taint)); assert (!taint_has_tainted_successor (taint)); } }