/* Copyright (C) 1995 Gerald Schueller. */
/* Copyright (C) 1995 Bjoern Beutel. */
/* Description. =============================================================*/
/* In this file, the administration of breakpoints is managed. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <glib.h>
#include "basic.h"
#include "pools.h"
#include "values.h"
#include "files.h"
#include "rule_type.h"
#include "rules.h"
#include "input.h"
#include "commands.h"
#include "breakpoints.h"
/* Types. ===================================================================*/
typedef struct /* Definition of a breakpoint. */
{
list_node_t *next;
int_t number; /* Breakpoint number. */
rule_sys_t *rule_sys; /* Rule system of breakpoint. */
int_t instr; /* Instruction to break at. */
} breakpoint_t;
/* Variables. ===============================================================*/
static int_t breakpoint_count; /* Number of breakpoints so far. */
static list_t breakpoints; /* List of breakpoints. */
static int_t rule_system_count; /* Number of loaded rule systems. */
static rule_sys_name_t *rule_systems; /* Name for each rule system. */
/* Functions. ===============================================================*/
static int_t
instruction_at( rule_sys_t *rule_sys, string_t file, int_t line )
/* Return the index of the first instruction at RULE_SYS, FILE, LINE
* or -1 if there is no code there. */
{
src_line_t *src_line;
for (src_line = rule_sys->src_lines;
src_line < rule_sys->src_lines + rule_sys->src_line_count;
src_line++)
{
if (src_line->file != -1
&& strcmp( rule_sys->strings + src_line->file, file ) == 0
&& src_line->line >= line)
{
return src_line->instr;
}
}
return -1;
}
/*---------------------------------------------------------------------------*/
static int_t
find_rule_by_name( rule_sys_t *rule_sys, string_t rule_name )
/* Find the first line of rule RULE_NAME in RULE_SYS.
* Return the first instruction of this rule. */
{
int_t i;
for (i = 0; i < rule_sys->rule_count; i++)
{
if (strcmp_no_case( rule_sys->strings + rule_sys->rules[i].name,
rule_name ) == 0)
{
return rule_sys->rules[i].first_instr;
}
}
return -1;
}
/*---------------------------------------------------------------------------*/
static string_t
complete_file_name( rule_sys_t *rule_sys, string_t file_name )
/* Return FILE_NAME completed if it is in RULE_SYS, else return NULL. */
{
src_line_t *src_line;
for (src_line = rule_sys->src_lines;
src_line < rule_sys->src_lines + rule_sys->src_line_count;
src_line++)
{
if (src_line->file != -1
&& strcmp( name_in_path( rule_sys->strings + src_line->file ),
file_name ) == 0)
{
return rule_sys->strings + src_line->file;
}
}
return NULL;
}
/*---------------------------------------------------------------------------*/
static void
parse_breakpoint( string_t arguments,
rule_sys_t **rule_sys,
string_t *file,
int_t *line )
/* Parse a breakpoint specification; ARGUMENTS must be
* "" (nothing) or
* "LINE_NUMBER" or
* "[RULE_SYS_NAME] [FILE LINE_NUMBER | RULE_NAME]".
* Set RULE_SYS, FILE and LINE according to it. */
{
int_t first_instr;
string_t argument;
int_t i;
if (pc == -1)
{
*rule_sys = NULL;
*file = NULL;
*line = -1;
}
else
{
*rule_sys = executed_rule_sys;
source_of_instr( executed_rule_sys, pc, line, file, NULL );
}
if (*arguments >= '0' && *arguments <= '9')
*line = parse_cardinal( &arguments );
else if (*arguments != EOS) /* Read file name or rule name. */
{
argument = parse_word( &arguments );
/* See if ARGUMENT is a rule system name. */
for (i = 0; i < rule_system_count; i++)
{
if (rule_systems[i].rule_sys != NULL
&& strcmp_no_case( argument, rule_systems[i].name ) == 0)
{
*rule_sys = rule_systems[i].rule_sys;
free_mem( &argument );
argument = parse_word( &arguments );
break;
}
}
/* If a line number follows, we have a file name. */
if (*arguments >= '0' && *arguments <= '9')
{
*line = parse_cardinal( &arguments );
if (*rule_sys != NULL)
*file = complete_file_name( *rule_sys, argument );
else
{
for (i = 0; i < rule_system_count; i++)
{
if (rule_systems[i].rule_sys != NULL)
{
*rule_sys = rule_systems[i].rule_sys;
*file = complete_file_name( *rule_sys, argument );
if (*file != NULL)
break;
}
}
}
if (*file == NULL)
complain( "No source file \"%s\".", argument );
}
else /* ARGUMENT should be a rule name. */
{
if (*rule_sys != NULL)
first_instr = find_rule_by_name( *rule_sys, argument );
else
{
first_instr = -1; /* Prevent a "not initialized" warning. */
for (i = 0; i < rule_system_count; i++)
{
if (rule_systems[i].rule_sys != NULL)
{
*rule_sys = rule_systems[i].rule_sys;
first_instr = find_rule_by_name( *rule_sys, argument );
if (first_instr != -1)
break;
}
}
}
if (first_instr == -1)
complain( "Rule \"%s\" is unknown.", argument );
/* Find the corresponding source line. */
source_of_instr( *rule_sys, first_instr, line, file, NULL );
}
free_mem( &argument );
}
parse_end( &arguments );
if (*file == NULL)
complain( "Missing file name." );
if (*line == -1)
complain( "Missing line number." );
}
/*---------------------------------------------------------------------------*/
int_t
at_breakpoint( rule_sys_t *rule_sys, int_t instr )
/* Return breakpoint number if INSTR in RULE_SYS hits a
* breakpoint; return 0 else. */
{
breakpoint_t *bp;
FOREACH( bp, breakpoints )
{
if (bp->rule_sys == rule_sys && bp->instr == instr)
return bp->number;
}
return 0;
}
/*---------------------------------------------------------------------------*/
static void
delete_all_breakpoints( void )
/* Run through breakpoint list and free all breakpoints. */
{
while (breakpoints.first != NULL)
free_first_node( &breakpoints );
}
/*---------------------------------------------------------------------------*/
static void
do_delete( string_t argument )
/* Remove a breakpoint. */
{
string_t word;
int_t break_num;
breakpoint_t *breakpoint;
if (*argument >= '0' && *argument <= '9')
{
while (*argument != EOS)
{
break_num = parse_cardinal( &argument );
/* Delete breakpoint with BREAK_NUM. */
FOREACH( breakpoint, breakpoints )
{
if (breakpoint->number == break_num)
break;
}
if (breakpoint == NULL)
complain( "No breakpoint %d.", break_num );
free_node( &breakpoints, (list_node_t *) breakpoint );
}
}
else
{
word = parse_word( &argument );
if (strcmp_no_case( word, "all" ) != 0)
complain( "\"all\" or breakpoint numbers expected, not \"%s\".", word );
delete_all_breakpoints();
}
parse_end( &argument );
}
command_t delete_command =
{
"delete d", do_delete,
"Delete breakpoints.\n"
"Usage:\n"
" delete NUMBER ... -- Delete specified breakpoints.\n"
" delete all -- Delete all breakpoints.\n"
};
/*---------------------------------------------------------------------------*/
void
get_breakpoint( string_t argument,
rule_sys_t **rule_sys_p, int_t *instr_p )
/* Parse a breakpoint in ARGUMENT and set the remaining arguments. */
{
string_t file;
int_t line;
/* Parse breakpoint ARGUMENT. */
parse_breakpoint( argument, rule_sys_p, &file, &line );
/* Find first instruction of this breakpoint */
*instr_p = instruction_at( *rule_sys_p, file, line );
if (*instr_p == -1)
complain( "No code at file \"%s\", line %d.", name_in_path( file ), line );
}
/*---------------------------------------------------------------------------*/
static void
do_break( string_t argument )
/* Define a breakpoint. */
{
rule_sys_t *rule_sys;
string_t file, rule;
int_t line, instr;
breakpoint_t *breakpoint;
/* Parse breakpoint ARGUMENT. */
get_breakpoint( argument, &rule_sys, &instr );
/* Check if other breakpoints exist at this position. */
FOREACH( breakpoint, breakpoints )
{
if (breakpoint->rule_sys == rule_sys && breakpoint->instr == instr)
complain( "Breakpoint %d already set here.", breakpoint->number );
}
/* Add breakpoint. */
breakpoint = new_node( &breakpoints, sizeof( breakpoint_t ), LIST_END );
breakpoint->number = ++breakpoint_count;
breakpoint->rule_sys = rule_sys;
breakpoint->instr = instr;
/* Print source position of the instruction. */
source_of_instr( rule_sys, instr, &line, &file, &rule );
printf( "Breakpoint %d in file \"%s\", line %d, rule \"%s\".\n",
breakpoint->number, name_in_path( file ), line, rule );
}
command_t break_command =
{
"break b", do_break,
"Set a breakpoint at the specified position.\n"
"Usage:\n"
" break RULE -- Set a breakpoint at the beginning of RULE.\n"
" break FILE LINE -- Set a breakpoint at LINE in FILE.\n"
" break LINE -- Set a breakpoint at LINE in the current rule file.\n"
" break -- Set a breakpoint at the current line in the current rule file.\n"
"The first two forms may begin with a rule system specification.\n"
"The last two forms can only be used in debug mode or after a rule error.\n"
"You can't set two breakpoints at the same position.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_list( string_t argument )
/* List breakpoints. */
{
breakpoint_t *breakpoint;
string_t file, rule;
int_t line;
parse_end( &argument );
if (breakpoints.first == NULL)
printf( "No breakpoints.\n" );
FOREACH( breakpoint, breakpoints )
{
source_of_instr( breakpoint->rule_sys, breakpoint->instr,
&line, &file, &rule );
printf( "Breakpoint %d in file \"%s\", line %d, rule \"%s\".\n",
breakpoint->number, name_in_path( file ), line, rule );
}
}
command_t list_command =
{
"list l", do_list,
"List all breakpoints.\n"
"Usage: list\n"
};
/*---------------------------------------------------------------------------*/
void
init_breakpoints( int_t rule_sys_count, rule_sys_name_t rule_sys[] )
/* Initialise this module.
* Pass the number of rule systems in RULE_SYS_COUNT
* and their names in RULE_SYS. */
{
rule_system_count = rule_sys_count;
rule_systems = rule_sys;
clear_list( &breakpoints );
}
/*---------------------------------------------------------------------------*/
void
terminate_breakpoints( void )
/* Terminate this module. */
{
delete_all_breakpoints();
}
/* End of file. =============================================================*/