Some more cleanup with value labels.
[pspp-builds.git] / src / language / data-io / get.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23
24 #include <data/any-reader.h>
25 #include <data/any-writer.h>
26 #include <data/case-sink.h>
27 #include <data/case-source.h>
28 #include <data/case.h>
29 #include <data/casefile.h>
30 #include <data/dictionary.h>
31 #include <data/por-file-writer.h>
32 #include <data/procedure.h>
33 #include <data/settings.h>
34 #include <data/storage-stream.h>
35 #include <data/sys-file-writer.h>
36 #include <data/transformations.h>
37 #include <data/value-labels.h>
38 #include <data/variable.h>
39 #include <language/command.h>
40 #include <language/data-io/file-handle.h>
41 #include <language/lexer/lexer.h>
42 #include <libpspp/alloc.h>
43 #include <libpspp/compiler.h>
44 #include <libpspp/hash.h>
45 #include <libpspp/message.h>
46 #include <libpspp/message.h>
47 #include <libpspp/misc.h>
48 #include <libpspp/str.h>
49
50 #include "gettext.h"
51 #define _(msgid) gettext (msgid)
52
53 /* Rearranging and reducing a dictionary. */
54 static void start_case_map (struct dictionary *);
55 static struct case_map *finish_case_map (struct dictionary *);
56 static void map_case (const struct case_map *,
57                       const struct ccase *, struct ccase *);
58 static void destroy_case_map (struct case_map *);
59
60 static bool parse_dict_trim (struct dictionary *);
61 \f
62 /* Reading system and portable files. */
63
64 /* Type of command. */
65 enum reader_command 
66   {
67     GET_CMD,
68     IMPORT_CMD
69   };
70
71 /* Case reader input program. */
72 struct case_reader_pgm 
73   {
74     struct any_reader *reader;  /* File reader. */
75     struct case_map *map;       /* Map from file dict to active file dict. */
76     struct ccase bounce;        /* Bounce buffer. */
77   };
78
79 static const struct case_source_class case_reader_source_class;
80
81 static void case_reader_pgm_free (struct case_reader_pgm *);
82
83 /* Parses a GET or IMPORT command. */
84 static int
85 parse_read_command (enum reader_command type)
86 {
87   struct case_reader_pgm *pgm = NULL;
88   struct file_handle *fh = NULL;
89   struct dictionary *dict = NULL;
90
91   for (;;)
92     {
93       lex_match ('/');
94
95       if (lex_match_id ("FILE") || token == T_STRING)
96         {
97           lex_match ('=');
98
99           fh = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
100           if (fh == NULL)
101             goto error;
102         }
103       else if (type == IMPORT_CMD && lex_match_id ("TYPE"))
104         {
105           lex_match ('=');
106
107           if (lex_match_id ("COMM"))
108             type = PFM_COMM;
109           else if (lex_match_id ("TAPE"))
110             type = PFM_TAPE;
111           else
112             {
113               lex_error (_("expecting COMM or TAPE"));
114               goto error;
115             }
116         }
117       else
118         break; 
119     }
120   
121   if (fh == NULL) 
122     {
123       lex_sbc_missing ("FILE");
124       goto error;
125     }
126               
127   discard_variables ();
128
129   pgm = xmalloc (sizeof *pgm);
130   pgm->reader = any_reader_open (fh, &dict);
131   pgm->map = NULL;
132   case_nullify (&pgm->bounce);
133   if (pgm->reader == NULL)
134     goto error;
135
136   case_create (&pgm->bounce, dict_get_next_value_idx (dict));
137   
138   start_case_map (dict);
139
140   while (token != '.')
141     {
142       lex_match ('/');
143       if (!parse_dict_trim (dict))
144         goto error;
145     }
146
147   pgm->map = finish_case_map (dict);
148   
149   dict_destroy (default_dict);
150   default_dict = dict;
151
152   proc_set_source (create_case_source (&case_reader_source_class, pgm));
153
154   return CMD_SUCCESS;
155
156  error:
157   case_reader_pgm_free (pgm);
158   if (dict != NULL)
159     dict_destroy (dict);
160   return CMD_CASCADING_FAILURE;
161 }
162
163 /* Frees a struct case_reader_pgm. */
164 static void
165 case_reader_pgm_free (struct case_reader_pgm *pgm) 
166 {
167   if (pgm != NULL) 
168     {
169       any_reader_close (pgm->reader);
170       destroy_case_map (pgm->map);
171       case_destroy (&pgm->bounce);
172       free (pgm);
173     }
174 }
175
176 /* Clears internal state related to case reader input procedure. */
177 static void
178 case_reader_source_destroy (struct case_source *source)
179 {
180   struct case_reader_pgm *pgm = source->aux;
181   case_reader_pgm_free (pgm);
182 }
183
184 /* Reads all the cases from the data file into C and passes them
185    to WRITE_CASE one by one, passing WC_DATA.
186    Returns true if successful, false if an I/O error occurred. */
187 static bool
188 case_reader_source_read (struct case_source *source,
189                          struct ccase *c,
190                          write_case_func *write_case, write_case_data wc_data)
191 {
192   struct case_reader_pgm *pgm = source->aux;
193   bool ok = true;
194
195   do
196     {
197       bool got_case;
198       if (pgm->map == NULL)
199         got_case = any_reader_read (pgm->reader, c);
200       else
201         {
202           got_case = any_reader_read (pgm->reader, &pgm->bounce);
203           if (got_case)
204             map_case (pgm->map, &pgm->bounce, c);
205         }
206       if (!got_case)
207         break;
208
209       ok = write_case (wc_data);
210     }
211   while (ok);
212
213   return ok && !any_reader_error (pgm->reader);
214 }
215
216 static const struct case_source_class case_reader_source_class =
217   {
218     "case reader",
219     NULL,
220     case_reader_source_read,
221     case_reader_source_destroy,
222   };
223 \f
224 /* GET. */
225 int
226 cmd_get (void) 
227 {
228   return parse_read_command (GET_CMD);
229 }
230
231 /* IMPORT. */
232 int
233 cmd_import (void) 
234 {
235   return parse_read_command (IMPORT_CMD);
236 }
237 \f
238 /* Writing system and portable files. */ 
239
240 /* Type of output file. */
241 enum writer_type
242   {
243     SYSFILE_WRITER,     /* System file. */
244     PORFILE_WRITER      /* Portable file. */
245   };
246
247 /* Type of a command. */
248 enum command_type 
249   {
250     XFORM_CMD,          /* Transformation. */
251     PROC_CMD            /* Procedure. */
252   };
253
254 /* File writer plus a case map. */
255 struct case_writer
256   {
257     struct any_writer *writer;  /* File writer. */
258     struct case_map *map;       /* Map to output file dictionary
259                                    (null pointer for identity mapping). */
260     struct ccase bounce;        /* Bounce buffer for mapping (if needed). */
261   };
262
263 /* Destroys AW. */
264 static bool
265 case_writer_destroy (struct case_writer *aw)
266 {
267   bool ok = true;
268   if (aw != NULL) 
269     {
270       ok = any_writer_close (aw->writer);
271       destroy_case_map (aw->map);
272       case_destroy (&aw->bounce);
273       free (aw);
274     }
275   return ok;
276 }
277
278 /* Parses SAVE or XSAVE or EXPORT or XEXPORT command.
279    WRITER_TYPE identifies the type of file to write,
280    and COMMAND_TYPE identifies the type of command.
281
282    On success, returns a writer.
283    For procedures only, sets *RETAIN_UNSELECTED to true if cases
284    that would otherwise be excluded by FILTER or USE should be
285    included.
286
287    On failure, returns a null pointer. */
288 static struct case_writer *
289 parse_write_command (enum writer_type writer_type,
290                      enum command_type command_type,
291                      bool *retain_unselected)
292 {
293   /* Common data. */
294   struct file_handle *handle; /* Output file. */
295   struct dictionary *dict;    /* Dictionary for output file. */
296   struct case_writer *aw;      /* Writer. */  
297
298   /* Common options. */
299   bool print_map;             /* Print map?  TODO. */
300   bool print_short_names;     /* Print long-to-short name map.  TODO. */
301   struct sfm_write_options sysfile_opts;
302   struct pfm_write_options porfile_opts;
303
304   assert (writer_type == SYSFILE_WRITER || writer_type == PORFILE_WRITER);
305   assert (command_type == XFORM_CMD || command_type == PROC_CMD);
306   assert ((retain_unselected != NULL) == (command_type == PROC_CMD));
307
308   if (command_type == PROC_CMD)
309     *retain_unselected = true;
310
311   handle = NULL;
312   dict = dict_clone (default_dict);
313   aw = xmalloc (sizeof *aw);
314   aw->writer = NULL;
315   aw->map = NULL;
316   case_nullify (&aw->bounce);
317   print_map = false;
318   print_short_names = false;
319   sysfile_opts = sfm_writer_default_options ();
320   porfile_opts = pfm_writer_default_options ();
321
322   start_case_map (dict);
323   dict_delete_scratch_vars (dict);
324
325   lex_match ('/');
326   for (;;)
327     {
328       if (lex_match_id ("OUTFILE"))
329         {
330           if (handle != NULL) 
331             {
332               lex_sbc_only_once ("OUTFILE");
333               goto error; 
334             }
335           
336           lex_match ('=');
337       
338           handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
339           if (handle == NULL)
340             goto error;
341         }
342       else if (lex_match_id ("NAMES"))
343         print_short_names = true;
344       else if (lex_match_id ("PERMISSIONS")) 
345         {
346           bool cw;
347           
348           lex_match ('=');
349           if (lex_match_id ("READONLY"))
350             cw = false;
351           else if (lex_match_id ("WRITEABLE"))
352             cw = true;
353           else
354             {
355               lex_error (_("expecting %s or %s"), "READONLY", "WRITEABLE");
356               goto error;
357             }
358           sysfile_opts.create_writeable = porfile_opts.create_writeable = cw;
359         }
360       else if (command_type == PROC_CMD && lex_match_id ("UNSELECTED")) 
361         {
362           lex_match ('=');
363           if (lex_match_id ("RETAIN"))
364             *retain_unselected = true;
365           else if (lex_match_id ("DELETE"))
366             *retain_unselected = false;
367           else
368             {
369               lex_error (_("expecting %s or %s"), "RETAIN", "DELETE");
370               goto error;
371             }
372         }
373       else if (writer_type == SYSFILE_WRITER && lex_match_id ("COMPRESSED"))
374         sysfile_opts.compress = true;
375       else if (writer_type == SYSFILE_WRITER && lex_match_id ("UNCOMPRESSED"))
376         sysfile_opts.compress = false;
377       else if (writer_type == SYSFILE_WRITER && lex_match_id ("VERSION"))
378         {
379           lex_match ('=');
380           if (!lex_force_int ())
381             goto error;
382           sysfile_opts.version = lex_integer ();
383           lex_get ();
384         }
385       else if (writer_type == PORFILE_WRITER && lex_match_id ("TYPE")) 
386         {
387           lex_match ('=');
388           if (lex_match_id ("COMMUNICATIONS"))
389             porfile_opts.type = PFM_COMM;
390           else if (lex_match_id ("TAPE"))
391             porfile_opts.type = PFM_TAPE;
392           else
393             {
394               lex_error (_("expecting %s or %s"), "COMM", "TAPE");
395               goto error;
396             }
397         }
398       else if (writer_type == PORFILE_WRITER && lex_match_id ("DIGITS")) 
399         {
400           lex_match ('=');
401           if (!lex_force_int ())
402             goto error;
403           porfile_opts.digits = lex_integer ();
404           lex_get ();
405         }
406       else if (!parse_dict_trim (dict))
407         goto error;
408       
409       if (!lex_match ('/'))
410         break;
411     }
412   if (lex_end_of_command () != CMD_SUCCESS)
413     goto error;
414
415   if (handle == NULL) 
416     {
417       lex_sbc_missing ("OUTFILE");
418       goto error;
419     }
420
421   dict_compact_values (dict);
422   aw->map = finish_case_map (dict);
423   if (aw->map != NULL)
424     case_create (&aw->bounce, dict_get_next_value_idx (dict));
425
426   if (fh_get_referent (handle) == FH_REF_FILE) 
427     {
428       switch (writer_type) 
429         {
430         case SYSFILE_WRITER:
431           aw->writer = any_writer_from_sfm_writer (
432             sfm_open_writer (handle, dict, sysfile_opts));
433           break;
434         case PORFILE_WRITER:
435           aw->writer = any_writer_from_pfm_writer (
436             pfm_open_writer (handle, dict, porfile_opts));
437           break;
438         }
439     }
440   else
441     aw->writer = any_writer_open (handle, dict);
442   dict_destroy (dict);
443   
444   return aw;
445
446  error:
447   case_writer_destroy (aw);
448   dict_destroy (dict);
449   return NULL;
450 }
451
452 /* Writes case C to writer AW. */
453 static bool
454 case_writer_write_case (struct case_writer *aw, const struct ccase *c) 
455 {
456   if (aw->map != NULL) 
457     {
458       map_case (aw->map, c, &aw->bounce);
459       c = &aw->bounce; 
460     }
461   return any_writer_write (aw->writer, c);
462 }
463 \f
464 /* SAVE and EXPORT. */
465
466 static bool output_proc (const struct ccase *, void *);
467
468 /* Parses and performs the SAVE or EXPORT procedure. */
469 static int
470 parse_output_proc (enum writer_type writer_type)
471 {
472   bool retain_unselected;
473   struct variable *saved_filter_variable;
474   struct case_writer *aw;
475   bool ok;
476
477   aw = parse_write_command (writer_type, PROC_CMD, &retain_unselected);
478   if (aw == NULL) 
479     return CMD_CASCADING_FAILURE;
480
481   saved_filter_variable = dict_get_filter (default_dict);
482   if (retain_unselected) 
483     dict_set_filter (default_dict, NULL);
484   ok = procedure (output_proc, aw);
485   dict_set_filter (default_dict, saved_filter_variable);
486
487   case_writer_destroy (aw);
488   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
489 }
490
491 /* Writes case C to file. */
492 static bool
493 output_proc (const struct ccase *c, void *aw_) 
494 {
495   struct case_writer *aw = aw_;
496   return case_writer_write_case (aw, c);
497 }
498
499 int
500 cmd_save (void) 
501 {
502   return parse_output_proc (SYSFILE_WRITER);
503 }
504
505 int
506 cmd_export (void) 
507 {
508   return parse_output_proc (PORFILE_WRITER);
509 }
510 \f
511 /* XSAVE and XEXPORT. */
512
513 /* Transformation. */
514 struct output_trns 
515   {
516     struct case_writer *aw;      /* Writer. */
517   };
518
519 static trns_proc_func output_trns_proc;
520 static trns_free_func output_trns_free;
521
522 /* Parses the XSAVE or XEXPORT transformation command. */
523 static int
524 parse_output_trns (enum writer_type writer_type) 
525 {
526   struct output_trns *t = xmalloc (sizeof *t);
527   t->aw = parse_write_command (writer_type, XFORM_CMD, NULL);
528   if (t->aw == NULL) 
529     {
530       free (t);
531       return CMD_CASCADING_FAILURE;
532     }
533
534   add_transformation (output_trns_proc, output_trns_free, t);
535   return CMD_SUCCESS;
536 }
537
538 /* Writes case C to the system file specified on XSAVE or XEXPORT. */
539 static int
540 output_trns_proc (void *trns_, struct ccase *c, int case_num UNUSED)
541 {
542   struct output_trns *t = trns_;
543   case_writer_write_case (t->aw, c);
544   return TRNS_CONTINUE;
545 }
546
547 /* Frees an XSAVE or XEXPORT transformation.
548    Returns true if successful, false if an I/O error occurred. */
549 static bool
550 output_trns_free (void *trns_)
551 {
552   struct output_trns *t = trns_;
553   bool ok = true;
554
555   if (t != NULL)
556     {
557       ok = case_writer_destroy (t->aw);
558       free (t);
559     }
560   return ok;
561 }
562
563 /* XSAVE command. */
564 int
565 cmd_xsave (void) 
566 {
567   return parse_output_trns (SYSFILE_WRITER);
568 }
569
570 /* XEXPORT command. */
571 int
572 cmd_xexport (void) 
573 {
574   return parse_output_trns (PORFILE_WRITER);
575 }
576 \f
577 static bool rename_variables (struct dictionary *dict);
578 static bool drop_variables (struct dictionary *dict);
579 static bool keep_variables (struct dictionary *dict);
580
581 /* Commands that read and write system files share a great deal
582    of common syntactic structure for rearranging and dropping
583    variables.  This function parses this syntax and modifies DICT
584    appropriately.  Returns true on success, false on failure. */
585 static bool
586 parse_dict_trim (struct dictionary *dict)
587 {
588   if (lex_match_id ("MAP")) 
589     {
590       /* FIXME. */
591       return true;
592     }
593   else if (lex_match_id ("DROP"))
594     return drop_variables (dict);
595   else if (lex_match_id ("KEEP"))
596     return keep_variables (dict);
597   else if (lex_match_id ("RENAME"))
598     return rename_variables (dict);
599   else
600     {
601       lex_error (_("expecting a valid subcommand"));
602       return false;
603     }
604 }
605
606 /* Parses and performs the RENAME subcommand of GET and SAVE. */
607 static bool
608 rename_variables (struct dictionary *dict)
609 {
610   size_t i;
611
612   int success = 0;
613
614   struct variable **v;
615   char **new_names;
616   size_t nv, nn;
617   char *err_name;
618
619   int group;
620
621   lex_match ('=');
622   if (token != '(')
623     {
624       struct variable *v;
625
626       v = parse_dict_variable (dict);
627       if (v == NULL)
628         return 0;
629       if (!lex_force_match ('=')
630           || !lex_force_id ())
631         return 0;
632       if (dict_lookup_var (dict, tokid) != NULL)
633         {
634           msg (SE, _("Cannot rename %s as %s because there already exists "
635                      "a variable named %s.  To rename variables with "
636                      "overlapping names, use a single RENAME subcommand "
637                      "such as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, "
638                      "\"/RENAME (A B C=B C A)\"."), v->name, tokid, tokid);
639           return 0;
640         }
641       
642       dict_rename_var (dict, v, tokid);
643       lex_get ();
644       return 1;
645     }
646
647   nv = nn = 0;
648   v = NULL;
649   new_names = 0;
650   group = 1;
651   while (lex_match ('('))
652     {
653       size_t old_nv = nv;
654
655       if (!parse_variables (dict, &v, &nv, PV_NO_DUPLICATE | PV_APPEND))
656         goto done;
657       if (!lex_match ('='))
658         {
659           msg (SE, _("`=' expected after variable list."));
660           goto done;
661         }
662       if (!parse_DATA_LIST_vars (&new_names, &nn, PV_APPEND | PV_NO_SCRATCH))
663         goto done;
664       if (nn != nv)
665         {
666           msg (SE, _("Number of variables on left side of `=' (%d) does not "
667                      "match number of variables on right side (%d), in "
668                      "parenthesized group %d of RENAME subcommand."),
669                (unsigned) (nv - old_nv), (unsigned) (nn - old_nv), group);
670           goto done;
671         }
672       if (!lex_force_match (')'))
673         goto done;
674       group++;
675     }
676
677   if (!dict_rename_vars (dict, v, new_names, nv, &err_name)) 
678     {
679       msg (SE, _("Requested renaming duplicates variable name %s."), err_name);
680       goto done;
681     }
682   success = 1;
683
684  done:
685   for (i = 0; i < nn; i++)
686     free (new_names[i]);
687   free (new_names);
688   free (v);
689
690   return success;
691 }
692
693 /* Parses and performs the DROP subcommand of GET and SAVE.
694    Returns true if successful, false on failure.*/
695 static bool
696 drop_variables (struct dictionary *dict)
697 {
698   struct variable **v;
699   size_t nv;
700
701   lex_match ('=');
702   if (!parse_variables (dict, &v, &nv, PV_NONE))
703     return false;
704   dict_delete_vars (dict, v, nv);
705   free (v);
706
707   if (dict_get_var_cnt (dict) == 0)
708     {
709       msg (SE, _("Cannot DROP all variables from dictionary."));
710       return false;
711     }
712   return true;
713 }
714
715 /* Parses and performs the KEEP subcommand of GET and SAVE.
716    Returns true if successful, false on failure.*/
717 static bool
718 keep_variables (struct dictionary *dict)
719 {
720   struct variable **v;
721   size_t nv;
722   size_t i;
723
724   lex_match ('=');
725   if (!parse_variables (dict, &v, &nv, PV_NONE))
726     return false;
727
728   /* Move the specified variables to the beginning. */
729   dict_reorder_vars (dict, v, nv);
730           
731   /* Delete the remaining variables. */
732   v = xnrealloc (v, dict_get_var_cnt (dict) - nv, sizeof *v);
733   for (i = nv; i < dict_get_var_cnt (dict); i++)
734     v[i - nv] = dict_get_var (dict, i);
735   dict_delete_vars (dict, v, dict_get_var_cnt (dict) - nv);
736   free (v);
737
738   return true;
739 }
740 \f
741 /* MATCH FILES. */
742
743 /* File types. */
744 enum
745   {
746     MTF_FILE,                   /* Specified on FILE= subcommand. */
747     MTF_TABLE                   /* Specified on TABLE= subcommand. */
748   };
749
750 /* One of the files on MATCH FILES. */
751 struct mtf_file
752   {
753     struct mtf_file *next, *prev; /* Next, previous in the list of files. */
754     struct mtf_file *next_min;  /* Next in the chain of minimums. */
755     
756     int type;                   /* One of MTF_*. */
757     struct variable **by;       /* List of BY variables for this file. */
758     struct file_handle *handle; /* File handle. */
759     struct any_reader *reader;  /* File reader. */
760     struct dictionary *dict;    /* Dictionary from system file. */
761
762     /* IN subcommand. */
763     char *in_name;              /* Variable name. */
764     struct variable *in_var;    /* Variable (in master dictionary). */
765
766     struct ccase input;         /* Input record. */
767   };
768
769 /* MATCH FILES procedure. */
770 struct mtf_proc 
771   {
772     struct mtf_file *head;      /* First file mentioned on FILE or TABLE. */
773     struct mtf_file *tail;      /* Last file mentioned on FILE or TABLE. */
774
775     bool ok;                    /* False if I/O error occurs. */
776
777     size_t by_cnt;              /* Number of variables on BY subcommand. */
778
779     /* Names of FIRST, LAST variables. */
780     char first[LONG_NAME_LEN + 1], last[LONG_NAME_LEN + 1];
781     
782     struct dictionary *dict;    /* Dictionary of output file. */
783     struct casefile *output;    /* MATCH FILES output. */
784     struct ccase mtf_case;      /* Case used for output. */
785
786     unsigned seq_num;           /* Have we initialized this variable? */
787     unsigned *seq_nums;         /* Sequence numbers for each var in dict. */
788   };
789
790 static bool mtf_free (struct mtf_proc *);
791 static bool mtf_close_file (struct mtf_file *);
792 static int mtf_merge_dictionary (struct dictionary *const, struct mtf_file *);
793 static bool mtf_delete_file_in_place (struct mtf_proc *, struct mtf_file **);
794
795 static bool mtf_read_nonactive_records (void *);
796 static bool mtf_processing_finish (void *);
797 static bool mtf_processing (const struct ccase *, void *);
798
799 static char *var_type_description (struct variable *);
800
801 static void set_master (struct variable *, struct variable *master);
802 static struct variable *get_master (struct variable *);
803
804 /* Parse and execute the MATCH FILES command. */
805 int
806 cmd_match_files (void)
807 {
808   struct mtf_proc mtf;
809   struct mtf_file *first_table = NULL;
810   struct mtf_file *iter;
811   
812   bool used_active_file = false;
813   bool saw_table = false;
814   bool saw_in = false;
815
816   bool ok;
817   
818   mtf.head = mtf.tail = NULL;
819   mtf.by_cnt = 0;
820   mtf.first[0] = '\0';
821   mtf.last[0] = '\0';
822   mtf.dict = dict_create ();
823   mtf.output = NULL;
824   case_nullify (&mtf.mtf_case);
825   mtf.seq_num = 0;
826   mtf.seq_nums = NULL;
827   dict_set_case_limit (mtf.dict, dict_get_case_limit (default_dict));
828
829   lex_match ('/');
830   while (token == T_ID
831          && (lex_id_match ("FILE", tokid) || lex_id_match ("TABLE", tokid)))
832     {
833       struct mtf_file *file = xmalloc (sizeof *file);
834
835       if (lex_match_id ("FILE"))
836         file->type = MTF_FILE;
837       else if (lex_match_id ("TABLE"))
838         {
839           file->type = MTF_TABLE;
840           saw_table = true;
841         }
842       else
843         assert (0);
844       lex_match ('=');
845
846       file->by = NULL;
847       file->handle = NULL;
848       file->reader = NULL;
849       file->dict = NULL;
850       file->in_name = NULL;
851       file->in_var = NULL;
852       case_nullify (&file->input);
853
854       /* FILEs go first, then TABLEs. */
855       if (file->type == MTF_TABLE || first_table == NULL)
856         {
857           file->next = NULL;
858           file->prev = mtf.tail;
859           if (mtf.tail)
860             mtf.tail->next = file;
861           mtf.tail = file;
862           if (mtf.head == NULL)
863             mtf.head = file;
864           if (file->type == MTF_TABLE && first_table == NULL)
865             first_table = file;
866         }
867       else 
868         {
869           assert (file->type == MTF_FILE);
870           file->next = first_table;
871           file->prev = first_table->prev;
872           if (first_table->prev)
873             first_table->prev->next = file;
874           else
875             mtf.head = file;
876           first_table->prev = file;
877         }
878
879       if (lex_match ('*'))
880         {
881           file->handle = NULL;
882           file->reader = NULL;
883               
884           if (used_active_file)
885             {
886               msg (SE, _("The active file may not be specified more "
887                          "than once."));
888               goto error;
889             }
890           used_active_file = true;
891
892           if (!proc_has_source ())
893             {
894               msg (SE, _("Cannot specify the active file since no active "
895                          "file has been defined."));
896               goto error;
897             }
898
899           if (proc_make_temporary_transformations_permanent ())
900             msg (SE,
901                  _("MATCH FILES may not be used after TEMPORARY when "
902                    "the active file is an input source.  "
903                    "Temporary transformations will be made permanent."));
904
905           file->dict = default_dict;
906         }
907       else
908         {
909           file->handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
910           if (file->handle == NULL)
911             goto error;
912
913           file->reader = any_reader_open (file->handle, &file->dict);
914           if (file->reader == NULL)
915             goto error;
916
917           case_create (&file->input, dict_get_next_value_idx (file->dict));
918         }
919
920       while (lex_match ('/'))
921         if (lex_match_id ("RENAME")) 
922           {
923             if (!rename_variables (file->dict))
924               goto error; 
925           }
926         else if (lex_match_id ("IN"))
927           {
928             lex_match ('=');
929             if (token != T_ID)
930               {
931                 lex_error (NULL);
932                 goto error;
933               }
934
935             if (file->in_name != NULL)
936               {
937                 msg (SE, _("Multiple IN subcommands for a single FILE or "
938                            "TABLE."));
939                 goto error;
940               }
941             file->in_name = xstrdup (tokid);
942             lex_get ();
943             saw_in = true;
944           }
945
946       mtf_merge_dictionary (mtf.dict, file);
947     }
948   
949   while (token != '.')
950     {
951       if (lex_match (T_BY))
952         {
953           struct variable **by;
954           
955           if (mtf.by_cnt)
956             {
957               msg (SE, _("BY may appear at most once."));
958               goto error;
959             }
960               
961           lex_match ('=');
962           if (!parse_variables (mtf.dict, &by, &mtf.by_cnt,
963                                 PV_NO_DUPLICATE | PV_NO_SCRATCH))
964             goto error;
965
966           for (iter = mtf.head; iter != NULL; iter = iter->next)
967             {
968               size_t i;
969           
970               iter->by = xnmalloc (mtf.by_cnt, sizeof *iter->by);
971
972               for (i = 0; i < mtf.by_cnt; i++)
973                 {
974                   iter->by[i] = dict_lookup_var (iter->dict, by[i]->name);
975                   if (iter->by[i] == NULL)
976                     {
977                       msg (SE, _("File %s lacks BY variable %s."),
978                            iter->handle ? fh_get_name (iter->handle) : "*",
979                            by[i]->name);
980                       free (by);
981                       goto error;
982                     }
983                 }
984             }
985           free (by);
986         }
987       else if (lex_match_id ("FIRST")) 
988         {
989           if (mtf.first[0] != '\0')
990             {
991               msg (SE, _("FIRST may appear at most once."));
992               goto error;
993             }
994               
995           lex_match ('=');
996           if (!lex_force_id ())
997             goto error;
998           strcpy (mtf.first, tokid);
999           lex_get ();
1000         }
1001       else if (lex_match_id ("LAST")) 
1002         {
1003           if (mtf.last[0] != '\0')
1004             {
1005               msg (SE, _("LAST may appear at most once."));
1006               goto error;
1007             }
1008               
1009           lex_match ('=');
1010           if (!lex_force_id ())
1011             goto error;
1012           strcpy (mtf.last, tokid);
1013           lex_get ();
1014         }
1015       else if (lex_match_id ("MAP"))
1016         {
1017           /* FIXME. */
1018         }
1019       else if (lex_match_id ("DROP")) 
1020         {
1021           if (!drop_variables (mtf.dict))
1022             goto error;
1023         }
1024       else if (lex_match_id ("KEEP")) 
1025         {
1026           if (!keep_variables (mtf.dict))
1027             goto error;
1028         }
1029       else
1030         {
1031           lex_error (NULL);
1032           goto error;
1033         }
1034
1035       if (!lex_match ('/') && token != '.') 
1036         {
1037           lex_end_of_command ();
1038           goto error;
1039         }
1040     }
1041
1042   if (mtf.by_cnt == 0)
1043     {
1044       if (saw_table)
1045         {
1046           msg (SE, _("BY is required when TABLE is specified."));
1047           goto error;
1048         }
1049       if (saw_in)
1050         {
1051           msg (SE, _("BY is required when IN is specified."));
1052           goto error;
1053         }
1054     }
1055
1056   /* Set up mapping from each file's variables to master
1057      variables. */
1058   for (iter = mtf.head; iter != NULL; iter = iter->next)
1059     {
1060       struct dictionary *d = iter->dict;
1061       int i;
1062
1063       for (i = 0; i < dict_get_var_cnt (d); i++)
1064         {
1065           struct variable *v = dict_get_var (d, i);
1066           struct variable *mv = dict_lookup_var (mtf.dict, v->name);
1067           if (mv != NULL)
1068             set_master (v, mv);
1069         }
1070     }
1071
1072   /* Add IN variables to master dictionary. */
1073   for (iter = mtf.head; iter != NULL; iter = iter->next) 
1074     if (iter->in_name != NULL)
1075       {
1076         iter->in_var = dict_create_var (mtf.dict, iter->in_name, 0);
1077         if (iter->in_var == NULL)
1078           {
1079             msg (SE, _("IN variable name %s duplicates an "
1080                        "existing variable name."),
1081                  iter->in_var->name);
1082             goto error;
1083           }
1084         iter->in_var->print = iter->in_var->write
1085           = make_output_format (FMT_F, 1, 0);
1086       }
1087     
1088   /* MATCH FILES performs an n-way merge on all its input files.
1089      Abstract algorithm:
1090
1091      1. Read one input record from every input FILE.
1092
1093      2. If no FILEs are left, stop.  Otherwise, proceed to step 3.
1094
1095      3. Find the FILE input record(s) that have minimum BY
1096      values.  Store all the values from these input records into
1097      the output record.
1098
1099      4. For every TABLE, read another record as long as the BY values
1100      on the TABLE's input record are less than the FILEs' BY values.
1101      If an exact match is found, store all the values from the TABLE
1102      input record into the output record.
1103
1104      5. Write the output record.
1105
1106      6. Read another record from each input file FILE and TABLE that
1107      we stored values from above.  If we come to the end of one of the
1108      input files, remove it from the list of input files.
1109
1110      7. Repeat from step 2.
1111
1112      Unfortunately, this algorithm can't be implemented in a
1113      straightforward way because there's no function to read a
1114      record from the active file.  Instead, it has to be written
1115      as a state machine.
1116
1117      FIXME: For merging large numbers of files (more than 10?) a
1118      better algorithm would use a heap for finding minimum
1119      values. */
1120
1121   if (!used_active_file)
1122     discard_variables ();
1123
1124   dict_compact_values (mtf.dict);
1125   mtf.output = casefile_create (dict_get_next_value_idx (mtf.dict));
1126   mtf.seq_nums = xcalloc (dict_get_var_cnt (mtf.dict), sizeof *mtf.seq_nums);
1127   case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict));
1128
1129   if (!mtf_read_nonactive_records (&mtf))
1130     goto error;
1131
1132   if (used_active_file) 
1133     {
1134       proc_set_sink (create_case_sink (&null_sink_class, default_dict, NULL));
1135       ok = procedure (mtf_processing, &mtf) && mtf_processing_finish (&mtf); 
1136     }
1137   else
1138     ok = mtf_processing_finish (&mtf);
1139
1140   discard_variables ();
1141
1142   default_dict = mtf.dict;
1143   mtf.dict = NULL;
1144   proc_set_source (storage_source_create (mtf.output));
1145   mtf.output = NULL;
1146   
1147   if (!mtf_free (&mtf))
1148     ok = false;
1149   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
1150   
1151  error:
1152   mtf_free (&mtf);
1153   return CMD_CASCADING_FAILURE;
1154 }
1155
1156 /* Repeats 2...7 an arbitrary number of times. */
1157 static bool
1158 mtf_processing_finish (void *mtf_)
1159 {
1160   struct mtf_proc *mtf = mtf_;
1161   struct mtf_file *iter;
1162
1163   /* Find the active file and delete it. */
1164   for (iter = mtf->head; iter; iter = iter->next)
1165     if (iter->handle == NULL)
1166       {
1167         if (!mtf_delete_file_in_place (mtf, &iter))
1168           abort ();
1169         break;
1170       }
1171   
1172   while (mtf->head && mtf->head->type == MTF_FILE)
1173     if (!mtf_processing (NULL, mtf))
1174       return false;
1175
1176   return true;
1177 }
1178
1179 /* Return a string in a static buffer describing V's variable type and
1180    width. */
1181 static char *
1182 var_type_description (struct variable *v)
1183 {
1184   static char buf[2][32];
1185   static int x = 0;
1186   char *s;
1187
1188   x ^= 1;
1189   s = buf[x];
1190
1191   if (v->type == NUMERIC)
1192     strcpy (s, "numeric");
1193   else
1194     {
1195       assert (v->type == ALPHA);
1196       sprintf (s, "string with width %d", v->width);
1197     }
1198   return s;
1199 }
1200
1201 /* Closes FILE and frees its associated data.
1202    Returns true if successful, false if an I/O error
1203    occurred on FILE. */
1204 static bool
1205 mtf_close_file (struct mtf_file *file)
1206 {
1207   bool ok = file->reader == NULL || !any_reader_error (file->reader);
1208   free (file->by);
1209   any_reader_close (file->reader);
1210   if (file->handle != NULL)
1211     dict_destroy (file->dict);
1212   case_destroy (&file->input);
1213   free (file->in_name);
1214   free (file);
1215   return ok;
1216 }
1217
1218 /* Free all the data for the MATCH FILES procedure.
1219    Returns true if successful, false if an I/O error
1220    occurred. */
1221 static bool
1222 mtf_free (struct mtf_proc *mtf)
1223 {
1224   struct mtf_file *iter, *next;
1225   bool ok = true;
1226
1227   for (iter = mtf->head; iter; iter = next)
1228     {
1229       next = iter->next;
1230       assert (iter->dict != mtf->dict);
1231       if (!mtf_close_file (iter))
1232         ok = false;
1233     }
1234   
1235   if (mtf->dict)
1236     dict_destroy (mtf->dict);
1237   case_destroy (&mtf->mtf_case);
1238   free (mtf->seq_nums);
1239
1240   return ok;
1241 }
1242
1243 /* Remove *FILE from the mtf_file chain.  Make *FILE point to the next
1244    file in the chain, or to NULL if was the last in the chain.
1245    Returns true if successful, false if an I/O error occurred. */
1246 static bool
1247 mtf_delete_file_in_place (struct mtf_proc *mtf, struct mtf_file **file)
1248 {
1249   struct mtf_file *f = *file;
1250   int i;
1251
1252   if (f->prev)
1253     f->prev->next = f->next;
1254   if (f->next)
1255     f->next->prev = f->prev;
1256   if (f == mtf->head)
1257     mtf->head = f->next;
1258   if (f == mtf->tail)
1259     mtf->tail = f->prev;
1260   *file = f->next;
1261
1262   if (f->in_var != NULL)
1263     case_data_rw (&mtf->mtf_case, f->in_var->fv)->f = 0.;
1264   for (i = 0; i < dict_get_var_cnt (f->dict); i++)
1265     {
1266       struct variable *v = dict_get_var (f->dict, i);
1267       struct variable *mv = get_master (v);
1268       if (mv != NULL) 
1269         {
1270           union value *out = case_data_rw (&mtf->mtf_case, mv->fv);
1271           
1272           if (v->type == NUMERIC)
1273             out->f = SYSMIS;
1274           else
1275             memset (out->s, ' ', v->width);
1276         } 
1277     }
1278
1279   return mtf_close_file (f);
1280 }
1281
1282 /* Read a record from every input file except the active file.
1283    Returns true if successful, false if an I/O error occurred. */
1284 static bool
1285 mtf_read_nonactive_records (void *mtf_)
1286 {
1287   struct mtf_proc *mtf = mtf_;
1288   struct mtf_file *iter, *next;
1289   bool ok = true;
1290
1291   for (iter = mtf->head; ok && iter != NULL; iter = next)
1292     {
1293       next = iter->next;
1294       if (iter->handle && !any_reader_read (iter->reader, &iter->input)) 
1295         if (!mtf_delete_file_in_place (mtf, &iter))
1296           ok = false;
1297     }
1298   return ok;
1299 }
1300
1301 /* Compare the BY variables for files A and B; return -1 if A < B, 0
1302    if A == B, 1 if A > B. */
1303 static inline int
1304 mtf_compare_BY_values (struct mtf_proc *mtf,
1305                        struct mtf_file *a, struct mtf_file *b,
1306                        const struct ccase *c)
1307 {
1308   const struct ccase *ca = case_is_null (&a->input) ? c : &a->input;
1309   const struct ccase *cb = case_is_null (&b->input) ? c : &b->input;
1310   assert ((a == NULL) + (b == NULL) + (c == NULL) <= 1);
1311   return case_compare_2dict (ca, cb, a->by, b->by, mtf->by_cnt);
1312 }
1313
1314 /* Perform one iteration of steps 3...7 above.
1315    Returns true if successful, false if an I/O error occurred. */
1316 static bool
1317 mtf_processing (const struct ccase *c, void *mtf_)
1318 {
1319   struct mtf_proc *mtf = mtf_;
1320
1321   /* Do we need another record from the active file? */
1322   bool read_active_file;
1323
1324   assert (mtf->head != NULL);
1325   if (mtf->head->type == MTF_TABLE)
1326     return true;
1327   
1328   do
1329     {
1330       struct mtf_file *min_head, *min_tail; /* Files with minimum BY values. */
1331       struct mtf_file *max_head, *max_tail; /* Files with non-minimum BYs. */
1332       struct mtf_file *iter, *next;
1333
1334       read_active_file = false;
1335       
1336       /* 3. Find the FILE input record(s) that have minimum BY
1337          values.  Store all the values from these input records into
1338          the output record. */
1339       min_head = min_tail = mtf->head;
1340       max_head = max_tail = NULL;
1341       for (iter = mtf->head->next; iter && iter->type == MTF_FILE;
1342            iter = iter->next) 
1343         {
1344           int cmp = mtf_compare_BY_values (mtf, min_head, iter, c);
1345           if (cmp < 0) 
1346             {
1347               if (max_head)
1348                 max_tail = max_tail->next_min = iter;
1349               else
1350                 max_head = max_tail = iter;
1351             }
1352           else if (cmp == 0) 
1353             min_tail = min_tail->next_min = iter;
1354           else /* cmp > 0 */
1355             {
1356               if (max_head)
1357                 {
1358                   max_tail->next_min = min_head;
1359                   max_tail = min_tail;
1360                 }
1361               else
1362                 {
1363                   max_head = min_head;
1364                   max_tail = min_tail;
1365                 }
1366               min_head = min_tail = iter;
1367             }
1368         }
1369       
1370       /* 4. For every TABLE, read another record as long as the BY
1371          values on the TABLE's input record are less than the FILEs'
1372          BY values.  If an exact match is found, store all the values
1373          from the TABLE input record into the output record. */
1374       for (; iter != NULL; iter = next)
1375         {
1376           assert (iter->type == MTF_TABLE);
1377       
1378           next = iter->next;
1379           for (;;) 
1380             {
1381               int cmp = mtf_compare_BY_values (mtf, min_head, iter, c);
1382               if (cmp < 0) 
1383                 {
1384                   if (max_head)
1385                     max_tail = max_tail->next_min = iter;
1386                   else
1387                     max_head = max_tail = iter;
1388                 }
1389               else if (cmp == 0)
1390                 min_tail = min_tail->next_min = iter;
1391               else /* cmp > 0 */
1392                 {
1393                   if (iter->handle == NULL)
1394                     return true;
1395                   if (any_reader_read (iter->reader, &iter->input))
1396                     continue;
1397                   if (!mtf_delete_file_in_place (mtf, &iter))
1398                     return false;
1399                 }
1400               break;
1401             }
1402         }
1403
1404       /* Next sequence number. */
1405       mtf->seq_num++;
1406
1407       /* Store data to all the records we are using. */
1408       if (min_tail)
1409         min_tail->next_min = NULL;
1410       for (iter = min_head; iter; iter = iter->next_min)
1411         {
1412           int i;
1413
1414           for (i = 0; i < dict_get_var_cnt (iter->dict); i++)
1415             {
1416               struct variable *v = dict_get_var (iter->dict, i);
1417               struct variable *mv = get_master (v);
1418           
1419               if (mv != NULL && mtf->seq_nums[mv->index] != mtf->seq_num) 
1420                 {
1421                   const struct ccase *record
1422                     = case_is_null (&iter->input) ? c : &iter->input;
1423                   union value *out = case_data_rw (&mtf->mtf_case, mv->fv);
1424
1425                   mtf->seq_nums[mv->index] = mtf->seq_num;
1426                   if (v->type == NUMERIC)
1427                     out->f = case_num (record, v->fv);
1428                   else
1429                     memcpy (out->s, case_str (record, v->fv), v->width);
1430                 } 
1431             }
1432           if (iter->in_var != NULL)
1433             case_data_rw (&mtf->mtf_case, iter->in_var->fv)->f = 1.;
1434
1435           if (iter->type == MTF_FILE && iter->handle == NULL)
1436             read_active_file = true;
1437         }
1438
1439       /* Store missing values to all the records we're not
1440          using. */
1441       if (max_tail)
1442         max_tail->next_min = NULL;
1443       for (iter = max_head; iter; iter = iter->next_min)
1444         {
1445           int i;
1446
1447           for (i = 0; i < dict_get_var_cnt (iter->dict); i++)
1448             {
1449               struct variable *v = dict_get_var (iter->dict, i);
1450               struct variable *mv = get_master (v);
1451
1452               if (mv != NULL && mtf->seq_nums[mv->index] != mtf->seq_num) 
1453                 {
1454                   union value *out = case_data_rw (&mtf->mtf_case, mv->fv);
1455                   mtf->seq_nums[mv->index] = mtf->seq_num;
1456
1457                   if (v->type == NUMERIC)
1458                     out->f = SYSMIS;
1459                   else
1460                     memset (out->s, ' ', v->width);
1461                 }
1462             }
1463           if (iter->in_var != NULL)
1464             case_data_rw (&mtf->mtf_case, iter->in_var->fv)->f = 0.;
1465         }
1466
1467       /* 5. Write the output record. */
1468       casefile_append (mtf->output, &mtf->mtf_case);
1469
1470       /* 6. Read another record from each input file FILE and TABLE
1471          that we stored values from above.  If we come to the end of
1472          one of the input files, remove it from the list of input
1473          files. */
1474       for (iter = min_head; iter && iter->type == MTF_FILE; iter = next)
1475         {
1476           next = iter->next_min;
1477           if (iter->reader != NULL
1478               && !any_reader_read (iter->reader, &iter->input))
1479             if (!mtf_delete_file_in_place (mtf, &iter))
1480               return false;
1481         }
1482     }
1483   while (!read_active_file
1484          && mtf->head != NULL && mtf->head->type == MTF_FILE);
1485
1486   return true;
1487 }
1488
1489 /* Merge the dictionary for file F into master dictionary M. */
1490 static int
1491 mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
1492 {
1493   struct dictionary *d = f->dict;
1494   const char *d_docs, *m_docs;
1495   int i;
1496
1497   if (dict_get_label (m) == NULL)
1498     dict_set_label (m, dict_get_label (d));
1499
1500   d_docs = dict_get_documents (d);
1501   m_docs = dict_get_documents (m);
1502   if (d_docs != NULL) 
1503     {
1504       if (m_docs == NULL)
1505         dict_set_documents (m, d_docs);
1506       else
1507         {
1508           char *new_docs;
1509           size_t new_len;
1510
1511           new_len = strlen (m_docs) + strlen (d_docs);
1512           new_docs = xmalloc (new_len + 1);
1513           strcpy (new_docs, m_docs);
1514           strcat (new_docs, d_docs);
1515           dict_set_documents (m, new_docs);
1516           free (new_docs);
1517         }
1518     }
1519   
1520   for (i = 0; i < dict_get_var_cnt (d); i++)
1521     {
1522       struct variable *dv = dict_get_var (d, i);
1523       struct variable *mv = dict_lookup_var (m, dv->name);
1524
1525       if (dict_class_from_id (dv->name) == DC_SCRATCH)
1526         continue;
1527
1528       if (mv != NULL)
1529         {
1530           if (mv->width != dv->width) 
1531             {
1532               msg (SE, _("Variable %s in file %s (%s) has different "
1533                          "type or width from the same variable in "
1534                          "earlier file (%s)."),
1535                    dv->name, fh_get_name (f->handle),
1536                    var_type_description (dv), var_type_description (mv));
1537               return 0;
1538             }
1539         
1540           if (dv->width == mv->width)
1541             {
1542               if (val_labs_count (dv->val_labs)
1543                   && !val_labs_count (mv->val_labs)) 
1544                 {
1545                   val_labs_destroy (mv->val_labs);
1546                   mv->val_labs = val_labs_copy (dv->val_labs); 
1547                 }
1548               if (!mv_is_empty (&dv->miss) && mv_is_empty (&mv->miss))
1549                 mv_copy (&mv->miss, &dv->miss);
1550             }
1551
1552           if (dv->label && !mv->label)
1553             mv->label = xstrdup (dv->label);
1554         }
1555       else
1556         mv = dict_clone_var_assert (m, dv, dv->name);
1557     }
1558
1559   return 1;
1560 }
1561
1562 /* Marks V's master variable as MASTER. */
1563 static void
1564 set_master (struct variable *v, struct variable *master) 
1565 {
1566   var_attach_aux (v, master, NULL);
1567 }
1568
1569 /* Returns the master variable corresponding to V,
1570    as set with set_master(). */
1571 static struct variable *
1572 get_master (struct variable *v) 
1573 {
1574   return v->aux;
1575 }
1576 \f
1577
1578 \f
1579 /* Case map.
1580
1581    A case map copies data from a case that corresponds for one
1582    dictionary to a case that corresponds to a second dictionary
1583    derived from the first by, optionally, deleting, reordering,
1584    or renaming variables.  (No new variables may be created.)
1585    */
1586
1587 /* A case map. */
1588 struct case_map
1589   {
1590     size_t value_cnt;   /* Number of values in map. */
1591     int *map;           /* For each destination index, the
1592                            corresponding source index. */
1593   };
1594
1595 /* Prepares dictionary D for producing a case map.  Afterward,
1596    the caller may delete, reorder, or rename variables within D
1597    at will before using finish_case_map() to produce the case
1598    map.
1599
1600    Uses D's aux members, which must otherwise not be in use. */
1601 static void
1602 start_case_map (struct dictionary *d) 
1603 {
1604   size_t var_cnt = dict_get_var_cnt (d);
1605   size_t i;
1606   
1607   for (i = 0; i < var_cnt; i++)
1608     {
1609       struct variable *v = dict_get_var (d, i);
1610       int *src_fv = xmalloc (sizeof *src_fv);
1611       *src_fv = v->fv;
1612       var_attach_aux (v, src_fv, var_dtor_free);
1613     }
1614 }
1615
1616 /* Produces a case map from dictionary D, which must have been
1617    previously prepared with start_case_map().
1618
1619    Does not retain any reference to D, and clears the aux members
1620    set up by start_case_map().
1621
1622    Returns the new case map, or a null pointer if no mapping is
1623    required (that is, no data has changed position). */
1624 static struct case_map *
1625 finish_case_map (struct dictionary *d) 
1626 {
1627   struct case_map *map;
1628   size_t var_cnt = dict_get_var_cnt (d);
1629   size_t i;
1630   int identity_map;
1631
1632   map = xmalloc (sizeof *map);
1633   map->value_cnt = dict_get_next_value_idx (d);
1634   map->map = xnmalloc (map->value_cnt, sizeof *map->map);
1635   for (i = 0; i < map->value_cnt; i++)
1636     map->map[i] = -1;
1637
1638   identity_map = 1;
1639   for (i = 0; i < var_cnt; i++) 
1640     {
1641       struct variable *v = dict_get_var (d, i);
1642       int *src_fv = (int *) var_detach_aux (v);
1643       size_t idx;
1644
1645       if (v->fv != *src_fv)
1646         identity_map = 0;
1647       
1648       for (idx = 0; idx < v->nv; idx++)
1649         {
1650           int src_idx = *src_fv + idx;
1651           int dst_idx = v->fv + idx;
1652           
1653           assert (map->map[dst_idx] == -1);
1654           map->map[dst_idx] = src_idx;
1655         }
1656       free (src_fv);
1657     }
1658
1659   if (identity_map) 
1660     {
1661       destroy_case_map (map);
1662       return NULL;
1663     }
1664
1665   while (map->value_cnt > 0 && map->map[map->value_cnt - 1] == -1)
1666     map->value_cnt--;
1667
1668   return map;
1669 }
1670
1671 /* Maps from SRC to DST, applying case map MAP. */
1672 static void
1673 map_case (const struct case_map *map,
1674           const struct ccase *src, struct ccase *dst) 
1675 {
1676   size_t dst_idx;
1677
1678   assert (map != NULL);
1679   assert (src != NULL);
1680   assert (dst != NULL);
1681   assert (src != dst);
1682
1683   for (dst_idx = 0; dst_idx < map->value_cnt; dst_idx++)
1684     {
1685       int src_idx = map->map[dst_idx];
1686       if (src_idx != -1)
1687         *case_data_rw (dst, dst_idx) = *case_data (src, src_idx);
1688     }
1689 }
1690
1691 /* Destroys case map MAP. */
1692 static void
1693 destroy_case_map (struct case_map *map) 
1694 {
1695   if (map != NULL) 
1696     {
1697       free (map->map);
1698       free (map);
1699     }
1700 }