/* tua.c 
 * HDB-uucp Activity Analyzer. Eats all the uucp logs and extracts per system,
 * user and daily stats.
 *
 * This file is part of TUA.
 * 
 *   Copyright (C) 1991,92,93  Lele Gaifax (lele@nautilus.sublink.org)
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the license, or (at
 *   your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "tua.h"
#include <time.h>
#include "patchlevel.h"
#include "getopt.h"
#include <ctype.h>
#include "pcdl.h"

#ifdef __GNUC__
static
#ifndef __STRICT_ANSI__
  __volatile__
#endif /* __STRICT_ANSI__ */
  void
error (char *str)
{
  fprintf (stderr, "Error in processing %s\n", str);
  exit (1);
}

#else /* not __GNUC__ */
#define	error(str)  { \
			fprintf(stderr, "Error in processing %s\n", str); \
			exit(1); \
		    }
#endif /* __GNUC__ */

/* GLOBALS */

#if defined (BOTH_OF_THEM)
int is_taylor_uucp = DEFAULT_MODE_IS_TAYLOR;
#endif

#ifdef DEBUG
int be_verbose = FALSE;
#endif

/* TRUE if we want a separated commands list */
int separated_command_report_opt = FALSE;

/* TRUE if we want the command report printed */
int do_command_report_opt = FALSE;

/* TRUE if at least one '--only-system' option is given */
int just_some_system_opt = FALSE;

/* Size in row of each port activity chart */
int chart_size_opt = 10;

/* The prefix path for the logs file. This is set by default to
   DEFAULT_TAYLOR_SPOOL_DIR or DEFAULT_HDB_SPOOL_DIR depending on
   which Uucp we are using */
char *logs_prefix_path_opt;

static int do_system_report_opt = TRUE;
static int do_user_report_opt = TRUE;
static int do_daily_report_opt = TRUE;
static int save_history_opt = FALSE;
static int do_history_report = TRUE;
static int reset_history_opt = FALSE;
static int do_monthly_activity_report_opt = TRUE;
static int do_hourly_activity_chart_opt = TRUE;
static int do_chart_only_opt = FALSE;
static int print_country_and_exit = FALSE;

#ifdef USE_TCL
static int load_tcl_rc = TRUE;
Tcl_Interp *TUA_interp;
static char *TUA_init_script = TUA_INIT_SCRIPT;
static int interactive_session_opt = FALSE;
static int interactive_session_done = FALSE;
#endif

int CurrentMonth;
char *Since;
int DaysSinceLastClean;
Julian_t StatStartingTime = 0.0;
Julian_t StatEndingTime = 0.0;

static struct option long_options[] =	/* !! KEEP IT IN ALPHAB ORDER !! */
{
  {"chart-only", 0, &do_chart_only_opt, 'o'},			/* -o */      
  {"chart-size", 1, 0, 'z'},					/* -z */
  {"country", 1, 0, 'a'},					/* -a */
  {"command-lists", 0, &do_command_report_opt, 'C'},		/* -C */
#ifdef GLOB_ALIASES
  {"glob-port-alias", 1, 0, 'R'},				/* -R */
  {"glob-system-alias", 1, 0, 'M'},				/* -M */
  {"glob-user-alias", 1, 0, 'E'},				/* -E */
#endif
#if defined (HDB_ERR_FACT) && defined (HDB_UUCP)
  {"hdb-factor", 1, 0, 'f'},					/* -f */
#endif
#ifdef BOTH_OF_THEM
  {"hdb-uucp", 0, &is_taylor_uucp, 'B'}, 			/* -B */
#endif
  {"help", 0, 0, 'i'},						/* -i */
#ifdef USE_TCL
  {"interactive", 0, &interactive_session_opt, 'I'},		/* -I */
#endif
  {"kill-system", 1, 0, 'k'},					/* -k */
  {"kill-user", 1, 0, 'K'},					/* -K */
  {"list-countries", 0, &pcd_list_countries, 1},
  {"no-daily-rep", 0, &do_daily_report_opt, 'D'},		/* -D */
  {"no-history", 0, &do_history_report, 'h'},			/* -h */
  {"no-hourly-chart", 0, &do_hourly_activity_chart_opt, 'y'},   /* -y */
  {"no-monthly-act", 0, &do_monthly_activity_report_opt, 'm'},	/* -m */
  {"no-system-rep", 0, &do_system_report_opt, 'S'},		/* -S */
#ifdef USE_TCL
  {"no-tua-rc", 0, &load_tcl_rc, 't'}, 				/* -t */
#endif
  {"no-user-rep", 0, &do_user_report_opt, 'U'},			/* -U */
  {"only-system", 1, 0, 'O'},					/* -O */
  {"port-alias", 1, 0, 'r'},					/* -r */
  {"prefix-path", 1, 0, 'p'},					/* -p */
  {"print-country", 0, &print_country_and_exit, 1},
#ifdef USE_TCL
  {"rc-file", 1, 0, 'T'},					/* -T */
#endif
  {"reset-history", 0, &reset_history_opt, '0'},		/* -0 */
  {"separate-command-report", 0, &separated_command_report_opt, 'c'}, 	/* -c */
  {"system-alias", 1, 0, 's'},					/* -s */
#ifdef BOTH_OF_THEM
  {"taylor-uucp", 0, &is_taylor_uucp, 'Y'}, 			/* -Y */
#endif
  {"update-history", 0, &save_history_opt, 'H'},		/* -H */
  {"user-alias", 1, 0, 'u'},					/* -u */
#ifdef DEBUG
  {"verbose", 0, &be_verbose, 'v'},				/* -v */
#endif
  {"version", 0, 0, 'V'},					/* -V */
  {0, 0, 0, 0}
};

/* Build a getopt string from the structure above */
static char *
DEFUN(build_getopt_string, (long_options),
      CONST struct option long_options[])
{
  if (long_options && long_options[0].name)
    {
      extern PTR EXFUN(malloc, (size_t));
      extern PTR EXFUN(realloc, (PTR, size_t));
      int curr_size = 10;
      char * getopt_string = (char *)malloc (curr_size);
      int gs_index, lo_index;
      
      for (gs_index=lo_index=0; long_options[lo_index].name; lo_index++)
	{
	  CONST struct option * option = &long_options[lo_index];
	  
	  if (isalnum (option->val))
	    {
	      if (gs_index >= curr_size-2)
		{
		  curr_size += 10;
		  getopt_string = (char *) realloc (getopt_string, curr_size);
		}
	      
	      getopt_string[gs_index++] = option->val;
	      if (option->has_arg)
		getopt_string[gs_index++] = ':';
	    }
	}
      getopt_string[gs_index] = 0;
      return getopt_string;
    }
  else
    return 0;
}

static char VersionFormatString[] =
"The Uucp Analyzer (TUA) rel. %s by lele@nautilus.sublink.org (Lele Gaifax)\n";
static char CopyleftInfo[] =
"TUA may be copied only under the terms of the GNU General Public License,\n\
a copy of which can be found with the TUA distribution package\n";

static void
DEFUN_VOID (DisplayVersion)
{
  fprintf (stderr, VersionFormatString, RELEASE);
}

static void
DEFUN_VOID (DisplayUsage)
{
  DisplayVersion ();

  fputs ("Options:\n\
-S, --no-system-rep		Do not show the report for the Systems\n\
-U, --no-user-rep		Do not show the report for the Users\n\
-D, --no-daily-rep		Do not show the day-by-day report\n\
-m, --no-monthly-act		Do not show the monthly activity report\n\
-h, --no-history		Do not show the history\n\
-y, --no-hourly-chart		Do not show the hourly activity chart\n\
-C, --command-lists		Show the various command lists\n\
-O, --only-system=SYS		Consider only SYS in the reports\n", stderr);
  fputs ("\
-c, --separate-command-report	Build a separate log for the commands\n\
-k, --kill-system=SYS		Do not show anything about system SYS\n\
-K, --kill-user=USER		Do not show anything about user USER\n\
-H, --update-history		Update the history file\n\
-0, --reset-history		Reset the history file\n\
-p, --prefix-path=DIR		Read log files in directory DIR\n\
-z, --chart-size=SIZE		Set the size of the hourly chart to SIZE rows\n", stderr);
  fputs ("\
-o, --chart-only		Print only the hourly activity chart\n", stderr);
#ifdef DEBUG
  fputs ("\
-v, --verbose			Print infos while processing\n", stderr);
#endif
  fputs ("\
-u, --user-alias USER=ALIAS	Replace every user named USER with ALIAS\n\
-r, --port-alias PORT=ALIAS	Replace every port named PORT with ALIAS\n\
-s, --system-alias SYS=ALIAS	Replace every system named SYS with ALIAS\n\
", stderr);

#ifdef GLOB_ALIASES
  fputs ("\
-E, --glob-user-alias GLOB=ALIAS    Like --user-alias but with globbing search\n\
-R, --glob-port-alias GLOB=ALIAS    Like --port-alias but with globbing search\n\
-M, --glob-system-alias GLOB=ALIAS  Like --sys-alias but with globbing search\n\
", stderr);
#endif

#if defined (HDB_ERR_FACT) && defined (HDB_UUCP)
  fputs ("\
-f, --hdb-factor F		Set the Correction Factor for HDB to F (by default. 2.5)\n\
", stderr);
#endif

#ifdef USE_TCL
  fputs ("\
-t, --no-tua-rc			Do not load the TCL Initialization script\n\
-T, --rc-file=FILE		Load FILE instead of the standard init script\n\
-I, --interactive		Put TUA in interactive mode\n\
", stderr);
#endif

#if defined (BOTH_OF_THEM)
#if DEFAULT_MODE_IS_TAYLOR
  fputs ("\
-Y, --taylor-uucp		Force Taylor Uucp Mode (default)\n\
-B, --hdb-uucp			Force HDB Mode\n\
", stderr);
#else
  fputs ("\
-Y, --taylor-uucp		Force Taylor Uucp Mode\n\
-B, --hdb-uucp			Force HDB Mode (default)\n\
", stderr);
#endif
#endif
  
  fputs ("\
-i, --help			Get this help\n\
-V, --version			Print the version number of TUA and exit\n\
-a, --country NAME              Override the name of your country\n\
    --print-country             Print the country configuration and exit\n\
    --list-countries            List the countries in the PCD file and exit\n\n\
	Please report bugs and problems to:\n\
		lele@nautilus.sublink.org\n\n", stderr);
  fputs (CopyleftInfo, stderr);
}

static void
DEFUN (process_command_line_option, (argc, argv),
       int argc AND
       char *argv[])
{
  extern int EXFUN (atoi, (char *));
  extern double EXFUN (atof, (char *));
  extern char *optarg;
  extern int opterr;
  int opt;
  int optidx;

  opterr = FALSE;

  while ((opt = getopt_long (argc, argv, build_getopt_string (long_options), long_options, &optidx)) != EOF)
    {
      if (opt == 0 && long_options[optidx].flag == 0)
	opt = long_options[optidx].val;
      
      switch (opt)
	{
	case 0:
	  break;

	case 'a':
	  default_country_name = optarg;
	  break;
	  
	case 'S':
	  do_system_report_opt = FALSE;
	  break;

	case 'U':
	  do_user_report_opt = FALSE;
	  break;

	case 'D':
	  do_daily_report_opt = FALSE;
	  break;

	case 'C':
	  do_command_report_opt = TRUE;
	  break;

	case 'm':
	  do_monthly_activity_report_opt = FALSE;
	  break;

	case 'h':
	  do_history_report = FALSE;
	  break;

	case 'c':
	  do_command_report_opt = TRUE;
	  separated_command_report_opt = TRUE;
	  break;

	case 'k':
	  kill_system (optarg);
	  break;

	case 'K':
	  kill_user (optarg);
	  break;

	case 'H':
	  save_history_opt = TRUE;
	  break;

	case '0':
	  reset_history_opt = TRUE;
	  break;

#ifdef HDB_ERR_FACT
	case 'f':
	  ERRFACT = atof (optarg);
	  if (ERRFACT <= 0.0)
	    {
	      fprintf (stderr, "Bad -f argument. Error factor fixed to %.1f\n",
		       HDB_ERR_FACT);
	      ERRFACT = HDB_ERR_FACT;
	    }
	  break;
#endif

	case 'p':
	  logs_prefix_path_opt = optarg;
	  break;

	case 'z':
	  chart_size_opt = atoi (optarg);
	  break;

	case 'y':
	  do_hourly_activity_chart_opt = FALSE;
	  break;

	case 'o':
	  do_chart_only_opt = TRUE;
	  break;

#ifdef DEBUG	  
	case 'v':
	  be_verbose = TRUE;
	  break;
#endif
	  
	case 'O':
	  (void) insert_system (optarg);
	  just_some_system_opt = TRUE;
	  break;

	case 'u':		/* User Alias */
	case 'r':		/* Port Alias */
	case 's':		/* System Alias */
#ifdef GLOB_ALIASES
	case 'E':
	case 'R':
	case 'M':
#endif
	  {
	    char *name = optarg;
	    char *alias;

	    for (alias = name; *alias && *alias != '='; alias++)
	      /* do nothing */ ;

	    if (*alias == '=')
	      {
		*alias = '\0';
		alias++;
		switch (opt)
		  {
		  case 'u':
		    insert_user_alias (name, alias);
		    break;

		  case 'r':
		    insert_port_alias (name, alias);
		    break;

		  case 's':
		    insert_system_alias (name, alias);
		    break;

#ifdef GLOB_ALIASES
		  case 'E':
		    insert_user_glob_alias (name, alias);
		    break;

		  case 'R':
		    insert_port_glob_alias (name, alias);
		    break;

		  case 'M':
		    insert_system_glob_alias (name, alias);
		    break;
#endif
		  }
	      }
	    else
	      {
		DisplayUsage ();
		exit (1);
	      }
	  }
	  break;

	case 'V':
	  DisplayVersion ();
	  fputs (CopyleftInfo, stdout);
	  exit (0);
	  break;

#ifdef USE_TCL
	case 't':
	  load_tcl_rc = FALSE;
	  break;

	case 'T':
	  TUA_init_script = optarg;
	  break;

	case 'I':
	  interactive_session_opt = TRUE;
	  break;
#endif

#if defined(BOTH_OF_THEM)
	case 'B':
	  is_taylor_uucp = 'B';
	  break;

	case 'Y':
	  is_taylor_uucp = 'Y';
	  break;
#endif
	    
	case '?':
	case 'i':
	  DisplayUsage ();
	  exit (opt == 'i' ? 0 : 1);
	  break;
	}
    }

#if defined (BOTH_OF_THEM)
  is_taylor_uucp = (is_taylor_uucp == 'Y' || is_taylor_uucp == TRUE ? TRUE : FALSE);
#endif

  if (reset_history_opt && !save_history_opt)
    LOG ("Warning: You must specify also option %s to reset it.\n", "`--update-history'");
}

void
DEFUN_VOID (print_the_report)
{
  if (do_system_report_opt)
    systems_report ();
  if (do_user_report_opt)
    users_report ();
  if (do_daily_report_opt)
    daily_report ();
  if (separated_command_report_opt)
    {
      if (do_system_report_opt)
	systems_command_report ();
      if (do_user_report_opt)
	users_command_report ();
      if (do_daily_report_opt)
	daily_command_report ();
    }
  systems_summary ();
  systems_summary_table ();
  if (*TheEpoc && do_history_report)
    {
      system_history_table ();
      if (do_monthly_activity_report_opt)
	monthly_history_table ();
    }
  if (do_hourly_activity_chart_opt)
    hourly_activity_charts ();
}

/* void get_complete_host_name (char * name)

   Attempt to get the system name by calling gethostname. If this does
   not work and this machine has a SYSTEMID file, try to use that. */

#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifndef MAXDOMNAMELEN
#define MAXDOMNAMELEN 255
#endif

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 255
#endif

static void
DEFUN(get_complete_host_name, (name),
      char name[MAXHOSTNAMELEN])
{
#ifdef SYSTEMID
  if ((gethostname (name, MAXHOSTNAMELEN) == -1) || *name == '\0')
    {
      FILE *fd;

      if ((fd = fopen (SYSTEMID, "r")) == NULL)
	strcpy (name, "UNKNOWN");
      else
	{
	  int last;

	  fgets (name, MAXHOSTNAMELEN, fd);
	  name[sizeof name - 1] = 0;
	  
	  last = strlen (name) - 1;

	  if (last > 0)
	    {
	      if (name[last] == '\n')
		name[last] = '\0';
	    }
	  else
	    strcpy (name, "UNKNOWN");
	  fclose (fd);
	}
    }
#else
  if ((gethostname (name, MAXHOSTNAMELEN) == -1) || *name == '\0')
    strcpy (name, "UNKNOWN");
#endif

  /* if gethostname doesn't return a Fully Qualified Domain Name */
  if (strchr (name, '.') == 0)
    {
      char domain[MAXDOMNAMELEN];

      if (getdomainname (domain, MAXDOMNAMELEN) == -1 || *domain == 0)
	strcat (name, NET_DOMAIN);
      else
	if (*domain == '.')
	  strcat (name, domain);
	else
	  {
	    strcat (name, ".");
	    strcat (name, domain);
	  }
    }
}

int
DEFUN (main, (argc, argv),
       int argc AND
       char *argv[])
{
  extern time_t EXFUN (time, (time_t *));
  char SystemName[MAXHOSTNAMELEN];
  Julian_t Now = julian_current_date();

#if defined(DEBUG) && defined(NeXT)
  malloc_debug (8);
#endif
  
#ifdef USE_READLINE
  initialize_readline (argv[0]);
#endif

  CurrentMonth = (julian_to_d (Now, 0)).Month;

  get_complete_host_name (SystemName);

#ifdef BOTH_OF_THEM  
  logs_prefix_path_opt = (is_taylor_uucp ? DEFAULT_TAYLOR_SPOOL_DIR : DEFAULT_HDB_SPOOL_DIR);
#else
#ifdef TAYLOR_UUCP
  logs_prefix_path_opt = DEFAULT_TAYLOR_SPOOL_DIR;
#else
  logs_prefix_path_opt = DEFAULT_HDB_SPOOL_DIR;
#endif /* TAYLOR_UUCP */
#endif /* BOTH_OF_THEM */
  
  process_command_line_option (argc, argv);

#ifdef USE_TCL

  initialize_TCL ();
  if (load_tcl_rc)
    {
      char init_cmd[sizeof (TCL_CMD) + (strlen (TUA_init_script) * 2)];
      int result;

      sprintf (init_cmd, TCL_CMD, TUA_init_script, TUA_init_script);
#if TCL_MAJOR_VERSION<7
      result = Tcl_Eval (TUA_interp, init_cmd, 0, (char **) NULL);
#else
      result = Tcl_Eval (TUA_interp, init_cmd);
#endif
      if (result != TCL_OK)
	{
	  fprintf (stderr, "There is an error in %s...\n\t%s\n",
		   TUA_init_script, TUA_interp->result);
	  exit (1);
	}
    }

#endif

  printf (VersionFormatString, RELEASE);

  if (pcd_list_countries)
    printf ("\nCountries I know about:\n");
  
  if (parse_pcd_file (PCD_FILENAME) == ERROR && !pcd_list_countries)
    fprintf (stderr, "Cannot parse pcd file '%s'. Ignoring.\n", PCD_FILENAME);
  else if (pcd_names_count == 0 && default_country_name && !pcd_list_countries)
    fprintf (stderr, "Country '%s' not found. I will omit phone costs.\n",
	     default_country_name);

  if (pcd_list_countries)
    exit (0);
  
  if (print_country_and_exit)
    {
      pcdl_print_country (&current_country);
      exit (0);
    }
  
  if (read_system_history () == ERROR)
    error ("rhistory");
  
#ifdef BOTH_OF_THEM
  
  if (!is_taylor_uucp)

#endif
#ifdef HDB_UUCP
    {
      if (read_xferstats_log () == ERROR)
	error ("xferstats");
      if (!do_chart_only_opt)
	{
	  if (read_uucico_log () == ERROR)
	    error ("uucico log");
	  if (read_uuxqt_log () == ERROR)
	    error ("uuxqt log");
	  if (read_uucp_log () == ERROR)
	    error ("uucp log");
	  if (read_uux_log () == ERROR)
	    error ("uux log");
	}
    }
#endif /* if HDB_UUCP */

#ifdef BOTH_OF_THEM

  if (is_taylor_uucp)

#endif
#ifdef TAYLOR_UUCP
    {
      if (read_stats_log () == ERROR)
	error ("stats");
      if (!do_chart_only_opt)
	if (read_log_log () == ERROR)
	  error ("log");
    }

#endif /* TAYLOR_UUCP */
  
  Since = savestring (julian_to_asc (StatStartingTime));
  
  DaysSinceLastClean = (int)(StatEndingTime - StatStartingTime)+1;
  printf ("%s UUCP Analysis for ``%s'',\n  since %s to %s (%d day%s)\n\n",
#if defined (BOTH_OF_THEM)
	  (is_taylor_uucp ? "Taylor" : "HoneyDanBer"),
#else
#if defined (TAYLOR_UUCP)	  
	  "Taylor",
#else /* !TAYLOR_UUCP */
	  "HoneyDanBer",
#endif /* TAYLOR_UUCP */
#endif /* BOTH_OF_THEM */
	  SystemName, Since, julian_to_asc (StatEndingTime), DaysSinceLastClean,
	  (DaysSinceLastClean > 1 ? "s" : ""));
  
  
#ifdef USE_TCL
  if (interactive_session_opt)
    {
      if (!interactive_session_done)
	interactive_mode (0, TUA_interp, 0, 0);
    }
  else
#endif
  if (!do_chart_only_opt)
    {
      print_the_report ();
      if (save_history_opt)
	if (write_system_history (reset_history_opt) == ERROR)
	  error ("whistory: probably you need uucp's write permission");
    }
  else
    {
      printf ("%s UUCP Analysis for ``%s''\n\n",
#if defined (TAYLOR_UUCP)
#if defined (HDB_UUCP)
	  (is_taylor_uucp ? "Taylor" : "HoneyDanBer"),
#else
	  "Taylor",
#endif /* HDB_UUCP */

#else /* !TAYLOR_UUCP */
	  "HoneyDanBer",
#endif /* TAYLOR_UUCP */	  
	      SystemName);
      hourly_activity_charts ();
    }

  exit (0);

 /*
   * Dummy return to stop gcc from complaining about non-void funct without
   * return
   */
  return (0);
}

#ifdef USE_TCL

int
DEFUN (tcl_do_option, (clientdata, interp, argc, argv),
       ClientData clientdata AND
       Tcl_Interp * interp AND
       int argc AND
       char *argv[])

{
  if (argc != 2)
    {
      sprintf (interp->result,
	       "Wrong number of Parameters: %s needs a LongOption name",
	       argv[0]);
      return TCL_ERROR;
    }
  else
    {
      struct option *cur_opt = &long_options[0];

      for (; cur_opt->name; cur_opt++)
	{
	  int result;

	  if ((result = strcmp (cur_opt->name, argv[1])) == 0)
	    {
	      if (cur_opt->flag)
		*(cur_opt->flag) = cur_opt->val;
	      return TCL_OK;
	    }
	 /*
           * The options are kept in order, so we know when to stop searching
           */
	  else if (result > 0)
	    break;
	}
    }

  sprintf (interp->result, "%s: option '%s' not found!", argv[0], argv[1]);
  return TCL_ERROR;
}

int
DEFUN (interactive_mode, (clientData, interp, argc, argv),
       ClientData clientData AND
       Tcl_Interp * interp AND
       int argc AND
       char *argv[])
{
  char *line;
#ifndef USE_READLINE
  char line_buffer[1000];
#endif
  char *cmd;
  int result, gotPartial;
#if TCL_MAJOR_VERSION<7
  Tcl_CmdBuf buffer = Tcl_CreateCmdBuf ();
#else
  Tcl_DString buffer;

  Tcl_DStringInit (&buffer);
#endif

  interactive_session_opt = TRUE;
  interactive_session_done = TRUE;
  gotPartial = 0;
  fputs ("\
You are now in Interactive Mode. You can enter any valid Tcl statement, or\n\
<EOF> (usually C-d) to exit.\n\n", stderr);

  for (;;)
    {
#ifdef USE_READLINE
      line = readline_gets (gotPartial ? "More> " : "TUA % ");
#else
      clearerr (stdin);
      fputs (gotPartial ? "More> " : "TUA % ", stdout);
      fflush (stdout);

      line = fgets (line_buffer, 1000, stdin);
#endif

      if (line == NULL)
	{
	  puts ("\n");

	  if (!gotPartial)
	    return TCL_OK;
	  else
	    continue;
	}

      if (!*line)
	continue;

#if TCL_MAJOR_VERSION<7
      cmd = Tcl_AssembleCmd (buffer, line);
#ifdef USE_READLINE
     /*
       * readline() strips the ending new-line character, and TCL
       * complains about that, so I add this char to the just evaluated
       * command.
       */
      cmd = Tcl_AssembleCmd (buffer, "\n");
#endif
      if (cmd == NULL)
	{
	  gotPartial = 1;
	  continue;
	}
#else /* TCL_MAJOR_VERSION >= 7 */
      cmd = Tcl_DStringAppend (&buffer, line, -1);
#ifdef USE_READLINE
      cmd = Tcl_DStringAppend (&buffer, "\n", -1);
#endif

      if (!Tcl_CommandComplete (cmd))
	{
	  gotPartial = 1;
	  continue;
	}
#endif /* TCL_MAJOR_VERSION<7 */
      
      gotPartial = 0;
      result = Tcl_RecordAndEval (interp, cmd, 0);

#if TCL_MAJOR_VERSION>=7
      Tcl_DStringFree(&buffer);
#endif

      if (result == TCL_OK)
	{
	  if (*interp->result != 0)
	    fprintf (stderr, "%s\n", interp->result);
	}
      else
	{
	  if (result == TCL_ERROR)
	    puts ("Error");
	  else
	    printf ("Error %d", result);
	  if (*interp->result != 0)
	    printf (": %s", interp->result);
	  puts ("\n");
	}
    }
  return TCL_OK;
}

#endif /* USE_TCL */


syntax highlighted by Code2HTML, v. 0.9.1