Use a new package version scheme
[swodec.git] / src / main.c
blob3a8a6c1b41b9db69cf982a66535021f51057b61c
1 /*
2 * This file is part of the swodec project.
4 * Copyright (C) 2014-2016 Marc Schink <swo-dev@marcschink.de>
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 3 of the License, or
9 * (at your option) any later version.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <glib.h>
31 #include <libswo/libswo.h>
33 #include "version.h"
36 * Exception names according to section B1.5 of ARMv7-M Architecture Reference
37 * Manual.
39 static const char *exception_names[] = {
40 "Thread",
41 "Reset",
42 "NMI",
43 "HardFault",
44 "MemManage",
45 "BusFault",
46 "UsageFault",
47 "Reserved",
48 "Reserved",
49 "Reserved",
50 "Reserved",
51 "SVCall",
52 "Debug Monitor",
53 "Reserved",
54 "PendSV",
55 "SysTick"
58 /* Number of exception names. */
59 #define NUM_EXCEPTION_NAMES 16
61 #define BUFFER_SIZE 1024
63 static gboolean opt_version;
64 static enum libswo_log_level opt_log_level;
65 static gchar *input_file = NULL;
66 static uint32_t packet_type_filter;
67 static uint32_t inst_address_filter;
68 static gboolean opt_dump_inst;
70 static gboolean parse_filter_option(const gchar *option_name,
71 const gchar *value, gpointer data, GError **error)
73 gchar **tokens;
74 unsigned int i;
75 uint32_t tmp;
76 gboolean invert;
78 (void)option_name;
79 (void)data;
80 (void)error;
82 if (!strlen(value))
83 return TRUE;
85 if (value[0] == '~') {
86 value++;
87 invert = TRUE;
88 } else {
89 invert = FALSE;
92 i = 0;
93 tokens = g_strsplit(value, ",", -1);
94 tmp = 0x00000000;
96 while (tokens[i]) {
97 g_strstrip(tokens[i]);
99 if (!strlen(tokens[i])) {
100 i++;
101 continue;
104 if (!g_ascii_strcasecmp(tokens[i], "unknown")) {
105 tmp |= (1 << LIBSWO_PACKET_TYPE_UNKNOWN);
106 } else if (!g_ascii_strcasecmp(tokens[i], "sync")) {
107 tmp |= (1 << LIBSWO_PACKET_TYPE_SYNC);
108 } else if (!g_ascii_strcasecmp(tokens[i], "of")) {
109 tmp |= (1 << LIBSWO_PACKET_TYPE_OF);
110 } else if (!g_ascii_strcasecmp(tokens[i], "lts")) {
111 tmp |= (1 << LIBSWO_PACKET_TYPE_LTS);
112 } else if (!g_ascii_strcasecmp(tokens[i], "gts")) {
113 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS1);
114 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS2);
115 } else if (!g_ascii_strcasecmp(tokens[i], "gts1")) {
116 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS1);
117 } else if (!g_ascii_strcasecmp(tokens[i], "gts2")) {
118 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS2);
119 } else if (!g_ascii_strcasecmp(tokens[i], "ext")) {
120 tmp |= (1 << LIBSWO_PACKET_TYPE_EXT);
121 } else if (!g_ascii_strcasecmp(tokens[i], "inst")) {
122 tmp |= (1 << LIBSWO_PACKET_TYPE_INST);
123 } else if (!g_ascii_strcasecmp(tokens[i], "hw")) {
124 tmp |= (1 << LIBSWO_PACKET_TYPE_HW);
125 } else if (!g_ascii_strcasecmp(tokens[i], "dwt")) {
126 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT);
127 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRC);
128 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE);
129 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE);
130 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET);
131 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE);
132 } else if (!g_ascii_strcasecmp(tokens[i], "evtcnt")) {
133 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT);
134 } else if (!g_ascii_strcasecmp(tokens[i], "exctrc")) {
135 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRC);
136 } else if (!g_ascii_strcasecmp(tokens[i], "pcsample")) {
137 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE);
138 } else if (!g_ascii_strcasecmp(tokens[i], "pcvalue")) {
139 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE);
140 } else if (!g_ascii_strcasecmp(tokens[i], "addroffset")) {
141 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET);
142 } else if (!g_ascii_strcasecmp(tokens[i], "datavalue")) {
143 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE);
144 } else {
145 g_critical("Invalid packet type: %s.", tokens[i]);
146 g_strfreev(tokens);
147 return FALSE;
150 i++;
153 if (invert)
154 tmp = ~tmp;
157 * Apply the packet type filter only if at least one valid packet type
158 * was specified.
160 if (tmp > 0)
161 packet_type_filter = tmp;
163 g_strfreev(tokens);
165 return TRUE;
168 static gboolean parse_inst_filter_option(const gchar *option_name,
169 const gchar *value, gpointer data, GError **error)
171 gchar **tokens;
172 unsigned int i;
173 uint32_t tmp;
174 long int address;
175 char *endptr;
176 gboolean invert;
178 (void)option_name;
179 (void)data;
180 (void)error;
182 if (!strlen(value))
183 return TRUE;
185 if (value[0] == '~') {
186 value++;
187 invert = TRUE;
188 } else {
189 invert = FALSE;
192 i = 0;
193 tokens = g_strsplit(value, ",", -1);
194 tmp = 0x00000000;
196 while (tokens[i]) {
197 g_strstrip(tokens[i]);
199 if (!strlen(tokens[i])) {
200 i++;
201 continue;
204 address = strtoll(tokens[i], &endptr, 10);
206 if (endptr == tokens[i] || *endptr != '\0') {
207 g_critical("Invalid source address: %s.", tokens[i]);
208 g_strfreev(tokens);
209 return FALSE;
212 if (address < 0 || address > LIBSWO_MAX_SOURCE_ADDRESS) {
213 g_critical("Source address out of range: %li.",
214 address);
215 g_strfreev(tokens);
216 return FALSE;
219 tmp |= (1 << address);
220 i++;
223 if (invert)
224 tmp = ~tmp;
227 * Apply the instrumentation source address filter only if at least one
228 * valid source address was specified.
230 if (tmp > 0)
231 inst_address_filter = tmp;
233 g_strfreev(tokens);
235 return TRUE;
238 static gboolean parse_log_level_option(const gchar *option_name,
239 const gchar *value, gpointer data, GError **error)
241 uint64_t tmp;
242 gchar *endptr;
244 (void)option_name;
245 (void)data;
246 (void)error;
248 if (!g_ascii_strcasecmp(value, "none")) {
249 opt_log_level = LIBSWO_LOG_LEVEL_NONE;
250 } else if (!g_ascii_strcasecmp(value, "error")) {
251 opt_log_level = LIBSWO_LOG_LEVEL_ERROR;
252 } else if (!g_ascii_strcasecmp(value, "warn")) {
253 opt_log_level = LIBSWO_LOG_LEVEL_WARNING;
254 } else if (!g_ascii_strcasecmp(value, "info")) {
255 opt_log_level = LIBSWO_LOG_LEVEL_INFO;
256 } else if (!g_ascii_strcasecmp(value, "debug")) {
257 opt_log_level = LIBSWO_LOG_LEVEL_DEBUG;
258 } else {
259 errno = 0;
260 tmp = g_ascii_strtoull(value, &endptr, 10);
262 if (*endptr != '\0' || errno != 0 ||
263 tmp > LIBSWO_LOG_LEVEL_DEBUG) {
264 g_critical("Invalid log level: %s.", value);
265 return FALSE;
268 opt_log_level = tmp;
271 return TRUE;
274 static GOptionEntry entries[] = {
275 {"version", 'V', 0, G_OPTION_ARG_NONE, &opt_version,
276 "Show version information", NULL},
277 {"log-level", 'l', 0, G_OPTION_ARG_CALLBACK, &parse_log_level_option,
278 "Log level", NULL},
279 {"input-file", 'i', 0, G_OPTION_ARG_FILENAME, &input_file,
280 "Load trace data from file", NULL},
281 {"filter", 'f', 0, G_OPTION_ARG_CALLBACK, &parse_filter_option,
282 "Filter for packet types", NULL},
283 {"filter-inst", 0, 0, G_OPTION_ARG_CALLBACK, &parse_inst_filter_option,
284 "Filter for instrumentation source addresses", NULL},
285 {"dump-inst", 0, 0, G_OPTION_ARG_NONE, &opt_dump_inst,
286 "Dump instrumentation payload", NULL},
287 {NULL, 0, 0, 0, NULL, NULL, NULL}
290 static void handle_unknown_packet(const union libswo_packet *packet)
292 printf("Unknown data (size = %zu bytes)\n", packet->unknown.size);
295 static void handle_sync_packet(const union libswo_packet *packet)
297 if (packet->sync.size % 8)
298 printf("Synchronization (size = %zu bits)\n",
299 packet->sync.size);
300 else
301 printf("Synchronization (size = %zu bytes)\n",
302 packet->sync.size / 8);
305 static void handle_overflow_packet(const union libswo_packet *packet)
307 (void)packet;
308 printf("Overflow\n");
311 static void handle_lts_packet(const union libswo_packet *packet)
313 const char *tc;
315 switch (packet->lts.relation) {
316 case LIBSWO_LTS_REL_SYNC:
317 tc = "synchronous";
318 break;
319 case LIBSWO_LTS_REL_TS:
320 tc = "timestamp delayed";
321 break;
322 case LIBSWO_LTS_REL_SRC:
323 tc = "data delayed";
324 break;
325 case LIBSWO_LTS_REL_BOTH:
326 tc = "data and timestamp delayed";
327 break;
328 default:
329 g_warning("Local timestamp packet with invalid relation: %u.",
330 packet->lts.relation);
331 return;
334 printf("Local timestamp (relation = %s, value = %x)\n", tc,
335 packet->lts.value);
338 static void handle_gts1_packet(const union libswo_packet *packet)
340 printf("Global timestamp (GTS1) (wrap = %u, clkch = %u, value = %x)\n",
341 packet->gts1.wrap, packet->gts1.clkch, packet->gts1.value);
344 static void handle_gts2_packet(const union libswo_packet *packet)
346 printf("Global timestamp (GTS2) (value = %x)\n", packet->gts2.value);
349 static void handle_ext_packet(const union libswo_packet *packet)
351 const char *src;
353 switch (packet->ext.source) {
354 case LIBSWO_EXT_SRC_ITM:
355 src = "ITM";
356 break;
357 case LIBSWO_EXT_SRC_HW:
358 src = "HW";
359 break;
360 default:
361 g_warning("Extension packet with invalid source: %u.",
362 packet->ext.source);
363 return;
366 printf("Extension (source = %s, value = %x)\n", src,
367 packet->ext.value);
370 static void handle_inst_packet(const union libswo_packet *packet)
372 if (!(inst_address_filter & (1 << packet->inst.address)))
373 return;
375 if (opt_dump_inst) {
376 fwrite(packet->inst.payload, packet->inst.size - 1, 1, stdout);
377 fflush(stdout);
378 return;
381 printf("Instrumentation (address = %u, value = %x, size = %zu bytes)\n",
382 packet->inst.address, packet->inst.value,
383 packet->inst.size - 1);
386 static void handle_hw_packet(const union libswo_packet *packet)
388 printf("Hardware source (address = %u, value = %x, size = %zu bytes)\n",
389 packet->hw.address, packet->hw.value, packet->hw.size - 1);
392 static void handle_dwt_evtcnt_packet(const union libswo_packet *packet)
394 printf("Event counter (CPI = %u, exc = %u, sleep = %u, LSU = %u, "
395 "fold = %u, cyc = %u)\n", packet->evtcnt.cpi,
396 packet->evtcnt.exc, packet->evtcnt.sleep, packet->evtcnt.lsu,
397 packet->evtcnt.fold, packet->evtcnt.cyc);
400 static void handle_dwt_exctrc_packet(const union libswo_packet *packet)
402 uint16_t exception;
403 const char *func;
404 const char *name;
405 char buf[25];
407 switch (packet->exctrc.function) {
408 case LIBSWO_EXCTRC_FUNC_ENTER:
409 func = "enter";
410 break;
411 case LIBSWO_EXCTRC_FUNC_EXIT:
412 func = "exit";
413 break;
414 case LIBSWO_EXCTRC_FUNC_RETURN:
415 func = "return";
416 break;
417 default:
418 func = "reserved";
421 exception = packet->exctrc.exception;
423 if (exception < NUM_EXCEPTION_NAMES) {
424 name = exception_names[exception];
425 } else {
426 snprintf(buf, sizeof(buf), "External interrupt %u",
427 exception - NUM_EXCEPTION_NAMES);
428 name = buf;
431 printf("Exception trace (function = %s, exception = %s)\n", func,
432 name);
435 static void handle_dwt_pc_sample_packet(const union libswo_packet *packet)
437 if (packet->pc_sample.sleep)
438 printf("Periodic PC sleep\n");
439 else
440 printf("Periodic PC sample (value = %x)\n",
441 packet->pc_sample.pc);
444 static void handle_dwt_pc_value_packet(const union libswo_packet *packet)
446 printf("Data trace PC value (comparator = %u, value = %x)\n",
447 packet->pc_value.cmpn, packet->pc_value.pc);
450 static void handle_dwt_addr_offset_packet(const union libswo_packet *packet)
452 printf("Data trace address offset (comparator = %u, value = %x)\n",
453 packet->addr_offset.cmpn, packet->addr_offset.offset);
456 static void handle_dwt_data_value_packet(const union libswo_packet *packet)
458 printf("Data trace data value (comparator = %u, WnR = %u, value = %x, "
459 "size = %zu bytes)\n", packet->data_value.cmpn,
460 packet->data_value.wnr, packet->data_value.data_value,
461 packet->data_value.size - 1);
464 static int packet_cb(struct libswo_context *ctx,
465 const union libswo_packet *packet, void *user_data)
467 (void)ctx;
468 (void)user_data;
470 if (!(packet_type_filter & (1 << packet->type)))
471 return TRUE;
473 switch (packet->type) {
474 case LIBSWO_PACKET_TYPE_UNKNOWN:
475 handle_unknown_packet(packet);
476 break;
477 case LIBSWO_PACKET_TYPE_SYNC:
478 handle_sync_packet(packet);
479 break;
480 case LIBSWO_PACKET_TYPE_INST:
481 handle_inst_packet(packet);
482 break;
483 case LIBSWO_PACKET_TYPE_OF:
484 handle_overflow_packet(packet);
485 break;
486 case LIBSWO_PACKET_TYPE_EXT:
487 handle_ext_packet(packet);
488 break;
489 case LIBSWO_PACKET_TYPE_LTS:
490 handle_lts_packet(packet);
491 break;
492 case LIBSWO_PACKET_TYPE_GTS1:
493 handle_gts1_packet(packet);
494 break;
495 case LIBSWO_PACKET_TYPE_GTS2:
496 handle_gts2_packet(packet);
497 break;
498 case LIBSWO_PACKET_TYPE_HW:
499 handle_hw_packet(packet);
500 break;
501 case LIBSWO_PACKET_TYPE_DWT_EVTCNT:
502 handle_dwt_evtcnt_packet(packet);
503 break;
504 case LIBSWO_PACKET_TYPE_DWT_EXCTRC:
505 handle_dwt_exctrc_packet(packet);
506 break;
507 case LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE:
508 handle_dwt_pc_sample_packet(packet);
509 break;
510 case LIBSWO_PACKET_TYPE_DWT_PC_VALUE:
511 handle_dwt_pc_value_packet(packet);
512 break;
513 case LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET:
514 handle_dwt_addr_offset_packet(packet);
515 break;
516 case LIBSWO_PACKET_TYPE_DWT_DATA_VALUE:
517 handle_dwt_data_value_packet(packet);
518 break;
519 default:
520 g_warning("Invalid packet type: %i.", packet->type);
523 return TRUE;
526 static void show_version(void)
528 printf("%s\n", SWODEC_VERSION_PACKAGE_STRING);
529 printf("Using libswo %s\n", libswo_version_package_get_string());
532 static int parse_options(int *argc, char ***argv)
534 GError *error;
535 GOptionContext *context;
537 error = NULL;
539 context = g_option_context_new(NULL);
540 g_option_context_add_main_entries(context, entries, NULL);
542 if (!g_option_context_parse(context, argc, argv, &error)) {
543 g_critical("%s.", error->message);
544 g_error_free(error);
545 g_option_context_free(context);
546 return FALSE;
549 g_option_context_free(context);
551 return TRUE;
554 static void log_handler(const gchar *domain, GLogLevelFlags level,
555 const gchar *message, gpointer user_data)
557 enum libswo_log_level tmp;
559 (void)domain;
560 (void)user_data;
562 if (level & G_LOG_LEVEL_ERROR)
563 tmp = LIBSWO_LOG_LEVEL_ERROR;
564 else if (level & G_LOG_LEVEL_CRITICAL)
565 tmp = LIBSWO_LOG_LEVEL_ERROR;
566 else if (level & G_LOG_LEVEL_WARNING)
567 tmp = LIBSWO_LOG_LEVEL_WARNING;
568 else if (level & G_LOG_LEVEL_MESSAGE)
569 tmp = LIBSWO_LOG_LEVEL_INFO;
570 else if (level & G_LOG_LEVEL_INFO)
571 tmp = LIBSWO_LOG_LEVEL_INFO;
572 else if (level & G_LOG_LEVEL_DEBUG)
573 tmp = LIBSWO_LOG_LEVEL_DEBUG;
574 else
575 tmp = LIBSWO_LOG_LEVEL_WARNING;
577 if (tmp > opt_log_level)
578 return;
580 fprintf(stderr, "%s\n", message);
581 fflush(stderr);
584 static int decoder_log_callback(struct libswo_context *ctx,
585 enum libswo_log_level level, const char *format, va_list args,
586 void *user_data)
588 (void)ctx;
589 (void)user_data;
591 if (level > opt_log_level)
592 return 0;
594 fprintf(stderr, "libswo: ");
595 vfprintf(stderr, format, args);
596 fprintf(stderr, "\n");
598 return 0;
601 int main(int argc, char **argv)
603 int ret;
604 struct libswo_context *ctx;
605 uint8_t buffer[BUFFER_SIZE];
606 GIOChannel *input;
607 GError *error;
608 GIOStatus iostat;
609 gsize num;
611 opt_version = FALSE;
612 opt_log_level = LIBSWO_LOG_LEVEL_WARNING;
613 opt_dump_inst = FALSE;
615 g_log_set_default_handler(&log_handler, NULL);
617 /* Disable packet filtering for all packet types by default. */
618 packet_type_filter = (1 << LIBSWO_PACKET_TYPE_UNKNOWN) | \
619 (1 << LIBSWO_PACKET_TYPE_SYNC) | \
620 (1 << LIBSWO_PACKET_TYPE_OF) | \
621 (1 << LIBSWO_PACKET_TYPE_LTS) | \
622 (1 << LIBSWO_PACKET_TYPE_GTS1) | \
623 (1 << LIBSWO_PACKET_TYPE_GTS2) | \
624 (1 << LIBSWO_PACKET_TYPE_EXT) | \
625 (1 << LIBSWO_PACKET_TYPE_INST) | \
626 (1 << LIBSWO_PACKET_TYPE_HW) | \
627 (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT) | \
628 (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRC) | \
629 (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE) | \
630 (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE) | \
631 (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET) | \
632 (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE);
634 /* Disable instrumentation source address filtering by default. */
635 inst_address_filter = 0xffffffff;
637 if (!parse_options(&argc, &argv))
638 return EXIT_FAILURE;
640 if (opt_version) {
641 show_version();
642 return EXIT_SUCCESS;
645 if (opt_dump_inst)
646 packet_type_filter = (1 << LIBSWO_PACKET_TYPE_INST);
648 error = NULL;
650 if (input_file) {
651 input = g_io_channel_new_file(input_file, "r", &error);
653 if (!input) {
654 g_critical("%s: %s.", input_file, error->message);
655 g_error_free(error);
656 g_free(input_file);
657 return EXIT_FAILURE;
660 g_free(input_file);
661 } else {
662 input = g_io_channel_unix_new(STDIN_FILENO);
665 /* Set encoding to binary (default is UTF-8). */
666 iostat = g_io_channel_set_encoding(input, NULL, &error);
668 if (iostat != G_IO_STATUS_NORMAL) {
669 g_critical("%s.", error->message);
670 g_error_free(error);
671 g_io_channel_unref(input);
672 return EXIT_FAILURE;
675 g_io_channel_set_buffered(input, FALSE);
677 ret = libswo_init(&ctx, NULL, BUFFER_SIZE * 2);
679 if (ret != LIBSWO_OK) {
680 g_critical("libswo_init() failed: %s.",
681 libswo_strerror_name(ret));
682 g_io_channel_unref(input);
683 return EXIT_FAILURE;
686 ret = libswo_set_callback(ctx, &packet_cb, NULL);
688 if (ret != LIBSWO_OK) {
689 g_critical("libswo_set_callback() failed: %s.",
690 libswo_strerror_name(ret));
691 g_io_channel_unref(input);
692 libswo_exit(ctx);
693 return EXIT_FAILURE;
696 ret = libswo_log_set_callback(ctx, &decoder_log_callback, NULL);
698 if (ret != LIBSWO_OK) {
699 g_critical("libswo_log_set_callback() failed: %s.",
700 libswo_strerror_name(ret));
701 g_io_channel_unref(input);
702 libswo_exit(ctx);
703 return EXIT_FAILURE;
706 while (TRUE) {
707 iostat = g_io_channel_read_chars(input, (gchar *)buffer,
708 BUFFER_SIZE, &num, &error);
710 if (iostat == G_IO_STATUS_ERROR)
711 break;
713 ret = libswo_feed(ctx, buffer, num);
715 if (ret != LIBSWO_OK) {
716 g_critical("libswo_feed() failed: %s.",
717 libswo_strerror_name(ret));
718 g_io_channel_unref(input);
719 libswo_exit(ctx);
720 return EXIT_FAILURE;
723 ret = libswo_decode(ctx, 0);
725 if (ret != LIBSWO_OK) {
726 g_critical("libswo_decode() failed: %s.",
727 libswo_strerror_name(ret));
728 g_io_channel_unref(input);
729 libswo_exit(ctx);
730 return EXIT_FAILURE;
733 if (iostat == G_IO_STATUS_EOF)
734 break;
737 if (iostat == G_IO_STATUS_ERROR) {
738 g_critical("%s.", error->message);
739 g_error_free(error);
740 g_io_channel_unref(input);
741 libswo_exit(ctx);
742 return EXIT_FAILURE;
745 ret = libswo_decode(ctx, LIBSWO_DF_EOS);
747 if (ret != LIBSWO_OK) {
748 g_critical("libswo_decode() failed: %s.",
749 libswo_strerror_name(ret));
750 g_io_channel_unref(input);
751 libswo_exit(ctx);
752 return EXIT_FAILURE;
755 g_io_channel_unref(input);
756 libswo_exit(ctx);
758 return EXIT_SUCCESS;