/src/clamav/libclamav/table.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2013-2024 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 * |
42 | | tableCreate(void) |
43 | 36.3k | { |
44 | 36.3k | return (struct table *)calloc(1, sizeof(struct table)); |
45 | 36.3k | } |
46 | | |
47 | | void tableDestroy(table_t *table) |
48 | 36.3k | { |
49 | 36.3k | tableEntry *tableItem; |
50 | | |
51 | 36.3k | assert(table != NULL); |
52 | | |
53 | 36.3k | tableItem = table->tableHead; |
54 | | |
55 | 108k | while (tableItem) { |
56 | 72.6k | tableEntry *tableNext = tableItem->next; |
57 | | |
58 | 72.6k | if (tableItem->key) |
59 | 72.6k | free(tableItem->key); |
60 | 72.6k | free(tableItem); |
61 | | |
62 | 72.6k | tableItem = tableNext; |
63 | 72.6k | } |
64 | | |
65 | 36.3k | free(table); |
66 | 36.3k | } |
67 | | |
68 | | /* |
69 | | * Returns the value, or -1 for failure |
70 | | */ |
71 | | int tableInsert(table_t *table, const char *key, int value) |
72 | 72.7k | { |
73 | 72.7k | const int v = tableFind(table, key); |
74 | | |
75 | 72.7k | if (v > 0) /* duplicate key */ |
76 | 0 | return (v == value) ? value : -1; /* allow real dups */ |
77 | | |
78 | 72.7k | assert(value != -1); /* that would confuse us */ |
79 | | |
80 | 72.7k | if (table->tableHead == NULL) |
81 | 36.3k | table->tableLast = table->tableHead = (tableEntry *)malloc(sizeof(tableEntry)); |
82 | 36.4k | else { |
83 | | /* |
84 | | * Re-use deleted items |
85 | | */ |
86 | 36.4k | if (table->flags & TABLE_HAS_DELETED_ENTRIES) { |
87 | 0 | tableEntry *tableItem; |
88 | |
|
89 | 0 | assert(table->tableHead != NULL); |
90 | | |
91 | 0 | for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next) |
92 | 0 | if (tableItem->key == NULL) { |
93 | | /* This item has been deleted */ |
94 | 0 | tableItem->key = cli_safer_strdup(key); |
95 | 0 | tableItem->value = value; |
96 | 0 | return value; |
97 | 0 | } |
98 | | |
99 | 0 | table->flags &= ~TABLE_HAS_DELETED_ENTRIES; |
100 | 0 | } |
101 | | |
102 | 36.4k | table->tableLast = table->tableLast->next = |
103 | 36.4k | (tableEntry *)malloc(sizeof(tableEntry)); |
104 | 36.4k | } |
105 | | |
106 | 72.7k | if (table->tableLast == NULL) { |
107 | 0 | cli_dbgmsg("tableInsert: Unable to allocate memory for table\n"); |
108 | 0 | return -1; |
109 | 0 | } |
110 | | |
111 | 72.7k | table->tableLast->next = NULL; |
112 | 72.7k | table->tableLast->key = cli_safer_strdup(key); |
113 | 72.7k | table->tableLast->value = value; |
114 | | |
115 | 72.7k | return value; |
116 | 72.7k | } |
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 | | */ |
125 | | int tableFind(const table_t *table, const char *key) |
126 | 19.6M | { |
127 | 19.6M | const tableEntry *tableItem; |
128 | | #ifdef CL_DEBUG |
129 | | int cost; |
130 | | #endif |
131 | | |
132 | 19.6M | assert(table != NULL); |
133 | | |
134 | 19.6M | if (key == NULL) |
135 | 0 | return -1; /* not treated as a fatal error */ |
136 | | |
137 | | #ifdef CL_DEBUG |
138 | | cost = 0; |
139 | | #endif |
140 | | |
141 | 90.0M | for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next) { |
142 | | #ifdef CL_DEBUG |
143 | | cost++; |
144 | | #endif |
145 | 79.3M | 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 | 8.89M | return tableItem->value; |
150 | 8.89M | } |
151 | 79.3M | } |
152 | | |
153 | 10.7M | return -1; /* not found */ |
154 | 19.6M | } |
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 | | */ |
160 | | int tableUpdate(table_t *table, const char *key, int new_value) |
161 | 0 | { |
162 | 0 | tableEntry *tableItem; |
163 | |
|
164 | 0 | assert(table != NULL); |
165 | | |
166 | 0 | if (key == NULL) |
167 | 0 | return -1; /* not treated as a fatal error */ |
168 | | |
169 | 0 | for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next) |
170 | 0 | if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) { |
171 | 0 | tableItem->value = new_value; |
172 | 0 | return new_value; |
173 | 0 | } |
174 | | |
175 | | /* not found */ |
176 | 0 | return tableInsert(table, key, new_value); |
177 | 0 | } |
178 | | |
179 | | /* |
180 | | * Remove an item from the table |
181 | | */ |
182 | | void tableRemove(table_t *table, const char *key) |
183 | 0 | { |
184 | 0 | tableEntry *tableItem; |
185 | |
|
186 | 0 | assert(table != NULL); |
187 | | |
188 | 0 | if (key == NULL) |
189 | 0 | return; /* not treated as a fatal error */ |
190 | | |
191 | 0 | for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next) |
192 | 0 | if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) { |
193 | 0 | free(tableItem->key); |
194 | 0 | tableItem->key = NULL; |
195 | 0 | table->flags |= TABLE_HAS_DELETED_ENTRIES; |
196 | | /* don't break, duplicate keys are allowed */ |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | void tableIterate(table_t *table, void (*callback)(char *key, int value, void *arg), void *arg) |
201 | 0 | { |
202 | 0 | tableEntry *tableItem; |
203 | |
|
204 | 0 | if (table == NULL) |
205 | 0 | return; |
206 | | |
207 | 0 | for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next) |
208 | 0 | if (tableItem->key) /* check node has not been deleted */ |
209 | 0 | (*callback)(tableItem->key, tableItem->value, arg); |
210 | 0 | } |