/* lookup.c --
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 *
 * This file contains a single routine used to look up a string in
 * a table, allowing unique abbreviations.
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/lookup.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <ctype.h>

#include "utils/magic.h"
#include "utils/utils.h"


/*---------------------------------------------------------
 * Lookup --
 *	Searches a table of strings to find one that matches a given
 *	string.  It's useful mostly for command lookup.
 *
 *	Only the portion of a string in the table up to the first
 *	blank character is considered significant for matching.
 *
 * Results:
 *	If str is the same as
 *      or an unambiguous abbreviation for one of the entries
 *	in table, then the index of the matching entry is returned.
 *	If str is not the same as any entry in the table, but
 *      an abbreviation for more than one entry,
 *	then -1 is returned.  If str doesn't match any entry, then
 *	-2 is returned.  Case differences are ignored.
 *
 * NOTE:
 *      Table entries need no longer be in alphabetical order
 *      and they need not be lower case.  The irouter command parsing
 *      depends on these features.
 *
 * Side Effects:
 *	None.
 *---------------------------------------------------------
 */

int
Lookup(str, table)
const char *str;		/* Pointer to a string to be looked up */
const char * const *table;	/* Pointer to an array of string pointers
				 * which are the valid commands.
				 * The end of
				 * the table is indicated by a NULL string.
				 */
{
    int match = -2;	/* result, initialized to -2 = no match */
    int pos;
    int ststart = 0;

#ifdef MAGIC_WRAPPER
    static const char *namespace = "::magic::";

    /* Skip over prefix of qualified namespaces "::magic::" and "magic::" */
    for (pos = 0; pos < 9; pos++)
	if ((str[pos] != namespace[pos]) || (str[pos] == '\0')) break;
    if (pos == 9) ststart = 9;
    else
    {
	for (pos = 0; pos < 7; pos++)
	    if ((str[pos] != namespace[pos + 2]) || (str[pos] == '\0')) break;
	if (pos == 7) ststart = 7;
    }
#endif

    /* search for match */
    for(pos=0; table[pos]!=NULL; pos++)
    {
	const char *tabc = table[pos];
	const char *strc = &(str[ststart]);
	while(*strc!='\0' && *tabc!=' ' &&
	    ((*tabc==*strc) ||
	     (isupper(*tabc) && islower(*strc) && (tolower(*tabc)== *strc))||
	     (islower(*tabc) && isupper(*strc) && (toupper(*tabc)== *strc)) ))
	{
	    strc++;
	    tabc++;
	}


	if(*strc=='\0')
	{
	    /* entry matches */
	    if(*tabc==' ' || *tabc=='\0')
	    {
		/* exact match - record it and terminate search */
		match = pos;
		break;
	    }
	    else if (match == -2)
	    {
		/* inexact match and no previous match - record this one
		 * and continue search */
		match = pos;
	    }
	    else
	    {
		/* previous match, so string is ambiguous unless exact
		 * match exists.  Mark ambiguous for now, and continue
		 * search.
		 */
		match = -1;
	    }
	}
    }
    return(match);
}


/*---------------------------------------------------------
 *
 * LookupStruct --
 *
 * Searches a table of structures, each of which contains a string
 * pointer as its first element, in a manner similar to that of Lookup()
 * above.  Each structure in the table has the following form:
 *
 *	struct
 *	{
 *		const char *string;
 *		... rest of structure
 *	};
 *
 * The 'string' field of each structure is matched against the
 * argument 'str'.  The size of a single structure is given by
 * the argument 'size'.
 *
 * Results:
 *	If str is the same as
 *      or an unambiguous abbreviation for one of the entries
 *	in table, then the index of the matching entry is returned.
 *	If str is not the same as any entry in the table, but
 *      an abbreviation for more than one entry,
 *	then -1 is returned.  If str doesn't match any entry, then
 *	-2 is returned.  Case differences are ignored.
 *
 *      NOTE:  Table entries need no longer be in alphabetical order
 *      and they need not be lower case.  The irouter command parsing
 *      depends on these features.
 *
 * Side Effects:
 *	None.
 *
 *---------------------------------------------------------
 */

int
LookupStruct(str, table_start, size)
    const char *str;	/* Pointer to a string to be looked up */
    const LookupTable *table_start;
			/* Pointer to an array of structs containing string
			 * pointers to valid commands.
			 * The last table entry should have a NULL
			 * string pointer.
			 */
    int	size;		/* The size, in bytes, of each table entry */
{
    const char * const *table = (const char * const *)table_start;
    int match = -2;	/* result, initialized to -2 = no match */
    const char * const *entry;
    int pos;
    const char *tabc , *strc ;

    /* search for match */
    for (entry = table, pos = 0; *entry != NULL; )
    {
	tabc = *entry;
	strc = str;
	while(*strc!='\0' && *tabc!=' ' &&
	    ((*tabc== *strc) ||
	     (isupper(*tabc) && islower(*strc) && (tolower(*tabc)== *strc))||
	     (islower(*tabc) && isupper(*strc) && (toupper(*tabc)== *strc)) ))
	{
	    strc++;
	    tabc++;
	}

	if(*strc=='\0')
	{
	    /* entry matches */
	    if(*tabc==' ' || *tabc=='\0')
	    {
		/* exact match - record it and terminate search */
		match = pos;
		break;
	    }
	    else if (match == -2)
	    {
		/* in exact match and no previous match - record this one
		 * and continue search */
		match = pos;
	    }
	    else
	    {
		/* previous match, so string is ambiguous unless exact
		 * match exists.  Mark ambiguous for now, and continue
		 * search.
		 */
		match = -1;
	    }
	}
	pos++;
	entry = (const char **)((pointertype)entry + (pointertype)size);
    }
    return(match);
}

