first version upgrade
[devspec.git] / devspec.en_US / project / recutils / utils / mdb2rec.c
blob7e221d7afb9a2b12c8d1dee7fbdd141ec0ed6f25
1 /* -*- mode: C -*-
3 * File: mdb2rec.c
4 * Date: Fri Aug 20 22:46:31 2010
6 * GNU recutils - mdb to rec converter.
8 */
10 /* Copyright (C) 2010-2019 Jose E. Marchesi */
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <getopt.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <xalloc.h>
33 #include <gettext.h>
34 #define _(str) gettext (str)
36 #include <glib.h>
37 #include <mdbtools.h>
39 #include <rec.h>
40 #include <recutl.h>
42 /* Forward declarations. */
43 static void parse_args (int argc, char **argv);
44 static rec_db_t process_mdb (void);
45 static rec_rset_t process_table (MdbCatalogEntry *entry);
46 static char *get_field_name (MdbHandle *mdb, const char *table_name, const char *col_name);
47 static void get_relationships (MdbHandle *mdb);
50 * Data types
53 struct relationship_s
55 char *table;
56 char *column;
57 char *referenced_table;
58 char *referenced_column;
62 * Global variables
65 char *mdb2rec_mdb_file = NULL;
66 char *mdb2rec_mdb_table = NULL;
67 bool mdb2rec_include_system = false;
68 bool mdb2rec_keep_empty_fields = false;
69 bool mdb2rec_list_tables = false;
70 struct relationship_s *relationships;
71 size_t num_relationships = 0;
74 * Command line options management
77 enum
79 COMMON_ARGS,
80 SYSTEM_TABLES_ARG,
81 KEEP_EMPTY_FIELDS_ARG,
82 LIST_TABLES_ARG
85 static const struct option GNU_longOptions[] =
87 COMMON_LONG_ARGS,
88 {"system-tables", no_argument, NULL, SYSTEM_TABLES_ARG},
89 {"keep-empty-fields", no_argument, NULL, KEEP_EMPTY_FIELDS_ARG},
90 {"list-tables", no_argument, NULL, LIST_TABLES_ARG},
91 {NULL, 0, NULL, 0}
95 * Functions.
98 void
99 recutl_print_help (void)
101 /* TRANSLATORS: --help output, mdb2rec synopsis.
102 no-wrap */
103 printf (_("\
104 Usage: mdb2rec [OPTIONS]... MDB_FILE [TABLE]\n"));
106 /* TRANSLATORS: --help output, mdb2rec short description.
107 no-wrap */
108 fputs (_("\
109 Convert an mdb file into a rec file.\n"), stdout);
111 puts ("");
112 /* TRANSLATORS: --help output, mdb2rec options.
113 no-wrap */
114 fputs (_("\
115 -s, --system-tables include system tables.\n\
116 -e, --keep-empty-fields don't prune empty fields in the rec\n\
117 output.\n\
118 -l, --list-tables dump a list of the table names contained\n\
119 in the mdb file.\n"),
120 stdout);
122 recutl_print_help_common ();
123 puts ("");
124 recutl_print_help_footer ();
127 static void
128 parse_args (int argc,
129 char **argv)
131 int ret;
132 char c;
134 while ((ret = getopt_long (argc,
135 argv,
136 "sel",
137 GNU_longOptions,
138 NULL)) != -1)
140 c = ret;
141 switch (c)
143 COMMON_ARGS_CASES
144 case SYSTEM_TABLES_ARG:
145 case 's':
147 mdb2rec_include_system = true;
148 break;
150 case KEEP_EMPTY_FIELDS_ARG:
151 case 'e':
153 mdb2rec_keep_empty_fields = true;
154 break;
156 case LIST_TABLES_ARG:
157 case 'l':
159 mdb2rec_list_tables = true;
160 break;
162 default:
164 exit (EXIT_FAILURE);
169 /* Read the name of the mdb file. */
170 if ((argc - optind) > 2)
172 recutl_print_help ();
173 exit (EXIT_FAILURE);
175 else
177 mdb2rec_mdb_file = argv[optind++];
179 if ((argc - optind) > 0)
181 mdb2rec_mdb_table = argv[optind++];
186 static void
187 get_relationships (MdbHandle *mdb)
189 char *bound[4];
190 MdbTableDef *table;
191 size_t i;
193 table = mdb_read_table_by_name (mdb,
194 "MsysRelationships",
195 MDB_TABLE);
196 if ((!table) || (table->num_rows == 0))
198 return;
201 mdb_read_columns (table);
202 for (i = 0; i < 4; i++)
204 bound[i] = xmalloc (MDB_BIND_SIZE);
207 mdb_bind_column_by_name (table, "szColumn", bound[0], NULL);
208 mdb_bind_column_by_name (table, "szObject", bound[1], NULL);
209 mdb_bind_column_by_name (table, "szReferencedColumn", bound[2], NULL);
210 mdb_bind_column_by_name (table, "szReferencedObject", bound[3], NULL);
211 mdb_rewind_table (table);
213 num_relationships = table->num_rows;
214 relationships = xmalloc (sizeof (struct relationship_s) * num_relationships);
216 i = 0;
217 while (mdb_fetch_row (table))
219 relationships[i].column = xstrdup (bound[0]);
220 relationships[i].table = xstrdup (bound[1]);
221 relationships[i].referenced_column = xstrdup (bound[2]);
222 relationships[i].referenced_table = xstrdup (bound[3]);
223 i++;
227 static char *
228 get_field_name (MdbHandle *mdb,
229 const char *table_name,
230 const char *col_name)
232 char *field_name;
233 char *referenced_table;
234 char *referenced_column;
235 size_t i;
237 /* If this field is a relationship to other table, build a compound
238 field name. */
240 referenced_table = NULL;
241 referenced_column = NULL;
242 for (i = 0; i < num_relationships; i++)
244 if ((strcmp (relationships[i].table, table_name) == 0)
245 && (strcmp (relationships[i].column, col_name) == 0))
247 referenced_table =
248 rec_field_name_normalise (relationships[i].referenced_table);
249 if (!referenced_table)
251 recutl_fatal (_("failed to normalise record type name %s\n"),
252 relationships[i].referenced_table);
255 referenced_column =
256 rec_field_name_normalise (relationships[i].referenced_column);
257 if (!referenced_column)
259 recutl_fatal (_("failed to normalise field name %s\n"),
260 relationships[i].referenced_column);
263 break;
267 field_name = rec_field_name_normalise (col_name);
268 if (!field_name)
270 recutl_fatal (_("failed to normalise field name %s\n"),
271 table_name);
274 if (referenced_table && referenced_column)
276 /* TODO: handle foreign keys. */
279 return field_name;
282 static rec_rset_t
283 process_table (MdbCatalogEntry *entry)
285 rec_rset_t rset;
286 MdbTableDef *table;
287 MdbHandle *mdb;
288 size_t i;
289 MdbColumn *col;
290 char *table_name;
291 char *column_name;
292 char *field_name;
293 char *field_value;
294 char **bound_values;
295 char *normalised;
296 int *bound_lens;
297 #define TYPE_VALUE_SIZE 256
298 char type_value[TYPE_VALUE_SIZE];
299 rec_record_t record;
300 rec_field_t field;
302 mdb = entry->mdb;
303 table_name = entry->object_name;
304 table = mdb_read_table (entry);
306 /* Create the record set. */
307 rset = rec_rset_new ();
308 if (!rset)
309 recutl_out_of_memory ();
311 /* Create the record descriptor and add the %rec: entry. */
312 field_name = rec_field_name_normalise (table_name);
313 if (!field_name)
315 recutl_fatal (_("failed to normalise record type name %s\n"),
316 table_name);
319 record = rec_record_new ();
320 field = rec_field_new ("%rec", field_name);
321 rec_mset_append (rec_record_mset (record), MSET_FIELD, (void *) field, MSET_ANY);
322 free (field_name);
324 /* Get the columns of the table. */
325 mdb_read_columns (table);
327 /* Loop on the columns. We will define the key and the types. */
328 for (i = 0; i < table->num_cols; i++)
330 col = g_ptr_array_index (table->columns, i);
331 column_name = col->name;
332 type_value[0] = 0;
333 normalised = rec_field_name_normalise (column_name);
334 if (!normalised)
336 recutl_fatal (_("failed to normalise the field name %s\n"),
337 column_name);
340 /* Emit a field type specification. */
341 switch (col->col_type)
343 case MDB_BOOL:
345 snprintf (type_value, TYPE_VALUE_SIZE,
346 "%s bool", normalised);
347 break;
349 case MDB_BYTE:
351 snprintf (type_value, TYPE_VALUE_SIZE,
352 "%s range 256", normalised);
353 break;
355 case MDB_INT:
356 case MDB_LONGINT:
357 case MDB_NUMERIC:
359 snprintf (type_value, TYPE_VALUE_SIZE,
360 "%s int", normalised);
361 break;
363 case MDB_MONEY:
364 case MDB_FLOAT:
365 case MDB_DOUBLE:
367 snprintf (type_value, TYPE_VALUE_SIZE,
368 "%s real", normalised);
369 break;
371 case MDB_DATETIME:
373 snprintf (type_value, TYPE_VALUE_SIZE,
374 "%s date", normalised);
375 break;
377 case MDB_TEXT:
379 if (col->col_size > 0)
381 snprintf (type_value, TYPE_VALUE_SIZE,
382 "%s size %d", normalised, col->col_size);
384 break;
386 case MDB_REPID:
387 case MDB_MEMO:
388 case MDB_OLE:
389 default:
391 break;
395 if (type_value[0] != 0)
397 /* Insert a type field for this column. */
398 field = rec_field_new ("%type", type_value);
399 rec_mset_append (rec_record_mset (record), MSET_FIELD, (void *) field, MSET_ANY);
403 rec_rset_set_descriptor (rset, record);
405 /* Add the records for this table. */
406 mdb_rewind_table (table);
408 bound_values = xmalloc (table->num_cols * sizeof(char *));
409 bound_lens = xmalloc(table->num_cols * sizeof(int));
410 for (i = 0; i < table->num_cols; i++)
412 bound_values[i] = xmalloc (MDB_BIND_SIZE);
413 mdb_bind_column (table, i+1, bound_values[i], &bound_lens[i]);
416 while (mdb_fetch_row (table))
418 record = rec_record_new ();
419 if (!record)
420 recutl_out_of_memory ();
422 for (i = 0; i < table->num_cols; i++)
424 col = g_ptr_array_index (table->columns, i);
426 if (col->col_type == MDB_OLE)
428 continue;
431 /* Compute the value of the field. */
432 field_value = xmalloc (bound_lens[i] + 1);
433 memcpy (field_value, bound_values[i], bound_lens[i]);
434 field_value[bound_lens[i]] = '\0';
436 if (mdb2rec_keep_empty_fields || (strlen (field_value) > 0))
438 /* Create the field and append it into the record. */
439 field = rec_field_new (get_field_name (mdb, table_name, column_name),
440 field_value);
441 if (!field)
443 recutl_fatal (_("invalid field name %s.\n"), column_name);
446 rec_mset_append (rec_record_mset (record), MSET_FIELD, (void *) field, MSET_ANY);
449 free (field_value);
452 rec_record_set_container (record, rset);
453 rec_mset_append (rec_rset_mset (rset), MSET_RECORD, (void *) record, MSET_ANY);
456 mdb_free_tabledef (table);
458 return rset;
461 static rec_db_t
462 process_mdb (void)
464 rec_db_t db;
465 MdbHandle *mdb;
466 MdbCatalogEntry *entry;
467 int i;
468 char *table_name;
470 /* Create the rec database. */
471 db = rec_db_new ();
472 if (!db)
473 recutl_out_of_memory ();
475 /* Initialize libmdb and open the input file. */
476 mdb_init();
477 mdb_set_date_fmt ("%Y-%m-%dT%H:%M:%S%z"); /* ISO 8601 */
479 mdb = mdb_open (mdb2rec_mdb_file, MDB_NOFLAGS);
480 if (!mdb)
482 recutl_fatal (_("could not open file %s\n"),
483 mdb2rec_mdb_file);
486 /* Read the catalog. */
487 if (!mdb_read_catalog (mdb, MDB_TABLE))
489 recutl_fatal (_("file does not appear to be an Access database\n"));
492 /* Read relationships from the database. Relationships in mdb files
493 are stored in the MSysRelationships table. */
494 get_relationships (mdb);
496 /* Iterate on the catalogs. */
497 for (i = 0; i < mdb->num_catalog; i++)
499 entry = g_ptr_array_index (mdb->catalog, i);
501 table_name = rec_field_name_normalise (entry->object_name);
503 if ((entry->object_type == MDB_TABLE)
504 && (mdb_is_user_table (entry) || mdb2rec_include_system)
505 && (!mdb2rec_mdb_table || (strcmp (mdb2rec_mdb_table, table_name) == 0)))
507 if (mdb2rec_list_tables)
509 fprintf (stdout, "%s\n", table_name);
511 else
513 rec_db_insert_rset (db,
514 process_table (entry),
515 rec_db_size (db));
520 return db;
524 main (int argc, char *argv[])
526 int ret;
527 rec_db_t db;
528 rec_writer_t writer;
530 recutl_init ("mdb2rec");
532 parse_args (argc, argv);
533 db = process_mdb ();
535 if (db)
537 writer = rec_writer_new (stdout);
538 rec_write_db (writer, db);
540 rec_writer_destroy (writer);
541 rec_db_destroy (db);
543 ret = EXIT_SUCCESS;
545 else
547 ret = EXIT_FAILURE;
550 return ret;
553 /* End of mdb2rec.c */