work on SAVE DATA COLLECTION
[pspp] / src / data / mdd-writer.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2018 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 "data/mdd-writer.h"
20
21 #include <errno.h>
22 #include <libxml/xmlwriter.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <sys/stat.h>
26
27 #include "data/dictionary.h"
28 #include "data/file-handle-def.h"
29 #include "data/make-file.h"
30 #include "data/short-names.h"
31 #include "data/value-labels.h"
32 #include "data/variable.h"
33 #include "libpspp/message.h"
34 #include "libpspp/misc.h"
35
36 #include "gl/ftoastr.h"
37 #include "gl/xalloc.h"
38 #include "gl/xmemdup0.h"
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42 #define N_(msgid) (msgid)
43
44 #define _xml(X) (CHAR_CAST (const xmlChar *, (X)))
45
46 /* Metadata file writer. */
47 struct mdd_writer
48   {
49     struct file_handle *fh;     /* File handle. */
50     struct fh_lock *lock;       /* Mutual exclusion for file. */
51     FILE *file;                 /* File stream. */
52     struct replace_file *rf;    /* Ticket for replacing output file. */
53
54     xmlTextWriter *writer;
55   };
56
57 /* Returns true if an I/O error has occurred on WRITER, false otherwise. */
58 static bool
59 mdd_write_error (const struct mdd_writer *writer)
60 {
61   return ferror (writer->file);
62 }
63
64 static bool
65 mdd_close (struct mdd_writer *w)
66 {
67   if (!w)
68     return true;
69
70   if (w->writer)
71     xmlFreeTextWriter (w->writer);
72
73   bool ok = true;
74   if (w->file)
75     {
76       fflush (w->file);
77
78       ok = !mdd_write_error (w);
79       if (fclose (w->file) == EOF)
80         ok = false;
81
82       if (!ok)
83         msg (ME, _("An I/O error occurred writing metadata file `%s'."),
84              fh_get_file_name (w->fh));
85
86       if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
87         ok = false;
88     }
89
90   fh_unlock (w->lock);
91   fh_unref (w->fh);
92
93   free (w);
94
95   return ok;
96 }
97
98 bool
99 mdd_write (struct file_handle *fh, struct dictionary *dict,
100            const char *sav_name)
101 {
102   struct mdd_writer *w = xzalloc (sizeof *w);
103
104   /* Open file handle as an exclusive writer. */
105   /* TRANSLATORS: this fragment will be interpolated into
106      messages in fh_lock() that identify types of files. */
107   w->lock = fh_lock (fh, FH_REF_FILE, N_("metadata file"), FH_ACC_WRITE, true);
108   if (w->lock == NULL)
109     goto error;
110
111   /* Create the file on disk. */
112   w->rf = replace_file_start (fh, "wb", 0444, &w->file);
113   if (w->rf == NULL)
114     {
115       msg (ME, _("Error opening `%s' for writing as a metadata file: %s."),
116            fh_get_file_name (fh), strerror (errno));
117       goto error;
118     }
119
120   w->writer = xmlNewTextWriter (xmlOutputBufferCreateFile (w->file, NULL));
121   if (!w->writer)
122     {
123       msg (ME, _("Internal error creating xmlTextWriter."));
124       goto error;
125     }
126
127   xmlTextWriterStartDocument (w->writer, NULL, "UTF-8", NULL);
128
129   /* <?xml-stylesheet type="text/xsl" href="mdd.xslt"?> */
130   xmlTextWriterStartPI (w->writer, _xml ("xml-stylesheet"));
131   xmlTextWriterWriteString (w->writer,
132                             _xml ("type=\"text/xsl\" href=\"mdd.xslt\""));
133   xmlTextWriterEndPI (w->writer);
134
135   xmlTextWriterStartElement (w->writer, _xml ("xml"));
136
137   /* <mdm:metadata xmlns:mdm="http://www.spss.com/mr/dm/metadatamodel/Arc
138      3/2000-02-04" mdm_createversion="7.0.0.0.331"
139      mdm_lastversion="7.0.0.0.331" id="c4c181c1-0d7c-42e3-abcd-f08296d1dfdc"
140      data_version="9" data_sub_version="1" systemvariable="0"
141      dbfiltervalidation="-1"> */
142   xmlTextWriterStartElementNS (
143     w->writer, _xml ("mdm"), _xml ("xml"),
144     _xml ("http://www.spss.com/mr/dm/metadatamodel/Arc 3/2000-02-04"));
145   static const struct pair
146     {
147       const char *key, *value;
148     }
149   pairs[] =
150     {
151       { "mdm_createversion", "7.0.0.0.331" },
152       { "mdm_lastversion", "7.0.0.0.331" },
153       { "id", "c4c181c1-0d7c-42e3-abcd-f08296d1dfdc" },
154       { "data_version", "9" },
155       { "data_sub_version", "1" },
156       { "systemvariable", "0" },
157       { "dbfiltervalidation", "-1" },
158     };
159   const int n_pairs = sizeof pairs / sizeof *pairs;
160   for (const struct pair *p = pairs; p < &pairs[n_pairs]; p++)
161     xmlTextWriterWriteAttribute (w->writer, _xml (p->key), _xml (p->value));
162   xmlTextWriterEndElement (w->writer);
163
164   /* <atoms> */
165   xmlTextWriterStartElement (w->writer, _xml ("atoms"));
166   xmlTextWriterEndElement (w->writer);
167
168   /* <datasources> */
169   xmlTextWriterStartElement (w->writer, _xml ("datasources"));
170   xmlTextWriterWriteAttribute (w->writer, _xml ("default"), _xml ("mrSavDsc"));
171
172   /* <connection> */
173   xmlTextWriterStartElement (w->writer, _xml ("connection"));
174   xmlTextWriterWriteAttribute (w->writer, _xml ("name"), _xml ("mrSavDsc"));
175   xmlTextWriterWriteAttribute (w->writer, _xml ("dblocation"),
176                                _xml (sav_name));
177   xmlTextWriterWriteAttribute (w->writer,
178                                _xml ("cdscname"), _xml ("mrSavDsc"));
179   xmlTextWriterWriteAttribute (w->writer, _xml ("project"), _xml ("126"));
180
181   size_t n_vars = dict_get_var_cnt (dict);
182   short_names_assign (dict);
183   for (size_t i = 0; i < n_vars; i++)
184     {
185       const struct variable *var = dict_get_var (dict, i);
186       xmlTextWriterStartElement (w->writer, _xml ("var"));
187
188       /* XXX Should convert short name to all-lowercase below.  */
189       xmlTextWriterWriteAttribute (w->writer, _xml ("fullname"),
190                                    _xml (var_get_short_name (var, 0)));
191       xmlTextWriterWriteAttribute (w->writer, _xml ("aliasname"),
192                                  _xml (var_get_name (var)));
193
194       const struct val_labs *val_labs = var_get_value_labels (var);
195       size_t n_vls = val_labs_count (val_labs);
196       if (n_vls)
197         {
198           const struct val_lab **vls = val_labs_sorted (val_labs);
199
200           xmlTextWriterStartElement (w->writer, _xml ("nativevalues"));
201           int width = var_get_width (var);
202           for (size_t j = 0; j < n_vls; j++)
203             {
204               const struct val_lab *vl = vls[j];
205               xmlTextWriterStartElement (w->writer, _xml ("nativevalue"));
206               /* XXX Should convert to lowercase, change non-id characters to
207                  _, prefix with _ if starts with non-letter */
208               xmlTextWriterWriteAttribute (w->writer, _xml ("fullname"),
209                                            _xml (val_lab_get_label (vl)));
210
211               /* XXX below would better use syntax_gen_value(). */
212               const union value *value = val_lab_get_value (vl);
213               if (width)
214                 {
215                   char *s = xmemdup0 (value_str (value, width), width);
216                   xmlTextWriterWriteAttribute (w->writer, _xml ("value"),
217                                                _xml (s));
218                   free (s);
219                 }
220               else
221                 {
222                   char s[DBL_BUFSIZE_BOUND];
223
224                   c_dtoastr (s, sizeof s, 0, 0, value->f);
225                   xmlTextWriterWriteAttribute (w->writer, _xml ("value"),
226                                                _xml (s));
227                 }
228               xmlTextWriterEndElement (w->writer);
229             }
230           xmlTextWriterEndElement (w->writer);
231
232           free (vls);
233         }
234
235       xmlTextWriterEndElement (w->writer);
236     }
237
238   xmlTextWriterEndElement (w->writer); /* </xml> */
239
240   xmlTextWriterEndDocument (w->writer);
241
242 error:
243   mdd_close (w);
244   return NULL;
245 }