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