/*
Copyright (c) 2002-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file	readau.c	The readau module of the genau program.

This module contains functions to read the state machine description
from a file.
*/



#ifndef DEBUG
/**	No debugging at this time.
*/
#define DEBUG 0
#endif

#include <dk.h>
#include <dksf.h>
#include <dkmem.h>
#include <dkstr.h>
#include <dkapp.h>
#include <dklic.h>
#include <dkstream.h>
#include <dksto.h>
#include <dkma.h>
#include <dkbf.h>
#include <dklogc.h>

/**	Inside the readau module.
*/
#define READAU_C 1

#include <genau.h>
#include <readau.h>

#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if DK_HAVE_STRIN_H
#include <string.h>
#endif
#if DK_HAVE_STRINGS_H
#include <strings.h>
#endif


#line 86 "readau.ctr"




/**	String: Opening bracket.
*/
static char str_bropen[] = { "(" };

/**	String: Closing bracket.
*/
static char str_brclose[] = { ")" };

/**	Sting: Space.
*/
static char str_space[] = { " " };

/**	String: Wildcard.
*/
static char str_asterisk[] = { "*" };

/*	@defgroup	readauerrors	[genau] Errors while reading. */
/*@{*/
/**	Not enough memory.
*/
#define READAU_ERROR_NOT_ENOUGH_MEMORY       1

/**	Section name not finished.
*/
#define READAU_ERROR_SECTION_NAME_UNFINISHED 2

/**	Empty section name.
*/
#define READAU_ERROR_EMPTY_SECTION_NAME      3

/**	Unknown section name.
*/
#define READAU_ERROR_UNKNOWN_SECTION_NAME    4

/**	Missing section name.
*/
#define READAU_ERROR_SECTION_NAME_NEEDED     5

/**	Incomplete input line.
*/
#define READAU_ERROR_LINE_INCOMPLETE         6

/**	Missing numeric value.
*/
#define READAU_ERROR_NOT_A_NUMBER		7

/**	State name already used.
*/
#define READAU_ERROR_STATE_ALREADY_EXISTS    8

/**	Input name already used.
*/
#define READAU_ERROR_INPUT_ALREADY_EXISTS    9

/**	Output name already used.
*/
#define READAU_ERROR_OUTPUT_ALREADY_EXISTS  10

/**	There is already a rule defined for state/input combination.
*/
#define READAU_ERROR_GENERAL_RULE_ALREADY_DEFINED 11

/**	There is already a rule defined for this input.
*/
#define READAU_ERROR_RULE_ALREADY_DEFINED_FOR_INPUT 12

/**	Too many states.
*/
#define READAU_ERROR_TOO_MANY_STATES		13

/**	Too many inputs.
*/
#define READAU_ERROR_TOO_MANY_INPUTS		14

/**	Too many outputs.
*/
#define READAU_ERROR_TOO_MANY_OUTPUTS		15

/**	Undefined input.
*/
#define READAU_ERROR_INPUT_UNDEFINED		16

/**	Undefined output.
*/
#define READAU_ERROR_OUTPUT_UNDEFINED		17
/*@}*/


/**	Delete an option.
	@param	o	Option to delete.
*/
static
void
delete_option DK_P1(au_option_line_t *, o)
{
  char *ptr;
  if(o) {
    
    ptr = o->line;
    if(ptr) { dk_delete(ptr); }
    o->line = NULL;
    o->lineno = 0UL;
    dk_delete(o);
  }
}



/**	Create new option.
	@param	l	Option line text.
	@param	ln	Line number of definition.
	@return	Pointer to new option on success, NULL on error.
*/
static
au_option_line_t *
new_option DK_P2(char *, l, unsigned long, ln)
{
  au_option_line_t *back = NULL;
  
  back = dk_new(au_option_line_t,1);
  if(back) {
    back->line = NULL;
    back->lineno = ln;
    back->line = dkstr_dup(l);
    if(!(back->line)) {
      delete_option(back);
      back = NULL;
    }
  } 
  return back;
}



/**	Delete input, release memory.
	@param	a	Input to delete.
*/
static
void
delete_input DK_P1(au_input_t *, a)
{
  char *ptr;
  if(a) {
    
    ptr = a->name;
    if(ptr) { dk_delete(ptr); }
    a->name = NULL;
    a->number = 0;
    a->number_defined = 0;
    a->lineno = 0UL;
    dk_delete(a);
  }
}



/**	Create new input.
	@param	name	Input name.
	@param	l	Line number of definition.
	@return	Pointer to new input on success, NULL on error.
*/
static
au_input_t *
new_input DK_P2(char *, name, unsigned long, l)
{
  au_input_t *back = NULL;
  
  back = dk_new(au_input_t,1);
  if(back) {
    back->name = NULL;
    back->number = 0;
    back->number_defined = 0;
    back->lineno = l;
    back->name = dkstr_dup(name);
    if(!(back->name)) {
      delete_input(back);
      back = NULL;
    }
  } 
  return back;
}



/**	Create new input and assign number.
	@param	name	Input name.
	@param	l	Line number of definition.
	@param	num	Input number.
	@return	Pointer to new input on success, NULL on error.
*/
static
au_input_t *
new_input_number DK_P3(char *, name, unsigned long, l, int, num)
{
  au_input_t *back = NULL;
  
  back = new_input(name,l);
  if(back) {
    back->number = num;
    back->number_defined = 1;
  } 
  return back;
}



/**	Delete output.
	@param	a	Output to delete.
*/
static
void
delete_output DK_P1(au_output_t *, a)
{
  char *ptr;
  if(a) {
    
    ptr = a->name;
    if(ptr) { dk_delete(ptr); }
    a->name = NULL;
    a->number = a->number_defined = 0;
    a->lineno = 0UL;
    dk_delete(a);
  }
}



/**	Delete transition, release memory.
	@param	t	Transition to delete.
*/
static
void
delete_transition DK_P1(au_state_transition_t *, t)
{
  char *ptr;
  if(t) {
    
    ptr = t->current_name;
    if(ptr) {dk_delete(ptr); }
    ptr = t->input_name;
    if(ptr) { dk_delete(ptr); }
    ptr = t->next_name;
    if(ptr) { dk_delete(ptr); }
    ptr = t->output_name;
    if(ptr) { dk_delete(ptr); }
    t->current_name = NULL;
    t->input_name = NULL;
    t->next_name = NULL;
    t->output_name = NULL;
    t->input_data = NULL;
    t->next_data = NULL;
    t->output_data = NULL;
    t->lineno = 0UL;
    dk_delete(t);
  }
}



/**	Create new transition.
	@param	c	Current state name.
	@param	i	Input name.
	@param	n	Next state name.
	@param	o	Output name.
	@param	l	Line number of definition.
	@return	Pointer to new transition on success, NULL on error.
*/
static
au_state_transition_t *
new_transition DK_P5(char *, c, char *, i, char *, n, char *, o, unsigned long, l)
{
  au_state_transition_t *back = NULL;
  
  back = dk_new(au_state_transition_t,1);
  if(back) {
    back->current_name = dkstr_dup(c);
    back->current_data = NULL;
    back->input_name = dkstr_dup(i);
    back->input_data = NULL;
    back->next_name = dkstr_dup(n);
    back->next_data = NULL;
    back->output_name = dkstr_dup(o);
    back->output_data = NULL;
    back->lineno = l;
    if(!((back->current_name) && (back->input_name) && (back->next_name) && (back->output_name))) {
      delete_transition(back);
      back = NULL;
    }
  } 
  return back;
}



/**	Delete state (release memory).
	@param	s	State to delete.
*/
static
void
delete_state DK_P1(au_state_t *, s)
{
  char *ptr;
  if(s) {
    
    ptr = s->name;
    if(ptr) { dk_delete(ptr); }
    s->name = NULL;
    s->number = 0;
    s->number_defined = 0;
    if((s->trans) && (s->trit)) {
      dksto_it_close(s->trit);
      s->trit = NULL;
    }
    if(s->trans) {
      dksto_close(s->trans);
      s->trans = NULL;
    }
    dk_delete(s);
  }
}



/**	Create new output.
	@param	name	Output name.
	@param	l	Line number of definition.
	@return	Pointer to new output on success, NULL on error.
*/
static
au_output_t *
new_output DK_P2(char *, name, unsigned long, l)
{
  au_output_t *back = NULL;
  
  back = dk_new(au_output_t,1);
  if(back) {
    back->name = NULL; back->number = 0;
    back->number_defined = 0; back->lineno = l;
    back->name = dkstr_dup(name);
    if(!(back->name)) {
      delete_output(back);
      back = NULL;
    }
  }  
  return back;
}



/**	Create new output and assign number.
	@param	name	Output name.
	@param	l	Line number of definition.
	@param	num	Output number.
	@return	Pointer to new output on success, NULL on error.
*/
static
au_output_t *
new_output_number DK_P3(char *, name, unsigned long, l, int, num)
{
  au_output_t *back = NULL;
  
  back = new_output(name,l);
  if(back) {
    back->number = num; back->number_defined = 1;
  } 
  return back;
}



/**	Compare two inputs by name.
	@param	l	Left input.
	@param	r	Right output or name text.
	@param	cr	Comparison criteria (0=input/input, 1=input/name).
	@return	Comparison criteria.
*/
static
int
compare_inputs DK_P3(void *, l, void *, r, int, cr)
{
  int back = 0;
  char *str;
  au_input_t *al, *ar;
  
  if(l) {
    if(r) {
      al = (au_input_t *)l;
      switch(cr) {
	case 1: {
	  str = (char *)r;
	  if(al->name) {
	    back = strcmp(al->name, str);
	    if(back < 0) back = -1;
	    if(back > 0) back =  1;
	  } else {
	    back = -1;
	  }
	} break;
	default: {
	  ar = (au_input_t *)r;
	  if(al->name) {
	    if(ar->name) {
	      back = strcmp(al->name, ar->name);
	      if(back < 0) back = -1;
	      if(back > 0) back =  1;
	    } else {
	      back = 1;
	    }
	  } else {
	    if(ar->name) { back = -1; }
	  }
	} break;
      }
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  } 
  return back;
}



/**	Compare outputs by name.
	@param	l	Left output.
	@param	r	Right output or name text.
	@param	cr	Comparison criteria (0=output/output, 1=output/name).
	@return	Comparison result.
*/
static
int
compare_outputs DK_P3(void *, l, void *, r, int, cr)
{
  int back = 0;
  char *str;
  au_output_t *al, *ar;
  
  if(l) {
    if(r) {
      al = (au_output_t *)l;
      switch(cr) {
	case 1: {
	  str = (char *)r;
	  if(al->name) {
	    back = strcmp(al->name, str);
	    if(back < 0) back = -1;
	    if(back > 0) back =  1;
	  } else {
	    back = -1;
	  }
	} break;
	default: {
	  ar = (au_output_t *)r;
	  if(al->name) {
	    if(ar->name) {
	      back = strcmp(al->name, ar->name);
	      if(back < 0) back = -1;
	      if(back > 0) back =  1;
	    } else {
	      back = 1;
	    }
	  } else {
	    if(ar->name) { back = -1; }
	  }
	} break;
      }
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  } 
  return back;
}



/**	Compare two states by name.
	@param	l	Left state.
	@param	r	Right state or name.
	@param	cr	Comparison criteria (0=state/state, 1=state/name).
	@return	Comparison result.
*/
static
int
compare_states DK_P3(void *, l, void *, r, int, cr)
{
  int back = 0;
  char *str;
  au_state_t *al, *ar;
  
  if(l) {
    if(r) {
      al = (au_state_t *)l;
      switch(cr) {
	case 1: {
	  str = (char *)r;
	  if(al->name) {
	    back = strcmp(al->name, str);
	    if(back < 0) back = -1;
	    if(back > 0) back =  1;
	  } else {
	    back = -1;
	  }
	} break;
	default: {
	  ar = (au_state_t *)r;
	  if(al->name) {
	    if(ar->name) {
	      back = strcmp(al->name, ar->name);
	      if(back < 0) back = -1;
	      if(back > 0) back =  1;
	    } else {
	      back = 1;
	    }
	  } else {
	    if(ar->name) { back = -1; }
	  }
	} break;
      }
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  } 
  return back;
}



/**	Compare two option lines by line number.
	@param	l	Left option line.
	@param	r	Right option line.
	@param	crit 	Comparison criteria (ignored).
	@return	Comparison result.
*/
static
int
compare_option_line DK_P3(void *, l, void *, r, int, crit)
{
  int back = 0;
  au_option_line_t *ol, *or;
  
  if(l) {
    if(r) {
      ol = (au_option_line_t *)l;
      or = (au_option_line_t *)r;
      if(ol->lineno > or->lineno) back =  1;
      if(ol->lineno < or->lineno) back = -1;
    } else {
      back = 1;
    }
  } else {
    if(r) { back = -1; }
  } 
  return back;
}



/**	Compare two state transitions.
	@param	l	Left transition rule.
	@param	r	Right transition rule.
	@param	cr	Comparison criteria (0=line number, 1=transition
	by name/transition by name, 2=transition by name/name string).
	@return	Comparison result.
*/
static
int
compare_transitions DK_P3(void *, l, void *, r, int, cr)
{
  int back = 0;
  au_state_transition_t *tl, *tr;
  char *str;
  
  if(l) {
    if(r) {
      tl = (au_state_transition_t *)l;
      tr = (au_state_transition_t *)r;
      switch(cr) {
	case 1: {
	  if(tl->input_name) {
	    if(tr->input_name) {
	      back = strcmp(tl->input_name, tr->input_name);
	      if(back < 0) back = -1;
	      if(back > 0) back =  1;
	    } else {
	      back = 1;
	    }
	  } else {
	    if(tr->input_name) {
	      back = -1;
	    }
	  }
	} break;
	case 2: {
	  str = (char *)r;
	  if(tl->input_name) {
	    back = strcmp(tl->input_name, str);
	    if(back < 0) back = -1;
	    if(back > 0) back =  1;
	  } else {
	    back = -1;
	  }
	} break;
	default: {
	  if(tl->lineno > tr->lineno) back =  1;
	  if(tl->lineno < tr->lineno) back = -1;
	} break;
      }
    } else {
      back = 1;
    }
  } else {
    if(r) { back = -1; }
  } 
  return back;
}



/**	Create new state entry.
	@param	n	State name.
	@param	l	Line number of definition.
	@return	Pointer to new state entry on success, NULL on error.
*/
static
au_state_t *
new_state DK_P2(char *, n, unsigned long, l)
{
  au_state_t *back = NULL;
  
  back = dk_new(au_state_t,1);
  if(back) {
    back->name = dkstr_dup(n);
    back->number = 0;
    back->number_defined = 0;
    back->lineno = l;
    back->trans = dksto_open(0);
    back->compat = NULL;
    if(back->trans) {
      dksto_set_comp(back->trans, compare_transitions, 1);
      back->trit = dksto_it_open(back->trans);
    } else {
      back->trit = NULL;
    }
    if(!((back->name) && (back->trans) && (back->trit))) {
      delete_state(back);
      back = NULL;
    }
  } 
  return back;
}



/**	Create new numbered state entry.
	@param	n	State name.
	@param	l	Line number of state definition.
	@param	num	State number.
	@return	Pointer to new entry on success, NULL on error.
*/
static 
au_state_t *
new_state_number DK_P3(char *, n, unsigned long, l, int, num)
{
  au_state_t *back = NULL;
  
  back = new_state(n,l);
  if(back) {
    back->number = num;
    back->number_defined = 1;
  } 
  return back;
}



void
readau_delete DK_P1(automata_t *, a)
{
  au_option_line_t *ol;
  au_state_t *st;
  au_input_t *in;
  au_output_t *out;
  au_state_transition_t *tr;
  
  if(a) {
    if(a->asttrl) {
      if(a->asttrlit) {
	
	dksto_it_close(a->asttrlit);
	a->asttrlit = NULL;
      }
      dksto_close(a->asttrl);
      a->asttrl = NULL;
    }
    if(a->asttr) {
      if(a->asttrit) {
	
	dksto_it_close(a->asttrit);
	a->asttrit = NULL;
      }
      dksto_close(a->asttr);
      a->asttr = NULL;
    }
    if(a->alltr) {
      if(a->alltrit) {
	
	dksto_it_reset(a->alltrit);
	while((tr = (au_state_transition_t *)dksto_it_next(a->alltrit)) != NULL) {
	  delete_transition(tr);
	}
	dksto_it_close(a->alltrit);
	a->alltrit = NULL;
      }
      dksto_close(a->alltr);
      a->alltr = NULL;
    }
    if(a->out) {
      if(a->outit) {
	
	dksto_it_reset(a->outit);
	while((out = (au_output_t *)dksto_it_next(a->outit)) != NULL) {
	  delete_output(out);
	}
	dksto_it_close(a->outit);
	a->outit = NULL;
      }
      dksto_close(a->out);
      a->out = NULL;
    }
    if(a->in) {
      if(a->init) {
	
	dksto_it_reset(a->init);
	while((in = (au_input_t *)dksto_it_next(a->init)) != NULL) {
	  delete_input(in);
	}
	dksto_it_close(a->init);
	a->init = NULL;
      }
      dksto_close(a->in);
      a->in = NULL;
    }
    if(a->st) {
      if(a->stit) {
	
	dksto_it_reset(a->stit);
	while((st = (au_state_t *)dksto_it_next(a->stit)) != NULL) {
	  delete_state(st);
	}
	dksto_it_close(a->stit);
	a->stit = NULL;
      }
      dksto_close(a->st);
      a->st = NULL;
    }
    if(a->opt) {
      if(a->optit) {
	
	dksto_it_reset(a->optit);
	while((ol = (au_option_line_t *)dksto_it_next(a->optit)) != NULL) {
	  delete_option(ol);
	}
	dksto_it_close(a->optit);
	a->optit = NULL;
      }
      dksto_close(a->opt);
      a->opt = NULL;
    }
    a->general_rule = NULL;
    a->opt = NULL; a->optit = NULL;
    a->st = NULL; a->stit = NULL;
    a->in = NULL; a->init = NULL;
    a->out = NULL; a->outit = NULL;
    a->alltr = NULL; a->alltrit = NULL;
    a->general_rule = NULL;
    a->input_file_name = NULL;
    a->error_code = 0;
    dk_delete(a);
  } 
}



/**	Create new automata struct.
	@param	flg	Pointer to error code variable.
	@return	Pointer to new automata on success, NULL on error.
*/
static
automata_t *
new_automata DK_P1(int *, flg)
{
  automata_t *back = NULL;
  
  back = dk_new(automata_t,1);
  if(back) {
    back->h_file_name = NULL;
    back->initial_state = NULL;
    back->prototypes = 1;
    back->name_trans = NULL;
    back->name_res = NULL;
    back->i_prot = NULL;
    back->error_code = 0;
    back->opt = NULL; back->optit = NULL;
    back->st  = NULL; back->stit  = NULL;
    back->in  = NULL; back->init  = NULL;
    back->out = NULL; back->outit = NULL;
    back->alltr = NULL; back->alltrit = NULL;
    back->general_rule = NULL;
    back->def_output = NULL;
    back->doxy_output = 0;
    back->squeezed_output = 0;
    back->opt = dksto_open(0);
    if(back->opt) {
      dksto_set_comp(back->opt, compare_option_line, 0);
      back->optit = dksto_it_open(back->opt);
      if(!(back->optit)) back->error_code = 1;
    } else {
      back->error_code = 1;
    }
    back->st = dksto_open(0);
    if(back->st) {
      dksto_set_comp(back->st, compare_states, 0);
      back->stit = dksto_it_open(back->st);
      if(!(back->stit)) back->error_code = 1;
    } else {
      back->error_code = 1;
    }
    back->in = dksto_open(0);
    if(back->in) {
      dksto_set_comp(back->in, compare_inputs, 0);
      back->init = dksto_it_open(back->in);
      if(!(back->init)) back->error_code = 1;
    } else {
      back->error_code = 1;
    }
    back->out = dksto_open(0);
    if(back->out) {
      dksto_set_comp(back->out, compare_outputs, 0);
      back->outit = dksto_it_open(back->out);
      if(!(back->outit)) back->error_code = 1;
    } else {
      back->error_code = 1;
    }
    back->alltr = dksto_open(0);
    if(back->alltr) {
      dksto_set_comp(back->alltr, compare_transitions, 0);
      back->alltrit = dksto_it_open(back->alltr);
      if(!(back->alltrit)) back->error_code = 1;
    } else {
      back->error_code = 1;
    }
    back->asttr = dksto_open(0);
    if(back->asttr) {
      dksto_set_comp(back->asttr, compare_transitions, 1);
      back->asttrit = dksto_it_open(back->asttr);
      if(!(back->asttrit)) back->error_code = 1;
    } else {
      back->error_code = 1;
    }
    back->asttrl = dksto_open(0);
    if(back->asttrl) {
      dksto_set_comp(back->asttrl, compare_transitions, 0);
      back->asttrlit = dksto_it_open(back->asttrl);
      if(!(back->asttrlit)) {
	back->error_code = 1;
      }
    } else {
      back->error_code = 1;
    }
    if(back->error_code) {
      *flg = back->error_code;
      readau_delete(back);
      back = NULL;
    }
  } else {
    *flg = 1;
  } 
  return back;
}



/**	Keyword "options".
*/
static char key_options[] = { "options" };

/**	Keyword "states".
*/
static char key_states[]  = { "states" };

/**	Keyword "inputs".
*/
static char key_inputs[]  = { "inputs" };

/**	Keyword "outputs",
*/
static char key_outputs[] = { "outputs" };

/**	Keyword "rules".
*/
static char key_rules[]   = { "rules" };

/**	Keyword "end".
*/
static char key_end[]     = { "end" };

/**	Keyword: Wildcard.
*/
static char key_all[]     = { "*" };



/**	Read automata description from stream.
	@param	a	Automata.
*/
static
void
read_from_stream DK_P1(automata_t *, a)
{
  int can_continue, section_number, number;
  char *line, *ptr, *ptr2, *ptr3, *ptr4, *ptr5;
  au_option_line_t *ol;
  au_state_t *st;
  au_input_t *in;
  au_output_t *out;
  au_state_transition_t *tr;
  char errbuffer[32], *errmsgs[16], **allstrings;

  
  allstrings = genau_strings();
  line = dk_new(char,LINELENGTH);
  if(line) {
    a->lineno = 0UL;
    can_continue = 1; section_number = 0;
    while(can_continue && (!(a->error_code))) {
      if(dkstream_gets(a->is,line,LINELENGTH)) {
        a->lineno += 1UL;
	dkapp_set_source_lineno(a->app, a->lineno);
        ptr = dkstr_chr(line, '#');
        if(ptr) { *ptr = '\0'; }
        ptr = dkstr_start(line, NULL);
        if(ptr) {
	  dkstr_chomp(ptr, NULL);
	  
	  if(*ptr == '[') {			/* new section */
	    
	    ptr2 = dkstr_chr(ptr, ']');
	    if(ptr2) {
	      *ptr2 = '\0';
	      ptr++;
	      ptr = dkstr_start(ptr, NULL);
	      if(ptr) {
	        dkstr_chomp(ptr, NULL);
	        section_number = 0;
	        if(strcmp(ptr, key_options) == 0) {
		  section_number = 1;
	        }
	        if(strcmp(ptr, key_states) == 0) {
		  section_number = 2;
	        }
	        if(strcmp(ptr, key_inputs) == 0) {
		  section_number = 3;
	        }
	        if(strcmp(ptr, key_outputs) == 0) {
		  section_number = 4;
	        }
	        if(strcmp(ptr, key_rules) == 0) {
		  section_number = 5;
	        }
	        if(strcmp(ptr, key_end) == 0) {
		  section_number = 6;
		  can_continue = 0;
	        }
	        if(section_number == 0) {
		  a->error_code = READAU_ERROR_UNKNOWN_SECTION_NAME;
		  errmsgs[0] = allstrings[21];
		  errmsgs[1] = str_space;
		  errmsgs[2] = str_bropen;
		  errmsgs[3] = str_space;
		  if(ptr) { errmsgs[3] = ptr; }
		  errmsgs[4] = str_brclose;
		  dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
	        }
	      } else {
	        a->error_code = READAU_ERROR_EMPTY_SECTION_NAME;
		errmsgs[0] = allstrings[22];
		dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 1);
	      }
	    } else {
	      a->error_code = READAU_ERROR_SECTION_NAME_UNFINISHED;
	      
	      errmsgs[0] = allstrings[23];
	      
	      errmsgs[1] = str_space;
	      
	      errmsgs[2] = str_bropen;
	      
	      errmsgs[3] = str_space;
	      
	      if(ptr) { errmsgs[3] = ptr; }
              
	      errmsgs[4] = str_brclose;
	      
	      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
	    }
	  } else {				/* no new section */
	    switch(section_number) {
	      case 1: {			/* options */
	        ol = new_option(ptr, a->lineno);
	        if(ol) {
		  if(!dksto_add(a->opt, (void *)ol)) {
		    delete_option(ol);
		    a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		  }
	        } else {
		  a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
	        }
	      } break;
	      case 2: {			/* states */
	        ptr2 = dkstr_next(ptr, NULL);
	        if(ptr2) {
		  ptr3 = dkstr_next(ptr2, NULL);
                  st = (au_state_t *)dksto_it_find_like(a->stit, (void *)ptr, 1);
		  if(st) {
		    /* 10 ERROR: state already exists */
		    allstrings = genau_strings();
		    if(allstrings && (a->app) && (a->input_file_name)) {
		      sprintf(errbuffer, "%lu", a->lineno);
		      errmsgs[0] = allstrings[9];
		      errmsgs[1] = str_space;
		      errmsgs[2] = str_bropen;
		      errmsgs[3] = ptr;
		      errmsgs[4] = str_brclose;
		      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
  
		    }
		    a->error_code = READAU_ERROR_STATE_ALREADY_EXISTS;
		  } else {
		    if(sscanf(ptr2, "%d", &number) == 1) {
		      st = new_state_number(ptr, a->lineno, number);
		      if(st) {
		        if(!dksto_add(a->st, (void *)st)) {
			  delete_state(st); st = NULL;
			  a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		        } else {
			  if(!(a->initial_state)) {
			    a->initial_state = st;
			  }
		        }
		      } else {
		        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		      }
		    } else {
		      /* 11 ERROR: Wrong state number */
		      allstrings = genau_strings();
		      if((a->app) && allstrings && (a->input_file_name)) {
		      sprintf(errbuffer, "%lu", a->lineno);
		      errmsgs[0] = allstrings[10];
		      errmsgs[1] = str_space;
		      errmsgs[2] = str_bropen;
		      errmsgs[3] = ptr2;
		      errmsgs[4] = str_brclose;
		      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		      }
		      a->error_code = READAU_ERROR_NOT_A_NUMBER;
		    }
		  }
	        } else {
		  st = (au_state_t *)dksto_it_find_like(a->stit, (void *)ptr, 1);
		  if(st) {
		    /* 12 ERROR: state already exists */
		    allstrings = genau_strings();
		    if(allstrings && (a->app) && (a->input_file_name)) {
		      sprintf(errbuffer, "%lu", a->lineno);
		      errmsgs[0] = allstrings[11];
		      errmsgs[1] = str_space;
		      errmsgs[2] = str_bropen;
		      errmsgs[3] = ptr;
		      errmsgs[4] = str_brclose;
		      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		    }
		    a->error_code = READAU_ERROR_STATE_ALREADY_EXISTS;
		  } else {
		    st = new_state(ptr, a->lineno);
		    if(st) {
		      if(!dksto_add(a->st, (void *)st)) {
		        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		        delete_state(st); st = NULL;
		      } else {
		        if(!(a->initial_state)) {
			  a->initial_state = st;
		        }
		      }
		    } else {
		      a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		    }
		  }
	        }
	      } break;
	      case 3: {			/* inputs */
	        ptr2 = dkstr_next(ptr, NULL);
	        if(ptr2) {
		  ptr3 = dkstr_next(ptr2, NULL);
		  if(sscanf(ptr2, "%d", &number) == 1) {
		    in = (au_input_t *)dksto_it_find_like(a->init, ptr, 1);
		    if(in) {
		      /* 13 ERROR: Input already exists */
		      allstrings = genau_strings();
		      if(allstrings && (a->app) && (a->input_file_name)) {
		        sprintf(errbuffer, "%lu", a->lineno);
		        errmsgs[0] = allstrings[12];
		        errmsgs[1] = str_space;
		        errmsgs[2] = str_bropen;
		        errmsgs[3] = ptr;
		        errmsgs[4] = str_brclose;
		        dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		      }
		      a->error_code = READAU_ERROR_INPUT_ALREADY_EXISTS;
		    } else {
		      in = new_input_number(ptr, a->lineno, number);
		      if(in) {
		        if(!dksto_add(a->in, (void *)in)) {
			  a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
			  delete_input(in);
			  in = NULL;
		        }
		      } else {
		        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		      }
		    }
		  } else {
		    /* 14 ERROR: Wrong input number */
		    allstrings = genau_strings();
		    if(allstrings && (a->app) && (a->input_file_name)) {
		      sprintf(errbuffer, "%lu", a->lineno);
		      errmsgs[0] = allstrings[13];
		      errmsgs[1] = str_space;
		      errmsgs[2] = str_bropen;
		      errmsgs[3] = ptr2;
		      errmsgs[4] = str_brclose;
		      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		    }
		    a->error_code = READAU_ERROR_NOT_A_NUMBER;
		  }
	        } else {
		  in = (au_input_t *)dksto_it_find_like(a->init, ptr, 1);
		  if(in) {
		    /* 13 ERROR: Input already exists */
		    allstrings = genau_strings();
		    if(allstrings && (a->app) && (a->input_file_name)) {
		      sprintf(errbuffer, "%lu", a->lineno);
		      errmsgs[0] = allstrings[12];
		      errmsgs[1] = str_space;
		      errmsgs[2] = str_bropen;
		      errmsgs[3] = ptr;
		      errmsgs[4] = str_brclose;
		      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		    }
		    a->error_code = READAU_ERROR_INPUT_ALREADY_EXISTS;
		  } else {
		    in = new_input(ptr, a->lineno);
		    if(in) {
		      if(!dksto_add(a->in, (void *)in)) {
		        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		        delete_input(in);
		        in = NULL;
		      }
		    } else {
		      a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		    }
		  }
	        }
	      } break;
	      case 4: {			/* outputs */
	        ptr2 = dkstr_next(ptr, NULL);
	        if(ptr2) {
		  ptr3 = dkstr_next(ptr2, NULL);
		  if(sscanf(ptr2, "%d", &number) == 1) {
		    out = (au_output_t *)dksto_it_find_like(a->outit, ptr, 1);
		    if(out) {
		      /* 15 ERROR: output already exists */
		      allstrings = genau_strings();
		      if(allstrings && (a->app) && (a->input_file_name)) {
		        sprintf(errbuffer, "%lu", a->lineno);
		        errmsgs[0] = allstrings[14];
		        errmsgs[1] = str_space;
		        errmsgs[2] = str_bropen;
		        errmsgs[3] = ptr;
		        errmsgs[4] = str_brclose;
		        dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		      }
		      a->error_code = READAU_ERROR_OUTPUT_ALREADY_EXISTS;
		    } else {
		      out = new_output_number(ptr, a->lineno, number);
		      if(out) {
		        if(!dksto_add(a->out, (void *)out)) {
			  a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
			  delete_output(out); out = NULL;
		        } else {
			  if(!(a->def_output)) { a->def_output = out; }
		        }
		      } else {
		        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		      }
		    }
		  } else {
		    a->error_code = READAU_ERROR_NOT_A_NUMBER;
		    allstrings = genau_strings();
		    if(allstrings && (a->app) && (a->input_file_name)) {
		      sprintf(errbuffer, "%lu", a->lineno);
		      errmsgs[0] = allstrings[13];
		      errmsgs[1] = str_space;
		      errmsgs[2] = str_bropen;
		      errmsgs[3] = ptr2;
		      errmsgs[4] = str_brclose;
		      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		    }
		    /* 14 ERROR: Wrong input number */
		  }
	        } else {
		  out = (au_output_t *)dksto_it_find_like(a->outit, ptr, 1);
		  if(out) {
		    a->error_code = READAU_ERROR_OUTPUT_ALREADY_EXISTS;
		    allstrings = genau_strings();
		    if(allstrings && (a->app) && (a->input_file_name)) {
		      sprintf(errbuffer, "%lu", a->lineno);
		      errmsgs[0] = allstrings[14];
		      errmsgs[1] = str_space;
		      errmsgs[2] = str_bropen;
		      errmsgs[3] = ptr;
		      errmsgs[4] = str_brclose;
		      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
		    }
		  } else {
		    out = new_output(ptr, a->lineno);
		    if(out) {
		      if(!dksto_add(a->out, (void *)out)) {
		        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		        delete_output(out); out = NULL;
		      } else {
		        if(!(a->def_output)) { a->def_output = out; }
		      }
		    } else {
		      a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		    }
		  }
	        }
	      } break;
	      case 5: {			/* rules */
	        ptr2 = dkstr_next(ptr, NULL);
	        if(ptr2) {
		  ptr3 = dkstr_next(ptr2, NULL);
		  if(ptr3) {
		    ptr4 = dkstr_next(ptr3, NULL);
		    if(ptr4) {
		      ptr5 = dkstr_next(ptr4, NULL);
		      if(strcmp(ptr2, str_asterisk)) {
		      if(!dksto_it_find_like(a->init, ptr2, 1)) {
		        errmsgs[0] = allstrings[24];
			errmsgs[1] = str_space;
			errmsgs[2] = str_bropen;
			errmsgs[3] = ptr2;
			errmsgs[4] = str_brclose;
			dkapp_log_msg(a->app, DK_LOG_LEVEL_WARNING, errmsgs, 5);
		      }
		      }
		      if(strcmp(ptr4, str_asterisk)) {
		      if(!dksto_it_find_like(a->outit, ptr4, 1)) {
		        errmsgs[0] = allstrings[25];
			errmsgs[1] = str_space;
			errmsgs[2] = str_bropen;
			errmsgs[3] = ptr4;
			errmsgs[4] = str_brclose;
			dkapp_log_msg(a->app, DK_LOG_LEVEL_WARNING, errmsgs, 5);
		      }
		      }
                      tr = new_transition(ptr,ptr2,ptr3,ptr4,a->lineno);
		      if(tr) {
		        if(!dksto_add(a->alltr, (void *)tr)) {
			  delete_transition(tr); tr = NULL;
			  a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		        }
		      } else {
		        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
		      }
		    } else {
		      /* 16 */
		      allstrings = genau_strings();
		      if(allstrings && (a->app) && (a->input_file_name)) {
		        sprintf(errbuffer, "%lu", a->lineno);
		        errmsgs[0] = allstrings[15];
		        dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 1);
		      }
		    }
		  } else {
		    /* 16 */
		      allstrings = genau_strings();
		      if(allstrings && (a->app) && (a->input_file_name)) {
		        sprintf(errbuffer, "%lu", a->lineno);
		        errmsgs[0] = allstrings[15];
		        dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 1);
		      }
		  }
	        } else {
		  /* 16 */
		      allstrings = genau_strings();
		      if(allstrings && (a->app) && (a->input_file_name)) {
		        sprintf(errbuffer, "%lu", a->lineno);
		        errmsgs[0] = allstrings[15];
		        dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 1);
		      }
	        }
	      } break;
	      default: {
	      } break;
	    }
	  }
        }
      } else {
        can_continue = 0;
      }
    }
    dk_delete(line);
    dkapp_set_source_lineno(a->app, 0UL);
  } else {
    a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
  } 
}



/**	Link data (for each transition rule find the numeric values
	for states, input and output).
	@param	a	Automata.
*/
static
void
link_data DK_P1(automata_t *, a)
{
  au_state_t *st;
  au_input_t *in;
  au_output_t *out;
  au_state_transition_t *tr, *tr2;
  int can_continue;
  char errbuffer[32], *errmsgs[16], **allstrings;
  
  dksto_it_reset(a->alltrit);
  can_continue = 1;
  while(can_continue && (!(a->error_code))) {
    tr = (au_state_transition_t *)dksto_it_next(a->alltrit);
    if(tr) {
      if(strcmp(tr->current_name, key_all)) {
	st = dksto_it_find_like(a->stit, (void *)(tr->current_name), 1);
	if(!st) {
	  /* 17 Warning: state not yet defined */
	  allstrings = genau_strings();
	  if(allstrings && (a->app) && (a->input_file_name)) {
	    sprintf(errbuffer, "%lu", tr->lineno);
	    errmsgs[0] = allstrings[16];
	    errmsgs[1] = str_space;
	    errmsgs[2] = str_bropen;
	    errmsgs[3] = tr->current_name;
	    errmsgs[4] = str_brclose;
	    dkapp_set_source_lineno(a->app, tr->lineno);
	    dkapp_log_msg(a->app, DK_LOG_LEVEL_WARNING, errmsgs, 5);
	    dkapp_set_source_lineno(a->app, 0UL);
	  }
	  st = new_state(tr->current_name, tr->lineno);
	  if(st) {
	    if(!dksto_add(a->st, (void *)st)) {
	      a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
	      delete_state(st); st = NULL;
	    } else {
	      if(!(a->initial_state)) {
		a->initial_state = st;
	      }
	    }
	  } else {
	    a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
	  }
        }
	if(st) {
	  tr2 = dksto_it_find_like(st->trit, (void *)tr, 1);
	  if(tr2) {
	    char linenobuffer[32];
	    a->error_code = READAU_ERROR_RULE_ALREADY_DEFINED_FOR_INPUT;
	    allstrings = genau_strings();
	    if(allstrings && (a->app) && (a->input_file_name)) {
	      sprintf(linenobuffer, "%lu", tr2->lineno);
	      errmsgs[0] = allstrings[26];
	      errmsgs[1] = str_space;
	      errmsgs[2] = str_bropen;
	      errmsgs[3] = linenobuffer;
	      errmsgs[4] = str_brclose;
	      dkapp_set_source_lineno(a->app, tr->lineno);
	      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
	      dkapp_set_source_lineno(a->app, 0UL);
	    }
	  } else {
	    if(dksto_add(st->trans, (void *)tr)) {
	      tr->current_data = st;
	    } else {
	      a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
	    }
	  }
	}
      } else {
	if(strcmp(tr->input_name, key_all)) {
	  tr2 = dksto_it_find_like(a->asttrit, (void *)tr, 1);
	  if(!tr2) {
	    if(!dksto_add(a->asttr, (void *)tr)) {
	      a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
	    }
	    if(!dksto_add(a->asttrl, (void *)tr)) {
	      a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
	    }
	  } else {
	    char linenobuffer[32];
	    a->error_code = READAU_ERROR_RULE_ALREADY_DEFINED_FOR_INPUT;
	    allstrings = genau_strings();
	    if(allstrings && (a->app) && (a->input_file_name)) {
	      sprintf(linenobuffer, "%lu", tr2->lineno);
	      errmsgs[0] = allstrings[26];
	      errmsgs[1] = str_space;
	      errmsgs[2] = str_bropen;
	      errmsgs[3] = linenobuffer;
	      errmsgs[4] = str_brclose;
	      dkapp_set_source_lineno(a->app, tr->lineno);
	      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
	      dkapp_set_source_lineno(a->app, 0UL);
	    }
	  }
	} else {
	  if(!(a->general_rule)) {
	    a->general_rule = tr;
	  } else {
	    char linenobuffer[32];
	    a->error_code = READAU_ERROR_GENERAL_RULE_ALREADY_DEFINED;
	    allstrings = genau_strings();
	    if(allstrings && (a->app) && (a->input_file_name)) {
	      sprintf(linenobuffer, "%lu", (a->general_rule)->lineno);
	      errmsgs[0] = allstrings[27];
	      errmsgs[1] = str_space;
	      errmsgs[2] = str_bropen;
	      errmsgs[3] = linenobuffer;
	      errmsgs[4] = str_brclose;
	      dkapp_set_source_lineno(a->app, tr->lineno);
	      dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 5);
	      dkapp_set_source_lineno(a->app, 0UL);
	    }
	  }
	}
      }
      if(!(a->error_code)) {
	if(strcmp(tr->next_name, key_all)) {
          st = dksto_it_find_like(a->stit, (void *)(tr->next_name), 1);
          if(!st) {
            /* 18 Warning: state undefined */
	    allstrings = genau_strings();
	    if(allstrings && (a->app) && (a->input_file_name)) {
	      sprintf(errbuffer, "%lu", tr->lineno);
	      errmsgs[0] = allstrings[17];
	      errmsgs[1] = str_space;
	      errmsgs[2] = str_bropen;
	      errmsgs[3] = tr->next_name;
	      errmsgs[4] = str_brclose;
	      dkapp_set_source_lineno(a->app, tr->lineno);
	      dkapp_log_msg(a->app, DK_LOG_LEVEL_WARNING, errmsgs, 5);
	      dkapp_set_source_lineno(a->app, 0UL);
	    }
            st = new_state(tr->next_name, tr->lineno);
            if(st) {
              if(!dksto_add(a->st, (void *)st)) {
      	        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
      	        delete_state(st); st = NULL;
              } else {
		if(!(a->initial_state)) {
		  a->initial_state = st;
		}
	      }
            } else {
              a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
            }
          }
          if(st) {
            tr->next_data = st;
          }
	}
      }
      if(!(a->error_code)) {
	if(strcmp(tr->input_name, key_all)) {
          in = dksto_it_find_like(a->init, (void *)(tr->input_name), 1);
          if(!in) {
            in = new_input(tr->input_name, tr->lineno);
            if(in) {
              if(!dksto_add(a->in, (void *)in)) {
      	        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
      	        delete_input(in); in = NULL;
              }
            } else {
              a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
            }
          }
          if(in) {
            tr->input_data = in;
          }
	}
      }
      if(!(a->error_code)) {
	if(strcmp(tr->output_name, key_all)) {
          out = dksto_it_find_like(a->outit, (void *)(tr->output_name), 1);
          if(!out) {
            out = new_output(tr->output_name, tr->lineno);
            if(out) {
              if(!dksto_add(a->out, (void *)out)) {
      	        a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
      	        delete_output(out); out = NULL;
              } else {
		if(!(a->def_output)) { a->def_output = out; }
	      }
            } else {
              a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
            }
          }
          if(out) {
            tr->output_data = out;
          }
	}
      }
    } else {
      can_continue = 0;
    }
  }
  
}

#if DEBUG

#define XPTR(x) (x ? "PTR" : "(NULL)")
#define XSTR(x) (x ? x : "(NULL)")

static
void
debug_print DK_P1(automata_t *, a)
{
  au_option_line_t *ol;
  au_state_t *st;
  au_input_t *in;
  au_output_t *out;
  au_state_transition_t *tr, *tr2;

  printf("[options]\n");
  dksto_it_reset(a->optit);
  while((ol = dksto_it_next(a->optit)) != NULL) {
    printf("\t%s\n", XSTR(ol->line));
  }
  printf("[inputs]\n");
  dksto_it_reset(a->init);
  while((in = dksto_it_next(a->init)) != NULL) {
    printf("\t%s # %d\n", XSTR(in->name), in->number);
  }
  printf("[outputs]\n");
  dksto_it_reset(a->outit);
  while((out = dksto_it_next(a->outit)) != NULL) {
    printf("\t%s # %d\n", XSTR(out->name), out->number);
  }
  printf("[states]\n");
  dksto_it_reset(a->stit);
  while((st = dksto_it_next(a->stit)) != NULL) {
    printf("\t%s # %d\n", XSTR(st->name), st->number);
    dksto_it_reset(st->trit);
    while((tr = dksto_it_next(st->trit)) != NULL) {
      printf("\t# %s:%s %s:%s %s:%s %s:%s\n",
	XPTR(tr->current_data), XSTR(tr->current_name),
	XPTR(tr->input_data), XSTR(tr->input_name),
	XPTR(tr->next_data), XSTR(tr->next_name),
	XPTR(tr->output_data), XSTR(tr->output_name)
      );
    }
  }
  printf("# General rule\n");
  tr = a->general_rule;
  if(tr) {
      printf("\t# %s:%s %s:%s %s:%s %s:%s\n",
	XPTR(tr->current_data), XSTR(tr->current_name),
	XPTR(tr->input_data), XSTR(tr->input_name),
	XPTR(tr->next_data), XSTR(tr->next_name),
	XPTR(tr->output_data), XSTR(tr->output_name)
      );
  }
  printf("# Rules for inputs by name\n");
  dksto_it_reset(a->asttrit);
  while((tr = dksto_it_next(a->asttrit)) != NULL) {
      printf("\t# %s:%s %s:%s %s:%s %s:%s\n",
	XPTR(tr->current_data), XSTR(tr->current_name),
	XPTR(tr->input_data), XSTR(tr->input_name),
	XPTR(tr->next_data), XSTR(tr->next_name),
	XPTR(tr->output_data), XSTR(tr->output_name)
      );
  }
  printf("# Rules for inputs by line number\n");
  dksto_it_reset(a->asttrlit);
  while((tr = dksto_it_next(a->asttrlit)) != NULL) {
      printf("\t# %s:%s %s:%s %s:%s %s:%s\n",
	XPTR(tr->current_data), XSTR(tr->current_name),
	XPTR(tr->input_data), XSTR(tr->input_name),
	XPTR(tr->next_data), XSTR(tr->next_name),
	XPTR(tr->output_data), XSTR(tr->output_name)
      );
  }
  printf("[rules]\n");
  dksto_it_reset(a->alltrit);
  while((tr = dksto_it_next(a->alltrit)) != NULL) {
    printf("\t%s %s %s %s # %s %s %s %s\n",
      XSTR(tr->current_name),
      XSTR(tr->input_name),
      XSTR(tr->next_name),
      XSTR(tr->output_name),
      XPTR(tr->current_data),
      XPTR(tr->input_data),
      XPTR(tr->next_data),
      XPTR(tr->output_data)
    );
  }
}

#endif



/**	Issue error "too many states".
	@param	a	Automata
*/
static
void
error_too_many_states DK_P1(automata_t *, a)
{
  char **allstrings;
  char *errmsgs[16];
  allstrings = genau_strings();
  if(allstrings && (a->app) && (a->input_file_name)) {
    errmsgs[0] = allstrings[28];
    dkapp_set_source_lineno(a->app, 0UL);
    dkapp_log_msg(a->app, DK_LOG_LEVEL_ERROR, errmsgs, 1);
  }
}



/**	Assign numbers to states, inputs and outputs.
	@param	a	Automata.
*/
static
void
set_numbers DK_P1(automata_t *, a)
{
  dk_bitfield_t *bf;
  au_state_t *st;
  au_input_t *in;
  au_output_t *out;
  int have_numbered, have_unnumbered, min, max;
  int direction, value, not_found;
  
  /*
    States
  */
  have_numbered = 0;
  have_unnumbered = 0;
  min = 0;
  max = 0;
  dksto_it_reset(a->stit);
  while((st = (au_state_t *)dksto_it_next(a->stit)) != NULL) {
    if(st->number_defined) {
      
      if(have_numbered) {
        
	if(min > (st->number)) min = st->number;
	if(max < (st->number)) max = st->number;
      } else {
        
	min = max = st->number;
      }
      have_numbered = 1;
    } else {
      have_unnumbered = 1;
    }
  }
  if(have_unnumbered) {		
    bf = NULL;
    direction = 1; value = -1;
    if(have_numbered) {		
      direction = 0;
      value = min;	
      bf = dkbf_open(1+max-min);
      if(bf) {
        
	dksto_it_reset(a->stit);
	while((st = (au_state_t *)dksto_it_next(a->stit)) != NULL) {
	  if(st->number_defined) {
	    dkbf_set(bf,((st->number)-min),1);
	  }
	}
      } else {
        
	a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
      }
    }
    dksto_it_reset(a->stit);
    while((st = (au_state_t *)dksto_it_next(a->stit)) != NULL) {
      if(!(st->number_defined)) {
	
        not_found = 1;
	if(not_found) {
	  if(direction == 0) {
	    if(bf) {
	      while(not_found && (value < max)) {
		if(!dkbf_get(bf,(value-min))) {
		  not_found = 0;
		  dkbf_set(bf,(value-min),1);
		} else {
		  value++;
		}
	      }
	      if(value == max) {
		direction = 1;
	      }
	    } else {
	      value = max ;
	      direction = 1;
	    }
	  }
	}
	if(not_found) {
	  if(direction == 1) {
	    if(value < DK_MAX_INT) {
	      value++; not_found = 0;
	    } else {
	      if(have_numbered) {
		value = min;
	      } else {
		value = 0;
	      }
	      direction = 2;
	    }
	  }
	}
	if(not_found) {
	  if(direction == 2) {
	    if(value > (0 - DK_MAX_INT)) {
	      value--;
	      not_found = 0;
	    } else {
	    }
	  }
	}
	if(not_found) {
	  a->error_code = READAU_ERROR_TOO_MANY_STATES;
	  error_too_many_states(a);
	}
	
	st->number = value;
	/* st->number_defined = 1; */
      }
    }
    if(bf) {
      dkbf_close(bf); 
    }
    bf = NULL;
  }
  /*
    Inputs
  */
  have_numbered = 0;
  have_unnumbered = 0;
  min = 0;
  max = 0;
  dksto_it_reset(a->init);
  while((in = (au_input_t *)dksto_it_next(a->init)) != NULL) {
    if(in->number_defined) {
      if(have_numbered) {
	if(min > (in->number)) min = in->number;
	if(max < (in->number)) max = in->number;
      } else {
	min = max = in->number;
      }
      have_numbered = 1;
    } else {
      have_unnumbered = 1;
    }
  }
  if(have_unnumbered) {
    bf = NULL;
    direction = 1; value = -1;
    if(have_numbered) {
      direction = 0;
      value = min;
      bf = dkbf_open(1+max-min);
      if(bf) {
	dksto_it_reset(a->init);
	while((in = (au_input_t *)dksto_it_next(a->init)) != NULL) {
	  if(in->number_defined) {
	    
	    dkbf_set(bf,((in->number)-min),1);
	  }
	}
      } else {
	a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
      }
    } 
    dksto_it_reset(a->init);
    while((in = (au_input_t *)dksto_it_next(a->init)) != NULL) {
      
      if(!(in->number_defined)) {
	
        not_found = 1;
	if(not_found) {
	  if(direction == 0) {
	    if(bf) {
	      while(not_found && (value < max)) {
		if(!dkbf_get(bf,(value-min))) {
		  not_found = 0;
		  dkbf_set(bf,(value-min),1);
		} else {
		  value++;
		}
	      }
	      if(value == max) {
		direction = 1;
	      }
	    } else {
	      value = max ;
	      direction = 1;
	    }
	  }
	}
	if(not_found) {
	  if(direction == 1) {
	    if(value < DK_MAX_INT) {
	      value++; not_found = 0;
	    } else {
	      if(have_numbered) {
		value = min;
	      } else {
		value = 0;
	      }
	      direction = 2;
	    }
	  }
	}
	if(not_found) {
	  if(direction == 2) {
	    if(value > (0 - DK_MAX_INT)) {
	      value--;
	      not_found = 0;
	    } else {
	    }
	  }
	}
	if(not_found) {
	  a->error_code = READAU_ERROR_TOO_MANY_STATES;
	  error_too_many_states(a);
	}
	
	in->number = value;
	/* in->number_defined = 1; */
      }
    }
    if(bf) {
      dkbf_close(bf); 
    }
    bf = NULL;
  }
  /*
    Outputs
  */
  have_numbered = 0;
  have_unnumbered = 0;
  min = 0;
  max = 0;
  dksto_it_reset(a->outit);
  while((out = (au_output_t *)dksto_it_next(a->outit)) != NULL) {
    if(out->number_defined) {
      if(have_numbered) {
	if(min > (out->number)) min = out->number;
	if(max < (out->number)) max = out->number;
      } else {
	min = max = out->number;
      }
      have_numbered = 1;
    } else {
      have_unnumbered = 1;
    }
  }
  if(have_unnumbered) {
    bf = NULL;
    direction = 1; value = -1;
    if(have_numbered) {
      direction = 0;
      value = min;
      bf = dkbf_open(1+max-min);
      if(bf) {
	dksto_it_reset(a->outit);
	while((out = (au_output_t *)dksto_it_next(a->outit)) != NULL) {
	  if(out->number_defined) {
	    dkbf_set(bf,((out->number)-min),1);
	  }
	}
      } else {
	a->error_code = READAU_ERROR_NOT_ENOUGH_MEMORY;
      }
    }
    dksto_it_reset(a->outit);
    while((out = (au_output_t *)dksto_it_next(a->outit)) != NULL) {
      if(!(out->number_defined)) {
        not_found = 1;
	if(not_found) {
	  if(direction == 0) {
	    if(bf) {
	      while(not_found && (value < max)) {
		if(!dkbf_get(bf,(value-min))) {
		  not_found = 0;
		  dkbf_set(bf,(value-min),1);
		} else {
		  value++;
		}
	      }
	      if(value == max) {
		direction = 1;
	      }
	    } else {
	      value = max ;
	      direction = 1;
	    }
	  }
	}
	if(not_found) {
	  if(direction == 1) {
	    if(value < DK_MAX_INT) {
	      value++; not_found = 0;
	    } else {
	      if(have_numbered) {
		value = min;
	      } else {
		value = 0;
	      }
	      direction = 2;
	    }
	  }
	}
	if(not_found) {
	  if(direction == 2) {
	    if(value > (0 - DK_MAX_INT)) {
	      value--;
	      not_found = 0;
	    } else {
	    }
	  }
	}
	if(not_found) {
	  a->error_code = READAU_ERROR_TOO_MANY_STATES;
	  error_too_many_states(a);
	}
	out->number = value;
	/* out->number_defined = 1; */
      }
    }
    if(bf) {
      dkbf_close(bf); 
    }
    bf = NULL;
  }
  
}


automata_t *
readau_new DK_P4(dk_stream_t *, i, dk_app_t *, app, int *, flg, char *, name)
{
  automata_t *back = NULL;
  char *errmsgs[32], **allstrings;
  if(i && flg) {
    back = new_automata(flg);
    if(back) {
      back->input_file_name = name;
      back->app = app; back->is = i;
      read_from_stream(back);
      if(!back->error_code) {
	link_data(back);
	if(!(back->error_code)) {
	  set_numbers(back);
	  if(!(back->error_code)) {
#if DEBUG
	    debug_print(back);
#endif
	  }
	}
      }
      if(back->error_code) {
	if((back->error_code) == READAU_ERROR_NOT_ENOUGH_MEMORY) {
	  allstrings = genau_strings();
	  if(allstrings && (back->app) && (back->input_file_name)) {
	    errmsgs[0] = allstrings[20];
	    dkapp_log_msg(back->app, DK_LOG_LEVEL_ERROR, errmsgs, 1);
	  }
	}
	*flg = back->error_code;
	readau_delete(back);
	back = NULL;
      }
    }
  }
  return back;
}


