54 |
{ |
{ |
55 |
OP_READ, /* GET or IMPORT. */ |
OP_READ, /* GET or IMPORT. */ |
56 |
OP_SAVE, /* SAVE or XSAVE. */ |
OP_SAVE, /* SAVE or XSAVE. */ |
57 |
OP_EXPORT, /* EXPORT. */ |
OP_EXPORT /* EXPORT. */ |
|
OP_MATCH /* MATCH FILES. */ |
|
58 |
}; |
}; |
59 |
|
|
60 |
static int trim_dictionary (struct dictionary *, |
static bool trim_dictionary (struct dictionary *, |
61 |
enum operation, int *compress); |
enum operation, int *compress); |
62 |
|
|
63 |
/* GET input program. */ |
/* GET input program. */ |
64 |
struct get_pgm |
struct get_pgm |
367 |
write a system file, *COMPRESS is set to 1 if the system file |
write a system file, *COMPRESS is set to 1 if the system file |
368 |
should be compressed, 0 otherwise. |
should be compressed, 0 otherwise. |
369 |
|
|
370 |
Returns nonzero on success, zero on failure. */ |
Returns true on success, false on failure. */ |
371 |
/* FIXME: IN, FIRST, LAST, MAP. */ |
static bool |
|
static int |
|
372 |
trim_dictionary (struct dictionary *dict, enum operation op, int *compress) |
trim_dictionary (struct dictionary *dict, enum operation op, int *compress) |
373 |
{ |
{ |
374 |
assert ((compress != NULL) == (op == OP_SAVE)); |
assert ((compress != NULL) == (op == OP_SAVE)); |
391 |
free (v); |
free (v); |
392 |
} |
} |
393 |
|
|
394 |
while (op == OP_MATCH || lex_match ('/')) |
while (lex_match ('/')) |
395 |
{ |
{ |
396 |
if (op == OP_SAVE && lex_match_id ("COMPRESSED")) |
if (op == OP_SAVE && lex_match_id ("COMPRESSED")) |
397 |
*compress = 1; |
*compress = 1; |
404 |
|
|
405 |
lex_match ('='); |
lex_match ('='); |
406 |
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
407 |
return 0; |
return false; |
408 |
dict_delete_vars (dict, v, nv); |
dict_delete_vars (dict, v, nv); |
409 |
free (v); |
free (v); |
410 |
} |
} |
416 |
|
|
417 |
lex_match ('='); |
lex_match ('='); |
418 |
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
if (!parse_variables (dict, &v, &nv, PV_NONE)) |
419 |
return 0; |
return false; |
420 |
|
|
421 |
/* Move the specified variables to the beginning. */ |
/* Move the specified variables to the beginning. */ |
422 |
dict_reorder_vars (dict, v, nv); |
dict_reorder_vars (dict, v, nv); |
431 |
else if (lex_match_id ("RENAME")) |
else if (lex_match_id ("RENAME")) |
432 |
{ |
{ |
433 |
if (!rename_variables (dict)) |
if (!rename_variables (dict)) |
434 |
return 0; |
return false; |
435 |
} |
} |
436 |
else |
else |
437 |
{ |
{ |
438 |
lex_error (_("while expecting a valid subcommand")); |
lex_error (_("while expecting a valid subcommand")); |
439 |
return 0; |
return false; |
440 |
} |
} |
441 |
|
|
442 |
if (dict_get_var_cnt (dict) == 0) |
if (dict_get_var_cnt (dict) == 0) |
443 |
{ |
{ |
444 |
msg (SE, _("All variables deleted from system file dictionary.")); |
msg (SE, _("All variables deleted from system file dictionary.")); |
445 |
return 0; |
return false; |
446 |
} |
} |
|
|
|
|
if (op == OP_MATCH) |
|
|
goto success; |
|
447 |
} |
} |
448 |
|
|
449 |
if (token != '.') |
if (!lex_end_of_command ()) |
450 |
{ |
return false; |
|
lex_error (_("expecting end of command")); |
|
|
return 0; |
|
|
} |
|
451 |
|
|
452 |
success: |
dict_compact_values (dict); |
453 |
if (op != OP_MATCH) |
return true; |
|
dict_compact_values (dict); |
|
|
return 1; |
|
454 |
} |
} |
455 |
|
|
456 |
/* Parses and performs the RENAME subcommand of GET and SAVE. */ |
/* Parses and performs the RENAME subcommand of GET and SAVE. */ |
650 |
struct file_handle *handle; /* File handle. */ |
struct file_handle *handle; /* File handle. */ |
651 |
struct sfm_reader *reader; /* System file reader. */ |
struct sfm_reader *reader; /* System file reader. */ |
652 |
struct dictionary *dict; /* Dictionary from system file. */ |
struct dictionary *dict; /* Dictionary from system file. */ |
653 |
char in[SHORT_NAME_LEN + 1]; /* Name of the variable from IN=. */ |
char in[SHORT_NAME_LEN + 1]; /* Name of the variable from IN=. */ |
654 |
char first[SHORT_NAME_LEN + 1]; |
|
|
char last[SHORT_NAME_LEN + 1]; /* Name of the variables from FIRST=, LAST=. */ |
|
655 |
struct ccase input; /* Input record. */ |
struct ccase input; /* Input record. */ |
656 |
}; |
}; |
657 |
|
|
661 |
struct mtf_file *head; /* First file mentioned on FILE or TABLE. */ |
struct mtf_file *head; /* First file mentioned on FILE or TABLE. */ |
662 |
struct mtf_file *tail; /* Last file mentioned on FILE or TABLE. */ |
struct mtf_file *tail; /* Last file mentioned on FILE or TABLE. */ |
663 |
|
|
|
struct variable **by; /* Variables on the BY subcommand. */ |
|
664 |
size_t by_cnt; /* Number of variables on BY subcommand. */ |
size_t by_cnt; /* Number of variables on BY subcommand. */ |
665 |
|
|
666 |
|
/* Names of FIRST, LAST variables. */ |
667 |
|
char first[SHORT_NAME_LEN + 1], last[SHORT_NAME_LEN + 1]; |
668 |
|
|
669 |
struct dictionary *dict; /* Dictionary of output file. */ |
struct dictionary *dict; /* Dictionary of output file. */ |
670 |
struct case_sink *sink; /* Sink to receive output. */ |
struct case_sink *sink; /* Sink to receive output. */ |
671 |
struct ccase *mtf_case; /* Case used for output. */ |
struct ccase mtf_case; /* Case used for output. */ |
672 |
|
|
673 |
unsigned seq_num; /* Have we initialized this variable? */ |
unsigned seq_num; /* Have we initialized this variable? */ |
674 |
unsigned *seq_nums; /* Sequence numbers for each var in dict. */ |
unsigned *seq_nums; /* Sequence numbers for each var in dict. */ |
694 |
{ |
{ |
695 |
struct mtf_proc mtf; |
struct mtf_proc mtf; |
696 |
struct mtf_file *first_table = NULL; |
struct mtf_file *first_table = NULL; |
|
struct mtf_file *iter; |
|
697 |
|
|
698 |
int seen = 0; |
bool used_active_file = false; |
699 |
|
bool saw_table = false; |
700 |
|
|
701 |
mtf.head = mtf.tail = NULL; |
mtf.head = mtf.tail = NULL; |
|
mtf.by = NULL; |
|
702 |
mtf.by_cnt = 0; |
mtf.by_cnt = 0; |
703 |
|
mtf.first[0] = '\0'; |
704 |
|
mtf.last[0] = '\0'; |
705 |
mtf.dict = dict_create (); |
mtf.dict = dict_create (); |
706 |
mtf.sink = NULL; |
mtf.sink = NULL; |
707 |
mtf.mtf_case = NULL; |
case_nullify (&mtf.mtf_case); |
708 |
mtf.seq_num = 0; |
mtf.seq_num = 0; |
709 |
mtf.seq_nums = NULL; |
mtf.seq_nums = NULL; |
710 |
dict_set_case_limit (mtf.dict, dict_get_case_limit (default_dict)); |
dict_set_case_limit (mtf.dict, dict_get_case_limit (default_dict)); |
|
|
|
|
do |
|
|
{ |
|
|
lex_match ('/'); |
|
711 |
|
|
712 |
if (lex_match (T_BY)) |
lex_match ('/'); |
713 |
{ |
while (lex_id_match ("FILE", tokid) || lex_id_match ("TABLE", tokid)) |
714 |
if (seen & 1) |
{ |
715 |
{ |
struct mtf_file *file = xmalloc (sizeof *file); |
|
msg (SE, _("The BY subcommand may be given once at most.")); |
|
|
goto error; |
|
|
} |
|
|
seen |= 1; |
|
|
|
|
|
lex_match ('='); |
|
|
if (!parse_variables (mtf.dict, &mtf.by, &mtf.by_cnt, |
|
|
PV_NO_DUPLICATE | PV_NO_SCRATCH)) |
|
|
goto error; |
|
|
} |
|
|
else if (token != T_ID) |
|
|
{ |
|
|
lex_error (NULL); |
|
|
goto error; |
|
|
} |
|
|
else if (lex_id_match ("FILE", tokid) || lex_id_match ("TABLE", tokid)) |
|
|
{ |
|
|
struct mtf_file *file = xmalloc (sizeof *file); |
|
716 |
|
|
717 |
if (lex_match_id ("FILE")) |
if (lex_match_id ("FILE")) |
718 |
file->type = MTF_FILE; |
file->type = MTF_FILE; |
719 |
else if (lex_match_id ("TABLE")) |
else if (lex_match_id ("TABLE")) |
720 |
{ |
{ |
721 |
file->type = MTF_TABLE; |
file->type = MTF_TABLE; |
722 |
seen |= 4; |
saw_table = true; |
723 |
} |
} |
724 |
else |
else |
725 |
assert (0); |
assert (0); |
726 |
|
|
727 |
file->by = NULL; |
file->by = NULL; |
728 |
file->handle = NULL; |
file->handle = NULL; |
729 |
file->reader = NULL; |
file->reader = NULL; |
730 |
file->dict = NULL; |
file->dict = NULL; |
731 |
file->in[0] = '\0'; |
file->in[0] = '\0'; |
732 |
file->first[0] = '\0'; |
case_nullify (&file->input); |
|
file->last[0] = '\0'; |
|
|
case_nullify (&file->input); |
|
733 |
|
|
734 |
/* FILEs go first, then TABLEs. */ |
/* FILEs go first, then TABLEs. */ |
735 |
if (file->type == MTF_TABLE || first_table == NULL) |
if (file->type == MTF_TABLE || first_table == NULL) |
736 |
{ |
{ |
737 |
file->next = NULL; |
file->next = NULL; |
738 |
file->prev = mtf.tail; |
file->prev = mtf.tail; |
739 |
if (mtf.tail) |
if (mtf.tail) |
740 |
mtf.tail->next = file; |
mtf.tail->next = file; |
741 |
mtf.tail = file; |
mtf.tail = file; |
742 |
if (mtf.head == NULL) |
if (mtf.head == NULL) |
743 |
mtf.head = file; |
mtf.head = file; |
744 |
if (file->type == MTF_TABLE && first_table == NULL) |
if (file->type == MTF_TABLE && first_table == NULL) |
745 |
first_table = file; |
first_table = file; |
746 |
} |
} |
747 |
else |
else |
748 |
{ |
{ |
749 |
assert (file->type == MTF_FILE); |
assert (file->type == MTF_FILE); |
750 |
file->next = first_table; |
file->next = first_table; |
751 |
file->prev = first_table->prev; |
file->prev = first_table->prev; |
752 |
if (first_table->prev) |
if (first_table->prev) |
753 |
first_table->prev->next = file; |
first_table->prev->next = file; |
754 |
else |
else |
755 |
mtf.head = file; |
mtf.head = file; |
756 |
first_table->prev = file; |
first_table->prev = file; |
757 |
} |
} |
758 |
|
|
759 |
lex_match ('='); |
lex_match ('='); |
760 |
|
|
761 |
if (lex_match ('*')) |
if (lex_match ('*')) |
762 |
{ |
{ |
763 |
file->handle = NULL; |
file->handle = NULL; |
764 |
file->reader = NULL; |
file->reader = NULL; |
765 |
|
|
766 |
if (seen & 2) |
if (used_active_file) |
767 |
{ |
{ |
768 |
msg (SE, _("The active file may not be specified more " |
msg (SE, _("The active file may not be specified more " |
769 |
"than once.")); |
"than once.")); |
770 |
goto error; |
goto error; |
771 |
} |
} |
772 |
seen |= 2; |
used_active_file = true; |
773 |
|
|
774 |
assert (pgm_state != STATE_INPUT); |
assert (pgm_state != STATE_INPUT); |
775 |
if (pgm_state == STATE_INIT) |
if (pgm_state == STATE_INIT) |
776 |
{ |
{ |
777 |
msg (SE, _("Cannot specify the active file since no active " |
msg (SE, _("Cannot specify the active file since no active " |
778 |
"file has been defined.")); |
"file has been defined.")); |
779 |
goto error; |
goto error; |
780 |
} |
} |
781 |
|
|
782 |
if (temporary != 0) |
if (temporary != 0) |
783 |
{ |
{ |
784 |
msg (SE, |
msg (SE, |
785 |
_("MATCH FILES may not be used after TEMPORARY when " |
_("MATCH FILES may not be used after TEMPORARY when " |
786 |
"the active file is an input source. " |
"the active file is an input source. " |
787 |
"Temporary transformations will be made permanent.")); |
"Temporary transformations will be made permanent.")); |
788 |
cancel_temporary (); |
cancel_temporary (); |
789 |
} |
} |
790 |
|
|
791 |
file->dict = default_dict; |
file->dict = default_dict; |
792 |
} |
} |
793 |
else |
else |
794 |
{ |
{ |
795 |
file->handle = fh_parse (); |
file->handle = fh_parse (); |
796 |
if (file->handle == NULL) |
if (file->handle == NULL) |
797 |
goto error; |
goto error; |
798 |
|
|
799 |
|
file->reader = sfm_open_reader (file->handle, &file->dict, NULL); |
800 |
|
if (file->reader == NULL) |
801 |
|
goto error; |
802 |
|
|
803 |
file->reader = sfm_open_reader (file->handle, &file->dict, NULL); |
case_create (&file->input, dict_get_next_value_idx (file->dict)); |
804 |
if (file->reader == NULL) |
} |
805 |
|
|
806 |
|
while (lex_match ('/')) |
807 |
|
if (lex_match_id ("RENAME")) |
808 |
|
{ |
809 |
|
if (!rename_variables (file->dict)) |
810 |
|
goto error; |
811 |
|
} |
812 |
|
else if (lex_match_id ("IN")) |
813 |
|
{ |
814 |
|
lex_match ('='); |
815 |
|
if (token != T_ID) |
816 |
|
{ |
817 |
|
lex_error (NULL); |
818 |
goto error; |
goto error; |
819 |
|
} |
820 |
|
|
821 |
case_create (&file->input, dict_get_next_value_idx (file->dict)); |
if (file->in[0]) |
822 |
} |
{ |
823 |
} |
msg (SE, _("Multiple IN subcommands for a single FILE or " |
824 |
else if (lex_id_match ("IN", tokid) |
"TABLE.")); |
825 |
|| lex_id_match ("FIRST", tokid) |
goto error; |
826 |
|| lex_id_match ("LAST", tokid)) |
} |
827 |
|
strcpy (file->in, tokid); |
828 |
|
lex_get (); |
829 |
|
} |
830 |
|
|
831 |
|
mtf_merge_dictionary (mtf.dict, file); |
832 |
|
} |
833 |
|
|
834 |
|
while (token != '.') |
835 |
|
{ |
836 |
|
if (lex_match (T_BY)) |
837 |
{ |
{ |
838 |
const char *sbc; |
struct variable **by; |
839 |
char *name; |
struct mtf_file *iter; |
840 |
|
|
841 |
if (mtf.tail == NULL) |
if (mtf.by_cnt) |
842 |
{ |
{ |
843 |
msg (SE, _("IN, FIRST, and LAST subcommands may not occur " |
msg (SE, _("BY may appear at most once.")); |
|
"before the first FILE or TABLE.")); |
|
844 |
goto error; |
goto error; |
845 |
} |
} |
846 |
|
|
|
if (lex_match_id ("IN")) |
|
|
{ |
|
|
name = mtf.tail->in; |
|
|
sbc = "IN"; |
|
|
} |
|
|
else if (lex_match_id ("FIRST")) |
|
|
{ |
|
|
name = mtf.tail->first; |
|
|
sbc = "FIRST"; |
|
|
} |
|
|
else if (lex_match_id ("LAST")) |
|
|
{ |
|
|
name = mtf.tail->last; |
|
|
sbc = "LAST"; |
|
|
} |
|
|
else |
|
|
{ |
|
|
assert (0); |
|
|
abort (); |
|
|
} |
|
|
|
|
847 |
lex_match ('='); |
lex_match ('='); |
848 |
if (token != T_ID) |
if (!parse_variables (mtf.dict, &by, &mtf.by_cnt, |
849 |
{ |
PV_NO_DUPLICATE | PV_NO_SCRATCH)) |
850 |
lex_error (NULL); |
goto error; |
|
goto error; |
|
|
} |
|
|
|
|
|
if (*name) |
|
|
{ |
|
|
msg (SE, _("Multiple %s subcommands for a single FILE or " |
|
|
"TABLE."), |
|
|
sbc); |
|
|
goto error; |
|
|
} |
|
|
strcpy (name, tokid); |
|
|
lex_get (); |
|
851 |
|
|
852 |
if (!dict_create_var (mtf.dict, name, 0)) |
for (iter = mtf.head; iter != NULL; iter = iter->next) |
853 |
{ |
{ |
854 |
msg (SE, _("Duplicate variable name %s while creating %s " |
int i; |
855 |
"variable."), |
|
856 |
name, sbc); |
iter->by = xmalloc (sizeof *iter->by * mtf.by_cnt); |
|
goto error; |
|
|
} |
|
|
} |
|
|
else if (lex_id_match ("RENAME", tokid) |
|
|
|| lex_id_match ("KEEP", tokid) |
|
|
|| lex_id_match ("DROP", tokid)) |
|
|
{ |
|
|
if (mtf.tail == NULL) |
|
|
{ |
|
|
msg (SE, _("RENAME, KEEP, and DROP subcommands may not occur " |
|
|
"before the first FILE or TABLE.")); |
|
|
goto error; |
|
|
} |
|
857 |
|
|
858 |
if (!trim_dictionary (mtf.tail->dict, OP_MATCH, NULL)) |
for (i = 0; i < mtf.by_cnt; i++) |
859 |
goto error; |
{ |
860 |
|
iter->by[i] = dict_lookup_var (iter->dict, by[i]->name); |
861 |
|
if (iter->by[i] == NULL) |
862 |
|
{ |
863 |
|
msg (SE, _("File %s lacks BY variable %s."), |
864 |
|
iter->handle ? handle_get_name (iter->handle) : "*", |
865 |
|
by[i]->name); |
866 |
|
free (by); |
867 |
|
goto error; |
868 |
|
} |
869 |
|
} |
870 |
|
} |
871 |
} |
} |
872 |
|
else if (lex_match_id ("FIRST")) |
873 |
|
{ |
874 |
|
if (mtf.first[0] != '\0') |
875 |
|
{ |
876 |
|
msg (SE, _("FIRST may appear at most once.")); |
877 |
|
goto error; |
878 |
|
} |
879 |
|
|
880 |
|
lex_match ('='); |
881 |
|
if (!lex_force_id ()) |
882 |
|
goto error; |
883 |
|
strcpy (mtf.first, tokid); |
884 |
|
lex_get (); |
885 |
|
} |
886 |
|
else if (lex_match_id ("LAST")) |
887 |
|
{ |
888 |
|
if (mtf.last[0] != '\0') |
889 |
|
{ |
890 |
|
msg (SE, _("LAST may appear at most once.")); |
891 |
|
goto error; |
892 |
|
} |
893 |
|
|
894 |
|
lex_match ('='); |
895 |
|
if (!lex_force_id ()) |
896 |
|
goto error; |
897 |
|
strcpy (mtf.last, tokid); |
898 |
|
lex_get (); |
899 |
|
} |
900 |
else if (lex_match_id ("MAP")) |
else if (lex_match_id ("MAP")) |
901 |
{ |
{ |
902 |
/* FIXME. */ |
/* FIXME. */ |
906 |
lex_error (NULL); |
lex_error (NULL); |
907 |
goto error; |
goto error; |
908 |
} |
} |
|
} |
|
|
while (token != '.'); |
|
|
|
|
|
for (iter = mtf.head; iter != NULL; iter = iter->next) |
|
|
mtf_merge_dictionary (mtf.dict, iter); |
|
909 |
|
|
910 |
if (seen & 4) |
if (!lex_match ('/') && token != '.') |
911 |
{ |
{ |
912 |
if (!(seen & 1)) |
lex_end_of_command (); |
913 |
{ |
goto error; |
914 |
msg (SE, _("The BY subcommand is required when a TABLE subcommand " |
} |
|
"is given.")); |
|
|
goto error; |
|
|
} |
|
915 |
} |
} |
916 |
|
|
917 |
if (seen & 1) |
if (mtf.by_cnt == 0 && saw_table) |
918 |
{ |
{ |
919 |
for (iter = mtf.head; iter != NULL; iter = iter->next) |
msg (SE, _("BY is required when TABLE is specified.")); |
920 |
{ |
goto error; |
|
int i; |
|
|
|
|
|
iter->by = xmalloc (sizeof *iter->by * mtf.by_cnt); |
|
|
|
|
|
for (i = 0; i < mtf.by_cnt; i++) |
|
|
{ |
|
|
iter->by[i] = dict_lookup_var (iter->dict, mtf.by[i]->name); |
|
|
if (iter->by[i] == NULL) |
|
|
{ |
|
|
msg (SE, _("File %s lacks BY variable %s."), |
|
|
iter->handle ? handle_get_name (iter->handle) : "*", |
|
|
mtf.by[i]->name); |
|
|
goto error; |
|
|
} |
|
|
} |
|
|
} |
|
921 |
} |
} |
922 |
|
|
923 |
/* MATCH FILES performs an n-way merge on all its input files. |
/* MATCH FILES performs an n-way merge on all its input files. |
955 |
better algorithm would use a heap for finding minimum |
better algorithm would use a heap for finding minimum |
956 |
values. */ |
values. */ |
957 |
|
|
958 |
if (!(seen & 2)) |
if (!used_active_file) |
959 |
discard_variables (); |
discard_variables (); |
960 |
|
|
961 |
mtf.sink = create_case_sink (&storage_sink_class, mtf.dict, NULL); |
mtf.sink = create_case_sink (&storage_sink_class, mtf.dict, NULL); |
962 |
|
if (mtf.sink->class->open != NULL) |
963 |
|
mtf.sink->class->open (mtf.sink); |
964 |
|
|
965 |
mtf.seq_nums = xmalloc (dict_get_var_cnt (mtf.dict) |
mtf.seq_nums = xmalloc (dict_get_var_cnt (mtf.dict) * sizeof *mtf.seq_nums); |
|
* sizeof *mtf.seq_nums); |
|
966 |
memset (mtf.seq_nums, 0, |
memset (mtf.seq_nums, 0, |
967 |
dict_get_var_cnt (mtf.dict) * sizeof *mtf.seq_nums); |
dict_get_var_cnt (mtf.dict) * sizeof *mtf.seq_nums); |
968 |
mtf.mtf_case = xmalloc (dict_get_case_size (mtf.dict)); |
case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict)); |
969 |
|
|
970 |
mtf_read_nonactive_records (NULL); |
mtf_read_nonactive_records (&mtf); |
971 |
if (seen & 2) |
if (used_active_file) |
972 |
procedure (mtf_processing, NULL); |
procedure (mtf_processing, &mtf); |
973 |
mtf_processing_finish (NULL); |
mtf_processing_finish (&mtf); |
974 |
|
|
975 |
dict_destroy (default_dict); |
dict_destroy (default_dict); |
976 |
default_dict = mtf.dict; |
default_dict = mtf.dict; |
1053 |
mtf_free_file (iter); |
mtf_free_file (iter); |
1054 |
} |
} |
1055 |
|
|
|
free (mtf->by); |
|
1056 |
if (mtf->dict) |
if (mtf->dict) |
1057 |
dict_destroy (mtf->dict); |
dict_destroy (mtf->dict); |
1058 |
|
case_destroy (&mtf->mtf_case); |
1059 |
free (mtf->seq_nums); |
free (mtf->seq_nums); |
1060 |
} |
} |
1061 |
|
|
1082 |
for (i = 0; i < dict_get_var_cnt (f->dict); i++) |
for (i = 0; i < dict_get_var_cnt (f->dict); i++) |
1083 |
{ |
{ |
1084 |
struct variable *v = dict_get_var (f->dict, i); |
struct variable *v = dict_get_var (f->dict, i); |
1085 |
union value *out = case_data_rw (mtf->mtf_case, get_master (v)->fv); |
union value *out = case_data_rw (&mtf->mtf_case, get_master (v)->fv); |
1086 |
|
|
1087 |
if (v->type == NUMERIC) |
if (v->type == NUMERIC) |
1088 |
out->f = SYSMIS; |
out->f = SYSMIS; |
1096 |
|
|
1097 |
/* Read a record from every input file except the active file. */ |
/* Read a record from every input file except the active file. */ |
1098 |
static void |
static void |
1099 |
mtf_read_nonactive_records (void *mtf_ UNUSED) |
mtf_read_nonactive_records (void *mtf_) |
1100 |
{ |
{ |
1101 |
struct mtf_proc *mtf = mtf_; |
struct mtf_proc *mtf = mtf_; |
1102 |
struct mtf_file *iter; |
struct mtf_file *iter; |
1162 |
|
|
1163 |
/* Perform one iteration of steps 3...7 above. */ |
/* Perform one iteration of steps 3...7 above. */ |
1164 |
static int |
static int |
1165 |
mtf_processing (struct ccase *c, void *mtf_ UNUSED) |
mtf_processing (struct ccase *c, void *mtf_) |
1166 |
{ |
{ |
1167 |
struct mtf_proc *mtf = mtf_; |
struct mtf_proc *mtf = mtf_; |
1168 |
struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */ |
struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */ |
1173 |
{ |
{ |
1174 |
/* If the active file doesn't have the minimum BY values, don't |
/* If the active file doesn't have the minimum BY values, don't |
1175 |
return because that would cause a record to be skipped. */ |
return because that would cause a record to be skipped. */ |
1176 |
int advance = 1; |
bool advance = true; |
1177 |
|
|
1178 |
if (mtf->head->type == MTF_TABLE) |
if (mtf->head->type == MTF_TABLE) |
1179 |
return 0; |
return 0; |
1230 |
assert (iter->type == MTF_TABLE); |
assert (iter->type == MTF_TABLE); |
1231 |
|
|
1232 |
if (iter->handle == NULL) |
if (iter->handle == NULL) |
1233 |
advance = 0; |
advance = false; |
1234 |
|
|
1235 |
again: |
again: |
1236 |
switch (mtf_compare_BY_values (mtf, min_head, iter, c)) |
switch (mtf_compare_BY_values (mtf, min_head, iter, c)) |
1274 |
for (i = 0; i < dict_get_var_cnt (iter->dict); i++) |
for (i = 0; i < dict_get_var_cnt (iter->dict); i++) |
1275 |
{ |
{ |
1276 |
struct variable *v = dict_get_var (iter->dict, i); |
struct variable *v = dict_get_var (iter->dict, i); |
1277 |
struct ccase *record; |
struct variable *mv = get_master (v); |
|
union value *out; |
|
1278 |
|
|
1279 |
if (mtf->seq_nums[get_master (v)->index] == mtf->seq_num) |
if (mtf->seq_nums[mv->index] != mtf->seq_num) |
1280 |
continue; |
{ |
1281 |
mtf->seq_nums[get_master (v)->index] = mtf->seq_num; |
struct ccase *record |
1282 |
|
= case_is_null (&iter->input) ? c : &iter->input; |
1283 |
record = case_is_null (&iter->input) ? c : &iter->input; |
union value *out = case_data_rw (&mtf->mtf_case, mv->fv); |
1284 |
|
|
1285 |
assert (v->type == NUMERIC || v->type == ALPHA); |
mtf->seq_nums[mv->index] = mtf->seq_num; |
1286 |
out = case_data_rw (mtf->mtf_case, get_master (v)->fv); |
if (v->type == NUMERIC) |
1287 |
if (v->type == NUMERIC) |
out->f = case_num (record, v->fv); |
1288 |
out->f = case_num (record, v->fv); |
else |
1289 |
else |
memcpy (out->s, case_str (record, v->fv), v->width); |
1290 |
memcpy (out->s, case_str (record, v->fv), v->width); |
} |
1291 |
} |
} |
1292 |
} |
} |
1293 |
|
|
1294 |
/* Store missing values to all the records we're not using. */ |
/* Store missing values to all the records we're not using. */ |
1301 |
for (i = 0; i < dict_get_var_cnt (iter->dict); i++) |
for (i = 0; i < dict_get_var_cnt (iter->dict); i++) |
1302 |
{ |
{ |
1303 |
struct variable *v = dict_get_var (iter->dict, i); |
struct variable *v = dict_get_var (iter->dict, i); |
1304 |
union value *out; |
struct variable *mv = get_master (v); |
1305 |
|
|
1306 |
if (mtf->seq_nums[get_master (v)->index] == mtf->seq_num) |
if (mtf->seq_nums[mv->index] != mtf->seq_num) |
1307 |
continue; |
{ |
1308 |
mtf->seq_nums[get_master (v)->index] = mtf->seq_num; |
union value *out = case_data_rw (&mtf->mtf_case, mv->fv); |
1309 |
|
mtf->seq_nums[mv->index] = mtf->seq_num; |
1310 |
out = case_data_rw (mtf->mtf_case, get_master (v)->fv); |
|
1311 |
if (v->type == NUMERIC) |
if (v->type == NUMERIC) |
1312 |
out->f = SYSMIS; |
out->f = SYSMIS; |
1313 |
else |
else |
1314 |
memset (out->s, ' ', v->width); |
memset (out->s, ' ', v->width); |
1315 |
} |
} |
1316 |
|
} |
1317 |
|
|
1318 |
if (iter->handle == NULL) |
if (iter->handle == NULL) |
1319 |
advance = 0; |
advance = false; |
1320 |
} |
} |
1321 |
|
|
1322 |
/* 6. Write the output record. */ |
/* 6. Write the output record. */ |
1323 |
mtf->sink->class->write (mtf->sink, mtf->mtf_case); |
mtf->sink->class->write (mtf->sink, &mtf->mtf_case); |
1324 |
|
|
1325 |
/* 7. Read another record from each input file FILE and TABLE |
/* 7. Read another record from each input file FILE and TABLE |
1326 |
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 |
1352 |
{ |
{ |
1353 |
struct dictionary *d = f->dict; |
struct dictionary *d = f->dict; |
1354 |
const char *d_docs, *m_docs; |
const char *d_docs, *m_docs; |
1355 |
|
int i; |
1356 |
|
|
1357 |
if (dict_get_label (m) == NULL) |
if (dict_get_label (m) == NULL) |
1358 |
dict_set_label (m, dict_get_label (d)); |
dict_set_label (m, dict_get_label (d)); |
1379 |
|
|
1380 |
dict_compact_values (d); |
dict_compact_values (d); |
1381 |
|
|
1382 |
{ |
for (i = 0; i < dict_get_var_cnt (d); i++) |
1383 |
int i; |
{ |
1384 |
|
struct variable *dv = dict_get_var (d, i); |
1385 |
|
struct variable *mv = dict_lookup_var (m, dv->name); |
1386 |
|
|
1387 |
for (i = 0; i < dict_get_var_cnt (d); i++) |
if (mv != NULL) |
1388 |
{ |
{ |
1389 |
struct variable *dv = dict_get_var (d, i); |
if (mv->width != dv->width) |
1390 |
struct variable *mv = dict_lookup_var (m, dv->name); |
{ |
1391 |
|
msg (SE, _("Variable %s in file %s (%s) has different " |
1392 |
|
"type or width from the same variable in " |
1393 |
|
"earlier file (%s)."), |
1394 |
|
dv->name, handle_get_name (f->handle), |
1395 |
|
var_type_description (dv), var_type_description (mv)); |
1396 |
|
return 0; |
1397 |
|
} |
1398 |
|
|
1399 |
|
if (dv->width == mv->width) |
1400 |
|
{ |
1401 |
|
if (val_labs_count (dv->val_labs) |
1402 |
|
&& !val_labs_count (mv->val_labs)) |
1403 |
|
mv->val_labs = val_labs_copy (dv->val_labs); |
1404 |
|
if (dv->miss_type != MISSING_NONE |
1405 |
|
&& mv->miss_type == MISSING_NONE) |
1406 |
|
copy_missing_values (mv, dv); |
1407 |
|
} |
1408 |
|
|
1409 |
assert (dv->type == ALPHA || dv->width == 0); |
if (dv->label && !mv->label) |
1410 |
assert (!mv || mv->type == ALPHA || mv->width == 0); |
mv->label = xstrdup (dv->label); |
1411 |
if (mv && dv->width == mv->width) |
} |
1412 |
{ |
else |
1413 |
if (val_labs_count (dv->val_labs) |
{ |
1414 |
&& !val_labs_count (mv->val_labs)) |
mv = dict_clone_var (m, dv, dv->name, dv->longname); |
1415 |
mv->val_labs = val_labs_copy (dv->val_labs); |
assert (mv != NULL); |
1416 |
if (dv->miss_type != MISSING_NONE |
} |
1417 |
&& mv->miss_type == MISSING_NONE) |
|
1418 |
copy_missing_values (mv, dv); |
set_master (dv, mv); |
1419 |
} |
} |
|
if (mv && dv->label && !mv->label) |
|
|
mv->label = xstrdup (dv->label); |
|
|
if (!mv) |
|
|
{ |
|
|
mv = dict_clone_var (m, dv, dv->name, dv->longname); |
|
|
assert (mv != NULL); |
|
|
} |
|
|
else if (mv->width != dv->width) |
|
|
{ |
|
|
msg (SE, _("Variable %s in file %s (%s) has different " |
|
|
"type or width from the same variable in " |
|
|
"earlier file (%s)."), |
|
|
dv->name, handle_get_name (f->handle), |
|
|
var_type_description (dv), var_type_description (mv)); |
|
|
return 0; |
|
|
} |
|
|
set_master (dv, mv); |
|
|
} |
|
|
} |
|
1420 |
|
|
1421 |
return 1; |
return 1; |
1422 |
} |
} |