1 /*
2   Copyright 2007-2019 David Robillard <http://drobilla.net>
3 
4   Permission to use, copy, modify, and/or distribute this software for any
5   purpose with or without fee is hereby granted, provided that the above
6   copyright notice and this permission notice appear in all copies.
7 
8   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 
17 #include "lilv_config.h"
18 
19 #include "lilv/lilv.h"
20 #include "lv2/core/lv2.h"
21 #include "lv2/event/event.h"
22 #include "lv2/port-groups/port-groups.h"
23 #include "lv2/presets/presets.h"
24 
25 #include <math.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 LilvNode* applies_to_pred     = NULL;
33 LilvNode* control_class       = NULL;
34 LilvNode* event_class         = NULL;
35 LilvNode* group_pred          = NULL;
36 LilvNode* label_pred          = NULL;
37 LilvNode* preset_class        = NULL;
38 LilvNode* designation_pred    = NULL;
39 LilvNode* supports_event_pred = NULL;
40 
41 static void
print_port(const LilvPlugin * p,uint32_t index,float * mins,float * maxes,float * defaults)42 print_port(const LilvPlugin* p,
43            uint32_t          index,
44            float*            mins,
45            float*            maxes,
46            float*            defaults)
47 {
48 	const LilvPort* port = lilv_plugin_get_port_by_index(p, index);
49 
50 	printf("\n\tPort %d:\n", index);
51 
52 	if (!port) {
53 		printf("\t\tERROR: Illegal/nonexistent port\n");
54 		return;
55 	}
56 
57 	bool first = true;
58 
59 	const LilvNodes* classes = lilv_port_get_classes(p, port);
60 	printf("\t\tType:        ");
61 	LILV_FOREACH(nodes, i, classes) {
62 		const LilvNode* value = lilv_nodes_get(classes, i);
63 		if (!first) {
64 			printf("\n\t\t             ");
65 		}
66 		printf("%s", lilv_node_as_uri(value));
67 		first = false;
68 	}
69 
70 	if (lilv_port_is_a(p, port, event_class)) {
71 		LilvNodes* supported = lilv_port_get_value(
72 			p, port, supports_event_pred);
73 		if (lilv_nodes_size(supported) > 0) {
74 			printf("\n\t\tSupported events:\n");
75 			LILV_FOREACH(nodes, i, supported) {
76 				const LilvNode* value = lilv_nodes_get(supported, i);
77 				printf("\t\t\t%s\n", lilv_node_as_uri(value));
78 			}
79 		}
80 		lilv_nodes_free(supported);
81 	}
82 
83 	LilvScalePoints* points = lilv_port_get_scale_points(p, port);
84 	if (points) {
85 		printf("\n\t\tScale Points:\n");
86 	}
87 	LILV_FOREACH(scale_points, i, points) {
88 		const LilvScalePoint* point = lilv_scale_points_get(points, i);
89 		printf("\t\t\t%s = \"%s\"\n",
90 				lilv_node_as_string(lilv_scale_point_get_value(point)),
91 				lilv_node_as_string(lilv_scale_point_get_label(point)));
92 	}
93 	lilv_scale_points_free(points);
94 
95 	const LilvNode* sym = lilv_port_get_symbol(p, port);
96 	printf("\n\t\tSymbol:      %s\n", lilv_node_as_string(sym));
97 
98 	LilvNode* name = lilv_port_get_name(p, port);
99 	printf("\t\tName:        %s\n", lilv_node_as_string(name));
100 	lilv_node_free(name);
101 
102 	LilvNodes* groups = lilv_port_get_value(p, port, group_pred);
103 	if (lilv_nodes_size(groups) > 0) {
104 		printf("\t\tGroup:       %s\n",
105 		       lilv_node_as_string(lilv_nodes_get_first(groups)));
106 	}
107 	lilv_nodes_free(groups);
108 
109 	LilvNodes* designations = lilv_port_get_value(p, port, designation_pred);
110 	if (lilv_nodes_size(designations) > 0) {
111 		printf("\t\tDesignation: %s\n",
112 		       lilv_node_as_string(lilv_nodes_get_first(designations)));
113 	}
114 	lilv_nodes_free(designations);
115 
116 	if (lilv_port_is_a(p, port, control_class)) {
117 		if (!isnan(mins[index])) {
118 			printf("\t\tMinimum:     %f\n", mins[index]);
119 		}
120 		if (!isnan(maxes[index])) {
121 			printf("\t\tMaximum:     %f\n", maxes[index]);
122 		}
123 		if (!isnan(defaults[index])) {
124 			printf("\t\tDefault:     %f\n", defaults[index]);
125 		}
126 	}
127 
128 	LilvNodes* properties = lilv_port_get_properties(p, port);
129 	if (lilv_nodes_size(properties) > 0) {
130 		printf("\t\tProperties:  ");
131 	}
132 	first = true;
133 	LILV_FOREACH(nodes, i, properties) {
134 		if (!first) {
135 			printf("\t\t             ");
136 		}
137 		printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i)));
138 		first = false;
139 	}
140 	if (lilv_nodes_size(properties) > 0) {
141 		printf("\n");
142 	}
143 	lilv_nodes_free(properties);
144 }
145 
146 static void
print_plugin(LilvWorld * world,const LilvPlugin * p)147 print_plugin(LilvWorld*        world,
148              const LilvPlugin* p)
149 {
150 	LilvNode* val = NULL;
151 
152 	printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p)));
153 
154 	val = lilv_plugin_get_name(p);
155 	if (val) {
156 		printf("\tName:              %s\n", lilv_node_as_string(val));
157 		lilv_node_free(val);
158 	}
159 
160 	const LilvPluginClass* pclass      = lilv_plugin_get_class(p);
161 	const LilvNode*       class_label = lilv_plugin_class_get_label(pclass);
162 	if (class_label) {
163 		printf("\tClass:             %s\n", lilv_node_as_string(class_label));
164 	}
165 
166 	val = lilv_plugin_get_author_name(p);
167 	if (val) {
168 		printf("\tAuthor:            %s\n", lilv_node_as_string(val));
169 		lilv_node_free(val);
170 	}
171 
172 	val = lilv_plugin_get_author_email(p);
173 	if (val) {
174 		printf("\tAuthor Email:      %s\n", lilv_node_as_uri(val));
175 		lilv_node_free(val);
176 	}
177 
178 	val = lilv_plugin_get_author_homepage(p);
179 	if (val) {
180 		printf("\tAuthor Homepage:   %s\n", lilv_node_as_uri(val));
181 		lilv_node_free(val);
182 	}
183 
184 	if (lilv_plugin_has_latency(p)) {
185 		uint32_t latency_port = lilv_plugin_get_latency_port_index(p);
186 		printf("\tHas latency:       yes, reported by port %d\n", latency_port);
187 	} else {
188 		printf("\tHas latency:       no\n");
189 	}
190 
191 	printf("\tBundle:            %s\n",
192 	       lilv_node_as_uri(lilv_plugin_get_bundle_uri(p)));
193 
194 	const LilvNode* binary_uri = lilv_plugin_get_library_uri(p);
195 	if (binary_uri) {
196 		printf("\tBinary:            %s\n",
197 		       lilv_node_as_uri(lilv_plugin_get_library_uri(p)));
198 	}
199 
200 	LilvUIs* uis = lilv_plugin_get_uis(p);
201 	if (lilv_nodes_size(uis) > 0) {
202 		printf("\tUIs:\n");
203 		LILV_FOREACH(uis, i, uis) {
204 			const LilvUI* ui = lilv_uis_get(uis, i);
205 			printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui)));
206 
207 			const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui));
208 
209 			const LilvNodes* types = lilv_ui_get_classes(ui);
210 			LILV_FOREACH(nodes, t, types) {
211 				printf("\t\t\tClass:  %s\n",
212 				       lilv_node_as_uri(lilv_nodes_get(types, t)));
213 			}
214 
215 			if (binary) {
216 				printf("\t\t\tBinary: %s\n", binary);
217 			}
218 
219 			printf("\t\t\tBundle: %s\n",
220 			       lilv_node_as_uri(lilv_ui_get_bundle_uri(ui)));
221 		}
222 	}
223 	lilv_uis_free(uis);
224 
225 	printf("\tData URIs:         ");
226 	const LilvNodes* data_uris = lilv_plugin_get_data_uris(p);
227 	bool first = true;
228 	LILV_FOREACH(nodes, i, data_uris) {
229 		if (!first) {
230 			printf("\n\t                   ");
231 		}
232 		printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i)));
233 		first = false;
234 	}
235 	printf("\n");
236 
237 	/* Required Features */
238 
239 	LilvNodes* features = lilv_plugin_get_required_features(p);
240 	if (features) {
241 		printf("\tRequired Features: ");
242 	}
243 	first = true;
244 	LILV_FOREACH(nodes, i, features) {
245 		if (!first) {
246 			printf("\n\t                   ");
247 		}
248 		printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i)));
249 		first = false;
250 	}
251 	if (features) {
252 		printf("\n");
253 	}
254 	lilv_nodes_free(features);
255 
256 	/* Optional Features */
257 
258 	features = lilv_plugin_get_optional_features(p);
259 	if (features) {
260 		printf("\tOptional Features: ");
261 	}
262 	first = true;
263 	LILV_FOREACH(nodes, i, features) {
264 		if (!first) {
265 			printf("\n\t                   ");
266 		}
267 		printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i)));
268 		first = false;
269 	}
270 	if (features) {
271 		printf("\n");
272 	}
273 	lilv_nodes_free(features);
274 
275 	/* Extension Data */
276 
277 	LilvNodes* data = lilv_plugin_get_extension_data(p);
278 	if (data) {
279 		printf("\tExtension Data:    ");
280 	}
281 	first = true;
282 	LILV_FOREACH(nodes, i, data) {
283 		if (!first) {
284 			printf("\n\t                   ");
285 		}
286 		printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i)));
287 		first = false;
288 	}
289 	if (data) {
290 		printf("\n");
291 	}
292 	lilv_nodes_free(data);
293 
294 	/* Presets */
295 
296 	LilvNodes* presets = lilv_plugin_get_related(p, preset_class);
297 	if (presets) {
298 		printf("\tPresets: \n");
299 	}
300 	LILV_FOREACH(nodes, i, presets) {
301 		const LilvNode* preset = lilv_nodes_get(presets, i);
302 		lilv_world_load_resource(world, preset);
303 		LilvNodes* titles = lilv_world_find_nodes(
304 			world, preset, label_pred, NULL);
305 		if (titles) {
306 			const LilvNode* title = lilv_nodes_get_first(titles);
307 			printf("\t         %s\n", lilv_node_as_string(title));
308 			lilv_nodes_free(titles);
309 		} else {
310 			fprintf(stderr, "Preset <%s> has no rdfs:label\n",
311 			        lilv_node_as_string(lilv_nodes_get(presets, i)));
312 		}
313 	}
314 	lilv_nodes_free(presets);
315 
316 	/* Ports */
317 
318 	const uint32_t num_ports = lilv_plugin_get_num_ports(p);
319 	float* mins     = (float*)calloc(num_ports, sizeof(float));
320 	float* maxes    = (float*)calloc(num_ports, sizeof(float));
321 	float* defaults = (float*)calloc(num_ports, sizeof(float));
322 	lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults);
323 
324 	for (uint32_t i = 0; i < num_ports; ++i) {
325 		print_port(p, i, mins, maxes, defaults);
326 	}
327 
328 	free(mins);
329 	free(maxes);
330 	free(defaults);
331 }
332 
333 static void
print_version(void)334 print_version(void)
335 {
336 	printf(
337 		"lv2info (lilv) " LILV_VERSION "\n"
338 		"Copyright 2007-2019 David Robillard <http://drobilla.net>\n"
339 		"License: <http://www.opensource.org/licenses/isc-license>\n"
340 		"This is free software: you are free to change and redistribute it.\n"
341 		"There is NO WARRANTY, to the extent permitted by law.\n");
342 }
343 
344 static void
print_usage(void)345 print_usage(void)
346 {
347 	printf(
348 		"Usage: lv2info [OPTION]... PLUGIN_URI\n"
349 		"Print information about an installed LV2 plugin.\n\n"
350 		"  -p FILE      Write Turtle description of plugin to FILE\n"
351 		"  -m FILE      Add record of plugin to manifest FILE\n"
352 		"  --help       Display this help and exit\n"
353 		"  --version    Display version information and exit\n\n"
354 		"For -p and -m, Turtle files are appended to (not overwritten),\n"
355 		"and @prefix directives are only written if the file was empty.\n"
356 		"This allows several plugins to be added to a single file.\n");
357 }
358 
359 int
main(int argc,char ** argv)360 main(int argc, char** argv)
361 {
362 	if (argc == 1) {
363 		print_usage();
364 		return 1;
365 	}
366 
367 	const char* plugin_file   = NULL;
368 	const char* manifest_file = NULL;
369 	const char* plugin_uri    = NULL;
370 	for (int i = 1; i < argc; ++i) {
371 		if (!strcmp(argv[i], "--version")) {
372 			print_version();
373 			return 0;
374 		} else if (!strcmp(argv[i], "--help")) {
375 			print_usage();
376 			return 0;
377 		} else if (!strcmp(argv[i], "-p")) {
378 			plugin_file = argv[++i];
379 		} else if (!strcmp(argv[i], "-m")) {
380 			manifest_file = argv[++i];
381 		} else if (argv[i][0] == '-') {
382 			print_usage();
383 			return 1;
384 		} else if (i == argc - 1) {
385 			plugin_uri = argv[i];
386 		}
387 	}
388 
389 	int ret = 0;
390 
391 	LilvWorld* world = lilv_world_new();
392 	lilv_world_load_all(world);
393 
394 	LilvNode* uri = lilv_new_uri(world, plugin_uri);
395 	if (!uri) {
396 		fprintf(stderr, "Invalid plugin URI\n");
397 		lilv_world_free(world);
398 		return 1;
399 	}
400 
401 	applies_to_pred     = lilv_new_uri(world, LV2_CORE__appliesTo);
402 	control_class       = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
403 	event_class         = lilv_new_uri(world, LILV_URI_EVENT_PORT);
404 	group_pred          = lilv_new_uri(world, LV2_PORT_GROUPS__group);
405 	label_pred          = lilv_new_uri(world, LILV_NS_RDFS "label");
406 	preset_class        = lilv_new_uri(world, LV2_PRESETS__Preset);
407 	designation_pred    = lilv_new_uri(world, LV2_CORE__designation);
408 	supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent);
409 
410 	const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
411 	const LilvPlugin*  p       = lilv_plugins_get_by_uri(plugins, uri);
412 
413 	if (p && plugin_file) {
414 		LilvNode* base = lilv_new_file_uri(world, NULL, plugin_file);
415 
416 		FILE* plugin_fd = fopen(plugin_file, "a");
417 		if (plugin_fd) {
418 			lilv_plugin_write_description(world, p, base, plugin_fd);
419 			fclose(plugin_fd);
420 		} else {
421 			fprintf(stderr, "error: Failed to open %s\n", plugin_file);
422 		}
423 
424 		if (manifest_file) {
425 			FILE* manifest_fd = fopen(manifest_file, "a");
426 			if (manifest_fd) {
427 				lilv_plugin_write_manifest_entry(
428 					world, p, base, manifest_fd, plugin_file);
429 				fclose(manifest_fd);
430 			} else {
431 				fprintf(stderr, "error: Failed to open %s\n", manifest_file);
432 			}
433 		}
434 		lilv_node_free(base);
435 	} else if (p) {
436 		print_plugin(world, p);
437 	} else {
438 		fprintf(stderr, "Plugin not found.\n");
439 	}
440 
441 	ret = (p != NULL ? 0 : -1);
442 
443 	lilv_node_free(uri);
444 
445 	lilv_node_free(supports_event_pred);
446 	lilv_node_free(designation_pred);
447 	lilv_node_free(preset_class);
448 	lilv_node_free(label_pred);
449 	lilv_node_free(group_pred);
450 	lilv_node_free(event_class);
451 	lilv_node_free(control_class);
452 	lilv_node_free(applies_to_pred);
453 
454 	lilv_world_free(world);
455 	return ret;
456 }
457 
458