1 /*
2 * Copyright (C) 2007 - 2011 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2010 David King <davidk@openismus.com>
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
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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 02110-1301, USA.
18 */
19 #include <stdio.h>
20 #include <string.h>
21 #include <glib.h>
22 #include <gmodule.h>
23 #include <libgda/libgda.h>
24 #include <libgda/binreloc/gda-binreloc.h>
25 #include <glib/gi18n-lib.h>
26 #include "gda-tree-mgr-xml.h"
27
28 gchar *prov = NULL;
29 gchar *op = NULL;
30 gboolean list_ops = FALSE;
31 gboolean out_tree = FALSE;
32 GdaServerProvider *prov_obj = NULL;
33
34 static GOptionEntry entries[] = {
35 { "provider", 'p', 0, G_OPTION_ARG_STRING, &prov, "Provider name", "provider"},
36 { "op", 'o', 0, G_OPTION_ARG_STRING, &op, "Operation", "operation name"},
37 { "list-ops", 'l', 0, G_OPTION_ARG_NONE, &list_ops, "List existing operations", NULL },
38 { "tree", 't', 0, G_OPTION_ARG_NONE, &out_tree, "Output results as a tree (default is as XML)", NULL },
39 { NULL, 0, 0, 0, NULL, NULL, NULL }
40 };
41
42 static xmlDocPtr merge_specs (const gchar *xml_dir, GdaServerOperationType type, gboolean *op_supported, GError **error);
43
44 gint
main(int argc,char ** argv)45 main (int argc, char **argv) {
46 gchar *xml_dir;
47 GOptionContext *context;
48 GError *error = NULL;
49
50 context = g_option_context_new (_("Gda server operations list"));
51 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
52 if (!g_option_context_parse (context, &argc, &argv, &error)) {
53 g_warning ("Can't parse arguments: %s", error->message);
54 return 1;
55 }
56 g_option_context_free (context);
57
58 gda_init ();
59 xml_dir = gda_gbr_get_file_path (GDA_DATA_DIR, "libgda-5.0", NULL);
60 g_print (_("Using XML descriptions in %s\n"), xml_dir);
61 if (prov)
62 g_print ("For provider %s\n", prov);
63
64 if (prov) {
65 prov_obj = gda_config_get_provider (prov, &error);
66 if (!prov_obj) {
67 g_print (_("Could not create provider object: %s\n"),
68 error && error->message ? error->message : _("No detail"));
69 return 1;
70 }
71 }
72
73 if (list_ops) {
74 GdaServerOperationType type;
75 if (prov)
76 g_print (_("Existing operation types for provider '%s':\n"), prov);
77 else
78 g_print (_("Existing operation types:\n"));
79 for (type = GDA_SERVER_OPERATION_CREATE_DB; type < GDA_SERVER_OPERATION_LAST; type++) {
80 if (! prov_obj ||
81 (prov_obj && gda_server_provider_supports_operation (prov_obj, NULL, type, NULL)))
82 g_print ("%s\n", gda_server_operation_op_type_to_string (type));
83 }
84 return 0;
85 }
86
87 GdaServerOperationType type;
88 for (type = GDA_SERVER_OPERATION_CREATE_DB; type != GDA_SERVER_OPERATION_LAST; type++) {
89 xmlDocPtr doc;
90 GError *error = NULL;
91 gboolean op_supported;
92
93 if (op && strcmp (op, gda_server_operation_op_type_to_string (type)))
94 continue;
95
96 g_print (_("Description for type: %s\n"), gda_server_operation_op_type_to_string (type));
97 doc = merge_specs (xml_dir, type, &op_supported, &error);
98 if (doc) {
99 if (out_tree) {
100 GdaTree *tree;
101 GdaTreeManager *mgr;
102
103 tree = gda_tree_new ();
104 mgr = gda_tree_mgr_xml_new (xmlDocGetRootElement (doc), "prov_name|id|name|gdatype|node_type|descr");
105 gda_tree_add_manager (tree, mgr);
106 gda_tree_manager_add_manager (mgr, mgr);
107 g_object_unref (mgr);
108 gda_tree_update_all (tree, NULL);
109 gda_tree_dump (tree, NULL, NULL);
110 g_object_unref (tree);
111 }
112 else {
113 xmlChar *buf;
114 gint len;
115 xmlKeepBlanksDefault (0);
116 xmlDocDumpFormatMemory (doc, &buf, &len, 1);
117 g_print ("%s\n", buf);
118 xmlFree (buf);
119 }
120 xmlFreeDoc (doc);
121 }
122 else {
123 if (!op_supported)
124 g_print (_("Operation not supported\n"));
125 else
126 g_print (_("Error: %s\n"), error && error->message ? error->message : _("No detail"));
127 if (error)
128 g_error_free (error);
129 }
130 }
131 g_free (xml_dir);
132
133 return 0;
134 }
135
136 static void
remove_all_extra(xmlNodePtr node)137 remove_all_extra (xmlNodePtr node)
138 {
139 xmlNodePtr child;
140 for (child = node->last; child; ) {
141 xmlChar *lang = xmlGetProp (child, (xmlChar*)"lang");
142 if (lang || !strcmp ((gchar *) child->name, "sources")) {
143 /* remove this node */
144 xmlNodePtr prev = child->prev;
145 if (lang)
146 xmlFree (lang);
147
148 xmlUnlinkNode (child);
149 xmlFreeNode (child);
150 child = prev;
151 }
152 else {
153 remove_all_extra (child);
154 child = child->prev;
155 }
156 }
157 }
158
159 typedef struct {
160 GdaServerOperationNodeType node_type;
161 gchar *path;
162 gchar *name;
163 gchar *type;
164 gchar *descr;
165 } Path;
166 static void
path_free(Path * path)167 path_free (Path *path)
168 {
169 g_free (path->path);
170 g_free (path->name);
171 g_free (path->type);
172 g_free (path->descr);
173 g_free (path);
174 }
175
176 /* create a list of Path structures */
177 static GSList *
make_paths(xmlNodePtr node,const gchar * parent_path,GSList * exist_list)178 make_paths (xmlNodePtr node, const gchar *parent_path, GSList *exist_list)
179 {
180 xmlChar *id;
181 GSList *retlist = exist_list;
182 id = xmlGetProp (node, BAD_CAST "id");
183 if (id) {
184 Path *path;
185 gchar *pstr = g_strdup_printf ("%s/%s", parent_path, id);
186
187 path = g_new0 (Path, 1);
188 path->path = pstr;
189 retlist = g_slist_append (retlist, path);
190
191 if (!strcmp ((gchar*) node->name, "parameters"))
192 path->node_type = GDA_SERVER_OPERATION_NODE_PARAMLIST;
193 else if (!strcmp ((gchar*) node->name, "parameter"))
194 path->node_type = GDA_SERVER_OPERATION_NODE_PARAM;
195 else if (!strcmp ((gchar*) node->name, "sequence"))
196 path->node_type = GDA_SERVER_OPERATION_NODE_SEQUENCE;
197 else if (!strcmp ((gchar*) node->name, "gda_array"))
198 path->node_type = GDA_SERVER_OPERATION_NODE_DATA_MODEL;
199 else if (!strcmp ((gchar*) node->name, "gda_array_field")) {
200 path->node_type = GDA_SERVER_OPERATION_NODE_DATA_MODEL_COLUMN;
201 g_free (path->path);
202 pstr = g_strdup_printf ("%s/@%s", parent_path, id);
203 path->path = pstr;
204 }
205 else
206 path->node_type = GDA_SERVER_OPERATION_NODE_UNKNOWN;
207
208 xmlChar *prop;
209 prop = xmlGetProp (node, BAD_CAST "name");
210 if (prop) {
211 path->name = g_strdup ((gchar*) prop);
212 xmlFree (prop);
213 }
214 prop = xmlGetProp (node, BAD_CAST "descr");
215 if (prop) {
216 path->descr = g_strdup ((gchar*) prop);
217 xmlFree (prop);
218 }
219 prop = xmlGetProp (node, BAD_CAST "gdatype");
220 if (prop) {
221 path->type = g_strdup ((gchar*) prop);
222 xmlFree (prop);
223 }
224
225 /* children */
226 xmlNodePtr child;
227 for (child = node->children; child; child = child->next)
228 retlist = make_paths (child, pstr, retlist);
229
230 xmlFree (id);
231 }
232 else {
233 /* children */
234 xmlNodePtr child;
235 for (child = node->children; child; child = child->next)
236 retlist = make_paths (child, parent_path, retlist);
237 }
238
239 return retlist;
240 }
241
242 const gchar *
node_type_to_string(GdaServerOperationType node_type)243 node_type_to_string (GdaServerOperationType node_type)
244 {
245 switch (node_type) {
246 case GDA_SERVER_OPERATION_NODE_PARAMLIST:
247 return "PARAMLIST";
248 case GDA_SERVER_OPERATION_NODE_DATA_MODEL:
249 return "DATA_MODEL";
250 case GDA_SERVER_OPERATION_NODE_PARAM:
251 return "PARAMETER";
252 case GDA_SERVER_OPERATION_NODE_SEQUENCE:
253 return "SEQUENCE";
254 case GDA_SERVER_OPERATION_NODE_SEQUENCE_ITEM:
255 return "SEQUENCE_ITEM";
256 default:
257 break;
258 }
259 return "Unknown";
260 }
261
262 static void
merge_spec_from_root(xmlNodePtr root,const gchar * provider,xmlDocPtr pdoc)263 merge_spec_from_root (xmlNodePtr root, const gchar *provider, xmlDocPtr pdoc)
264 {
265 /* make list of paths */
266 GSList *paths, *list;
267
268 paths = make_paths (xmlDocGetRootElement (pdoc), "", NULL);
269 for (list = paths; list; list = list->next) {
270 Path *path = (Path *) list->data;
271
272 /* find node for this path ID, and create it if not yet present */
273 xmlNodePtr node = NULL;
274 for (node = root->children; node; node = node->next) {
275 if (!strcmp ((gchar*) node->name, "path")) {
276 xmlChar *pid;
277 pid = xmlGetProp (node, BAD_CAST "id");
278 if (pid) {
279 if (!strcmp ((gchar*) pid, path->path)) {
280 xmlFree (pid);
281 break;
282 }
283 xmlFree (pid);
284 }
285 }
286
287 }
288 if (!node) {
289 node = xmlNewChild (root, NULL, BAD_CAST "path", NULL);
290 xmlSetProp (node, BAD_CAST "id", BAD_CAST path->path);
291 xmlSetProp (node, BAD_CAST "node_type", BAD_CAST node_type_to_string (path->node_type));
292 if (path->type && *path->type)
293 xmlSetProp (node, BAD_CAST "gdatype", BAD_CAST path->type);
294 }
295
296 /* add this provider's specific information */
297 xmlNodePtr pnode;
298 if (!prov) {
299 pnode = xmlNewChild (node, NULL, BAD_CAST "prov", NULL);
300 xmlSetProp (pnode, BAD_CAST "prov_name", BAD_CAST provider);
301 }
302 else
303 pnode = node;
304 xmlSetProp (pnode, BAD_CAST "name", BAD_CAST path->name);
305 if (path->descr && *path->descr)
306 xmlSetProp (pnode, BAD_CAST "descr", BAD_CAST path->descr);
307
308 if (!prov) {
309 /* check node type is similar to "node->node_type" */
310 xmlChar *node_type;
311 node_type = xmlGetProp (node, BAD_CAST "node_type");
312 g_assert (node_type);
313 if (strcmp ((gchar*) node_type,
314 node_type_to_string (path->node_type)))
315 xmlSetProp (pnode, BAD_CAST "node_type",
316 BAD_CAST node_type_to_string (path->node_type));
317 xmlFree (node_type);
318
319 /* check gdatype is similar to "node->gdatype" */
320 node_type = xmlGetProp (node, BAD_CAST "gdatype");
321 if ((node_type && path->type &&
322 strcmp ((gchar*) node_type, path->type)) ||
323 (!node_type && path->type) ||
324 (node_type && *node_type && !path->type))
325 xmlSetProp (pnode, BAD_CAST "gdatype", BAD_CAST path->type);
326 if (node_type)
327 xmlFree (node_type);
328 }
329 }
330
331 /* mem free */
332 g_slist_foreach (paths, (GFunc) path_free, NULL);
333 g_slist_free (paths);
334 }
335
336 static xmlDocPtr
merge_specs(const gchar * xml_dir,GdaServerOperationType type,gboolean * op_supported,GError ** error)337 merge_specs (const gchar *xml_dir, GdaServerOperationType type, gboolean *op_supported, GError **error)
338 {
339 xmlDocPtr doc = NULL;
340 xmlNodePtr root = NULL;
341 GDir *dir;
342 const gchar *file;
343 gchar *lcprov = NULL;
344 dir = g_dir_open (xml_dir, 0, error);
345 if (!dir)
346 return NULL;
347
348 if (prov)
349 lcprov = g_ascii_strdown (prov, -1);
350 *op_supported = FALSE;
351
352 for (file = g_dir_read_name (dir); file; file = g_dir_read_name (dir)) {
353 gchar *suffix, *lc;
354
355 lc = g_ascii_strdown (gda_server_operation_op_type_to_string (type), -1);
356 suffix = g_strdup_printf ("_specs_%s.xml", lc);
357 g_free (lc);
358 if (g_str_has_suffix (file, suffix)) {
359 gchar *provider_end, *provider;
360
361 xmlDocPtr pdoc;
362 gchar *cfile = g_build_filename (xml_dir, file, NULL);
363
364 provider = g_strdup (file);
365 provider_end = g_strrstr (provider, suffix);
366 *provider_end = 0;
367
368 if (lcprov && strcmp (lcprov, provider)) {
369 /* handle the name mismatch between the provider named "PostgreSQL" and
370 * the file names which start with "postgres" */
371 if (strcmp (provider, "postgres") || strcmp (lcprov, "postgresql"))
372 continue;
373 }
374
375 *op_supported = TRUE;
376 pdoc = xmlParseFile (cfile);
377 g_free (cfile);
378 if (!pdoc) {
379 g_warning (_("Can't parse file '%s', ignoring it"), file);
380 continue;
381 }
382
383 remove_all_extra (xmlDocGetRootElement (pdoc));
384
385 if (!doc) {
386 doc = xmlNewDoc (BAD_CAST "1.0");
387 root = xmlNewNode (NULL, BAD_CAST "server_op");
388 xmlDocSetRootElement (doc, root);
389 }
390 merge_spec_from_root (root, provider, pdoc);
391
392 xmlFreeDoc (pdoc);
393 g_free (provider);
394 }
395 g_free (suffix);
396 }
397 g_dir_close (dir);
398 g_free (lcprov);
399 return doc;
400 }
401