/* 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 (¤t_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