libclamav/table.c
e3aaff8e
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
2023340a
  *
  *  Authors: Nigel Horne
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
2023340a
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
e3aaff8e
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
23a5e3b7
  *
  * TODO: Allow individual items to be updated or removed
  *
  * It is up to the caller to create a mutex for the table if needed
e3aaff8e
  */
 
7c5a7a47
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdlib.h>
 #include <string.h>
288057e9
 #ifdef HAVE_STRINGS_H
e3aaff8e
 #include <strings.h>
bc6bbeff
 #endif
e3aaff8e
 #include <assert.h>
 
60d8d2c3
 #include "clamav.h"
e3aaff8e
 #include "table.h"
 #include "others.h"
 
 struct table *
 tableCreate(void)
 {
288057e9
     return (struct table *)cli_calloc(1, sizeof(struct table));
e3aaff8e
 }
 
288057e9
 void tableDestroy(table_t *table)
e3aaff8e
 {
288057e9
     tableEntry *tableItem;
e3aaff8e
 
288057e9
     assert(table != NULL);
e3aaff8e
 
288057e9
     tableItem = table->tableHead;
e3aaff8e
 
288057e9
     while (tableItem) {
         tableEntry *tableNext = tableItem->next;
e3aaff8e
 
288057e9
         if (tableItem->key)
             free(tableItem->key);
         free(tableItem);
e3aaff8e
 
288057e9
         tableItem = tableNext;
     }
e3aaff8e
 
288057e9
     free(table);
e3aaff8e
 }
 
 /*
  * Returns the value, or -1 for failure
  */
288057e9
 int tableInsert(table_t *table, const char *key, int value)
e3aaff8e
 {
288057e9
     const int v = tableFind(table, key);
e3aaff8e
 
288057e9
     if (v > 0)                            /* duplicate key */
         return (v == value) ? value : -1; /* allow real dups */
e3aaff8e
 
288057e9
     assert(value != -1); /* that would confuse us */
e3aaff8e
 
288057e9
     if (table->tableHead == NULL)
         table->tableLast = table->tableHead = (tableEntry *)cli_malloc(sizeof(tableEntry));
     else {
         /*
821dd71e
 		 * Re-use deleted items
 		 */
288057e9
         if (table->flags & TABLE_HAS_DELETED_ENTRIES) {
             tableEntry *tableItem;
821dd71e
 
288057e9
             assert(table->tableHead != NULL);
821dd71e
 
288057e9
             for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
                 if (tableItem->key == NULL) {
                     /* This item has been deleted */
                     tableItem->key   = cli_strdup(key);
                     tableItem->value = value;
                     return value;
                 }
821dd71e
 
288057e9
             table->flags &= ~TABLE_HAS_DELETED_ENTRIES;
         }
821dd71e
 
288057e9
         table->tableLast = table->tableLast->next =
             (tableEntry *)cli_malloc(sizeof(tableEntry));
     }
e3aaff8e
 
288057e9
     if (table->tableLast == NULL) {
241e7eb1
         cli_dbgmsg("tableInsert: Unable to allocate memory for table\n");
288057e9
         return -1;
241e7eb1
     }
767f16ab
 
288057e9
     table->tableLast->next  = NULL;
     table->tableLast->key   = cli_strdup(key);
     table->tableLast->value = value;
e3aaff8e
 
288057e9
     return value;
e3aaff8e
 }
 
 /*
23a5e3b7
  * Returns the value - -1 for not found. This means the value of a valid key
  *	can't be -1 :-(
9fe789f8
  *
  * Linear search. Since tables are rarely more than 3 or 4 in size, and never
7cd9337a
  *	reach double figures, there's no need for optimization
e3aaff8e
  */
288057e9
 int tableFind(const table_t *table, const char *key)
e3aaff8e
 {
288057e9
     const tableEntry *tableItem;
 #ifdef CL_DEBUG
     int cost;
7c5a7a47
 #endif
e3aaff8e
 
288057e9
     assert(table != NULL);
e3aaff8e
 
288057e9
     if (key == NULL)
         return -1; /* not treated as a fatal error */
e3aaff8e
 
288057e9
 #ifdef CL_DEBUG
     cost = 0;
27328356
 #endif
7c5a7a47
 
288057e9
     for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next) {
 #ifdef CL_DEBUG
         cost++;
7c5a7a47
 #endif
288057e9
         if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
 #ifdef CL_DEBUG
             cli_dbgmsg("tableFind: Cost of '%s' = %d\n", key, cost);
7c5a7a47
 #endif
288057e9
             return tableItem->value;
         }
     }
e3aaff8e
 
288057e9
     return -1; /* not found */
e3aaff8e
 }
23a5e3b7
 
 /*
  * Change a value in the table. If the key isn't in the table insert it
  * Returns -1 for error, otherwise the new value
  */
288057e9
 int tableUpdate(table_t *table, const char *key, int new_value)
23a5e3b7
 {
288057e9
     tableEntry *tableItem;
23a5e3b7
 
288057e9
     assert(table != NULL);
23a5e3b7
 
288057e9
     if (key == NULL)
         return -1; /* not treated as a fatal error */
23a5e3b7
 
288057e9
     for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
         if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
             tableItem->value = new_value;
             return new_value;
         }
23a5e3b7
 
288057e9
     /* not found */
     return tableInsert(table, key, new_value);
23a5e3b7
 }
b650cc71
 
 /*
  * Remove an item from the table
  */
288057e9
 void tableRemove(table_t *table, const char *key)
b650cc71
 {
288057e9
     tableEntry *tableItem;
b650cc71
 
288057e9
     assert(table != NULL);
b650cc71
 
288057e9
     if (key == NULL)
         return; /* not treated as a fatal error */
b650cc71
 
288057e9
     for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
         if (tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
             free(tableItem->key);
             tableItem->key = NULL;
             table->flags |= TABLE_HAS_DELETED_ENTRIES;
             /* don't break, duplicate keys are allowed */
         }
b650cc71
 }
 
288057e9
 void tableIterate(table_t *table, void (*callback)(char *key, int value, void *arg), void *arg)
b650cc71
 {
288057e9
     tableEntry *tableItem;
b650cc71
 
288057e9
     if (table == NULL)
         return;
b650cc71
 
288057e9
     for (tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
         if (tableItem->key) /* check node has not been deleted */
             (*callback)(tableItem->key, tableItem->value, arg);
b650cc71
 }