Logo Search packages:      
Sourcecode: qstat version File versions  Download package

config.c

/*
 * qstat 2.6
 * by Steve Jankowski
 * steve@qstat.org
 * http://www.qstat.org
 *
 * Copyright 1996,1997,1998,1999 by Steve Jankowski
 *
 * Licensed under the Artistic License, see LICENSE.txt for license terms
 */

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

#include "config.h"

#ifdef __hpux
#define STATIC static
#else
#define STATIC
#endif

#ifdef _WIN32
#define strcasecmp      stricmp
#endif


#ifdef _WIN32
#define HOME_CONFIG_FILE "qstat.cfg"
#else
#define HOME_CONFIG_FILE ".qstatrc"
#endif


static server_type ** config_types;
static int n_config_types;
static int max_config_types;
static int next_gametype_id= LAST_BUILTIN_SERVER+1;

static int load_config_file( char *filename);
static int try_load_config_file( char *filename, int show_error);
static int pf_top_level( char *text, void *context);
static int pf_gametype_new( char *text, void *context);
static int pf_gametype_modify( char *text, void *context);

static int set_game_type_value( server_type *gametype, int key, char *value);
static int modify_game_type_value( server_type *gametype, int key, char *value);
static int set_packet_value( server_type *gametype, char *value,
      char *packet_name, char **packet, int *len);

static char *first_token( char *text);
static char *next_token();
static char *next_token_dup();
static char *next_value();
static char * parse_value( char *source, int len);
static unsigned int parse_hex( char *hex, int len, int *error);
static unsigned int parse_oct( char *oct, int len, int *error);
static char *force_lower_case( char *string);
static char *force_upper_case( char *string);
static char *get_token();
static void * memdup( void *mem, unsigned int len);

int parse_config_file( FILE *file);

static int (*parse_func)( char *, void *);
static void *parse_context;
static int line;
static char *current_file_name;

static char *parse_text;
static char *parse_end;
static char *lex;
static char *token_end;
static char token_buf[1024];
static int value_len;

static int debug= 0;

#define REPORT_ERROR(a)       print_location(); fprintf a; fprintf(stderr, "\n")

enum {
    CK_NONE = 0,
    CK_MASTER_PROTOCOL,
    CK_MASTER_QUERY,
    CK_MASTER_PACKET,
    CK_FLAGS,
    CK_NAME,
    CK_DEFAULT_PORT,
    CK_GAME_RULE,
    CK_TEMPLATE_VAR,
    CK_MASTER_TYPE,
    CK_STATUS_PACKET,
    CK_STATUS2_PACKET,
    CK_PLAYER_PACKET,
    CK_RULE_PACKET,
    CK_PORT_OFFSET
};

00101 typedef struct _config_key {
    int key;
    char *key_name;
} ConfigKey;

static ConfigKey new_keys[] = {
{ CK_MASTER_PROTOCOL, "master protocol" },
{ CK_MASTER_QUERY, "master query" },
{ CK_MASTER_PACKET, "master packet" },
{ CK_FLAGS, "flags" },
{ CK_NAME, "name" },
{ CK_DEFAULT_PORT, "default port" },
{ CK_GAME_RULE, "game rule" },
{ CK_TEMPLATE_VAR, "template var" },
{ CK_MASTER_TYPE, "master for gametype" },
{ CK_STATUS_PACKET, "status packet" },
{ CK_STATUS2_PACKET, "status2 packet" },
{ CK_PLAYER_PACKET, "player packet" },
{ CK_RULE_PACKET, "rule packet" },
{ CK_PORT_OFFSET, "status port offset" },
{ 0, NULL },
};

static ConfigKey modify_keys[] = {
{ CK_MASTER_PROTOCOL, "master protocol" },
{ CK_MASTER_QUERY, "master query" },
{ CK_MASTER_PACKET, "master packet" },
{ CK_FLAGS, "flags" },
{ CK_MASTER_TYPE, "master for gametype" },
{ 0, NULL },
};

00133 typedef struct {
    const char *name;
    int value;
} ServerFlag;

#define SERVER_FLAG(x) { #x, x }
ServerFlag server_flags[] = {
    SERVER_FLAG(TF_SINGLE_QUERY),
    SERVER_FLAG(TF_OUTFILE),
    SERVER_FLAG(TF_MASTER_MULTI_RESPONSE),
    SERVER_FLAG(TF_TCP_CONNECT),
    SERVER_FLAG(TF_QUERY_ARG),
    SERVER_FLAG(TF_QUERY_ARG_REQUIRED),
    SERVER_FLAG(TF_QUAKE3_NAMES),
    SERVER_FLAG(TF_TRIBES2_NAMES),
    SERVER_FLAG(TF_SOF_NAMES),
    SERVER_FLAG(TF_U2_NAMES),
    SERVER_FLAG(TF_RAW_STYLE_QUAKE),
    SERVER_FLAG(TF_RAW_STYLE_TRIBES),
    SERVER_FLAG(TF_RAW_STYLE_GHOSTRECON),
    SERVER_FLAG(TF_NO_PORT_OFFSET),
    SERVER_FLAG(TF_SHOW_GAME_PORT),
    { NULL, 0 }
};
#undef SERVER_FLAG

static int get_config_key( char *first_token, ConfigKey *keys);
static void add_config_type( server_type *gametype);
static server_type * get_config_type( char *game_type);
static void copy_server_type( server_type *dest, server_type *source);
static server_type * get_server_type( char *game_type);
static server_type * get_builtin_type( char *game_type);

00166 typedef struct _gametype_context {
    char *type;
    char *extend_type;
    server_type *gametype;
} GameTypeContext;


static void
print_location()
{
    fprintf( stderr, "%s line %d: ", current_file_name, line);
}

int
qsc_load_default_config_files()
{
    int rc= 0;
    char *filename= NULL, *var;
    char path[1024];

    var= getenv( "QSTAT_CONFIG");
    if ( var != NULL && var[0] != '\0')  {
      rc= try_load_config_file( var, 1);
      if ( rc == 0 || rc == -1)
          return rc;
    }

    var= getenv( "HOME");
    if ( var != NULL && var[0] != '\0')  {
      int len= strlen(var);
      if ( len > 900)
          len= 900;
      strncpy( path, var, len);
      path[len]= '\0';
      strcat( path, "/");
      strcat( path, HOME_CONFIG_FILE);
/*    sprintf( path, "%s/%s", var, HOME_CONFIG_FILE); */
      rc= try_load_config_file( path, 0);
      if ( rc == 0 || rc == -1)
          return rc;
    }

#ifdef sysconfdir
    strcpy( path, sysconfdir "/qstat.cfg");
    filename= path;
#elif defined(_WIN32)
    if ( filename == NULL && _pgmptr && strchr( _pgmptr, '\\'))  {
          char *slash= strrchr( _pgmptr, '\\');
          strncpy( path, _pgmptr, slash - _pgmptr);
          path[slash - _pgmptr]= '\0';
          strcat( path, "\\qstat.cfg");
          filename= path;
    }
#endif

    if ( filename != NULL)  {
      rc= try_load_config_file( filename, 0);
      if ( rc == 0 || rc == -1)
          return rc;
    }
    return rc;
/*
    if ( rc == -2 && show_error)  {
      perror( filename);
      fprintf( stderr, "Error: Could not open config file \"%s\"\n",
            filename);
    }
    else if ( rc == -1 && show_error)
      fprintf( stderr, "Error: Error loading $QSTAT_CONFIG file\n");
    return rc;
*/

#ifdef foo
    filename= getenv( "HOME");
    if ( filename != NULL && filename[0] != '\0')  {
      char path[1024];
      sprintf( path, "%s/%s", filename, HOME_CONFIG_FILE);
    }
/* 1. $QSTAT_CONFIG
   2. UNIX: $HOME/.qstatrc         WIN: $HOME/qstat.cfg
   3. UNIX: sysconfdir/qstat.cfg   WIN: qstat.exe-dir/qstat.cfg
*/

    rc= load_config_file( "qstat.cfg");
    if ( rc == -1)
      fprintf( stderr, "Warning: Error loading default qstat.cfg\n");
    return 0;
#endif
}

int
qsc_load_config_file( char *filename)
{
    int rc= load_config_file( filename);
    if ( rc == -2)  {
      perror( filename);
      fprintf( stderr, "Could not open config file \"%s\"\n",
            filename);
      return -1;
    }
    return rc;
}

server_type **
qsc_get_config_server_types( int *n_config_types_ref)
{
    if ( n_config_types_ref)
      *n_config_types_ref= n_config_types;
    return config_types;
}

STATIC int
try_load_config_file( char *filename, int show_error)
{
    int rc= load_config_file( filename);
    if ( rc == -2 && show_error)  {
      perror( filename);
      fprintf( stderr, "Error: Could not open config file \"%s\"\n",
            filename);
    }
    else if ( rc == -1)
      fprintf( stderr, "Error: Could not load config file \"%s\"\n",
            filename);
    return rc;
}

STATIC int
load_config_file( char *filename)
{
    FILE *file;
    int rc;

    file= fopen( filename, "r");
    if ( file == NULL)
      return -2;

    current_file_name= filename;
    rc= parse_config_file( file);
    fclose(file);
    return rc;
}

int
parse_config_file( FILE *file)
{
    char buf[4096];
    int rc;

    line= 0;
    parse_func= pf_top_level;

    while ( fgets( buf, sizeof(buf), file) != NULL)  {
      int len= strlen(buf);
      while ( len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
         len--;
      buf[len]= '\0';
      line++;
      rc= parse_func( buf, parse_context);
      if ( rc != 0)
          return rc;
    }
    return 0;
}


/* Top level
 *   Keywords: gametype
 */
STATIC int
pf_top_level( char *text, void *_context)
{
    GameTypeContext *context;
    server_type *extend;
    char *token, *game_type, *extend_type;
    token= first_token( text);
    if ( token == NULL)
      return 0;

    if ( strcmp( token, "gametype") != 0)  {
      REPORT_ERROR(( stderr, "Unknown config command \"%s\"", token));
      return -1;
    }

    game_type= next_token_dup();
    if ( game_type == NULL)  {
      REPORT_ERROR(( stderr, "Missing game type"));
      return -1;
    }

    force_lower_case( game_type);

    token= next_token();
    if ( token == NULL)  {
      REPORT_ERROR(( stderr, "Expecting \"new\" or \"modify\""));
      return -1;
    }

    if ( strcmp( token, "new") == 0)  {
      parse_func= pf_gametype_new;
    }
    else if ( strcmp( token, "modify") == 0)  {
      parse_func= pf_gametype_modify;
    }
    else  {
      REPORT_ERROR(( stderr, "Expecting \"new\" or \"modify\""));
      return -1;
    }

    context= (GameTypeContext*) malloc( sizeof(GameTypeContext));
    context->type= game_type;
    context->extend_type= NULL;
    context->gametype= NULL;

    token= next_token();

    if ( parse_func == pf_gametype_modify)  {
      if ( token != NULL)  {
          REPORT_ERROR(( stderr, "Extra text after gametype modify"));
          return -1;
      }
      context->gametype= get_server_type( game_type);
      if ( context->gametype == NULL)  {
          REPORT_ERROR(( stderr, "Unknown game type \"%s\"", game_type));
          free(context);
          return -1;
      }
      parse_context= context;
      return 0;
    }

    if ( token == NULL || strcmp( token, "extend") != 0)  {
      REPORT_ERROR(( stderr, "Expecting \"extend\""));
      return -1;
    }

    extend_type= next_token();

    if ( extend_type == NULL)  {
      REPORT_ERROR(( stderr, "Missing extend game type"));
      return -1;
    }

    force_lower_case( extend_type);

    if ( strcasecmp( extend_type, game_type) == 0)  {
      REPORT_ERROR(( stderr, "Identical game type and extend type"));
      return -1;
    }

    context->extend_type= extend_type;
    extend= get_server_type( extend_type);
    if ( extend == NULL)  {
      REPORT_ERROR(( stderr, "Unknown extend game type \"%s\"", extend_type));
      return -1;
    }

    /* Over-write a previous gametype new */
    context->gametype= get_config_type( game_type);
    if ( context->gametype == NULL)
      context->gametype= (server_type*) malloc( sizeof(server_type));
    copy_server_type( context->gametype, extend);

    /* Set flag for new type-id if not re-defining previous config type */
    if ( get_config_type( game_type) == NULL)
      context->gametype->id= 0;

    context->gametype->type_string= game_type;
    context->gametype->type_prefix= strdup(game_type);
    force_upper_case( context->gametype->type_prefix);
    context->gametype->type_option= (char*)malloc( strlen(game_type)+2);
    context->gametype->type_option[0]= '-';
    strcpy( &context->gametype->type_option[1], game_type);

    parse_context= context;

    return 0;
}

STATIC int
pf_gametype_modify( char *text, void *_context)
{
    GameTypeContext *context= (GameTypeContext*) _context;
    char *token;
    int key;

    token= first_token( text);
    if ( token == NULL)
      return 0;

    if ( strcmp( token, "end") == 0)  {
      parse_func= pf_top_level;
      parse_context= NULL;
      return 0;
    }

    key= get_config_key( token, modify_keys);

    token= next_token();

    if ( strcmp( token, "=") != 0)  {
      REPORT_ERROR(( stderr, "Expecting \"=\", found \"%s\"", token));
      return -1;
    }

    token= next_value();
    if ( token == NULL)  {
      REPORT_ERROR(( stderr, "Missing value after \"=\""));
      return -1;
    }

    if ( debug)
      printf( "%d %s = <%s>\n", key, modify_keys[key-1].key_name,
            token?token:"");

    return modify_game_type_value( context->gametype, key, token);
}

STATIC int
pf_gametype_new( char *text, void *_context)
{
    GameTypeContext *context= (GameTypeContext*) _context;
    char *token;
    int key;

    token= first_token( text);
    if ( token == NULL)
      return 0;

    if ( strcmp( token, "end") == 0)  {
      add_config_type( context->gametype);
      parse_func= pf_top_level;
      parse_context= NULL;
      return 0;
    }

    key= get_config_key( token, new_keys);

    if ( key <= 0)
      return key;

    token= next_token();
    if ( strcmp( token, "=") != 0)  {
      REPORT_ERROR(( stderr, "Expecting \"=\", found \"%s\"", token));
      return -1;
    }

    token= next_value();
    if ( token == NULL && (key != CK_MASTER_PROTOCOL && key != CK_MASTER_QUERY))  {
      REPORT_ERROR(( stderr, "Missing value after \"=\""));
      return -1;
    }

    if ( debug)
      printf("%d %s = <%s>\n", key, new_keys[key-1].key_name, token?token:"");

    return set_game_type_value( context->gametype, key, token);
}

STATIC int
get_config_key( char *first_token, ConfigKey *keys)
{
    char key_name[1024], *token;
    int key= 0;

    strcpy( key_name, first_token);
    do  {
      int k;
      for ( k= 0; keys[k].key_name; k++)  {
          if ( strcmp( keys[k].key_name, key_name) == 0)  {
            key= keys[k].key;
            break;
          }
      }
      if ( key)
          break;
      token= next_token();
      if ( token == NULL || strcmp( token, "=") == 0)
          break;
      if ( strlen(key_name) + strlen(token) > sizeof(key_name)-2)  {
          REPORT_ERROR(( stderr, "Key name too long"));
          return -1;
      }
      strcat( key_name, " ");
      strcat( key_name, token);
    } while ( 1);

    if ( key == 0)  {
      REPORT_ERROR(( stderr, "Unknown config key \"%s\"", key_name));
      return -1;
    }

    return key;
}

STATIC int get_server_flag_value(const char* value, unsigned len)
{
    int i= 0;

    for ( i= 0; server_flags[i].name; ++i)  {
      if ( len == strlen(server_flags[i].name) &&
            strncmp( server_flags[i].name, value, len) == 0)  {
          return server_flags[i].value;
      }
    }

    return -1;
}

STATIC int parse_server_flags(const char* value)
{
    int val = 0, v, first = 1;
    const char* s = value;
    const char* e;

    while(*s)
    {
      while(isspace((unsigned char)*s))
          ++s;

      if(!first)
      {
          if(*s != '|') {
            REPORT_ERROR(( stderr, "Syntax error: expecting |"));
            val = -1;
            break;
          }

          ++s;

          while(isspace((unsigned char)*s))
            ++s;
      }
      else
          first = 0;

      e = s;

      while(isalnum((unsigned char)*e) || *e == '_')
          ++e;

      if(e == s) {
          REPORT_ERROR(( stderr, "Syntax error: expecting flag"));
          val = -1;
          break;
      }

      v = get_server_flag_value(s, e-s);
      if(v == -1) {
          REPORT_ERROR(( stderr, "Syntax error: invalid flag"));
          val = -1;
          break;
      }

      s = e;
      val |= v;
    }

    return val;
}

STATIC int
set_game_type_value( server_type *gametype, int key, char *value)
{
    switch ( key)  {
    case CK_NAME:
      gametype->game_name= strdup(value);
      break;
    case CK_FLAGS:
      {
          int flags = parse_server_flags(value);
          if(flags == -1)
            return -1;
          gametype->flags = flags;
      }
      break;
    case CK_DEFAULT_PORT: {
      unsigned short port;
      if ( sscanf( value, "%hu", &port) != 1)  {
          REPORT_ERROR(( stderr, "Syntax error on port.  Should be a number between 1 and 65535."));
          return -1;
      }
      gametype->default_port= port;
      break;
      }
    case CK_GAME_RULE:
      gametype->game_rule= strdup(value);
      break;
    case CK_TEMPLATE_VAR:
      gametype->template_var= strdup(value);
      force_upper_case( gametype->template_var);
      break;
    case CK_MASTER_PROTOCOL:
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master protocol on non-master game type\n"));
          return -1;
      }
      if ( value)
          gametype->master_protocol= strdup(value);
      else
          gametype->master_protocol= NULL;
      break;
    case CK_MASTER_QUERY:
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master query on non-master game type\n"));
          return -1;
      }
      if ( value)
          gametype->master_query= strdup(value);
      else
          gametype->master_query= NULL;
      break;
    case CK_MASTER_TYPE:
      {
      server_type *type;
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master type on non-master game type\n"));
          return -1;
      }
      force_lower_case( value);
      type= get_server_type( value);
      if ( type == NULL)  {
          REPORT_ERROR((stderr, "Unknown server type \"%s\"\n", value));
          return -1;
      }
      gametype->master= type->id;
      }
      break;
    case CK_MASTER_PACKET:
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master packet on non-master game type"));
          return -1;
      }
      if ( value == NULL || value[0] == '\0')  {
          REPORT_ERROR((stderr, "Empty master packet"));
          return -1;
      }
      gametype->master_packet= memdup( value, value_len);
      gametype->master_len= value_len;
      break;
    case CK_STATUS_PACKET:
      return set_packet_value( gametype, value, "status",
            &gametype->status_packet, &gametype->status_len);
    case CK_STATUS2_PACKET:
      return set_packet_value( gametype, value, "status2",
            &gametype->rule_packet, &gametype->rule_len);
    case CK_PLAYER_PACKET:
      return set_packet_value( gametype, value, "player",
            &gametype->player_packet, &gametype->player_len);
    case CK_RULE_PACKET:
      return set_packet_value( gametype, value, "rule",
            &gametype->rule_packet, &gametype->rule_len);
    case CK_PORT_OFFSET:  {
      short port;
      if ( sscanf( value, "%hd", &port) != 1)  {
          REPORT_ERROR(( stderr, "Syntax error on port.  Should be a number between 32767 and -32768."));
          return -1;
      }
      gametype->port_offset= port;
      break;
      }
    }

    return 0;
}

STATIC int
set_packet_value( server_type *gametype, char *value, char *packet_name,
      char **packet, int *len)
{
    if ( gametype->master)  {
      REPORT_ERROR((stderr, "Cannot set info packet on master game type"));
      return -1;
    }
      if ( value == NULL || value[0] == '\0')  {
          REPORT_ERROR((stderr, "Empty %s packet", packet_name));
          return -1;
      }
      if ( *packet == NULL)  {
          REPORT_ERROR((stderr, "Cannot set %s packet; extend game type does not define a %s packet", packet_name, packet_name));
          return -1;
      }
      *packet= memdup( value, value_len);
      *len= value_len;
    return 0;
}

STATIC int
modify_game_type_value( server_type *gametype, int key, char *value)
{
    switch ( key)  {
    case CK_FLAGS:
      {
          int flags = parse_server_flags(value);
          if(flags == -1)
            return -1;
          gametype->flags = flags;
      }
      break;
    case CK_MASTER_PROTOCOL:
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master protocol on non-master game type"));
          return -1;
      }
      if ( value)
          gametype->master_protocol= strdup(value);
      else
          gametype->master_protocol= NULL;
      break;
    case CK_MASTER_QUERY:
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master query on non-master game type"));
          return -1;
      }
      if ( value)
          gametype->master_query= strdup(value);
      else
          gametype->master_query= NULL;
      break;
    case CK_MASTER_PACKET:
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master packet on non-master game type"));
          return -1;
      }
      if ( value == NULL || value[0] == '\0')  {
          REPORT_ERROR((stderr, "Empty master packet"));
          return -1;
      }
      gametype->master_packet= memdup( value, value_len);
      gametype->master_len= value_len;
      break;
    case CK_MASTER_TYPE:
      {
      server_type *type;
      if ( ! gametype->master)  {
          REPORT_ERROR((stderr, "Cannot set master type on non-master game type\n"));
          return -1;
      }
      force_lower_case( value);
      type= get_server_type( value);
      if ( type == NULL)  {
          REPORT_ERROR((stderr, "Unknown server type \"%s\"\n", value));
          return -1;
      }
      gametype->master= type->id;
      }
      break;
    }
    return 0;
}

STATIC server_type *
get_server_type( char *game_type)
{
    server_type *result;
    result= get_builtin_type( game_type);
    if ( result != NULL)
      return result;

    return get_config_type( game_type);
}

STATIC server_type*
get_builtin_type( char* type_string)
{
    server_type *type= &builtin_types[0];

    for ( ; type->id != Q_UNKNOWN_TYPE; type++)
      if ( strcmp( type->type_string, type_string) == 0)
          return type;
    return NULL;
}

STATIC void
add_config_type( server_type *gametype)
{
    if ( gametype->id == 0)  {
      if ( next_gametype_id >= MASTER_SERVER)  {
          REPORT_ERROR(( stderr, "Exceeded game type limit, ignoring \"%s\"",
            gametype->type_string));
          return;
      }
      gametype->id= next_gametype_id;
      if ( gametype->master)
          gametype->id |= MASTER_SERVER;
      next_gametype_id++;
    }

    if ( max_config_types == 0)  {
      max_config_types= 4;
      config_types= (server_type**) malloc(sizeof(server_type*) * (max_config_types + 1));
    }
    else if ( n_config_types >= max_config_types)  {
      max_config_types*= 2;
      config_types= (server_type**) realloc( config_types,
            sizeof(server_type*) * (max_config_types + 1));
    }
    config_types[ n_config_types]= gametype;
    n_config_types++;
}

STATIC server_type *
get_config_type( char *game_type)
{
    int i;
    for ( i= 0; i < n_config_types; i++)
      if ( strcmp( config_types[i]->type_string, game_type) == 0)
          return config_types[i];

    return NULL;
}

STATIC void
copy_server_type( server_type *dest, server_type *source)
{
    *dest= *source;
}

STATIC void *
memdup( void *mem, unsigned int len)
{
    void *result= malloc( len);
    memcpy( result, mem, len);
    return result;
}

/* Parsing primitives
 */

STATIC char *
first_token( char *text)
{
    parse_text= text;
    parse_end= parse_text + strlen( parse_text);
    lex= text;
    token_end= text;

    if ( *lex == '#')
      return NULL;

    return get_token();
}

STATIC char *
next_token()
{
    lex= token_end;
    return get_token();
}

STATIC char *
next_token_dup()
{
    return strdup( next_token());
}

STATIC char *
get_token()
{
    char *token= &token_buf[0];
    while ( isspace((unsigned char)*token_end))
      token_end++;

    if ( token_end == parse_end)
      return NULL;

    while ( (isalnum((unsigned char)*token_end) || *token_end == '.' || *token_end == '_' || *token_end == '-')
            && token < &token_buf[0] + sizeof(token_buf))
      *token++= *token_end++;

    if ( token == &token_buf[0])
      *token++= *token_end++;
    *token= '\0';
    return &token_buf[0];
}

STATIC char *
next_value()
{
    char *token= &token_buf[0];
    while ( isspace((unsigned char)*token_end))
      token_end++;

    if ( token_end == parse_end)
      return NULL;

    while ( token_end < parse_end && token < &token_buf[0] + sizeof(token_buf))
      *token++= *token_end++;

    *token--= '\0';

    if ( strchr( token_buf, '\\'))
      return parse_value(token_buf, (token - token_buf)+1);

    while ( isspace((unsigned char)*token))
      *token--= '\0';

    value_len= token - token_buf + 1;

    return &token_buf[0];
}

STATIC char *
parse_value( char *source, int len)
{
    char *value, *v, *end;
    int error= 0;

    value= (char*)malloc( len + 1);
    end = v = value;

/*
printf( "parse_value <%.*s>\n", len, source);
*/

    for ( ; len; len--, source++)  {
      if ( *source != '\\')  {
          *v++= *source;
          if ( *source != ' ')
            end= v;
          continue;
      }
      source++; len--;
      if ( len == 0)
          break;
      if ( *source == '\\')
          *v++= *source;
      else if ( *source == 'n')
          *v++= '\n';
      else if ( *source == 'r')
          *v++= '\r';
      else if ( *source == ' ')
          *v++= ' ';
      else if ( *source == 'x')  {
          source++; len--;
          if ( len < 2)
            break;
          *v++= parse_hex(source, 2, &error);
          if ( error)
            break;
          source++; len--;
      }
      else if ( isdigit( (unsigned char)*source))  {
          if ( len < 3)
            break;
          *v++= parse_oct(source, 3, &error);
          if ( error)
            break;
          source++; len--;
          source++; len--;
      }
      else  {
          error= 1;
          REPORT_ERROR(( stderr, "Invalid character escape \"%.*s\"", 2,
            source-1));
          break;
      }
      end= v;
    }

    if ( error)  {
      free(value);
      return NULL;
    }

    value_len= end - value;
    memcpy( token_buf, value, value_len);
    token_buf[value_len]= '\0';

    free(value);
    return &token_buf[0];
}

STATIC unsigned int
parse_hex( char *hex, int len, int *error)
{
    char *save_hex= hex;
    int save_len= len;
    unsigned int result= 0;
    *error= 0;
    for ( ; len; len--, hex++)  {
      result <<= 4;
      if ( ! isxdigit( (unsigned char)*hex))  {
          *error= 1;
          REPORT_ERROR(( stderr, "Invalid hex \"%.*s\"", save_len+2,
            save_hex-2));
          return 0;
      }
      if ( *hex >= '0' && *hex <= '9')
          result|= *hex - '0';
      else if ( *hex >= 'A' && *hex <= 'F')
          result|= ((*hex - 'A') + 10);
      else if ( *hex >= 'a' && *hex <= 'f')
          result|= ((*hex - 'a') + 10);
    }
    return result;
}

STATIC unsigned int
parse_oct( char *oct, int len, int *error)
{
    char *save_oct= oct;
    int save_len= len;
    unsigned int result= 0;
    *error= 0;
    for ( ; len; len--, oct++)  {
      result <<= 3;
      if ( *oct >= '0' && *oct <= '7')
          result|= *oct - '0';
      else  {
          *error= 1;
          REPORT_ERROR(( stderr, "Invalid octal \"%.*s\"", save_len+1,
            save_oct-1));
          return 0;
      }
    }
    return result;
}

STATIC char *
force_lower_case( char *string)
{
    char *s= string;
    for ( ; *s; s++)
      *s= tolower(*s);
    return string;
}

STATIC char *
force_upper_case( char *string)
{
    char *s= string;
    for ( ; *s; s++)
      *s= toupper(*s);
    return string;
}


Generated by  Doxygen 1.6.0   Back to index