root/usr/src/lib/udapl/libdat/common/udat_sr_parser.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) 2002-2003, Network Appliance, Inc. All rights reserved.
 */

/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 *
 * MODULE: dat_sr_parser.c
 *
 * PURPOSE: static registry parser
 *
 * $Id: udat_sr_parser.c,v 1.1 2003/07/31 14:04:19 jlentini Exp $
 */


#include "udat_sr_parser.h"
#include "dat_sr.h"


/*
 *
 * Constants
 *
 */

#define DAT_SR_CONF_ENV                 "DAT_OVERRIDE"
#define DAT_SR_CONF_DEFAULT             "/etc/dat/dat.conf"

#define DAT_SR_TOKEN_THREADSAFE         "threadsafe"
#define DAT_SR_TOKEN_NONTHREADSAFE      "nonthreadsafe"
#define DAT_SR_TOKEN_DEFAULT            "default"
#define DAT_SR_TOKEN_NONDEFAULT         "nondefault"

#define DAT_SR_CHAR_NEWLINE             '\n'
#define DAT_SR_CHAR_COMMENT             '#'
#define DAT_SR_CHAR_QUOTE               '"'
#define DAT_SR_CHAR_BACKSLASH           '\\'


/*
 *
 * Enumerations
 *
 */

typedef enum
{
        DAT_SR_TOKEN_STRING,    /* text field (both quoted or unquoted) */
        DAT_SR_TOKEN_EOR,       /* end of record (newline)              */
        DAT_SR_TOKEN_EOF        /* end of file                          */
} DAT_SR_TOKEN_TYPE;

typedef enum
{
        DAT_SR_API_UDAT,
        DAT_SR_API_KDAT
} DAT_SR_API_TYPE;


/*
 *
 * Structures
 *
 */

typedef struct
{
    DAT_SR_TOKEN_TYPE   type;
    char                *value; /* valid if type is DAT_SR_TOKEN_STRING */
    DAT_OS_SIZE         value_len;
} DAT_SR_TOKEN;

typedef struct DAT_SR_STACK_NODE
{
    DAT_SR_TOKEN                token;
    struct DAT_SR_STACK_NODE    *next;
} DAT_SR_STACK_NODE;

typedef struct
{
    DAT_UINT32                  major;
    DAT_UINT32                  minor;
} DAT_SR_VERSION;

typedef struct
{
    char                        *id;
    DAT_SR_VERSION              version;
} DAT_SR_PROVIDER_VERSION;

typedef struct
{
    DAT_SR_API_TYPE             type;
    DAT_SR_VERSION              version;
} DAT_SR_API_VERSION;

typedef struct
{
    char                        *ia_name;
    DAT_SR_API_VERSION          api_version;
    DAT_BOOLEAN                 is_thread_safe;
    DAT_BOOLEAN                 is_default;
    char                        *lib_path;
    DAT_SR_PROVIDER_VERSION     provider_version;
    char                        *ia_params;
    char                        *platform_params;
} DAT_SR_CONF_ENTRY;


/*
 *
 * Internal Function Declarations
 *
 */

static DAT_RETURN
dat_sr_load_entry(
    DAT_SR_CONF_ENTRY *entry);

static DAT_BOOLEAN
dat_sr_is_valid_entry(
    DAT_SR_CONF_ENTRY *entry);

static char *
dat_sr_type_to_str(
    DAT_SR_TOKEN_TYPE type);

static DAT_RETURN
dat_sr_parse_eof(
    DAT_OS_FILE *file);

static DAT_RETURN
dat_sr_parse_entry(
    DAT_OS_FILE *file);

static DAT_RETURN
dat_sr_parse_ia_name(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_api(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_thread_safety(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_default(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_lib_path(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_provider_version(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_ia_params(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_platform_params(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_parse_eoe(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry);

static DAT_RETURN
dat_sr_convert_api(
    char *str,
    DAT_SR_API_VERSION *api_version);

static DAT_RETURN
dat_sr_convert_thread_safety(
    char *str,
    DAT_BOOLEAN *is_thread_safe);

static DAT_RETURN
dat_sr_convert_default(
    char *str,
    DAT_BOOLEAN *is_default);

static DAT_RETURN
dat_sr_convert_provider_version(
    char *str,
    DAT_SR_PROVIDER_VERSION *provider_version);

static DAT_RETURN
dat_sr_get_token(
    DAT_OS_FILE *file,
    DAT_SR_TOKEN *token);

static DAT_RETURN
dat_sr_put_token(
    DAT_OS_FILE *file,
    DAT_SR_TOKEN *token);

static DAT_RETURN
dat_sr_read_token(
    DAT_OS_FILE *file,
    DAT_SR_TOKEN *token);

static DAT_RETURN
dat_sr_read_str(
    DAT_OS_FILE *file,
    DAT_SR_TOKEN *token,
    DAT_OS_SIZE token_len);

static DAT_RETURN
dat_sr_read_quoted_str(
    DAT_OS_FILE *file,
    DAT_SR_TOKEN *token,
    DAT_OS_SIZE token_len,
    DAT_COUNT num_escape_seq);

static void
dat_sr_read_comment(
    DAT_OS_FILE *file);


/*
 *
 * Global Variables
 *
 */

static DAT_SR_STACK_NODE        *g_token_stack = NULL;


/*
 *
 * External Function Definitions
 *
 */

/*
 * Function: dat_sr_load
 */

DAT_RETURN
dat_sr_load(void)
{
        char                    *sr_path;
        DAT_OS_FILE             *sr_file;

        sr_path = dat_os_getenv(DAT_SR_CONF_ENV);
        if (sr_path == NULL) {
                sr_path = DAT_SR_CONF_DEFAULT;
        }

        dat_os_dbg_print(DAT_OS_DBG_TYPE_SR,
            "DAT Registry: static registry file <%s> \n", sr_path);

        sr_file = dat_os_fopen(sr_path);
        if (sr_file == NULL) {
                return (DAT_INTERNAL_ERROR);
        }

        for (;;) {
                if (DAT_SUCCESS == dat_sr_parse_eof(sr_file)) {
                        break;
                } else if (DAT_SUCCESS == dat_sr_parse_entry(sr_file)) {
                        continue;
                } else {
                        dat_os_assert(!"unable to parse static registry file");
                        break;
                }
        }

        if (0 != dat_os_fclose(sr_file)) {
                return (DAT_INTERNAL_ERROR);
        }

        return (DAT_SUCCESS);
}


/*
 *
 * Internal Function Definitions
 *
 */

/*
 * Function: dat_sr_is_valid_entry
 */

DAT_BOOLEAN
dat_sr_is_valid_entry(
    DAT_SR_CONF_ENTRY *entry)
{
        if ((DAT_SR_API_UDAT == entry->api_version.type) &&
            (entry->is_default)) {
                return (DAT_TRUE);
        } else {
                return (DAT_FALSE);
        }
}


/*
 * Function: dat_sr_load_entry
 */

DAT_RETURN
dat_sr_load_entry(
    DAT_SR_CONF_ENTRY *conf_entry)
{
        DAT_SR_ENTRY entry;

        if (DAT_NAME_MAX_LENGTH < (strlen(conf_entry->ia_name) + 1)) {
                dat_os_dbg_print(DAT_OS_DBG_TYPE_SR,
                    "DAT Registry: ia name %s is longer than "
                    "DAT_NAME_MAX_LENGTH (%i)\n",
                    conf_entry->ia_name, DAT_NAME_MAX_LENGTH);

                return (DAT_INSUFFICIENT_RESOURCES);
        }

        (void) dat_os_strncpy(entry.info.ia_name, conf_entry->ia_name,
            DAT_NAME_MAX_LENGTH);
        entry.info.dapl_version_major = conf_entry->api_version.version.major;
        entry.info.dapl_version_minor = conf_entry->api_version.version.minor;
        entry.info.is_thread_safe = conf_entry->is_thread_safe;
        entry.lib_path = conf_entry->lib_path;
        entry.ia_params = conf_entry->ia_params;
        entry.lib_handle = NULL;
        entry.ref_count = 0;

        dat_os_dbg_print(DAT_OS_DBG_TYPE_SR,
            "DAT Registry: loading provider for %s\n",
            conf_entry->ia_name);

        return (dat_sr_insert(&entry.info, &entry));
}


/*
 * Function: dat_sr_type_to_str
 */

char *
dat_sr_type_to_str(
    DAT_SR_TOKEN_TYPE type)
{
        static char *str_array[] = { "string", "eor", "eof" };

        if ((type < 0) || (2 < type)) {
                return ("error: invalid token type");
        }

        return (str_array[type]);
}


/*
 * Function: dat_sr_parse_eof
 */

DAT_RETURN
dat_sr_parse_eof(
        DAT_OS_FILE             *file)
{
        DAT_SR_TOKEN    token;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_EOF == token.type) {
                return (DAT_SUCCESS);
        } else {
                (void) dat_sr_put_token(file, &token);
                return (DAT_INTERNAL_ERROR);
        }
}


/*
 * Function: dat_sr_parse_ia_name
 */

DAT_RETURN
dat_sr_parse_entry(
    DAT_OS_FILE                 *file)
{
        DAT_SR_CONF_ENTRY               entry;
        DAT_RETURN                      status;

        (void) dat_os_memset(&entry, 0, sizeof (DAT_SR_CONF_ENTRY));

        if ((DAT_SUCCESS == dat_sr_parse_ia_name(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_api(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_thread_safety(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_default(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_lib_path(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_provider_version(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_ia_params(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_platform_params(file, &entry)) &&
            (DAT_SUCCESS == dat_sr_parse_eoe(file, &entry))) {
                dat_os_dbg_print(DAT_OS_DBG_TYPE_SR,
                    "\n"
                    "DAT Registry: entry \n"
                    " ia_name %s\n"
                    " api_version\n"
                    "     type 0x%X\n"
                    "     major.minor %d.%d\n"
                    " is_thread_safe %d\n"
                    " is_default %d\n"
                    " lib_path %s\n"
                    " provider_version\n"
                    "     id %s\n"
                    "     major.minor %d.%d\n"
                    " ia_params %s\n"
                    "\n",
                    entry.ia_name,
                    entry.api_version.type,
                    entry.api_version.version.major,
                    entry.api_version.version.minor,
                    entry.is_thread_safe,
                    entry.is_default,
                    entry.lib_path,
                    entry.provider_version.id,
                    entry.provider_version.version.major,
                    entry.provider_version.version.minor,
                    entry.ia_params);

                if (DAT_TRUE == dat_sr_is_valid_entry(&entry)) {
                        /*
                         * The static registry configuration file may have
                         * multiple entries with the same IA name. The first
                         * entry will be installed in the static registry
                         * causing subsequent attempts to register the same IA
                         * name to fail. Therefore the return code from
                         * dat_sr_load_entry() is ignored.
                         */
                        (void) dat_sr_load_entry(&entry);
                }

                status = DAT_SUCCESS;
        } else { /* resync */
                DAT_SR_TOKEN            token;

                /*
                 * The static registry format is specified in the DAT
                 * specification. While the registry file's contents may change
                 * between revisions of the specification, there is no way to
                 * determine the specification version to which the
                 * configuration file conforms. If an entry is found that does
                 * not match the expected format, the entry is discarded
                 * and the parsing of the file continues. There is no way to
                 * determine if the entry was an error or an entry confirming
                 * to an alternate version of specification.
                 */

                for (;;) {
                        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                                status = DAT_INTERNAL_ERROR;
                                break;
                        }

                        if (DAT_SR_TOKEN_STRING != token.type) {
                                status = DAT_SUCCESS;
                                break;
                        } else {
                                dat_os_free(token.value,
                                    (sizeof (char) *
                                        dat_os_strlen(token.value)) + 1);
                                continue;
                        }
                }
        }

        /* free resources */
        if (NULL != entry.ia_name) {
                dat_os_free(entry.ia_name,
                    sizeof (char) * (dat_os_strlen(entry.ia_name) + 1));
        }
        if (NULL != entry.lib_path) {
                dat_os_free(entry.lib_path,
                    sizeof (char) * (dat_os_strlen(entry.lib_path) + 1));
        }

        if (NULL != entry.provider_version.id) {
                dat_os_free(entry.provider_version.id,
                    sizeof (char) *
                    (dat_os_strlen(entry.provider_version.id) + 1));
        }

        if (NULL != entry.ia_params) {
                dat_os_free(entry.ia_params,
                    sizeof (char) * (dat_os_strlen(entry.ia_params) + 1));
        }

        return (status);
}


/*
 * Function: dat_sr_parse_ia_name
 */

DAT_RETURN
dat_sr_parse_ia_name(
    DAT_OS_FILE         *file,
    DAT_SR_CONF_ENTRY   *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else {
                entry->ia_name = token.value;

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_parse_ia_name
 */

DAT_RETURN
dat_sr_parse_api(
    DAT_OS_FILE         *file,
    DAT_SR_CONF_ENTRY   *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else if (DAT_SUCCESS != dat_sr_convert_api(
                token.value, &entry->api_version)) {
                status = DAT_INTERNAL_ERROR;
        } else {
                dat_os_free(token.value,
                    (sizeof (char) * dat_os_strlen(token.value)) + 1);

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_parse_thread_safety
 */

static DAT_RETURN
dat_sr_parse_thread_safety(
    DAT_OS_FILE         *file,
    DAT_SR_CONF_ENTRY   *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else if (DAT_SUCCESS != dat_sr_convert_thread_safety(
                token.value, &entry->is_thread_safe)) {
                status = DAT_INTERNAL_ERROR;
        } else {
                dat_os_free(token.value,
                    (sizeof (char) * dat_os_strlen(token.value)) + 1);

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_parse_default
 */

DAT_RETURN
dat_sr_parse_default(
    DAT_OS_FILE         *file,
    DAT_SR_CONF_ENTRY   *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else if (DAT_SUCCESS != dat_sr_convert_default(
                token.value, &entry->is_default)) {
                status = DAT_INTERNAL_ERROR;
        } else {
                dat_os_free(token.value,
                    (sizeof (char) * dat_os_strlen(token.value)) + 1);

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_parse_lib_path
 */

DAT_RETURN
dat_sr_parse_lib_path(
    DAT_OS_FILE         *file,
    DAT_SR_CONF_ENTRY   *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else {
                entry->lib_path = token.value;

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}

/*
 * Function: dat_sr_parse_provider_version
 */

DAT_RETURN
dat_sr_parse_provider_version(
    DAT_OS_FILE         *file,
    DAT_SR_CONF_ENTRY   *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else if (DAT_SUCCESS != dat_sr_convert_provider_version(
                token.value, &entry->provider_version)) {
                status = DAT_INTERNAL_ERROR;
        } else {
                dat_os_free(token.value,
                    (sizeof (char) * dat_os_strlen(token.value)) + 1);

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_parse_ia_params
 */

DAT_RETURN
dat_sr_parse_ia_params(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else {
                entry->ia_params = token.value;

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_parse_platform_params
 */

DAT_RETURN
dat_sr_parse_platform_params(
    DAT_OS_FILE *file,
    DAT_SR_CONF_ENTRY *entry)
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (DAT_SR_TOKEN_STRING != token.type) {
                status = DAT_INTERNAL_ERROR;
        } else {
                entry->platform_params = token.value;

                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_parse_eoe
 */

DAT_RETURN
dat_sr_parse_eoe(
    DAT_OS_FILE *file,
        DAT_SR_CONF_ENTRY *entry) /*ARGSUSED*/
{
        DAT_SR_TOKEN    token;
        DAT_RETURN      status;

        if (DAT_SUCCESS != dat_sr_get_token(file, &token)) {
                return (DAT_INTERNAL_ERROR);
        }

        if ((DAT_SR_TOKEN_EOF != token.type) &&
            (DAT_SR_TOKEN_EOR != token.type)) {
                status = DAT_INTERNAL_ERROR;
        } else {
                status = DAT_SUCCESS;
        }

        if (DAT_SUCCESS != status) {
                DAT_RETURN      status_success;

                status_success = dat_sr_put_token(file, &token);
                dat_os_assert(DAT_SUCCESS == status_success);
        }

        return (status);
}


/*
 * Function: dat_sr_convert_api
 */

DAT_RETURN
dat_sr_convert_api(
    char *str,
    DAT_SR_API_VERSION *api_version)
{
        int i;
        int minor_i;

        dat_os_assert(0 < dat_os_strlen(str));

        if ('u' == str[0]) {
                api_version->type = DAT_SR_API_UDAT;
        } else if ('k' == str[0]) {
                api_version->type = DAT_SR_API_KDAT;
        } else {
                return (DAT_INTERNAL_ERROR);
        }

        for (i = 1 /* move past initial [u|k] */; '\0' != str[i]; i++) {
                if ('.' == str[i]) {
                        break;
                } else if (DAT_TRUE != dat_os_isdigit(str[i])) {
                        return (DAT_INTERNAL_ERROR);
                }
        }

        api_version->version.major = (DAT_UINT32)dat_os_strtol(str + 1, NULL,
            10);

        /* move past '.' */
        minor_i = ++i;

        for (; '\0' != str[i]; i++) {
                if (DAT_TRUE != dat_os_isdigit(str[i])) {
                        return (DAT_INTERNAL_ERROR);
                }
        }

        api_version->version.minor = (DAT_UINT32)dat_os_strtol(str + minor_i,
            NULL, 10);

        if ('\0' != str[i]) {
                return (DAT_INTERNAL_ERROR);
        }

        return (DAT_SUCCESS);
}


/*
 * Function: dat_sr_convert_thread_safety
 */

static DAT_RETURN
dat_sr_convert_thread_safety(
    char *str,
    DAT_BOOLEAN *is_thread_safe)
{
        if (!dat_os_strncmp(str,
            DAT_SR_TOKEN_THREADSAFE,
            dat_os_strlen(DAT_SR_TOKEN_THREADSAFE))) {
                *is_thread_safe = DAT_TRUE;
                return (DAT_SUCCESS);
        } else if (!dat_os_strncmp(str,
            DAT_SR_TOKEN_NONTHREADSAFE,
            dat_os_strlen(DAT_SR_TOKEN_NONTHREADSAFE))) {
                *is_thread_safe = DAT_FALSE;
                return (DAT_SUCCESS);
        } else {
                return (DAT_INTERNAL_ERROR);
        }
}


/*
 * Function: dat_sr_convert_default
 */

static DAT_RETURN
dat_sr_convert_default(
    char *str,
    DAT_BOOLEAN *is_default)
{
        if (!dat_os_strncmp(str,
            DAT_SR_TOKEN_DEFAULT,
            dat_os_strlen(DAT_SR_TOKEN_DEFAULT))) {
                *is_default = DAT_TRUE;
                return (DAT_SUCCESS);
        } else if (!dat_os_strncmp(str,
            DAT_SR_TOKEN_NONDEFAULT,
            dat_os_strlen(DAT_SR_TOKEN_NONDEFAULT))) {
                *is_default = DAT_FALSE;
                return (DAT_SUCCESS);
        } else {
                return (DAT_INTERNAL_ERROR);
        }
}


/*
 * Function: dat_sr_convert_provider_version
 */

DAT_RETURN
dat_sr_convert_provider_version(
    char *str,
    DAT_SR_PROVIDER_VERSION *provider_version)
{
        DAT_RETURN      status;
        int             i;
        int             decimal_i;

        dat_os_assert(0 < dat_os_strlen(str));
        dat_os_assert(NULL == provider_version->id);

        status = DAT_SUCCESS;

        for (i = 0; '\0' != str[i]; i++) {
                if ('.' == str[i]) {
                        break;
                }
        }

        /* if no id value was found */
        if (0 == i) {
                status = DAT_INTERNAL_ERROR;
                goto exit;
        }

        if (NULL == (provider_version->id = dat_os_alloc(sizeof (char) *
            (i + 1)))) {
                status = DAT_INSUFFICIENT_RESOURCES | DAT_RESOURCE_MEMORY;
                goto exit;
        }

        (void) dat_os_strncpy(provider_version->id, str, i);
        provider_version->id[i] = '\0';

        /* move past '.' */
        decimal_i = ++i;

        for (; '\0' != str[i]; i++) {
                if ('.' == str[i]) {
                        break;
                } else if (DAT_TRUE != dat_os_isdigit(str[i])) {
                        status = DAT_INTERNAL_ERROR;
                        goto exit;
                }
        }

        /* if no version value was found */
        if (decimal_i == i) {
                status = DAT_INTERNAL_ERROR;
                goto exit;
        }

        provider_version->version.major = (DAT_UINT32)
            dat_os_strtol(str + decimal_i, NULL, 10);

        /* move past '.' */
        decimal_i = ++i;

        for (; '\0' != str[i]; i++) {
                if (DAT_TRUE != dat_os_isdigit(str[i])) {
                        status = DAT_INTERNAL_ERROR;
                        goto exit;
                }
        }

        /* if no version value was found */
        if (decimal_i == i) {
                status = DAT_INTERNAL_ERROR;
                goto exit;
        }

        provider_version->version.minor = (DAT_UINT32)
            dat_os_strtol(str + decimal_i, NULL, 10);

        if ('\0' != str[i]) {
                status = DAT_INTERNAL_ERROR;
                goto exit;
        }

exit:
        if (DAT_SUCCESS != status) {
                if (NULL != provider_version->id) {
                        dat_os_free(provider_version->id,
                            sizeof (char) *
                            (dat_os_strlen(provider_version->id) + 1));
                        provider_version->id = NULL;
                }
        }

        return (status);
}


/*
 * Function: dat_sr_get_token
 */

DAT_RETURN
dat_sr_get_token(
    DAT_OS_FILE *file,
    DAT_SR_TOKEN *token)
{
        if (NULL == g_token_stack) {
                return (dat_sr_read_token(file, token));
        } else {
                DAT_SR_STACK_NODE *top;

                top = g_token_stack;

                *token = top->token;
                g_token_stack = top->next;

                dat_os_free(top, sizeof (DAT_SR_STACK_NODE));

                return (DAT_SUCCESS);
        }
}


/*
 * Function: dat_sr_put_token
 */

DAT_RETURN
dat_sr_put_token(
    DAT_OS_FILE *file,
        DAT_SR_TOKEN *token) /*ARGSUSED*/
{
        DAT_SR_STACK_NODE *top;

        if (NULL == (top = dat_os_alloc(sizeof (DAT_SR_STACK_NODE)))) {
                return (DAT_INSUFFICIENT_RESOURCES | DAT_RESOURCE_MEMORY);
        }

        top->token = *token;
        top->next = g_token_stack;
        g_token_stack = top;

        return (DAT_SUCCESS);
}


/*
 * Function: dat_sr_read_token
 */

DAT_RETURN
dat_sr_read_token(
    DAT_OS_FILE                 *file,
    DAT_SR_TOKEN                *token)
{
        DAT_OS_FILE_POS         pos;
        DAT_OS_SIZE             token_len;
        DAT_COUNT               num_escape_seq;
        DAT_BOOLEAN             is_quoted_str;
        DAT_BOOLEAN             is_prev_char_backslash;

        /*
         * The DAT standard does not specify a maximum size for quoted strings.
         * Therefore the tokenizer must be able to read in a token of arbitrary
         * size. Instead of allocating a fixed length buffer, the tokenizer
         * first scans the input a single character at a time looking for the
         * begining and end of the token. Once the these positions are found,
         * the entire token is read into memory. By using this algorithm, the
         * implementation does not place an arbitrary maximum on the token size.
         */

        token_len = 0;
        num_escape_seq = 0;
        is_quoted_str = DAT_FALSE;
        is_prev_char_backslash = DAT_FALSE;

        for (;;) {
                DAT_OS_FILE_POS cur_pos;
                int c;

                /* if looking for start of the token */
                if (0 == token_len) {
                        if (DAT_SUCCESS != dat_os_fgetpos(file, &cur_pos)) {
                                return (DAT_INTERNAL_ERROR);
                        }
                }

                c = dat_os_fgetc(file);

                /* if looking for start of the token */
                if (0 == token_len) {
                        if (EOF == c) {
                                token->type = DAT_SR_TOKEN_EOF;
                                token->value = NULL;
                                token->value_len = 0;
                                goto success;
                        } else if (DAT_SR_CHAR_NEWLINE == c) {
                                token->type = DAT_SR_TOKEN_EOR;
                                token->value = NULL;
                                token->value_len = 0;
                                goto success;
                        } else if (dat_os_isblank(c)) {
                                continue;
                        } else if (DAT_SR_CHAR_COMMENT == c) {
                                dat_sr_read_comment(file);
                                continue;
                        } else {
                                if (DAT_SR_CHAR_QUOTE == c) {
                                        is_quoted_str = DAT_TRUE;
                                }

                                pos = cur_pos;
                                token_len++;
                        }
                } else { /* looking for the end of the token */
                        if (EOF == c) {
                                break;
                        } else if (DAT_SR_CHAR_NEWLINE == c) {
                                /* put back the newline */
                                (void) dat_os_fungetc(file);
                                break;
                        } else if (!is_quoted_str && dat_os_isblank(c)) {
                                break;
                        } else {
                                token_len++;

                                if ((DAT_SR_CHAR_QUOTE == c) &&
                                    !is_prev_char_backslash) {
                                        break;
                                } else if ((DAT_SR_CHAR_BACKSLASH == c) &&
                                    !is_prev_char_backslash) {
                                        is_prev_char_backslash = DAT_TRUE;
                                        num_escape_seq++;
                                } else {
                                        is_prev_char_backslash = DAT_FALSE;
                                }
                        }
                }
        }

        /* the token was a string */
        if (DAT_SUCCESS != dat_os_fsetpos(file, &pos)) {
                return (DAT_INTERNAL_ERROR);
        }

        if (is_quoted_str) {
                if (DAT_SUCCESS != dat_sr_read_quoted_str(file,
                    token,
                    token_len,
                    num_escape_seq)) {
                        return (DAT_INTERNAL_ERROR);
                }
        } else {
                if (DAT_SUCCESS != dat_sr_read_str(file,
                    token,
                    token_len)) {
                        return (DAT_INTERNAL_ERROR);
                }
        }

success:
        dat_os_dbg_print(DAT_OS_DBG_TYPE_SR,
            "\n"
            "DAT Registry: token\n"
            " type  %s\n"
            " value <%s>\n"
            "\n",
            dat_sr_type_to_str(token->type),
            ((DAT_SR_TOKEN_STRING == token->type) ? token->value : ""));

        return (DAT_SUCCESS);
}


/*
 * Function: dat_sr_read_str
 */

DAT_RETURN
dat_sr_read_str(
    DAT_OS_FILE *file,
    DAT_SR_TOKEN *token,
    DAT_OS_SIZE token_len)
{
        token->type = DAT_SR_TOKEN_STRING;
        /* +1 for null termination */
        token->value_len = sizeof (char) * (token_len + 1);
        if (NULL == (token->value = dat_os_alloc(token->value_len))) {
                return (DAT_INSUFFICIENT_RESOURCES | DAT_RESOURCE_MEMORY);
        }

        if (token_len != dat_os_fread(file, token->value, token_len)) {
                dat_os_free(token->value, token->value_len);
                token->value = NULL;

                return (DAT_INTERNAL_ERROR);
        }

        token->value[token->value_len - 1] = '\0';

        return (DAT_SUCCESS);
}


/*
 * Function: dat_sr_read_quoted_str
 */

DAT_RETURN
dat_sr_read_quoted_str(
    DAT_OS_FILE         *file,
    DAT_SR_TOKEN        *token,
    DAT_OS_SIZE         token_len,
    DAT_COUNT           num_escape_seq)
{
        DAT_OS_SIZE     str_len;
        DAT_OS_SIZE     i;
        DAT_OS_SIZE     j;
        int             c;
        DAT_RETURN      status;
        DAT_BOOLEAN     is_prev_char_backslash;

        str_len = token_len - 2; /* minus 2 " characters */
        is_prev_char_backslash = DAT_FALSE;
        status = DAT_SUCCESS;

        token->type = DAT_SR_TOKEN_STRING;
        /* +1 for null termination */
        token->value_len = sizeof (char) * (str_len - num_escape_seq + 1);

        if (NULL == (token->value = dat_os_alloc(token->value_len))) {
                status = DAT_INSUFFICIENT_RESOURCES | DAT_RESOURCE_MEMORY;
                goto exit;
        }

        /* throw away " */
        if (DAT_SR_CHAR_QUOTE != dat_os_fgetc(file)) {
                status = DAT_INTERNAL_ERROR;
                goto exit;
        }

        for (i = 0, j = 0; i < str_len; i++) {
                c = dat_os_fgetc(file);

                if (EOF == c) {
                        status = DAT_INTERNAL_ERROR;
                        goto exit;
                } else if ((DAT_SR_CHAR_BACKSLASH == c) &&
                    !is_prev_char_backslash) {
                        is_prev_char_backslash = DAT_TRUE;
                } else {
                        token->value[j] = c;
                        j++;

                        is_prev_char_backslash = DAT_FALSE;
                }
        }

        /* throw away " */
        if (DAT_SR_CHAR_QUOTE != dat_os_fgetc(file)) {
                status = DAT_INTERNAL_ERROR;
                goto exit;
        }

        token->value[token->value_len - 1] = '\0';

exit:
        if (DAT_SUCCESS != status) {
                if (NULL != token->value) {
                        dat_os_free(token->value, token->value_len);
                        token->value = NULL;
                }
        }

        return (status);
}


/*
 * Function: dat_sr_read_comment
 */

void
dat_sr_read_comment(
    DAT_OS_FILE *file)
{
        int c;

        /* read up to an EOR or EOF to move past the comment */
        do {
                c = dat_os_fgetc(file);
        } while ((DAT_SR_CHAR_NEWLINE != c) && (EOF != c));

        /* put back the newline */
        (void) dat_os_fungetc(file);
}