X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fxforms%2Frecode.c;h=467a18652db46384e5b045a174f29d310f3fdee8;hb=3036141100b876878ab28efd464b6e17c42dd90d;hp=62cf387eee9faf30b6afa4cff210c17124e76a26;hpb=bc19562deb692e6db3271eb0402e9f9c99e4cbcb;p=pspp diff --git a/src/language/xforms/recode.c b/src/language/xforms/recode.c index 62cf387eee..467a18652d 100644 --- a/src/language/xforms/recode.c +++ b/src/language/xforms/recode.c @@ -22,9 +22,9 @@ #include "data/case.h" #include "data/data-in.h" +#include "data/dataset.h" #include "data/dictionary.h" #include "data/format.h" -#include "data/procedure.h" #include "data/transformations.h" #include "data/variable.h" #include "language/command.h" @@ -85,8 +85,6 @@ struct recode_trns { struct pool *pool; - - /* Variable types, for convenience. */ enum val_type src_type; /* src_vars[*] type. */ enum val_type dst_type; /* dst_vars[*] type. */ @@ -96,39 +94,43 @@ struct recode_trns const struct variable **dst_vars; /* Destination variables. */ const struct dictionary *dst_dict; /* Dictionary of dst_vars */ char **dst_names; /* Name of dest variables, if they're new. */ - size_t var_cnt; /* Number of variables. */ + size_t n_vars; /* Number of variables. */ /* Mappings. */ struct mapping *mappings; /* Value mappings. */ - size_t map_cnt; /* Number of mappings. */ + size_t n_maps; /* Number of mappings. */ int max_src_width; /* Maximum width of src_vars[*]. */ int max_dst_width; /* Maximum width of any map_out in mappings. */ }; static bool parse_src_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict); -static bool parse_mappings (struct lexer *, struct recode_trns *); +static bool parse_mappings (struct lexer *, struct recode_trns *, + const char *dict_encoding); static bool parse_dst_vars (struct lexer *, struct recode_trns *, const struct dictionary *dict); static void add_mapping (struct recode_trns *, size_t *map_allocated, const struct map_in *); static bool parse_map_in (struct lexer *lexer, struct map_in *, struct pool *, - enum val_type src_type, size_t max_src_width); + enum val_type src_type, size_t max_src_width, + const char *dict_encoding); static void set_map_in_generic (struct map_in *, enum map_in_type); static void set_map_in_num (struct map_in *, enum map_in_type, double, double); static void set_map_in_str (struct map_in *, struct pool *, - struct substring, size_t width); + struct substring, size_t width, + const char *dict_encoding); static bool parse_map_out (struct lexer *lexer, struct pool *, struct map_out *); static void set_map_out_num (struct map_out *, double); static void set_map_out_str (struct map_out *, struct pool *, struct substring); -static void enlarge_dst_widths (struct recode_trns *); +static bool enlarge_dst_widths (struct recode_trns *); static void create_dst_vars (struct recode_trns *, struct dictionary *); -static trns_proc_func recode_trns_proc; -static trns_free_func recode_trns_free; +static bool recode_trns_free (void *trns_); + +static const struct trns_class recode_trns_class; /* Parser. */ @@ -138,15 +140,16 @@ cmd_recode (struct lexer *lexer, struct dataset *ds) { do { + struct dictionary *dict = dataset_dict (ds); struct recode_trns *trns = pool_create_container (struct recode_trns, pool); /* Parse source variable names, then input to output mappings, then destintation variable names. */ - if (!parse_src_vars (lexer, trns, dataset_dict (ds) ) - || !parse_mappings (lexer, trns) - || !parse_dst_vars (lexer, trns, dataset_dict (ds))) + if (!parse_src_vars (lexer, trns, dict) + || !parse_mappings (lexer, trns, dict_get_encoding (dict)) + || !parse_dst_vars (lexer, trns, dict)) { recode_trns_free (trns); return CMD_FAILURE; @@ -155,32 +158,37 @@ cmd_recode (struct lexer *lexer, struct dataset *ds) /* Ensure that all the output strings are at least as wide as the widest destination variable. */ if (trns->dst_type == VAL_STRING) - enlarge_dst_widths (trns); + { + if (! enlarge_dst_widths (trns)) + { + recode_trns_free (trns); + return CMD_FAILURE; + } + } /* Create destination variables, if needed. This must be the final step; otherwise we'd have to delete destination variables on failure. */ - trns->dst_dict = dataset_dict (ds); + trns->dst_dict = dict; if (trns->src_vars != trns->dst_vars) - create_dst_vars (trns, dataset_dict (ds)); + create_dst_vars (trns, dict); /* Done. */ - add_transformation (ds, - recode_trns_proc, recode_trns_free, trns); + add_transformation (ds, &recode_trns_class, trns); } while (lex_match (lexer, T_SLASH)); - return lex_end_of_command (lexer); + return CMD_SUCCESS; } /* Parses a set of variables to recode into TRNS->src_vars and - TRNS->var_cnt. Sets TRNS->src_type. Returns true if + TRNS->n_vars. Sets TRNS->src_type. Returns true if successful, false on parse error. */ static bool parse_src_vars (struct lexer *lexer, struct recode_trns *trns, const struct dictionary *dict) { - if (!parse_variables_const (lexer, dict, &trns->src_vars, &trns->var_cnt, + if (!parse_variables_const (lexer, dict, &trns->src_vars, &trns->n_vars, PV_SAME_TYPE)) return false; pool_register (trns->pool, free, trns->src_vars); @@ -189,10 +197,11 @@ parse_src_vars (struct lexer *lexer, } /* Parses a set of mappings, which take the form (input=output), - into TRNS->mappings and TRNS->map_cnt. Sets TRNS->dst_type. + into TRNS->mappings and TRNS->n_maps. Sets TRNS->dst_type. Returns true if successful, false on parse error. */ static bool -parse_mappings (struct lexer *lexer, struct recode_trns *trns) +parse_mappings (struct lexer *lexer, struct recode_trns *trns, + const char *dict_encoding) { size_t map_allocated; bool have_dst_type; @@ -200,7 +209,7 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns) /* Find length of longest source variable. */ trns->max_src_width = var_get_width (trns->src_vars[0]); - for (i = 1; i < trns->var_cnt; i++) + for (i = 1; i < trns->n_vars; i++) { size_t var_width = var_get_width (trns->src_vars[i]); if (var_width > trns->max_src_width) @@ -209,7 +218,7 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns) /* Parse the mappings in parentheses. */ trns->mappings = NULL; - trns->map_cnt = 0; + trns->n_maps = 0; map_allocated = 0; have_dst_type = false; if (!lex_force_match (lexer, T_LPAREN)) @@ -224,7 +233,7 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns) size_t first_map_idx; size_t i; - first_map_idx = trns->map_cnt; + first_map_idx = trns->n_maps; /* Parse source specifications. */ do @@ -232,7 +241,8 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns) struct map_in in; if (!parse_map_in (lexer, &in, trns->pool, - trns->src_type, trns->max_src_width)) + trns->src_type, trns->max_src_width, + dict_encoding)) return false; add_mapping (trns, &map_allocated, &in); lex_match (lexer, T_COMMA); @@ -254,7 +264,7 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns) return false; } - for (i = first_map_idx; i < trns->map_cnt; i++) + for (i = first_map_idx; i < trns->n_maps; i++) trns->mappings[i].out = out; } else @@ -263,7 +273,7 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns) struct map_in in; set_map_in_generic (&in, MAP_CONVERT); add_mapping (trns, &map_allocated, &in); - set_map_out_num (&trns->mappings[trns->map_cnt - 1].out, 0.0); + set_map_out_num (&trns->mappings[trns->n_maps - 1].out, 0.0); dst_type = VAL_NUMERIC; if (trns->src_type != VAL_STRING @@ -292,7 +302,8 @@ parse_mappings (struct lexer *lexer, struct recode_trns *trns) false on parse error. */ static bool parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool, - enum val_type src_type, size_t max_src_width) + enum val_type src_type, size_t max_src_width, + const char *dict_encoding) { if (lex_match_id (lexer, "ELSE")) @@ -317,14 +328,15 @@ parse_map_in (struct lexer *lexer, struct map_in *in, struct pool *pool, set_map_in_generic (in, MAP_MISSING); else if (!lex_force_string (lexer)) return false; - else + else { - set_map_in_str (in, pool, lex_tokss (lexer), max_src_width); + set_map_in_str (in, pool, lex_tokss (lexer), max_src_width, + dict_encoding); lex_get (lexer); if (lex_token (lexer) == T_ID && lex_id_match (ss_cstr ("THRU"), lex_tokss (lexer))) { - msg (SE, _("THRU is not allowed with string variables.")); + msg (SE, _("%s is not allowed with string variables."), "THRU"); return false; } } @@ -341,11 +353,11 @@ add_mapping (struct recode_trns *trns, size_t *map_allocated, const struct map_in *in) { struct mapping *m; - if (trns->map_cnt >= *map_allocated) + if (trns->n_maps >= *map_allocated) trns->mappings = pool_2nrealloc (trns->pool, trns->mappings, map_allocated, sizeof *trns->mappings); - m = &trns->mappings[trns->map_cnt++]; + m = &trns->mappings[trns->n_maps++]; m->in = *in; } @@ -371,13 +383,16 @@ set_map_in_num (struct map_in *in, enum map_in_type type, double x, double y) right to WIDTH characters long. */ static void set_map_in_str (struct map_in *in, struct pool *pool, - struct substring string, size_t width) + struct substring string, size_t width, + const char *dict_encoding) { + char *s = recode_string (dict_encoding, "UTF-8", + ss_data (string), ss_length (string)); in->type = MAP_SINGLE; value_init_pool (pool, &in->x, width); value_copy_buf_rpad (&in->x, width, - CHAR_CAST_BUG (uint8_t *, ss_data (string)), - ss_length (string), ' '); + CHAR_CAST (uint8_t *, s), strlen (s), ' '); + free (s); } /* Parses a mapping output value into OUT, allocating memory from @@ -397,10 +412,10 @@ parse_map_out (struct lexer *lexer, struct pool *pool, struct map_out *out) set_map_out_str (out, pool, lex_tokss (lexer)); lex_get (lexer); } - else if (lex_match_id (lexer, "COPY")) + else if (lex_match_id (lexer, "COPY")) { out->copy_input = true; - out->width = 0; + out->width = 0; } else { @@ -437,7 +452,7 @@ set_map_out_str (struct map_out *out, struct pool *pool, out->copy_input = false; value_init_pool (pool, &out->value, length); - memcpy (value_str_rw (&out->value, length), string, length); + memcpy (out->value.s, string, length); out->width = length; } @@ -451,26 +466,26 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns, if (lex_match_id (lexer, "INTO")) { - size_t name_cnt; + size_t n_names; size_t i; if (!parse_mixed_vars_pool (lexer, dict, trns->pool, - &trns->dst_names, &name_cnt, + &trns->dst_names, &n_names, PV_NONE)) return false; - if (name_cnt != trns->var_cnt) + if (n_names != trns->n_vars) { msg (SE, _("%zu variable(s) cannot be recoded into " "%zu variable(s). Specify the same number " "of variables as source and target variables."), - trns->var_cnt, name_cnt); + trns->n_vars, n_names); return false; } trns->dst_vars = pool_nalloc (trns->pool, - trns->var_cnt, sizeof *trns->dst_vars); - for (i = 0; i < trns->var_cnt; i++) + trns->n_vars, sizeof *trns->dst_vars); + for (i = 0; i < trns->n_vars; i++) { const struct variable *v; v = trns->dst_vars[i] = dict_lookup_var (dict, trns->dst_names[i]); @@ -500,7 +515,7 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns, } } - for (i = 0; i < trns->var_cnt; i++) + for (i = 0; i < trns->n_vars; i++) { const struct variable *v = trns->dst_vars[i]; if (v != NULL && var_get_type (v) != trns->dst_type) @@ -519,26 +534,46 @@ parse_dst_vars (struct lexer *lexer, struct recode_trns *trns, /* Ensures that all the output values in TRNS are as wide as the widest destination variable. */ -static void +static bool enlarge_dst_widths (struct recode_trns *trns) { size_t i; - + const struct variable *narrow_var = NULL; + int min_dst_width = INT_MAX; trns->max_dst_width = 0; - for (i = 0; i < trns->var_cnt; i++) + + for (i = 0; i < trns->n_vars; i++) { const struct variable *v = trns->dst_vars[i]; if (var_get_width (v) > trns->max_dst_width) trns->max_dst_width = var_get_width (v); + + if (var_get_width (v) < min_dst_width) + { + min_dst_width = var_get_width (v); + narrow_var = v; + } } - for (i = 0; i < trns->map_cnt; i++) + for (i = 0; i < trns->n_maps; i++) { struct map_out *out = &trns->mappings[i].out; if (!out->copy_input) - value_resize_pool (trns->pool, &out->value, - out->width, trns->max_dst_width); + { + if (out->width > min_dst_width) + { + msg (ME, + _("Cannot recode because the variable %s would require a width of %d bytes or greater, but it has a width of only %d bytes."), + var_get_name (narrow_var), out->width, min_dst_width); + return false; + } + + value_resize_pool (trns->pool, &out->value, + out->width, trns->max_dst_width); + } } + + return true; } /* Creates destination variables that don't already exist. */ @@ -547,7 +582,7 @@ create_dst_vars (struct recode_trns *trns, struct dictionary *dict) { size_t i; - for (i = 0; i < trns->var_cnt; i++) + for (i = 0; i < trns->n_vars; i++) { const struct variable **var = &trns->dst_vars[i]; const char *name = trns->dst_names[i]; @@ -568,7 +603,7 @@ find_src_numeric (struct recode_trns *trns, double value, const struct variable { struct mapping *m; - for (m = trns->mappings; m < trns->mappings + trns->map_cnt; m++) + for (m = trns->mappings; m < trns->mappings + trns->n_maps; m++) { const struct map_in *in = &m->in; const struct map_out *out = &m->out; @@ -580,7 +615,7 @@ find_src_numeric (struct recode_trns *trns, double value, const struct variable match = value == in->x.f; break; case MAP_MISSING: - match = var_is_num_missing (v, value, MV_ANY); + match = var_is_num_missing (v, value) != 0; break; case MAP_RANGE: match = value >= in->x.f && value <= in->y.f; @@ -612,7 +647,7 @@ find_src_string (struct recode_trns *trns, const uint8_t *value, int width = var_get_width (src_var); struct mapping *m; - for (m = trns->mappings; m < trns->mappings + trns->map_cnt; m++) + for (m = trns->mappings; m < trns->mappings + trns->n_maps; m++) { const struct map_in *in = &m->in; struct map_out *out = &m->out; @@ -621,8 +656,7 @@ find_src_string (struct recode_trns *trns, const uint8_t *value, switch (in->type) { case MAP_SINGLE: - match = !memcmp (value, value_str (&in->x, trns->max_src_width), - width); + match = !memcmp (value, in->x.s, width); break; case MAP_ELSE: match = true; @@ -633,7 +667,8 @@ find_src_string (struct recode_trns *trns, const uint8_t *value, char *error; error = data_in (ss_buffer (CHAR_CAST_BUG (char *, value), width), - C_ENCODING, FMT_F, &uv, 0, encoding); + C_ENCODING, FMT_F, settings_get_fmt_settings (), + &uv, 0, encoding); match = error == NULL; free (error); @@ -641,7 +676,7 @@ find_src_string (struct recode_trns *trns, const uint8_t *value, break; } case MAP_MISSING: - match = var_is_str_missing (src_var, value, MV_ANY); + match = var_is_str_missing (src_var, value) != 0; break; default: NOT_REACHED (); @@ -655,14 +690,14 @@ find_src_string (struct recode_trns *trns, const uint8_t *value, } /* Performs RECODE transformation. */ -static int +static enum trns_result recode_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED) { struct recode_trns *trns = trns_; size_t i; *c = case_unshare (*c); - for (i = 0; i < trns->var_cnt; i++) + for (i = 0; i < trns->n_vars; i++) { const struct variable *src_var = trns->src_vars[i]; const struct variable *dst_var = trns->dst_vars[i]; @@ -675,7 +710,7 @@ recode_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED) if (trns->dst_type == VAL_NUMERIC) { - double *dst = &case_data_rw (*c, dst_var)->f; + double *dst = case_num_rw (*c, dst_var); if (out != NULL) *dst = !out->copy_input ? out->value.f : case_num (*c, src_var); else if (trns->src_vars != trns->dst_vars) @@ -687,8 +722,7 @@ recode_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED) if (out != NULL) { if (!out->copy_input) - memcpy (dst, value_str (&out->value, trns->max_dst_width), - var_get_width (dst_var)); + memcpy (dst, out->value.s, var_get_width (dst_var)); else if (trns->src_vars != trns->dst_vars) { union value *dst_data = case_data_rw (*c, dst_var); @@ -713,3 +747,9 @@ recode_trns_free (void *trns_) pool_destroy (trns->pool); return true; } + +static const struct trns_class recode_trns_class = { + .name = "RECODE", + .execute = recode_trns_proc, + .destroy = recode_trns_free, +};