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 = ¶m_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