1 /*
2  * Copyright (c) 2016-2021 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <assert.h>
24 #if defined(HAS_FNMATCH)
25 #	include <fnmatch.h>
26 #endif
27 
28 #include <lv2lint.h>
29 
30 #include <lv2/patch/patch.h>
31 #include <lv2/atom/atom.h>
32 #include <lv2/worker/worker.h>
33 #include <lv2/log/log.h>
34 #include <lv2/port-groups/port-groups.h>
35 #include <lv2/event/event.h>
36 #include <lv2/morph/morph.h>
37 #include <lv2/uri-map/uri-map.h>
38 #include <lv2/instance-access/instance-access.h>
39 #include <lv2/parameters/parameters.h>
40 #include <lv2/port-props/port-props.h>
41 #include <lv2/buf-size/buf-size.h>
42 #include <lv2/resize-port/resize-port.h>
43 #include <lv2/options/options.h>
44 #include <lv2/data-access/data-access.h>
45 #include <lv2/dynmanifest/dynmanifest.h>
46 #include <lv2/state/state.h>
47 #include <lv2/ui/ui.h>
48 #include <lv2/units/units.h>
49 
50 #ifdef ENABLE_ELF_TESTS
51 #	include <fcntl.h>
52 #	include <libelf.h>
53 #	include <gelf.h>
54 #endif
55 
56 #define MAPPER_API static inline
57 #define MAPPER_IMPLEMENTATION
58 #include <mapper.lv2/mapper.h>
59 
60 const char *colors [2][ANSI_COLOR_MAX] = {
61 	{
62 		[ANSI_COLOR_BOLD]    = "",
63 		[ANSI_COLOR_RED]     = "",
64 		[ANSI_COLOR_GREEN]   = "",
65 		[ANSI_COLOR_YELLOW]  = "",
66 		[ANSI_COLOR_BLUE]    = "",
67 		[ANSI_COLOR_MAGENTA] = "",
68 		[ANSI_COLOR_CYAN]    = "",
69 		[ANSI_COLOR_RESET]   = ""
70 	},
71 	{
72 		[ANSI_COLOR_BOLD]    = "\x1b[1m",
73 		[ANSI_COLOR_RED]     = "\x1b[31m",
74 		[ANSI_COLOR_GREEN]   = "\x1b[32m",
75 		[ANSI_COLOR_YELLOW]  = "\x1b[33m",
76 		[ANSI_COLOR_BLUE]    = "\x1b[34m",
77 		[ANSI_COLOR_MAGENTA] = "\x1b[35m",
78 		[ANSI_COLOR_CYAN]    = "\x1b[36m",
79 		[ANSI_COLOR_RESET]   = "\x1b[0m"
80 	}
81 };
82 
83 #define NS_ITM(EXT, ID) [EXT ## __ ## ID] = LILV_NS_ ## EXT # ID
84 #define ITM(ID) [ID] = LV2_ ## ID
85 
86 static const char *stat_uris [STAT_URID_MAX] = {
87 	[STAT_URID_INVALID] = NULL,
88 
89 	NS_ITM(RDFS, label),
90 	NS_ITM(RDFS, comment),
91 	NS_ITM(RDFS, range),
92 	NS_ITM(RDFS, subClassOf),
93 
94 	NS_ITM(RDF, type),
95 	NS_ITM(RDF, value),
96 
97 	NS_ITM(DOAP, description),
98 	NS_ITM(DOAP, license),
99 	NS_ITM(DOAP, name),
100 	NS_ITM(DOAP, shortdesc),
101 
102 	NS_ITM(XSD, int),
103 	NS_ITM(XSD, nonNegativeInteger),
104 	NS_ITM(XSD, long),
105 	NS_ITM(XSD, float),
106 	NS_ITM(XSD, double),
107 
108 	ITM(ATOM__Atom),
109 	ITM(ATOM__AtomPort),
110 	ITM(ATOM__Blank),
111 	ITM(ATOM__Bool),
112 	ITM(ATOM__Chunk),
113 	ITM(ATOM__Double),
114 	ITM(ATOM__Event),
115 	ITM(ATOM__Float),
116 	ITM(ATOM__Int),
117 	ITM(ATOM__Literal),
118 	ITM(ATOM__Long),
119 	ITM(ATOM__Number),
120 	ITM(ATOM__Object),
121 	ITM(ATOM__Path),
122 	ITM(ATOM__Property),
123 	ITM(ATOM__Resource),
124 	ITM(ATOM__Sequence),
125 	ITM(ATOM__Sound),
126 	ITM(ATOM__String),
127 	ITM(ATOM__Tuple),
128 	ITM(ATOM__URI),
129 	ITM(ATOM__URID),
130 	ITM(ATOM__Vector),
131 	ITM(ATOM__atomTransfer),
132 	ITM(ATOM__beatTime),
133 	ITM(ATOM__bufferType),
134 	ITM(ATOM__childType),
135 	ITM(ATOM__eventTransfer),
136 	ITM(ATOM__frameTime),
137 	ITM(ATOM__supports),
138 	ITM(ATOM__timeUnit),
139 
140 	ITM(BUF_SIZE__boundedBlockLength),
141 	ITM(BUF_SIZE__coarseBlockLength),
142 	ITM(BUF_SIZE__fixedBlockLength),
143 	ITM(BUF_SIZE__maxBlockLength),
144 	ITM(BUF_SIZE__minBlockLength),
145 	ITM(BUF_SIZE__nominalBlockLength),
146 	ITM(BUF_SIZE__powerOf2BlockLength),
147 	ITM(BUF_SIZE__sequenceSize),
148 
149 	ITM(CORE__AllpassPlugin),
150 	ITM(CORE__AmplifierPlugin),
151 	ITM(CORE__AnalyserPlugin),
152 	ITM(CORE__AudioPort),
153 	ITM(CORE__BandpassPlugin),
154 	ITM(CORE__CVPort),
155 	ITM(CORE__ChorusPlugin),
156 	ITM(CORE__CombPlugin),
157 	ITM(CORE__CompressorPlugin),
158 	ITM(CORE__ConstantPlugin),
159 	ITM(CORE__ControlPort),
160 	ITM(CORE__ConverterPlugin),
161 	ITM(CORE__DelayPlugin),
162 	ITM(CORE__DistortionPlugin),
163 	ITM(CORE__DynamicsPlugin),
164 	ITM(CORE__EQPlugin),
165 	ITM(CORE__EnvelopePlugin),
166 	ITM(CORE__ExpanderPlugin),
167 	ITM(CORE__ExtensionData),
168 	ITM(CORE__Feature),
169 	ITM(CORE__FilterPlugin),
170 	ITM(CORE__FlangerPlugin),
171 	ITM(CORE__FunctionPlugin),
172 	ITM(CORE__GatePlugin),
173 	ITM(CORE__GeneratorPlugin),
174 	ITM(CORE__HighpassPlugin),
175 	ITM(CORE__InputPort),
176 	ITM(CORE__InstrumentPlugin),
177 	ITM(CORE__LimiterPlugin),
178 	ITM(CORE__LowpassPlugin),
179 	ITM(CORE__MixerPlugin),
180 	ITM(CORE__ModulatorPlugin),
181 	ITM(CORE__MultiEQPlugin),
182 	ITM(CORE__OscillatorPlugin),
183 	ITM(CORE__OutputPort),
184 	ITM(CORE__ParaEQPlugin),
185 	ITM(CORE__PhaserPlugin),
186 	ITM(CORE__PitchPlugin),
187 	ITM(CORE__Plugin),
188 	ITM(CORE__PluginBase),
189 	ITM(CORE__Point),
190 	ITM(CORE__Port),
191 	ITM(CORE__PortProperty),
192 	ITM(CORE__Resource),
193 	ITM(CORE__ReverbPlugin),
194 	ITM(CORE__ScalePoint),
195 	ITM(CORE__SimulatorPlugin),
196 	ITM(CORE__SpatialPlugin),
197 	ITM(CORE__Specification),
198 	ITM(CORE__SpectralPlugin),
199 	ITM(CORE__UtilityPlugin),
200 	ITM(CORE__WaveshaperPlugin),
201 	ITM(CORE__appliesTo),
202 	ITM(CORE__binary),
203 	ITM(CORE__connectionOptional),
204 	ITM(CORE__control),
205 	ITM(CORE__default),
206 	ITM(CORE__designation),
207 	ITM(CORE__documentation),
208 	ITM(CORE__enumeration),
209 	ITM(CORE__extensionData),
210 	ITM(CORE__freeWheeling),
211 	ITM(CORE__hardRTCapable),
212 	ITM(CORE__inPlaceBroken),
213 	ITM(CORE__index),
214 	ITM(CORE__integer),
215 	ITM(CORE__isLive),
216 	ITM(CORE__latency),
217 	ITM(CORE__maximum),
218 	ITM(CORE__microVersion),
219 	ITM(CORE__minimum),
220 	ITM(CORE__minorVersion),
221 	ITM(CORE__name),
222 	ITM(CORE__optionalFeature),
223 	ITM(CORE__port),
224 	ITM(CORE__portProperty),
225 	ITM(CORE__project),
226 	ITM(CORE__prototype),
227 	ITM(CORE__reportsLatency),
228 	ITM(CORE__requiredFeature),
229 	ITM(CORE__sampleRate),
230 	ITM(CORE__scalePoint),
231 	ITM(CORE__symbol),
232 	ITM(CORE__toggled),
233 
234 	[DATA_ACCESS] = LV2_DATA_ACCESS_URI,
235 
236 	[DYN_MANIFEST] = LV2_DYN_MANIFEST_URI,
237 
238 	ITM(EVENT__Event),
239 	ITM(EVENT__EventPort),
240 	ITM(EVENT__FrameStamp),
241 	ITM(EVENT__TimeStamp),
242 	ITM(EVENT__generatesTimeStamp),
243 	ITM(EVENT__generic),
244 	ITM(EVENT__inheritsEvent),
245 	ITM(EVENT__inheritsTimeStamp),
246 	ITM(EVENT__supportsEvent),
247 	ITM(EVENT__supportsTimeStamp),
248 
249 	[INSTANCE_ACCESS] = LV2_INSTANCE_ACCESS_URI,
250 
251 	ITM(LOG__Entry),
252 	ITM(LOG__Error),
253 	ITM(LOG__Note),
254 	ITM(LOG__Trace),
255 	ITM(LOG__Warning),
256 	ITM(LOG__log),
257 
258 	ITM(MIDI__ActiveSense),
259 	ITM(MIDI__Aftertouch),
260 	ITM(MIDI__Bender),
261 	ITM(MIDI__ChannelPressure),
262 	ITM(MIDI__Chunk),
263 	ITM(MIDI__Clock),
264 	ITM(MIDI__Continue),
265 	ITM(MIDI__Controller),
266 	ITM(MIDI__MidiEvent),
267 	ITM(MIDI__NoteOff),
268 	ITM(MIDI__NoteOn),
269 	ITM(MIDI__ProgramChange),
270 	ITM(MIDI__QuarterFrame),
271 	ITM(MIDI__Reset),
272 	ITM(MIDI__SongPosition),
273 	ITM(MIDI__SongSelect),
274 	ITM(MIDI__Start),
275 	ITM(MIDI__Stop),
276 	ITM(MIDI__SystemCommon),
277 	ITM(MIDI__SystemExclusive),
278 	ITM(MIDI__SystemMessage),
279 	ITM(MIDI__SystemRealtime),
280 	ITM(MIDI__Tick),
281 	ITM(MIDI__TuneRequest),
282 	ITM(MIDI__VoiceMessage),
283 	ITM(MIDI__benderValue),
284 	ITM(MIDI__binding),
285 	ITM(MIDI__byteNumber),
286 	ITM(MIDI__channel),
287 	ITM(MIDI__chunk),
288 	ITM(MIDI__controllerNumber),
289 	ITM(MIDI__controllerValue),
290 	ITM(MIDI__noteNumber),
291 	ITM(MIDI__pressure),
292 	ITM(MIDI__programNumber),
293 	ITM(MIDI__property),
294 	ITM(MIDI__songNumber),
295 	ITM(MIDI__songPosition),
296 	ITM(MIDI__status),
297 	ITM(MIDI__statusMask),
298 	ITM(MIDI__velocity),
299 
300 	ITM(MORPH__AutoMorphPort),
301 	ITM(MORPH__MorphPort),
302 	ITM(MORPH__interface),
303 	ITM(MORPH__supportsType),
304 	ITM(MORPH__currentType),
305 
306 	ITM(OPTIONS__Option),
307 	ITM(OPTIONS__interface),
308 	ITM(OPTIONS__options),
309 	ITM(OPTIONS__requiredOption),
310 	ITM(OPTIONS__supportedOption),
311 
312 	ITM(PARAMETERS__CompressorControls),
313 	ITM(PARAMETERS__ControlGroup),
314 	ITM(PARAMETERS__EnvelopeControls),
315 	ITM(PARAMETERS__FilterControls),
316 	ITM(PARAMETERS__OscillatorControls),
317 	ITM(PARAMETERS__amplitude),
318 	ITM(PARAMETERS__attack),
319 	ITM(PARAMETERS__bypass),
320 	ITM(PARAMETERS__cutoffFrequency),
321 	ITM(PARAMETERS__decay),
322 	ITM(PARAMETERS__delay),
323 	ITM(PARAMETERS__dryLevel),
324 	ITM(PARAMETERS__frequency),
325 	ITM(PARAMETERS__gain),
326 	ITM(PARAMETERS__hold),
327 	ITM(PARAMETERS__pulseWidth),
328 	ITM(PARAMETERS__ratio),
329 	ITM(PARAMETERS__release),
330 	ITM(PARAMETERS__resonance),
331 	ITM(PARAMETERS__sampleRate),
332 	ITM(PARAMETERS__sustain),
333 	ITM(PARAMETERS__threshold),
334 	ITM(PARAMETERS__waveform),
335 	ITM(PARAMETERS__wetDryRatio),
336 	ITM(PARAMETERS__wetLevel),
337 
338 	ITM(PATCH__Ack),
339 	ITM(PATCH__Delete),
340 	ITM(PATCH__Copy),
341 	ITM(PATCH__Error),
342 	ITM(PATCH__Get),
343 	ITM(PATCH__Message),
344 	ITM(PATCH__Move),
345 	ITM(PATCH__Patch),
346 	ITM(PATCH__Post),
347 	ITM(PATCH__Put),
348 	ITM(PATCH__Request),
349 	ITM(PATCH__Response),
350 	ITM(PATCH__Set),
351 	ITM(PATCH__accept),
352 	ITM(PATCH__add),
353 	ITM(PATCH__body),
354 	ITM(PATCH__context),
355 	ITM(PATCH__destination),
356 	ITM(PATCH__property),
357 	ITM(PATCH__readable),
358 	ITM(PATCH__remove),
359 	ITM(PATCH__request),
360 	ITM(PATCH__subject),
361 	ITM(PATCH__sequenceNumber),
362 	ITM(PATCH__value),
363 	ITM(PATCH__wildcard),
364 	ITM(PATCH__writable),
365 
366 	ITM(PORT_GROUPS__DiscreteGroup),
367 	ITM(PORT_GROUPS__Element),
368 	ITM(PORT_GROUPS__FivePointOneGroup),
369 	ITM(PORT_GROUPS__FivePointZeroGroup),
370 	ITM(PORT_GROUPS__FourPointZeroGroup),
371 	ITM(PORT_GROUPS__Group),
372 	ITM(PORT_GROUPS__InputGroup),
373 	ITM(PORT_GROUPS__MidSideGroup),
374 	ITM(PORT_GROUPS__MonoGroup),
375 	ITM(PORT_GROUPS__OutputGroup),
376 	ITM(PORT_GROUPS__SevenPointOneGroup),
377 	ITM(PORT_GROUPS__SevenPointOneWideGroup),
378 	ITM(PORT_GROUPS__SixPointOneGroup),
379 	ITM(PORT_GROUPS__StereoGroup),
380 	ITM(PORT_GROUPS__ThreePointZeroGroup),
381 	ITM(PORT_GROUPS__center),
382 	ITM(PORT_GROUPS__centerLeft),
383 	ITM(PORT_GROUPS__centerRight),
384 	ITM(PORT_GROUPS__element),
385 	ITM(PORT_GROUPS__group),
386 	ITM(PORT_GROUPS__left),
387 	ITM(PORT_GROUPS__lowFrequencyEffects),
388 	ITM(PORT_GROUPS__mainInput),
389 	ITM(PORT_GROUPS__mainOutput),
390 	ITM(PORT_GROUPS__rearCenter),
391 	ITM(PORT_GROUPS__rearLeft),
392 	ITM(PORT_GROUPS__rearRight),
393 	ITM(PORT_GROUPS__right),
394 	ITM(PORT_GROUPS__side),
395 	ITM(PORT_GROUPS__sideChainOf),
396 	ITM(PORT_GROUPS__sideLeft),
397 	ITM(PORT_GROUPS__sideRight),
398 	ITM(PORT_GROUPS__source),
399 	ITM(PORT_GROUPS__subGroupOf),
400 
401 	ITM(PORT_PROPS__causesArtifacts),
402 	ITM(PORT_PROPS__continuousCV),
403 	ITM(PORT_PROPS__discreteCV),
404 	ITM(PORT_PROPS__displayPriority),
405 	ITM(PORT_PROPS__expensive),
406 	ITM(PORT_PROPS__hasStrictBounds),
407 	ITM(PORT_PROPS__logarithmic),
408 	ITM(PORT_PROPS__notAutomatic),
409 	ITM(PORT_PROPS__notOnGUI),
410 	ITM(PORT_PROPS__rangeSteps),
411 	ITM(PORT_PROPS__supportsStrictBounds),
412 	ITM(PORT_PROPS__trigger),
413 
414 	ITM(PRESETS__Bank),
415 	ITM(PRESETS__Preset),
416 	ITM(PRESETS__bank),
417 	ITM(PRESETS__preset),
418 	ITM(PRESETS__value),
419 
420 	ITM(RESIZE_PORT__asLargeAs),
421 	ITM(RESIZE_PORT__minimumSize),
422 	ITM(RESIZE_PORT__resize),
423 
424 	ITM(STATE__State),
425 	ITM(STATE__interface),
426 	ITM(STATE__loadDefaultState),
427 	ITM(STATE__freePath),
428 	ITM(STATE__makePath),
429 	ITM(STATE__mapPath),
430 	ITM(STATE__state),
431 	ITM(STATE__threadSafeRestore),
432 	ITM(STATE__StateChanged),
433 
434 	ITM(TIME__Time),
435 	ITM(TIME__Position),
436 	ITM(TIME__Rate),
437 	ITM(TIME__position),
438 	ITM(TIME__barBeat),
439 	ITM(TIME__bar),
440 	ITM(TIME__beat),
441 	ITM(TIME__beatUnit),
442 	ITM(TIME__beatsPerBar),
443 	ITM(TIME__beatsPerMinute),
444 	ITM(TIME__frame),
445 	ITM(TIME__framesPerSecond),
446 	ITM(TIME__speed),
447 
448 	ITM(UI__CocoaUI),
449 	ITM(UI__Gtk3UI),
450 	ITM(UI__GtkUI),
451 	ITM(UI__PortNotification),
452 	ITM(UI__PortProtocol),
453 	ITM(UI__Qt4UI),
454 	ITM(UI__Qt5UI),
455 	ITM(UI__UI),
456 	ITM(UI__WindowsUI),
457 	ITM(UI__X11UI),
458 	ITM(UI__binary),
459 	ITM(UI__fixedSize),
460 	ITM(UI__idleInterface),
461 	ITM(UI__noUserResize),
462 	ITM(UI__notifyType),
463 	ITM(UI__parent),
464 	ITM(UI__plugin),
465 	ITM(UI__portIndex),
466 	ITM(UI__portMap),
467 	ITM(UI__portNotification),
468 	ITM(UI__portSubscribe),
469 	ITM(UI__protocol),
470 	ITM(UI__requestValue),
471 	ITM(UI__floatProtocol),
472 	ITM(UI__peakProtocol),
473 	ITM(UI__resize),
474 	ITM(UI__showInterface),
475 	ITM(UI__touch),
476 	ITM(UI__ui),
477 	ITM(UI__updateRate),
478 	ITM(UI__windowTitle),
479 	ITM(UI__scaleFactor),
480 	ITM(UI__foregroundColor),
481 	ITM(UI__backgroundColor),
482 	ITM(UI__makeSONameResident),
483 
484 	ITM(UNITS__Conversion),
485 	ITM(UNITS__Unit),
486 	ITM(UNITS__bar),
487 	ITM(UNITS__beat),
488 	ITM(UNITS__bpm),
489 	ITM(UNITS__cent),
490 	ITM(UNITS__cm),
491 	ITM(UNITS__coef),
492 	ITM(UNITS__conversion),
493 	ITM(UNITS__db),
494 	ITM(UNITS__degree),
495 	ITM(UNITS__frame),
496 	ITM(UNITS__hz),
497 	ITM(UNITS__inch),
498 	ITM(UNITS__khz),
499 	ITM(UNITS__km),
500 	ITM(UNITS__m),
501 	ITM(UNITS__mhz),
502 	ITM(UNITS__midiNote),
503 	ITM(UNITS__mile),
504 	ITM(UNITS__min),
505 	ITM(UNITS__mm),
506 	ITM(UNITS__ms),
507 	ITM(UNITS__name),
508 	ITM(UNITS__oct),
509 	ITM(UNITS__pc),
510 	ITM(UNITS__prefixConversion),
511 	ITM(UNITS__render),
512 	ITM(UNITS__s),
513 	ITM(UNITS__semitone12TET),
514 	ITM(UNITS__symbol),
515 	ITM(UNITS__unit),
516 
517 	ITM(URID__map),
518 	ITM(URID__unmap),
519 
520 	[URI_MAP] = LV2_URI_MAP_URI,
521 
522 	ITM(WORKER__interface),
523 	ITM(WORKER__schedule),
524 
525 	ITM(EXTERNAL_UI__Widget),
526 
527 	ITM(INLINEDISPLAY__interface),
528 	ITM(INLINEDISPLAY__queue_draw)
529 };
530 
531 #undef ITM
532 
533 static void
_map_uris(app_t * app)534 _map_uris(app_t *app)
535 {
536 	for(unsigned i = 1; i < STAT_URID_MAX; i++)
537 	{
538 		app->nodes[i] = lilv_new_uri(app->world, stat_uris[i]);
539 	}
540 }
541 
542 static void
_unmap_uris(app_t * app)543 _unmap_uris(app_t *app)
544 {
545 	for(unsigned i = 1; i < STAT_URID_MAX; i++)
546 	{
547 		lilv_node_free(app->nodes[i]);
548 	}
549 }
550 
551 static void
_free_urids(app_t * app)552 _free_urids(app_t *app)
553 {
554 	for(unsigned i = 0; i < app->nurids; i++)
555 	{
556 		urid_t *itm = &app->urids[i];
557 
558 		if(itm->uri)
559 			free(itm->uri);
560 	}
561 	free(app->urids);
562 
563 	app->urids = NULL;
564 	app->nurids = 0;
565 }
566 
567 static LV2_Worker_Status
_respond(LV2_Worker_Respond_Handle instance,uint32_t size,const void * data)568 _respond(LV2_Worker_Respond_Handle instance, uint32_t size, const void *data)
569 {
570 	app_t *app = instance;
571 
572 	if(app->work_iface && app->work_iface->work_response)
573 		return app->work_iface->work_response(&app->instance, size, data);
574 
575 	else return LV2_WORKER_ERR_UNKNOWN;
576 }
577 
578 static LV2_Worker_Status
_sched(LV2_Worker_Schedule_Handle instance,uint32_t size,const void * data)579 _sched(LV2_Worker_Schedule_Handle instance, uint32_t size, const void *data)
580 {
581 	app_t *app = instance;
582 
583 	LV2_Worker_Status status = LV2_WORKER_SUCCESS;
584 	if(app->work_iface && app->work_iface->work)
585 		status |= app->work_iface->work(&app->instance, _respond, app, size, data);
586 	if(app->work_iface && app->work_iface->end_run)
587 		status |= app->work_iface->end_run(&app->instance);
588 
589 	return status;
590 }
591 
592 #if defined(_WIN32)
593 static inline char *
strsep(char ** sp,const char * sep)594 strsep(char **sp, const char *sep)
595 {
596 	char *p, *s;
597 	if(sp == NULL || *sp == NULL || **sp == '\0')
598 		return(NULL);
599 	s = *sp;
600 	p = s + strcspn(s, sep);
601 	if(*p != '\0')
602 		*p++ = '\0';
603 	*sp = p;
604 	return(s);
605 }
606 #endif
607 
608 int
log_vprintf(void * data __unused,LV2_URID type __unused,const char * fmt,va_list args)609 log_vprintf(void *data __unused, LV2_URID type __unused, const char *fmt,
610 	va_list args)
611 {
612 	char *buf = NULL;
613 
614 	if(asprintf(&buf, fmt, args) == -1)
615 	{
616 		buf = NULL;
617 	}
618 
619 	if(buf)
620 	{
621 		const char *sep = "\n\0";
622 		for(char *bufp = buf, *ptr = strsep(&bufp, sep);
623 			ptr;
624 			ptr = strsep(&bufp, sep) )
625 		{
626 			fprintf(stderr, "%s\n", ptr);
627 		}
628 
629 		free(buf);
630 	}
631 
632 	return 0;
633 }
634 
635 int
log_printf(void * data,LV2_URID type,const char * fmt,...)636 log_printf(void *data, LV2_URID type, const char *fmt, ...)
637 {
638   va_list args;
639 	int ret;
640 
641   va_start (args, fmt);
642 	ret = log_vprintf(data, type, fmt, args);
643   va_end(args);
644 
645 	return ret;
646 }
647 
648 static char *
_mkpath(LV2_State_Make_Path_Handle instance __unused,const char * abstract_path)649 _mkpath(LV2_State_Make_Path_Handle instance __unused, const char *abstract_path)
650 {
651 	char *absolute_path = NULL;
652 
653 	if(asprintf(&absolute_path, "/tmp/%s", abstract_path) == -1)
654 		absolute_path = NULL;
655 
656 	return absolute_path;
657 }
658 
659 static void
_freepath(LV2_State_Free_Path_Handle instance __unused,char * absolute_path)660 _freepath(LV2_State_Free_Path_Handle instance __unused, char *absolute_path)
661 {
662 	if(absolute_path)
663 	{
664 		free(absolute_path);
665 	}
666 }
667 
668 static LV2_Resize_Port_Status
_resize(LV2_Resize_Port_Feature_Data instance __unused,uint32_t index __unused,size_t size __unused)669 _resize(LV2_Resize_Port_Feature_Data instance __unused, uint32_t index __unused,
670 	size_t size __unused)
671 {
672 	return LV2_RESIZE_PORT_SUCCESS;
673 }
674 
675 #pragma GCC diagnostic push
676 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
677 uint32_t
uri_to_id(LV2_URI_Map_Callback_Data instance,const char * _map,const char * uri)678 uri_to_id(LV2_URI_Map_Callback_Data instance,
679 	const char *_map __attribute__((unused)), const char *uri)
680 {
681 	LV2_URID_Map *map = instance;
682 
683 	return map->map(map->handle, uri);
684 }
685 #pragma GCC diagnostic pop
686 
687 static void
_queue_draw(LV2_Inline_Display_Handle instance)688 _queue_draw(LV2_Inline_Display_Handle instance)
689 {
690 	app_t *app = instance;
691 	(void)app;
692 }
693 
694 static void
_header(char ** argv)695 _header(char **argv)
696 {
697 	fprintf(stderr,
698 		"%s "LV2LINT_VERSION"\n"
699 		"Copyright (c) 2016-2021 Hanspeter Portner (dev@open-music-kontrollers.ch)\n"
700 		"Released under Artistic License 2.0 by Open Music Kontrollers\n",
701 		argv[0]);
702 }
703 
704 static void
_version(char ** argv)705 _version(char **argv)
706 {
707 	_header(argv);
708 
709 	fprintf(stderr,
710 		"--------------------------------------------------------------------\n"
711 		"This is free software: you can redistribute it and/or modify\n"
712 		"it under the terms of the Artistic License 2.0 as published by\n"
713 		"The Perl Foundation.\n"
714 		"\n"
715 		"This source is distributed in the hope that it will be useful,\n"
716 		"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
717 		"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
718 		"Artistic License 2.0 for more details.\n"
719 		"\n"
720 		"You should have received a copy of the Artistic License 2.0\n"
721 		"along the source as a COPYING file. If not, obtain it from\n"
722 		"http://www.perlfoundation.org/artistic_license_2_0.\n\n");
723 }
724 
725 static void
_usage(char ** argv)726 _usage(char **argv)
727 {
728 	_header(argv);
729 
730 	fprintf(stderr,
731 		"--------------------------------------------------------------------\n"
732 		"USAGE\n"
733 		"   %s [OPTIONS] {PLUGIN_URI}*\n"
734 		"\n"
735 		"OPTIONS\n"
736 		"   [-v]                         print version information\n"
737 		"   [-h]                         print usage information\n"
738 		"   [-q]                         quiet mode, show only a summary\n"
739 		"   [-d]                         show verbose test item documentation\n"
740 		"   [-I] INCLUDE_DIR             use include directory to search for plugins"
741 		                                 " (can be used multiple times)\n"
742 		"   [-u] URI_PATTERN             URI pattern (shell wildcards) to prefix other whitelist patterns "
743 		                                 " (can be used multiple times)\n"
744 		"   [-t] TEST_PATTERN            test name pattern (shell wildcards) to whitelist"
745 		                                 " (can be used multiple times)\n"
746 #ifdef ENABLE_ELF_TESTS
747 		"   [-s] SYMBOL_PATTERN          symbol pattern (shell wildcards) to whitelist"
748 		                                 " (can be used multiple times)\n"
749 		"   [-l] LIBRARY_PATTERN         library pattern (shell wildcards) to whitelist"
750 		                                 " (can be used multiple times)\n"
751 #endif
752 #ifdef ENABLE_ONLINE_TESTS
753 		"   [-o]                         run online test items\n"
754 		"   [-m]                         create mail to plugin author\n"
755 		"   [-g] GREETER                 custom mail greeter\n"
756 #endif
757 
758 		"   [-M] (no)pack                skip some tests for distribution packagers\n"
759 		"   [-S] (no)warn|note|pass|all  show warnings, notes, passes or all\n"
760 		"   [-E] (no)warn|note|all       treat warnings, notes or all as errors\n\n"
761 		, argv[0]);
762 }
763 
764 #ifdef ENABLE_ONLINE_TESTS
765 static const char *http_prefix = "http://";
766 static const char *https_prefix = "https://";
767 static const char *ftp_prefix = "ftp://";
768 static const char *ftps_prefix = "ftps://";
769 
770 bool
is_url(const char * uri)771 is_url(const char *uri)
772 {
773 	const bool is_http = strncmp(uri, http_prefix, strlen(http_prefix));
774 	const bool is_https = strncmp(uri, https_prefix, strlen(https_prefix));
775 	const bool is_ftp = strncmp(uri, ftp_prefix, strlen(ftp_prefix));
776 	const bool is_ftps = strncmp(uri, ftps_prefix, strlen(ftps_prefix));
777 
778 	return is_http || is_https || is_ftp || is_ftps;
779 }
780 
781 bool
test_url(app_t * app,const char * url)782 test_url(app_t *app, const char *url)
783 {
784 	curl_easy_setopt(app->curl, CURLOPT_URL, url);
785 	curl_easy_setopt(app->curl, CURLOPT_FOLLOWLOCATION, 1);
786 	curl_easy_setopt(app->curl, CURLOPT_NOBODY, 1);
787 	curl_easy_setopt(app->curl, CURLOPT_CONNECTTIMEOUT, 10L); // secs
788 	curl_easy_setopt(app->curl, CURLOPT_TIMEOUT, 20L); //secs
789 
790 	const CURLcode resp = curl_easy_perform(app->curl);
791 
792 	long http_code;
793 	curl_easy_getinfo(app->curl, CURLINFO_RESPONSE_CODE, &http_code);
794 
795 	if( (resp == CURLE_OK) && (http_code == 200) )
796 	{
797 		return true;
798 	}
799 
800 	return false;
801 }
802 #endif
803 
804 static white_t *
_white_append(white_t * parent,const char * uri,const char * pattern)805 _white_append(white_t *parent, const char *uri, const char *pattern)
806 {
807 	white_t *white = calloc(1, sizeof(white_t));
808 
809 	if(!white)
810 	{
811 		return parent;
812 	}
813 
814 	white->uri = uri;
815 	white->pattern = pattern;
816 	white->next = parent;
817 
818 	return white;
819 }
820 
821 static white_t *
_white_remove(white_t * parent)822 _white_remove(white_t *parent)
823 {
824 	white_t *next = NULL;
825 
826 	if(parent)
827 	{
828 		next = parent->next;
829 		free(parent);
830 	}
831 
832 	return next;
833 }
834 
835 static white_t *
_white_free(white_t * white)836 _white_free(white_t *white)
837 {
838 	while(white)
839 	{
840 		white = _white_remove(white);
841 	}
842 
843 	return NULL;
844 }
845 
846 static bool
_pattern_match(const char * pattern,const char * str)847 _pattern_match(const char *pattern, const char *str)
848 {
849 	if(pattern == NULL)
850 	{
851 		return true;
852 	}
853 
854 #if defined(HAS_FNMATCH)
855 	if(fnmatch(pattern, str, FNM_CASEFOLD) == 0)
856 #else
857 	if(strcasecmp(pattern, str) == 0)
858 #endif
859 	{
860 		return true;
861 	}
862 
863 	return false;
864 }
865 
866 static bool
_white_match(const white_t * white,const char * uri,const char * str)867 _white_match(const white_t *white, const char *uri, const char *str)
868 {
869 	for( ; white; white = white->next)
870 	{
871 		if(_pattern_match(white->uri, uri) && _pattern_match(white->pattern, str))
872 		{
873 			return true;
874 		}
875 	}
876 
877 	return false;
878 }
879 
880 #ifdef ENABLE_ELF_TESTS
881 static void
_append_to(char ** dst,const char * src)882 _append_to(char **dst, const char *src)
883 {
884 	static const char *prefix = "\n                * ";
885 
886 	if(*dst)
887 	{
888 		const size_t sz = strlen(*dst) + strlen(prefix) + strlen(src) + 1;
889 		*dst = realloc(*dst, sz);
890 		strcat(*dst, prefix);
891 		strcat(*dst, src);
892 	}
893 	else
894 	{
895 		const size_t sz = strlen(src) + strlen(prefix) + 1;
896 		*dst = malloc(sz);
897 		strcpy(*dst, prefix);
898 		strcat(*dst, src);
899 	}
900 }
901 
902 bool
test_visibility(app_t * app,const char * path,const char * uri,const char * description,char ** symbols)903 test_visibility(app_t *app, const char *path, const char *uri,
904 	const char *description, char **symbols)
905 {
906 	static const char *whitelist [] = {
907 		// LV2
908 		"lv2_descriptor",
909 		"lv2ui_descriptor",
910 		"lv2_dyn_manifest_open",
911 		"lv2_dyn_manifest_get_subjects",
912 		"lv2_dyn_manifest_get_data",
913 		"lv2_dyn_manifest_close",
914 		// C
915 		"_init",
916 		"_fini",
917 		"_edata",
918 		"_end",
919 		"__bss_start",
920 		"__rt_data__start",
921 		"__rt_data__end",
922 		"__rt_text__start",
923 		"__rt_text__end",
924 		// Rust
925 		"__rdl_alloc",
926 		"__rdl_alloc_excess",
927 		"__rdl_alloc_zeroed",
928 		"__rdl_dealloc",
929 		"__rdl_grow_in_place",
930 		"__rdl_oom",
931 		"__rdl_realloc",
932 		"__rdl_realloc_excess",
933 		"__rdl_shrink_in_place",
934 		"__rdl_usable_size",
935 		"rust_eh_personality"
936 	};
937 	const unsigned n_whitelist = sizeof(whitelist) / sizeof(const char *);
938 	bool desc = false;
939 	unsigned invalid = 0;
940 
941 	const int fd = open(path, O_RDONLY);
942 	if(fd != -1)
943 	{
944 		elf_version(EV_CURRENT);
945 
946 		Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
947 		if(elf)
948 		{
949 			for(Elf_Scn *scn = elf_nextscn(elf, NULL);
950 				scn;
951 				scn = elf_nextscn(elf, scn))
952 			{
953 				GElf_Shdr shdr;
954 				memset(&shdr, 0x0, sizeof(GElf_Shdr));
955 				gelf_getshdr(scn, &shdr);
956 
957 				if( (shdr.sh_type == SHT_SYMTAB) || (shdr.sh_type == SHT_DYNSYM) )
958 				{
959 					// found a symbol table
960 					Elf_Data *data = elf_getdata(scn, NULL);
961 					const unsigned count = shdr.sh_size / shdr.sh_entsize;
962 
963 					// iterate over symbol names
964 					for(unsigned i = 0; i < count; i++)
965 					{
966 						GElf_Sym sym;
967 						memset(&sym, 0x0, sizeof(GElf_Sym));
968 						gelf_getsym(data, i, &sym);
969 
970 						const bool is_global = GELF_ST_BIND(sym.st_info) == STB_GLOBAL;
971 						if(sym.st_value && is_global)
972 						{
973 							const char *name = elf_strptr(elf, shdr.sh_link, sym.st_name);
974 
975 							if(!strcmp(name, description))
976 							{
977 								desc = true;
978 							}
979 							else
980 							{
981 								bool whitelist_match = false;
982 
983 								for(unsigned j = 0; j < n_whitelist; j++)
984 								{
985 									if(!strcmp(name, whitelist[j]))
986 									{
987 										whitelist_match = true;
988 										break;
989 									}
990 								}
991 
992 								if(!whitelist_match)
993 								{
994 									if(_white_match(app->whitelist_symbols, uri, name))
995 									{
996 										whitelist_match = true;
997 									}
998 								}
999 
1000 								if(!whitelist_match)
1001 								{
1002 									if(invalid <= 10)
1003 									{
1004 										_append_to(symbols, (invalid == 10)
1005 											? "... there is more, but the rest is being truncated"
1006 											: name);
1007 									}
1008 									invalid++;
1009 								}
1010 							}
1011 						}
1012 					}
1013 
1014 					break;
1015 				}
1016 			}
1017 			elf_end(elf);
1018 		}
1019 		close(fd);
1020 	}
1021 
1022 	return !(!desc || invalid);
1023 }
1024 
1025 bool
check_for_symbol(app_t * app,const char * path,const char * description)1026 check_for_symbol(app_t *app __attribute__((unused)), const char *path,
1027 	const char *description)
1028 {
1029 	bool desc = false;
1030 
1031 	const int fd = open(path, O_RDONLY);
1032 	if(fd != -1)
1033 	{
1034 		elf_version(EV_CURRENT);
1035 
1036 		Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
1037 		if(elf)
1038 		{
1039 			for(Elf_Scn *scn = elf_nextscn(elf, NULL);
1040 				scn;
1041 				scn = elf_nextscn(elf, scn))
1042 			{
1043 				GElf_Shdr shdr;
1044 				memset(&shdr, 0x0, sizeof(GElf_Shdr));
1045 				gelf_getshdr(scn, &shdr);
1046 
1047 				if( (shdr.sh_type == SHT_SYMTAB) || (shdr.sh_type == SHT_DYNSYM) )
1048 				{
1049 					// found a symbol table
1050 					Elf_Data *data = elf_getdata(scn, NULL);
1051 					const unsigned count = shdr.sh_size / shdr.sh_entsize;
1052 
1053 					// iterate over symbol names
1054 					for(unsigned i = 0; i < count; i++)
1055 					{
1056 						GElf_Sym sym;
1057 						memset(&sym, 0x0, sizeof(GElf_Sym));
1058 						gelf_getsym(data, i, &sym);
1059 
1060 						const char *name = elf_strptr(elf, shdr.sh_link, sym.st_name);
1061 						if(!strcmp(name, description))
1062 						{
1063 							desc = true;
1064 							break;
1065 						}
1066 					}
1067 				}
1068 			}
1069 			elf_end(elf);
1070 		}
1071 		close(fd);
1072 	}
1073 
1074 	return desc;
1075 }
1076 
1077 bool
test_shared_libraries(app_t * app,const char * path,const char * uri,const char * const * whitelist,unsigned n_whitelist,const char * const * blacklist,unsigned n_blacklist,char ** libraries)1078 test_shared_libraries(app_t *app, const char *path, const char *uri,
1079 	const char *const *whitelist, unsigned n_whitelist,
1080 	const char *const *blacklist, unsigned n_blacklist,
1081 	char **libraries)
1082 {
1083 	unsigned invalid = 0;
1084 
1085 	const int fd = open(path, O_RDONLY);
1086 	if(fd != -1)
1087 	{
1088 		elf_version(EV_CURRENT);
1089 
1090 		Elf *elf = elf_begin(fd, ELF_C_READ, NULL);
1091 		if(elf)
1092 		{
1093 			for(Elf_Scn *scn = elf_nextscn(elf, NULL);
1094 				scn;
1095 				scn = elf_nextscn(elf, scn))
1096 			{
1097 				GElf_Shdr shdr;
1098 				memset(&shdr, 0x0, sizeof(GElf_Shdr));
1099 				gelf_getshdr(scn, &shdr);
1100 
1101 				if(shdr.sh_type == SHT_DYNAMIC)
1102 				{
1103 					// found a dynamic table
1104 					Elf_Data *data = elf_getdata(scn, NULL);
1105 					const unsigned count = shdr.sh_size / shdr.sh_entsize;
1106 
1107 					// iterate over linked shared library names
1108 					for(unsigned i = 0; i < count; i++)
1109 					{
1110 						GElf_Dyn dyn;
1111 						memset(&dyn, 0x0, sizeof(GElf_Dyn));
1112 						gelf_getdyn(data, i, &dyn);
1113 
1114 						if(dyn.d_tag == DT_NEEDED)
1115 						{
1116 							const char *name = elf_strptr(elf, shdr.sh_link, dyn.d_un.d_val);
1117 
1118 							bool whitelist_match = false;
1119 							bool blacklist_match = false;
1120 
1121 							for(unsigned j = 0; j < n_whitelist; j++)
1122 							{
1123 								if(!strncmp(name, whitelist[j], strlen(whitelist[j])))
1124 								{
1125 									whitelist_match = true;
1126 									break;
1127 								}
1128 							}
1129 
1130 							if(_white_match(app->whitelist_libs, uri, name))
1131 							{
1132 								whitelist_match = true;
1133 								break;
1134 							}
1135 
1136 							for(unsigned j = 0; j < n_blacklist; j++)
1137 							{
1138 								if(!strncmp(name, blacklist[j], strlen(blacklist[j])))
1139 								{
1140 									blacklist_match = true;
1141 									break;
1142 								}
1143 							}
1144 
1145 							if(n_whitelist && !whitelist_match)
1146 							{
1147 								_append_to(libraries, name);
1148 								invalid++;
1149 							}
1150 							if(n_blacklist && blacklist_match && !whitelist_match)
1151 							{
1152 								_append_to(libraries, name);
1153 								invalid++;
1154 							}
1155 						}
1156 						//FIXME
1157 					}
1158 
1159 					break;
1160 				}
1161 			}
1162 			elf_end(elf);
1163 		}
1164 		close(fd);
1165 	}
1166 
1167 	return !invalid;
1168 }
1169 #endif
1170 
1171 static void
_state_set_value(const char * symbol __unused,void * data __unused,const void * value __unused,uint32_t size __unused,uint32_t type __unused)1172 _state_set_value(const char *symbol __unused, void *data __unused,
1173 	const void *value __unused, uint32_t size __unused, uint32_t type __unused)
1174 {
1175 	//FIXME
1176 }
1177 
1178 static void
_append_include_dir(app_t * app,char * include_dir)1179 _append_include_dir(app_t *app, char *include_dir)
1180 {
1181 	char **include_dirs = realloc(app->include_dirs,
1182 		(app->n_include_dirs + 1) * sizeof(const char *));
1183 	if(!include_dirs)
1184 	{
1185 		return;
1186 	}
1187 
1188 	app->include_dirs = include_dirs;
1189 
1190 	size_t len = strlen(include_dir) + 1;
1191 
1192 	if(include_dir[len - 2] == '/')
1193 	{
1194 		char *dst = malloc(len);
1195 
1196 		if(dst)
1197 		{
1198 			app->include_dirs[app->n_include_dirs] = dst;
1199 			snprintf(app->include_dirs[app->n_include_dirs], len, "%s", include_dir);
1200 
1201 			app->n_include_dirs++;
1202 		}
1203 	}
1204 	else
1205 	{
1206 		len++;
1207 		char *dst = malloc(len);
1208 
1209 		if(dst)
1210 		{
1211 			app->include_dirs[app->n_include_dirs] = dst;
1212 			snprintf(app->include_dirs[app->n_include_dirs], len, "%s/", include_dir);
1213 
1214 			app->n_include_dirs++;
1215 		}
1216 	}
1217 }
1218 
1219 static void
_load_include_dirs(app_t * app)1220 _load_include_dirs(app_t *app)
1221 {
1222 	for(unsigned i = 0; i < app->n_include_dirs; i++)
1223 	{
1224 		char *include_dir = app->include_dirs ? app->include_dirs[i] : NULL;
1225 
1226 		if(!include_dir)
1227 		{
1228 			continue;
1229 		}
1230 
1231 		LilvNode *bundle_node = lilv_new_file_uri(app->world, NULL, include_dir);
1232 
1233 		if(bundle_node)
1234 		{
1235 			lilv_world_load_bundle(app->world, bundle_node);
1236 			lilv_world_load_resource(app->world, bundle_node);
1237 
1238 			lilv_node_free(bundle_node);
1239 		}
1240 	}
1241 }
1242 
1243 static void
_free_include_dirs(app_t * app)1244 _free_include_dirs(app_t *app)
1245 {
1246 	for(unsigned i = 0; i < app->n_include_dirs; i++)
1247 	{
1248 		char *include_dir = app->include_dirs ? app->include_dirs[i] : NULL;
1249 
1250 		if(!include_dir)
1251 		{
1252 			continue;
1253 		}
1254 
1255 		LilvNode *bundle_node = lilv_new_file_uri(app->world, NULL, include_dir);
1256 
1257 		if(bundle_node)
1258 		{
1259 			lilv_world_unload_resource(app->world, bundle_node);
1260 			lilv_world_unload_bundle(app->world, bundle_node);
1261 
1262 			lilv_node_free(bundle_node);
1263 		}
1264 
1265 		free(include_dir);
1266 	}
1267 
1268 	if(app->include_dirs)
1269 	{
1270 		free(app->include_dirs);
1271 	}
1272 }
1273 
1274 static void
_append_whitelist_test(app_t * app,const char * uri,const char * pattern)1275 _append_whitelist_test(app_t *app, const char *uri, const char *pattern)
1276 {
1277 	app->whitelist_tests = _white_append(app->whitelist_tests, uri, pattern);
1278 }
1279 
1280 static void
_free_whitelist_tests(app_t * app)1281 _free_whitelist_tests(app_t *app)
1282 {
1283 	app->whitelist_tests = _white_free(app->whitelist_tests);
1284 }
1285 
1286 bool
lv2lint_test_is_whitelisted(app_t * app,const char * uri,const test_t * test)1287 lv2lint_test_is_whitelisted(app_t *app, const char *uri, const test_t *test)
1288 {
1289 	return _white_match(app->whitelist_tests, uri, test->id);
1290 }
1291 
1292 #ifdef ENABLE_ELF_TESTS
1293 static void
_append_whitelist_symbol(app_t * app,const char * uri,char * pattern)1294 _append_whitelist_symbol(app_t *app, const char *uri, char *pattern)
1295 {
1296 	app->whitelist_symbols = _white_append(app->whitelist_symbols, uri, pattern);
1297 }
1298 
1299 static void
_free_whitelist_symbols(app_t * app)1300 _free_whitelist_symbols(app_t *app)
1301 {
1302 	app->whitelist_symbols = _white_free(app->whitelist_symbols);
1303 }
1304 
1305 static void
_append_whitelist_lib(app_t * app,const char * uri,char * pattern)1306 _append_whitelist_lib(app_t *app, const char *uri, char *pattern)
1307 {
1308 	app->whitelist_libs = _white_append(app->whitelist_libs, uri, pattern);
1309 }
1310 
1311 static void
_free_whitelist_libs(app_t * app)1312 _free_whitelist_libs(app_t *app)
1313 {
1314 	app->whitelist_libs = _white_free(app->whitelist_libs);
1315 }
1316 #endif
1317 
1318 int
main(int argc,char ** argv)1319 main(int argc, char **argv)
1320 {
1321 	static app_t app;
1322 	const char *uri = NULL;
1323 	app.atty = isatty(1);
1324 	app.show = LINT_FAIL | LINT_WARN; // always report failed and warned tests
1325 	app.mask = LINT_FAIL; // always fail at failed tests
1326 	app.pck = true;
1327 #ifdef ENABLE_ONLINE_TESTS
1328 	app.greet = "Dear LV2 plugin developer\n"
1329 		"\n"
1330 		"We would like to congratulate you for your efforts to have created this\n"
1331 		"awesome plugin for the LV2 ecosystem.\n"
1332 		"\n"
1333 		"However, we have found some minor issues where your plugin deviates from\n"
1334 		"the LV2 plugin specification and/or its best implementation practices.\n"
1335 		"By fixing those, you can make your plugin more conforming and thus likely\n"
1336 		"usable in more hosts and with less issues for your users.\n"
1337 		"\n"
1338 		"Kindly find below an automatically generated bug report with a summary\n"
1339 		"of potential issues.\n"
1340 		"\n"
1341 		"Yours sincerely\n"
1342 		"                                 /The unofficial LV2 inquisitorial squad/\n"
1343 		"\n"
1344 		"---\n\n";
1345 #endif
1346 
1347 	int c;
1348 	while( (c = getopt(argc, argv, "vhqdM:S:E:I:u:t:"
1349 #ifdef ENABLE_ONLINE_TESTS
1350 		"omg:"
1351 #endif
1352 #ifdef ENABLE_ELF_TESTS
1353 		"s:l:"
1354 #endif
1355 		) ) != -1)
1356 	{
1357 		switch(c)
1358 		{
1359 			case 'v':
1360 				_version(argv);
1361 				return 0;
1362 			case 'h':
1363 				_usage(argv);
1364 				return 0;
1365 			case 'q':
1366 				app.quiet = true;
1367 				break;
1368 			case 'd':
1369 				app.debug = true;
1370 				break;
1371 			case 'I':
1372 				_append_include_dir(&app, optarg);
1373 				break;
1374 			case 'u':
1375 				uri = optarg;
1376 				break;
1377 			case 't':
1378 				_append_whitelist_test(&app, uri, optarg);
1379 				break;
1380 #ifdef ENABLE_ELF_TESTS
1381 			case 's':
1382 				_append_whitelist_symbol(&app, uri, optarg);
1383 				break;
1384 			case 'l':
1385 				_append_whitelist_lib(&app, uri, optarg);
1386 				break;
1387 #endif
1388 #ifdef ENABLE_ONLINE_TESTS
1389 			case 'o':
1390 				app.online = true;
1391 				break;
1392 			case 'm':
1393 				app.mailto = true;
1394 				app.atty = false;
1395 				break;
1396 			case 'g':
1397 				app.greet = optarg;
1398 				break;
1399 #endif
1400 			case 'M':
1401 				if(!strcmp(optarg, "pack"))
1402 				{
1403 					app.pck = true;
1404 				}
1405 
1406 				else if(!strcmp(optarg, "nopack"))
1407 				{
1408 					app.pck = false;
1409 				}
1410 
1411 				break;
1412 			case 'S':
1413 				if(!strcmp(optarg, "warn"))
1414 				{
1415 					app.show |= LINT_WARN;
1416 				}
1417 				else if(!strcmp(optarg, "note"))
1418 				{
1419 					app.show |= LINT_NOTE;
1420 				}
1421 				else if(!strcmp(optarg, "pass"))
1422 				{
1423 					app.show |= LINT_PASS;
1424 				}
1425 				else if(!strcmp(optarg, "all"))
1426 				{
1427 					app.show |= (LINT_WARN | LINT_NOTE | LINT_PASS);
1428 				}
1429 
1430 				else if(!strcmp(optarg, "nowarn"))
1431 				{
1432 					app.show &= ~LINT_WARN;
1433 				}
1434 				else if(!strcmp(optarg, "nonote"))
1435 				{
1436 					app.show &= ~LINT_NOTE;
1437 				}
1438 				else if(!strcmp(optarg, "nopass"))
1439 				{
1440 					app.show &= ~LINT_PASS;
1441 				}
1442 				else if(!strcmp(optarg, "noall"))
1443 				{
1444 					app.show &= ~(LINT_WARN | LINT_NOTE | LINT_PASS);
1445 				}
1446 
1447 				break;
1448 			case 'E':
1449 				if(!strcmp(optarg, "warn"))
1450 				{
1451 					app.show |= LINT_WARN;
1452 					app.mask |= LINT_WARN;
1453 				}
1454 				else if(!strcmp(optarg, "note"))
1455 				{
1456 					app.show |= LINT_NOTE;
1457 					app.mask |= LINT_NOTE;
1458 				}
1459 				else if(!strcmp(optarg, "all"))
1460 				{
1461 					app.show |= (LINT_WARN | LINT_NOTE);
1462 					app.mask |= (LINT_WARN | LINT_NOTE);
1463 				}
1464 
1465 				else if(!strcmp(optarg, "nowarn"))
1466 				{
1467 					app.show &= ~LINT_WARN;
1468 					app.mask &= ~LINT_WARN;
1469 				}
1470 				else if(!strcmp(optarg, "nonote"))
1471 				{
1472 					app.show &= ~LINT_NOTE;
1473 					app.mask &= ~LINT_NOTE;
1474 				}
1475 				else if(!strcmp(optarg, "noall"))
1476 				{
1477 					app.show &= ~(LINT_WARN | LINT_NOTE);
1478 					app.mask &= ~(LINT_WARN | LINT_NOTE);
1479 				}
1480 
1481 				break;
1482 			case '?':
1483 #ifdef ENABLE_ONLINE_TESTS
1484 				if( (optopt == 'S') || (optopt == 'E') || (optopt == 'g') )
1485 #else
1486 				if( (optopt == 'S') || (optopt == 'E') )
1487 #endif
1488 					fprintf(stderr, "Option `-%c' requires an argument.\n", optopt);
1489 				else if(isprint(optopt))
1490 					fprintf(stderr, "Unknown option `-%c'.\n", optopt);
1491 				else
1492 					fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
1493 				return -1;
1494 			default:
1495 				return -1;
1496 		}
1497 	}
1498 
1499 	if(optind == argc) // no URI given
1500 	{
1501 		_usage(argv);
1502 		return -1;
1503 	}
1504 
1505 	if(!app.quiet)
1506 	{
1507 		_header(argv);
1508 	}
1509 
1510 #ifdef ENABLE_ONLINE_TESTS
1511 	app.curl = curl_easy_init();
1512 	if(!app.curl)
1513 		return -1;
1514 #endif
1515 
1516 	app.world = lilv_world_new();
1517 	if(!app.world)
1518 		return -1;
1519 
1520 	mapper_t *mapper = mapper_new(8192, STAT_URID_MAX, stat_uris, NULL, NULL, NULL);
1521 	if(!mapper)
1522 		return -1;
1523 
1524 	_map_uris(&app);
1525 	lilv_world_load_all(app.world);
1526 	_load_include_dirs(&app);
1527 
1528 	app.map = mapper_get_map(mapper);
1529 	app.unmap = mapper_get_unmap(mapper);
1530 	LV2_Worker_Schedule sched = {
1531 		.handle = &app,
1532 		.schedule_work = _sched
1533 	};
1534 	LV2_Log_Log log = {
1535 		.handle = &app,
1536 		.printf = log_printf,
1537 		.vprintf = log_vprintf
1538 	};
1539 	LV2_State_Make_Path mkpath = {
1540 		.handle = &app,
1541 		.path = _mkpath
1542 	};
1543 	LV2_State_Free_Path freepath = {
1544 		.handle = &app,
1545 		.free_path = _freepath
1546 	};
1547 	LV2_Resize_Port_Resize rsz = {
1548 		.data = &app,
1549 		.resize = _resize
1550 	};
1551 #pragma GCC diagnostic push
1552 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1553 	LV2_URI_Map_Feature urimap = {
1554 		.callback_data = app.map,
1555 		.uri_to_id = uri_to_id
1556 	};
1557 #pragma GCC diagnostic pop
1558 	LV2_Inline_Display queue_draw = {
1559 		.handle = &app,
1560 		.queue_draw = _queue_draw
1561 	};
1562 
1563 	const float param_sample_rate = 48000.f;
1564 	const float ui_update_rate = 25.f;
1565 	const int32_t bufsz_min_block_length = 256;
1566 	const int32_t bufsz_max_block_length = 256;
1567 	const int32_t bufsz_nominal_block_length = 256;
1568 	const int32_t bufsz_sequence_size = 2048;
1569 
1570 	const LV2_Options_Option opts_sampleRate = {
1571 		.key = PARAMETERS__sampleRate,
1572 		.size = sizeof(float),
1573 		.type = ATOM__Float,
1574 		.value = &param_sample_rate
1575 	};
1576 
1577 	const LV2_Options_Option opts_updateRate = {
1578 		.key = UI__updateRate,
1579 		.size = sizeof(float),
1580 		.type = ATOM__Float,
1581 		.value = &ui_update_rate
1582 	};
1583 
1584 	const LV2_Options_Option opts_minBlockLength = {
1585 		.key = BUF_SIZE__minBlockLength,
1586 		.size = sizeof(int32_t),
1587 		.type = ATOM__Int,
1588 		.value = &bufsz_min_block_length
1589 	};
1590 
1591 	const LV2_Options_Option opts_maxBlockLength = {
1592 		.key = BUF_SIZE__maxBlockLength,
1593 		.size = sizeof(int32_t),
1594 		.type = ATOM__Int,
1595 		.value = &bufsz_max_block_length
1596 	};
1597 
1598 	const LV2_Options_Option opts_nominalBlockLength = {
1599 		.key = BUF_SIZE__nominalBlockLength,
1600 		.size = sizeof(int32_t),
1601 		.type = ATOM__Int,
1602 		.value = &bufsz_nominal_block_length
1603 	};
1604 
1605 	const LV2_Options_Option opts_sequenceSize = {
1606 		.key = BUF_SIZE__sequenceSize,
1607 		.size = sizeof(int32_t),
1608 		.type = ATOM__Int,
1609 		.value = &bufsz_sequence_size
1610 	};
1611 
1612 	const LV2_Options_Option opts_sentinel = {
1613 		.key = 0,
1614 		.value =NULL
1615 	};
1616 
1617 #define MAX_OPTS  7
1618 	LV2_Options_Option opts [MAX_OPTS];
1619 
1620 	const LV2_Feature feat_map = {
1621 		.URI = LV2_URID__map,
1622 		.data = app.map
1623 	};
1624 	const LV2_Feature feat_unmap = {
1625 		.URI = LV2_URID__unmap,
1626 		.data = app.unmap
1627 	};
1628 	const LV2_Feature feat_sched = {
1629 		.URI = LV2_WORKER__schedule,
1630 		.data = &sched
1631 	};
1632 	const LV2_Feature feat_log = {
1633 		.URI = LV2_LOG__log,
1634 		.data = &log
1635 	};
1636 	const LV2_Feature feat_mkpath = {
1637 		.URI = LV2_STATE__makePath,
1638 		.data = &mkpath
1639 	};
1640 	const LV2_Feature feat_freepath = {
1641 		.URI = LV2_STATE__freePath,
1642 		.data = &freepath
1643 	};
1644 	const LV2_Feature feat_rsz = {
1645 		.URI = LV2_RESIZE_PORT__resize,
1646 		.data = &rsz
1647 	};
1648 	const LV2_Feature feat_opts = {
1649 		.URI = LV2_OPTIONS__options,
1650 		.data = opts
1651 	};
1652 	const LV2_Feature feat_urimap = {
1653 		.URI = LV2_URI_MAP_URI,
1654 		.data = &urimap
1655 	};
1656 
1657 	const LV2_Feature feat_islive = {
1658 		.URI = LV2_CORE__isLive
1659 	};
1660 	const LV2_Feature feat_inplacebroken = {
1661 		.URI = LV2_CORE__inPlaceBroken
1662 	};
1663 	const LV2_Feature feat_hardrtcapable = {
1664 		.URI = LV2_CORE__hardRTCapable
1665 	};
1666 	const LV2_Feature feat_supportsstrictbounds = {
1667 		.URI = LV2_PORT_PROPS__supportsStrictBounds
1668 	};
1669 	const LV2_Feature feat_boundedblocklength = {
1670 		.URI = LV2_BUF_SIZE__boundedBlockLength
1671 	};
1672 	const LV2_Feature feat_fixedblocklength = {
1673 		.URI = LV2_BUF_SIZE__fixedBlockLength
1674 	};
1675 	const LV2_Feature feat_powerof2blocklength = {
1676 		.URI = LV2_BUF_SIZE__powerOf2BlockLength
1677 	};
1678 	const LV2_Feature feat_coarseblocklength = {
1679 		.URI = LV2_BUF_SIZE_PREFIX"coarseBlockLength"
1680 	};
1681 	const LV2_Feature feat_loaddefaultstate = {
1682 		.URI = LV2_STATE__loadDefaultState
1683 	};
1684 	const LV2_Feature feat_threadsaferestore = {
1685 		.URI = LV2_STATE_PREFIX"threadSafeRestore"
1686 	};
1687 	const LV2_Feature feat_idispqueuedraw = {
1688 		.URI = LV2_INLINEDISPLAY__queue_draw,
1689 		.data = &queue_draw
1690 	};
1691 
1692 	int ret = 0;
1693 	const LilvPlugin *plugins = lilv_world_get_all_plugins(app.world);
1694 	if(plugins)
1695 	{
1696 		for(int i=optind; i<argc; i++)
1697 		{
1698 			app.plugin_uri = argv[i];
1699 			LilvNode *plugin_uri_node = lilv_new_uri(app.world, app.plugin_uri);
1700 			if(plugin_uri_node)
1701 			{
1702 				app.plugin = lilv_plugins_get_by_uri(plugins, plugin_uri_node);
1703 				if(app.plugin)
1704 				{
1705 #define MAX_FEATURES 21
1706 					const LV2_Feature *features [MAX_FEATURES];
1707 					bool requires_bounded_block_length = false;
1708 
1709 					// populate feature list
1710 					{
1711 						int f = 0;
1712 
1713 						LilvNodes *required_features = lilv_plugin_get_required_features(app.plugin);
1714 						if(required_features)
1715 						{
1716 							LILV_FOREACH(nodes, itr, required_features)
1717 							{
1718 								const LilvNode *feature = lilv_nodes_get(required_features, itr);
1719 								const LV2_URID feat = app.map->map(app.map->handle, lilv_node_as_uri(feature));
1720 
1721 								switch(feat)
1722 								{
1723 									case URID__map:
1724 									{
1725 										features[f++] = &feat_map;
1726 									}	break;
1727 									case URID__unmap:
1728 									{
1729 										features[f++] = &feat_unmap;
1730 									}	break;
1731 									case WORKER__schedule:
1732 									{
1733 										features[f++] = &feat_sched;
1734 									}	break;
1735 									case LOG__log:
1736 									{
1737 										features[f++] = &feat_log;
1738 									}	break;
1739 									case STATE__makePath:
1740 									{
1741 										features[f++] = &feat_mkpath;
1742 									}	break;
1743 									case STATE__freePath:
1744 									{
1745 										features[f++] = &feat_freepath;
1746 									}	break;
1747 									case UI__resize:
1748 									{
1749 										features[f++] = &feat_rsz;
1750 									}	break;
1751 									case OPTIONS__options:
1752 									{
1753 										features[f++] = &feat_opts;
1754 									}	break;
1755 									case URI_MAP:
1756 									{
1757 										features[f++] = &feat_urimap;
1758 									}	break;
1759 									case CORE__isLive:
1760 									{
1761 										features[f++] = &feat_islive;
1762 									}	break;
1763 									case CORE__inPlaceBroken:
1764 									{
1765 										features[f++] = &feat_inplacebroken;
1766 									}	break;
1767 									case CORE__hardRTCapable:
1768 									{
1769 										features[f++] = &feat_hardrtcapable;
1770 									}	break;
1771 									case PORT_PROPS__supportsStrictBounds:
1772 									{
1773 										features[f++] = &feat_supportsstrictbounds;
1774 									}	break;
1775 									case BUF_SIZE__boundedBlockLength:
1776 									{
1777 										features[f++] = &feat_boundedblocklength;
1778 										requires_bounded_block_length = true;
1779 									}	break;
1780 									case BUF_SIZE__fixedBlockLength:
1781 									{
1782 										features[f++] = &feat_fixedblocklength;
1783 									}	break;
1784 									case BUF_SIZE__powerOf2BlockLength:
1785 									{
1786 										features[f++] = &feat_powerof2blocklength;
1787 									}	break;
1788 									case BUF_SIZE__coarseBlockLength:
1789 									{
1790 										features[f++] = &feat_coarseblocklength;
1791 									}	break;
1792 									case STATE__loadDefaultState:
1793 									{
1794 										features[f++] = &feat_loaddefaultstate;
1795 									}	break;
1796 									case STATE__threadSafeRestore:
1797 									{
1798 										features[f++] = &feat_threadsaferestore;
1799 									}	break;
1800 									case INLINEDISPLAY__queue_draw:
1801 									{
1802 										features[f++] = &feat_idispqueuedraw;
1803 									}	break;
1804 								}
1805 							}
1806 							lilv_nodes_free(required_features);
1807 						}
1808 
1809 						features[f++] = NULL; // sentinel
1810 						assert(f <= MAX_FEATURES);
1811 					}
1812 
1813 					// populate required option list
1814 					{
1815 						unsigned n_opts = 0;
1816 						bool requires_min_block_length = false;
1817 						bool requires_max_block_length = false;
1818 
1819 						LilvNodes *required_options = lilv_plugin_get_value(app.plugin, NODE(&app, OPTIONS__requiredOption));
1820 						if(required_options)
1821 						{
1822 							LILV_FOREACH(nodes, itr, required_options)
1823 							{
1824 								const LilvNode *option = lilv_nodes_get(required_options, itr);
1825 								const LV2_URID opt = app.map->map(app.map->handle, lilv_node_as_uri(option));
1826 
1827 								switch(opt)
1828 								{
1829 									case PARAMETERS__sampleRate:
1830 									{
1831 										opts[n_opts++] = opts_sampleRate;
1832 									} break;
1833 									case BUF_SIZE__minBlockLength:
1834 									{
1835 										opts[n_opts++] = opts_minBlockLength;
1836 										requires_min_block_length = true;
1837 									} break;
1838 									case BUF_SIZE__maxBlockLength:
1839 									{
1840 										opts[n_opts++] = opts_maxBlockLength;
1841 										requires_max_block_length = true;
1842 									} break;
1843 									case BUF_SIZE__nominalBlockLength:
1844 									{
1845 										opts[n_opts++] = opts_nominalBlockLength;
1846 									} break;
1847 									case BUF_SIZE__sequenceSize:
1848 									{
1849 										opts[n_opts++] = opts_sequenceSize;
1850 									} break;
1851 									case UI__updateRate:
1852 									{
1853 										opts[n_opts++] = opts_updateRate;
1854 									} break;
1855 								}
1856 							}
1857 
1858 							lilv_nodes_free(required_options);
1859 						}
1860 
1861 						// handle bufsz:boundedBlockLength feature which activates options itself
1862 						if(requires_bounded_block_length)
1863 						{
1864 							if(!requires_min_block_length) // was not explicitely required
1865 								opts[n_opts++] = opts_minBlockLength;
1866 
1867 							if(!requires_max_block_length) // was not explicitely required
1868 								opts[n_opts++] = opts_maxBlockLength;
1869 						}
1870 
1871 						opts[n_opts++] = opts_sentinel; // sentinel
1872 						assert(n_opts <= MAX_OPTS);
1873 					}
1874 
1875 #ifdef ENABLE_ONLINE_TESTS
1876 					if(app.mailto)
1877 					{
1878 						app.mail = calloc(1, sizeof(char));
1879 					}
1880 #endif
1881 
1882 					lv2lint_printf(&app, "%s<%s>%s\n",
1883 						colors[app.atty][ANSI_COLOR_BOLD],
1884 						lilv_node_as_uri(lilv_plugin_get_uri(app.plugin)),
1885 						colors[app.atty][ANSI_COLOR_RESET]);
1886 
1887 					app.instance = lilv_plugin_instantiate(app.plugin, param_sample_rate, features);
1888 					app.descriptor = app.instance
1889 						? lilv_instance_get_descriptor(app.instance)
1890 						: NULL;
1891 
1892 					if(app.instance)
1893 					{
1894 						app.work_iface = lilv_instance_get_extension_data(app.instance, LV2_WORKER__interface);
1895 						app.idisp_iface = lilv_instance_get_extension_data(app.instance, LV2_INLINEDISPLAY__interface);
1896 						app.state_iface = lilv_instance_get_extension_data(app.instance, LV2_STATE__interface);
1897 						app.opts_iface = lilv_instance_get_extension_data(app.instance, LV2_OPTIONS__interface);
1898 
1899 						const bool has_load_default = lilv_plugin_has_feature(app.plugin,
1900 							NODE(&app, STATE__loadDefaultState));
1901 						if(has_load_default)
1902 						{
1903 							const LilvNode *pset = lilv_plugin_get_uri(app.plugin);
1904 
1905 							lilv_world_load_resource(app.world, pset);
1906 
1907 							LilvState *state = lilv_state_new_from_world(app.world, app.map, pset);
1908 							if(state)
1909 							{
1910 								lilv_state_restore(state, app.instance, _state_set_value, &app,
1911 									LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, NULL); //FIXME features
1912 
1913 								lilv_state_free(state);
1914 							}
1915 
1916 							lilv_world_unload_resource(app.world, pset);
1917 						}
1918 					}
1919 
1920 					if(!test_plugin(&app))
1921 					{
1922 #ifdef ENABLE_ONLINE_TESTS // only print mailto strings if errors were encountered
1923 						if(app.mailto && app.mail)
1924 						{
1925 							char *subj;
1926 							unsigned minor_version = 0;
1927 							unsigned micro_version = 0;
1928 
1929 							LilvNode *minor_version_nodes = lilv_plugin_get_value(app.plugin , NODE(&app, CORE__minorVersion));
1930 							if(minor_version_nodes)
1931 							{
1932 								const LilvNode *minor_version_node = lilv_nodes_get_first(minor_version_nodes);
1933 								if(minor_version_node && lilv_node_is_int(minor_version_node))
1934 								{
1935 									minor_version = lilv_node_as_int(minor_version_node);
1936 								}
1937 
1938 								lilv_nodes_free(minor_version_nodes);
1939 							}
1940 
1941 							LilvNode *micro_version_nodes = lilv_plugin_get_value(app.plugin , NODE(&app, CORE__microVersion));
1942 							if(micro_version_nodes)
1943 							{
1944 								const LilvNode *micro_version_node = lilv_nodes_get_first(micro_version_nodes);
1945 								if(micro_version_node && lilv_node_is_int(micro_version_node))
1946 								{
1947 									micro_version = lilv_node_as_int(micro_version_node);
1948 								}
1949 
1950 								lilv_nodes_free(micro_version_nodes);
1951 							}
1952 
1953 							if(asprintf(&subj, "[%s "LV2LINT_VERSION"] bug report for <%s> version %u.%u",
1954 								argv[0], app.plugin_uri, minor_version, micro_version) != -1)
1955 							{
1956 								char *subj_esc = curl_easy_escape(app.curl, subj, strlen(subj));
1957 								if(subj_esc)
1958 								{
1959 									char *greet_esc = curl_easy_escape(app.curl, app.greet, strlen(app.greet));
1960 									if(greet_esc)
1961 									{
1962 										char *body_esc = curl_easy_escape(app.curl, app.mail, strlen(app.mail));
1963 										if(body_esc)
1964 										{
1965 											LilvNode *email_node = lilv_plugin_get_author_email(app.plugin);
1966 											const char *email = email_node && lilv_node_is_uri(email_node)
1967 												? lilv_node_as_uri(email_node)
1968 												: "mailto:unknown@example.com";
1969 
1970 											fprintf(stdout, "%s?subject=%s&body=%s%s\n",
1971 												email, subj_esc, greet_esc, body_esc);
1972 
1973 											if(email_node)
1974 											{
1975 												lilv_node_free(email_node);
1976 											}
1977 
1978 											curl_free(body_esc);
1979 										}
1980 
1981 										curl_free(greet_esc);
1982 									}
1983 
1984 									curl_free(subj_esc);
1985 								}
1986 
1987 								free(subj);
1988 							}
1989 						}
1990 #endif
1991 
1992 						ret += 1;
1993 					}
1994 
1995 #ifdef ENABLE_ONLINE_TESTS
1996 					if(app.mail)
1997 					{
1998 						free(app.mail);
1999 						app.mail = NULL;
2000 					}
2001 #endif
2002 
2003 					if(app.instance)
2004 					{
2005 						lilv_instance_free(app.instance);
2006 						app.instance = NULL;
2007 						app.descriptor = NULL;
2008 						app.work_iface = NULL;
2009 						app.idisp_iface = NULL;
2010 						app.state_iface= NULL;
2011 						app.opts_iface = NULL;
2012 					}
2013 
2014 					app.plugin = NULL;
2015 
2016 				}
2017 				else
2018 				{
2019 					ret += 1;
2020 				}
2021 			}
2022 			else
2023 			{
2024 				ret += 1;
2025 			}
2026 			lilv_node_free(plugin_uri_node);
2027 		}
2028 	}
2029 	else
2030 	{
2031 		ret = -1;
2032 	}
2033 
2034 	_unmap_uris(&app);
2035 	_free_urids(&app);
2036 	_free_include_dirs(&app);
2037 	_free_whitelist_tests(&app);
2038 #ifdef ENABLE_ELF_TESTS
2039 	_free_whitelist_symbols(&app);
2040 	_free_whitelist_libs(&app);
2041 #endif
2042 	mapper_free(mapper);
2043 
2044 	lilv_world_free(app.world);
2045 
2046 #ifdef ENABLE_ONLINE_TESTS
2047 	curl_easy_cleanup(app.curl);
2048 #endif
2049 
2050 	return ret;
2051 }
2052 
2053 int
lv2lint_vprintf(app_t * app,const char * fmt,va_list args)2054 lv2lint_vprintf(app_t *app, const char *fmt, va_list args)
2055 {
2056 #ifdef ENABLE_ONLINE_TESTS
2057 	if(app->mailto)
2058 	{
2059 		char *buf = NULL;
2060 		int len;
2061 
2062 		if( (len = vasprintf(&buf, fmt, args)) != -1)
2063 		{
2064 			len += strlen(app->mail);
2065 			app->mail = realloc(app->mail, len + 1);
2066 
2067 			if(app->mail)
2068 			{
2069 				app->mail = strncat(app->mail, buf, len + 1);
2070 			}
2071 
2072 			free(buf);
2073 		}
2074 	}
2075 	else
2076 #else
2077 	(void)app;
2078 #endif
2079 	{
2080 		vfprintf(stdout, fmt, args);
2081 	}
2082 
2083 	return 0;
2084 }
2085 
2086 int
lv2lint_printf(app_t * app,const char * fmt,...)2087 lv2lint_printf(app_t *app, const char *fmt, ...)
2088 {
2089 	va_list args;
2090 
2091 	va_start(args, fmt);
2092 
2093 	const int ret = lv2lint_vprintf(app, fmt, args);
2094 
2095 	va_end(args);
2096 
2097 	return ret;
2098 }
2099 
2100 static void
_escape_markup(char * docu)2101 _escape_markup(char *docu)
2102 {
2103 	char *wrp = docu;
2104 	bool tag = false;
2105 	bool amp = false;
2106 	bool sep = false;
2107 
2108 	for(const char *rdp = docu; *rdp != '\0'; rdp++)
2109 	{
2110 		switch(*rdp)
2111 		{
2112 			case '<':
2113 			{
2114 				tag = true;
2115 			} continue;
2116 			case '>':
2117 			{
2118 				if(tag)
2119 				{
2120 					tag = false;
2121 					continue;
2122 				}
2123 			} break;
2124 
2125 			case '&':
2126 			{
2127 				amp = true;
2128 			} continue;
2129 			case ';':
2130 			{
2131 				if(amp)
2132 				{
2133 					amp = false;
2134 					continue;
2135 				}
2136 			} break;
2137 
2138 			case ' ':
2139 			{
2140 				if(sep) // escape double spaces
2141 				{
2142 					continue;
2143 				}
2144 
2145 				sep = true;
2146 			} break;
2147 
2148 			default:
2149 			{
2150 				sep = false;
2151 			} break;
2152 		}
2153 
2154 		if(tag || amp)
2155 		{
2156 			continue;
2157 		}
2158 
2159 		*wrp++ = *rdp;
2160 	}
2161 
2162 	*wrp = '\0';
2163 }
2164 
2165 static void
_report_head(app_t * app,const char * label,ansi_color_t col,const test_t * test)2166 _report_head(app_t *app, const char *label, ansi_color_t col, const test_t *test)
2167 {
2168 	lv2lint_printf(app, "    [%s%s%s]  %s\n",
2169 		colors[app->atty][col], label, colors[app->atty][ANSI_COLOR_RESET], test->id);
2170 }
2171 
2172 static void
_report_body(app_t * app,const char * label,ansi_color_t col,const test_t * test,const ret_t * ret,const char * repl,char * docu)2173 _report_body(app_t *app, const char *label, ansi_color_t col, const test_t *test,
2174 	const ret_t *ret, const char *repl, char *docu)
2175 {
2176 	_report_head(app, label, col, test);
2177 
2178 	lv2lint_printf(app, "              %s\n", repl ? repl : ret->msg);
2179 
2180 	if(docu)
2181 	{
2182 		_escape_markup(docu);
2183 
2184 		const char *sep = "\n";
2185 		for(char *docup = docu, *ptr = strsep(&docup, sep);
2186 			ptr;
2187 			ptr = strsep(&docup, sep) )
2188 		{
2189 			lv2lint_printf(app, "                %s\n", ptr);
2190 		}
2191 	}
2192 
2193 	lv2lint_printf(app, "              seeAlso: <%s>\n", ret->uri);
2194 }
2195 
2196 void
lv2lint_report(app_t * app,const test_t * test,res_t * res,bool show_passes,bool * flag)2197 lv2lint_report(app_t *app, const test_t *test, res_t *res, bool show_passes, bool *flag)
2198 {
2199 	const ret_t *ret = res->ret;
2200 
2201 	if(ret)
2202 	{
2203 		char *repl = NULL;
2204 
2205 		if(res->urn)
2206 		{
2207 			if(strstr(ret->msg, "%s"))
2208 			{
2209 				if(asprintf(&repl, ret->msg, res->urn) == -1)
2210 					repl = NULL;
2211 			}
2212 		}
2213 
2214 		char *docu = NULL;
2215 
2216 		if(app->debug)
2217 		{
2218 			if(ret->dsc)
2219 			{
2220 				docu = lv2lint_strdup(ret->dsc);
2221 			}
2222 			else
2223 			{
2224 				LilvNode *subj_node = ret->uri ? lilv_new_uri(app->world, ret->uri) : NULL;
2225 				if(subj_node)
2226 				{
2227 					LilvNode *docu_node = lilv_world_get(app->world, subj_node, NODE(app, CORE__documentation), NULL);
2228 					if(docu_node)
2229 					{
2230 						docu = lv2lint_node_as_string_strdup(docu_node);
2231 
2232 						lilv_node_free(docu_node);
2233 					}
2234 
2235 					lilv_node_free(subj_node);
2236 				}
2237 			}
2238 		}
2239 
2240 		const lint_t lnt = lv2lint_extract(app, ret);
2241 
2242 		if(res->is_whitelisted)
2243 		{
2244 			_report_body(app, "SKIP", ANSI_COLOR_GREEN, test, ret, repl, docu);
2245 		}
2246 		else
2247 		{
2248 			switch(lnt & app->show)
2249 			{
2250 				case LINT_FAIL:
2251 					_report_body(app, "FAIL", ANSI_COLOR_RED, test, ret, repl, docu);
2252 					break;
2253 				case LINT_WARN:
2254 					_report_body(app, "WARN", ANSI_COLOR_YELLOW, test, ret, repl, docu);
2255 					break;
2256 				case LINT_NOTE:
2257 					_report_body(app, "NOTE", ANSI_COLOR_CYAN, test, ret, repl, docu);
2258 					break;
2259 			}
2260 		}
2261 
2262 		if(docu)
2263 		{
2264 			free(docu);
2265 		}
2266 
2267 		if(repl)
2268 		{
2269 			free(repl);
2270 		}
2271 
2272 		if(res->is_whitelisted)
2273 		{
2274 			return; // short-circuit here
2275 		}
2276 
2277 		if(flag && *flag)
2278 		{
2279 			*flag = (lnt & app->mask) ? false : true;
2280 		}
2281 	}
2282 	else if(show_passes)
2283 	{
2284 		_report_head(app, "PASS", ANSI_COLOR_GREEN, test);
2285 	}
2286 }
2287 
2288 lint_t
lv2lint_extract(app_t * app,const ret_t * ret)2289 lv2lint_extract(app_t *app, const ret_t *ret)
2290 {
2291 	if(!ret)
2292 	{
2293 		return LINT_NONE;
2294 	}
2295 
2296 	return app->pck && (ret->pck != LINT_NONE)
2297 		? ret->pck
2298 		: ret->lnt;
2299 }
2300 
2301 char *
lv2lint_node_as_string_strdup(const LilvNode * node)2302 lv2lint_node_as_string_strdup(const LilvNode *node)
2303 {
2304 	if(!node)
2305 	{
2306 		return NULL;
2307 	}
2308 
2309 	if(!lilv_node_is_string(node))
2310 	{
2311 		return NULL;
2312 	}
2313 
2314 	const char *str = lilv_node_as_string(node);
2315 
2316 	if (!str)
2317 	{
2318 		return NULL;
2319 	}
2320 
2321 	return lv2lint_strdup(str);
2322 }
2323 
2324 char *
lv2lint_node_as_uri_strdup(const LilvNode * node)2325 lv2lint_node_as_uri_strdup(const LilvNode *node)
2326 {
2327 	if(!node)
2328 	{
2329 		return NULL;
2330 	}
2331 
2332 	if(!lilv_node_is_uri(node))
2333 	{
2334 		return NULL;
2335 	}
2336 
2337 	const char *uri = lilv_node_as_uri(node);
2338 
2339 	return lv2lint_strdup(uri);
2340 }
2341 
2342 char *
lv2lint_strdup(const char * str)2343 lv2lint_strdup(const char *str)
2344 {
2345 	static const char *empty = "";
2346 
2347 	if(!str)
2348 	{
2349 		str = empty;
2350 	}
2351 
2352 	return strdup(str);
2353 }
2354