/* Copyright (C) 2002 Bjoern Beutel. */
/* Description. =============================================================*/
/* This is a GTK program that displays Malaga values. */
/* Includes. ================================================================*/
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <setjmp.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>
#include <glib.h>
#include "basic.h"
#include "input.h"
#include "scanner.h"
#include "files.h"
#include "canvas.h"
#include "allomorphs.h"
#include "expressions.h"
#include "result.h"
#include "tree.h"
#include "variables.h"
#ifdef POSIX
#include <unistd.h>
#endif
/* Variables. ===============================================================*/
#ifdef WIN32
static bool_t input_consumed; /* Set by consumer, reset by pipe thread. */
static GCond *input_consumed_cond; /* Signals that "input_consumed" is set. */
static GMutex *input_consumed_mutex;
#endif
/* Functions. ===============================================================*/
static void
parse_font_size( string_t input, int_t *font_size )
{
int_t size;
size = parse_cardinal( &input );
parse_end( &input );
if (size != 8 && size != 10 && size != 12 && size != 14 && size != 18
&& size != 24)
{
complain( "Illegal font size, valid sizes are 8, 10, 12, 14, 18, 24." );
}
*font_size = size;
}
/*---------------------------------------------------------------------------*/
static int_t
my_parse_int( string_t *input_p )
/* Parse an integer number in *INPUT_P, adjust *INPUT_P.
* Parsing stops when the first non-digit is encountered. */
{
int_t number;
if (**input_p < '0' || **input_p > '9')
complain( "Number expected." );
number = 0;
while (**input_p >= '0' && **input_p <= '9')
number = 10 * number + (*(*input_p)++ - '0');
return number;
}
/*---------------------------------------------------------------------------*/
static void
parse_geometry( string_t input, rectangle_t *geometry )
{
static char_t malformed[] = "Malformed geometry.";
string_t arg, arg_p;
arg_p = arg = parse_word( &input );
parse_end( &input );
if ((*arg_p < '0' || *arg_p > '9') && *arg_p != '+')
complain( malformed );
if (*arg_p >= '0' && *arg_p <= '9')
{
/* Parse size. */
geometry->width = my_parse_int( &arg_p );
if (*arg_p != 'x')
complain( malformed );
arg_p++;
geometry->height = my_parse_int( &arg_p );
}
if (*arg_p == '+')
{
/* Parse position. */
arg_p++;
geometry->x = my_parse_int( &arg_p );
if (*arg_p != '+')
complain( malformed );
arg_p++;
geometry->y = my_parse_int( &arg_p );
}
if (*arg_p != EOS)
complain( malformed );
free_mem( &arg );
}
/*---------------------------------------------------------------------------*/
static void
read_profile( string_t file_name )
{
FILE *stream;
string_t line_p, argument, value;
char_t *line;
volatile int_t line_count;
/* Initialise positions to be ignored. */
allomorphs_geometry.x = allomorphs_geometry.y = -1;
expressions_geometry.x = expressions_geometry.y = -1;
path_geometry.x = path_geometry.y = -1;
result_geometry.x = result_geometry.y = -1;
tree_geometry.x = tree_geometry.y = -1;
variables_geometry.x = variables_geometry.y = -1;
stream = open_stream( file_name, "r" );
line_count = 0;
while (TRUE)
{
line = read_line( stream );
if (line == NULL)
break;
line_count++;
cut_comment( line );
line_p = line;
if (*line_p != EOS)
{
argument = NULL;
TRY
{
argument = parse_word( &line_p );
if (strcmp_no_case( argument, "allomorphs_geometry:" ) == 0)
parse_geometry( line_p, &allomorphs_geometry );
else if (strcmp_no_case( argument, "expressions_geometry:" ) == 0)
parse_geometry( line_p, &expressions_geometry );
else if (strcmp_no_case( argument, "path_geometry:" ) == 0)
parse_geometry( line_p, &path_geometry );
else if (strcmp_no_case( argument, "result_geometry:" ) == 0)
parse_geometry( line_p, &result_geometry );
else if (strcmp_no_case( argument, "tree_geometry:" ) == 0)
parse_geometry( line_p, &tree_geometry );
else if (strcmp_no_case( argument, "variables_geometry:" ) == 0)
parse_geometry( line_p, &variables_geometry );
else if (strcmp_no_case( argument, "font_size:" ) == 0)
parse_font_size( line_p, &font_size );
else if (strcmp_no_case( argument, "font:" ) == 0)
{
if (font_family != NULL)
complain( "Font family already defined." );
font_family = parse_word( &line_p );
parse_end( &line_p );
}
else if (strcmp_no_case( argument, "show_indexes:" ) == 0)
{
show_state_indexes = parse_yes_no( &line_p );
parse_end( &line_p );
}
else if (strcmp_no_case( argument, "hanging_style:" ) == 0)
{
hanging_style = parse_yes_no( &line_p );
parse_end( &line_p );
}
else if (strcmp_no_case( argument, "inline_path:" ) == 0)
{
inline_path = parse_yes_no( &line_p );
parse_end( &line_p );
}
else if (strcmp_no_case( argument, "show_tree:" ) == 0)
{
value = parse_word( &line_p );
if (strcmp_no_case( value, "full" ) == 0)
tree_mode = FULL_TREE;
else if (strcmp_no_case( value, "no_dead_ends" ) == 0)
tree_mode = NO_DEAD_ENDS;
else if (strcmp_no_case( value, "result_paths" ) == 0)
tree_mode = RESULT_PATHS;
else
complain( "\"full\", \"no_dead_ends\" or \"result_paths\" "
"expected." );
free_mem( &value );
}
}
IF_ERROR
{
print_text( error_text, " (\"%s\", line %d)\n",
name_in_path( file_name ), line_count );
}
FINALLY
free_mem( &argument );
END_TRY;
}
free_mem( &line );
}
close_stream( &stream, file_name );
}
/*---------------------------------------------------------------------------*/
static void
read_input( string_t input_type )
/* Read and show INPUT_TYPE from STDIN. */
{
if (strcmp_no_case( input_type, "allomorph" ) == 0)
read_allomorphs();
else if (strcmp_no_case( input_type, "expressions" ) == 0)
read_expressions();
else if (strcmp_no_case( input_type, "result" ) == 0)
read_result();
else if (strcmp_no_case( input_type, "tree" ) == 0)
read_tree();
else if (strcmp_no_case( input_type, "variables" ) == 0)
read_variables();
}
/*---------------------------------------------------------------------------*/
#ifdef POSIX
static void
read_on_condition( gpointer data, gint source, GdkInputCondition condition )
/* This is called by GDK whenever there is something to read.
* It reads input from malaga and displays the appropriate canvas. */
{
string_t input_type;
input_type = read_line( stdin );
if (input_type == NULL)
gtk_exit(0);
read_input( input_type );
free_mem( &input_type );
}
#endif
/*---------------------------------------------------------------------------*/
#ifdef WIN32
static bool_t
read_input_notify( string_t input_type )
/* Read INPUT_TYPE from stdin, display it, and notify waiting thread. */
{
if (input_type == NULL)
gtk_exit(0);
read_input( input_type );
g_mutex_lock( input_consumed_mutex );
input_consumed = TRUE;
g_cond_signal( input_consumed_cond );
g_mutex_unlock( input_consumed_mutex );
return FALSE;
}
#endif
/*---------------------------------------------------------------------------*/
#ifdef WIN32
static void *
input_thread( void *dummy )
/* Read one line from STDIN, inform main thread when new input has arrived. */
{
string_t input_type;
while (TRUE)
{
input_type = read_line( stdin );
g_idle_add( (GSourceFunc) read_input_notify, input_type );
g_mutex_lock( input_consumed_mutex );
while (! input_consumed)
g_cond_wait( input_consumed_cond, input_consumed_mutex );
input_consumed = FALSE;
g_mutex_unlock( input_consumed_mutex );
free_mem( &input_type );
}
return NULL;
}
#endif
/*---------------------------------------------------------------------------*/
int
main( int argc, char *argv[] )
{
string_t malagarc_path, input_type;
init_basic( "malshow" );
init_input();
init_scanner();
if (argc == 2)
{
if (strcmp_no_case( argv[1], "--version" ) == 0
|| strcmp_no_case( argv[1], "-version" ) == 0
|| strcmp_no_case( argv[1], "-v" ) == 0)
{
program_message();
exit(0);
}
else if (strcmp_no_case( argv[1], "--help" ) == 0
|| strcmp_no_case( argv[1], "-help" ) == 0
|| strcmp_no_case( argv[1], "-h" ) == 0)
{
printf( "Display results of a Malaga process in a graphical format.\n\n"
"Usage:\n"
"malshow -- Display results from malaga.\n"
"malshow -v[ersion] -- Print the version number.\n"
"malshow -h[elp] -- Print this help.\n\n"
"This program is usually started by malaga or mallex.\n" );
exit(0);
}
}
gtk_init( &argc, &argv );
/* Read Malaga profile. */
malagarc_path = NULL;
#ifdef POSIX
TRY
malagarc_path = absolute_path( "~/.malagarc", NULL );
IF_ERROR
RESUME;
END_TRY;
#endif
#ifdef WIN32
TRY
malagarc_path = absolute_path( "~\\malaga.ini", NULL );
IF_ERROR
RESUME;
END_TRY;
#endif
if (malagarc_path != NULL && file_exists( malagarc_path ))
read_profile( malagarc_path );
free_mem( &malagarc_path );
/* Determine font name. */
if (font_family == NULL)
{
#ifdef POSIX
font_family = new_string( "Sans", NULL );
#endif
#ifdef WIN32
font_family = new_string( "Arial", NULL );
#endif
}
/* Read first input. */
input_type = read_line( stdin );
read_input( input_type );
free_mem( &input_type );
/* Prepare to read again whenever there is new input. */
#ifdef POSIX
gdk_input_add( STDIN_FILENO, GDK_INPUT_READ, read_on_condition, NULL );
#endif
#ifdef WIN32
g_thread_init( NULL );
input_consumed_cond = g_cond_new();
input_consumed_mutex = g_mutex_new();
g_thread_create( input_thread, NULL, FALSE, NULL );
#endif
gtk_main();
free_mem( &font_family );
terminate_scanner();
terminate_input();
terminate_basic();
return 0;
}
/* End of file. =============================================================*/