1 /*
2  * disco.c
3  * vim: expandtab:ts=4:sts=4:sw=4
4  *
5  * Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
6  *
7  * This file is part of Profanity.
8  *
9  * Profanity is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Profanity is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Profanity.  If not, see <https://www.gnu.org/licenses/>.
21  *
22  * In addition, as a special exception, the copyright holders give permission to
23  * link the code of portions of this program with the OpenSSL library under
24  * certain conditions as described in each individual source file, and
25  * distribute linked combinations including the two.
26  *
27  * You must obey the GNU General Public License in all respects for all of the
28  * code used other than OpenSSL. If you modify file(s) with this exception, you
29  * may extend this exception to your version of the file(s), but you are not
30  * obligated to do so. If you do not wish to do so, delete this exception
31  * statement from your version. If you delete this exception statement from all
32  * source files in the program, then also delete it here.
33  *
34  */
35 
36 #include "config.h"
37 
38 #include <string.h>
39 #include <stdlib.h>
40 
41 #include <glib.h>
42 
43 // features to reference count map
44 static GHashTable* features = NULL;
45 
46 // plugin to feature map
47 static GHashTable* plugin_to_features = NULL;
48 
49 static void
_free_features(GHashTable * features)50 _free_features(GHashTable* features)
51 {
52     g_hash_table_destroy(features);
53 }
54 
55 void
disco_add_feature(const char * plugin_name,char * feature)56 disco_add_feature(const char* plugin_name, char* feature)
57 {
58     if (feature == NULL || plugin_name == NULL) {
59         return;
60     }
61 
62     if (!features) {
63         features = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
64     }
65     if (!plugin_to_features) {
66         plugin_to_features = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_features);
67     }
68 
69     GHashTable* plugin_features = g_hash_table_lookup(plugin_to_features, plugin_name);
70     gboolean added = FALSE;
71     if (plugin_features == NULL) {
72         plugin_features = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
73         g_hash_table_add(plugin_features, strdup(feature));
74         g_hash_table_insert(plugin_to_features, strdup(plugin_name), plugin_features);
75         added = TRUE;
76     } else if (!g_hash_table_contains(plugin_features, feature)) {
77         g_hash_table_add(plugin_features, strdup(feature));
78         added = TRUE;
79     }
80 
81     if (added == FALSE) {
82         return;
83     }
84 
85     if (!g_hash_table_contains(features, feature)) {
86         g_hash_table_insert(features, strdup(feature), GINT_TO_POINTER(1));
87     } else {
88         void* refcountp = g_hash_table_lookup(features, feature);
89         int refcount = GPOINTER_TO_INT(refcountp);
90         refcount++;
91         g_hash_table_replace(features, strdup(feature), GINT_TO_POINTER(refcount));
92     }
93 }
94 
95 void
disco_remove_features(const char * plugin_name)96 disco_remove_features(const char* plugin_name)
97 {
98     if (!features) {
99         return;
100     }
101     if (!plugin_to_features) {
102         return;
103     }
104 
105     GHashTable* plugin_features_set = g_hash_table_lookup(plugin_to_features, plugin_name);
106     if (!plugin_features_set) {
107         return;
108     }
109 
110     GList* plugin_feature_list = g_hash_table_get_keys(plugin_features_set);
111     GList* curr = plugin_feature_list;
112     while (curr) {
113         char* feature = curr->data;
114         if (g_hash_table_contains(features, feature)) {
115             void* refcountp = g_hash_table_lookup(features, feature);
116             int refcount = GPOINTER_TO_INT(refcountp);
117             if (refcount == 1) {
118                 g_hash_table_remove(features, feature);
119             } else {
120                 refcount--;
121                 g_hash_table_replace(features, strdup(feature), GINT_TO_POINTER(refcount));
122             }
123         }
124 
125         curr = g_list_next(curr);
126     }
127     g_list_free(plugin_feature_list);
128 }
129 
130 GList*
disco_get_features(void)131 disco_get_features(void)
132 {
133     if (features == NULL) {
134         return NULL;
135     }
136 
137     return g_hash_table_get_keys(features);
138 }
139 
140 void
disco_close(void)141 disco_close(void)
142 {
143     if (features) {
144         g_hash_table_destroy(features);
145         features = NULL;
146     }
147 
148     if (plugin_to_features) {
149         g_hash_table_destroy(plugin_to_features);
150         plugin_to_features = NULL;
151     }
152 }
153