27 |
#include "error.h" |
#include "error.h" |
28 |
#include "hash.h" |
#include "hash.h" |
29 |
#include "misc.h" |
#include "misc.h" |
30 |
|
#include "settings.h" |
31 |
#include "str.h" |
#include "str.h" |
32 |
#include "value-labels.h" |
#include "value-labels.h" |
33 |
#include "var.h" |
#include "var.h" |
38 |
struct variable **var; /* Variables. */ |
struct variable **var; /* Variables. */ |
39 |
size_t var_cnt, var_cap; /* Number of variables, capacity. */ |
size_t var_cnt, var_cap; /* Number of variables, capacity. */ |
40 |
struct hsh_table *name_tab; /* Variable index by name. */ |
struct hsh_table *name_tab; /* Variable index by name. */ |
|
struct hsh_table *long_name_tab; /* Variable indexed by long name */ |
|
41 |
int next_value_idx; /* Index of next `union value' to allocate. */ |
int next_value_idx; /* Index of next `union value' to allocate. */ |
42 |
struct variable **split; /* SPLIT FILE vars. */ |
struct variable **split; /* SPLIT FILE vars. */ |
43 |
size_t split_cnt; /* SPLIT FILE count. */ |
size_t split_cnt; /* SPLIT FILE count. */ |
50 |
size_t vector_cnt; /* Number of vectors. */ |
size_t vector_cnt; /* Number of vectors. */ |
51 |
}; |
}; |
52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
compare_long_names(const void *a_, const void *b_, void *aux UNUSED) |
|
|
{ |
|
|
const struct name_table_entry *a = a_; |
|
|
const struct name_table_entry *b = b_; |
|
|
|
|
|
return strcasecmp(a->longname, b->longname); |
|
|
} |
|
|
|
|
|
|
|
|
/* Long names use case insensitive comparison */ |
|
|
unsigned int |
|
|
hash_long_name (const void *e_, void *aux UNUSED) |
|
|
{ |
|
|
const struct name_table_entry *e = e_; |
|
|
unsigned int hash; |
|
|
int i; |
|
|
|
|
|
char *s = strdup(e->longname); |
|
|
|
|
|
for ( i = 0 ; i < strlen(s) ; ++i ) |
|
|
s[i] = toupper(s[i]); |
|
|
|
|
|
hash = hsh_hash_string (s); |
|
|
|
|
|
free (s); |
|
|
|
|
|
return hash; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *make_short_name(struct dictionary *dict, const char *longname) ; |
|
|
|
|
|
|
|
53 |
/* Creates and returns a new dictionary. */ |
/* Creates and returns a new dictionary. */ |
54 |
struct dictionary * |
struct dictionary * |
55 |
dict_create (void) |
dict_create (void) |
59 |
d->var = NULL; |
d->var = NULL; |
60 |
d->var_cnt = d->var_cap = 0; |
d->var_cnt = d->var_cap = 0; |
61 |
d->name_tab = hsh_create (8, compare_var_names, hash_var_name, NULL, NULL); |
d->name_tab = hsh_create (8, compare_var_names, hash_var_name, NULL, NULL); |
|
d->long_name_tab = hsh_create (8, compare_long_names, hash_long_name, |
|
|
(hsh_free_func *) free_nte, NULL); |
|
62 |
d->next_value_idx = 0; |
d->next_value_idx = 0; |
63 |
d->split = NULL; |
d->split = NULL; |
64 |
d->split_cnt = 0; |
d->split_cnt = 0; |
85 |
|
|
86 |
d = dict_create (); |
d = dict_create (); |
87 |
|
|
88 |
for (i = 0; i < s->var_cnt; i++) |
for (i = 0; i < s->var_cnt; i++) |
89 |
dict_clone_var (d, s->var[i], s->var[i]->name, s->var[i]->longname); |
{ |
90 |
|
struct variable *sv = s->var[i]; |
91 |
|
struct variable *dv = dict_clone_var_assert (d, sv, sv->name); |
92 |
|
var_set_short_name (dv, sv->short_name); |
93 |
|
} |
94 |
|
|
95 |
d->next_value_idx = s->next_value_idx; |
d->next_value_idx = s->next_value_idx; |
96 |
|
|
112 |
dict_set_label (d, dict_get_label (s)); |
dict_set_label (d, dict_get_label (s)); |
113 |
dict_set_documents (d, dict_get_documents (s)); |
dict_set_documents (d, dict_get_documents (s)); |
114 |
|
|
115 |
|
d->vector_cnt = s->vector_cnt; |
116 |
|
d->vector = xmalloc (d->vector_cnt * sizeof *d->vector); |
117 |
for (i = 0; i < s->vector_cnt; i++) |
for (i = 0; i < s->vector_cnt; i++) |
118 |
dict_create_vector (d, s->vector[i]->name, |
{ |
119 |
s->vector[i]->var, s->vector[i]->cnt); |
struct vector *sv = s->vector[i]; |
120 |
|
struct vector *dv = d->vector[i] = xmalloc (sizeof *dv); |
121 |
|
int j; |
122 |
|
|
123 |
|
dv->idx = i; |
124 |
|
strcpy (dv->name, sv->name); |
125 |
|
dv->cnt = sv->cnt; |
126 |
|
dv->var = xmalloc (dv->cnt * sizeof *dv->var); |
127 |
|
for (j = 0; j < dv->cnt; j++) |
128 |
|
dv->var[j] = d->var[sv->var[j]->index]; |
129 |
|
} |
130 |
|
|
131 |
return d; |
return d; |
132 |
} |
} |
154 |
d->var = NULL; |
d->var = NULL; |
155 |
d->var_cnt = d->var_cap = 0; |
d->var_cnt = d->var_cap = 0; |
156 |
hsh_clear (d->name_tab); |
hsh_clear (d->name_tab); |
|
if ( d->long_name_tab) |
|
|
hsh_clear (d->long_name_tab); |
|
157 |
d->next_value_idx = 0; |
d->next_value_idx = 0; |
158 |
free (d->split); |
free (d->split); |
159 |
d->split = NULL; |
d->split = NULL; |
168 |
dict_clear_vectors (d); |
dict_clear_vectors (d); |
169 |
} |
} |
170 |
|
|
|
/* Allocate the pointer TEXT and fill it with text representing the |
|
|
long variable name buffer. SIZE will contain the size of TEXT. |
|
|
TEXT must be freed by the caller when no longer required. |
|
|
*/ |
|
|
void |
|
|
dict_get_varname_block(const struct dictionary *dict, char **text, int *size) |
|
|
{ |
|
|
char *buf = 0; |
|
|
int bufsize = 0; |
|
|
struct hsh_iterator hi; |
|
|
struct name_table_entry *nte; |
|
|
short first = 1; |
|
|
|
|
|
for ( nte = hsh_first(dict->long_name_tab, &hi); |
|
|
nte; |
|
|
nte = hsh_next(dict->long_name_tab, &hi)) |
|
|
{ |
|
|
bufsize += strlen(nte->name) + strlen(nte->longname) + 2; |
|
|
buf = xrealloc(buf, bufsize + 1); |
|
|
if ( first ) |
|
|
strcpy(buf, ""); |
|
|
first = 0; |
|
|
|
|
|
strcat(buf, nte->name); |
|
|
strcat(buf, "="); |
|
|
strcat(buf, nte->longname); |
|
|
strcat(buf, "\t"); |
|
|
} |
|
|
|
|
|
if ( bufsize > 0 ) |
|
|
{ |
|
|
/* Loose the final delimiting TAB */ |
|
|
buf[bufsize]='\0'; |
|
|
bufsize--; |
|
|
} |
|
|
|
|
|
*text = buf; |
|
|
*size = bufsize; |
|
|
} |
|
|
|
|
|
/* Add a new entry into the dictionary's long name table, and update the |
|
|
corresponding variable with the relevant long name. |
|
|
*/ |
|
|
void |
|
|
dict_add_longvar_entry(struct dictionary *d, |
|
|
const char *name, |
|
|
const char *longname) |
|
|
{ |
|
|
struct variable *v; |
|
|
assert ( name ) ; |
|
|
assert ( longname ); |
|
|
struct name_table_entry *nte = xmalloc (sizeof (struct name_table_entry)); |
|
|
nte->longname = strdup(longname); |
|
|
nte->name = strdup(name); |
|
|
|
|
|
/* Look up the name in name_tab */ |
|
|
v = hsh_find ( d->name_tab, name); |
|
|
if ( !v ) |
|
|
{ |
|
|
msg (FE, _("The entry \"%s\" in the variable name map, has no corresponding variable"), name); |
|
|
return ; |
|
|
} |
|
|
assert ( 0 == strcmp(v->name, name) ); |
|
|
v->longname = nte->longname; |
|
|
|
|
|
hsh_insert(d->long_name_tab, nte); |
|
|
} |
|
|
|
|
|
/* Destroy and free up an nte */ |
|
|
void |
|
|
free_nte(struct name_table_entry *nte) |
|
|
{ |
|
|
assert(nte); |
|
|
free(nte->longname); |
|
|
free(nte->name); |
|
|
free(nte); |
|
|
} |
|
|
|
|
|
|
|
171 |
/* Destroys the aux data for every variable in D, by calling |
/* Destroys the aux data for every variable in D, by calling |
172 |
var_clear_aux() for each variable. */ |
var_clear_aux() for each variable. */ |
173 |
void |
void |
189 |
{ |
{ |
190 |
dict_clear (d); |
dict_clear (d); |
191 |
hsh_destroy (d->name_tab); |
hsh_destroy (d->name_tab); |
|
hsh_destroy (d->long_name_tab); |
|
192 |
free (d); |
free (d); |
193 |
} |
} |
194 |
} |
} |
246 |
} |
} |
247 |
|
|
248 |
|
|
|
static struct variable * dict_create_var_x (struct dictionary *d, |
|
|
const char *name, int width, |
|
|
short name_is_short) ; |
|
|
|
|
|
/* Creates and returns a new variable in D with the given LONGNAME |
|
|
and WIDTH. Returns a null pointer if the given LONGNAME would |
|
|
duplicate that of an existing variable in the dictionary. |
|
|
*/ |
|
|
struct variable * |
|
|
dict_create_var (struct dictionary *d, const char *longname, int width) |
|
|
{ |
|
|
return dict_create_var_x(d, longname, width, 0); |
|
|
} |
|
|
|
|
|
|
|
|
/* Creates and returns a new variable in D with the given SHORTNAME and |
|
|
WIDTH. The long name table is not updated */ |
|
|
struct variable * |
|
|
dict_create_var_from_short (struct dictionary *d, const char *shortname, |
|
|
int width) |
|
|
{ |
|
|
return dict_create_var_x(d, shortname, width, 1); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
249 |
/* Creates and returns a new variable in D with the given NAME |
/* Creates and returns a new variable in D with the given NAME |
250 |
and WIDTH. |
and WIDTH. Returns a null pointer if the given NAME would |
251 |
If NAME_IS_SHORT, assume NAME is the short name. Otherwise assumes |
duplicate that of an existing variable in the dictionary. */ |
252 |
NAME is the long name, and creates the corresponding entry in the |
struct variable * |
253 |
Dictionary's lookup name table . |
dict_create_var (struct dictionary *d, const char *name, int width) |
|
Returns a null pointer if the given NAME would |
|
|
duplicate that of an existing variable in the dictionary. |
|
|
|
|
|
*/ |
|
|
static struct variable * |
|
|
dict_create_var_x (struct dictionary *d, const char *name, int width, |
|
|
short name_is_short) |
|
254 |
{ |
{ |
255 |
struct variable *v; |
struct variable *v; |
256 |
|
|
258 |
assert (name != NULL); |
assert (name != NULL); |
259 |
|
|
260 |
assert (strlen (name) >= 1); |
assert (strlen (name) >= 1); |
261 |
|
assert (strlen (name) <= LONG_NAME_LEN); |
262 |
|
|
263 |
assert (width >= 0 && width < 256); |
assert (width >= 0 && width < 256); |
264 |
|
|
|
if ( name_is_short ) |
|
|
assert(strlen (name) <= SHORT_NAME_LEN); |
|
|
else |
|
|
assert(strlen (name) <= LONG_NAME_LEN); |
|
|
|
|
265 |
/* Make sure there's not already a variable by that name. */ |
/* Make sure there's not already a variable by that name. */ |
266 |
if (dict_lookup_var (d, name) != NULL) |
if (dict_lookup_var (d, name) != NULL) |
267 |
return NULL; |
return NULL; |
268 |
|
|
269 |
/* Allocate and initialize variable. */ |
/* Allocate and initialize variable. */ |
270 |
v = xmalloc (sizeof *v); |
v = xmalloc (sizeof *v); |
271 |
|
st_trim_copy (v->name, name, sizeof v->name); |
|
if ( name_is_short ) |
|
|
{ |
|
|
strncpy (v->name, name, sizeof v->name); |
|
|
v->name[SHORT_NAME_LEN] = '\0'; |
|
|
} |
|
|
else |
|
|
{ |
|
|
const char *sn = make_short_name(d, name); |
|
|
strncpy(v->name, sn, SHORT_NAME_LEN + 1); |
|
|
free(sn); |
|
|
} |
|
|
|
|
|
|
|
|
v->index = d->var_cnt; |
|
272 |
v->type = width == 0 ? NUMERIC : ALPHA; |
v->type = width == 0 ? NUMERIC : ALPHA; |
273 |
v->width = width; |
v->width = width; |
274 |
v->fv = d->next_value_idx; |
v->fv = d->next_value_idx; |
275 |
v->nv = width == 0 ? 1 : DIV_RND_UP (width, 8); |
v->nv = width == 0 ? 1 : DIV_RND_UP (width, 8); |
276 |
v->init = 1; |
v->init = 1; |
277 |
v->reinit = dict_class_from_id (v->name) != DC_SCRATCH; |
v->reinit = dict_class_from_id (v->name) != DC_SCRATCH; |
278 |
|
v->index = d->var_cnt; |
279 |
v->miss_type = MISSING_NONE; |
v->miss_type = MISSING_NONE; |
280 |
if (v->type == NUMERIC) |
if (v->type == NUMERIC) |
281 |
{ |
{ |
300 |
v->write = v->print; |
v->write = v->print; |
301 |
v->val_labs = val_labs_create (v->width); |
v->val_labs = val_labs_create (v->width); |
302 |
v->label = NULL; |
v->label = NULL; |
303 |
|
var_clear_short_name (v); |
304 |
v->aux = NULL; |
v->aux = NULL; |
305 |
v->aux_dtor = NULL; |
v->aux_dtor = NULL; |
306 |
|
|
314 |
d->var_cnt++; |
d->var_cnt++; |
315 |
hsh_force_insert (d->name_tab, v); |
hsh_force_insert (d->name_tab, v); |
316 |
|
|
|
if ( ! name_is_short) |
|
|
dict_add_longvar_entry(d, v->name, name); |
|
|
|
|
317 |
d->next_value_idx += v->nv; |
d->next_value_idx += v->nv; |
318 |
|
|
319 |
return v; |
return v; |
323 |
and WIDTH. Assert-fails if the given NAME would duplicate |
and WIDTH. Assert-fails if the given NAME would duplicate |
324 |
that of an existing variable in the dictionary. */ |
that of an existing variable in the dictionary. */ |
325 |
struct variable * |
struct variable * |
326 |
dict_create_var_assert (struct dictionary *d, const char *longname, int width) |
dict_create_var_assert (struct dictionary *d, const char *name, int width) |
327 |
{ |
{ |
328 |
struct variable *v = dict_create_var (d, longname, width); |
struct variable *v = dict_create_var (d, name, width); |
329 |
assert (v != NULL); |
assert (v != NULL); |
330 |
return v; |
return v; |
331 |
} |
} |
332 |
|
|
333 |
/* Creates a new variable in D with longname LONGNAME, as a copy of |
/* Creates and returns a new variable in D with name NAME, as a |
334 |
existing variable OV, which need not be in D or in any |
copy of existing variable OV, which need not be in D or in any |
335 |
dictionary. |
dictionary. Returns a null pointer if the given NAME would |
336 |
If SHORTNAME is non null, it will be used as the short name |
duplicate that of an existing variable in the dictionary. */ |
|
otherwise a new short name will be generated. |
|
|
*/ |
|
337 |
struct variable * |
struct variable * |
338 |
dict_clone_var (struct dictionary *d, const struct variable *ov, |
dict_clone_var (struct dictionary *d, const struct variable *ov, |
339 |
const char *name, const char *longname) |
const char *name) |
340 |
{ |
{ |
341 |
struct variable *nv; |
struct variable *nv; |
342 |
|
|
343 |
assert (d != NULL); |
assert (d != NULL); |
344 |
assert (ov != NULL); |
assert (ov != NULL); |
345 |
assert (strlen (longname) <= LONG_NAME_LEN); |
assert (name != NULL); |
|
|
|
|
struct name_table_entry *nte = xmalloc (sizeof (struct name_table_entry)); |
|
|
|
|
|
nte->longname = strdup(longname); |
|
|
if ( name ) |
|
|
{ |
|
|
assert (strlen (name) >= 1); |
|
|
assert (strlen (name) <= SHORT_NAME_LEN); |
|
|
nte->name = strdup(name); |
|
|
} |
|
|
else |
|
|
nte->name = make_short_name(d, longname); |
|
346 |
|
|
347 |
|
assert (strlen (name) >= 1); |
348 |
|
assert (strlen (name) <= LONG_NAME_LEN); |
349 |
|
|
350 |
nv = dict_create_var_from_short (d, nte->name, ov->width); |
nv = dict_create_var (d, name, ov->width); |
351 |
if (nv == NULL) |
if (nv == NULL) |
352 |
return NULL; |
return NULL; |
353 |
|
|
354 |
hsh_insert(d->long_name_tab, nte); |
/* Copy most members not copied via dict_create_var(). |
355 |
nv->longname = nte->longname; |
short_name[] is intentionally not copied, because there is |
356 |
|
no reason to give a new variable with potentially a new name |
357 |
|
the same short name. */ |
358 |
nv->init = 1; |
nv->init = 1; |
359 |
nv->reinit = ov->reinit; |
nv->reinit = ov->reinit; |
360 |
nv->miss_type = ov->miss_type; |
nv->miss_type = ov->miss_type; |
365 |
nv->val_labs = val_labs_copy (ov->val_labs); |
nv->val_labs = val_labs_copy (ov->val_labs); |
366 |
if (ov->label != NULL) |
if (ov->label != NULL) |
367 |
nv->label = xstrdup (ov->label); |
nv->label = xstrdup (ov->label); |
|
|
|
|
nv->alignment = ov->alignment; |
|
368 |
nv->measure = ov->measure; |
nv->measure = ov->measure; |
369 |
nv->display_width = ov->display_width; |
nv->display_width = ov->display_width; |
370 |
|
nv->alignment = ov->alignment; |
371 |
|
|
372 |
return nv; |
return nv; |
373 |
} |
} |
374 |
|
|
375 |
/* Changes the name of V in D to name NEW_NAME. Assert-fails if |
/* Creates and returns a new variable in D with name NAME, as a |
376 |
a variable named NEW_NAME is already in D, except that |
copy of existing variable OV, which need not be in D or in any |
377 |
NEW_NAME may be the same as V's existing name. */ |
dictionary. Assert-fails if the given NAME would duplicate |
378 |
void |
that of an existing variable in the dictionary. */ |
379 |
dict_rename_var (struct dictionary *d, struct variable *v, |
struct variable * |
380 |
const char *new_name) |
dict_clone_var_assert (struct dictionary *d, const struct variable *ov, |
381 |
|
const char *name) |
382 |
{ |
{ |
383 |
assert (d != NULL); |
struct variable *v = dict_clone_var (d, ov, name); |
384 |
assert (v != NULL); |
assert (v != NULL); |
385 |
assert (new_name != NULL); |
return v; |
|
assert (strlen (new_name) >= 1 && strlen (new_name) <= SHORT_NAME_LEN); |
|
|
assert (dict_contains_var (d, v)); |
|
|
|
|
|
if (!strcmp (v->name, new_name)) |
|
|
return; |
|
|
|
|
|
assert (dict_lookup_var (d, new_name) == NULL); |
|
|
|
|
|
hsh_force_delete (d->name_tab, v); |
|
|
strncpy (v->name, new_name, sizeof v->name); |
|
|
v->name[SHORT_NAME_LEN] = '\0'; |
|
|
hsh_force_insert (d->name_tab, v); |
|
|
dict_add_longvar_entry (d, new_name, new_name); |
|
386 |
} |
} |
387 |
|
|
388 |
/* Returns the variable named NAME in D, or a null pointer if no |
/* Returns the variable named NAME in D, or a null pointer if no |
391 |
dict_lookup_var (const struct dictionary *d, const char *name) |
dict_lookup_var (const struct dictionary *d, const char *name) |
392 |
{ |
{ |
393 |
struct variable v; |
struct variable v; |
|
struct variable *vr; |
|
394 |
|
|
|
char *short_name; |
|
|
struct name_table_entry key; |
|
|
struct name_table_entry *nte; |
|
|
|
|
395 |
assert (d != NULL); |
assert (d != NULL); |
396 |
assert (name != NULL); |
assert (name != NULL); |
|
assert (strlen (name) >= 1 && strlen (name) <= LONG_NAME_LEN); |
|
|
|
|
|
key.longname = name; |
|
|
nte = hsh_find (d->long_name_tab, &key); |
|
|
|
|
|
if ( ! nte ) |
|
|
{ |
|
|
return 0; |
|
|
} |
|
397 |
|
|
398 |
short_name = nte->name ; |
st_trim_copy (v.name, name, sizeof v.name); |
399 |
|
return hsh_find (d->name_tab, &v); |
|
strncpy (v.name, short_name, sizeof v.name); |
|
|
v.name[SHORT_NAME_LEN] = '\0'; |
|
|
|
|
|
vr = hsh_find (d->name_tab, &v); |
|
|
|
|
|
return vr; |
|
400 |
} |
} |
401 |
|
|
|
|
|
402 |
/* Returns the variable named NAME in D. Assert-fails if no |
/* Returns the variable named NAME in D. Assert-fails if no |
403 |
variable has that name. */ |
variable has that name. */ |
404 |
struct variable * |
struct variable * |
496 |
dict_delete_var (d, *vars++); |
dict_delete_var (d, *vars++); |
497 |
} |
} |
498 |
|
|
499 |
|
/* Moves V to 0-based position IDX in D. Other variables in D, |
500 |
|
if any, retain their relative positions. Runs in time linear |
501 |
|
in the distance moved. */ |
502 |
|
void |
503 |
|
dict_reorder_var (struct dictionary *d, struct variable *v, |
504 |
|
size_t new_index) |
505 |
|
{ |
506 |
|
size_t min_idx, max_idx; |
507 |
|
size_t i; |
508 |
|
|
509 |
|
assert (d != NULL); |
510 |
|
assert (v != NULL); |
511 |
|
assert (dict_contains_var (d, v)); |
512 |
|
assert (new_index < d->var_cnt); |
513 |
|
|
514 |
|
move_element (d->var, d->var_cnt, sizeof *d->var, v->index, new_index); |
515 |
|
|
516 |
|
min_idx = min (v->index, new_index); |
517 |
|
max_idx = max (v->index, new_index); |
518 |
|
for (i = min_idx; i <= max_idx; i++) |
519 |
|
d->var[i]->index = i; |
520 |
|
} |
521 |
|
|
522 |
/* Reorders the variables in D, placing the COUNT variables |
/* Reorders the variables in D, placing the COUNT variables |
523 |
listed in ORDER in that order at the beginning of D. The |
listed in ORDER in that order at the beginning of D. The |
524 |
other variables in D, if any, retain their relative |
other variables in D, if any, retain their relative |
554 |
d->var = new_var; |
d->var = new_var; |
555 |
} |
} |
556 |
|
|
557 |
|
/* Changes the name of V in D to name NEW_NAME. Assert-fails if |
558 |
|
a variable named NEW_NAME is already in D, except that |
559 |
|
NEW_NAME may be the same as V's existing name. */ |
560 |
|
void |
561 |
|
dict_rename_var (struct dictionary *d, struct variable *v, |
562 |
|
const char *new_name) |
563 |
|
{ |
564 |
|
assert (d != NULL); |
565 |
|
assert (v != NULL); |
566 |
|
assert (new_name != NULL); |
567 |
|
assert (var_is_valid_name (new_name, false)); |
568 |
|
assert (dict_contains_var (d, v)); |
569 |
|
assert (!compare_var_names (v->name, new_name, NULL) |
570 |
|
|| dict_lookup_var (d, new_name) == NULL); |
571 |
|
|
572 |
|
hsh_force_delete (d->name_tab, v); |
573 |
|
st_trim_copy (v->name, new_name, sizeof v->name); |
574 |
|
hsh_force_insert (d->name_tab, v); |
575 |
|
|
576 |
|
if (get_algorithm () == ENHANCED) |
577 |
|
var_clear_short_name (v); |
578 |
|
} |
579 |
|
|
580 |
/* Renames COUNT variables specified in VARS to the names given |
/* Renames COUNT variables specified in VARS to the names given |
581 |
in NEW_NAMES within dictionary D. If the renaming would |
in NEW_NAMES within dictionary D. If the renaming would |
582 |
result in a duplicate variable name, returns zero and stores a |
result in a duplicate variable name, returns zero and stores a |
592 |
size_t i; |
size_t i; |
593 |
int success = 1; |
int success = 1; |
594 |
|
|
|
|
|
595 |
assert (d != NULL); |
assert (d != NULL); |
596 |
assert (count == 0 || vars != NULL); |
assert (count == 0 || vars != NULL); |
597 |
assert (count == 0 || new_names != NULL); |
assert (count == 0 || new_names != NULL); |
598 |
|
|
599 |
|
/* Remove the variables to be renamed from the name hash, |
600 |
|
save their names, and rename them. */ |
601 |
old_names = xmalloc (count * sizeof *old_names); |
old_names = xmalloc (count * sizeof *old_names); |
602 |
for (i = 0; i < count; i++) |
for (i = 0; i < count; i++) |
603 |
{ |
{ |
604 |
assert (d->var[vars[i]->index] == vars[i]); |
assert (d->var[vars[i]->index] == vars[i]); |
605 |
|
assert (var_is_valid_name (new_names[i], false)); |
606 |
hsh_force_delete (d->name_tab, vars[i]); |
hsh_force_delete (d->name_tab, vars[i]); |
607 |
old_names[i] = xstrdup (vars[i]->name); |
old_names[i] = xstrdup (vars[i]->name); |
608 |
|
strcpy (vars[i]->name, new_names[i]); |
609 |
} |
} |
610 |
|
|
611 |
|
/* Add the renamed variables back into the name hash, |
612 |
|
checking for conflicts. */ |
613 |
for (i = 0; i < count; i++) |
for (i = 0; i < count; i++) |
614 |
{ |
{ |
|
char *sn; |
|
|
struct name_table_entry key; |
|
|
struct name_table_entry *nte; |
|
615 |
assert (new_names[i] != NULL); |
assert (new_names[i] != NULL); |
616 |
assert (*new_names[i] != '\0'); |
assert (*new_names[i] != '\0'); |
617 |
assert (strlen (new_names[i]) <= LONG_NAME_LEN ); |
assert (strlen (new_names[i]) >= 1); |
618 |
|
assert (strlen (new_names[i]) <= LONG_NAME_LEN); |
|
sn = make_short_name(d, new_names[i]); |
|
|
strncpy(vars[i]->name, sn, SHORT_NAME_LEN + 1); |
|
|
free(sn); |
|
|
|
|
|
|
|
|
|
|
|
key.longname = vars[i]->longname; |
|
|
nte = hsh_find (d->long_name_tab, &key); |
|
|
|
|
|
free( nte->longname ) ; |
|
|
nte->longname = strdup ( new_names[i]); |
|
|
vars[i]->longname = nte->longname; |
|
619 |
|
|
620 |
if (hsh_insert (d->name_tab, vars[i]) != NULL ) |
if (hsh_insert (d->name_tab, vars[i]) != NULL) |
621 |
{ |
{ |
622 |
|
/* There is a name conflict. |
623 |
|
Back out all the name changes that have already |
624 |
|
taken place, and indicate failure. */ |
625 |
size_t fail_idx = i; |
size_t fail_idx = i; |
626 |
if (err_name != NULL) |
if (err_name != NULL) |
627 |
*err_name = new_names[i]; |
*err_name = new_names[i]; |
633 |
{ |
{ |
634 |
strcpy (vars[i]->name, old_names[i]); |
strcpy (vars[i]->name, old_names[i]); |
635 |
hsh_force_insert (d->name_tab, vars[i]); |
hsh_force_insert (d->name_tab, vars[i]); |
|
|
|
|
key.longname = vars[i]->longname; |
|
|
nte = hsh_find (d->long_name_tab, &key); |
|
|
|
|
|
free( nte->longname ) ; |
|
|
nte->longname = strdup ( old_names[i]); |
|
|
vars[i]->longname = nte->longname; |
|
|
|
|
636 |
} |
} |
637 |
|
|
638 |
success = 0; |
success = 0; |
639 |
break; |
goto done; |
640 |
} |
} |
641 |
} |
} |
642 |
|
|
643 |
|
/* Clear short names. */ |
644 |
|
if (get_algorithm () == ENHANCED) |
645 |
|
for (i = 0; i < count; i++) |
646 |
|
var_clear_short_name (vars[i]); |
647 |
|
|
648 |
|
done: |
649 |
|
/* Free the old names we kept around. */ |
650 |
for (i = 0; i < count; i++) |
for (i = 0; i < count; i++) |
651 |
free (old_names[i]); |
free (old_names[i]); |
652 |
free (old_names); |
free (old_names); |
957 |
struct variable **var, size_t cnt) |
struct variable **var, size_t cnt) |
958 |
{ |
{ |
959 |
struct vector *vector; |
struct vector *vector; |
960 |
|
size_t i; |
961 |
|
|
962 |
assert (d != NULL); |
assert (d != NULL); |
963 |
assert (name != NULL); |
assert (name != NULL); |
964 |
assert (strlen (name) > 0 && strlen (name) <= SHORT_NAME_LEN ); |
assert (var_is_valid_name (name, false)); |
965 |
assert (var != NULL); |
assert (var != NULL); |
966 |
assert (cnt > 0); |
assert (cnt > 0); |
967 |
|
|
971 |
d->vector = xrealloc (d->vector, (d->vector_cnt + 1) * sizeof *d->vector); |
d->vector = xrealloc (d->vector, (d->vector_cnt + 1) * sizeof *d->vector); |
972 |
vector = d->vector[d->vector_cnt] = xmalloc (sizeof *vector); |
vector = d->vector[d->vector_cnt] = xmalloc (sizeof *vector); |
973 |
vector->idx = d->vector_cnt++; |
vector->idx = d->vector_cnt++; |
974 |
strncpy (vector->name, name, SHORT_NAME_LEN); |
st_trim_copy (vector->name, name, sizeof vector->name); |
|
vector->name[SHORT_NAME_LEN] = '\0'; |
|
975 |
vector->var = xmalloc (cnt * sizeof *var); |
vector->var = xmalloc (cnt * sizeof *var); |
976 |
memcpy (vector->var, var, cnt * sizeof *var); |
for (i = 0; i < cnt; i++) |
977 |
|
{ |
978 |
|
assert (dict_contains_var (d, var[i])); |
979 |
|
vector->var[i] = var[i]; |
980 |
|
} |
981 |
vector->cnt = cnt; |
vector->cnt = cnt; |
982 |
|
|
983 |
return 1; |
return 1; |
1014 |
assert (name != NULL); |
assert (name != NULL); |
1015 |
|
|
1016 |
for (i = 0; i < d->vector_cnt; i++) |
for (i = 0; i < d->vector_cnt; i++) |
1017 |
if (!strcmp (d->vector[i]->name, name)) |
if (!strcasecmp (d->vector[i]->name, name)) |
1018 |
return d->vector[i]; |
return d->vector[i]; |
1019 |
return NULL; |
return NULL; |
1020 |
} |
} |
1037 |
d->vector_cnt = 0; |
d->vector_cnt = 0; |
1038 |
} |
} |
1039 |
|
|
1040 |
|
/* Compares two strings. */ |
1041 |
static const char * quasi_base27(int i); |
static int |
1042 |
|
compare_strings (const void *a, const void *b, void *aux UNUSED) |
|
|
|
|
/* Convert I to quasi base 27 |
|
|
The result is a staticly allocated string. |
|
|
*/ |
|
|
static const char * |
|
|
quasi_base27(int i) |
|
1043 |
{ |
{ |
1044 |
static char result[SHORT_NAME_LEN + 1]; |
return strcmp (a, b); |
|
static char reverse[SHORT_NAME_LEN + 1]; |
|
|
|
|
|
/* FIXME: check the result cant overflow these arrays */ |
|
|
|
|
|
char *s = result ; |
|
|
const int radix = 27; |
|
|
int units; |
|
|
|
|
|
/* and here's the quasi-ness of this routine */ |
|
|
i = i + ( i / radix ); |
|
|
|
|
|
strcpy(result,""); |
|
|
do { |
|
|
units = i % radix; |
|
|
*s++ = (units > 0 ) ? units + 'A' - 1 : 'A'; |
|
|
i = i / radix; |
|
|
} while (i > 0 ) ; |
|
|
*s = '\0'; |
|
|
|
|
|
/* Reverse the result */ |
|
|
i = strlen(result); |
|
|
s = reverse; |
|
|
while(i >= 0) |
|
|
*s++ = result[--i]; |
|
|
*s = '\0'; |
|
|
|
|
|
return reverse; |
|
1045 |
} |
} |
1046 |
|
|
1047 |
|
/* Hashes a string. */ |
1048 |
/* Generate a short name, given a long name. |
static unsigned |
1049 |
The return value of this function must be freed by the caller. |
hash_string (const void *s, void *aux UNUSED) |
|
*/ |
|
|
static char * |
|
|
make_short_name(struct dictionary *dict, const char *longname) |
|
1050 |
{ |
{ |
1051 |
int i = 0; |
return hsh_hash_string (s); |
1052 |
char *p; |
} |
|
|
|
|
|
|
|
char *d = xmalloc ( SHORT_NAME_LEN + 1); |
|
1053 |
|
|
1054 |
/* Truncate the name */ |
/* Assigns a valid, unique short_name[] to each variable in D. |
1055 |
strncpy(d, longname, SHORT_NAME_LEN); |
Each variable whose actual name is short has highest priority |
1056 |
d[SHORT_NAME_LEN] = '\0'; |
for that short name. Otherwise, variables with an existing |
1057 |
|
short_name[] have the next highest priority for a given short |
1058 |
|
name; if it is already taken, then the variable is treated as |
1059 |
|
if short_name[] had been empty. Otherwise, long names are |
1060 |
|
truncated to form short names. If that causes conflicts, |
1061 |
|
variables are renamed as PREFIX_A, PREFIX_B, and so on. */ |
1062 |
|
void |
1063 |
|
dict_assign_short_names (struct dictionary *d) |
1064 |
|
{ |
1065 |
|
struct hsh_table *short_names; |
1066 |
|
size_t i; |
1067 |
|
|
1068 |
/* Convert to upper case */ |
/* Give variables whose names are short the corresponding short |
1069 |
for ( p = d; *p ; ++p ) |
names, and clear short_names[] that conflict with a variable |
1070 |
*p = toupper(*p); |
name. */ |
1071 |
|
for (i = 0; i < d->var_cnt; i++) |
1072 |
|
{ |
1073 |
|
struct variable *v = d->var[i]; |
1074 |
|
if (strlen (v->name) <= SHORT_NAME_LEN) |
1075 |
|
var_set_short_name (v, v->name); |
1076 |
|
else if (dict_lookup_var (d, v->short_name) != NULL) |
1077 |
|
var_clear_short_name (v); |
1078 |
|
} |
1079 |
|
|
1080 |
/* If a variable with that name already exists, then munge it |
/* Each variable with an assigned short_name[] now gets it |
1081 |
until there's no conflict */ |
unless there is a conflict. */ |
1082 |
while (0 != hsh_find (dict->name_tab, d)) |
short_names = hsh_create (d->var_cnt, compare_strings, hash_string, |
1083 |
{ |
NULL, NULL); |
1084 |
const char *suffix = quasi_base27(i++); |
for (i = 0; i < d->var_cnt; i++) |
1085 |
|
{ |
1086 |
|
struct variable *v = d->var[i]; |
1087 |
|
if (v->short_name[0] && hsh_insert (short_names, v->short_name) != NULL) |
1088 |
|
var_clear_short_name (v); |
1089 |
|
} |
1090 |
|
|
1091 |
|
/* Now assign short names to remaining variables. */ |
1092 |
|
for (i = 0; i < d->var_cnt; i++) |
1093 |
|
{ |
1094 |
|
struct variable *v = d->var[i]; |
1095 |
|
if (v->short_name[0] == '\0') |
1096 |
|
{ |
1097 |
|
int sfx; |
1098 |
|
|
1099 |
d[SHORT_NAME_LEN - strlen(suffix) - 1 ] = '_'; |
/* Form initial short_name. */ |
1100 |
d[SHORT_NAME_LEN - strlen(suffix) ] = '\0'; |
var_set_short_name (v, v->name); |
|
strcat(d, suffix); |
|
|
} |
|
1101 |
|
|
1102 |
|
/* Try _A, _B, ... _AA, _AB, etc., if needed. */ |
1103 |
|
for (sfx = 0; hsh_insert (short_names, v->short_name) != NULL; sfx++) |
1104 |
|
var_set_short_name_suffix (v, v->name, sfx); |
1105 |
|
} |
1106 |
|
} |
1107 |
|
|
1108 |
return d; |
/* Get rid of hash table. */ |
1109 |
|
hsh_destroy (short_names); |
1110 |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|