356 |
} |
} |
357 |
} |
} |
358 |
|
|
359 |
static int rename_variables (struct dictionary *dict); |
static bool rename_variables (struct dictionary *dict); |
360 |
|
static bool drop_variables (struct dictionary *dict); |
361 |
|
static bool keep_variables (struct dictionary *dict); |
362 |
|
|
363 |
/* Commands that read and write system files share a great deal |
/* Commands that read and write system files share a great deal |
364 |
of common syntactic structure for rearranging and dropping |
of common syntactic structure for rearranging and dropping |
395 |
|
|
396 |
while (lex_match ('/')) |
while (lex_match ('/')) |
397 |
{ |
{ |
398 |
|
bool ok = true; |
399 |
|
|
400 |
if (op == OP_SAVE && lex_match_id ("COMPRESSED")) |
if (op == OP_SAVE && lex_match_id ("COMPRESSED")) |
401 |
*compress = 1; |
*compress = 1; |
402 |
else if (op == OP_SAVE && lex_match_id ("UNCOMPRESSED")) |
else if (op == OP_SAVE && lex_match_id ("UNCOMPRESSED")) |
403 |
*compress = 0; |
*compress = 0; |
404 |
else if (lex_match_id ("DROP")) |
else if (lex_match_id ("DROP")) |
405 |
{ |
ok = drop_variables (dict); |
|
struct variable **v; |
|
|
int nv; |
|
|
|
|
|
lex_match ('='); |
|
|
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
|
|
return false; |
|
|
dict_delete_vars (dict, v, nv); |
|
|
free (v); |
|
|
} |
|
406 |
else if (lex_match_id ("KEEP")) |
else if (lex_match_id ("KEEP")) |
407 |
{ |
ok = keep_variables (dict); |
|
struct variable **v; |
|
|
int nv; |
|
|
int i; |
|
|
|
|
|
lex_match ('='); |
|
|
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
|
|
return false; |
|
|
|
|
|
/* Move the specified variables to the beginning. */ |
|
|
dict_reorder_vars (dict, v, nv); |
|
|
|
|
|
/* Delete the remaining variables. */ |
|
|
v = xrealloc (v, (dict_get_var_cnt (dict) - nv) * sizeof *v); |
|
|
for (i = nv; i < dict_get_var_cnt (dict); i++) |
|
|
v[i - nv] = dict_get_var (dict, i); |
|
|
dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv); |
|
|
free (v); |
|
|
} |
|
408 |
else if (lex_match_id ("RENAME")) |
else if (lex_match_id ("RENAME")) |
409 |
{ |
ok = rename_variables (dict); |
|
if (!rename_variables (dict)) |
|
|
return false; |
|
|
} |
|
410 |
else |
else |
411 |
{ |
{ |
412 |
lex_error (_("while expecting a valid subcommand")); |
lex_error (_("expecting a valid subcommand")); |
413 |
return false; |
ok = false; |
414 |
} |
} |
415 |
|
|
416 |
if (dict_get_var_cnt (dict) == 0) |
if (!ok) |
417 |
{ |
return false; |
|
msg (SE, _("All variables deleted from system file dictionary.")); |
|
|
return false; |
|
|
} |
|
418 |
} |
} |
419 |
|
|
420 |
if (!lex_end_of_command ()) |
if (!lex_end_of_command ()) |
425 |
} |
} |
426 |
|
|
427 |
/* Parses and performs the RENAME subcommand of GET and SAVE. */ |
/* Parses and performs the RENAME subcommand of GET and SAVE. */ |
428 |
static int |
static bool |
429 |
rename_variables (struct dictionary *dict) |
rename_variables (struct dictionary *dict) |
430 |
{ |
{ |
431 |
int i; |
int i; |
512 |
|
|
513 |
return success; |
return success; |
514 |
} |
} |
515 |
|
|
516 |
|
/* Parses and performs the DROP subcommand of GET and SAVE. |
517 |
|
Returns true if successful, false on failure.*/ |
518 |
|
static bool |
519 |
|
drop_variables (struct dictionary *dict) |
520 |
|
{ |
521 |
|
struct variable **v; |
522 |
|
int nv; |
523 |
|
|
524 |
|
lex_match ('='); |
525 |
|
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
526 |
|
return false; |
527 |
|
dict_delete_vars (dict, v, nv); |
528 |
|
free (v); |
529 |
|
|
530 |
|
if (dict_get_var_cnt (dict) == 0) |
531 |
|
{ |
532 |
|
msg (SE, _("Cannot DROP all variables from dictionary.")); |
533 |
|
return false; |
534 |
|
} |
535 |
|
return true; |
536 |
|
} |
537 |
|
|
538 |
|
/* Parses and performs the KEEP subcommand of GET and SAVE. |
539 |
|
Returns true if successful, false on failure.*/ |
540 |
|
static bool |
541 |
|
keep_variables (struct dictionary *dict) |
542 |
|
{ |
543 |
|
struct variable **v; |
544 |
|
int nv; |
545 |
|
int i; |
546 |
|
|
547 |
|
lex_match ('='); |
548 |
|
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
549 |
|
return false; |
550 |
|
|
551 |
|
/* Move the specified variables to the beginning. */ |
552 |
|
dict_reorder_vars (dict, v, nv); |
553 |
|
|
554 |
|
/* Delete the remaining variables. */ |
555 |
|
v = xrealloc (v, (dict_get_var_cnt (dict) - nv) * sizeof *v); |
556 |
|
for (i = nv; i < dict_get_var_cnt (dict); i++) |
557 |
|
v[i - nv] = dict_get_var (dict, i); |
558 |
|
dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv); |
559 |
|
free (v); |
560 |
|
|
561 |
|
return true; |
562 |
|
} |
563 |
|
|
564 |
/* EXPORT procedure. */ |
/* EXPORT procedure. */ |
565 |
struct export_proc |
struct export_proc |
669 |
struct file_handle *handle; /* File handle. */ |
struct file_handle *handle; /* File handle. */ |
670 |
struct sfm_reader *reader; /* System file reader. */ |
struct sfm_reader *reader; /* System file reader. */ |
671 |
struct dictionary *dict; /* Dictionary from system file. */ |
struct dictionary *dict; /* Dictionary from system file. */ |
672 |
char in[SHORT_NAME_LEN + 1]; /* Name of the variable from IN=. */ |
|
673 |
|
/* IN subcommand. */ |
674 |
|
char *in_name; /* Variable name. */ |
675 |
|
struct variable *in_var; /* Variable (in master dictionary). */ |
676 |
|
|
677 |
struct ccase input; /* Input record. */ |
struct ccase input; /* Input record. */ |
678 |
}; |
}; |
716 |
{ |
{ |
717 |
struct mtf_proc mtf; |
struct mtf_proc mtf; |
718 |
struct mtf_file *first_table = NULL; |
struct mtf_file *first_table = NULL; |
719 |
|
struct mtf_file *iter; |
720 |
|
|
721 |
bool used_active_file = false; |
bool used_active_file = false; |
722 |
bool saw_table = false; |
bool saw_table = false; |
723 |
|
bool saw_in = false; |
724 |
|
|
725 |
mtf.head = mtf.tail = NULL; |
mtf.head = mtf.tail = NULL; |
726 |
mtf.by_cnt = 0; |
mtf.by_cnt = 0; |
752 |
file->handle = NULL; |
file->handle = NULL; |
753 |
file->reader = NULL; |
file->reader = NULL; |
754 |
file->dict = NULL; |
file->dict = NULL; |
755 |
file->in[0] = '\0'; |
file->in_name = NULL; |
756 |
|
file->in_var = NULL; |
757 |
case_nullify (&file->input); |
case_nullify (&file->input); |
758 |
|
|
759 |
/* FILEs go first, then TABLEs. */ |
/* FILEs go first, then TABLEs. */ |
843 |
goto error; |
goto error; |
844 |
} |
} |
845 |
|
|
846 |
if (file->in[0]) |
if (file->in_name != NULL) |
847 |
{ |
{ |
848 |
msg (SE, _("Multiple IN subcommands for a single FILE or " |
msg (SE, _("Multiple IN subcommands for a single FILE or " |
849 |
"TABLE.")); |
"TABLE.")); |
850 |
goto error; |
goto error; |
851 |
} |
} |
852 |
strcpy (file->in, tokid); |
file->in_name = xstrdup (tokid); |
853 |
lex_get (); |
lex_get (); |
854 |
|
saw_in = true; |
855 |
} |
} |
856 |
|
|
857 |
mtf_merge_dictionary (mtf.dict, file); |
mtf_merge_dictionary (mtf.dict, file); |
858 |
} |
} |
859 |
|
|
860 |
while (token != '.') |
while (token != '.') |
861 |
{ |
{ |
862 |
if (lex_match (T_BY)) |
if (lex_match (T_BY)) |
863 |
{ |
{ |
864 |
struct variable **by; |
struct variable **by; |
|
struct mtf_file *iter; |
|
865 |
|
|
866 |
if (mtf.by_cnt) |
if (mtf.by_cnt) |
867 |
{ |
{ |
926 |
{ |
{ |
927 |
/* FIXME. */ |
/* FIXME. */ |
928 |
} |
} |
929 |
|
else if (lex_match_id ("DROP")) |
930 |
|
{ |
931 |
|
if (!drop_variables (mtf.dict)) |
932 |
|
goto error; |
933 |
|
} |
934 |
|
else if (lex_match_id ("KEEP")) |
935 |
|
{ |
936 |
|
if (!keep_variables (mtf.dict)) |
937 |
|
goto error; |
938 |
|
} |
939 |
else |
else |
940 |
{ |
{ |
941 |
lex_error (NULL); |
lex_error (NULL); |
949 |
} |
} |
950 |
} |
} |
951 |
|
|
952 |
if (mtf.by_cnt == 0 && saw_table) |
if (mtf.by_cnt == 0) |
953 |
|
{ |
954 |
|
if (saw_table) |
955 |
|
{ |
956 |
|
msg (SE, _("BY is required when TABLE is specified.")); |
957 |
|
goto error; |
958 |
|
} |
959 |
|
if (saw_in) |
960 |
|
{ |
961 |
|
msg (SE, _("BY is required when IN is specified.")); |
962 |
|
goto error; |
963 |
|
} |
964 |
|
} |
965 |
|
|
966 |
|
for (iter = mtf.head; iter != NULL; iter = iter->next) |
967 |
{ |
{ |
968 |
msg (SE, _("BY is required when TABLE is specified.")); |
struct dictionary *d = iter->dict; |
969 |
goto error; |
int i; |
970 |
|
|
971 |
|
for (i = 0; i < dict_get_var_cnt (d); i++) |
972 |
|
{ |
973 |
|
struct variable *v = dict_get_var (d, i); |
974 |
|
struct variable *mv = dict_lookup_var (mtf.dict, v->name); |
975 |
|
if (mv != NULL) |
976 |
|
set_master (v, mv); |
977 |
|
} |
978 |
} |
} |
979 |
|
|
980 |
|
for (iter = mtf.head; iter != NULL; iter = iter->next) |
981 |
|
if (iter->in_name != NULL) |
982 |
|
{ |
983 |
|
static const struct fmt_spec f1_0 = {FMT_F, 1, 0}; |
984 |
|
|
985 |
|
iter->in_var = dict_create_var (mtf.dict, iter->in_name, 0); |
986 |
|
if (iter->in_var == NULL) |
987 |
|
{ |
988 |
|
msg (SE, _("IN variable name %s duplicates an " |
989 |
|
"existing variable name."), |
990 |
|
iter->in_var); |
991 |
|
goto error; |
992 |
|
} |
993 |
|
iter->in_var->print = iter->in_var->write = f1_0; |
994 |
|
} |
995 |
|
|
996 |
/* MATCH FILES performs an n-way merge on all its input files. |
/* MATCH FILES performs an n-way merge on all its input files. |
997 |
Abstract algorithm: |
Abstract algorithm: |
998 |
|
|
1000 |
|
|
1001 |
2. If no FILEs are left, stop. Otherwise, proceed to step 3. |
2. If no FILEs are left, stop. Otherwise, proceed to step 3. |
1002 |
|
|
1003 |
3. Find the FILE input record with minimum BY values. Store all |
3. Find the FILE input record(s) that have minimum BY |
1004 |
the values from this input record into the output record. |
values. Store all the values from these input records into |
|
|
|
|
4. Find all the FILE input records with BY values identical to |
|
|
the minimums. Store all the values from these input records into |
|
1005 |
the output record. |
the output record. |
1006 |
|
|
1007 |
5. For every TABLE, read another record as long as the BY values |
4. For every TABLE, read another record as long as the BY values |
1008 |
on the TABLE's input record are less than the FILEs' BY values. |
on the TABLE's input record are less than the FILEs' BY values. |
1009 |
If an exact match is found, store all the values from the TABLE |
If an exact match is found, store all the values from the TABLE |
1010 |
input record into the output record. |
input record into the output record. |
1011 |
|
|
1012 |
6. Write the output record. |
5. Write the output record. |
1013 |
|
|
1014 |
7. Read another record from each input file FILE and TABLE that |
6. Read another record from each input file FILE and TABLE that |
1015 |
we stored values from above. If we come to the end of one of the |
we stored values from above. If we come to the end of one of the |
1016 |
input files, remove it from the list of input files. |
input files, remove it from the list of input files. |
1017 |
|
|
1018 |
8. Repeat from step 2. |
7. Repeat from step 2. |
1019 |
|
|
1020 |
Unfortunately, this algorithm can't be directly implemented |
Unfortunately, this algorithm can't be implemented in a |
1021 |
because there's no function to read a record from the active |
straightforward way because there's no function to read a |
1022 |
file; instead, it has to be done using callbacks. |
record from the active file. Instead, it has to be written |
1023 |
|
as a state machine. |
1024 |
|
|
1025 |
FIXME: For merging large numbers of files (more than 10?) a |
FIXME: For merging large numbers of files (more than 10?) a |
1026 |
better algorithm would use a heap for finding minimum |
better algorithm would use a heap for finding minimum |
1029 |
if (!used_active_file) |
if (!used_active_file) |
1030 |
discard_variables (); |
discard_variables (); |
1031 |
|
|
1032 |
|
dict_compact_values (mtf.dict); |
1033 |
mtf.sink = create_case_sink (&storage_sink_class, mtf.dict, NULL); |
mtf.sink = create_case_sink (&storage_sink_class, mtf.dict, NULL); |
1034 |
if (mtf.sink->class->open != NULL) |
if (mtf.sink->class->open != NULL) |
1035 |
mtf.sink->class->open (mtf.sink); |
mtf.sink->class->open (mtf.sink); |
1036 |
|
|
1037 |
mtf.seq_nums = xmalloc (dict_get_var_cnt (mtf.dict) * sizeof *mtf.seq_nums); |
mtf.seq_nums = xcalloc (dict_get_var_cnt (mtf.dict) * sizeof *mtf.seq_nums); |
|
memset (mtf.seq_nums, 0, |
|
|
dict_get_var_cnt (mtf.dict) * sizeof *mtf.seq_nums); |
|
1038 |
case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict)); |
case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict)); |
1039 |
|
|
1040 |
mtf_read_nonactive_records (&mtf); |
mtf_read_nonactive_records (&mtf); |
1056 |
return CMD_FAILURE; |
return CMD_FAILURE; |
1057 |
} |
} |
1058 |
|
|
1059 |
/* Repeats 2...8 an arbitrary number of times. */ |
/* Repeats 2...7 an arbitrary number of times. */ |
1060 |
static void |
static void |
1061 |
mtf_processing_finish (void *mtf_) |
mtf_processing_finish (void *mtf_) |
1062 |
{ |
{ |
1107 |
if (file->dict != default_dict) |
if (file->dict != default_dict) |
1108 |
dict_destroy (file->dict); |
dict_destroy (file->dict); |
1109 |
case_destroy (&file->input); |
case_destroy (&file->input); |
1110 |
|
free (file->in_name); |
1111 |
free (file); |
free (file); |
1112 |
} |
} |
1113 |
|
|
1120 |
for (iter = mtf->head; iter; iter = next) |
for (iter = mtf->head; iter; iter = next) |
1121 |
{ |
{ |
1122 |
next = iter->next; |
next = iter->next; |
|
|
|
1123 |
mtf_free_file (iter); |
mtf_free_file (iter); |
1124 |
} |
} |
1125 |
|
|
1135 |
mtf_delete_file_in_place (struct mtf_proc *mtf, struct mtf_file **file) |
mtf_delete_file_in_place (struct mtf_proc *mtf, struct mtf_file **file) |
1136 |
{ |
{ |
1137 |
struct mtf_file *f = *file; |
struct mtf_file *f = *file; |
1138 |
|
int i; |
1139 |
|
|
1140 |
if (f->prev) |
if (f->prev) |
1141 |
f->prev->next = f->next; |
f->prev->next = f->next; |
1147 |
mtf->tail = f->prev; |
mtf->tail = f->prev; |
1148 |
*file = f->next; |
*file = f->next; |
1149 |
|
|
1150 |
{ |
if (f->in_var != NULL) |
1151 |
int i; |
case_data_rw (&mtf->mtf_case, f->in_var->fv)->f = 0.; |
1152 |
|
for (i = 0; i < dict_get_var_cnt (f->dict); i++) |
1153 |
for (i = 0; i < dict_get_var_cnt (f->dict); i++) |
{ |
1154 |
{ |
struct variable *v = dict_get_var (f->dict, i); |
1155 |
struct variable *v = dict_get_var (f->dict, i); |
struct variable *mv = get_master (v); |
1156 |
union value *out = case_data_rw (&mtf->mtf_case, get_master (v)->fv); |
if (mv != NULL) |
1157 |
|
{ |
1158 |
|
union value *out = case_data_rw (&mtf->mtf_case, mv->fv); |
1159 |
|
|
1160 |
if (v->type == NUMERIC) |
if (v->type == NUMERIC) |
1161 |
out->f = SYSMIS; |
out->f = SYSMIS; |
1162 |
else |
else |
1163 |
memset (out->s, ' ', v->width); |
memset (out->s, ' ', v->width); |
1164 |
} |
} |
1165 |
} |
} |
1166 |
|
|
1167 |
mtf_free_file (f); |
mtf_free_file (f); |
1168 |
} |
} |
1172 |
mtf_read_nonactive_records (void *mtf_) |
mtf_read_nonactive_records (void *mtf_) |
1173 |
{ |
{ |
1174 |
struct mtf_proc *mtf = mtf_; |
struct mtf_proc *mtf = mtf_; |
1175 |
struct mtf_file *iter; |
struct mtf_file *iter, *next; |
1176 |
|
|
1177 |
for (iter = mtf->head; iter; ) |
for (iter = mtf->head; iter != NULL; iter = next) |
1178 |
{ |
{ |
1179 |
if (iter->handle) |
next = iter->next; |
1180 |
{ |
if (iter->handle && !sfm_read_case (iter->reader, &iter->input)) |
1181 |
if (!sfm_read_case (iter->reader, &iter->input)) |
mtf_delete_file_in_place (mtf, &iter); |
|
mtf_delete_file_in_place (mtf, &iter); |
|
|
else |
|
|
iter = iter->next; |
|
|
} |
|
|
else |
|
|
iter = iter->next; |
|
1182 |
} |
} |
1183 |
} |
} |
1184 |
|
|
1189 |
struct mtf_file *a, struct mtf_file *b, |
struct mtf_file *a, struct mtf_file *b, |
1190 |
struct ccase *c) |
struct ccase *c) |
1191 |
{ |
{ |
1192 |
struct ccase *a_input, *b_input; |
struct ccase *ca = case_is_null (&a->input) ? c : &a->input; |
1193 |
int i; |
struct ccase *cb = case_is_null (&b->input) ? c : &b->input; |
|
|
|
1194 |
assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1); |
assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1); |
1195 |
a_input = case_is_null (&a->input) ? c : &a->input; |
return case_compare_2dict (ca, cb, a->by, b->by, mtf->by_cnt); |
|
b_input = case_is_null (&b->input) ? c : &b->input; |
|
|
for (i = 0; i < mtf->by_cnt; i++) |
|
|
{ |
|
|
assert (a->by[i]->type == b->by[i]->type); |
|
|
assert (a->by[i]->width == b->by[i]->width); |
|
|
|
|
|
if (a->by[i]->type == NUMERIC) |
|
|
{ |
|
|
double af = case_num (a_input, a->by[i]->fv); |
|
|
double bf = case_num (b_input, b->by[i]->fv); |
|
|
|
|
|
if (af < bf) |
|
|
return -1; |
|
|
else if (af > bf) |
|
|
return 1; |
|
|
} |
|
|
else |
|
|
{ |
|
|
int result; |
|
|
|
|
|
assert (a->by[i]->type == ALPHA); |
|
|
result = memcmp (case_str (a_input, a->by[i]->fv), |
|
|
case_str (b_input, b->by[i]->fv), |
|
|
a->by[i]->width); |
|
|
if (result < 0) |
|
|
return -1; |
|
|
else if (result > 0) |
|
|
return 1; |
|
|
} |
|
|
} |
|
|
return 0; |
|
1196 |
} |
} |
1197 |
|
|
1198 |
/* Perform one iteration of steps 3...7 above. */ |
/* Perform one iteration of steps 3...7 above. */ |
1200 |
mtf_processing (struct ccase *c, void *mtf_) |
mtf_processing (struct ccase *c, void *mtf_) |
1201 |
{ |
{ |
1202 |
struct mtf_proc *mtf = mtf_; |
struct mtf_proc *mtf = mtf_; |
|
struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */ |
|
|
struct mtf_file *max_head, *max_tail; /* Files with non-minimum BY values. */ |
|
|
struct mtf_file *iter; /* Iterator. */ |
|
1203 |
|
|
1204 |
for (;;) |
/* Do we need another record from the active file? */ |
1205 |
|
bool read_active_file; |
1206 |
|
|
1207 |
|
assert (mtf->head != NULL); |
1208 |
|
assert (mtf->head->type == MTF_FILE); |
1209 |
|
do |
1210 |
{ |
{ |
1211 |
/* If the active file doesn't have the minimum BY values, don't |
struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */ |
1212 |
return because that would cause a record to be skipped. */ |
struct mtf_file *max_head, *max_tail; /* Files with non-minimum BYs. */ |
1213 |
bool advance = true; |
struct mtf_file *iter, *next; |
1214 |
|
|
1215 |
if (mtf->head->type == MTF_TABLE) |
read_active_file = false; |
|
return 0; |
|
1216 |
|
|
1217 |
/* 3. Find the FILE input record with minimum BY values. Store |
/* 3. Find the FILE input record(s) that have minimum BY |
1218 |
all the values from this input record into the output record. |
values. Store all the values from these input records into |
1219 |
|
the output record. */ |
|
4. Find all the FILE input records with BY values identical |
|
|
to the minimums. Store all the values from these input |
|
|
records into the output record. */ |
|
1220 |
min_head = min_tail = mtf->head; |
min_head = min_tail = mtf->head; |
1221 |
max_head = max_tail = NULL; |
max_head = max_tail = NULL; |
1222 |
for (iter = mtf->head->next; iter && iter->type == MTF_FILE; |
for (iter = mtf->head->next; iter && iter->type == MTF_FILE; |
1252 |
assert (0); |
assert (0); |
1253 |
} |
} |
1254 |
|
|
1255 |
/* 5. For every TABLE, read another record as long as the BY |
/* 4. For every TABLE, read another record as long as the BY |
1256 |
values on the TABLE's input record are less than the FILEs' |
values on the TABLE's input record are less than the FILEs' |
1257 |
BY values. If an exact match is found, store all the values |
BY values. If an exact match is found, store all the values |
1258 |
from the TABLE input record into the output record. */ |
from the TABLE input record into the output record. */ |
1259 |
while (iter) |
for (; iter != NULL; iter = next) |
1260 |
{ |
{ |
|
struct mtf_file *next = iter->next; |
|
|
|
|
1261 |
assert (iter->type == MTF_TABLE); |
assert (iter->type == MTF_TABLE); |
1262 |
|
|
1263 |
if (iter->handle == NULL) |
next = iter->next; |
|
advance = false; |
|
1264 |
|
|
1265 |
again: |
again: |
1266 |
switch (mtf_compare_BY_values (mtf, min_head, iter, c)) |
switch (mtf_compare_BY_values (mtf, min_head, iter, c)) |
1287 |
default: |
default: |
1288 |
assert (0); |
assert (0); |
1289 |
} |
} |
|
|
|
|
iter = next; |
|
1290 |
} |
} |
1291 |
|
|
1292 |
/* Next sequence number. */ |
/* Next sequence number. */ |
1293 |
mtf->seq_num++; |
mtf->seq_num++; |
1294 |
|
|
1295 |
/* Store data to all the records we are using. */ |
/* Store data to all the records we are using. */ |
1296 |
if (min_tail) |
if (min_tail) |
1297 |
min_tail->next_min = NULL; |
min_tail->next_min = NULL; |
1304 |
struct variable *v = dict_get_var (iter->dict, i); |
struct variable *v = dict_get_var (iter->dict, i); |
1305 |
struct variable *mv = get_master (v); |
struct variable *mv = get_master (v); |
1306 |
|
|
1307 |
if (mtf->seq_nums[mv->index] != mtf->seq_num) |
if (mv != NULL && mtf->seq_nums[mv->index] != mtf->seq_num) |
1308 |
{ |
{ |
1309 |
struct ccase *record |
struct ccase *record |
1310 |
= case_is_null (&iter->input) ? c : &iter->input; |
= case_is_null (&iter->input) ? c : &iter->input; |
1317 |
memcpy (out->s, case_str (record, v->fv), v->width); |
memcpy (out->s, case_str (record, v->fv), v->width); |
1318 |
} |
} |
1319 |
} |
} |
1320 |
|
if (iter->in_var != NULL) |
1321 |
|
case_data_rw (&mtf->mtf_case, iter->in_var->fv)->f = 1.; |
1322 |
|
|
1323 |
|
if (iter->type == MTF_FILE && iter->handle == NULL) |
1324 |
|
read_active_file = true; |
1325 |
} |
} |
1326 |
|
|
1327 |
/* Store missing values to all the records we're not using. */ |
/* Store missing values to all the records we're not |
1328 |
|
using. */ |
1329 |
if (max_tail) |
if (max_tail) |
1330 |
max_tail->next_min = NULL; |
max_tail->next_min = NULL; |
1331 |
for (iter = max_head; iter; iter = iter->next_min) |
for (iter = max_head; iter; iter = iter->next_min) |
1336 |
{ |
{ |
1337 |
struct variable *v = dict_get_var (iter->dict, i); |
struct variable *v = dict_get_var (iter->dict, i); |
1338 |
struct variable *mv = get_master (v); |
struct variable *mv = get_master (v); |
1339 |
|
|
1340 |
if (mtf->seq_nums[mv->index] != mtf->seq_num) |
if (mv != NULL && mtf->seq_nums[mv->index] != mtf->seq_num) |
1341 |
{ |
{ |
1342 |
union value *out = case_data_rw (&mtf->mtf_case, mv->fv); |
union value *out = case_data_rw (&mtf->mtf_case, mv->fv); |
1343 |
mtf->seq_nums[mv->index] = mtf->seq_num; |
mtf->seq_nums[mv->index] = mtf->seq_num; |
1348 |
memset (out->s, ' ', v->width); |
memset (out->s, ' ', v->width); |
1349 |
} |
} |
1350 |
} |
} |
1351 |
|
if (iter->in_var != NULL) |
1352 |
if (iter->handle == NULL) |
case_data_rw (&mtf->mtf_case, iter->in_var->fv)->f = 0.; |
|
advance = false; |
|
1353 |
} |
} |
1354 |
|
|
1355 |
/* 6. Write the output record. */ |
/* 5. Write the output record. */ |
1356 |
mtf->sink->class->write (mtf->sink, &mtf->mtf_case); |
mtf->sink->class->write (mtf->sink, &mtf->mtf_case); |
1357 |
|
|
1358 |
/* 7. Read another record from each input file FILE and TABLE |
/* 6. Read another record from each input file FILE and TABLE |
1359 |
that we stored values from above. If we come to the end of |
that we stored values from above. If we come to the end of |
1360 |
one of the input files, remove it from the list of input |
one of the input files, remove it from the list of input |
1361 |
files. */ |
files. */ |
1362 |
for (iter = min_head; iter && iter->type == MTF_FILE; ) |
for (iter = min_head; iter && iter->type == MTF_FILE; iter = next) |
1363 |
{ |
{ |
1364 |
struct mtf_file *next = iter->next_min; |
next = iter->next_min; |
1365 |
|
if (iter->reader != NULL |
1366 |
if (iter->reader != NULL) |
&& !sfm_read_case (iter->reader, &iter->input)) |
1367 |
{ |
mtf_delete_file_in_place (mtf, &iter); |
|
if (!sfm_read_case (iter->reader, &iter->input)) |
|
|
mtf_delete_file_in_place (mtf, &iter); |
|
|
} |
|
|
|
|
|
iter = next; |
|
1368 |
} |
} |
|
|
|
|
if (advance) |
|
|
break; |
|
1369 |
} |
} |
1370 |
|
while (!read_active_file |
1371 |
|
&& mtf->head != NULL && mtf->head->type == MTF_FILE); |
1372 |
|
|
1373 |
return (mtf->head && mtf->head->type != MTF_TABLE); |
return mtf->head != NULL && mtf->head->type == MTF_FILE; |
1374 |
} |
} |
1375 |
|
|
1376 |
/* Merge the dictionary for file F into master dictionary M. */ |
/* Merge the dictionary for file F into master dictionary M. */ |
1441 |
mv = dict_clone_var (m, dv, dv->name, dv->longname); |
mv = dict_clone_var (m, dv, dv->name, dv->longname); |
1442 |
assert (mv != NULL); |
assert (mv != NULL); |
1443 |
} |
} |
|
|
|
|
set_master (dv, mv); |
|
1444 |
} |
} |
1445 |
|
|
1446 |
return 1; |
return 1; |
1458 |
static struct variable * |
static struct variable * |
1459 |
get_master (struct variable *v) |
get_master (struct variable *v) |
1460 |
{ |
{ |
|
assert (v->aux != NULL); |
|
1461 |
return v->aux; |
return v->aux; |
1462 |
} |
} |
1463 |
|
|