1135 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1135 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GLIB - Library of useful routines for C programming
 | |
|  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 | |
|  *
 | |
|  * GScanner: Flexible lexical scanner for general purpose.
 | |
|  * Copyright (C) 1997 Tim Janik
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Library General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library 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
 | |
|  * Library General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Library General Public
 | |
|  * License along with this library; if not, write to the Free
 | |
|  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
|  */
 | |
| #define		__gscanner_c__
 | |
| 
 | |
| #include	<stdlib.h>
 | |
| #include	<string.h>
 | |
| #include	<unistd.h>
 | |
| #include	<errno.h>
 | |
| #include	"glib.h"
 | |
| 
 | |
| 
 | |
| 
 | |
| /* --- defines --- */
 | |
| #define	to_lower(c)				( \
 | |
| 	(guchar) (							\
 | |
| 	  ( (((guchar)(c))>='A' && ((guchar)(c))<='Z') * ('a'-'A') ) +	\
 | |
| 	  ( (((guchar)(c))>=192 && ((guchar)(c))<=214) * (224-192) ) +	\
 | |
| 	  ( (((guchar)(c))>=216 && ((guchar)(c))<=222) * (248-216) ) +	\
 | |
| 	  ((guchar)(c))							\
 | |
| 	)								\
 | |
| )
 | |
| 
 | |
| 
 | |
| /* --- typedefs --- */
 | |
| typedef	struct	_GScannerHashVal	GScannerHashVal;
 | |
| 
 | |
| struct	_GScannerHashVal
 | |
| {
 | |
|   gchar		*key;
 | |
|   gpointer	value;
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| /* --- variables --- */
 | |
| static	GScannerConfig	g_scanner_config_template =
 | |
| {
 | |
|   (
 | |
|    " \t\n"
 | |
|    )			/* cset_skip_characters */,
 | |
|   (
 | |
|    G_CSET_a_2_z
 | |
|    "_"
 | |
|    G_CSET_A_2_Z
 | |
|    )			/* cset_identifier_first */,
 | |
|   (
 | |
|    G_CSET_a_2_z
 | |
|    "_0123456789"
 | |
|    G_CSET_A_2_Z
 | |
|    G_CSET_LATINS
 | |
|    G_CSET_LATINC
 | |
|    )			/* cset_identifier_nth */,
 | |
|   ( "#\n" )		/* cpair_comment_single */,
 | |
|   
 | |
|   FALSE			/* case_sensitive */,
 | |
|   
 | |
|   TRUE			/* skip_comment_multi */,
 | |
|   TRUE			/* skip_comment_single */,
 | |
|   TRUE			/* scan_comment_multi */,
 | |
|   TRUE			/* scan_identifier */,
 | |
|   FALSE			/* scan_identifier_1char */,
 | |
|   FALSE			/* scan_identifier_NULL */,
 | |
|   TRUE			/* scan_symbols */,
 | |
|   FALSE			/* scan_binary */,
 | |
|   TRUE			/* scan_octal */,
 | |
|   TRUE			/* scan_float */,
 | |
|   TRUE			/* scan_hex */,
 | |
|   FALSE			/* scan_hex_dollar */,
 | |
|   TRUE			/* scan_string_sq */,
 | |
|   TRUE			/* scan_string_dq */,
 | |
|   TRUE			/* numbers_2_int */,
 | |
|   FALSE			/* int_2_float */,
 | |
|   FALSE			/* identifier_2_string */,
 | |
|   TRUE			/* char_2_token */,
 | |
|   FALSE			/* symbol_2_token */,
 | |
| };
 | |
| 
 | |
| 
 | |
| /* --- prototypes --- */
 | |
| static	GScannerHashVal* g_scanner_lookup_internal (GScanner	*scanner,
 | |
| 						    const gchar	*symbol);
 | |
| static	void	g_scanner_get_token_ll	(GScanner	*scanner,
 | |
| 					 GTokenType	*token_p,
 | |
| 					 GValue		*value_p,
 | |
| 					 guint		*line_p,
 | |
| 					 guint		*position_p);
 | |
| static	void	g_scanner_get_token_i	(GScanner	*scanner,
 | |
| 					 GTokenType	*token_p,
 | |
| 					 GValue		*value_p,
 | |
| 					 guint		*line_p,
 | |
| 					 guint		*position_p);
 | |
| static	void	g_scanner_free_value	(GTokenType	*token_p,
 | |
| 					 GValue		*value_p);
 | |
| 
 | |
| static  gint	g_scanner_char_2_num	(guchar		c,
 | |
| 					 guchar		base);
 | |
| static	guchar	g_scanner_peek_next_char(GScanner	*scanner);
 | |
| static	guchar	g_scanner_get_char	(GScanner	*scanner,
 | |
| 					 guint		*line_p,
 | |
| 					 guint		*position_p);
 | |
| 
 | |
| 
 | |
| /* --- functions --- */
 | |
| static gint
 | |
| g_scanner_char_2_num (guchar	c,
 | |
| 		      guchar	base)
 | |
| {
 | |
|   if (c >= '0' && c <= '9')
 | |
|     c -= '0';
 | |
|   else if (c >= 'A' && c <= 'Z')
 | |
|     c -= 'A' - 10;
 | |
|   else if (c >= 'a' && c <= 'z')
 | |
|     c -= 'a' - 10;
 | |
|   else
 | |
|     return -1;
 | |
|   
 | |
|   if (c < base)
 | |
|     return c;
 | |
|   
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| GScanner*
 | |
| g_scanner_new (GScannerConfig	*config_templ)
 | |
| {
 | |
|   register GScanner	*scanner;
 | |
|   
 | |
|   if (!config_templ)
 | |
|     config_templ = &g_scanner_config_template;
 | |
|   
 | |
|   scanner = g_new0 (GScanner, 1);
 | |
|   
 | |
|   scanner->user_data = NULL;
 | |
|   scanner->input_name = NULL;
 | |
|   scanner->parse_errors	= 0;
 | |
|   scanner->max_parse_errors = 0;
 | |
|   
 | |
|   scanner->config = g_new0 (GScannerConfig, 1);
 | |
|   
 | |
|   scanner->config->case_sensitive	= config_templ->case_sensitive;
 | |
|   scanner->config->cset_skip_characters	= config_templ->cset_skip_characters;
 | |
|   scanner->config->cset_identifier_first= config_templ->cset_identifier_first;
 | |
|   scanner->config->cset_identifier_nth	= config_templ->cset_identifier_nth;
 | |
|   scanner->config->cpair_comment_single	= config_templ->cpair_comment_single;
 | |
|   scanner->config->skip_comment_multi	= config_templ->skip_comment_multi;
 | |
|   scanner->config->skip_comment_single	= config_templ->skip_comment_single;
 | |
|   scanner->config->scan_comment_multi	= config_templ->scan_comment_multi;
 | |
|   scanner->config->scan_identifier	= config_templ->scan_identifier;
 | |
|   scanner->config->scan_identifier_1char= config_templ->scan_identifier_1char;
 | |
|   scanner->config->scan_identifier_NULL	= config_templ->scan_identifier_NULL;
 | |
|   scanner->config->scan_symbols		= config_templ->scan_symbols;
 | |
|   scanner->config->scan_binary		= config_templ->scan_binary;
 | |
|   scanner->config->scan_octal		= config_templ->scan_octal;
 | |
|   scanner->config->scan_float		= config_templ->scan_float;
 | |
|   scanner->config->scan_hex		= config_templ->scan_hex;
 | |
|   scanner->config->scan_hex_dollar	= config_templ->scan_hex_dollar;
 | |
|   scanner->config->scan_string_sq	= config_templ->scan_string_sq;
 | |
|   scanner->config->scan_string_dq	= config_templ->scan_string_dq;
 | |
|   scanner->config->numbers_2_int	= config_templ->numbers_2_int;
 | |
|   scanner->config->int_2_float		= config_templ->int_2_float;
 | |
|   scanner->config->identifier_2_string	= config_templ->identifier_2_string;
 | |
|   scanner->config->char_2_token		= config_templ->char_2_token;
 | |
|   scanner->config->symbol_2_token	= config_templ->symbol_2_token;
 | |
|   
 | |
|   scanner->token = G_TOKEN_NONE;
 | |
|   scanner->value.v_int = 0;
 | |
|   scanner->line = 1;
 | |
|   scanner->position = 0;
 | |
|   
 | |
|   scanner->next_token = G_TOKEN_NONE;
 | |
|   scanner->next_value.v_int = 0;
 | |
|   scanner->next_line = 1;
 | |
|   scanner->next_position = 0;
 | |
|   
 | |
|   scanner->symbol_table = g_hash_table_new (g_string_hash, g_string_equal);
 | |
|   scanner->text = NULL;
 | |
|   scanner->text_len = 0;
 | |
|   scanner->input_fd = -1;
 | |
|   scanner->peeked_char = -1;
 | |
|   
 | |
|   return scanner;
 | |
| }
 | |
| 
 | |
| void
 | |
| g_scanner_destroy (GScanner	*scanner)
 | |
| {
 | |
|   g_return_if_fail (scanner != NULL);
 | |
|   
 | |
|   g_hash_table_destroy (scanner->symbol_table);
 | |
|   g_scanner_free_value (&scanner->token, &scanner->value);
 | |
|   g_scanner_free_value (&scanner->next_token, &scanner->next_value);
 | |
|   g_free (scanner->config);
 | |
|   g_free (scanner);
 | |
| }
 | |
| 
 | |
| void
 | |
| g_scanner_input_file (GScanner	*scanner,
 | |
| 		      gint	input_fd)
 | |
| {
 | |
|   g_return_if_fail (input_fd >= 0);
 | |
|   
 | |
|   scanner->token = G_TOKEN_NONE;
 | |
|   scanner->value.v_int = 0;
 | |
|   scanner->line = 1;
 | |
|   scanner->position = 0;
 | |
|   scanner->next_token = G_TOKEN_NONE;
 | |
|   
 | |
|   scanner->text = NULL;
 | |
|   scanner->text_len = 0;
 | |
|   scanner->input_fd = input_fd;
 | |
|   scanner->peeked_char = -1;
 | |
| }
 | |
| 
 | |
| void
 | |
| g_scanner_input_text (GScanner	     *scanner,
 | |
| 		      const  gchar   *text,
 | |
| 		      guint	     text_len)
 | |
| {
 | |
|   g_return_if_fail (text != NULL);
 | |
|   
 | |
|   scanner->token = G_TOKEN_NONE;
 | |
|   scanner->value.v_int = 0;
 | |
|   scanner->line = 1;
 | |
|   scanner->position = 0;
 | |
|   scanner->next_token = G_TOKEN_NONE;
 | |
|   
 | |
|   scanner->text = text;
 | |
|   scanner->text_len = text_len;
 | |
|   scanner->input_fd = -1;
 | |
|   scanner->peeked_char = -1;
 | |
| }
 | |
| 
 | |
| void
 | |
| g_scanner_add_symbol (GScanner		*scanner,
 | |
| 		      const gchar	*symbol,
 | |
| 		      gpointer		value)
 | |
| {
 | |
|   register GScannerHashVal	*hash_val;
 | |
|   
 | |
|   g_return_if_fail (symbol != NULL);
 | |
|   g_return_if_fail (scanner != NULL);
 | |
|   
 | |
|   hash_val = g_scanner_lookup_internal (scanner, symbol);
 | |
|   
 | |
|   if (!hash_val)
 | |
|     {
 | |
|       hash_val = g_new (GScannerHashVal, 1);
 | |
|       hash_val->key = g_strdup (symbol);
 | |
|       hash_val->value = value;
 | |
|       if (!scanner->config->case_sensitive)
 | |
| 	{
 | |
| 	  register guint	i, l;
 | |
| 	  
 | |
| 	  l = strlen (hash_val->key);
 | |
| 	  for (i = 0; i < l; i++)
 | |
| 	    hash_val->key[i] = to_lower (hash_val->key[i]);
 | |
| 	}
 | |
|       g_hash_table_insert (scanner->symbol_table, hash_val->key, hash_val);
 | |
|     }
 | |
|   else
 | |
|     hash_val->value = value;
 | |
| }
 | |
| 
 | |
| gpointer
 | |
| g_scanner_lookup_symbol (GScanner	*scanner,
 | |
| 			 const gchar	*symbol)
 | |
| {
 | |
|   register GScannerHashVal	*hash_val;
 | |
|   
 | |
|   g_return_val_if_fail (scanner != NULL, NULL);
 | |
|   
 | |
|   if (!symbol)
 | |
|     return NULL;
 | |
|   
 | |
|   hash_val = g_scanner_lookup_internal (scanner, symbol);
 | |
|   
 | |
|   if (hash_val)
 | |
|     return hash_val->value;
 | |
|   else
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| g_scanner_remove_symbol (GScanner	*scanner,
 | |
| 			 const gchar	*symbol)
 | |
| {
 | |
|   register GScannerHashVal	*hash_val;
 | |
|   
 | |
|   hash_val = g_scanner_lookup_internal (scanner, symbol);
 | |
|   
 | |
|   if (hash_val)
 | |
|     {
 | |
|       g_hash_table_remove (scanner->symbol_table, hash_val->key);
 | |
|       g_free (hash_val->key);
 | |
|       g_free (hash_val);
 | |
|     }
 | |
| }
 | |
| 
 | |
| GTokenType
 | |
| g_scanner_peek_next_token (GScanner	*scanner)
 | |
| {
 | |
|   g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
 | |
|   
 | |
|   if (scanner->next_token == G_TOKEN_NONE)
 | |
|     {
 | |
|       scanner->next_line = scanner->line;
 | |
|       scanner->next_position = scanner->position;
 | |
|       g_scanner_get_token_i (scanner,
 | |
| 			     &scanner->next_token,
 | |
| 			     &scanner->next_value,
 | |
| 			     &scanner->next_line,
 | |
| 			     &scanner->next_position);
 | |
|     }
 | |
|   
 | |
|   return scanner->next_token;
 | |
| }
 | |
| 
 | |
| GTokenType
 | |
| g_scanner_get_next_token (GScanner	*scanner)
 | |
| {
 | |
|   g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
 | |
|   
 | |
|   if (scanner->next_token != G_TOKEN_NONE)
 | |
|     {
 | |
|       g_scanner_free_value (&scanner->token, &scanner->value);
 | |
|       
 | |
|       scanner->token = scanner->next_token;
 | |
|       scanner->value = scanner->next_value;
 | |
|       scanner->line = scanner->next_line;
 | |
|       scanner->position = scanner->next_position;
 | |
|       scanner->next_token = G_TOKEN_NONE;
 | |
|     }
 | |
|   else
 | |
|     g_scanner_get_token_i (scanner,
 | |
| 			   &scanner->token,
 | |
| 			   &scanner->value,
 | |
| 			   &scanner->line,
 | |
| 			   &scanner->position);
 | |
|   
 | |
|   return scanner->token;
 | |
| }
 | |
| 
 | |
| GTokenType
 | |
| g_scanner_cur_token (GScanner *scanner)
 | |
| {
 | |
|   g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
 | |
|   
 | |
|   return scanner->token;
 | |
| }
 | |
| 
 | |
| GValue
 | |
| g_scanner_cur_value (GScanner *scanner)
 | |
| {
 | |
|   register GValue v;
 | |
|   
 | |
|   v.v_int = 0;
 | |
|   g_return_val_if_fail (scanner != NULL, v);
 | |
|   
 | |
|   return scanner->value;
 | |
| }
 | |
| 
 | |
| guint
 | |
| g_scanner_cur_line (GScanner *scanner)
 | |
| {
 | |
|   g_return_val_if_fail (scanner != NULL, 0);
 | |
|   
 | |
|   return scanner->line;
 | |
| }
 | |
| 
 | |
| guint
 | |
| g_scanner_cur_position (GScanner *scanner)
 | |
| {
 | |
|   g_return_val_if_fail (scanner != NULL, 0);
 | |
|   
 | |
|   return scanner->position;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| g_scanner_eof (GScanner	*scanner)
 | |
| {
 | |
|   g_return_val_if_fail (scanner != NULL, TRUE);
 | |
|   
 | |
|   return scanner->token == G_TOKEN_EOF;
 | |
| }
 | |
| 
 | |
| static GScannerHashVal*
 | |
| g_scanner_lookup_internal (GScanner	*scanner,
 | |
| 			   const gchar	*symbol)
 | |
| {
 | |
|   register GScannerHashVal	*hash_val;
 | |
|   
 | |
|   if (!scanner->config->case_sensitive)
 | |
|     {
 | |
|       register gchar *buffer;
 | |
|       register guint i, l;
 | |
|       
 | |
|       l = strlen (symbol);
 | |
|       buffer = g_new (gchar, l + 1);
 | |
|       for (i = 0; i < l; i++)
 | |
| 	buffer[i] = to_lower (symbol[i]);
 | |
|       buffer[i] = 0;
 | |
|       hash_val = g_hash_table_lookup (scanner->symbol_table, buffer);
 | |
|       g_free (buffer);
 | |
|     }
 | |
|   else
 | |
|     hash_val = g_hash_table_lookup (scanner->symbol_table, (gchar*) symbol);
 | |
|   
 | |
|   return hash_val;
 | |
| }
 | |
| 
 | |
| static guchar
 | |
| g_scanner_peek_next_char (GScanner *scanner)
 | |
| {
 | |
|   guchar fchar;
 | |
|   
 | |
|   if (scanner->text_len)
 | |
|     {
 | |
|       fchar = scanner->text[0];
 | |
|     }
 | |
|   else if (scanner->input_fd >= 0)
 | |
|     {
 | |
|       if (scanner->peeked_char < 0)
 | |
| 	{
 | |
| 	  register gint count;
 | |
| 	  
 | |
| 	  do
 | |
| 	    {
 | |
| 	      count = read (scanner->input_fd, &fchar, 1);
 | |
| 	    }
 | |
| 	  while (count == -1 &&
 | |
| 		 (errno == EINTR ||
 | |
| 		  errno == EAGAIN));
 | |
| 	  
 | |
| 	  if (count != 1)
 | |
| 	    fchar = 0;
 | |
| 	  
 | |
| 	  scanner->peeked_char = fchar;
 | |
| 	}
 | |
|       else
 | |
| 	fchar = scanner->peeked_char;
 | |
|     }
 | |
|   else
 | |
|     fchar = 0;
 | |
|   
 | |
|   return fchar;
 | |
| }
 | |
| 
 | |
| static guchar
 | |
| g_scanner_get_char (GScanner	*scanner,
 | |
| 		    guint	*line_p,
 | |
| 		    guint	*position_p)
 | |
| {
 | |
|   guchar fchar;
 | |
|   
 | |
|   if (scanner->text_len)
 | |
|     {
 | |
|       fchar = *(scanner->text++);
 | |
|       scanner->text_len--;
 | |
|     }
 | |
|   else if (scanner->input_fd >= 0)
 | |
|     {
 | |
|       if (scanner->peeked_char < 0)
 | |
| 	{
 | |
| 	  register gint count;
 | |
| 	  
 | |
| 	  do
 | |
| 	    {
 | |
| 	      count = read (scanner->input_fd, &fchar, 1);
 | |
| 	    }
 | |
| 	  while (count == -1 &&
 | |
| 		 (errno == EINTR ||
 | |
| 		  errno == EAGAIN));
 | |
| 	  if (count != 1 || fchar == 0)
 | |
| 	    {
 | |
| 	      fchar = 0;
 | |
| 	      scanner->peeked_char = 0;
 | |
| 	    }
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  fchar = scanner->peeked_char;
 | |
| 	  if (fchar)
 | |
| 	    scanner->peeked_char = -1;
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     fchar = 0;
 | |
|   
 | |
|   if (fchar == '\n')
 | |
|     {
 | |
|       (*position_p) = 0;
 | |
|       (*line_p)++;
 | |
|     }
 | |
|   else if (fchar)
 | |
|     {
 | |
|       (*position_p)++;
 | |
|     }
 | |
|   
 | |
|   return fchar;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_scanner_free_value (GTokenType     *token_p,
 | |
| 		      GValue	     *value_p)
 | |
| {
 | |
|   switch (*token_p)
 | |
|     {
 | |
|     case  G_TOKEN_STRING:
 | |
|     case  G_TOKEN_IDENTIFIER:
 | |
|     case  G_TOKEN_IDENTIFIER_NULL:
 | |
|     case  G_TOKEN_COMMENT_SINGLE:
 | |
|     case  G_TOKEN_COMMENT_MULTI:
 | |
|       g_free (value_p->v_string);
 | |
|       break;
 | |
|       
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|   
 | |
|   *token_p = G_TOKEN_NONE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_scanner_get_token_i (GScanner	*scanner,
 | |
| 		       GTokenType	*token_p,
 | |
| 		       GValue		*value_p,
 | |
| 		       guint		*line_p,
 | |
| 		       guint		*position_p)
 | |
| {
 | |
|   do
 | |
|     {
 | |
|       g_scanner_free_value (token_p, value_p);
 | |
|       g_scanner_get_token_ll (scanner, token_p, value_p, line_p, position_p);
 | |
|     }
 | |
|   while (((*token_p > 0 && *token_p < 256) &&
 | |
| 	  strchr (scanner->config->cset_skip_characters, *token_p)) ||
 | |
| 	 (*token_p == G_TOKEN_CHAR &&
 | |
| 	  strchr (scanner->config->cset_skip_characters, value_p->v_char)) ||
 | |
| 	 (*token_p == G_TOKEN_COMMENT_MULTI &&
 | |
| 	  scanner->config->skip_comment_multi) ||
 | |
| 	 (*token_p == G_TOKEN_COMMENT_SINGLE &&
 | |
| 	  scanner->config->skip_comment_single));
 | |
|   
 | |
|   switch (*token_p)
 | |
|     {
 | |
|     case	G_TOKEN_IDENTIFIER:
 | |
|       if (scanner->config->identifier_2_string)
 | |
| 	*token_p = G_TOKEN_STRING;
 | |
|       break;
 | |
|       
 | |
|     case	G_TOKEN_SYMBOL:
 | |
|       if (scanner->config->symbol_2_token)
 | |
| 	*token_p = (GTokenType) value_p->v_symbol;
 | |
|       break;
 | |
|       
 | |
|     case	G_TOKEN_BINARY:
 | |
|     case	G_TOKEN_OCTAL:
 | |
|     case	G_TOKEN_HEX:
 | |
|       if (scanner->config->numbers_2_int)
 | |
| 	*token_p = G_TOKEN_INT;
 | |
|       break;
 | |
|       
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|   
 | |
|   if (*token_p == G_TOKEN_INT &&
 | |
|       scanner->config->int_2_float)
 | |
|     {
 | |
|       *token_p = G_TOKEN_FLOAT;
 | |
|       value_p->v_float = value_p->v_int;
 | |
|     }
 | |
|   
 | |
|   errno = 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_scanner_get_token_ll	(GScanner	*scanner,
 | |
| 			 GTokenType	*token_p,
 | |
| 			 GValue		*value_p,
 | |
| 			 guint		*line_p,
 | |
| 			 guint		*position_p)
 | |
| {
 | |
|   register GScannerConfig	*config;
 | |
|   register gboolean		in_comment_multi;
 | |
|   register gboolean		in_comment_single;
 | |
|   register gboolean		in_string_sq;
 | |
|   register gboolean		in_string_dq;
 | |
|   static   guchar		ch;
 | |
|   register GTokenType		token;
 | |
|   register GValue		value;
 | |
|   register GString		*gstring;
 | |
|   
 | |
|   config = scanner->config;
 | |
|   (*value_p).v_int = 0;
 | |
|   
 | |
|   if (scanner->token == G_TOKEN_EOF ||
 | |
|       (!scanner->text_len &&
 | |
|        (scanner->input_fd < 0 ||
 | |
| 	scanner->peeked_char == 0)))
 | |
|     {
 | |
|       *token_p = G_TOKEN_EOF;
 | |
|       return;
 | |
|     }
 | |
|   
 | |
|   in_comment_multi = FALSE;
 | |
|   in_comment_single = FALSE;
 | |
|   in_string_sq = FALSE;
 | |
|   in_string_dq = FALSE;
 | |
|   gstring = NULL;
 | |
|   
 | |
|   do
 | |
|     {
 | |
|       ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
|       
 | |
|       value.v_int = 0;
 | |
|       token = G_TOKEN_NONE;
 | |
|       
 | |
|       /* this is *evil*, but needed ;(
 | |
|        * we first check for identifier first character, because	 it
 | |
|        * might interfere with other key chars like slashes or numbers
 | |
|        */
 | |
|       if (config->scan_identifier &&
 | |
| 	  ch && strchr (config->cset_identifier_first, ch))
 | |
| 	goto identifier_precedence;
 | |
|       
 | |
|       switch (ch)
 | |
| 	{
 | |
| 	  register gboolean	in_number;
 | |
| 	  static	 gchar		*endptr;
 | |
| 	  
 | |
| 	case  0:
 | |
| 	  token = G_TOKEN_EOF;
 | |
| 	  (*position_p)++;
 | |
| 	  ch = 0;
 | |
| 	  break;
 | |
| 	  
 | |
| 	case  '/':
 | |
| 	  if (!config->scan_comment_multi ||
 | |
| 	      g_scanner_peek_next_char (scanner) != '*')
 | |
| 	    goto default_case;
 | |
| 	  g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	  token = G_TOKEN_COMMENT_MULTI;
 | |
| 	  in_comment_multi = TRUE;
 | |
| 	  gstring = g_string_new ("");
 | |
| 	  while ((ch = g_scanner_get_char (scanner, line_p, position_p)) != 0)
 | |
| 	    {
 | |
| 	      if (ch == '*' && g_scanner_peek_next_char (scanner) == '/')
 | |
| 		{
 | |
| 		  g_scanner_get_char (scanner, line_p, position_p);
 | |
| 		  in_comment_multi = FALSE;
 | |
| 		  break;
 | |
| 		}
 | |
| 	      else
 | |
| 		gstring = g_string_append_c (gstring, ch);
 | |
| 	    }
 | |
| 	  ch = 0;
 | |
| 	  break;
 | |
| 	  
 | |
| 	case  '\'':
 | |
| 	  if (!config->scan_string_sq)
 | |
| 	    goto default_case;
 | |
| 	  token = G_TOKEN_STRING;
 | |
| 	  in_string_sq = TRUE;
 | |
| 	  gstring = g_string_new ("");
 | |
| 	  while ((ch = g_scanner_get_char (scanner, line_p, position_p)) != 0)
 | |
| 	    {
 | |
| 	      if (ch == '\'')
 | |
| 		{
 | |
| 		  in_string_sq = FALSE;
 | |
| 		  break;
 | |
| 		}
 | |
| 	      else
 | |
| 		gstring = g_string_append_c (gstring, ch);
 | |
| 	    }
 | |
| 	  ch = 0;
 | |
| 	  break;
 | |
| 	  
 | |
| 	case  '"':
 | |
| 	  if (!config->scan_string_dq)
 | |
| 	    goto default_case;
 | |
| 	  token = G_TOKEN_STRING;
 | |
| 	  in_string_dq = TRUE;
 | |
| 	  gstring = g_string_new ("");
 | |
| 	  while ((ch = g_scanner_get_char (scanner, line_p, position_p)) != 0)
 | |
| 	    {
 | |
| 	      if (ch == '"')
 | |
| 		{
 | |
| 		  in_string_dq = FALSE;
 | |
| 		  break;
 | |
| 		}
 | |
| 	      else
 | |
| 		{
 | |
| 		  if (ch == '\\')
 | |
| 		    {
 | |
| 		      ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 		      switch (ch)
 | |
| 			{
 | |
| 			  register guint	i;
 | |
| 			  register guint	fchar;
 | |
| 			  
 | |
| 			case  0:
 | |
| 			  break;
 | |
| 			  
 | |
| 			case  '\\':
 | |
| 			  gstring = g_string_append_c (gstring, '\\');
 | |
| 			  break;
 | |
| 			  
 | |
| 			case  'n':
 | |
| 			  gstring = g_string_append_c (gstring, '\n');
 | |
| 			  break;
 | |
| 			  
 | |
| 			case  't':
 | |
| 			  gstring = g_string_append_c (gstring, '\t');
 | |
| 			  break;
 | |
| 			  
 | |
| 			case  'r':
 | |
| 			  gstring = g_string_append_c (gstring, '\r');
 | |
| 			  break;
 | |
| 			  
 | |
| 			case  'b':
 | |
| 			  gstring = g_string_append_c (gstring, '\b');
 | |
| 			  break;
 | |
| 			  
 | |
| 			case  'f':
 | |
| 			  gstring = g_string_append_c (gstring, '\f');
 | |
| 			  break;
 | |
| 			  
 | |
| 			case  '0':
 | |
| 			case  '1':
 | |
| 			case  '2':
 | |
| 			case  '3':
 | |
| 			case  '4':
 | |
| 			case  '5':
 | |
| 			case  '6':
 | |
| 			case  '7':
 | |
| 			  i = ch - '0';
 | |
| 			  fchar = g_scanner_peek_next_char (scanner);
 | |
| 			  if (fchar >= '0' && fchar <= '7')
 | |
| 			    {
 | |
| 			      ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 			      i= i * 8 + ch - '0';
 | |
| 			      fchar = g_scanner_peek_next_char (scanner);
 | |
| 			      if (fchar >= '0' && fchar <= '7')
 | |
| 				{
 | |
| 				  ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 				  i = i * 8 + ch - '0';
 | |
| 				}
 | |
| 			    }
 | |
| 			  gstring = g_string_append_c (gstring, i);
 | |
| 			  break;
 | |
| 			  
 | |
| 			default:
 | |
| 			  gstring = g_string_append_c (gstring, ch);
 | |
| 			  break;
 | |
| 			}
 | |
| 		    }
 | |
| 		  else
 | |
| 		    gstring = g_string_append_c (gstring, ch);
 | |
| 		}
 | |
| 	    }
 | |
| 	  ch = 0;
 | |
| 	  break;
 | |
| 	  
 | |
| 	case  '.':
 | |
| 	  if (!config->scan_float)
 | |
| 	    goto default_case;
 | |
| 	  token = G_TOKEN_FLOAT;
 | |
| 	  ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	  goto number_parsing;
 | |
| 	  
 | |
| 	case  '$':
 | |
| 	  if (!config->scan_hex_dollar)
 | |
| 	    goto default_case;
 | |
| 	  token = G_TOKEN_HEX;
 | |
| 	  ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	  goto number_parsing;
 | |
| 	  
 | |
| 	case  '0':
 | |
| 	  if (config->scan_octal)
 | |
| 	    token = G_TOKEN_OCTAL;
 | |
| 	  else
 | |
| 	    token = G_TOKEN_INT;
 | |
| 	  ch = g_scanner_peek_next_char (scanner);
 | |
| 	  if (config->scan_hex && (ch == 'x' || ch == 'X'))
 | |
| 	    {
 | |
| 	      token = G_TOKEN_HEX;
 | |
| 	      g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	      ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	      if (ch == 0)
 | |
| 		{
 | |
| 		  token = G_TOKEN_ERROR;
 | |
| 		  value.v_error = G_ERR_UNEXP_EOF;
 | |
| 		  (*position_p)++;
 | |
| 		  break;
 | |
| 		}
 | |
| 	      if (g_scanner_char_2_num (ch, 16) < 0)
 | |
| 		{
 | |
| 		  token = G_TOKEN_ERROR;
 | |
| 		  value.v_error = G_ERR_DIGIT_RADIX;
 | |
| 		  ch = 0;
 | |
| 		  break;
 | |
| 		}
 | |
| 	    }
 | |
| 	  else if (config->scan_binary && (ch == 'b' || ch == 'B'))
 | |
| 	    {
 | |
| 	      token = G_TOKEN_BINARY;
 | |
| 	      g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	      ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	      if (ch == 0)
 | |
| 		{
 | |
| 		  token = G_TOKEN_ERROR;
 | |
| 		  value.v_error = G_ERR_UNEXP_EOF;
 | |
| 		  (*position_p)++;
 | |
| 		  break;
 | |
| 		}
 | |
| 	      if (g_scanner_char_2_num (ch, 10) < 0)
 | |
| 		{
 | |
| 		  token = G_TOKEN_ERROR;
 | |
| 		  value.v_error = G_ERR_NON_DIGIT_IN_CONST;
 | |
| 		  ch = 0;
 | |
| 		  break;
 | |
| 		}
 | |
| 	    }
 | |
| 	  else
 | |
| 	    ch = '0';
 | |
| 	  /* fall through */
 | |
| 	case  '1':
 | |
| 	case  '2':
 | |
| 	case  '3':
 | |
| 	case  '4':
 | |
| 	case  '5':
 | |
| 	case  '6':
 | |
| 	case  '7':
 | |
| 	case  '8':
 | |
| 	case  '9':
 | |
| 	number_parsing:
 | |
| 	if (token == G_TOKEN_NONE)
 | |
| 	  token = G_TOKEN_INT;
 | |
| 	
 | |
| 	gstring = g_string_new ("");
 | |
| 	gstring = g_string_append_c (gstring, ch);
 | |
| 	in_number = TRUE;
 | |
| 	while (in_number)
 | |
| 	  {
 | |
| 	    register gboolean is_E;
 | |
| 	    
 | |
| 	    is_E = (ch == 'e' || ch == 'E') && token == G_TOKEN_FLOAT;
 | |
| 	    ch = g_scanner_peek_next_char (scanner);
 | |
| 	    
 | |
| 	    if (g_scanner_char_2_num (ch, 36) >= 0 ||
 | |
| 		(config->scan_float && ch == '.') ||
 | |
| 		(is_E && ch == '+') ||
 | |
| 		(is_E && ch == '-') )
 | |
| 	      ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 	    else
 | |
| 	      in_number = FALSE;
 | |
| 	    
 | |
| 	    if (in_number)
 | |
| 	      switch (ch)
 | |
| 		{
 | |
| 		case  '.':
 | |
| 		  if (token != G_TOKEN_INT &&
 | |
| 		      token != G_TOKEN_OCTAL)
 | |
| 		    {
 | |
| 		      token = G_TOKEN_ERROR;
 | |
| 		      if (token == G_TOKEN_FLOAT)
 | |
| 			value.v_error = G_ERR_FLOAT_MALFORMED;
 | |
| 		      else
 | |
| 			value.v_error = G_ERR_FLOAT_RADIX;
 | |
| 		      in_number = FALSE;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      token = G_TOKEN_FLOAT;
 | |
| 		      gstring = g_string_append_c (gstring, ch);
 | |
| 		    }
 | |
| 		  break;
 | |
| 		  
 | |
| 		case	'0':
 | |
| 		case  '1':
 | |
| 		case  '2':
 | |
| 		case  '3':
 | |
| 		case  '4':
 | |
| 		case  '5':
 | |
| 		case  '6':
 | |
| 		case  '7':
 | |
| 		case  '8':
 | |
| 		case  '9':
 | |
| 		  gstring = g_string_append_c (gstring, ch);
 | |
| 		  break;
 | |
| 		  
 | |
| 		case	'-':
 | |
| 		case	'+':
 | |
| 		  if (token != G_TOKEN_FLOAT)
 | |
| 		    {
 | |
| 		      token = G_TOKEN_ERROR;
 | |
| 		      value.v_error = G_ERR_NON_DIGIT_IN_CONST;
 | |
| 		      in_number = FALSE;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    gstring = g_string_append_c (gstring, ch);
 | |
| 		  break;
 | |
| 		  
 | |
| 		case	'e':
 | |
| 		case	'E':
 | |
| 		  if ((token != G_TOKEN_HEX && !config->scan_float) ||
 | |
| 		      (token != G_TOKEN_HEX &&
 | |
| 		       token != G_TOKEN_OCTAL &&
 | |
| 		       token != G_TOKEN_FLOAT &&
 | |
| 		       token != G_TOKEN_INT))
 | |
| 		    {
 | |
| 		      token = G_TOKEN_ERROR;
 | |
| 		      value.v_error = G_ERR_NON_DIGIT_IN_CONST;
 | |
| 		      in_number = FALSE;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      if (token != G_TOKEN_HEX)
 | |
| 			token = G_TOKEN_FLOAT;
 | |
| 		      gstring = g_string_append_c (gstring, ch);
 | |
| 		    }
 | |
| 		  break;
 | |
| 		  
 | |
| 		default:
 | |
| 		  if (token != G_TOKEN_HEX)
 | |
| 		    {
 | |
| 		      token = G_TOKEN_ERROR;
 | |
| 		      value.v_error = G_ERR_NON_DIGIT_IN_CONST;
 | |
| 		      in_number = FALSE;
 | |
| 		    }
 | |
| 		  else
 | |
| 		    gstring = g_string_append_c (gstring, ch);
 | |
| 		  break;
 | |
| 		}
 | |
| 	  }
 | |
| 	endptr = NULL;
 | |
| 	switch (token)
 | |
| 	  {
 | |
| 	  case	G_TOKEN_BINARY:
 | |
| 	    value.v_binary = strtoul (gstring->str, &endptr, 2);
 | |
| 	    break;
 | |
| 	    
 | |
| 	  case	G_TOKEN_OCTAL:
 | |
| 	    value.v_octal = strtoul (gstring->str, &endptr, 8);
 | |
| 	    break;
 | |
| 	    
 | |
| 	  case	G_TOKEN_INT:
 | |
| 	    value.v_int = strtoul (gstring->str, &endptr, 10);
 | |
| 	    break;
 | |
| 	    
 | |
| 	  case	G_TOKEN_FLOAT:
 | |
| 	    value.v_float = g_strtod (gstring->str, &endptr);
 | |
| 	    break;
 | |
| 	    
 | |
| 	  case	G_TOKEN_HEX:
 | |
| 	    value.v_hex = strtoul (gstring->str, &endptr, 16);
 | |
| 	    break;
 | |
| 	    
 | |
| 	  default:
 | |
| 	    break;
 | |
| 	  }
 | |
| 	if (endptr && *endptr)
 | |
| 	  {
 | |
| 	    token = G_TOKEN_ERROR;
 | |
| 	    if (*endptr == 'e' || *endptr == 'E')
 | |
| 	      value.v_error = G_ERR_NON_DIGIT_IN_CONST;
 | |
| 	    else
 | |
| 	      value.v_error = G_ERR_DIGIT_RADIX;
 | |
| 	  }
 | |
| 	g_string_free (gstring, TRUE);
 | |
| 	gstring = NULL;
 | |
| 	ch = 0;
 | |
| 	break;
 | |
| 	
 | |
| 	default:
 | |
| 	default_case:
 | |
| 	if (config->cpair_comment_single &&
 | |
| 	    ch == config->cpair_comment_single[0])
 | |
| 	  {
 | |
| 	    token = G_TOKEN_COMMENT_SINGLE;
 | |
| 	    in_comment_single = TRUE;
 | |
| 	    gstring = g_string_new ("");
 | |
| 	    while ((ch = g_scanner_get_char (scanner,
 | |
| 					     line_p,
 | |
| 					     position_p)) != 0)
 | |
| 	      {
 | |
| 		if (ch == config->cpair_comment_single[1])
 | |
| 		  {
 | |
| 		    in_comment_single = FALSE;
 | |
| 		    ch = 0;
 | |
| 		    break;
 | |
| 		  }
 | |
| 		
 | |
| 		gstring = g_string_append_c (gstring, ch);
 | |
| 		ch = 0;
 | |
| 	      }
 | |
| 	  }
 | |
| 	else if (config->scan_identifier && ch &&
 | |
| 		 strchr (config->cset_identifier_first, ch))
 | |
| 	  {
 | |
| 	  identifier_precedence:
 | |
| 	    
 | |
| 	    if (config->cset_identifier_nth && ch &&
 | |
| 		strchr (config->cset_identifier_nth,
 | |
| 			g_scanner_peek_next_char (scanner)))
 | |
| 	      {
 | |
| 		token = G_TOKEN_IDENTIFIER;
 | |
| 		gstring = g_string_new ("");
 | |
| 		gstring = g_string_append_c (gstring, ch);
 | |
| 		do
 | |
| 		  {
 | |
| 		    ch = g_scanner_get_char (scanner, line_p, position_p);
 | |
| 		    gstring = g_string_append_c (gstring, ch);
 | |
| 		    ch = g_scanner_peek_next_char (scanner);
 | |
| 		  }
 | |
| 		while (ch && strchr (config->cset_identifier_nth, ch));
 | |
| 		ch = 0;
 | |
| 	      }
 | |
| 	    else if (config->scan_identifier_1char)
 | |
| 	      {
 | |
| 		token = G_TOKEN_IDENTIFIER;
 | |
| 		value.v_identifier = g_new0 (gchar, 2);
 | |
| 		value.v_identifier[0] = ch;
 | |
| 		ch = 0;
 | |
| 	      }
 | |
| 	  }
 | |
| 	if (ch)
 | |
| 	  {
 | |
| 	    if (config->char_2_token)
 | |
| 	      token = ch;
 | |
| 	    else
 | |
| 	      {
 | |
| 		token = G_TOKEN_CHAR;
 | |
| 		value.v_char = ch;
 | |
| 	      }
 | |
| 	    ch = 0;
 | |
| 	  }
 | |
| 	break;
 | |
| 	}
 | |
|       g_assert (ch == 0 && token != G_TOKEN_NONE);
 | |
|     }
 | |
|   while (ch != 0);
 | |
|   
 | |
|   if (in_comment_multi ||
 | |
|       in_comment_single ||
 | |
|       in_string_sq ||
 | |
|       in_string_dq)
 | |
|     {
 | |
|       token = G_TOKEN_ERROR;
 | |
|       if (gstring)
 | |
| 	{
 | |
| 	  g_string_free (gstring, TRUE);
 | |
| 	  gstring = NULL;
 | |
| 	}
 | |
|       (*position_p)++;
 | |
|       if (in_comment_multi || in_comment_single)
 | |
| 	value.v_error = G_ERR_UNEXP_EOF_IN_COMMENT;
 | |
|       else if (in_string_sq || in_string_dq)
 | |
| 	value.v_error = G_ERR_UNEXP_EOF_IN_STRING;
 | |
|     }
 | |
|   
 | |
|   if (gstring)
 | |
|     {
 | |
|       value.v_string = gstring->str;
 | |
|       g_string_free (gstring, FALSE);
 | |
|       gstring = NULL;
 | |
|     }
 | |
|   
 | |
|   if (token == G_TOKEN_IDENTIFIER &&
 | |
|       config->scan_symbols)
 | |
|     {
 | |
|       register GScannerHashVal	*hash_val;
 | |
|       
 | |
|       hash_val = g_scanner_lookup_internal (scanner, value.v_identifier);
 | |
|       
 | |
|       if (hash_val)
 | |
| 	{
 | |
| 	  g_free (value.v_identifier);
 | |
| 	  token = G_TOKEN_SYMBOL;
 | |
| 	  value.v_symbol = hash_val->value;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   if (token == G_TOKEN_IDENTIFIER &&
 | |
|       config->scan_identifier_NULL &&
 | |
|       strlen (value.v_identifier) == 4)
 | |
|     {
 | |
|       gchar *null_upper = "NULL";
 | |
|       gchar *null_lower = "null";
 | |
|       
 | |
|       if (scanner->config->case_sensitive)
 | |
| 	{
 | |
| 	  if (value.v_identifier[0] == null_upper[0] &&
 | |
| 	      value.v_identifier[1] == null_upper[1] &&
 | |
| 	      value.v_identifier[2] == null_upper[2] &&
 | |
| 	      value.v_identifier[3] == null_upper[3])
 | |
| 	    token = G_TOKEN_IDENTIFIER_NULL;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  if ((value.v_identifier[0] == null_upper[0] ||
 | |
| 	       value.v_identifier[0] == null_lower[0]) &&
 | |
| 	      (value.v_identifier[1] == null_upper[1] ||
 | |
| 	       value.v_identifier[1] == null_lower[1]) &&
 | |
| 	      (value.v_identifier[2] == null_upper[2] ||
 | |
| 	       value.v_identifier[2] == null_lower[2]) &&
 | |
| 	      (value.v_identifier[3] == null_upper[3] ||
 | |
| 	       value.v_identifier[3] == null_lower[3]))
 | |
| 	    token = G_TOKEN_IDENTIFIER_NULL;
 | |
| 	}
 | |
|     }
 | |
|   
 | |
|   *token_p = token;
 | |
|   *value_p = value;
 | |
| }
 | 
