Implement ADD FILES and UPDATE.
[pspp-builds.git] / src / language / data-io / match-files.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2008 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20
21 #include <data/any-reader.h>
22 #include <data/case-matcher.h>
23 #include <data/case.h>
24 #include <data/casereader.h>
25 #include <data/casewriter.h>
26 #include <data/dictionary.h>
27 #include <data/format.h>
28 #include <data/procedure.h>
29 #include <data/subcase.h>
30 #include <data/variable.h>
31 #include <language/command.h>
32 #include <language/data-io/file-handle.h>
33 #include <language/data-io/trim.h>
34 #include <language/lexer/lexer.h>
35 #include <language/lexer/variable-parser.h>
36 #include <language/stats/sort-criteria.h>
37 #include <libpspp/assertion.h>
38 #include <libpspp/message.h>
39 #include <libpspp/taint.h>
40 #include <math/sort.h>
41
42 #include "xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46
47 enum command_type
48   {
49     ADD_FILES,
50     MATCH_FILES,
51     UPDATE
52   };
53
54 /* File types. */
55 enum mtf_type
56   {
57     MTF_FILE,                   /* Specified on FILE= subcommand. */
58     MTF_TABLE                   /* Specified on TABLE= subcommand. */
59   };
60
61 /* One FILE or TABLE subcommand. */
62 struct mtf_file
63   {
64     enum mtf_type type;
65     struct casereader *reader;
66     struct subcase by;
67     int idx;
68     struct mtf_variable *vars;  /* Variables to copy to output. */
69     size_t var_cnt;             /* Number of other variables. */
70     bool is_sorted;             /* Is presorted on the BY variables? */
71
72     struct file_handle *handle; /* Input file handle. */
73     struct dictionary *dict;    /* Input file dictionary. */
74
75     /* Used by TABLE. */
76     struct ccase c;
77
78     char in_name[VAR_NAME_LEN + 1];
79     struct variable *in_var;
80   };
81
82 struct mtf_variable
83   {
84     struct variable *in_var;
85     struct variable *out_var;
86   };
87
88 struct mtf_proc
89   {
90     struct mtf_file **files;    /* All the files being merged. */
91     size_t n_files;             /* Number of files. */
92
93     struct dictionary *dict;    /* Dictionary of output file. */
94     struct casewriter *output;  /* Destination for output. */
95
96     struct case_matcher *matcher;
97     struct subcase by;
98
99     /* FIRST, LAST.
100        Only if "first" or "last" is nonnull are the remaining
101        members used. */
102     struct variable *first;     /* Variable specified on FIRST (if any). */
103     struct variable *last;      /* Variable specified on LAST (if any). */
104     struct ccase buffered_case; /* Case ready for output except that we don't
105                                    know the value for the LAST variable yet. */
106     union value *prev_BY;       /* Values of BY vars in buffered_case. */
107   };
108
109 static int combine_files (enum command_type, struct lexer *, struct dataset *);
110 static void mtf_free (struct mtf_proc *);
111
112 static bool mtf_close_all_files (struct mtf_proc *);
113 static bool mtf_merge_dictionary (struct dictionary *const, struct mtf_file *);
114
115 static void process_update (struct mtf_proc *);
116 static void process_match_files (struct mtf_proc *);
117 static void process_add_files (struct mtf_proc *);
118
119 static bool create_flag_var (const char *subcommand_name, const char *var_name,
120                              struct dictionary *, struct variable **);
121 static char *var_type_description (struct variable *);
122 static void output_case (struct mtf_proc *, struct ccase *, union value *by);
123 static void output_buffered_case (struct mtf_proc *);
124
125 int
126 cmd_add_files (struct lexer *lexer, struct dataset *ds)
127 {
128   return combine_files (ADD_FILES, lexer, ds);
129 }
130
131 int
132 cmd_match_files (struct lexer *lexer, struct dataset *ds)
133 {
134   return combine_files (MATCH_FILES, lexer, ds);
135 }
136
137 int
138 cmd_update (struct lexer *lexer, struct dataset *ds)
139 {
140   return combine_files (UPDATE, lexer, ds);
141 }
142
143 static int
144 combine_files (enum command_type command,
145                struct lexer *lexer, struct dataset *ds)
146 {
147   struct mtf_proc mtf;
148
149   bool saw_by = false;
150   bool saw_sort = false;
151   struct casereader *active_file = NULL;
152
153   char first_name[VAR_NAME_LEN + 1] = "";
154   char last_name[VAR_NAME_LEN + 1] = "";
155
156   struct taint *taint = NULL;
157
158   size_t n_files = 0;
159   size_t n_tables = 0;
160   size_t allocated_files = 0;
161
162   size_t i;
163
164   mtf.files = NULL;
165   mtf.n_files = 0;
166   mtf.dict = dict_create ();
167   mtf.output = NULL;
168   mtf.matcher = NULL;
169   subcase_init_empty (&mtf.by);
170   mtf.first = NULL;
171   mtf.last = NULL;
172   case_nullify (&mtf.buffered_case);
173   mtf.prev_BY = NULL;
174
175   dict_set_case_limit (mtf.dict, dict_get_case_limit (dataset_dict (ds)));
176
177   lex_match (lexer, '/');
178   for (;;)
179     {
180       struct mtf_file *file;
181       enum mtf_type type;
182
183       if (lex_match_id (lexer, "FILE")) 
184         type = MTF_FILE;
185       else if (command == MATCH_FILES && lex_match_id (lexer, "TABLE")) 
186         type = MTF_TABLE;
187       else
188         break;
189       lex_match (lexer, '=');
190
191       if (mtf.n_files >= allocated_files)
192         mtf.files = x2nrealloc (mtf.files, &allocated_files,
193                                 sizeof *mtf.files);
194       mtf.files[mtf.n_files++] = file = xmalloc (sizeof *file);
195       file->type = type;
196       file->reader = NULL;
197       subcase_init_empty (&file->by);
198       file->idx = type == MTF_FILE ? n_files++ : n_tables++;
199       file->vars = NULL;
200       file->var_cnt = 0;
201       file->is_sorted = true;
202       file->handle = NULL;
203       file->dict = NULL;
204       case_nullify (&file->c);
205       file->in_name[0] = '\0';
206       file->in_var = NULL;
207
208       if (lex_match (lexer, '*'))
209         {
210           if (!proc_has_active_file (ds))
211             {
212               msg (SE, _("Cannot specify the active file since no active "
213                          "file has been defined."));
214               goto error;
215             }
216
217           if (proc_make_temporary_transformations_permanent (ds))
218             msg (SE,
219                  _("This command may not be used after TEMPORARY when "
220                    "the active file is an input source.  "
221                    "Temporary transformations will be made permanent."));
222
223           file->dict = dict_clone (dataset_dict (ds));
224         }
225       else
226         {
227           file->handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
228           if (file->handle == NULL)
229             goto error;
230
231           file->reader = any_reader_open (file->handle, &file->dict);
232           if (file->reader == NULL)
233             goto error;
234         }
235
236       while (lex_match (lexer, '/'))
237         if (lex_match_id (lexer, "RENAME"))
238           {
239             if (!parse_dict_rename (lexer, file->dict))
240               goto error;
241           }
242         else if (lex_match_id (lexer, "IN"))
243           {
244             lex_match (lexer, '=');
245             if (lex_token (lexer) != T_ID)
246               {
247                 lex_error (lexer, NULL);
248                 goto error;
249               }
250
251             if (file->in_name[0])
252               {
253                 msg (SE, _("Multiple IN subcommands for a single FILE or "
254                            "TABLE."));
255                 goto error;
256               }
257             strcpy (file->in_name, lex_tokid (lexer));
258             lex_get (lexer);
259           }
260         else if (lex_match_id (lexer, "SORT"))
261           {
262             file->is_sorted = false;
263             saw_sort = true;
264           }
265
266       mtf_merge_dictionary (mtf.dict, file);
267     }
268
269   while (lex_token (lexer) != '.')
270     {
271       if (lex_match (lexer, T_BY))
272         {
273           const struct variable **by_vars;
274           size_t i;
275           bool ok;
276
277           if (saw_by)
278             {
279               lex_sbc_only_once ("BY");
280               goto error;
281             }
282           saw_by = true;
283
284           lex_match (lexer, '=');
285           if (!parse_sort_criteria (lexer, mtf.dict, &mtf.by, &by_vars, NULL))
286             goto error;
287
288           ok = true;
289           for (i = 0; i < mtf.n_files; i++)
290             {
291               struct mtf_file *file = mtf.files[i];
292               size_t j;
293
294               for (j = 0; j < subcase_get_n_values (&mtf.by); j++)
295                 {
296                   const char *name = var_get_name (by_vars[j]);
297                   struct variable *var = dict_lookup_var (file->dict, name);
298                   if (var != NULL)
299                     subcase_add_var (&file->by, var,
300                                      subcase_get_direction (&mtf.by, j));
301                   else
302                     {
303                       if (file->handle != NULL)
304                         msg (SE, _("File %s lacks BY variable %s."),
305                              fh_get_name (file->handle), name);
306                       else
307                         msg (SE, _("Active file lacks BY variable %s."), name);
308                       ok = false;
309                     }
310                 }
311               assert (!ok || subcase_conformable (&file->by,
312                                                   &mtf.files[0]->by));
313             }
314           free (by_vars);
315
316           if (!ok)
317             goto error;
318         }
319       else if (command != UPDATE && lex_match_id (lexer, "FIRST"))
320         {
321           if (first_name[0] != '\0')
322             {
323               lex_sbc_only_once ("FIRST");
324               goto error;
325             }
326
327           lex_match (lexer, '=');
328           if (!lex_force_id (lexer))
329             goto error;
330           strcpy (first_name, lex_tokid (lexer));
331           lex_get (lexer);
332         }
333       else if (command != UPDATE && lex_match_id (lexer, "LAST"))
334         {
335           if (last_name[0] != '\0')
336             {
337               lex_sbc_only_once ("LAST");
338               goto error;
339             }
340
341           lex_match (lexer, '=');
342           if (!lex_force_id (lexer))
343             goto error;
344           strcpy (last_name, lex_tokid (lexer));
345           lex_get (lexer);
346         }
347       else if (lex_match_id (lexer, "MAP"))
348         {
349           /* FIXME. */
350         }
351       else if (lex_match_id (lexer, "DROP"))
352         {
353           if (!parse_dict_drop (lexer, mtf.dict))
354             goto error;
355         }
356       else if (lex_match_id (lexer, "KEEP"))
357         {
358           if (!parse_dict_keep (lexer, mtf.dict))
359             goto error;
360         }
361       else
362         {
363           lex_error (lexer, NULL);
364           goto error;
365         }
366
367       if (!lex_match (lexer, '/') && lex_token (lexer) != '.')
368         {
369           lex_end_of_command (lexer);
370           goto error;
371         }
372     }
373
374   if (!saw_by)
375     {
376       if (command == UPDATE) 
377         {
378           msg (SE, _("The BY subcommand is required."));
379           goto error;
380         }
381       if (n_tables)
382         {
383           msg (SE, _("BY is required when TABLE is specified."));
384           goto error;
385         }
386       if (saw_sort)
387         {
388           msg (SE, _("BY is required when SORT is specified."));
389           goto error;
390         }
391     }
392
393   /* Set up mapping from each file's variables to master
394      variables. */
395   for (i = 0; i < mtf.n_files; i++)
396     {
397       struct mtf_file *file = mtf.files[i];
398       size_t in_var_cnt = dict_get_var_cnt (file->dict);
399       size_t j;
400
401       file->vars = xnmalloc (in_var_cnt, sizeof *file->vars);
402       file->var_cnt = 0;
403       for (j = 0; j < in_var_cnt; j++)
404         {
405           struct variable *in_var = dict_get_var (file->dict, j);
406           struct variable *out_var = dict_lookup_var (mtf.dict,
407                                                       var_get_name (in_var));
408
409           if (out_var != NULL)
410             {
411               struct mtf_variable *mv = &file->vars[file->var_cnt++];
412               mv->in_var = in_var;
413               mv->out_var = out_var;
414             }
415         }
416     }
417
418   /* Add IN, FIRST, and LAST variables to master dictionary. */
419   for (i = 0; i < mtf.n_files; i++)
420     {
421       struct mtf_file *file = mtf.files[i];
422       if (!create_flag_var ("IN", file->in_name, mtf.dict, &file->in_var))
423         goto error; 
424     }
425   if (!create_flag_var ("FIRST", first_name, mtf.dict, &mtf.first)
426       || !create_flag_var ("LAST", last_name, mtf.dict, &mtf.last))
427     goto error;
428
429   dict_delete_scratch_vars (mtf.dict);
430   dict_compact_values (mtf.dict);
431   mtf.output = autopaging_writer_create (dict_get_next_value_idx (mtf.dict));
432   taint = taint_clone (casewriter_get_taint (mtf.output));
433
434   mtf.matcher = case_matcher_create ();
435   taint_propagate (case_matcher_get_taint (mtf.matcher), taint);
436   for (i = 0; i < mtf.n_files; i++)
437     {
438       struct mtf_file *file = mtf.files[i];
439       if (file->reader == NULL)
440         {
441           if (active_file == NULL)
442             {
443               proc_discard_output (ds);
444               file->reader = active_file = proc_open (ds);
445             }
446           else
447             file->reader = casereader_clone (active_file);
448         }
449       if (!file->is_sorted) 
450         file->reader = sort_execute (file->reader, &file->by);
451       if (file->type == MTF_FILE)
452         case_matcher_add_input (mtf.matcher, file->reader, &file->by);
453       else 
454         {
455           casereader_read (file->reader, &file->c);
456           taint_propagate (casereader_get_taint (file->reader), taint);
457         }
458     }
459
460   if (command == ADD_FILES)
461     process_add_files (&mtf);
462   else if (command == MATCH_FILES)
463     process_match_files (&mtf);
464   else if (command == UPDATE)
465     process_update (&mtf);
466   else
467     NOT_REACHED ();
468
469   case_matcher_destroy (mtf.matcher);
470   mtf_close_all_files (&mtf);
471   if (active_file != NULL)
472     proc_commit (ds);
473
474   proc_set_active_file (ds, casewriter_make_reader (mtf.output), mtf.dict);
475   mtf.dict = NULL;
476   mtf.output = NULL;
477
478   mtf_free (&mtf);
479
480   return taint_destroy (taint) ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
481
482  error:
483   if (active_file != NULL)
484     proc_commit (ds);
485   mtf_free (&mtf);
486   taint_destroy (taint);
487   return CMD_CASCADING_FAILURE;
488 }
489
490 /* If VAR_NAME is a non-empty string, attempts to create a
491    variable named VAR_NAME, with format F1.0, in DICT, and stores
492    a pointer to the variable in *VAR.  Returns true if
493    successful, false if the variable name is a duplicate (in
494    which case a message saying that the variable specified on the
495    given SUBCOMMAND is a duplicate is emitted).  Also returns
496    true, without doing anything, if VAR_NAME is null or empty. */
497 static bool
498 create_flag_var (const char *subcommand, const char *var_name,
499                  struct dictionary *dict, struct variable **var)
500 {
501   if (var_name[0] != '\0')
502     {
503       struct fmt_spec format = fmt_for_output (FMT_F, 1, 0);
504       *var = dict_create_var (dict, var_name, 0);
505       if (*var == NULL)
506         {
507           msg (SE, _("Variable name %s specified on %s subcommand "
508                      "duplicates an existing variable name."),
509                subcommand, var_name);
510           return false;
511         }
512       var_set_both_formats (*var, &format);
513     }
514   else
515     *var = NULL;
516   return true;
517 }
518
519 /* Return a string in an allocated buffer describing V's variable
520    type and width. */
521 static char *
522 var_type_description (struct variable *v)
523 {
524   if (var_is_numeric (v))
525     return xstrdup ("numeric");
526   else
527     return xasprintf ("string with width %d", var_get_width (v));
528 }
529
530 /* Closes all the files in MTF and frees their associated data.
531    Returns true if successful, false if an I/O error occurred on
532    any of the files. */
533 static bool
534 mtf_close_all_files (struct mtf_proc *mtf)
535 {
536   bool ok = true;
537   size_t i;
538
539   for (i = 0; i < mtf->n_files; i++)
540     {
541       struct mtf_file *file = mtf->files[i];
542       fh_unref (file->handle);
543       dict_destroy (file->dict);
544       subcase_destroy (&file->by);
545       if (file->type == MTF_TABLE)
546         casereader_destroy (file->reader);
547       free (file->vars);
548       free (file);
549     }
550   free (mtf->files);
551   mtf->files = NULL;
552   mtf->n_files = 0;
553
554   return ok;
555 }
556
557 /* Frees all the data for the procedure. */
558 static void
559 mtf_free (struct mtf_proc *mtf)
560 {
561   mtf_close_all_files (mtf);
562   dict_destroy (mtf->dict);
563   subcase_destroy (&mtf->by);
564   casewriter_destroy (mtf->output);
565   case_destroy (&mtf->buffered_case);
566   free (mtf->prev_BY);
567 }
568
569 static bool
570 scan_table (struct mtf_file *file, union value *by) 
571 {
572   while (!case_is_null (&file->c))
573     {
574       int cmp = subcase_compare_3way_xc (&file->by, by, &file->c);
575       if (cmp > 0)
576         casereader_read (file->reader, &file->c);
577       else
578         return cmp == 0;
579     }
580   return false;
581 }
582
583 static void
584 create_output_case (const struct mtf_proc *mtf, struct ccase *c)
585 {
586   size_t i;
587
588   case_create (c, dict_get_next_value_idx (mtf->dict));
589   for (i = 0; i < dict_get_var_cnt (mtf->dict); i++)
590     {
591       struct variable *v = dict_get_var (mtf->dict, i);
592       value_set_missing (case_data_rw (c, v), var_get_width (v));
593     }
594   for (i = 0; i < mtf->n_files; i++)
595     {
596       struct mtf_file *file = mtf->files[i];
597       if (file->in_var != NULL)
598         case_data_rw (c, file->in_var)->f = false;
599     }
600 }
601
602 static void
603 apply_case (const struct mtf_file *file, struct ccase *file_case,
604             struct ccase *c) 
605 {
606   /* XXX subcases */
607   size_t j;
608   for (j = 0; j < file->var_cnt; j++)
609     {
610       const struct mtf_variable *mv = &file->vars[j];
611       const union value *in = case_data (file_case, mv->in_var);
612       union value *out = case_data_rw (c, mv->out_var);
613       value_copy (out, in, var_get_width (mv->in_var));
614     }
615   case_destroy (file_case);
616   if (file->in_var != NULL)
617     case_data_rw (c, file->in_var)->f = true; 
618 }
619
620 static size_t
621 find_first_match (struct ccase *cases) 
622 {
623   size_t i;
624   for (i = 0; ; i++)
625     if (!case_is_null (&cases[i]))
626       return i;
627 }
628
629 static void
630 process_update (struct mtf_proc *mtf)
631 {
632   struct ccase *cases;
633   union value *by;
634
635   while (case_matcher_read (mtf->matcher, &cases, &by))
636     {
637       struct mtf_file *min;
638       struct ccase c;
639       size_t min_idx;
640       size_t i;
641
642       create_output_case (mtf, &c);
643       min_idx = find_first_match (cases);
644       min = mtf->files[min_idx];
645       apply_case (min, &cases[min_idx], &c);
646       case_matcher_advance (mtf->matcher, min_idx, &cases[min_idx]);
647       for (i = MAX (1, min_idx); i < mtf->n_files; i++)
648         while (!case_is_null (&cases[i]))
649           {
650             apply_case (mtf->files[i], &cases[i], &c);
651             case_matcher_advance (mtf->matcher, i, &cases[i]);
652           }
653       casewriter_write (mtf->output, &c);
654       
655       if (min_idx == 0)
656         {
657           size_t n_dups;
658
659           for (n_dups = 0; !case_is_null (&cases[0]); n_dups++)
660             {
661               create_output_case (mtf, &c);
662               apply_case (mtf->files[0], &cases[0], &c);
663               case_matcher_advance (mtf->matcher, 0, &cases[0]);
664               casewriter_write (mtf->output, &c);
665             }
666 #if 0
667           if (n_dups > 0) 
668             msg (SW, _("Encountered %zu duplicates."), n_dups);
669 #endif
670           /* XXX warn.  That's the whole point; otherwise we
671              don't need the 'if' statement at all. */
672         }
673     }
674 }
675
676 /* Executes MATCH FILES for key-based matches. */
677 static void
678 process_match_files (struct mtf_proc *mtf)
679 {
680   union value *by;
681   struct ccase *cases;
682
683   while (case_matcher_read (mtf->matcher, &cases, &by))
684     {
685       struct ccase c;
686       size_t i;
687
688       create_output_case (mtf, &c);
689       for (i = mtf->n_files; i-- > 0; )
690         {
691           struct mtf_file *file = mtf->files[i];
692           struct ccase *file_case;
693           bool include;
694           if (file->type == MTF_FILE) 
695             {
696               file_case = &cases[file->idx];
697               include = !case_is_null (file_case);
698               if (include)
699                 case_matcher_advance (mtf->matcher, file->idx, NULL);
700             }
701           else
702             {
703               file_case = &file->c;
704               include = scan_table (file, by);
705               if (include)
706                 case_clone (file_case, file_case);
707             }
708           if (include) 
709             apply_case (file, file_case, &c);
710         }
711       output_case (mtf, &c, by);
712     }
713   output_buffered_case (mtf);
714 }
715
716 /* Processes input files and write one case to the output file. */
717 static void
718 process_add_files (struct mtf_proc *mtf)
719 {
720   union value *by;
721   struct ccase *cases;
722
723   while (case_matcher_read (mtf->matcher, &cases, &by))
724     {
725       struct ccase c;
726       size_t i;
727
728       for (i = 0; i < mtf->n_files; i++)
729         {
730           struct mtf_file *file = mtf->files[i];
731           while (!case_is_null (&cases[i])) 
732             {
733               create_output_case (mtf, &c);
734               apply_case (file, &cases[i], &c);
735               case_matcher_advance (mtf->matcher, i, &cases[i]);
736               output_case (mtf, &c, by);
737             }
738         }
739     }
740   output_buffered_case (mtf);
741 }
742
743 static void
744 output_case (struct mtf_proc *mtf, struct ccase *c, union value *by)
745 {
746   if (mtf->first == NULL && mtf->last == NULL)
747     casewriter_write (mtf->output, c);
748   else
749     {
750       /* It's harder with LAST, because we can't know whether
751          this case is the last in a group until we've prepared
752          the *next* case also.  Thus, we buffer the previous
753          output case until the next one is ready. */
754       bool new_BY;
755       if (mtf->prev_BY != NULL)
756         {
757           new_BY = !subcase_equal_xx (&mtf->by, mtf->prev_BY, by);
758           if (mtf->last != NULL)
759             case_data_rw (&mtf->buffered_case, mtf->last)->f = new_BY;
760           casewriter_write (mtf->output, &mtf->buffered_case);
761         }
762       else
763         new_BY = true;
764
765       case_move (&mtf->buffered_case, c);
766       if (mtf->first != NULL)
767         case_data_rw (&mtf->buffered_case, mtf->first)->f = new_BY;
768
769       if (new_BY)
770         {
771           size_t n = subcase_get_n_values (&mtf->by) * sizeof (union value);
772           if (mtf->prev_BY == NULL)
773             mtf->prev_BY = xmalloc (n);
774           memcpy (mtf->prev_BY, by, n);
775         }
776     }
777 }
778
779 static void
780 output_buffered_case (struct mtf_proc *mtf) 
781 {
782   if (mtf->prev_BY != NULL)
783     {
784       if (mtf->last != NULL)
785         case_data_rw (&mtf->buffered_case, mtf->last)->f = 1.0;
786       casewriter_write (mtf->output, &mtf->buffered_case);
787       case_nullify (&mtf->buffered_case);
788     }
789 }
790
791 /* Merge the dictionary for file F into master dictionary M. */
792 static bool
793 mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
794 {
795   struct dictionary *d = f->dict;
796   const char *d_docs, *m_docs;
797   int i;
798
799   if (dict_get_label (m) == NULL)
800     dict_set_label (m, dict_get_label (d));
801
802   d_docs = dict_get_documents (d);
803   m_docs = dict_get_documents (m);
804   if (d_docs != NULL)
805     {
806       if (m_docs == NULL)
807         dict_set_documents (m, d_docs);
808       else
809         {
810           char *new_docs = xasprintf ("%s%s", m_docs, d_docs);
811           dict_set_documents (m, new_docs);
812           free (new_docs);
813         }
814     }
815
816   for (i = 0; i < dict_get_var_cnt (d); i++)
817     {
818       struct variable *dv = dict_get_var (d, i);
819       struct variable *mv = dict_lookup_var (m, var_get_name (dv));
820
821       if (dict_class_from_id (var_get_name (dv)) == DC_SCRATCH)
822         continue;
823
824       if (mv != NULL)
825         {
826           if (var_get_width (mv) != var_get_width (dv))
827             {
828               char *dv_description = var_type_description (dv);
829               char *mv_description = var_type_description (mv);
830               msg (SE, _("Variable %s in file %s (%s) has different "
831                          "type or width from the same variable in "
832                          "earlier file (%s)."),
833                    var_get_name (dv), fh_get_name (f->handle),
834                    dv_description, mv_description);
835               free (dv_description);
836               free (mv_description);
837               return false;
838             }
839
840           if (var_has_value_labels (dv) && !var_has_value_labels (mv))
841             var_set_value_labels (mv, var_get_value_labels (dv));
842           if (var_has_missing_values (dv) && !var_has_missing_values (mv))
843             var_set_missing_values (mv, var_get_missing_values (dv));
844           if (var_get_label (dv) && !var_get_label (mv))
845             var_set_label (mv, var_get_label (dv));
846         }
847       else
848         mv = dict_clone_var_assert (m, dv, var_get_name (dv));
849     }
850
851   return true;
852 }