libclamav/table.c
e3aaff8e
 /*
c442ca9c
  *  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>
bc6bbeff
 #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)
 {
 	return (struct table *)cli_calloc(1, sizeof(struct table));
 }
 
 void
 tableDestroy(table_t *table)
 {
 	tableEntry *tableItem;
 
 	assert(table != NULL);
 
 	tableItem = table->tableHead;
 
 	while(tableItem) {
 		tableEntry *tableNext = tableItem->next;
 
b650cc71
 		if(tableItem->key)
 			free(tableItem->key);
e3aaff8e
 		free(tableItem);
 
 		tableItem = tableNext;
 	}
 
 	free(table);
 }
 
 /*
  * Returns the value, or -1 for failure
  */
 int
 tableInsert(table_t *table, const char *key, int value)
 {
 	const int v = tableFind(table, key);
 
 	if(v > 0)	/* duplicate key */
 		return (v == value) ? value : -1;	/* allow real dups */
 
 	assert(value != -1);	/* that would confuse us */
 
 	if(table->tableHead == NULL)
496e1116
 		table->tableLast = table->tableHead = (tableEntry *)cli_malloc(sizeof(tableEntry));
821dd71e
 	else {
 		/*
 		 * Re-use deleted items
 		 */
 		if(table->flags&TABLE_HAS_DELETED_ENTRIES) {
 			tableEntry *tableItem;
 
 			assert(table->tableHead != NULL);
 
 			for(tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
 				if(tableItem->key == NULL) {
 					/* This item has been deleted */
8797c784
 					tableItem->key = cli_strdup(key);
821dd71e
 					tableItem->value = value;
 					return value;
 				}
 
 			table->flags &= ~TABLE_HAS_DELETED_ENTRIES;
 		}
 
51fc2aa8
 		table->tableLast = table->tableLast->next =
496e1116
 			(tableEntry *)cli_malloc(sizeof(tableEntry));
821dd71e
 	}
e3aaff8e
 
241e7eb1
 	if(table->tableLast == NULL) {
         cli_dbgmsg("tableInsert: Unable to allocate memory for table\n");
767f16ab
 		return -1;
241e7eb1
     }
767f16ab
 
e3aaff8e
 	table->tableLast->next = NULL;
8797c784
 	table->tableLast->key = cli_strdup(key);
e3aaff8e
 	table->tableLast->value = value;
 
 	return value;
 }
 
 /*
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
  */
 int
 tableFind(const table_t *table, const char *key)
 {
 	const tableEntry *tableItem;
7c5a7a47
 #ifdef	CL_DEBUG
 	int cost;
 #endif
e3aaff8e
 
 	assert(table != NULL);
 
 	if(key == NULL)
 		return -1;	/* not treated as a fatal error */
 
27328356
 #ifdef	CL_DEBUG
7c5a7a47
 	cost = 0;
27328356
 #endif
7c5a7a47
 
 	for(tableItem = table->tableHead; tableItem; tableItem = tableItem->next) {
 #ifdef	CL_DEBUG
 		cost++;
 #endif
21e3c1e2
 		if(tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
7c5a7a47
 #ifdef	CL_DEBUG
 			cli_dbgmsg("tableFind: Cost of '%s' = %d\n", key, cost);
 #endif
496e1116
 			return tableItem->value;
7c5a7a47
 		}
 	}
e3aaff8e
 
 	return -1;	/* not found */
 }
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
  */
 int
 tableUpdate(table_t *table, const char *key, int new_value)
 {
 	tableEntry *tableItem;
 
 	assert(table != NULL);
 
 	if(key == NULL)
 		return -1;	/* not treated as a fatal error */
 
 	for(tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
21e3c1e2
 		if(tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
23a5e3b7
 			tableItem->value = new_value;
 			return new_value;
 		}
 
 	/* not found */
 	return tableInsert(table, key, new_value);
 }
b650cc71
 
 /*
  * Remove an item from the table
  */
 void
 tableRemove(table_t *table, const char *key)
 {
 	tableEntry *tableItem;
 
 	assert(table != NULL);
 
 	if(key == NULL)
 		return;	/* not treated as a fatal error */
 
 	for(tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
21e3c1e2
 		if(tableItem->key && (strcasecmp(tableItem->key, key) == 0)) {
b650cc71
 			free(tableItem->key);
 			tableItem->key = NULL;
 			table->flags |= TABLE_HAS_DELETED_ENTRIES;
 			/* don't break, duplicate keys are allowed */
 		}
 }
 
 void
93928eab
 tableIterate(table_t *table, void(*callback)(char *key, int value, void *arg), void *arg)
b650cc71
 {
 	tableEntry *tableItem;
 
 	if(table == NULL)
 		return;
 
 	for(tableItem = table->tableHead; tableItem; tableItem = tableItem->next)
821dd71e
 		if(tableItem->key)	/* check node has not been deleted */
93928eab
 			(*callback)(tableItem->key, tableItem->value, arg);
b650cc71
 }