+static void channel_free(void *data)
+{
+ struct srd_channel *ch = data;
+
+ if (!ch)
+ return;
+
+ g_free(ch->desc);
+ g_free(ch->name);
+ g_free(ch->id);
+ g_free(ch);
+}
+
+static void variant_free(void *data)
+{
+ GVariant *var = data;
+
+ if (!var)
+ return;
+
+ g_variant_unref(var);
+}
+
+static void annotation_row_free(void *data)
+{
+ struct srd_decoder_annotation_row *row = data;
+
+ if (!row)
+ return;
+
+ g_slist_free(row->ann_classes);
+ g_free(row->desc);
+ g_free(row->id);
+ g_free(row);
+}
+
+static void decoder_option_free(void *data)
+{
+ struct srd_decoder_option *opt = data;
+
+ if (!opt)
+ return;
+
+ g_slist_free_full(opt->values, &variant_free);
+ variant_free(opt->def);
+ g_free(opt->desc);
+ g_free(opt->id);
+ g_free(opt);
+}
+
+static void decoder_free(struct srd_decoder *dec)
+{
+ PyGILState_STATE gstate;
+
+ if (!dec)
+ return;
+
+ gstate = PyGILState_Ensure();
+ Py_XDECREF(dec->py_dec);
+ Py_XDECREF(dec->py_mod);
+ PyGILState_Release(gstate);
+
+ g_slist_free_full(dec->options, &decoder_option_free);
+ g_slist_free_full(dec->binary, (GDestroyNotify)&g_strfreev);
+ g_slist_free_full(dec->annotation_rows, &annotation_row_free);
+ g_slist_free_full(dec->annotations, (GDestroyNotify)&g_strfreev);
+ g_slist_free_full(dec->opt_channels, &channel_free);
+ g_slist_free_full(dec->channels, &channel_free);
+
+ g_slist_free_full(dec->outputs, g_free);
+ g_slist_free_full(dec->inputs, g_free);
+ g_slist_free_full(dec->tags, g_free);
+ g_free(dec->license);
+ g_free(dec->desc);
+ g_free(dec->longname);
+ g_free(dec->name);
+ g_free(dec->id);
+
+ g_free(dec);
+}
+
+static int get_channels(const struct srd_decoder *d, const char *attr,
+ GSList **out_pdchl, int offset)
+{
+ PyObject *py_channellist, *py_entry;
+ struct srd_channel *pdch;
+ GSList *pdchl;
+ ssize_t i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ if (!PyObject_HasAttrString(d->py_dec, attr)) {
+ /* No channels of this type specified. */
+ PyGILState_Release(gstate);
+ return SRD_OK;
+ }
+
+ pdchl = NULL;
+
+ py_channellist = PyObject_GetAttrString(d->py_dec, attr);
+ if (!py_channellist)
+ goto except_out;
+
+ if (!PyTuple_Check(py_channellist)) {
+ srd_err("Protocol decoder %s %s attribute is not a tuple.",
+ d->name, attr);
+ goto err_out;
+ }
+
+ for (i = PyTuple_Size(py_channellist) - 1; i >= 0; i--) {
+ py_entry = PyTuple_GetItem(py_channellist, i);
+ if (!py_entry)
+ goto except_out;
+
+ if (!PyDict_Check(py_entry)) {
+ srd_err("Protocol decoder %s %s attribute is not "
+ "a list of dict elements.", d->name, attr);
+ goto err_out;
+ }
+ pdch = g_malloc(sizeof(struct srd_channel));
+ /* Add to list right away so it doesn't get lost. */
+ pdchl = g_slist_prepend(pdchl, pdch);
+
+ if (py_dictitem_as_str(py_entry, "id", &pdch->id) != SRD_OK)
+ goto err_out;
+ if (py_dictitem_as_str(py_entry, "name", &pdch->name) != SRD_OK)
+ goto err_out;
+ if (py_dictitem_as_str(py_entry, "desc", &pdch->desc) != SRD_OK)
+ goto err_out;
+
+ pdch->order = offset + i;
+ }
+
+ Py_DECREF(py_channellist);
+ *out_pdchl = pdchl;
+
+ PyGILState_Release(gstate);
+
+ return SRD_OK;
+
+except_out:
+ srd_exception_catch("Failed to get %s list of %s decoder",
+ attr, d->name);
+
+err_out:
+ g_slist_free_full(pdchl, &channel_free);
+ Py_XDECREF(py_channellist);
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
+static int get_options(struct srd_decoder *d)
+{
+ PyObject *py_opts, *py_opt, *py_str, *py_values, *py_default, *py_item;
+ GSList *options;
+ struct srd_decoder_option *o;
+ GVariant *gvar;
+ ssize_t opt, i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ if (!PyObject_HasAttrString(d->py_dec, "options")) {
+ /* No options, that's fine. */
+ PyGILState_Release(gstate);
+ return SRD_OK;
+ }
+
+ options = NULL;
+
+ /* If present, options must be a tuple. */
+ py_opts = PyObject_GetAttrString(d->py_dec, "options");
+ if (!py_opts)
+ goto except_out;
+
+ if (!PyTuple_Check(py_opts)) {
+ srd_err("Protocol decoder %s: options attribute is not "
+ "a tuple.", d->id);
+ goto err_out;
+ }
+
+ for (opt = PyTuple_Size(py_opts) - 1; opt >= 0; opt--) {
+ py_opt = PyTuple_GetItem(py_opts, opt);
+ if (!py_opt)
+ goto except_out;
+
+ if (!PyDict_Check(py_opt)) {
+ srd_err("Protocol decoder %s options: each option "
+ "must consist of a dictionary.", d->name);
+ goto err_out;
+ }
+
+ o = g_malloc0(sizeof(struct srd_decoder_option));
+ /* Add to list right away so it doesn't get lost. */
+ options = g_slist_prepend(options, o);
+
+ py_str = PyDict_GetItemString(py_opt, "id");
+ if (!py_str) {
+ srd_err("Protocol decoder %s option %zd has no ID.",
+ d->name, opt);
+ goto err_out;
+ }
+ if (py_str_as_str(py_str, &o->id) != SRD_OK)
+ goto err_out;
+
+ py_str = PyDict_GetItemString(py_opt, "desc");
+ if (py_str) {
+ if (py_str_as_str(py_str, &o->desc) != SRD_OK)
+ goto err_out;
+ }
+
+ py_default = PyDict_GetItemString(py_opt, "default");
+ if (py_default) {
+ gvar = py_obj_to_variant(py_default);
+ if (!gvar) {
+ srd_err("Protocol decoder %s option 'default' has "
+ "invalid default value.", d->name);
+ goto err_out;
+ }
+ o->def = g_variant_ref_sink(gvar);
+ }
+
+ py_values = PyDict_GetItemString(py_opt, "values");
+ if (py_values) {
+ /*
+ * A default is required if a list of values is
+ * given, since it's used to verify their type.
+ */
+ if (!o->def) {
+ srd_err("No default for option '%s'.", o->id);
+ goto err_out;
+ }
+ if (!PyTuple_Check(py_values)) {
+ srd_err("Option '%s' values should be a tuple.", o->id);
+ goto err_out;
+ }
+
+ for (i = PyTuple_Size(py_values) - 1; i >= 0; i--) {
+ py_item = PyTuple_GetItem(py_values, i);
+ if (!py_item)
+ goto except_out;
+
+ if (py_default && (Py_TYPE(py_default) != Py_TYPE(py_item))) {
+ srd_err("All values for option '%s' must be "
+ "of the same type as the default.",
+ o->id);
+ goto err_out;
+ }
+ gvar = py_obj_to_variant(py_item);
+ if (!gvar) {
+ srd_err("Protocol decoder %s option 'values' "
+ "contains invalid value.", d->name);
+ goto err_out;
+ }
+ o->values = g_slist_prepend(o->values,
+ g_variant_ref_sink(gvar));
+ }
+ }
+ }
+ d->options = options;
+ Py_DECREF(py_opts);
+ PyGILState_Release(gstate);
+
+ return SRD_OK;
+
+except_out:
+ srd_exception_catch("Failed to get %s decoder options", d->name);
+
+err_out:
+ g_slist_free_full(options, &decoder_option_free);
+ Py_XDECREF(py_opts);
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
+/* Convert annotation class attribute to GSList of char **. */
+static int get_annotations(struct srd_decoder *dec)
+{
+ PyObject *py_annlist, *py_ann;
+ GSList *annotations;
+ char **annpair;
+ ssize_t i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ if (!PyObject_HasAttrString(dec->py_dec, "annotations")) {
+ PyGILState_Release(gstate);
+ return SRD_OK;
+ }
+
+ annotations = NULL;
+
+ py_annlist = PyObject_GetAttrString(dec->py_dec, "annotations");
+ if (!py_annlist)
+ goto except_out;
+
+ if (!PyTuple_Check(py_annlist)) {
+ srd_err("Protocol decoder %s annotations should "
+ "be a tuple.", dec->name);
+ goto err_out;
+ }
+
+ for (i = PyTuple_Size(py_annlist) - 1; i >= 0; i--) {
+ py_ann = PyTuple_GetItem(py_annlist, i);
+ if (!py_ann)
+ goto except_out;
+
+ if (!PyTuple_Check(py_ann) || PyTuple_Size(py_ann) != 2) {
+ srd_err("Protocol decoder %s annotation %zd should "
+ "be a tuple with two elements.",
+ dec->name, i + 1);
+ goto err_out;
+ }
+ if (py_strseq_to_char(py_ann, &annpair) != SRD_OK)
+ goto err_out;
+
+ annotations = g_slist_prepend(annotations, annpair);
+ }
+ dec->annotations = annotations;
+ Py_DECREF(py_annlist);
+ PyGILState_Release(gstate);
+
+ return SRD_OK;
+
+except_out:
+ srd_exception_catch("Failed to get %s decoder annotations", dec->name);
+
+err_out:
+ g_slist_free_full(annotations, (GDestroyNotify)&g_strfreev);
+ Py_XDECREF(py_annlist);
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
+/* Convert annotation_rows to GSList of 'struct srd_decoder_annotation_row'. */
+static int get_annotation_rows(struct srd_decoder *dec)
+{
+ PyObject *py_ann_rows, *py_ann_row, *py_ann_classes, *py_item;
+ GSList *annotation_rows;
+ struct srd_decoder_annotation_row *ann_row;
+ ssize_t i, k;
+ size_t class_idx;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows")) {
+ PyGILState_Release(gstate);
+ return SRD_OK;
+ }
+
+ annotation_rows = NULL;
+
+ py_ann_rows = PyObject_GetAttrString(dec->py_dec, "annotation_rows");
+ if (!py_ann_rows)
+ goto except_out;
+
+ if (!PyTuple_Check(py_ann_rows)) {
+ srd_err("Protocol decoder %s annotation_rows "
+ "must be a tuple.", dec->name);
+ goto err_out;
+ }
+
+ for (i = PyTuple_Size(py_ann_rows) - 1; i >= 0; i--) {
+ py_ann_row = PyTuple_GetItem(py_ann_rows, i);
+ if (!py_ann_row)
+ goto except_out;
+
+ if (!PyTuple_Check(py_ann_row) || PyTuple_Size(py_ann_row) != 3) {
+ srd_err("Protocol decoder %s annotation_rows "
+ "must contain only tuples of 3 elements.",
+ dec->name);
+ goto err_out;
+ }
+ ann_row = g_malloc0(sizeof(struct srd_decoder_annotation_row));
+ /* Add to list right away so it doesn't get lost. */
+ annotation_rows = g_slist_prepend(annotation_rows, ann_row);
+
+ py_item = PyTuple_GetItem(py_ann_row, 0);
+ if (!py_item)
+ goto except_out;
+ if (py_str_as_str(py_item, &ann_row->id) != SRD_OK)
+ goto err_out;
+
+ py_item = PyTuple_GetItem(py_ann_row, 1);
+ if (!py_item)
+ goto except_out;
+ if (py_str_as_str(py_item, &ann_row->desc) != SRD_OK)
+ goto err_out;
+
+ py_ann_classes = PyTuple_GetItem(py_ann_row, 2);
+ if (!py_ann_classes)
+ goto except_out;
+
+ if (!PyTuple_Check(py_ann_classes)) {
+ srd_err("Protocol decoder %s annotation_rows tuples "
+ "must have a tuple of numbers as 3rd element.",
+ dec->name);
+ goto err_out;
+ }
+
+ for (k = PyTuple_Size(py_ann_classes) - 1; k >= 0; k--) {
+ py_item = PyTuple_GetItem(py_ann_classes, k);
+ if (!py_item)
+ goto except_out;
+
+ if (!PyLong_Check(py_item)) {
+ srd_err("Protocol decoder %s annotation row "
+ "class tuple must only contain numbers.",
+ dec->name);
+ goto err_out;
+ }
+ class_idx = PyLong_AsSize_t(py_item);
+ if (PyErr_Occurred())
+ goto except_out;
+
+ ann_row->ann_classes = g_slist_prepend(ann_row->ann_classes,
+ GSIZE_TO_POINTER(class_idx));
+ }
+ }
+ dec->annotation_rows = annotation_rows;
+ Py_DECREF(py_ann_rows);
+ PyGILState_Release(gstate);
+
+ return SRD_OK;
+
+except_out:
+ srd_exception_catch("Failed to get %s decoder annotation rows",
+ dec->name);
+
+err_out:
+ g_slist_free_full(annotation_rows, &annotation_row_free);
+ Py_XDECREF(py_ann_rows);
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
+/* Convert binary classes to GSList of char **. */
+static int get_binary_classes(struct srd_decoder *dec)
+{
+ PyObject *py_bin_classes, *py_bin_class;
+ GSList *bin_classes;
+ char **bin;
+ ssize_t i;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ if (!PyObject_HasAttrString(dec->py_dec, "binary")) {
+ PyGILState_Release(gstate);
+ return SRD_OK;
+ }
+
+ bin_classes = NULL;
+
+ py_bin_classes = PyObject_GetAttrString(dec->py_dec, "binary");
+ if (!py_bin_classes)
+ goto except_out;
+
+ if (!PyTuple_Check(py_bin_classes)) {
+ srd_err("Protocol decoder %s binary classes should "
+ "be a tuple.", dec->name);
+ goto err_out;
+ }
+
+ for (i = PyTuple_Size(py_bin_classes) - 1; i >= 0; i--) {
+ py_bin_class = PyTuple_GetItem(py_bin_classes, i);
+ if (!py_bin_class)
+ goto except_out;
+
+ if (!PyTuple_Check(py_bin_class)
+ || PyTuple_Size(py_bin_class) != 2) {
+ srd_err("Protocol decoder %s binary classes should "
+ "consist only of tuples of 2 elements.",
+ dec->name);
+ goto err_out;
+ }
+ if (py_strseq_to_char(py_bin_class, &bin) != SRD_OK)
+ goto err_out;
+
+ bin_classes = g_slist_prepend(bin_classes, bin);
+ }
+ dec->binary = bin_classes;
+ Py_DECREF(py_bin_classes);
+ PyGILState_Release(gstate);
+
+ return SRD_OK;
+
+except_out:
+ srd_exception_catch("Failed to get %s decoder binary classes",
+ dec->name);
+
+err_out:
+ g_slist_free_full(bin_classes, (GDestroyNotify)&g_strfreev);
+ Py_XDECREF(py_bin_classes);
+ PyGILState_Release(gstate);
+
+ return SRD_ERR_PYTHON;
+}
+
+/* Check whether the Decoder class defines the named method. */
+static int check_method(PyObject *py_dec, const char *mod_name,
+ const char *method_name)
+{
+ PyObject *py_method;
+ int is_callable;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ py_method = PyObject_GetAttrString(py_dec, method_name);
+ if (!py_method) {
+ srd_exception_catch("Protocol decoder %s Decoder class "
+ "has no %s() method", mod_name, method_name);
+ PyGILState_Release(gstate);
+ return SRD_ERR_PYTHON;
+ }
+
+ is_callable = PyCallable_Check(py_method);
+ Py_DECREF(py_method);
+
+ PyGILState_Release(gstate);
+
+ if (!is_callable) {
+ srd_err("Protocol decoder %s Decoder class attribute '%s' "
+ "is not a method.", mod_name, method_name);
+ return SRD_ERR_PYTHON;
+ }
+
+ return SRD_OK;
+}
+
+/**
+ * Get the API version of the specified decoder.
+ *
+ * @param d The decoder to use. Must not be NULL.
+ *
+ * @return The API version of the decoder, or 0 upon errors.
+ *
+ * @private
+ */
+SRD_PRIV long srd_decoder_apiver(const struct srd_decoder *d)
+{
+ PyObject *py_apiver;
+ long apiver;
+ PyGILState_STATE gstate;
+
+ if (!d)
+ return 0;
+
+ gstate = PyGILState_Ensure();
+
+ py_apiver = PyObject_GetAttrString(d->py_dec, "api_version");
+ apiver = (py_apiver && PyLong_Check(py_apiver))
+ ? PyLong_AsLong(py_apiver) : 0;
+ Py_XDECREF(py_apiver);
+
+ PyGILState_Release(gstate);
+
+ return apiver;
+}
+
+static gboolean contains_duplicates(GSList *list)
+{
+ for (GSList *l1 = list; l1; l1 = l1->next) {
+ for (GSList *l2 = l1->next; l2; l2 = l2->next)
+ if (!strcmp(l1->data, l2->data))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean contains_duplicate_ids(GSList *list1, GSList *list2)
+{
+ for (GSList *l1 = list1; l1; l1 = l1->next) {
+ unsigned int cnt = 0;
+ const char **s1 = l1->data;
+ for (GSList *l2 = list2; l2; l2 = l2->next) {
+ const char **s2 = l2->data;
+ if (!strcmp(s1[0], s2[0]))
+ cnt++;
+ if ((list1 == list2) && cnt > 1)
+ return TRUE;
+ if ((list1 != list2) && cnt > 0)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean contains_duplicate_row_ids(GSList *list1, GSList *list2)
+{
+ for (GSList *l1 = list1; l1; l1 = l1->next) {
+ unsigned int cnt = 0;
+ const struct srd_decoder_annotation_row *r1 = l1->data;
+ for (GSList *l2 = list2; l2; l2 = l2->next) {
+ const struct srd_decoder_annotation_row *r2 = l2->data;
+ if (!strcmp(r1->id, r2->id))
+ cnt++;
+ if (cnt > 1)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}