/*
Copyright (c) 2000-2005, 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
  its 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	traceco.c	The worker module of the tracecc program.
*/



/**	Inside the traceco module.
 * */
#define TRACECO_C 1

#include <tracecc.h>



/**	Maximum control line length.
*/
#define TRACE_CONTROL 512



/**	About to debug.
*/
#define DEBUG 1



/**	Abbreviation.
*/
typedef char *p_char_t;



/**	Message.
*/
typedef struct {
  char *key;	/**< Key (message name). */
  char *def;	/**< Default text. */
} message_contents_t;



/**	Mapping of source file name suffix to destination file name suffix.
*/
typedef struct {
  char  *olds;		/**< Source file name suffix. */
  char  *news;		/**< Destination file name suffix. */
} suffix_t;

/**	Map source file name suffix to destination file name suffix.
*/
static suffix_t  suffixlist[] = {
  { ".ctr", ".c"    },
  { ".cpt", ".cpp"  },
  { ".mtr", ".m"    },
  { ".jtr", ".java" },
  { NULL, NULL }
};



/**	Relationship between destination file name suffix and file type.
*/
typedef struct {
  char  *suffix;	/**< File name suffix. */
  int   	 ft;		/**< File type: 1=C, 2=Java. */
} file_t;


/**	File type list (maps file name suffix to numeric type value).
*/
static file_t  ftlist[] = {
  { ".c",    1 },
  { ".cpp",  1 },
  { ".m",    1 },
  { ".java", 2 },
  { NULL, 0 }
};



/**	Function type invoked for $(xxx) or $!xxx.
*/
#if DK_HAVE_PROTOTYPES
typedef int IFCT(tracecc_t *tc, char *args);
#else
typedef int IFCT();
#endif
/**	Pointer to command function.
*/
typedef IFCT *i_fct_t;



/**	String table name.
*/
static char str_tracecc[] = { "tracecc" };


/**	String table name.
*/
static char string_table_name[] = { "tracecc" };



/**	Messages printed by the program.
*/
static message_contents_t mc[] = {
  {
  (char *)"/m/00",
  (char *)"Small buffer in use."
  },

  {
  (char *)"/m/01",
  (char *)"Command not executed!"
  },

  {
  (char *)"/m/02",
  (char *)"No popen() function!"
  },

  {
  (char *)"/m/03",
  (char *)"Arguments needed!"
  },

  {
  (char *)"/m/04",
  (char *)"Missing arguments!"
  },

  {
  (char *)"/m/05",
  (char *)"No such function!"
  },

  {
  (char *)"/m/06",
  (char *)"Empty function name!"
  },

  {
  (char *)"/m/07",
  (char *)"Failed to write file!"
  },

  {
  (char *)"/m/08",
  (char *)"Failed to read file!"
  },

  {
  (char *)"/m/09",
  (char *)"Multiple files for pattern!"
  },

  {
  (char *)"/m/10",
  (char *)"No file for pattern!"
  },

  {
  (char *)"/m/11",
  (char *)"Multiple files for pattern!"
  },

  {
  (char *)"/m/12",
  (char *)"No file for pattern!"
  },

  {
  (char *)"/m/13",
  (char *)"Destination file name too long!"
  },

  {
  (char *)"/m/14",
  (char *)"Failed to open directory!"
  },

  {
  (char *)"/m/15",
  (char *)"No directory for pattern!"
  },

  {
  (char *)"/m/16",
  (char *)"Processing file \""
  },

  {
  (char *)"/m/17",
  (char *)"\"."
  },

  {
  (char *)"/m/18",
  (char *)"Finished processing file \""
  },

  {
  (char *)"/m/19",
  (char *)"\"."
  },

  {
  (char *)"/m/20",
  (char *)"Processing directory \""
  },

  {
  (char *)"/m/21",
  (char *)"\"."
  },

  {
  (char *)"/m/22",
  (char *)"Finished processing directory \""
  },

  {
  (char *)"/m/23",
  (char *)"\"."
  },

  {
  (char *)"/m/24",
  (char *)"Skipping source file \""
  },

  {
  (char *)"/m/25",
  (char *)"\". Destination file is newer."
  },

  {
  (char *)"/m/26",
  (char *)"Input line too long!"
  },

  {
  (char *)"/m/27",
  (char *)"File is not a directory!"
  },

  {
    (char *)"/m/28",
    (char *)"Failed to change to directory \""
  },

  {
    (char *)"/m/29",
    (char *)"\"!"
  },

  {
    (char *)"/m/30",
    (char *)"Failed to find current working directory!"
  },

  {
    (char *)"/m/31",
    (char *)"\""
  },

  {
    (char *)"/m/32",
    (char *)"\" is not an absolute path!"
  },

  { NULL, NULL }
};

/**	Number of messages in \a mc.
*/
static size_t number_of_messages =
sizeof(mc)/sizeof(message_contents_t);



/**	IDE styles for debug messages.
*/
static char *ide_styles[] = {
  (char *)"none",
  (char *)"gcc",
  (char *)"msvc",
  (char *)"workshop",
  (char *)"tasm",
  NULL
};



static char  *string_file_keywords[] = {
  /*  0 */ "macro",
  /*  1 */ "prefix",
  NULL
};



/**	Preference key: Write debug messages.
*/
static char pk_debug_enable[] = { "/debug/enable" };

/**	Preference key: Write debug messages to stdout.
*/
static char pk_debug_stdout[] = { "/debug/stdout" };

/**	Preference key: Print timestamp for debug mesages.
*/
static char pk_debug_timestamp[] = { "/debug/timestamp" };

/**	Preference key: Write "debug" keyword.
*/
static char pk_debug_keyword[] = { "/debug/keyword" };

/**	Preference key: Write line numbers.
*/
static char pk_linenumbers[] = { "/linenumbers" };

/**	Preference key: Use make mode.
*/
static char pk_make[] = { "/make" };

/**	Preference key: Allow C++ style comments.
*/
static char pk_cpp_comments[] = { "/cpp-comments" };

/**	Preference key: Maximum comment box width.
*/
static char pk_boxwidth[] = { "/box-width" };

/**	Preference key: IDE style for debug messages.
*/
static char pk_ide_style[] = { "/debug/ide-style" };

/**	Preference key: Input line length.
*/
static char pk_input_length[] = { "/input/length" };

/**	Preference key: Command length.
*/
static char pk_command_length[] = { "/command/length" };



/**	Boolean value: on.
*/
static char str_on[] = { "on" };

/**	Boolean value: Off.
*/
static char str_off[] = { "off" };

/**	Space.
*/
static char str_spc[] = { " " };

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

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



/**	Function name: Retrieve output FILE *.
*/
static char str_c_fct_get_file[] = { "dktrace_file" };

/**	Function name: Start tracing.
*/
static char str_c_fct_init[] = { "dktrace_init" };

/**	Function name: Finish tracing.
*/
static char str_c_fct_end[] = { "dktrace_end" };

/**	Function name: Print timestamp.
*/
static char str_c_fct_time[] = { "dktrace_time" };

/**	Function name: Print timestamp to standard output.
*/
static char str_c_fct_stdout_time[] = { "dktrace_stdout_time" };



/**	Path component separator.
*/
static char fnsep[] = {
#if DK_HAVE_FEATURE_BACKSLASH
"\\"
#else
"/"
#endif
};



/**	Character used to print hexadecimal numbers.
*/
static char hexdigits[] = { "0123456789ABCDEF0000" };



/**	File open mode: Read.
*/
static char str_r[] = { "r" };

/**	File open mode: Write.
*/
static char str_w[] = { "w" };



/**	Modify one bit in a byte depending on a preference.
	@param	mode	Pointer to variable (in: old mode, out: changed mode).
	@param	app	Application.
	@param	key	Prefrence key.
	@param	bit	Bit to set or reset.
*/
static
void
set_mode_bit_bool DK_P4(int *,mode,dk_app_t *,app,char *,key,int,bit)
{
  int val;
  char inputline[256], *ptr;
  val = *mode;
  if(dkapp_get_pref(app,key,inputline,sizeof(inputline),0)) {
    ptr = dkstr_start(inputline, NULL);
    if(ptr) {
      dkstr_chomp(ptr, NULL);
      if(dkstr_is_on(ptr)) {
        val |= bit;
      } else {
        val &= (~bit);
      }
    }
  }
  *mode = val;
}



/**	Save a boolean preference.
	@param	app	Application.
	@param	key	Preference key.
	@param	v	Boolean value to save.
*/
static
void
set_bool DK_P3(dk_app_t *,app,char *,key,int,v)
{
  dkapp_set_pref(app, key, (v ? str_on : str_off));
}



/**	Set int value from preference.
	@param	iptr	Pointer to result variable.
	@param	app	Application.
	@param	key	Preference key.
*/
static
void
set_int_from_pref DK_P3(int *,iptr,dk_app_t *,app,char *,key)
{
  char inputline[32];
  int i;
  if(dkapp_get_pref(app,key,inputline,sizeof(inputline),0)) {
    if(sscanf(inputline, "%d", &i) == 1) {
      *iptr = i;
    }
  }
}



/**	Set all pointers in the message text array to NULL.
	@param	msgarray	Message text array.
*/
static
void
reset_strings_for_messages DK_P1(char **,msgarray)
{
  char **lfdptr; size_t i;
  
  lfdptr = msgarray;
  for(i = 0; i < number_of_messages; i++) {
    *(lfdptr++) = NULL;
  }
  
}



/**	Set message array to default values.
	@param	msgarray	Message text array.
*/
static
void
set_strings_for_messages DK_P1(char **,msgarray)
{
  char **lfdptr;
  size_t i;
  message_contents_t *mcptr;
  
  mcptr = mc; lfdptr = msgarray;
  for(i = 0; i < number_of_messages; i++) {
    *(lfdptr++) = (mcptr++)->def;
  }
  
}



/**	Retrieve message texts using the application.
	@param	msgarray	Text array to fill.
	@param	app		Application.
*/
static
void
set_strings_by_app DK_P2(char **,msgarray,dk_app_t *,app)
{
  char **lfdptr;
  dk_string_finder_t *fptr, *fptr2;
  message_contents_t *mcptr;
  size_t i;
  
  fptr = dk_new(dk_string_finder_t,number_of_messages);
  if(fptr) {
    
    lfdptr = msgarray; fptr2 = fptr; mcptr = mc;
    for(i = 0; i < number_of_messages; i++) {
      fptr2->key = mcptr->key;
      fptr2->value_pointer = lfdptr;
      fptr2->default_value = mcptr->def;
      lfdptr++; fptr2++; mcptr++;
    }
    dkapp_find_multi(app, fptr, string_table_name);
    dk_delete(fptr);
  } else {
    
    set_strings_for_messages(msgarray);
    dkapp_err_memory(app,sizeof(dk_string_finder_t),number_of_messages);
  }
  
}



/**	Put unsigned long to file (as text).
	@param	ul	The value to write.
	@param	f	Output file.
*/
static
void
fputul DK_P2(unsigned long,ul,FILE *,f)
{
  char buffer[32];
  sprintf(buffer, "%lu", ul);
  fputs(buffer,f);
}



/**	Check current character whether or not it is the tracecc control character.
	@param	ft	File type.
	@param	c	Character to check.
	@return	1=control, 0=normal character.
*/
static
int
is_trace_control DK_P2(int,ft,char,c)
{
  int back = 0;
  
  switch(ft) {
    /* Java */
    case 2:  { if(c == '$') back = 1; } break;
    /* C, C++, Objective-C */
    case 1:  { if(c == '$') back = 1; } break;
    /* all others */
    default: { if(c == '$') back = 1; } break;
  } 
  return back;
}



/**	Transition function of the state machine.
	@param	ptr	Pointer to state variable (in:current state, out: new state).
	@param	input	Current input.
	@return	Output for transition.
*/
static
int
automata_reaction DK_P2(int *, ptr, int, input)
{
  int back = 0;
  int newstate = 0;
  if(ptr) {
    
    newstate = back = 0;
    switch(*ptr) {
      case 0: { /* initial state */
        switch(input) {
          case '\\': { newstate = back = 1; } break;
	  case '"': { newstate = 2; back = 1; } break;
	  case '\'': { newstate = 4; back = 1; } break;
	  case '/': { newstate = 6; back = 0; } break;
	  case TRACE_CONTROL: { newstate = 10; back = 0; } break;
	  default: { newstate = 0; back = 1; } break;
	}
      } break;
      case 1: { /* after backslash */
        newstate = 0; back = 1;
      } break;
      case 2: { /* in a string */
	back = 1;
        switch(input) {
          case '"': { newstate = 0; } break;
	  case '\\': { newstate = 3; } break;
	  default: { newstate = 2; } break;
	}
      } break;
      case 3: { /* after backslash in string */
        newstate = 2; back = 1;
      } break;
      case 4: { /* single character constant */
	back = 1;
        switch(input) {
          case '\'': { newstate = 0; } break;
	  case '\\': { newstate = 5; } break;
	  default: { newstate = 4; } break;
	}
      } break;
      case 5: { /* after backslash in single character constant */
        newstate = 4; back = 1;
      } break;
      case 6: { /* after slash */
        switch(input) {
          case '/': { newstate = 7; back = 16; } break;
	  case '*': { newstate = 8; back = 18; } break;
	  default: { newstate = 0; back = 18; } break;
	}
      } break;
      case 7: { /* in C++ style comment */
	newstate = 7; back = 1;
        if(input == '\n') {
          newstate = 0; back = 17;
	}
      } break;
      case 8: { /* in normal comment */
        newstate = 8; back = 1;
	if(input == '*') newstate = 9;
      } break;
      case 9: { /* after asterisk in normal C comment */
	back = 1;
        switch(input) {
          case '*': { newstate = 9; } break;
	  case '/': { newstate = 0; } break;
	  default: { newstate = 8; } break;
	}
      } break;
      case 10: { /* after TRACE_CONTROL */
        switch(input) {
          case '(': { newstate = 15; back = 0; } break;
	  case '!': { newstate = 12; back = 0; } break;
	  case '?': { newstate = 11; back = 3; } break;
	  case '*': { newstate = 17; back = 11; } break;
	  default: { newstate = 0; back = 2; } break;
	}
      } break;
      case 11: { /* after TRACE_CONTROL, question mark */
        switch(input) {
          case '\n': { newstate = 0; back = 10; } break;
	  case ' ' : { newstate = 11; back = 0; } break;
	  case '\t': { newstate = 11; back = 0; } break;
	  default: { newstate = 13; back = 4; } break;
	}
      } break;
      case 12: { /* after TRACE_CONTROL, exclam */
        switch(input) {
          case '\n': { newstate = 0; back = 1; } break;
	  case ' ':
	  case '\t': { newstate = 12; back = 0; } break;
	  default: { newstate = 14; back = 6; } break;
	}
      } break;
      case 13: { /* in debug output */
	newstate = 13; back = 9;
        if(input == '\n') {
          newstate = 0; back = 5;
	}
      } break;
      case 14: { /* in interprete context */
	newstate = 14; back = 7;
        if(input == '\n') {
          newstate = 0; back = 19;
	}
      } break;
      case 15: { /* in brackets in interprete context*/
        switch(input) {
          case '\n': { newstate = 0; back = 1; } break;
	  case ')': { newstate = 1; back = 0; } break;
	  case ' ': 
	  case '\t': { newstate = 15; back = 0; } break;
	  default: { newstate = 16; back = 6; } break;
	}
      } break;
      case 16: {
	newstate = 16; back = 7;
        if(input == ')') {
          newstate = 0; back = 8;
	}
      } break;
      case 17: { /* in comment box */
        switch(input) {
          case '\n': { newstate = 17; back = 12; } break;
	  case TRACE_CONTROL: { newstate = 18; back = 0; } break;
	  default: { newstate = 17; back = 13; } break;
	}
      } break;
      case 18: { /* in comment box, found TRACE_CONTROL */
	newstate = 17; back = 14;
        if(input == '*') {
          newstate = 0; back = 15;
	}
      } break;
    }
    *ptr = newstate;
    
  }
  return back;
}



/**	Find file type by checking the destination file name suffix.
	@param	suffix	Destination file name suffix.
	@return	File type (numeric value).
*/
static
int
get_new_file_type DK_P1(char *,suffix)
{
  int back = 0; file_t  *ptr;
  
  ptr = ftlist;
  while((ptr->suffix) && (!back)) {
    if(strcmp(ptr->suffix, suffix) == 0) {
      back = ptr->ft;
    } else {
      ptr++;
    }
  }
  return back;
}



/**	Find destination file name suffix for given source file suffix.
	@param	suffix	Source file suffix.
	@return	Destination file suffix on success, NULL on error.
*/
static
char  *
get_new_suffix DK_P1(char *,suffix)
{
  char  *back = NULL;
  suffix_t  *sptr;
  
  sptr = suffixlist;
  while((sptr->olds) && (!back)) {
    if(strcmp(sptr->olds, suffix) == 0) {
      back = sptr->news;
    }
    sptr++;
  }
  
  return back;
}



/**	Indent current line to same indent level as previous line.
	@param	tc	Tracecc job.
*/
static
void
indent_this_line DK_P1(tracecc_t *,tc)
{
  int i;
  
  for(i = 0; i < tc->indent; i++) {
    fputc(' ', tc->outfile);
  }
  
}



/**	Check whether or not to show timestamps in debug output.
	@param	tc	Tracecc job.
	@return	1=yes, 0=no.
*/
static
int
is_show_timestamp DK_P1(tracecc_t *,tc)
{
  int back;
  
  back = (((tc->f).o) & TRACECC_OPT_DATETIME) ? 1 : 0;
  
  return back;
}



/**	Print current character.
	@param	tc	Tracecc job.
*/
static
void
print_current_char DK_P1(tracecc_t *,tc)
{
  
  fputc(tc->currentchar, tc->outfile);
  if(tc->currentchar == '\n') fflush(tc->outfile);
}



/**	Print tracecc control character and current character to output.
	@param	tc	Tracecc job.
*/
static
void
tc_and_current_char DK_P1(tracecc_t *,tc)
{
  char c;
  
  switch(tc->newft) {
    case 2:
    case 1:
    default: {
      c = '$';
    } break;
  }
  fputc(c, tc->outfile);
  print_current_char(tc);
  
}



/**	Check whether C++ style comments are allowed.
	@param	tc	Tracecc job.
	@return	1=yes, 0=no.
*/
static
int
have_cpp_comment DK_P1(tracecc_t *,tc)
{
  int back;
  back = (((tc->o).o) & ((tc->f).o) & TRACECC_OPT_CPPCOMMENT) ? 1 : 0;
  
  return back;
}



/**	Check for debug mode.
	@param	tc	Tracecc job.
	@return	1=on, 0=off.
*/
static
int
is_debug DK_P1(tracecc_t *,tc)
{
  int back;
  back = (((tc->o).o) & ((tc->f).o) & TRACECC_OPT_DEBUG) ? 1 : 0;
  
  return back;
}



/**	Add character to output only if we are in debug mode.
	@param	tc	Tracecc job.
*/
static
void
print_debug_char DK_P1(tracecc_t *,tc)
{
  
  if(is_debug(tc)) {
    print_current_char(tc);
  }
  
}



/**	Check whether or not we have to write debug messages to stdout.
	@param	tc	Tracecc job.
	@return	1=yes, 0=no.
*/
static
int
is_debst DK_P1(tracecc_t *,tc)
{
  int back = 0;
  if(is_debug(tc)) {
    back = (((tc->o).o) & ((tc->f).o) & TRACECC_OPT_DEBST) ? 1 : 0;
  }
  
  return back;
}



/**	Write newline to output file.
	@param	tc	Tracecc job.
*/
static
void
nl DK_P1(tracecc_t *,tc)
{
  fputc('\n', tc->outfile);
  
}



/**	Check whether we need to print the source position.
	@param	tc	Tracecc job.
	@return	1=yes, 0=no.
*/
static
int
need_source_position DK_P1(tracecc_t *,tc)
{
  int back = 0;
  switch(tc->newft) {
    case 1:
    default: {
      if(((tc->o).o) & ((tc->f).o) & TRACECC_OPT_LINENO) back = 1;
    } break;
  }
  return back;
}



/**	Print current source position.
	@param	tc	Tracecc job.
	@param	cor	Correction value for line number (-1, 0 or 1).
*/
static
void
print_source_position DK_P2(tracecc_t *,tc,int,cor)
{
  FILE *f;
  unsigned long ul;

  f = tc->outfile; ul = tc->lineno;
  switch(cor) {
    case -1: ul--; break;
    case  1: ul++; break;
  }
  fputs("#line ", f),
  fputul(ul, f);
  fputc(' ', f);
  fputc('"', f);
  fputs(tc->in_short, f);
  fputc('"', f);
  nl(tc);
}



/**	Print file name and line number depending on IDE style.
	@param	tc	Tracecc job.
*/
static
void
show_filename_and_lineno DK_P1(tracecc_t *,tc)
{
  FILE *f;
  
  f = tc->outfile;
  fputs("\"", f);
  switch((tc->f).i) {
    case DK_APP_IDE_TASM: {	
      fputs("*Warning* ", f);
      fputs(tc->in_short, f);
      fputc('(', f);
      fputul(tc->lineno, f);
      fputc(')', f);
    } break;
    case DK_APP_IDE_WORKSHOP: {	
      fputc('"', f);
      fputs(tc->in_short, f);
      fputc('"', f);
      fputs(", line ", f);
      fputul(tc->lineno, f);
      fputc(':', f);
    } break;
    case DK_APP_IDE_MSVC: {	
      fputs(tc->in_short, f);
      fputc('(', f);
      fputul(tc->lineno, f);
      fputs(") :", f);
    } break;
    case DK_APP_IDE_GCC:
    default: {		
      fputs(tc->in_short, f);
      fputc(':', f);
      fputul(tc->lineno, f);
      fputc(':', f);
    } break;
  }
  if(((tc->f).o) & TRACECC_OPT_KEYWORD) {
    fputs(" trace", f);
  }
  fputs(" \"", f);
  
}



/**	Start debug line for C output.
	@param	tc	Tracecc job.
*/
static
void
start_c_line DK_P1(tracecc_t *,tc)
{
  FILE *f;
  
  f = tc->outfile;
  if(is_debst(tc)) {
    fputc( '{', f);
    nl(tc);
    indent_this_line(tc);
    fputs( "printf(", f);
  } else {
    fputs("{ if(", f);
    fputs(str_c_fct_get_file, f);
    fputs("()) {", f);
    nl(tc);
    indent_this_line(tc);
    fputs("fprintf(", f);
    fputs(str_c_fct_get_file, f);
    fputs("(), ", f);
  }
  
}



/**	End debug output block for C output.
	@param	tc	Tracecc job.
*/
static
void
end_c_line DK_P1(tracecc_t *,tc)
{
  FILE *f;
  
  f = tc->outfile;
  fputs(");", f);
  if(is_debst(tc)) {
    nl(tc);
    indent_this_line(tc);
  } else {
    nl(tc);
    indent_this_line(tc);
    fputs("} ", f);
  }
  fputs("}\n", f);
  
}



/**	Begin debug output block for Java output.
	@param	tc	Tracecc job.
*/
static
void
begin_java_trace DK_P1(tracecc_t *,tc)
{
  FILE *f;
  f = tc->outfile;
  if(is_debug(tc)) {
    if(is_show_timestamp(tc)) {
    }
    fputs("KrTrace.printMessage(", f);
    show_filename_and_lineno(tc);
    fputs(" + ", f);
  }
}



/**	Begin a debug output block for C output.
	@param	tc	Tracecc job.
*/
static
void
begin_c_trace DK_P1(tracecc_t *,tc)
{
  FILE *f;
  
  f = tc->outfile;
  fputs("/* TRACE BEGIN */\n", f);
  indent_this_line(tc);
  if(is_show_timestamp(tc)) {
    if(is_debst(tc)) {
      fputs(str_c_fct_stdout_time, f);
    } else {
      fputs(str_c_fct_time, f);
    }
    fputs("();\n", f);
    indent_this_line(tc);
  }
  /* start_c_line(tc); */
  /* show_filename_and_lineno(tc); */
  /* fputc(' ', f); */
  /* end_c_line(tc); */
  if(!is_debst(tc)) {
    fputs("{ if(", f);
    fputs(str_c_fct_get_file, f);
    fputs("()) ", f);
  }
  fputs("{ fputs(", f);
  show_filename_and_lineno(tc);
  fputs(", ", f);
  if(is_debst(tc)) {
    fputs("stdout", f);
  } else {
    fputs(str_c_fct_get_file, f);
    fputs("()", f);
  }
  fputs("); }", f);
  if(!is_debst(tc)) {
    fputs(" }", f);
  }
  nl(tc);
  
}



/**	Begin a debug output block.
	@param	tc	Tracecc job.
*/
static
void
begin_trace_block DK_P1(tracecc_t *,tc)
{
  
  if(is_debug(tc)) {
    switch(tc->newft) {
      case 2: {
        begin_java_trace(tc);
      } break;
      default: {
        begin_c_trace(tc);
      } break;
    }
  }
  
}



/**	Begin debug message for C output.
	@param	tc	Tracecc job.
*/
static
void
begin_c_trace_contents DK_P1(tracecc_t *,tc)
{
  
  indent_this_line(tc);
  start_c_line(tc);
  print_current_char(tc);
  
}



/**	Begin debug message for Java output.
	@param	tc	Tracecc job.
*/
static
void
begin_java_trace_contents DK_P1(tracecc_t *,tc)
{
  print_current_char(tc);
}



/**	Begin debug message.
	@param	tc	Tracecc job.
*/
static
void
begin_trace_contents DK_P1(tracecc_t *,tc)
{
  
  if(is_debug(tc)) {
    switch(tc->newft) {
      case 2: {
        begin_java_trace_contents(tc);
      } break;
      default: {
        begin_c_trace_contents(tc);
      } break;
    }
  }
}



/**	End debug message for C output.
	@param	tc	Tracecc job.
*/
static
void
end_c_trace_contents DK_P1(tracecc_t *,tc)
{
  FILE *f;
  
  f = tc->outfile;
  end_c_line(tc);
  indent_this_line(tc);

  /*
  start_c_line(tc);
  fputs("\"\\n\"", f);
  end_c_line(tc);
  */

  if(!is_debst(tc)) {
    fputs("{ if(", f);
    fputs(str_c_fct_get_file,f);
    fputs("()) ", f);
  }
  fputs("{ fputc('\\n', ", f);
  if(is_debst(tc)) {
    fputs("stdout", f);
  } else {
    fputs(str_c_fct_get_file,f);
    fputs("()", f);
  }
  fputs("); }", f);
  if(!is_debst(tc)) {
    fputs(" }", f);
  }
  fputc('\n', f);
  indent_this_line(tc);
  if(is_debst(tc)) {
    fputs("fflush(stdout);\n", f);
  } else {
    fputs(
      "{ if(dktrace_file()) { fflush(dktrace_file()); } }\n",
      f
    );
  }
  indent_this_line(tc);
  fputs("/* TRACE END */", f);
  if(need_source_position(tc)) {
    nl(tc);
    print_source_position(tc, 0);
  }
  
}



/**	End debug message for Java output.
	@param	tc	Tracecc job.
*/
static
void
end_java_trace_contents DK_P1(tracecc_t *,tc)
{
  if(is_debug(tc)) {
    fputs(");", tc->outfile);
  }
}



/**	Finishe debug message.
	@param	tc	Tracecc job.
*/
static
void
end_trace_contents DK_P1(tracecc_t *,tc)
{
  
  if(is_debug(tc)) {
    switch(tc->newft) {
      case 2: {
        end_java_trace_contents(tc);
      } break;
      default: {
        end_c_trace_contents(tc);
      } break;
    }
  }
  
  fputc('\n', tc->outfile);
}



/* ********************************************************************* */
/**	Add one further character to command buffer.
	@param	tc	Tracecc job.
*/
static
int
add_to_interpreter_command DK_P1(tracecc_t *,tc)
{
  int back = 0;
  
  if((tc->icmdu) < ((tc->icmdsz) - 1)) {
    back = 1;
    (tc->icmdbuf)[tc->icmdu] = tc->currentchar;
    tc->icmdu += 1;
  } else {
    if(tc->app && (!(tc->line_too_long_printed))) {
      char *xxxptr[3];
      dkapp_set_source_filename(tc->app, tc->in_short);
      dkapp_set_source_lineno(tc->app, tc->lineno);
      xxxptr[0] = (tc->messages)[26];
      dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
      dkapp_set_source_filename(tc->app, NULL);
      dkapp_set_source_lineno(tc->app, 0UL);
      tc->line_too_long_printed = 1;
    }
  }
  
  return back;
}



/**	Start new command in command buffer.
	@param	tc	Tracecc job.
*/
static
void
begin_interpreter_command DK_P1(tracecc_t *,tc)
{
  
  tc->icmdu = 0;
  (void)add_to_interpreter_command(tc);
  tc->line_too_long_printed = 0;
}



/**	Turn timestamp mode off.
	@param	tc	Tracecc job.
	@param	args	Command arguments (ignored).
	@return	1 on success, 0 on error.
*/
static
int
fct_timestamp_off DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  (tc->f).o &= (~(TRACECC_OPT_DATETIME));
  return back;
}



/**	Turn timestamp mode on.
	@param	tc	Tracecc job.
	@param	args	Command arguments (ignored).
	@return	1 on success, 0 on error.
*/
static
int
fct_timestamp_on DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  (tc->f).o |= TRACECC_OPT_DATETIME;
  return back;
}



/**	Turn line number mode off.
	@param	tc	Tracecc job.
	@param	args	Command arguments (ignored).
	@return	1 on success, 0 on error.
*/
static
int
fct_lineno_off DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  (tc->f).o &= (~(TRACECC_OPT_LINENO));
  return back;
}



/**	Turn line number mode on.
	@param	tc	Tracecc command.
	@param	args	Command arguments (ignored).
	@return	1 on success, 0 on error.
*/
static
int
fct_lineno_on DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  if(((tc->o).o) & TRACECC_OPT_LINENO) {
    (tc->f).o |= TRACECC_OPT_LINENO;
  }
  return back;
}



/**	Turn debug mode off.
	@param	tc	Tracecc command.
	@param	args	Command arguments (ignored).
	@return	1 on success, 0 on error.
*/
static
int
fct_trace_off DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  (tc->f).o &= (~(TRACECC_OPT_DEBUG));
  return back;
}



/**	Turn debug mode on.
	@param	tc	Tracecc job.
	@param	args	Parameters (unused).
	@return	1 on success, 0 on error.
*/
static
int
fct_trace_on DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  if(((tc->o).o) & TRACECC_OPT_DEBUG) {
    (tc->f).o |= TRACECC_OPT_DEBUG;
  }
  return back;
}



/**	Read output of a command and write it to output file
	@param	tc	Tracecc job.
	@param	args	Command to execute.
	@return	1 on success, 0 on error.
*/
static
int
fct_trace_pipe DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 0;
  FILE *f;
  char smallbuffer[128], *largebuffer, *tb;
  size_t bs, u;
  int cc;
  f = NULL;
  if(args) {
#if DK_HAVE_POPEN || DK_HAVE__POPEN
#if DK_HAVE_POPEN
    f = popen(args, str_r);
#else
    f = _popen(args, str_r);
#endif
    if(f) {
      back = 1;
      largebuffer = dk_new(char,4096);
      if(largebuffer) {
        tb = largebuffer; bs = 4096;
      } else {
        tb = smallbuffer; bs = sizeof(smallbuffer);
	/* XXXXX WARNING: Only small buffer */
        if(tc->app) {
          char *xxxptr[8];
	  dkapp_set_source_filename(tc->app, tc->in_short);
	  dkapp_set_source_lineno(tc->app, tc->lineno);
          xxxptr[0] = (tc->messages)[0];
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
	  dkapp_set_source_filename(tc->app, NULL);
	  dkapp_set_source_lineno(tc->app, 0UL);
        }
      }
      cc = 1;
      while(cc) {
        u = fread(tb,1,bs,f);
	if(u > 0) {
	  (void)fwrite(tb,1,u,tc->outfile);
	} else {
	  cc = 0;
	}
      }
      if(largebuffer) {
        dk_delete(largebuffer);
      }
#if DK_HAVE_POPEN
      if(pclose(f) != 0) { back = 0; }
#else
      if(_pclose(f) != 0) { back = 0; }
#endif
    } else {
      /* XXXXX ERROR: Failed to execute command */
      if(tc->app) {
        char *xxxptr[8];
        xxxptr[0] = (tc->messages)[1];
        xxxptr[1] = str_spc;
        xxxptr[2] = str_bropen;
        xxxptr[3] = args;
        xxxptr[4] = str_brclose;
	dkapp_set_source_filename(tc->app, tc->in_short);
	dkapp_set_source_lineno(tc->app, tc->lineno);
        dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
	dkapp_set_source_filename(tc->app, NULL);
	dkapp_set_source_lineno(tc->app, 0UL);
      }
    }
#else
    /* XXXXX ERROR: No popen function on this system! */
    if(tc->app) {
      char *xxxptr[8];
      xxxptr[0] = (tc->messages)[2];
      dkapp_set_source_filename(tc->app, tc->in_short);
      dkapp_set_source_lineno(tc->app, tc->lineno);
      dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
      dkapp_set_source_filename(tc->app, NULL);
      dkapp_set_source_lineno(tc->app, 0UL);
    }
#endif
  } else {
    /* XXXXXX ERROR: Missing arguments */
    if(tc->app) {
      char *xxxptr[8];
      xxxptr[0] = (tc->messages)[3];
      dkapp_set_source_filename(tc->app, tc->in_short);
      dkapp_set_source_lineno(tc->app, tc->lineno);
      dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
      dkapp_set_source_filename(tc->app, NULL);
      dkapp_set_source_lineno(tc->app, 0UL);
    }
  }
  return back;
}



/**	Write trace code.
	@param	tc	Tracecc command.
	@param	args	Command to write.
	@return	1 on success, 0 on error.
*/
static
int
fct_trace_code DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  if(is_debug(tc)) {
    if(args) {
      fputs(args, tc->outfile);
    }
  }
  return back;
}



/**	put a character encoded to file (in a string).
	@param	c	Character to write.
	@param	f	Output file.
	@param	ft	File type.
*/
static
void
encoded_fputc DK_P3(char,c,FILE *,f,int,ft)
{
  switch(c) {
    case '\n': {
      switch(ft) {
        case 2: {
	  fputs("\",\n\"", f);
	} break;
	case 1:
	default: {
	  fputs("\",\n(char *)\"", f);
	} break;
      }
    } break;
    case '\t': {
      fputs("\\t", f);
    } break;
    case '"' : {
      fputs("\\\"", f);
    } break;
    default: {
      fputc(c,f);
    } break;
  }
}



/**	Write a byte in hexadecimal notation.
	@param	c	Byte to write.
	@param	f	Output file.
*/
static
void
hex_fputc DK_P2(char,c,FILE *,f)
{
  unsigned char uc, uc1;
  unsigned u;
  uc = c;
  fputs("0x", f);
  uc1 = ((uc >> 4) & 0x0F); u = uc1;
  fputc(hexdigits[u], f);
  uc1 = (uc & 0x0F); u = uc1;
  fputc(hexdigits[u], f);
}



/**	Read file to array of bytes.
	@param	tc	Tracecc job.
	@param	args	File name.
	@return	1 on success, 0 on error.
*/
static
int
fct_byte_file DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 0;
  int r, cc;
  char smallbuffer[128], *largebuffer, *buffer, *ptr1;
  size_t bs, used, i;
  FILE *inputfile, *f;
  unsigned long bytesused;

  r = 0;
  f = tc->outfile; bytesused = 0UL;
  ptr1 = dkstr_start(args, NULL);
  if(ptr1) {
    dkstr_chomp(ptr1, NULL);
    if(tc->app) {
      inputfile = dkapp_fopen(tc->app, ptr1, str_r);
    } else {
      inputfile = dksf_msfo(ptr1, str_r, 0, &r);
    }
    if(inputfile) {
      buffer = smallbuffer; bs = sizeof(smallbuffer);
      largebuffer = dk_new(char, 4096);
      if(largebuffer) {
        buffer = largebuffer; bs = 4096;
      } else {
        /* XXXXX Warning for small buffer */
        if(tc->app) {
          char *xxxptr[8];
	  dkapp_set_source_filename(tc->app, tc->in_short);
	  dkapp_set_source_lineno(tc->app, tc->lineno);
          xxxptr[0] = (tc->messages)[0];
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
	  dkapp_set_source_filename(tc->app, NULL);
	  dkapp_set_source_lineno(tc->app, 0UL);
        }
      }
      switch(tc->newft) {
        case 2:
	case 1:
	default: {
	  /* transfer file contents */
	  cc = 1;
	  while(cc) {
	    used = fread(buffer,1,bs,inputfile);
	    if(used > 0) {
	      for(i = 0; i < used; i++) {
	        if(bytesused) {
		  fputc(',', f);
		  if(!(bytesused % 12)) {
		    fputc('\n', f);
		  }
		}
	        hex_fputc(buffer[i], f);
		bytesused++;
	      }
	    } else {
	      cc = 0;
	    }
	  }
	  if(bytesused) {
	    fputc('\n', f);
	  }
	} break;
      }
      if(largebuffer) {
        dk_delete(largebuffer);
	largebuffer = NULL;
      }
      fclose(inputfile);
      back = 1;
    } else {
      /* ERROR: Failed to read file */
    }
  }
  return back;
}



/**	Read a string file.
	@param	tc	Tracecc job.
	@param	args	File name.
	@return	1 on success, 0 on error.
*/
static
int
fct_string_file DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 0;
  int r, cc;
  char smallbuffer[128], *largebuffer, *buffer, *ptr1, *ptr2;
  size_t bs, used, i;
  FILE *inputfile, *f;

  r = 0;
  f = tc->outfile;
  ptr1 = dkstr_start(args, NULL);
  if(ptr1) {
    dkstr_chomp(ptr1, NULL);
    if(tc->app) {
      inputfile = dkapp_fopen(tc->app, ptr1, str_r);
    } else {
      inputfile = dksf_msfo(ptr1, str_r, 0, &r);
    }
    if(inputfile) {
      buffer = smallbuffer; bs = sizeof(smallbuffer);
      largebuffer = dk_new(char, 4096);
      if(largebuffer) {
        buffer = largebuffer; bs = 4096;
      } else {
        /* XXXXX Warning for small buffer */
        if(tc->app) {
          char *xxxptr[8];
	  dkapp_set_source_filename(tc->app, tc->in_short);
	  dkapp_set_source_lineno(tc->app, tc->lineno);
          xxxptr[0] = (tc->messages)[0];
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
	  dkapp_set_source_filename(tc->app, NULL);
	  dkapp_set_source_lineno(tc->app, 0UL);
        }
      }
      switch(tc->newft) {
        case 2: {
	} break;
	case 1:
	default: {
	  /* show file position if necessary */
	  if(need_source_position(tc)) {
	    nl(tc);
	    fputs("#line 1 \"", f);
	    ptr2 = ptr1;
	    while(*ptr2) {
	      if(*ptr2 == '\\') {
	        fputc('/', f);
	      } else {
	        fputc(*ptr2, f);
	      }
	      ptr2++;
	    }
	    fputs("\"\n", f);
	  }
	  /* transfer file contents */
	  cc = 1;
	  fputs("(char *)\"", f);
	  while(cc) {
	    used = fread(buffer,1,bs,inputfile);
	    if(used > 0) {
	      for(i = 0; i < used; i++) {
	        encoded_fputc(buffer[i], f, tc->newft);
	      }
	    } else {
	      cc = 0;
	    }
	  }
	  fputs("\",\nNULL\n", f);
	} break;
      }
      if(largebuffer) {
        dk_delete(largebuffer);
	largebuffer = NULL;
      }
      fclose(inputfile);
      back = 1;
    } else {
      /* ERROR: Failed to read file */
    }
  }
  return back;
}



/**	Process $(trace-init xxx) command.
	@param	tc	Tracecc job.
	@param	args	Command arguments (file name).
	@return	1 on success, 0 on error.
*/
static
int
fct_trace_init DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 1;
  char *ptr;
  FILE *f;
  
  f = tc->outfile;
  switch(tc->newft) {
    case 2: {
      if(is_debug(tc)) {
        if(is_debst(tc)) {
	  fputs("KrTrace.start();", tc->outfile);
	} else {
	  ptr = dkstr_start(args, NULL);
	  if(ptr) {
	    (void)dkstr_next(ptr,NULL);
	    fputs("KrTrace.start(\"", tc->outfile);
	    fputs(ptr, tc->outfile);
	    fputs("\");", tc->outfile);
	  } else {
            if(tc->app) {
              char *xxxptr[8];
              xxxptr[0] = (tc->messages)[3];
              dkapp_set_source_filename(tc->app, tc->in_short);
              dkapp_set_source_lineno(tc->app, tc->lineno);
              dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
              dkapp_set_source_filename(tc->app, NULL);
              dkapp_set_source_lineno(tc->app, 0UL);
            }
	  }
	}
      }
    } break;
    case 1:
    default: {
      if(is_debug(tc) && (!is_debst(tc))) {
        if(args) {
          ptr = dkstr_start(args, NULL);
          if(ptr) {
            (void)dkstr_next(ptr, NULL);
            back = 1;
            fputs(str_c_fct_init, f);
            fputs("(\"", f);
            fputs(ptr, f);
            fputs("\");", f);
          }
        } else {
          /* XXXXX ERROR: Need argument */
          if(tc->app) {
            char *xxxptr[8];
            xxxptr[0] = (tc->messages)[3];
            dkapp_set_source_filename(tc->app, tc->in_short);
            dkapp_set_source_lineno(tc->app, tc->lineno);
            dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
            dkapp_set_source_filename(tc->app, NULL);
            dkapp_set_source_lineno(tc->app, 0UL);
          }
        }
      } else {
        back = 1;
      }
    } break;
  }
  return back;
}



/**	Process $(trace-end).
	@param	tc	Tracecc job.
	@param	args	Command arguments (ignored).
	@return	1 on success, 0 on error.
*/
static
int
fct_trace_end DK_P2(tracecc_t *,tc,char *,args)
{
  FILE *f;
  f = tc->outfile;
  switch(tc->newft) {
    case 2: {
      if(is_debug(tc)) {
        fputs("KrTrace.stop();", tc->outfile);
      }
    } break;
    case 1:
    default: {
      if(is_debug(tc) && (!is_debst(tc))) {
        fputs(str_c_fct_end, f);
        fputs("();", f);
      }
    } break;
  }
  return 1;
}



static
int
fct_string_array DK_P2(tracecc_t *,tc,char *,args)
{
  int back = 0;
  char *macroname = NULL;
  char *prefixname = NULL;
  char *filename = NULL;
  char *shortfilename = NULL;
  char *p1, *p2, *p3;
  char il[1024];
  unsigned long entryno = 0UL;
  unsigned long lineno = 0UL;
  int is_first = 1;
  int state = 0;
  FILE *fipo = NULL;
  if(args) {
    p1 = dkstr_start(args, NULL);
    while(p1) {
      p2 = dkstr_chr(p1, ',');
      if(p2) {
        *(p2++) = '\0';
	p2 = dkstr_start(p2, NULL);
      }
      dkstr_chomp(p1, NULL);
      p3 = dkstr_chr(p1, '=');
      if(p3) {
        *(p3++) = '\0'; p3 = dkstr_start(p3,NULL); dkstr_chomp(p1,NULL);
	if(dkstr_casecmp(p1, string_file_keywords[0]) == 0) {
	  if(macroname) {
	    /* ##### ERROR: Only one macro name allowed. */
	  }
	  macroname = p3;
	} else {
	  if(dkstr_casecmp(p1, string_file_keywords[1]) == 0) {
	    if(prefixname) {
	      /* ##### ERROR: Only one prefix name allowed. */
	    }
	    prefixname = p3;
	  }
	}
      } else {
        if(filename == NULL) { filename = p1; }
	else {
	  /* ##### ERROR: Too many file names. */
	}
      }
      dkstr_chomp(p1, NULL);
      if(filename) {
        dksf_correct_fnsep(filename);
	shortfilename = dksf_get_last_filename(filename);
	if(!(shortfilename)) { shortfilename = filename; }
        fipo = fopen(filename, "r");
	if(fipo) {
	  while(fgets(il, sizeof(il), fipo)) {
	    lineno++;
	    if(il[0] != '#') {
	      if(!is_first) {
	        fputc(',', tc->outfile); fputs("\n\n", tc->outfile);
	      } is_first = 0;
	      fprintf(tc->outfile, "/* %5lu */\n", entryno);
	      if(need_source_position(tc)) {
	        fprintf(
		  tc->outfile,
		  "#line %lu \"%s\"\n", lineno, shortfilename
		);
	      }
	      if(macroname) {
	        fputs(macroname, tc->outfile); fputc('(', tc->outfile);
	      } else {
	        if(prefixname) { fputs(prefixname, tc->outfile); }
	      }
	      fputc('"', tc->outfile);
	      p1 = il; state = 0;
	      while((*p1 != '\n') && (*p1)) {
	        switch(state) {
		  case 1: {
		    switch(*p1) {
		      case '#': {
		        fputc('#', tc->outfile);
		      } break;
		      default: {
		        fputc('\\', tc->outfile);
			fputc(*p1, tc->outfile);
		      } break;
		    }
		    state = 0;
		  } break;
		  default: {
		    switch(*p1) {
		      case '\\': {
		        state = 1;
		      } break;
		      case '"': {
		        fputc('\\', tc->outfile);
			fputc('"', tc->outfile);
		      } break;
		      case '\t': {
		        fputc('\\', tc->outfile); fputc('t', tc->outfile);
		      } break;
		      default: {
		        fputc(*p1, tc->outfile);
		      } break;
		    }
		  } break;
		}
	        p1++;
	      }
	      fputc('"', tc->outfile);
	      if(macroname) {
	        fputc(')', tc->outfile);
	      }
	      entryno++;
	    }
	  }
	  if(!is_first) { fputc('\n', tc->outfile); }
	  fclose(fipo);
	} else {
	  /* ##### ERROR: Failed to open file. */
	}
      } else {
        /* ##### ERROR: No file name specified. */
      }
      p1 = p2;
    }
  }
  return back;
}


/**	Add dktrace.h include file.
	@param	tc	Tracecc job.
	@param	args	Command arguments (ignored).
	@return	1 on success, 0 on error.
*/
static
int
fct_trace_include DK_P2(tracecc_t *,tc,char *,args)
{
  if(is_debug(tc)) {
    switch(tc->newft) {
      case 2: {
        fputs("import KrTrace;\n", tc->outfile);
      } break;
      case 1:
      default: {
        fputs("#include <dktrace.h>\n", tc->outfile);
      } break;
    } 
  }
  return 1;
}



/**	Mapping of command names to functions.
*/
typedef struct {
  char  *name;		/**< Command name. */
  IFCT	*fct;		/**< Function to execute. */
  int    need_lineno;	/**< Flag: Must print file name and line. */
} function_info_t ;

/**	Map command names to functions.
*/
static function_info_t fctlist[] = {
  { (char *)"trace-init",    fct_trace_init,    0 },
  { (char *)"trace-end",     fct_trace_end,     0 },
  { (char *)"trace-include", fct_trace_include, 0 },
  { (char *)"trace-code",    fct_trace_code,    0 },
  { (char *)"!",             fct_trace_code,    0 },
  { (char *)"trace-on",      fct_trace_on,      0 },
  { (char *)"trace-off",     fct_trace_off,     0 },
  { (char *)"debug-on",      fct_trace_on,      0 },
  { (char *)"debug-off",     fct_trace_off,     0 },
  { (char *)"timestamp-on",  fct_timestamp_on,  0 },
  { (char *)"timestamp-off", fct_timestamp_off, 0 },
  { (char *)"lineno-on",     fct_lineno_on,     0 },
  { (char *)"lineno-off",    fct_lineno_off,    0 },
  { (char *)"pipe",          fct_trace_pipe,    1 },
  { (char *)"|",             fct_trace_pipe,    1 },
  { (char *)"string-file",   fct_string_file,   1 },
  { (char *)"byte-file",     fct_byte_file,     1 },
  { (char *)"string-array",  fct_string_array,	1 },
  { NULL, NULL, 0 }
};



/**	Run command saved in command line buffer.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
i_run_interpreter_command DK_P1(tracecc_t *,tc)
{
  int back = 0;
  char *ptr, *args;
  function_info_t *fctptr, *fctlfd;
  i_fct_t fct;

  
  if((tc->icmdu) < (tc->icmdsz)) {
    (tc->icmdbuf)[tc->icmdu] = '\0';
    ptr = dkstr_start(tc->icmdbuf, NULL);
    if(ptr) {
      dkstr_chomp(ptr, NULL);
      args = dkstr_next(ptr, NULL);
      fctptr = NULL; fctlfd = fctlist;
      while((fctlfd->name) && (!fctptr)) {
        if(strcmp(fctlfd->name, ptr) == 0) {
	  fctptr = fctlfd;
	} else {
	  fctlfd++;
	}
      }
      if(fctptr) {
        fct = fctptr->fct;
	back = (*fct)(tc, args);
      } else {
        /* XXXXX ERROR: Function not found */
        if(tc->app) {
          char *xxxptr[8];
          xxxptr[0] = (tc->messages)[5];
          xxxptr[1] = str_spc;
          xxxptr[2] = str_bropen;
          xxxptr[3] = ptr;
          xxxptr[4] = str_brclose;
	  dkapp_set_source_filename(tc->app, tc->in_short);
	  dkapp_set_source_lineno(tc->app, tc->lineno);
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
	  dkapp_set_source_filename(tc->app, NULL);
	  dkapp_set_source_lineno(tc->app, 0UL);
        }
      }
    } else {
      /* XXXXX ERROR: Empty function name */
      if(tc->app) {
        char *xxxptr[8];
        xxxptr[0] = (tc->messages)[6];
	dkapp_set_source_filename(tc->app, tc->in_short);
	dkapp_set_source_lineno(tc->app, tc->lineno);
        dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 1);
	dkapp_set_source_filename(tc->app, NULL);
	dkapp_set_source_lineno(tc->app, 0UL);
      }
    }
  }
  return back;
}



/**	Run command.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_interpreter_command DK_P1(tracecc_t *,tc)
{
  int back;
  back = i_run_interpreter_command(tc);
  if(need_source_position(tc)) {
    nl(tc);
    print_source_position(tc, 0);
  }
  return back;
}



/**	Run command and print newline to source file.
	The $!... commands consume a newline, so we add one explicitly.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_interpreter_command_nl DK_P1(tracecc_t *,tc)
{
  int back;
  back = i_run_interpreter_command(tc);
  fputc('\n', tc->outfile);
  if(need_source_position(tc)) {
    print_source_position(tc, 1);
  }
  return back;
}



/**	Start of new comment box.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
open_comment_box_file DK_P1(tracecc_t *,tc)
{
  int back = 0;
  
  if(tc->tmpfile) {
    fclose(tc->tmpfile);
  }
  tc->tmpfile = fopen(tc->tmp_name_buf, "w+");
  if(tc->tmpfile) {
    back = 1;
  }
  tc->cmbll = tc->cmbmax = 0;
  
  return back;
}



/**	Add newline to comment box data file.
	@param	tc	Tracecc job.
*/
static
void
add_newline_to_comment_box DK_P1(tracecc_t *,tc)
{
  
  if(tc->tmpfile) {
    fputc('\n', tc->tmpfile);
    tc->cmbll = 0;
  }
  
}



/**	Add character to comment box data file.
	@param	tc	Tracecc job.
*/
static
void
add_char_to_comment_box DK_P1(tracecc_t *,tc)
{
  
  if(tc->tmpfile) {
    fputc(tc->currentchar, tc->tmpfile);
  }
  tc->cmbll += 1;
  if((tc->cmbll) > (tc->cmbmax)) {
    tc->cmbmax = tc->cmbll;
  }
  
}



/**	Add control character and current character to comment box data file.
	@param	tc	Tracecc job.
*/
static
void
add_control_and_char_to_comment_box DK_P1(tracecc_t *,tc)
{
  
  if(tc->tmpfile) {
    switch(tc->newft) {
      case 2:
      case 1:
      default: {
	fputc('$', tc->tmpfile);
      } break;
    }
    fputc(tc->currentchar, tc->tmpfile);
  }
  tc->cmbll += 2;
  if((tc->cmbll) > (tc->cmbmax)) { tc->cmbmax = tc->cmbll; }
  
}



/**	Start comment box line.
	@param	f	Output file.
*/
static
void
start_line DK_P1(FILE *,f)
{
  fputs("/* * ", f);
}



/**	End comment box line.
	@param	f	Output file.
*/
static
void
end_line DK_P1(FILE *,f)
{
  fputs(" * */", f);
  fputc('\n', f);
}



/**	Start/end line of a box.
	@param	f	Output file.
	@param	mll	Maximum text line length.
*/
static
void
box_line DK_P2(FILE *,f,int,mll)
{
  int i, mx;
  mx = mll + 2;
  fputs("/* *", f);
  for(i = 0; i < mx; i++) { fputc('*', f); }
  fputs("* */", f);
  fputc('\n', f);
}



/**	Transfer comment box data file to destination file.
	@param	tc	Tracecc job.
*/
static
void
print_comment_box DK_P1(tracecc_t *,tc)
{
#if FIRST_ATTEMPT_FOR_COMMENT_BOX
  int maxval, cc;
  char buffer[128]; size_t max, u;
  FILE *f;
  int ll;
  f = tc->outfile;
  if(tc->tmpfile) {
    fflush(tc->tmpfile);
    rewind(tc->tmpfile);
    clearerr(tc->tmpfile);
    fputs("/*\n", f);
    cc = 1;
    while(cc) {
      max = fread(buffer,1,sizeof(buffer),tc->tmpfile);
      if(max > 0) {
        for(u = 0; u < max; u++) {
	  fputc(buffer[u], f);
	}
      } else {
        cc = 0;
      }
    }
    fputs("*/\n", f);
    fclose(tc->tmpfile); tc->tmpfile = NULL;
    dksf_remove_file(tc->tmp_name_buf);
  }
#else
  char smallbuffer[128], *largebuffer, *buffer;
  size_t bufsize, max, u;
  int mll, curpos, cc;
  FILE *fi, *fo;
  
  fi = tc->tmpfile;
  fo = tc->outfile;
  if(tc->tmpfile) {
    largebuffer = dk_new(char,4096);
    if(largebuffer) {
      buffer = largebuffer; bufsize = 4096;
    } else {
      buffer = smallbuffer; bufsize = sizeof(smallbuffer);
    }
    mll = tc->cmbmax;
    if((tc->cmbmax) < ((tc->f).b - 10)) {
      mll = (tc->f).b - 10;
    }
    curpos = 0;
    fflush(fi);
    rewind(fi);
    clearerr(fi);
    fputc('\n', fo);
    /* starting box line */
    box_line(fo, mll);
    start_line(fo);
    cc = 1;
    while(cc) {
      max = fread(buffer, 1, bufsize, fi);
      if(max > 0) {
        for(u = 0; u < max; u++) {
	  /* buffer[u] verarbeiten */
	  switch(buffer[u]) {
	    case '\n' : {
	      while(curpos++ < mll) { fputc(' ', fo); }
	      end_line(fo);
	      start_line(fo);
	      curpos = 0;
	    } break;
	    case '\t' : {
	      fputc(' ', fo); curpos++;
	      while(curpos % 8) {
	        fputc(' ', fo); curpos++;
	      }
	    } break;
	    default : {
	      fputc(buffer[u], fo);
	      curpos++;
	    } break;
	  }
	}
      } else {
        cc = 0;
      }
    }
    while(curpos++ < mll) { fputc(' ', fo); }
    end_line(fo);
    /* finishing box line */
    box_line(fo, mll);
    if(largebuffer) {
      dk_delete(largebuffer);
    }
  }
  if(need_source_position(tc)) {
    nl(tc);
    print_source_position(tc, 0);
  }
#endif
}



/**	Start C++ style comment.
	@param	tc	Tracecc job.
*/
static
void
start_line_comment DK_P1(tracecc_t *,tc)
{
  FILE *f;
  
  f = tc->outfile;
  switch(tc->newft) {
    case 2: {
      fputs("//", f);
    } break;
    default: {
      if(have_cpp_comment(tc)) {
	fputs("//", f);
      } else {
	fputs("/*", f);
      }
    } break;
  }
}



/**	Finish C++ style comment.
	@param	tc	Tracecc job.
*/
static
void
end_line_comment DK_P1(tracecc_t *,tc)
{
  FILE *f;
  
  f = tc->outfile;
  switch(tc->newft) {
    case 2: {
      fputc('\n', f);
    } break;
    default: {
      if(have_cpp_comment(tc)) {
	fputc('\n', f);
      } else {
	fputs(" */\n", f);
      }
    } break;
  }
}



/**	Start a normal comment.
	@param	tc	Tracecc job.
*/
static
void
start_normal_comment DK_P1(tracecc_t *,tc)
{
  fputc('/', tc->outfile);
  print_current_char(tc);
}



/**	Perform the action for the character.
	@param	tc	Tracecc job.
	@return	1 on 
*/
static
int
perform_action DK_P1(tracecc_t *,tc)
{
  int back = 0;
  
  switch(tc->reaction) {
    case  0: { back = 1; /* important to set back */ } break;
    case  1: { print_current_char(tc); back = 1; } break;
    case  2: { tc_and_current_char(tc); back = 1; } break;
    case  3: { begin_trace_block(tc); back = 1; } break;
    case  4: { begin_trace_contents(tc); back = 1; } break;
    case  5: { end_trace_contents(tc); back = 1; } break;
    case  6: { begin_interpreter_command(tc); back = 1; } break;
    case  7: { back = add_to_interpreter_command(tc); } break;
    case  8: { back = run_interpreter_command(tc); } break;
    case  9: { print_debug_char(tc); back = 1; } break;
    case 10: { end_trace_contents(tc); back = 1; } break;
    case 11: { back = open_comment_box_file(tc); } break;
    case 12: { add_newline_to_comment_box(tc); back = 1; } break;
    case 13: { add_char_to_comment_box(tc); back = 1; } break;
    case 14: { add_control_and_char_to_comment_box(tc); back = 1; } break;
    case 15: { print_comment_box(tc); back = 1; } break;
    case 16: { start_line_comment(tc); back = 1; } break;
    case 17: { end_line_comment(tc); back = 1; } break;
    case 18: { start_normal_comment(tc); back = 1; } break;
    case 19: { back = run_interpreter_command_nl(tc); } break;
  }
  return back;
}



/**	Add one character to tracecc job.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
add_char DK_P1(tracecc_t *,tc)
{
  int back = 0;
  int auin;
  
  auin =
  (is_trace_control(tc->newft,tc->currentchar)?TRACE_CONTROL:(tc->currentchar));
  tc->reaction = automata_reaction(&(tc->state), auin);
  back = perform_action(tc);
  
  return back;
}



/**	Process one input character.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
process_char DK_P1(tracecc_t *,tc)
{
  int back = 0;
  
  if(!((tc->textflags) & 1)) {
    if((tc->currentchar == ' ') || (tc->currentchar == '\t')) {
      tc->indent += 1;
      if(tc->currentchar == '\t') {
        while((tc->indent) % 8) {
	  tc->indent += 1;
	}
      }
    } else {
      tc->textflags |= 1;
    }
  }
  back = add_char(tc);
  if((tc->currentchar) == '\n') {
    tc->lineno += 1UL;
    tc->textflags &= (~1);
    tc->indent = 0;
  }
  return back;
}



/**	Run for opened files.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_opened_files DK_P1(tracecc_t *,tc)
{
  int back = 1; int cc;
  
  cc = 1;
  tc->lineno = 1UL;
  tc->indent = 0;
  tc->textflags = 0;

/* + 2004/09/09 */
  tc->cmbll = tc->cmbmax = 0;
  tc->icmdu = 0;
  tc->state = tc->reaction = tc->indent = tc->textflags = 0;
  tc->currentchar = '\0';
  tc->ibu = tc->ibc = 0;
/* - 2004/09/09 */

/* + 2005/03/21, trace-on and traceoff settings valid only until
   end of file */
  (tc->f).o = (tc->o).o;
  (tc->f).b = (tc->o).b;
  (tc->f).i = (tc->o).i;
/* - 2005/03/21 */

  while(cc) {
    tc->ibu = fread((void *)(tc->inbuffer), 1, tc->ibl, tc->infile);
    if(tc->ibu) {
      tc->ibc = 0;
      while((tc->ibc) < (tc->ibu)) {
        tc->currentchar = (tc->inbuffer)[tc->ibc];
        if(!process_char(tc)) {
	  back = 0;
	}
	tc->ibc += 1;
      }
    } else {
      cc = 0;
    }
  }
  return back;
}



/**	Check whether or not to rebuild destination file.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
must_run_for_these_files DK_P1(tracecc_t *,tc)
{
  int back = 1;
  if(((tc->o).o) & TRACECC_OPT_MAKE) {
    back = dksf_must_rebuild(tc->out_real, tc->in_real);
  }
  return back;
}



/**	Run for exact file names.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_real_in_and_real_out DK_P1(tracecc_t *,tc)
{
  int back = 0;
  int reason;
  char *ptr;
  
  tc->newft = 0;
  ptr = dksf_get_file_type_dot(tc->out_real);
  if(ptr) { tc->newft = get_new_file_type(ptr); }
  tc->in_short = NULL;
  ptr = tc->in_real;
  while(*ptr) {
    if(*ptr == fnsep[0]) tc->in_short = ptr;
    ptr++;
  }
  if(tc->in_short) {
    ptr = tc->in_short; ptr++; tc->in_short = ptr;
  } else {
    tc->in_short = tc->in_real;
  }
  if(must_run_for_these_files(tc)) {
    if(tc->app) {
      tc->infile = dkapp_fopen(tc->app, tc->in_real, str_r);
      if(tc->infile) {
        tc->outfile = dkapp_fopen(tc->app, tc->out_real, str_w);
        if(tc->outfile) {
          if(tc->app) {
            char *xxxptr[8];
            xxxptr[0] = (tc->messages)[16];
            xxxptr[1] = tc->in_short;
            xxxptr[2] = (tc->messages)[17];
            dkapp_log_msg(tc->app, DK_LOG_LEVEL_PROGRESS, xxxptr, 3);
          }
          back = run_for_opened_files(tc);
          if(tc->app) {
            char *xxxptr[8];
            xxxptr[0] = (tc->messages)[18];
            xxxptr[1] = tc->in_short;
            xxxptr[2] = (tc->messages)[19];
            dkapp_log_msg(tc->app, DK_LOG_LEVEL_PROGRESS, xxxptr, 3);
          }
          fclose(tc->outfile); tc->outfile = NULL;
        } else {
          /* XXXXX ERROR: Failed to open output file */
          if(tc->app) {
            char *xxxptr[8];
            xxxptr[0] = (tc->messages)[7];
            xxxptr[1] = str_spc;
            xxxptr[2] = str_bropen;
            xxxptr[3] = tc->out_real;
            xxxptr[4] = str_brclose;
            dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
          }
        }
        fclose(tc->infile); tc->infile = NULL;
      } else {
        /* XXXXX ERROR: Failed to open input file */
        if(tc->app) {
          char *xxxptr[8];
          xxxptr[0] = (tc->messages)[8];
          xxxptr[1] = str_spc;
          xxxptr[2] = str_bropen;
          xxxptr[3] = tc->in_real;
          xxxptr[4] = str_brclose;
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
        }
      }
    } else {
      tc->infile = dksf_msfo(tc->in_real, str_r, 0, &reason);
      if(tc->infile) {
        tc->outfile = dksf_msfo(tc->out_real, str_w, 0, &reason);
        if(tc->outfile) {
          back = run_for_opened_files(tc);
          fclose(tc->outfile); tc->outfile = NULL;
        } else {
          /* XXXXX ERROR: Failed to open output file */
          if(tc->app) {
            char *xxxptr[8];
            xxxptr[0] = (tc->messages)[7];
            xxxptr[1] = str_spc;
            xxxptr[2] = str_bropen;
            xxxptr[3] = tc->in_real;
            xxxptr[4] = str_brclose;
            dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
          }
        }
        fclose(tc->infile); tc->infile = NULL;
      } else {
        /* XXXXX ERROR: Failed to open input file */
        if(tc->app) {
          char *xxxptr[8];
          xxxptr[0] = (tc->messages)[8];
          xxxptr[1] = str_spc;
          xxxptr[2] = str_bropen;
          xxxptr[3] = tc->in_real;
          xxxptr[4] = str_brclose;
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
        }
      }
    }
  } else {
    back = 1; /* if files are up to date, this is success */
    if(tc->app) {
      char *xxxptr[8];
      xxxptr[0] = (tc->messages)[24];
      xxxptr[1] = tc->in_short,
      xxxptr[2] = (tc->messages)[25];
      dkapp_log_msg(tc->app, DK_LOG_LEVEL_PROGRESS, xxxptr, 3);
    }
  }
  tc->in_short = NULL;
  return back;
}



/**	When havin a good output file name inspect input file name for
	wildcards and run.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_in_and_real_out DK_P1(tracecc_t *,tc)
{
  int back = 0;
  
  if(dksf_must_expand_filename(tc->in_ori)) {
    tc->fne_in = dkfne_open(tc->in_ori, 1, 0);
    if(tc->fne_in) {
      if(dkfne_next(tc->fne_in)) {
        tc->in_real = (char *)dkfne_get_fullname(tc->fne_in);	/* QDH */
	back = run_for_real_in_and_real_out(tc);
	tc->in_real = NULL;
	if(dkfne_next(tc->fne_in)) {
	  /* XXXXX WARNING: Multiple input files matching pattern */
          if(tc->app) {
            char *xxxptr[8];
            xxxptr[0] = (tc->messages)[9];
            xxxptr[1] = str_spc;
            xxxptr[2] = str_bropen;
            xxxptr[3] = tc->in_ori;
            xxxptr[4] = str_brclose;
            dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
          }
	}
      } else {
        /* XXXXX ERROR: No matching input file */
        if(tc->app) {
          char *xxxptr[10];
          xxxptr[0] = (tc->messages)[13];
          xxxptr[1] = str_spc;
          xxxptr[2] = str_bropen;
          xxxptr[3] = tc->in_ori;
          xxxptr[4] = str_brclose;
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
        }
      }
      dkfne_close(tc->fne_in); tc->fne_in = NULL;
    } else {
      tc->error_code = DK_ERR_NOMEM;
      if(tc->app) dkapp_err_memory(tc->app,sizeof(dk_fne_t),1);
    }
  } else {
    tc->in_real = tc->in_ori;
    back = run_for_real_in_and_real_out(tc);
    tc->in_real = NULL;
  }
  return back;
}



/**	Check both input and output file name for wildcards an run.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_in_and_out_file_name DK_P1(tracecc_t *,tc)
{
  int back = 0;
  
  if(dksf_must_expand_filename(tc->out_ori)) {
    tc->fne_out = dkfne_open(tc->out_ori,1,0);
    if(tc->fne_out) {
      if(dkfne_next(tc->fne_out)) {
        tc->out_real = (char *)dkfne_get_fullname(tc->fne_out);	/* QDH */
	back = run_for_in_and_real_out(tc);
	tc->out_real = NULL;
	if(dkfne_next(tc->fne_out)) {
	  /* XXXXX WARNING: Multiple file names for pattern */
          if(tc->app) {
            char *xxxptr[8];
            xxxptr[0] = (tc->messages)[11];
            xxxptr[1] = str_spc;
            xxxptr[2] = str_bropen;
            xxxptr[3] = tc->out_ori;
            xxxptr[4] = str_brclose;
            dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
          }
	}
      } else {
        /* XXXXX ERROR: No matching output file name */
        if(tc->app) {
          char *xxxptr[8];
          xxxptr[0] = (tc->messages)[12];
          xxxptr[1] = str_spc;
          xxxptr[2] = str_bropen;
          xxxptr[3] = tc->in_real;
          xxxptr[4] = str_brclose;
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
        }
      }
      dkfne_close(tc->fne_out); tc->fne_out = NULL;
    } else {
      tc->error_code = DK_ERR_NOMEM;
      if(tc->app) dkapp_err_memory(tc->app,sizeof(dk_fne_t),1);
    }
  } else {
    tc->out_real = tc->out_ori;
    back = run_for_in_and_real_out(tc);
    tc->out_real = NULL;
  } 
  return back;
}



/**	Check whether the named file is a directory.
	@param	dn	Directory name.
	@return	1=directory, 0=other file type.
*/
static
int
is_dir DK_P1(char *,dn)
{
  int back = 0;
  dk_stat_t *stptr;
  int ft;
  
  stptr = dkstat_open(dn);
  if(stptr) {
    ft = dkstat_filetype(stptr);
    if((ft & (~DK_FT_SYMLINK)) == DK_FT_DIR) {
      back = 1;
    }
    dkstat_close(stptr);
  } else {
    
  }
  
  return back;
}



/**	Run for a directory, improved version 1.
	@param	tc	Tracecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_real_dir_v1 DK_P1(tracecc_t *,tc)
{
  int back = 0;
  int ft;
  char *suffixptr;
  char  *newsuffix;
  
  if(is_dir(tc->dir_cur)) {
  tc->dir_tra = dkdir_open(tc->dir_cur);
  if(tc->dir_tra) {	
    back = 1;
    while(dkdir_next(tc->dir_tra)) {
      
      ft = dkdir_filetype(tc->dir_tra);
      if((ft & (~DK_FT_SYMLINK)) == DK_FT_REG) {
        
	if(strcmp(dkdir_get_shortname(tc->dir_tra), ".")) {
	if(strcmp(dkdir_get_shortname(tc->dir_tra), "..")) {
	  tc->in_real = (char *)dkdir_get_fullname(tc->dir_tra); /* QDH */
	  tc->out_real = tc->out_name_buf;
	  suffixptr = dksf_get_file_type_dot(tc->in_real);
	  if(suffixptr) {
	    newsuffix = get_new_suffix(suffixptr);
	    if(newsuffix) {
if((size_t)(tc->mpl) > (strlen(tc->in_real) - strlen(suffixptr) + strlen(newsuffix))) {
  strcpy(tc->out_real, tc->in_real);
  suffixptr = dksf_get_file_type_dot(tc->out_real);
  if(suffixptr) {
    strcpy(suffixptr, newsuffix);
    if(!run_for_real_in_and_real_out(tc)) {
      back = 0;
    }
  }
} else {
  back = 0;
  if(tc->app) {
    char *xxxptr[8];
    xxxptr[0] = (tc->messages)[13];
    xxxptr[1] = str_spc;
    xxxptr[2] = str_bropen;
    xxxptr[3] = tc->in_real;
    xxxptr[4] = str_brclose;
    dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
  }
}
	    }
	  }
	  tc->out_real = NULL;
	  tc->in_real = NULL;
	}
	}
      }
    }
    dkdir_close(tc->dir_tra); tc->dir_tra = NULL;
  } else {
    /* XXXXX ERROR: Failed to traverse directory */
    if(tc->app) {
      char *xxxptr[8];
      xxxptr[0] = (tc->messages)[14];
      xxxptr[1] = str_spc;
      xxxptr[2] = str_bropen;
      xxxptr[3] = tc->dir_cur;
      xxxptr[4] = str_brclose;
      dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
    }
  }
  } else {	
    if(tc->app) {
      char *xxxptr[8];
      xxxptr[0] = (tc->messages)[27];
      xxxptr[1] = str_spc;
      xxxptr[2] = str_bropen;
      xxxptr[3] = tc->dir_cur;
      xxxptr[4] = str_brclose;
      dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
    }
  }
  return back;
}



/**	Run for a directory.
	@param	tc	Trcecc job.
	@return	1 on success, 0 on error.
*/
static
int
run_for_real_dir DK_P1(tracecc_t *,tc)
{
  int back = 0;
  size_t mpl = dksf_get_maxpathlen();
  char *cwdbuffer, *errmsg [3];
  int cwdfound;

  if(mpl < 128) mpl = 128;
  if(mpl > 4096) mpl = 4096;
  if(is_dir(tc->dir_cur)) {
    cwdfound = 0;
    cwdbuffer = dk_new(char,mpl);
    if(cwdbuffer) {
      if(getcwd(cwdbuffer, (unsigned long)mpl)) {
        cwdfound = 1;
      } else {
	if(tc->app) {
	  errmsg[0] = (tc->messages)[30];
	  dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, errmsg, 1);
	}
      }
    } else {
      if(tc->app) {
        dkapp_err_memory(tc->app, 1, mpl);
      }
    }
    if(cwdfound) {
      if(dksf_is_abs_path(tc->dir_cur)) {
        if(chdir(tc->dir_cur) != 0) {
          if(tc->app) {
	    errmsg[0] = (tc->messages)[28]; errmsg[1] = tc->dir_cur;
	    errmsg[2] = (tc->messages)[29];
	    dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, errmsg, 3);
	  }
        }
      } else {
	if(strcmp(tc->dir_cur, ".")) {
          if(tc->app) {
            errmsg[0] = (tc->messages)[31];
	    errmsg[1] = tc->dir_cur;
	    errmsg[2] = (tc->messages)[32];
	    dkapp_log_msg(tc->app, DK_LOG_LEVEL_WARNING, errmsg, 3);
	  }
	}
      }
    }
    back = run_for_real_dir_v1(tc);
    if(cwdbuffer) {
      if(cwdfound) {
        if(chdir(cwdbuffer) != 0) {
          if(tc->app) {
	    errmsg[0] = (tc->messages)[28]; errmsg[1] = tc->dir_cur;
	    errmsg[2] = (tc->messages)[29];
	    dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, errmsg, 3);
	  }
	}
      }
      dk_delete(cwdbuffer);
    }
  }
  return back;
}



/**	Retrieve IDE style from preferences.
	@param	i	Pointer to IDE style variable.
	@param	app	Application.
*/
static
void
set_ide_style_from_pref DK_P2(int *,i,dk_app_t *,app)
{
  char buffer[32], *ptr;
  
  if(dkapp_get_pref(app,pk_ide_style, buffer, sizeof(buffer), 0)) {
    ptr = dkstr_start(buffer, NULL);
    if(ptr) {
      dkstr_chomp(ptr, NULL);
      *i = dkapp_ide_type(ptr);
    }
  }
  
}



/**	Retrieve preferences.
	@param	app	Application.
	@param	tc	Tracecc option set.
*/
static
void
tracecc_retrieve_preferences DK_P2(dk_app_t *,app,tracecc_opt_t *,tc)
{
  int val;
  char buffer[32], *ptr;
  if(app && tc) {
    val = tc->o;
    set_mode_bit_bool(&val, app, pk_debug_enable, TRACECC_OPT_DEBUG);
    set_mode_bit_bool(&val, app, pk_debug_stdout, TRACECC_OPT_DEBST);
    set_mode_bit_bool(&val, app, pk_debug_timestamp, TRACECC_OPT_DATETIME);
    set_mode_bit_bool(&val, app, pk_debug_keyword, TRACECC_OPT_KEYWORD);
    /* set_mode_bit_bool(&val, app, pk_debug_ide, TRACECC_OPT_DEB_IDE); */
    if(dkapp_get_pref(app, pk_ide_style,buffer,sizeof(buffer),0)) {
      ptr = dkstr_start(buffer, NULL);
      if(ptr) {
        dkstr_chomp(ptr, NULL);
	tc->i = dkapp_ide_type(ptr);
      }
    }
    set_mode_bit_bool(&val, app, pk_linenumbers, TRACECC_OPT_LINENO);
    set_mode_bit_bool(&val, app, pk_make, TRACECC_OPT_MAKE);
    set_mode_bit_bool(&val, app, pk_cpp_comments, TRACECC_OPT_CPPCOMMENT);
    tc->o = val;
    set_int_from_pref(&(tc->b), app, pk_boxwidth);
    set_ide_style_from_pref(&(tc->i), app);
  }
}



/**	Save preferences.
	@param	app	Application.
	@param	mode	Operating mode.
	@param	bw	Comment box width.
	@param	is	IDE style index.
*/
void
tracecc_save_preferences DK_P4(dk_app_t *,app,int,mode,int,bw,int,is)
{
  char buffer[32];
  int i;
  if(app) {
    set_bool(app, pk_debug_enable, (mode & TRACECC_OPT_DEBUG));
    set_bool(app, pk_debug_stdout, (mode & TRACECC_OPT_DEBST));
    set_bool(app, pk_debug_timestamp, (mode & TRACECC_OPT_DATETIME));
    /* set_bool(app, pk_debug_ide, (mode & TRACECC_OPT_DEB_IDE)); */
    set_bool(app, pk_debug_keyword, (mode & TRACECC_OPT_KEYWORD));
    i = is;
    if(i < 0) i = 0; if(i > 4) i = 4;
    dkapp_set_pref(app, pk_ide_style, ide_styles[i]);
    set_bool(app, pk_linenumbers, (mode & TRACECC_OPT_LINENO));
    set_bool(app, pk_make, (mode & TRACECC_OPT_MAKE));
    set_bool(app, pk_cpp_comments, (mode & TRACECC_OPT_CPPCOMMENT));
    sprintf(buffer, "%d", bw);
    dkapp_set_pref(app, pk_boxwidth, buffer);
  }
}



/**	Cleanup before we start running _and_ after finishing the run.
	@param	tc	Tracecc job.
*/
static
void
tracecc_cleanup_before_run DK_P1(tracecc_t *,tc)
{
  char *ptr;
  
  if(tc) {
    (tc->f).o = (tc->o).o; (tc->f).b = (tc->o).b; (tc->f).i = (tc->o).i;
    tc->cmbll = tc->cmbmax = 0;
    tc->icmdu = 0;
    tc->state = tc->reaction = tc->indent = tc->textflags = 0;
    tc->currentchar = '\0';
    tc->ibu = tc->ibc = 0;
    if(tc->tmpfile) {
      fclose(tc->tmpfile);
    }
    tc->tmpfile = NULL;
    (void)dksf_remove_file(tc->tmp_name_buf);
    if(tc->outfile) {
      fclose(tc->outfile);
    }
    tc->outfile = NULL;
    if(tc->infile) {
      fclose(tc->infile);
    }
    tc->infile = NULL;
    tc->in_short = NULL;
    tc->lineno = 0UL;
    tc->what = 0;
    if(tc->dir_ori) {
      ptr = tc->dir_ori; dk_delete(ptr);
    }
    tc->dir_ori = NULL;
    if(tc->fne_ori) {
      dkfne_close(tc->fne_ori);
    }
    tc->fne_ori = NULL;
    tc->dir_cur = NULL;
    if(tc->dir_tra) {
      dkdir_close(tc->dir_tra);
    }
    tc->dir_tra = NULL;
    if(tc->in_ori) {
      ptr = tc->in_ori; dk_delete(ptr);
    }
    tc->in_ori = NULL;
    if(tc->out_ori) {
      ptr = tc->out_ori; dk_delete(ptr);
    }
    tc->out_ori = NULL;
    if(tc->fne_in) {
      dkfne_close(tc->fne_in);
    }
    tc->fne_in = NULL;
    if(tc->fne_out) {
      dkfne_close(tc->fne_out);
    }
    tc->fne_out = NULL;
    tc->in_real = NULL;
    tc->out_real = NULL;
  }
}



/**	Initialize a tracecc_t when opening it.
 	@param	tc	Tracecc job.
	@param	app	Application.
	@return	1 on success, 0 on error.
*/
static
int
tracecc_initialize_at_open DK_P2(tracecc_t *,tc,dk_app_t *,app)
{
  int back = 0;
  long mpl; unsigned long ul; size_t sz;
  char buffer[32];
  
  if(tc) {
    (tc->o).o = 0; (tc->o).b = 75; (tc->o).i = DK_APP_IDE_NONE;
    tc->error_code = 0;
    tc->messages = NULL;
    tc->app = app;
    mpl = tc->mpl = dksf_get_maxpathlen();
    sz = (size_t)4096;
    /* tc->icmdsz = 128; */
    tc->icmdsz = 512;
    if(app) {
      if(dkapp_get_pref(app,pk_input_length,buffer,sizeof(buffer), 0)) {
        if(sscanf(buffer, "%lu", &ul) == 1) {
	  sz = (size_t)ul;
	  if(sz > 16384) sz = 16384;
	  if(sz <   128) sz =   128;
	}
      }
      if(dkapp_get_pref(app,pk_command_length,buffer,sizeof(buffer), 0)) {
        if(sscanf(buffer, "%lu", &ul) == 1) {
	  tc->icmdsz = (size_t)ul;
	  if(tc->icmdsz > 1024) tc->icmdsz = 1024;
	  if(tc->icmdsz < 64)   tc->icmdsz = 64;
	}
      }
    }
    tc->ibl = sz;
    tc->inbuffer = dk_new(char,sz);
    tc->messages = dk_new(p_char_t,number_of_messages);
    tc->out_name_buf = dk_new(char,mpl);
    tc->tmp_name_buf = dk_new(char,mpl);
    tc->icmdbuf = dk_new(char,(tc->icmdsz));
    if((tc->messages) && (tc->out_name_buf) && (tc->inbuffer) && (tc->tmp_name_buf) && (tc->icmdbuf)) {
      (tc->tmp_name_buf)[0] = '\0';
      reset_strings_for_messages(tc->messages);
      if(app) {
        set_strings_by_app(tc->messages, app);
	back = dkapp_tmpnam(app, tc->tmp_name_buf, mpl);
      } else {
        set_strings_for_messages(tc->messages);
	if(dksf_get_tempdir(tc->tmp_name_buf, mpl)) {
	  char xbuffer[32];
	  sprintf(xbuffer, "%ld", (long)dksf_getpid());
	  if((size_t)mpl > (strlen(str_tracecc) + strlen(xbuffer) + 3)) {
	    size_t lgt;
	    back = 1;
	    lgt = strlen(tc->tmp_name_buf);
	    sprintf(&((tc->tmp_name_buf)[lgt]), "/%s.%s", str_tracecc, xbuffer);
	  }
	}
      }
      
    } else {
      tc->error_code = DK_ERR_NOMEM;
      if(app) {
        dkapp_err_memory(app, sizeof(p_char_t),number_of_messages);
      }
    }
    if(app) {
      tracecc_retrieve_preferences(
        app, &(tc->o)
      );
    }
    tc->what = 0;
    tc->dir_ori = NULL;
    tc->fne_ori = NULL;
    tc->dir_cur = NULL;
    tc->dir_tra = NULL;
    tc->in_ori = NULL;
    tc->out_ori = NULL;
    tc->fne_in = NULL;
    tc->fne_out = NULL;
    tc->in_real = NULL;
    tc->out_real = NULL;
    tc->newft = 0;
    tc->in_short = NULL;
    tc->infile = NULL;
    tc->outfile = NULL;
    tc->tmpfile = NULL;
    tc->lineno = 0UL;
    tc->ibu = 0;
    tc->ibc = 0;
    tc->currentchar = '\0';
    tc->textflags = 0;
    tc->indent = 0;
  } 
  return back;
}



void
tracecc_close DK_P1(tracecc_t *,ptr)
{
  char *p;
  
  if(ptr) {
    tracecc_cleanup_before_run(ptr);
    if(ptr->icmdbuf) {
      p = ptr->icmdbuf;
      dk_delete(p);
    }
    ptr->icmdbuf = NULL;
    if(ptr->inbuffer) {
      p = ptr->inbuffer;
      dk_delete(p);
    }
    ptr->inbuffer = NULL;
    if(ptr->tmp_name_buf) {
      p = ptr->tmp_name_buf;
      dk_delete(p);
    }
    ptr->tmp_name_buf = NULL;
    if(ptr->out_name_buf) {
      p = ptr->out_name_buf;
      dk_delete(p);
    }
    ptr->out_name_buf = NULL;
    if(ptr->messages) {
      char **cptr;
      cptr = ptr->messages;
      dk_delete(cptr);
    }
    DK_MEMRES(ptr,sizeof(tracecc_t));
    dk_delete(ptr);
  }
}



tracecc_t *
tracecc_open DK_P1(dk_app_t *,app)
{
  tracecc_t *back = NULL;
  
  back = dk_new(tracecc_t,1);
  if(back) {
    DK_MEMRES(back,sizeof(tracecc_t));
    if(!tracecc_initialize_at_open(back,app)) {
      dk_delete(back); back = NULL;
    }
  } 
  return back;
}



int
tracecc_get_mode DK_P1(tracecc_t *,tc)
{
  int back = 0;
  if(tc) { back = (tc->o).o; }
  return back;
}



int
tracecc_get_boxwidth DK_P1(tracecc_t *,tc)
{
  int back = 0;
  if(tc) { back = (tc->o).b; }
  return back;
}



int
tracecc_get_idestyle DK_P1(tracecc_t *,tc)
{
  int back = 0;
  if(tc) { back = (tc->o).i; }
  return back;
}



void
tracecc_set_mode DK_P2(tracecc_t *,tc,int,mode)
{
  if(tc) { (tc->o).o = mode; }
}



void
tracecc_set_boxwidth DK_P2(tracecc_t *,tc,int,bw)
{
  
  if(tc) { (tc->o).b = bw; }
  
}



void
tracecc_set_idestyle DK_P2(tracecc_t *,tc,int,bw)
{
  
  if(tc) { (tc->o).i = bw; }
  
}



/**	Remove trailing separator from directory name.
	@param	d	Buffer containing a directory name.
*/
static
void
remove_trailing_separator DK_P1(char *,d)
{
  size_t lgt;
  if(d) {
    lgt = strlen(d);
    if(d[lgt - 1] == fnsep[0]) { d[lgt - 1] = '\0'; }
  }
}



int
tracecc_dir DK_P2(tracecc_t *,tc,char *,d)
{
  int back = 0; char *ptr;
  
  if(tc && d) {
    tracecc_cleanup_before_run(tc);
    tc->what = 1;
    tc->dir_ori = dkstr_dup(d);
    if(tc->dir_ori) {
      remove_trailing_separator(tc->dir_ori);
      dksf_correct_fnsep(tc->dir_ori);
      if(dksf_must_expand_filename(tc->dir_ori)) {
        tc->fne_ori = dkfne_open(tc->dir_ori,0,1);
	if(tc->fne_ori) {
	  if(dkfne_next(tc->fne_ori)) {
	    back = 1;
	    do {
	      tc->dir_cur = (char *)dkfne_get_fullname(tc->fne_ori); /* QDH */
              if(tc->app) {
                char *xxxptr[8];
                xxxptr[0] = (tc->messages)[20];
                xxxptr[1] = tc->dir_cur;
                xxxptr[2] = (tc->messages)[21];
                dkapp_log_msg(tc->app, DK_LOG_LEVEL_PROGRESS, xxxptr, 3);
              }
	      if(!run_for_real_dir(tc)) {
	        back = 0;
	      }
              if(tc->app) {
                char *xxxptr[8];
                xxxptr[0] = (tc->messages)[23];
                xxxptr[1] = tc->dir_cur;
                xxxptr[2] = (tc->messages)[24];
                dkapp_log_msg(tc->app, DK_LOG_LEVEL_PROGRESS, xxxptr, 3);
              }
	      tc->dir_cur = NULL;
	    } while(dkfne_next(tc->fne_ori));
	  } else {
	    /* XXXX ERROR: No directory matching pattern */
            if(tc->app) {
              char *xxxptr[8];
              xxxptr[0] = (tc->messages)[15];
              xxxptr[1] = str_spc;
              xxxptr[2] = str_bropen;
              xxxptr[3] = tc->dir_ori;
              xxxptr[4] = str_brclose;
              dkapp_log_msg(tc->app, DK_LOG_LEVEL_ERROR, xxxptr, 5);
            }
	  }
	  dkfne_close(tc->fne_ori); tc->fne_ori = NULL;
	} else {
	  tc->error_code = DK_ERR_NOMEM;
	  if(tc->app) dkapp_err_memory(tc->app,sizeof(dk_fne_t),1);
	}
      } else {
        tc->dir_cur = tc->dir_ori;
        if(tc->app) {
          char *xxxptr[8];
          xxxptr[0] = (tc->messages)[20];
          xxxptr[1] = tc->dir_cur;
          xxxptr[2] = (tc->messages)[21];
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_PROGRESS, xxxptr, 3);
        }
	back = run_for_real_dir(tc);
        if(tc->app) {
          char *xxxptr[8];
          xxxptr[0] = (tc->messages)[22];
          xxxptr[1] = tc->dir_cur;
          xxxptr[2] = (tc->messages)[23];
          dkapp_log_msg(tc->app, DK_LOG_LEVEL_PROGRESS, xxxptr, 3);
        }
	tc->dir_cur = NULL;
      }
      ptr = tc->dir_ori; dk_delete(ptr); tc->dir_ori = NULL;
    } else {
      tc->error_code = DK_ERR_NOMEM;
      if(tc->app) dkapp_err_memory(tc->app,1,strlen(d));
    }
    tracecc_cleanup_before_run(tc);
  }
  return back;
}



int
tracecc_files DK_P3(tracecc_t *,tc,char *,i,char *,o)
{
  int back = 0; char *ptr;
  
  if(tc && i && o) {
    tracecc_cleanup_before_run(tc);
    tc->what = 2;
    tc->in_ori = dkstr_dup(i);
    if(tc->in_ori) {
      tc->out_ori = dkstr_dup(o);
      if(tc->out_ori) {
        
        dksf_correct_fnsep(tc->in_ori);
	dksf_correct_fnsep(tc->out_ori);
	back = run_for_in_and_out_file_name(tc);
	ptr = tc->out_ori; dk_delete(ptr); tc->out_ori = NULL;
      } else {
        tc->error_code = DK_ERR_NOMEM;
        if(tc->app) dkapp_err_memory(tc->app,1,strlen(o));
      }
      ptr = tc->in_ori; dk_delete(ptr); tc->in_ori = NULL;
    } else {
      tc->error_code = DK_ERR_NOMEM;
      if(tc->app) dkapp_err_memory(tc->app,1,strlen(i));
    }
    tracecc_cleanup_before_run(tc);
  }
  
  return back;
}


