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