root/usr/src/lib/libeti/form/common/ty_enum.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*      Copyright (c) 1988 AT&T */
/*        All Rights Reserved   */


/*
 *      Copyright (c) 1997, by Sun Microsystems, Inc.
 *      All rights reserved.
 */

/*LINTLIBRARY*/

#include <sys/types.h>
#include <stdlib.h>
#include "utility.h"

/*
 *      TYPE_ENUM standard type
 *
 *      usage:
 *              set_field_type(f, TYPE_ENUM, list, checkcase, checkuniq);
 *
 *              char ** list;   list of acceptable strings
 *              int checkcase;  TRUE - upper/lower case is significant
 *              int checkuniq;  TRUE - unique match required
 *
 */
typedef struct {

        char ** list;
        int     checkcase;
        int     checkuniq;
        int     count;
} ENUM;

static char * make_enum(va_list *);
static char * copy_enum(char *);
static void free_enum(char *);
static int fcheck_enum(FIELD *, char *);
static int next_enum(FIELD *, char *);
static int prev_enum(FIELD *, char *);

static FIELDTYPE typeENUM =
{
                                ARGS | CHOICE,          /* status       */
                                1,                      /* ref          */
                                (FIELDTYPE *) 0,        /* left         */
                                (FIELDTYPE *) 0,        /* right        */
                                make_enum,              /* makearg      */
                                copy_enum,              /* copyarg      */
                                free_enum,              /* freearg      */
                                fcheck_enum,            /* fcheck       */
                                (PTF_int) 0,            /* ccheck       */
                                next_enum,              /* next         */
                                prev_enum,              /* prev         */
};

FIELDTYPE * TYPE_ENUM = &typeENUM;

static char *
make_enum(va_list *ap)
{
        ENUM * n;

        if (Alloc(n, ENUM)) {
                char **         v;

                n -> list       = va_arg(*ap, char **);
                n -> checkcase  = va_arg(*ap, int);
                n -> checkuniq  = va_arg(*ap, int);

                for (v = n -> list; *v; ++v)
                        ;
                n -> count = (int) (v - n -> list);
        }
        return ((char *) n);
}

static char *
copy_enum(char *arg)
{
        ENUM * n;

        if (Alloc(n, ENUM))
                *n = *((ENUM *) arg);
        return ((char *) n);
}

static void
free_enum(char *arg)
{
        Free(arg);
}

#define NO_MATCH                0
#define PARTIAL_MATCH           1
#define EXACT_MATCH             2

static int
cmp(char *x, char *v, int checkcase)
{
        while (*v && *v == ' ')                 /* remove leading blanks */
                ++v;
        while (*x && *x == ' ')                 /* remove leading blanks */
                ++x;

        if (*v == '\0')
                return (*x == '\0' ? EXACT_MATCH : NO_MATCH);

        if (checkcase) {                        /* case is significant */
                while (*x++ == *v)
                        if (*v++ == '\0')
                                return (EXACT_MATCH);
        } else {                                /* ignore case */
                while (toupper (*x++) == toupper (*v))
                        if (*v++ == '\0')
                                return (EXACT_MATCH);
        }
        while (*v && *v == ' ')                 /* remove trailing blanks */
                ++v;
        if (*v)
                return (NO_MATCH);
        else
                return (*--x ? PARTIAL_MATCH : EXACT_MATCH);
}

static int
fcheck_enum(FIELD *f, char *arg)
{
        ENUM *          n               = (ENUM *) arg;
        char **         list            = n -> list;
        int             checkcase       = n -> checkcase;
        int             checkuniq       = n -> checkuniq;
        int             m;
        char *          v               = field_buffer(f, 0);
        char *          x;

        while (x = *list++)
                if (m = cmp(x, v, checkcase)) {
                        char * value = x;

                        if (checkuniq && m != EXACT_MATCH)
                                while (x = *list++)
                                        if (m = cmp(x, v, checkcase)) {
                                                if (m == EXACT_MATCH) {
                                                        value = x;
                                                        break;
                                                }
                                                else
                                                        value = (char *) 0;
                                        }
                        if (! value)
                                return (FALSE);

                        (void) set_field_buffer(f, 0, value);
                        return (TRUE);
                }

        return (FALSE);
}

static int
next_enum(FIELD *f, char *arg)
{
        ENUM *          n               = (ENUM *) arg;
        char **         list            = n -> list;
        int             checkcase       = n -> checkcase;
        int             count           = n -> count;
        char *          v               = field_buffer(f, 0);

        while (count--)
                if (cmp(*list++, v, checkcase) == EXACT_MATCH)
                        break;
        if (count <= 0)
                list = n -> list;

        if (count >= 0 || cmp("", v, checkcase) == EXACT_MATCH) {
                (void) set_field_buffer(f, 0, *list);
                return (TRUE);
        }
        return (FALSE);
}

static int
prev_enum(FIELD *f, char *arg)
{
        ENUM *          n               = (ENUM *) arg;
        char **         list            = n -> list + n -> count - 1;
        int             checkcase       = n -> checkcase;
        int             count           = n -> count;
        char *          v               = field_buffer(f, 0);

        while (count--)
                if (cmp(*list--, v, checkcase) == EXACT_MATCH)
                        break;
        if (count <= 0)
                list = n -> list + n -> count - 1;

        if (count >= 0 || cmp("", v, checkcase) == EXACT_MATCH) {
                (void) set_field_buffer(f, 0, *list);
                return (TRUE);
        }
        return (FALSE);
}