]> sigrok.org Git - libsigrok.git/blob - src/output/csv.c
csv: Fix a segfault when using non-hardware input.
[libsigrok.git] / src / output / csv.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2011 Uwe Hermann <uwe@hermann-uwe.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20
21 /*
22  * Options and their values:
23  *
24  * gnuplot: Write out a gnuplot interpreter script (.gpi file) to plot
25  *          the datafile using the parameters given. It should be called
26  *          from a gnuplot session with the data file name as a parameter
27  *          after adjusting line styles, terminal, etc.
28  *
29  * scale:   The gnuplot graphs are scaled so they all have the same
30  *          peak-to-peak distance. Defaults to TRUE.
31  *
32  * value:   The string used to separate values in a record. Defaults to ','.
33  *
34  * record:  The string to use to separate records. Default is newline. gnuplot
35  *          files must use newline.
36  *
37  * frame:   The string to use when a frame ends. The default is a blank line.
38  *          This may confuse some CSV parsers, but it makes gnuplot happy.
39  *
40  * comment: The string that starts a comment line. Defaults to ';'.
41  *
42  * header:  Print header comment with capture metadata. Defaults to TRUE.
43  *
44  * label:   Add a line of channel labels as the first line of output. Defaults
45  *          to TRUE.
46  *
47  * time:    Whether or not the first column should include the time the sample
48  *          was taken. Defaults to TRUE.
49  *
50  * trigger: Whether or not to add a "trigger" column as the last column.
51  *          Defaults to FALSE.
52  *
53  * dedup:   Don't output duplicate rows. Defaults to TRUE. If time is off, then
54  *          this is forced to be off.
55  */
56
57 #include <math.h>
58 #include <config.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <glib.h>
62 #include <libsigrok/libsigrok.h>
63 #include "libsigrok-internal.h"
64
65 #define LOG_PREFIX "output/csv"
66
67 struct ctx_channel {
68         struct sr_channel *ch;
69         float min, max;
70 };
71
72 struct context {
73         /* Options */
74         const char *gnuplot;
75         gboolean scale;
76         const char *value;
77         const char *record;
78         const char *frame;
79         const char *comment;
80         gboolean header, did_header;
81         gboolean label, did_label;
82         gboolean time;
83         gboolean do_trigger;
84         gboolean dedup;
85
86         /* Plot data */
87         unsigned int num_analog_channels;
88         unsigned int num_logic_channels;
89         struct ctx_channel *channels;
90
91         /* Metadata */
92         gboolean trigger;
93         uint32_t num_samples;
94         uint32_t channel_count, logic_channel_count;
95         uint32_t channels_seen;
96         uint64_t period;
97         uint64_t sample_time;
98         uint8_t *previous_sample;
99         float *analog_samples;
100         uint8_t *logic_samples;
101         const char *xlabel;     /* Don't free: will point to a static string. */
102         const char *title;      /* Don't free: will point into the driver struct. */
103 };
104
105 /*
106  * TODO:
107  *  - Option to print comma-separated bits, or whole bytes/words (for 8/16
108  *    channel LAs) as ASCII/hex etc. etc.
109  */
110
111 static int init(struct sr_output *o, GHashTable *options)
112 {
113         unsigned int i, analog_channels, logic_channels;
114         struct context *ctx;
115         struct sr_channel *ch;
116         GSList *l;
117
118         if (!o || !o->sdi)
119                 return SR_ERR_ARG;
120
121         ctx = g_malloc0(sizeof(struct context));
122         o->priv = ctx;
123
124         /* Options */
125         ctx->gnuplot = g_strdup(g_variant_get_string(
126                 g_hash_table_lookup(options, "gnuplot"), NULL));
127         ctx->scale = g_variant_get_boolean(g_hash_table_lookup(options, "scale"));
128         ctx->value = g_strdup(g_variant_get_string(
129                 g_hash_table_lookup(options, "value"), NULL));
130         ctx->record = g_strdup(g_variant_get_string(
131                 g_hash_table_lookup(options, "record"), NULL));
132         ctx->frame = g_strdup(g_variant_get_string(
133                 g_hash_table_lookup(options, "frame"), NULL));
134         ctx->comment = g_strdup(g_variant_get_string(
135                 g_hash_table_lookup(options, "comment"), NULL));
136         ctx->header = g_variant_get_boolean(g_hash_table_lookup(options, "header"));
137         ctx->time = g_variant_get_boolean(g_hash_table_lookup(options, "time"));
138         ctx->do_trigger = g_variant_get_boolean(g_hash_table_lookup(options, "trigger"));
139         ctx->label = g_variant_get_boolean(g_hash_table_lookup(options, "label"));
140         ctx->dedup = g_variant_get_boolean(g_hash_table_lookup(options, "dedup"));
141         ctx->dedup &= ctx->time;
142
143         if (*ctx->gnuplot && g_strcmp0(ctx->record, "\n"))
144                 sr_warn("gnuplot record separator must be newline.");
145
146         if (*ctx->gnuplot && strlen(ctx->value) > 1)
147                 sr_warn("gnuplot doesn't support multichar value separators.");
148
149         sr_dbg("gnuplot = '%s', scale = %d", ctx->gnuplot, ctx->scale);
150         sr_dbg("value = '%s', record = '%s', frame = '%s', comment = '%s'",
151                ctx->value, ctx->record, ctx->frame, ctx->comment);
152         sr_dbg("header = %d, label = %d, time = %d, do_trigger = %d, dedup = %d",
153                ctx->header, ctx->label, ctx->time, ctx->do_trigger, ctx->dedup);
154
155         analog_channels = logic_channels = 0;
156         /* Get the number of channels, and the unitsize. */
157         for (l = o->sdi->channels; l; l = l->next) {
158                 ch = l->data;
159                 if (ch->type == SR_CHANNEL_LOGIC) {
160                         ctx->logic_channel_count++;
161                         if (ch->enabled)
162                                 logic_channels++;
163                 }
164                 if (ch->type == SR_CHANNEL_ANALOG && ch->enabled)
165                         analog_channels++;
166         }
167         if (analog_channels) {
168                 sr_info("Outputting %d analog values", analog_channels);
169                 ctx->num_analog_channels = analog_channels;
170         }
171         if (logic_channels) {
172                 sr_info("Outputting %d logic values", logic_channels);
173                 ctx->num_logic_channels = logic_channels;
174         }
175         ctx->channels = g_malloc(sizeof(struct ctx_channel)
176                 * (ctx->num_analog_channels + ctx->num_logic_channels));
177
178         /* Once more to map the enabled channels. */
179         ctx->channel_count = g_slist_length(o->sdi->channels);
180         for (i = 0, l = o->sdi->channels; l; l = l->next) {
181                 ch = l->data;
182                 if (ch->enabled) {
183                         if (ch->type == SR_CHANNEL_ANALOG) {
184                                 ctx->channels[i].min = FLT_MAX;
185                                 ctx->channels[i].max = FLT_MIN;
186                         } else if (ch->type == SR_CHANNEL_LOGIC) {
187                                 ctx->channels[i].min = 0;
188                                 ctx->channels[i].max = 1;
189                         } else {
190                                 sr_warn("Unknown channel type %d.", ch->type);
191                         }
192                         ctx->channels[i++].ch = ch;
193                 }
194         }
195
196         return SR_OK;
197 }
198
199 static const char *xlabels[] = {
200         "samples", "milliseconds", "microseconds", "nanoseconds", "picoseconds",
201         "femtoseconds", "attoseconds",
202 };
203
204 static GString *gen_header(const struct sr_output *o,
205                            const struct sr_datafeed_header *hdr)
206 {
207         struct context *ctx;
208         struct sr_channel *ch;
209         GVariant *gvar;
210         GString *header;
211         GSList *l;
212         unsigned int num_channels, i;
213         uint64_t samplerate = 0, sr;
214         char *samplerate_s;
215
216         ctx = o->priv;
217         header = g_string_sized_new(512);
218
219         if (ctx->period == 0) {
220                 if (sr_config_get(o->sdi->driver, o->sdi, NULL,
221                                   SR_CONF_SAMPLERATE, &gvar) == SR_OK) {
222                         samplerate = g_variant_get_uint64(gvar);
223                         g_variant_unref(gvar);
224                 }
225
226                 i = 0;
227                 sr = 1;
228                 while (sr < samplerate) {
229                         i++;
230                         sr *= 1000;
231                 }
232                 if (samplerate)
233                         ctx->period = sr / samplerate;
234                 if (i < ARRAY_SIZE(xlabels))
235                         ctx->xlabel = xlabels[i];
236                 sr_info("Set sample period to %" PRIu64 " %s",
237                         ctx->period, ctx->xlabel);
238         }
239         ctx->title = (o->sdi && o->sdi->driver) ? o->sdi->driver->longname : "unknown";
240
241         /* Some metadata */
242         if (ctx->header && !ctx->did_header) {
243                 /* save_gnuplot knows how many lines we print. */
244                 g_string_append_printf(header,
245                         "%s CSV generated by %s %s\n%s from %s on %s",
246                         ctx->comment, PACKAGE_NAME,
247                         SR_PACKAGE_VERSION_STRING, ctx->comment,
248                         ctx->title, ctime(&hdr->starttime.tv_sec));
249
250                 /* Columns / channels */
251                 num_channels = g_slist_length(o->sdi->channels);
252                 g_string_append_printf(header, "%s Channels (%d/%d):",
253                         ctx->comment, ctx->num_analog_channels +
254                         ctx->num_logic_channels, num_channels);
255                 for (i = 0, l = o->sdi->channels; l; l = l->next, i++) {
256                         ch = l->data;
257                         if (ch->enabled)
258                                 g_string_append_printf(header, " %s,", ch->name);
259                 }
260                 if (o->sdi->channels)
261                         /* Drop last separator. */
262                         g_string_truncate(header, header->len - 1);
263                 g_string_append_printf(header, "\n");
264                 if (samplerate != 0) {
265                         samplerate_s = sr_samplerate_string(samplerate);
266                         g_string_append_printf(header, "%s Samplerate: %s\n",
267                                                ctx->comment, samplerate_s);
268                         g_free(samplerate_s);
269                 }
270                 ctx->did_header = TRUE;
271         }
272
273         return header;
274 }
275
276 /*
277  * Analog devices can have samples of different types. Since each
278  * packet has only one meaning, it is restricted to having at most one
279  * type of data. So they can send multiple packets for a single sample.
280  * To further complicate things, they can send multiple samples in a
281  * single packet.
282  *
283  * So we need to pull any channels of interest out of a packet and save
284  * them until we have complete samples to output. Some devices make this
285  * simple by sending DF_FRAME_BEGIN/DF_FRAME_END packets, the latter of which
286  * signals the end of a set of samples, so we can dump things there.
287  *
288  * At least one driver (the demo driver) sends packets that contain parts of
289  * multiple samples without wrapping them in DF_FRAME. Possibly this driver
290  * is buggy, but it's also the standard for testing, so it has to be supported
291  * as is.
292  *
293  * Many assumptions about the "shape" of the data here:
294  *
295  * All of the data for a channel is assumed to be in one frame;
296  * otherwise the data in the second packet will overwrite the data in
297  * the first packet.
298  */
299 static void process_analog(struct context *ctx,
300                            const struct sr_datafeed_analog *analog)
301 {
302         int ret;
303         unsigned int i, j, c, num_channels;
304         struct sr_analog_meaning *meaning;
305         GSList *l;
306         float *fdata = NULL;
307
308         if (!ctx->analog_samples) {
309                 ctx->analog_samples = g_malloc(analog->num_samples
310                         * sizeof(float) * ctx->num_analog_channels);
311                 if (!ctx->num_samples)
312                         ctx->num_samples = analog->num_samples;
313         }
314         if (ctx->num_samples != analog->num_samples)
315                 sr_warn("Expecting %u analog samples, got %u.",
316                         ctx->num_samples, analog->num_samples);
317
318         meaning = analog->meaning;
319         num_channels = g_slist_length(meaning->channels);
320         ctx->channels_seen += num_channels;
321         sr_dbg("Processing packet of %u analog channels", num_channels);
322         fdata = g_malloc(analog->num_samples * num_channels);
323         if ((ret = sr_analog_to_float(analog, fdata)) != SR_OK)
324                 sr_warn("Problems converting data to floating point values.");
325
326         for (i = 0; i < ctx->num_analog_channels + ctx->num_logic_channels; i++) {
327                 if (ctx->channels[i].ch->type == SR_CHANNEL_ANALOG) {
328                         sr_dbg("Looking for channel %s",
329                                ctx->channels[i].ch->name);
330                         for (l = meaning->channels, c = 0; l; l = l->next, c++) {
331                                 struct sr_channel *ch = l->data;
332                                 sr_dbg("Checking %s", ch->name);
333                                 if (ctx->channels[i].ch == l->data) {
334                                         for (j = 0; j < analog->num_samples; j++)
335                                                 ctx->analog_samples[j * ctx->num_analog_channels + i] = fdata[j * num_channels + c];
336                                         break;
337                                 }
338                         }
339                 }
340         }
341         g_free(fdata);
342 }
343
344 /*
345  * We treat logic packets the same as analog packets, though it's not
346  * strictly required. This allows us to process mixed signals properly.
347  */
348 static void process_logic(struct context *ctx,
349                           const struct sr_datafeed_logic *logic)
350 {
351         unsigned int i, j, ch, num_samples;
352         int idx;
353         uint8_t *sample;
354
355         num_samples = logic->length / logic->unitsize;
356         ctx->channels_seen += ctx->logic_channel_count;
357         sr_dbg("Logic packet had %d channels", logic->unitsize * 8);
358         if (!ctx->logic_samples) {
359                 ctx->logic_samples = g_malloc(num_samples * ctx->num_logic_channels);
360                 if (!ctx->num_samples)
361                         ctx->num_samples = num_samples;
362         }
363         if (ctx->num_samples != num_samples)
364                 sr_warn("Expecting %u samples, got %u",
365                         ctx->num_samples, num_samples);
366
367         for (j = ch = 0; ch < ctx->num_logic_channels; j++) {
368                 if (ctx->channels[j].ch->type == SR_CHANNEL_LOGIC) {
369                         for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) {
370                                 sample = logic->data + i;
371                                 idx = ctx->channels[ch].ch->index;
372                                 ctx->logic_samples[i * ctx->num_logic_channels + ch] = sample[idx / 8] & (1 << (idx % 8));
373                         }
374                         ch++;
375                 }
376         }
377 }
378
379 static void dump_saved_values(struct context *ctx, GString **out)
380 {
381         unsigned int i, j, analog_size, num_channels;
382         float *analog_sample, value;
383         uint8_t *logic_sample;
384
385         /* If we haven't seen samples we're expecting, skip them. */
386         if ((ctx->num_analog_channels && !ctx->analog_samples) ||
387             (ctx->num_logic_channels && !ctx->logic_samples)) {
388                 sr_warn("Discarding partial packet");
389         } else {
390                 sr_info("Dumping %u samples", ctx->num_samples);
391
392                 *out = g_string_sized_new(512);
393                 num_channels =
394                     ctx->num_logic_channels + ctx->num_analog_channels;
395
396                 if (ctx->label && !ctx->did_label) {
397                         if (ctx->time)
398                                 g_string_append_printf(*out, "Time%s",
399                                                        ctx->value);
400                         for (i = 0; i < num_channels; i++) {
401                                 g_string_append_printf(*out, "%s%s",
402                                         ctx->channels[i].ch->name, ctx->value);
403                         }
404                         if (ctx->do_trigger)
405                                 g_string_append_printf(*out, "Trigger%s",
406                                                        ctx->value);
407                         /* Drop last separator. */
408                         g_string_truncate(*out, (*out)->len - 1);
409                         g_string_append(*out, ctx->record);
410
411                         ctx->did_label = TRUE;
412                 }
413
414                 analog_size = ctx->num_analog_channels * sizeof(float);
415                 if (ctx->dedup && !ctx->previous_sample)
416                         ctx->previous_sample = g_malloc0(analog_size + ctx->num_logic_channels);
417
418                 for (i = 0; i < ctx->num_samples; i++) {
419                         ctx->sample_time += ctx->period;
420                         analog_sample =
421                             &ctx->analog_samples[i * ctx->num_analog_channels];
422                         logic_sample =
423                             &ctx->logic_samples[i * ctx->num_logic_channels];
424
425                         if (ctx->dedup) {
426                                 if (i > 0 && i < ctx->num_samples - 1 &&
427                                     !memcmp(logic_sample, ctx->previous_sample,
428                                             ctx->num_logic_channels) &&
429                                     !memcmp(analog_sample,
430                                             ctx->previous_sample +
431                                             ctx->num_logic_channels,
432                                             analog_size))
433                                         continue;
434                                 memcpy(ctx->previous_sample, logic_sample,
435                                        ctx->num_logic_channels);
436                                 memcpy(ctx->previous_sample
437                                        + ctx->num_logic_channels,
438                                        analog_sample, analog_size);
439                         }
440
441                         if (ctx->time)
442                                 g_string_append_printf(*out, "%lu%s",
443                                         ctx->sample_time, ctx->value);
444
445                         for (j = 0; j < num_channels; j++) {
446                                 if (ctx->channels[j].ch->type == SR_CHANNEL_ANALOG) {
447                                         value = ctx->analog_samples[i * ctx->num_analog_channels + j];
448                                         ctx->channels[j].max =
449                                             fmax(value, ctx->channels[j].max);
450                                         ctx->channels[j].min =
451                                             fmin(value, ctx->channels[j].min);
452                                         g_string_append_printf(*out, "%g%s",
453                                                 value, ctx->value);
454                                 } else if (ctx->channels[j].ch->type == SR_CHANNEL_LOGIC) {
455                                         g_string_append_printf(*out, "%c%s",
456                                                                ctx->logic_samples[i * ctx->num_logic_channels + j] ? '1' : '0', ctx->value);
457                                 } else {
458                                         sr_warn("Unexpected channel type: %d",
459                                                 ctx->channels[i].ch->type);
460                                 }
461                         }
462
463                         if (ctx->do_trigger) {
464                                 g_string_append_printf(*out, "%d%s",
465                                         ctx->trigger, ctx->value);
466                                 ctx->trigger = FALSE;
467                         }
468                         g_string_truncate(*out, (*out)->len - 1);
469                         g_string_append(*out, ctx->record);
470                 }
471         }
472
473         /* Discard all of the working space. */
474         g_free(ctx->previous_sample);
475         g_free(ctx->analog_samples);
476         g_free(ctx->logic_samples);
477         ctx->channels_seen = 0;
478         ctx->num_samples = 0;
479         ctx->previous_sample = NULL;
480         ctx->analog_samples = NULL;
481         ctx->logic_samples = NULL;
482 }
483
484 static void save_gnuplot(struct context *ctx)
485 {
486         float offset, max, sum;
487         unsigned int i, num_channels;
488         GString *script;
489
490         script = g_string_sized_new(512);
491         g_string_append_printf(script, "set datafile separator '%s'\n",
492                                ctx->value);
493         if (ctx->did_label)
494                 g_string_append(script, "set key autotitle columnhead\n");
495         if (ctx->xlabel && ctx->time)
496                 g_string_append_printf(script, "set xlabel '%s'\n",
497                                        ctx->xlabel);
498
499         g_string_append(script, "plot ");
500
501         num_channels = ctx->num_analog_channels + ctx->num_logic_channels;
502
503         /* Graph position and scaling. */
504         max = FLT_MIN;
505         sum = 0;
506         for (i = 0; i < num_channels; i++) {
507                 ctx->channels[i].max =
508                     ctx->channels[i].max - ctx->channels[i].min;
509                 max = fmax(max, ctx->channels[i].max);
510                 sum += ctx->channels[i].max;
511         }
512         sum = (ctx->scale ? max : sum / num_channels) / 4;
513         offset = sum;
514         for (i = num_channels; i > 0;) {
515                 i--;
516                 ctx->channels[i].min = offset - ctx->channels[i].min;
517                 offset += sum + (ctx->scale ? max : ctx->channels[i].max);
518         }
519
520         for (i = 0; i < num_channels; i++) {
521                 sr_spew("Channel %d, min %g, max %g", i, ctx->channels[i].min,
522                         ctx->channels[i].max);
523                 g_string_append(script, "ARG1 ");
524                 if (ctx->did_header)
525                         g_string_append(script, "skip 4 ");
526                 g_string_append_printf(script, "using %u:($%u * %g + %g), ",
527                         ctx->time, i + 1 + ctx->time, ctx->scale ?
528                         max / ctx->channels[i].max : 1, ctx->channels[i].min);
529                 offset += 1.1 * (ctx->channels[i].max - ctx->channels[i].min);
530         }
531         g_string_truncate(script, script->len - 2);
532         g_file_set_contents(ctx->gnuplot, script->str, script->len, NULL);
533         g_string_free(script, TRUE);
534 }
535
536 static int receive(const struct sr_output *o,
537                    const struct sr_datafeed_packet *packet, GString **out)
538 {
539         struct context *ctx;
540
541         *out = NULL;
542         if (!o || !o->sdi)
543                 return SR_ERR_ARG;
544         if (!(ctx = o->priv))
545                 return SR_ERR_ARG;
546
547         sr_dbg("Got packet of type %d", packet->type);
548         switch (packet->type) {
549         case SR_DF_HEADER:
550                 *out = gen_header(o, packet->payload);
551                 break;
552         case SR_DF_TRIGGER:
553                 ctx->trigger = TRUE;
554                 break;
555         case SR_DF_LOGIC:
556                 process_logic(ctx, packet->payload);
557                 break;
558         case SR_DF_ANALOG:
559                 process_analog(ctx, packet->payload);
560                 break;
561         case SR_DF_FRAME_BEGIN:
562                 *out = g_string_new(ctx->frame);
563                 /* And then fall through to... */
564         case SR_DF_END:
565                 /* Got to end of frame/session with part of the data. */
566                 if (ctx->channels_seen)
567                         ctx->channels_seen = ctx->channel_count;
568                 if (*ctx->gnuplot)
569                         save_gnuplot(ctx);
570                 break;
571         }
572
573         /* If we've got them all, dump the values. */
574         if (ctx->channels_seen >= ctx->channel_count)
575                 dump_saved_values(ctx, out);
576
577         return SR_OK;
578 }
579
580 static int cleanup(struct sr_output *o)
581 {
582         struct context *ctx;
583
584         if (!o || !o->sdi)
585                 return SR_ERR_ARG;
586
587         if (o->priv) {
588                 ctx = o->priv;
589                 g_free((gpointer)ctx->record);
590                 g_free((gpointer)ctx->frame);
591                 g_free((gpointer)ctx->comment);
592                 g_free((gpointer)ctx->gnuplot);
593                 g_free(ctx->previous_sample);
594                 g_free(ctx->channels);
595                 g_free(o->priv);
596                 o->priv = NULL;
597         }
598
599         return SR_OK;
600 }
601
602 static struct sr_option options[] = {
603         {"gnuplot", "gnuplot", "gnuplot script file name", NULL, NULL},
604         {"scale", "scale", "Scale gnuplot graphs", NULL, NULL},
605         {"value", "Value separator", "Character to print between values", NULL, NULL},
606         {"record", "Record separator", "String to print between records", NULL, NULL},
607         {"frame", "Frame seperator", "String to print between frames", NULL, NULL},
608         {"comment", "Comment start string", "String used at start of comment lines", NULL, NULL},
609         {"header", "Output header", "Output header comment with capture metdata", NULL, NULL},
610         {"label", "Label values", "Output labels for each value", NULL, NULL},
611         {"time", "Time column", "Output sample time as column 1", NULL, NULL},
612         {"trigger", "Trigger column", "Output trigger indicator as last column ", NULL, NULL},
613         {"dedup", "Dedup rows", "Set to false to output duplicate rows", NULL, NULL},
614         ALL_ZERO
615 };
616
617 static const struct sr_option *get_options(void)
618 {
619         if (!options[0].def) {
620                 options[0].def = g_variant_ref_sink(g_variant_new_string(""));
621                 options[1].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
622                 options[2].def = g_variant_ref_sink(g_variant_new_string(","));
623                 options[3].def = g_variant_ref_sink(g_variant_new_string("\n"));
624                 options[4].def = g_variant_ref_sink(g_variant_new_string("\n"));
625                 options[5].def = g_variant_ref_sink(g_variant_new_string(";"));
626                 options[6].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
627                 options[7].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
628                 options[8].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
629                 options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
630                 options[10].def = g_variant_ref_sink(g_variant_new_boolean(TRUE));
631         }
632
633         return options;
634 }
635
636 SR_PRIV struct sr_output_module output_csv = {
637         .id = "csv",
638         .name = "CSV",
639         .desc = "Comma-separated values",
640         .exts = (const char *[]){"csv", NULL},
641         .flags = 0,
642         .options = get_options,
643         .init = init,
644         .receive = receive,
645         .cleanup = cleanup,
646 };