1 /*
2 * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3 * Copyright (C) 2007-2013 Sourcefire, Inc.
4 *
5 * Authors: Nigel Horne
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
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 Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 *
21 * TODO: Allow individual items to be updated or removed
22 *
23 * It is up to the caller to create a mutex for the table if needed
24 */
25
26 #if HAVE_CONFIG_H
27 #include "clamav-config.h"
28 #endif
29
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_STRINGS_H
33 #include <strings.h>
34 #endif
35 #include <assert.h>
36
37 #include "clamav.h"
38 #include "table.h"
39 #include "others.h"
40
41 struct table *
tableCreate(void)42 tableCreate(void)
43 {
44 return (struct table *)cli_calloc(1, sizeof(struct table));
45 }
46
tableDestroy(table_t * table)47 void tableDestroy(table_t *table)
48 {
49 tableEntry *tableItem;
50
51 assert(table != NULL);
52
53 tableItem = table->tableHead;
54
55 while (tableItem) {
56 tableEntry *tableNext = tableItem->next;
57
58 if (tableItem->key)
59 free(tableItem->key);
60 free(tableItem);
61
62 tableItem = tableNext;
63 }
64
65 free(table);
66 }
67
68 /*
69 * Returns the value, or -1 for failure
70 */
tableInsert(table_t * table,const char * key,int value)71 int tableInsert(table_t *table, const char *key, int value)
72 {
73 const int v = tableFind(table, key);
74
75 if (v > 0) /* duplicate key */
76 return (v == value) ? value : -1; /* allow real dups */
77
78 assert(value != -1); /* that would confuse us */
79
80 if (table->tableHead == NULL)
81 table->tableLast = table->tableHead = (tableEntry *)cli_malloc(sizeof(tableEntry));
82 else {
83 /*
84 * Re-use deleted items
85 */
86 if (table->flags & TABLE_HAS_DELETED_ENTRIES) {
87 tableEntry *tableItem;
88
89 assert(table->tableHead != NULL);
90
91 for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
92 if (tableItem->key == NULL) {
93 /* This item has been deleted */
94 tableItem->key = cli_strdup(key);
95 tableItem->value = value;
96 return value;
97 }
98
99 table->flags &= ~TABLE_HAS_DELETED_ENTRIES;
100 }
101
102 table->tableLast = table->tableLast->next =
103 (tableEntry *)cli_malloc(sizeof(tableEntry));
104 }
105
106 if (table->tableLast == NULL) {
107 cli_dbgmsg("tableInsert: Unable to allocate memory for table\n");
108 return -1;
109 }
110
111 table->tableLast->next = NULL;
112 table->tableLast->key = cli_strdup(key);
113 table->tableLast->value = value;
114
115 return value;
116 }
117
118 /*
119 * Returns the value - -1 for not found. This means the value of a valid key
120 * can't be -1 :-(
121 *
122 * Linear search. Since tables are rarely more than 3 or 4 in size, and never
123 * reach double figures, there's no need for optimization
124 */
tableFind(const table_t * table,const char * key)125 int tableFind(const table_t *table, const char *key)
126 {
127 const tableEntry *tableItem;
128 #ifdef CL_DEBUG
129 int cost;
130 #endif
131
132 assert(table != NULL);
133
134 if (key == NULL)
135 return -1; /* not treated as a fatal error */
136
137 #ifdef CL_DEBUG
138 cost = 0;
139 #endif
140
141 for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next) {
142 #ifdef CL_DEBUG
143 cost++;
144 #endif
145 if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
146 #ifdef CL_DEBUG
147 cli_dbgmsg("tableFind: Cost of '%s' = %d\n", key, cost);
148 #endif
149 return tableItem->value;
150 }
151 }
152
153 return -1; /* not found */
154 }
155
156 /*
157 * Change a value in the table. If the key isn't in the table insert it
158 * Returns -1 for error, otherwise the new value
159 */
tableUpdate(table_t * table,const char * key,int new_value)160 int tableUpdate(table_t *table, const char *key, int new_value)
161 {
162 tableEntry *tableItem;
163
164 assert(table != NULL);
165
166 if (key == NULL)
167 return -1; /* not treated as a fatal error */
168
169 for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
170 if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
171 tableItem->value = new_value;
172 return new_value;
173 }
174
175 /* not found */
176 return tableInsert(table, key, new_value);
177 }
178
179 /*
180 * Remove an item from the table
181 */
tableRemove(table_t * table,const char * key)182 void tableRemove(table_t *table, const char *key)
183 {
184 tableEntry *tableItem;
185
186 assert(table != NULL);
187
188 if (key == NULL)
189 return; /* not treated as a fatal error */
190
191 for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
192 if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
193 free(tableItem->key);
194 tableItem->key = NULL;
195 table->flags |= TABLE_HAS_DELETED_ENTRIES;
196 /* don't break, duplicate keys are allowed */
197 }
198 }
199
tableIterate(table_t * table,void (* callback)(char * key,int value,void * arg),void * arg)200 void tableIterate(table_t *table, void (*callback)(char *key, int value, void *arg), void *arg)
201 {
202 tableEntry *tableItem;
203
204 if (table == NULL)
205 return;
206
207 for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
208 if (tableItem->key) /* check node has not been deleted */
209 (*callback)(tableItem->key, tableItem->value, arg);
210 }
211