Make use of LIBSWO_MAX_SOURCE_ADDRESS
[swodec.git] / src / main.c
blobb303c19f886791a9e7b1505503740538b49be660
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_OVERFLOW);
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_EXCTRACE);
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], "evcnt")) {
133 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT);
134 } else if (!g_ascii_strcasecmp(tokens[i], "exc")) {
135 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRACE);
136 } else if (!g_ascii_strcasecmp(tokens[i], "pc")) {
137 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE);
138 } else if (!g_ascii_strcasecmp(tokens[i], "dtpc")) {
139 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE);
140 } else if (!g_ascii_strcasecmp(tokens[i], "dtaddr")) {
141 tmp |= (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET);
142 } else if (!g_ascii_strcasecmp(tokens[i], "dtval")) {
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_hw_packet(const union libswo_packet *packet)
292 printf("Hardware source (address = %u, value = %x, size = %zu bytes)\n",
293 packet->hw.address, packet->hw.value, packet->hw.size - 1);
296 static void handle_inst_packet(const union libswo_packet *packet)
298 if (!(inst_address_filter & (1 << packet->inst.address)))
299 return;
301 if (opt_dump_inst) {
302 fwrite(packet->inst.payload, packet->inst.size - 1, 1, stdout);
303 fflush(stdout);
304 return;
307 printf("Instrumentation (address = %u, value = %x, size = %zu bytes)\n",
308 packet->inst.address, packet->inst.value,
309 packet->inst.size - 1);
312 static void handle_overflow_packet(const union libswo_packet *packet)
314 (void)packet;
315 printf("Overflow\n");
318 static void handle_ext_packet(const union libswo_packet *packet)
320 const char *src;
322 switch (packet->ext.source) {
323 case LIBSWO_EXT_SRC_ITM:
324 src = "ITM";
325 break;
326 case LIBSWO_EXT_SRC_HW:
327 src = "HW";
328 break;
329 default:
330 g_warning("Extension packet with invalid source: %u.",
331 packet->ext.source);
332 return;
335 printf("Extension (source = %s, value = %x)\n", src,
336 packet->ext.value);
339 static void handle_unknown_packet(const union libswo_packet *packet)
341 printf("Unknown data (size = %zu bytes)\n", packet->unknown.size);
344 static void handle_sync_packet(const union libswo_packet *packet)
346 if (packet->sync.size % 8)
347 printf("Synchronization (size = %zu bits)\n",
348 packet->sync.size);
349 else
350 printf("Synchronization (size = %zu bytes)\n",
351 packet->sync.size / 8);
354 static void handle_lts_packet(const union libswo_packet *packet)
356 const char *tc;
358 switch (packet->lts.relation) {
359 case LIBSWO_LTS_REL_SYNC:
360 tc = "synchronous";
361 break;
362 case LIBSWO_LTS_REL_TS:
363 tc = "timestamp delayed";
364 break;
365 case LIBSWO_LTS_REL_SRC:
366 tc = "data delayed";
367 break;
368 case LIBSWO_LTS_REL_BOTH:
369 tc = "data and timestamp delayed";
370 break;
371 default:
372 g_warning("Local timestamp packet with invalid relation: %u.",
373 packet->lts.relation);
374 return;
377 printf("Local timestamp (relation = %s, value = %x)\n", tc,
378 packet->lts.value);
381 static void handle_gts1_packet(const union libswo_packet *packet)
383 printf("Global timestamp (GTS1) (wrap = %u, clkch = %u, value = %x)\n",
384 packet->gts1.wrap, packet->gts1.clkch, packet->gts1.value);
387 static void handle_gts2_packet(const union libswo_packet *packet)
389 printf("Global timestamp (GTS2) (value = %x)\n", packet->gts2.value);
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_exctrace_packet(const union libswo_packet *packet)
402 uint16_t exception;
403 const char *func;
404 const char *name;
405 char buf[23];
407 switch (packet->exctrace.function) {
408 case LIBSWO_EXCTRACE_FUNC_ENTER:
409 func = "enter";
410 break;
411 case LIBSWO_EXCTRACE_FUNC_EXIT:
412 func = "exit";
413 break;
414 case LIBSWO_EXCTRACE_FUNC_RETURN:
415 func = "return";
416 break;
417 default:
418 func = "reserved";
421 exception = packet->exctrace.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_OVERFLOW:
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_EXCTRACE:
505 handle_dwt_exctrace_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: %u.", packet->type);
521 break;
524 return TRUE;
527 static void show_version(void)
529 printf("%s\n", SWODEC_VERSION_PACKAGE_STRING);
530 printf("Using libswo %s\n", libswo_version_package_get_string());
533 static int parse_options(int *argc, char ***argv)
535 GError *error;
536 GOptionContext *context;
538 error = NULL;
540 context = g_option_context_new(NULL);
541 g_option_context_add_main_entries(context, entries, NULL);
543 if (!g_option_context_parse(context, argc, argv, &error)) {
544 g_critical("%s.", error->message);
545 g_error_free(error);
546 g_option_context_free(context);
547 return FALSE;
550 g_option_context_free(context);
552 return TRUE;
555 static void log_handler(const gchar *domain, GLogLevelFlags level,
556 const gchar *message, gpointer user_data)
558 enum libswo_log_level tmp;
560 (void)domain;
561 (void)user_data;
563 if (level & G_LOG_LEVEL_ERROR)
564 tmp = LIBSWO_LOG_LEVEL_ERROR;
565 else if (level & G_LOG_LEVEL_CRITICAL)
566 tmp = LIBSWO_LOG_LEVEL_ERROR;
567 else if (level & G_LOG_LEVEL_WARNING)
568 tmp = LIBSWO_LOG_LEVEL_WARNING;
569 else if (level & G_LOG_LEVEL_MESSAGE)
570 tmp = LIBSWO_LOG_LEVEL_INFO;
571 else if (level & G_LOG_LEVEL_INFO)
572 tmp = LIBSWO_LOG_LEVEL_INFO;
573 else if (level & G_LOG_LEVEL_DEBUG)
574 tmp = LIBSWO_LOG_LEVEL_DEBUG;
575 else
576 tmp = LIBSWO_LOG_LEVEL_WARNING;
578 if (tmp > opt_log_level)
579 return;
581 fprintf(stderr, "%s\n", message);
582 fflush(stderr);
585 static int decoder_log_callback(struct libswo_context *ctx,
586 enum libswo_log_level level, const char *format, va_list args,
587 void *user_data)
589 (void)ctx;
590 (void)user_data;
592 if (level > opt_log_level)
593 return 0;
595 fprintf(stderr, "libswo: ");
596 vfprintf(stderr, format, args);
597 fprintf(stderr, "\n");
599 return 0;
602 int main(int argc, char **argv)
604 int ret;
605 struct libswo_context *ctx;
606 uint8_t buffer[BUFFER_SIZE];
607 GIOChannel *input;
608 GError *error;
609 GIOStatus iostat;
610 gsize num;
612 opt_version = FALSE;
613 opt_log_level = LIBSWO_LOG_LEVEL_WARNING;
614 opt_dump_inst = FALSE;
616 g_log_set_default_handler(&log_handler, NULL);
618 /* Disable packet filtering for all packet types by default. */
619 packet_type_filter = (1 << LIBSWO_PACKET_TYPE_UNKNOWN) | \
620 (1 << LIBSWO_PACKET_TYPE_SYNC) | \
621 (1 << LIBSWO_PACKET_TYPE_OVERFLOW) | \
622 (1 << LIBSWO_PACKET_TYPE_LTS) | \
623 (1 << LIBSWO_PACKET_TYPE_GTS1) | \
624 (1 << LIBSWO_PACKET_TYPE_GTS2) | \
625 (1 << LIBSWO_PACKET_TYPE_EXT) | \
626 (1 << LIBSWO_PACKET_TYPE_INST) | \
627 (1 << LIBSWO_PACKET_TYPE_HW) | \
628 (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT) | \
629 (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRACE) | \
630 (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE) | \
631 (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE) | \
632 (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET) | \
633 (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE);
635 /* Disable instrumentation source address filtering by default. */
636 inst_address_filter = 0xffffffff;
638 if (!parse_options(&argc, &argv))
639 return EXIT_FAILURE;
641 if (opt_version) {
642 show_version();
643 return EXIT_SUCCESS;
646 if (opt_dump_inst)
647 packet_type_filter = (1 << LIBSWO_PACKET_TYPE_INST);
649 error = NULL;
651 if (input_file) {
652 input = g_io_channel_new_file(input_file, "r", &error);
654 if (!input) {
655 g_critical("%s: %s.", input_file, error->message);
656 g_error_free(error);
657 g_free(input_file);
658 return EXIT_FAILURE;
661 g_free(input_file);
662 } else {
663 input = g_io_channel_unix_new(STDIN_FILENO);
666 /* Set encoding to binary (default is UTF-8). */
667 iostat = g_io_channel_set_encoding(input, NULL, &error);
669 if (iostat != G_IO_STATUS_NORMAL) {
670 g_critical("%s.", error->message);
671 g_error_free(error);
672 g_io_channel_unref(input);
673 return EXIT_FAILURE;
676 g_io_channel_set_buffered(input, FALSE);
678 ret = libswo_init(&ctx, NULL, BUFFER_SIZE * 2);
680 if (ret != LIBSWO_OK) {
681 g_critical("libswo_init() failed: %s.",
682 libswo_strerror_name(ret));
683 g_io_channel_unref(input);
684 return EXIT_FAILURE;
687 ret = libswo_set_callback(ctx, &packet_cb, NULL);
689 if (ret != LIBSWO_OK) {
690 g_critical("libswo_set_callback() failed: %s.",
691 libswo_strerror_name(ret));
692 g_io_channel_unref(input);
693 libswo_exit(ctx);
694 return EXIT_FAILURE;
697 ret = libswo_log_set_callback(ctx, &decoder_log_callback, NULL);
699 if (ret != LIBSWO_OK) {
700 g_critical("libswo_log_set_callback() failed: %s.",
701 libswo_strerror_name(ret));
702 g_io_channel_unref(input);
703 libswo_exit(ctx);
704 return EXIT_FAILURE;
707 while (TRUE) {
708 iostat = g_io_channel_read_chars(input, (gchar *)buffer,
709 BUFFER_SIZE, &num, &error);
711 if (iostat == G_IO_STATUS_ERROR)
712 break;
714 ret = libswo_feed(ctx, buffer, num);
716 if (ret != LIBSWO_OK) {
717 g_critical("libswo_feed() failed: %s.",
718 libswo_strerror_name(ret));
719 g_io_channel_unref(input);
720 libswo_exit(ctx);
721 return EXIT_FAILURE;
724 ret = libswo_decode(ctx, 0);
726 if (ret != LIBSWO_OK) {
727 g_critical("libswo_decode() failed: %s.",
728 libswo_strerror_name(ret));
729 g_io_channel_unref(input);
730 libswo_exit(ctx);
731 return EXIT_FAILURE;
734 if (iostat == G_IO_STATUS_EOF)
735 break;
738 if (iostat == G_IO_STATUS_ERROR) {
739 g_critical("%s.", error->message);
740 g_error_free(error);
741 g_io_channel_unref(input);
742 libswo_exit(ctx);
743 return EXIT_FAILURE;
746 ret = libswo_decode(ctx, LIBSWO_DF_EOS);
748 if (ret != LIBSWO_OK) {
749 g_critical("libswo_decode() failed: %s.",
750 libswo_strerror_name(ret));
751 g_io_channel_unref(input);
752 libswo_exit(ctx);
753 return EXIT_FAILURE;
756 g_io_channel_unref(input);
757 libswo_exit(ctx);
759 return EXIT_SUCCESS;