home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
High Voltage Shareware
/
high1.zip
/
high1
/
DIR24
/
USAGEP24.ZIP
/
USAGE.C
< prev
next >
Wrap
Text File
|
1993-02-04
|
39KB
|
1,153 lines
/*
USAGE -- Reads one or more Maximus or Binkley logs and generates a usage graph
Version 2.4 (02/04/93)
Written by Bob Quinlan of Austin, Texas, USA
Sysop of Red October at 512-834-2593 (1:382/111)
Special thanks to Steve Antonoff for suggestions and code.
Copyright 1993 by Bob Quinlan
Compatible with Maximus 2.00 and 2.01 and BinkleyTerm 2.55 and 2.56
This program reads one or more Maximus or Binkley type logs and
generates a BBS-format usage graph based on the data. By default it
will read MAX.LOG in the current directory and write USAGE.BBS. The
following switches allow you to configure it for your system and
preferences:
/Cx=y Set color number 'x' to Avatar color 'y'. 'x' values
mean the following:
0 = default color
1 = title
2 = frame
3 = reference text (%)
4 = reference lines (%)
5 = hour text
6 = hour dots (odd hour values)
7 = hour lines
8 = data bars
9 = enter prompt
Avatar color codes are given in two-digit hexadecimal.
The first digit sets the background color (0-7 only) and
the second digit sets the foreground color (0-F). Each
digit corresponds to the following colors:
0 = black
1 = blue
2 = green
3 = cyan
4 = red
5 = magenta
6 = brown
7 = white
8 = grey
9 = bright blue
A = bright green
B = bright cyan
C = bright red
D = bright magenta
E = yellow
F = bright white
Example: To set the data bars to yellow on blue you
would use the switch /C8=1E.
/Ddays Days of history to use. Any log events or history file
entries older than this many days will be ignored.
/Hfile History file to process. This eliminates the need to
rescan old log data. Every time USAGE runs a new record
is added to the history file for future reference.
Records older than the /D parameter specifies will be
removed from the history file automatically.
This information is stored in USAGE.HST by default. You
can specify another name using the /H parameter. Using
/H by itself will prevent history file processing.
/Ifile Incremental file to process. This file keeps track of
where and in what state the previous log ended. If the
file has been restarted since the last run that will be
detected and the location pointer reset to the beginning
of the new file.
Incremental processing means that your maintenance times
will be included in the graph as "in use" periods.
Without this feature they would never register because
the processing would also end with an unresolved active
event.
This information is stored in USAGE.INC by default. You
can specify another name using the /I parameter. Using
/I by itself will prevent incremental file processing.
/Lfile Log file to process. Note that the /L parameter can be
used more than once to specify several logs. This
feature is particularly useful if you have multiple
lines with separate logs.
/M Military time. Display the hours as 0-23.
/Sfile Search file to process. You can override the default
search strings that are used to recognize system
activity in the log by specifying a search file.
A search file is just a text file containing one string
per line. Search strings must begin with the log field
immediately following the date. Add a + as the first
character of each line that indicates an activity is
starting. Add a - as the first character of each line
that indicates an activity is ending.
By default USAGE uses a set of search strings that
should work with both BinkleyTerm and Maximus. BINK.S
and MAX.S are included both as samples to help you
construct your own search files. Other .S files may
also be included.
/Ttitle Title for graph. You may want to supply multiple word
values for title. You can separate the words by spaces
or by underscores (which will be replaced by spaces).
For example, both of these lines would produce the same
results:
usage /lmax.log /tRed October Usage
usage /lmax.log /tRed_October_Usage
/Ufile The file to which the usage graph will be written. The
.BBS extension is not automatically added.
/V Verbose diagnostic mode. Prints out each log entry that
triggers a starting or ending time. Prints the elapsed
time between each start/end pair. Not recommended for
normal use.
/Vcount Volume. If verbose mode is not turned on a dot is
printed every time a new usage entry is found. Setting
'count' to zero turns off the dots. Setting 'count' to
any other number divides down the number of dots by a
factor of 'count'. (For example: /V3 produces one dot
for every three usage entries.)
If you use WFC within Maximus pass USAGE the Maximus log. If you use
Binkley to answer the phone pass it the Binkley log. You can mix log
types if you have lines with different configurations.
As an example, lets say a system has two lines. One runs Binkley and
the runs Maximus with WFC. A new log is started every month. Here is
how to generate an overall usage graph:
usage /lbink01.log /lmax02.log /tOverall Usage
I also want to generate usage graphs for each line separately. I want to
reprocess the same logs for this purpose and I want to keep the history
data separate, so I specify different history and incremental files for
each:
usage /husage01.hst /iusage01.inc /lbink01.log /uusage01.bbs /tLine One
usage /husage02.hst /iusage02.inc /lmax02.log /uusage02.bbs /tLine Two
USAGE returns ERRORLEVEL 0 after a successful run. ERRORLEVEL 1 is
returned to indicate an error.
NOTICE: You may use, copy, and distribute this program freely as long
as you insure that both the executable and the documentation (.DOC)
files are included in the distribution package. The source code does
not need to be included. You may modify this program and document, so
long as reasonable credit is given to the original author if a
substantial portion of the original remains intact. The author is not
responsible for any losses which may occur either directly or indirectly
as a result of using this program.
HISTORY:
Version 2.4 (02/04/93) -- Added three new default Binkley search
strings thanks to Walter Anderson.
Version 2.3 (11/10/92) -- Modified the /I file format (again!) so that
file contents are checked against stored
positions. This eliminates the need to worry
about new files being shorter than old ones.
The default is now /IUSAGE.INC. /I with no
parameter will turn off this feature. /H now
defaults to /HUSAGE.HST. /H with no
parameter will turn history off.
Version 2.2 (11/07/92) -- Modified /I file format so that active usage
can be tracked across log boundaries. This
should keep your maintenance period from
showing up as a blank spot on the graph. A
CVTINC utility is included to convert 2.1
format incremental files to the 2.2 format.
Version 2.1 (11/04/92) -- Added the /I parameter to allow incremental
processing of log files. Added a new /V
switch option (volume) to limit screen
output.
Version 2.0 (11/03/92) -- Added the /H parameter to specify a
self-maintaining history file. Added the /S
parameter to specify a file of user-defined
search strings. Added the /D parameter to
specify how many days to include in the
graph. Improved the speed of elapsed time
calculations. Track event times in seconds
instead of minutes. Track total time by
seconds intsead of days. Cleaned up a lot of
messy code. Many of the improvements were
suggested and originally coded by Steve
Antonoff.
Verison 1.9 (10/28/92) -- Skipped.
Version 1.8 (8/28/92) -- Added the /M switch for military time.
Version 1.7 (8/25/92) -- Added the ability to pass multiple-word
parameters using spaces.
Version 1.6 (6/03/92) -- Adjusted graph to start at midnight.
Version 1.5 (5/29/92) -- Improved Binkley support. Added verbose
diagnostic mode.
Version 1.4 (5/27/92) -- Allow custom color selections.
Version 1.3 (5/26/92) -- Removed event and keyboard switches.
Added support for Binkley logs.
Added log file sharing (again).
Version 1.2 (5/14/92) -- Removed log file sharing due to compiler bug.
Version 1.1 (5/13/92) -- Added log file sharing.
Version 1.0 (4/22/92) -- Original release. Written in Borland C.
Large memory model
*/
#include <ctype.h>
#include <dos.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <share.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys\stat.h>
#include <sys\types.h>
#include <time.h>
#define VERSION "2.4"
#define DATE "02/04/93"
#define MAXLINE (512)
#define MAXPATH (128)
#define MAXTITLE (72)
#define MAXSTAMP (16)
#define MAXDATE (6)
#define MAXLOGS (16)
#define NUM_COLORS (10)
#define TOPROW 0
#define BOTTOMROW 22
#define LEFTCOL 4
#define RIGHTCOL 77
#define FULLHEIGHT 219
#define HALFHEIGHT 220
#define HORIZLINE 196
#define VERTLINE 179
#define TOPLEFT 201
#define TOPRIGHT 187
#define BOTTOMLEFT 200
#define BOTTOMRIGHT 188
#define DOUBLEHORIZ 205
#define DOUBLEVERT 186
#define DOT 249
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
#define DAYSECS (86400L)
#define COLS (72)
#define CDIV (DAYSECS/COLS)
typedef struct _strings
{
char *string;
int length;
struct _strings *next;
} strings;
typedef struct _histories
{
long firsts;
long lasts;
long uses[COLS];
long totals[COLS];
} histories;
typedef struct _increments
{
long lastpos;
char last[MAXSTAMP];
int online;
char start[MAXSTAMP];
int slen;
} increments;
enum
{
default_color=0,
title_color,
frame_color,
reference_text_color,
reference_lines_color,
hour_text_color,
hour_dots_color,
hour_lines_color,
data_bars_color,
enter_prompt_color
};
void append_strings(strings **link, char *string);
void stamps_to_columns(long ages, char *start, char *end, int year,
long *starts, long *ends, long *columns);
long stamp_to_secs(char *stamp, int year);
long date_to_secs(struct date *datep, struct time *timep);
int draw_graph(histories *h, char colors[], int military, char *usefile,
char *title);
char *search_strings[] =
{
"+BINK Ring",
"-BINK No Carrier",
"+BINK Processing node",
"-BINK End of WaZOO",
"-BINK End of FTS-0001 compatible session",
"-BINK End of connection attempt",
"+BINK Exit after receiving mail with errorlevel",
"+BINK Exit after compressed mail with errorlevel",
"+BINK Exit at start of event with errorlevel",
"+BINK Function key exit - errorlevel",
"+BINK Exit requested from keyboard",
"-BINK begin,",
"+MAX Event - Exiting with errorlevel",
"+MAX Exit by keyboard request",
"+MAX Connected at",
"-MAX Begin,",
""
};
int main(int argc, char *argv[])
{
char histfile[MAXPATH] = {"USAGE.HST"};
char incfile[MAXPATH] = {"USAGE.INC"};
char *logfile[MAXLOGS];
char searchfile[MAXPATH] = {""};
char title[MAXTITLE] = {""};
char usefile[MAXPATH] = {"USAGE.BBS"};
int military = FALSE;
int volume = 1;
int vloop;
char colors[NUM_COLORS] =
{
0x07, /* 0: default color */
0x0f, /* 1: title */
0x09, /* 2: frame */
0x0a, /* 3: reference text */
0x02, /* 4: reference lines */
0x0b, /* 5: hour text */
0x09, /* 6: hour dots */
0x03, /* 7: hour lines */
0x09, /* 8: data bars */
0x02 /* 9: enter prompt */
};
int comment_offset = 18;
strings *start_strings = NULL;
strings *end_strings = NULL;
strings *str;
FILE *logfile_fp;
FILE *searchfile_fp;
int histfile_fh;
int incfile_fh = -1L;
long incpos;
int found;
int logs = 0;
int curlog = 0;
long logpos;
char *comment;
histories h;
histories h_new;
histories h_old;
increments increment;
int days;
long ages = 0L;
long read_record = 0L;
long write_record = 0L;
struct date datep;
struct time timep;
char first[MAXSTAMP];
long starts;
long ends;
char param = '\0';
char line[MAXLINE];
char *ch;
int i, j, k;
/***********/
/* USAGE */
/***********/
printf("USAGE %s -- Copyright 1992 by Bob Quinlan (%s)\n\n", VERSION, DATE);
printf("Special thanks to Steve Antonoff for suggestions and code.\n\n");
/* Get current date */
getdate(&datep);
gettime(&timep);
/* Process switches */
for (i = 1; i < argc; i++)
{
/* Convert previous param to uppercase so single-pass switches will
not recognize it more than once */
param = toupper(param);
/* If new parameter set it to lower case for first pass */
if (argv[i][0] == '/')
param = tolower(argv[i][1]);
switch (param)
{
case 'c': /* color */
if (sscanf(argv[i]+2, "%x=%x", &j, &k) >= 2)
{
if ((j < NUM_COLORS) && (k <= 0xff))
{
colors[j] = k;
}
else
{
fprintf(stderr, "USAGE: Illegal value in %s\n",
argv[i]);
}
}
else
{
fprintf(stderr, "USAGE: Illegal format in %s\n", argv[i]);
}
break;
case 'd': /* days */
days = atoi(argv[i]+2);
/* Update ages */
ages = date_to_secs(&datep, &timep)-(days*DAYSECS);
break;
case 'h': /* history file */
strncpy(histfile, argv[i]+2, MAXPATH);
break;
case 'i': /* incremental log file */
strncpy(incfile, argv[i]+2, MAXPATH);
break;
case 'l': /* log file */
if (logs < MAXLOGS)
{
logfile[logs++] = argv[i]+2;
}
else
{
fprintf(stderr, "USAGE: %s exceeds log limit\n", argv[i]+2);
}
break;
case 'm': /* military time */
military = TRUE;
break;
case 's': /* search string file */
strncpy(searchfile, argv[i]+2, MAXPATH);
break;
case 'T': /* title: append additional words */
strncat(title, " ", MAXTITLE);
/* Fall through to next case! */
case 't': /* title */
strncat(title, argv[i]+((islower(param) != 0)*2), MAXTITLE);
while ((ch = strchr(title, '_')) != NULL)
{
*ch = ' ';
}
break;
case 'u': /* use file */
strncpy(usefile, argv[i]+2, MAXPATH);
break;
case 'v': /* verbose and volume */
if (argv[i][2] == '\0')
{
volume = -1;
}
else
{
volume = atoi(argv[i]+2);
if (volume < 0)
{
volume = 0;
}
}
break;
default:
fprintf(stderr, "USAGE: Unknown switch: %s\n", argv[i]);
break;
}
}
/* Read in search file or defaults */
if (searchfile[0] != '\0')
{
if ((searchfile_fp = _fsopen(searchfile, "rt", SH_DENYNO)) == NULL)
{
fprintf(stderr, "USAGE: Unable to open %s\n", searchfile);
exit(1);
}
while (fgets(line, MAXLINE, searchfile_fp) != NULL)
{
ch = strpbrk(line, "\r\n");
*ch = '\0';
if (line[0] == '+')
{
append_strings(&start_strings, line+1);
}
else if (line[0] == '-')
{
append_strings(&end_strings, line+1);
}
else
{
fprintf(stderr, "USAGE: Bad search string \"%s\"\n", line);
}
}
fclose(searchfile_fp);
}
else
/* Supply default search strings */
{
for (i = 0; search_strings[i][0] != '\0'; i++)
{
if (search_strings[i][0] == '+')
{
append_strings(&start_strings, search_strings[i]+1);
}
else
{
append_strings(&end_strings, search_strings[i]+1);
}
}
}
/* If not defined supply defaults */
if (*title == '\0')
strcpy(title, "SYSTEM USAGE");
if (!logs)
logfile[logs++] = "BINK.LOG";
/* Initialize history record */
memset(&h, 0, sizeof(histories));
/* Open incremental log file */
if (incfile[0])
{
if ((incfile_fh = sopen(incfile, O_RDWR | O_CREAT | O_BINARY | O_DENYALL,
SH_DENYRW, S_IREAD | S_IWRITE)) == -1)
{
fprintf(stderr, "USAGE: Unable to open %s\n", incfile);
exit(1);
}
}
/* Process all log files */
while ((logfile_fp = _fsopen(logfile[curlog], "rt", SH_DENYNO)) != NULL)
{
printf("Processing %s ", logfile[curlog]);
memset(&increment, 0, sizeof(increments));
first[0] = '\0';
if (incfile[0])
{
found = FALSE;
lseek(incfile_fh, 0L, SEEK_SET);
do
{
/* Store postion for later update */
incpos = tell(incfile_fh);
/* Read in the incremental data */
if (read(incfile_fh, &increment, sizeof(increments)) <
sizeof(increments))
{
break;
}
if (read(incfile_fh, line, increment.slen) < increment.slen)
{
break;
}
/* Check to see if the log name matches this record */
if (stricmp(logfile[curlog], line) == 0)
{
/* Reposition the log file */
if (increment.lastpos >
filelength(fileno(logfile_fp)+MAXSTAMP))
{
fseek(logfile_fp, increment.lastpos, SEEK_SET);
fgets(line, MAXLINE, logfile_fp);
/* The override for a null last string is to
simplify conversion from older file formats */
if ((increment.last[0]) &&
(strncmpi(line+2, increment.last, MAXSTAMP-1) != 0))
{
fseek(logfile_fp, 0L, SEEK_SET);
}
}
/* Set the first date back to where we left off */
if (increment.online)
{
strcpy(first, increment.start);
}
/* Keep track of the record match */
found = TRUE;
}
} while (!found);
}
vloop = -1;
while (logpos = ftell(logfile_fp), fgets(line, MAXLINE, logfile_fp) !=
NULL)
{
/* Ignore blank lines and abnormal lines */
if ((line[0] != '\r') && (atoi(line+2) > 0) && (line[11] == ':') &&
(line[14] == ':'))
{
/* Keep track of the current datestamp */
strlwr(line);
strncpy(increment.last, line+2, MAXSTAMP-1);
increment.last[MAXSTAMP-1] = '\0';
increment.lastpos = logpos;
/* Store the first datestamp */
if (first[0] == '\0')
{
strcpy(first, increment.last);
}
/* Get the comment section of the entry */
comment = line+comment_offset;
/**********************************************************
If already processing usage, look for either an end
string OR a start string
************************************************************/
if (increment.online)
{
str = end_strings;
while (str != NULL)
{
if (strncmp(comment, str->string, str->length) == 0)
{
stamps_to_columns(ages, increment.start,
increment.last, datep.da_year, &starts, &ends,
h.uses);
/* Print line and total for verbose mode */
if (volume == -1)
{
printf(
"%s---------------------->elapsed: %ld seconds\n",
line, ends-starts);
}
else if (volume)
{
if ((vloop = (vloop+1)%volume) == 0)
{
printf(".");
}
}
increment.online = FALSE;
break;
}
str = str->next;
}
}
if (increment.online == FALSE)
{
/* If looking for new usage... */
str = start_strings;
while (str != NULL)
{
if (strncmp(comment, str->string, str->length) == 0)
{
strcpy(increment.start, increment.last);
increment.online = TRUE;
if (volume == -1)
{
printf("%s", line);
}
break;
}
str = str->next;
}
}
}
}
printf("\n");
/* Update incremental log information */
if (incfile[0])
{
if (found)
{
/* Update the existing incremental log entry */
lseek(incfile_fh, incpos, SEEK_SET);
write(incfile_fh, &increment, sizeof(increments));
}
else
{
/* Append a new incremental log entry */
increment.slen = strlen(logfile[curlog])+1;
lseek(incfile_fh, 0L, SEEK_END);
write(incfile_fh, &increment, sizeof(increments));
write(incfile_fh, logfile[curlog], increment.slen);
}
}
/* Close log file */
fclose(logfile_fp);
/* Update total time array */
if (increment.online)
{
stamps_to_columns(ages, first, increment.start, datep.da_year,
&starts, &ends, h.totals);
}
else
{
stamps_to_columns(ages, first, increment.last, datep.da_year,
&starts, &ends, h.totals);
}
if ((h.firsts > starts) || (h.firsts == 0))
{
h.firsts = starts;
}
if ((h.lasts < ends) || (h.lasts == 0))
{
h.lasts = ends;
}
/* Print line and total for verbose mode */
if (volume == -1)
{
printf("======================>total: %ld seconds\n", ends-starts);
}
/* Check for last log processed */
if (++curlog >= logs)
break;
}
/* Check for finish before last log processed */
if (curlog < logs)
{
fprintf(stderr, "USAGE: Unable to open the log file: %s\n",
logfile[curlog]);
exit(1);
}
/* Close the incremental log file */
close(incfile_fh);
/* Save new data for later writing to the history file */
memcpy(&h_new, &h, sizeof(histories));
/* Merge in history data */
if (histfile[0] != '\0')
{
if ((histfile_fh = sopen(histfile, O_RDWR | O_CREAT | O_BINARY |
O_DENYALL, SH_DENYRW, S_IREAD | S_IWRITE)) == -1)
{
fprintf(stderr, "USAGE: Unable to open %s\n", histfile);
}
else
{
/* Read all original records */
while (read(histfile_fh, &h_old, sizeof(histories)) >= sizeof(histories))
{
/* Process records newer than the age cutoff */
if (ages <= h_old.lasts)
{
/* Integrate data from saved records */
h.firsts = min(h.firsts, h_old.firsts);
h.lasts = max(h.lasts, h_old.lasts);
for (i = 0; i < COLS; i++)
{
h.uses[i] += h_old.uses[i];
h.totals[i] += h_old.totals[i];
}
/* Write old records into new positions when needed */
if (read_record != write_record)
{
lseek(histfile_fh, write_record, SEEK_SET);
write(histfile_fh, &h_old, sizeof(histories));
lseek(histfile_fh, read_record+sizeof(histories),
SEEK_SET);
}
write_record += sizeof(histories);
}
read_record += sizeof(histories);
}
/* Append the new history data */
lseek(histfile_fh, write_record, SEEK_SET);
write(histfile_fh, &h_new, sizeof(histories));
write_record += sizeof(histories);
/* Update the file size if the new is smaller than the old */
if (read_record > write_record)
{
chsize(histfile_fh, write_record);
}
close(histfile_fh);
}
}
draw_graph(&h, colors, military, usefile, title);
return 0;
}
void append_strings(strings **link, char *string)
{
strings **tmp;
/* Find the end of the linked list */
tmp = link;
while (*tmp != NULL)
{
tmp = &((*tmp)->next);
}
/* Allocate a new record */
*tmp = (strings *)malloc(sizeof(strings));
/* Allocate and store the string */
(*tmp)->string = malloc(strlen(string)+1);
strcpy((*tmp)->string, string);
/* Convert the string to lower case */
strlwr((*tmp)->string);
/* Store the length of the string */
(*tmp)->length = strlen(string);
/* Initialize the next pointer */
(*tmp)->next = NULL;
}
void stamps_to_columns(long ages, char *start, char *end, int year,
long *starts, long *ends, long *columns)
{
long s;
long e;
long partial;
int i;
*starts = stamp_to_secs(start, year);
*ends = stamp_to_secs(end, year);
if (*ends < *starts)
{
if ((*starts)-(*ends) < DAYSECS*90)
{
*starts = (*ends)-1;
}
else
{
*starts = stamp_to_secs(start, year-1);
}
}
if (*ends <= ages)
{
*starts = 0L;
*ends = 0L;
return;
}
if (*starts < ages)
{
*starts = ages;
}
/* Normalize to the nearest day */
partial = ((*starts)/DAYSECS)*DAYSECS;
s = (*starts)-partial;
e = (*ends)-partial;
/* Increment wraparound totals for each column */
partial = ((e-s)/DAYSECS)*CDIV;
if (partial)
{
for (i = 0; i < COLS; i++)
{
columns[i] += partial;
}
}
e -= ((e-s)/DAYSECS)*DAYSECS;
/* Increment non-wrapped totals */
for (i = (int)(s/CDIV); i <= (int)(e/CDIV); i++)
{
columns[i%COLS] += min(e, (i+1)*CDIV) - max(s, i*CDIV);
}
}
long stamp_to_secs(char *stamp, int year)
{
struct tm tmstamp;
char month[12][3] = {
"jan",
"feb",
"mar",
"apr",
"may",
"jun",
"jul",
"aug",
"sep",
"oct",
"nov",
"dec"
};
timezone = 0L;
daylight = 0;
memset(&tmstamp, 0, sizeof(struct tm));
tmstamp.tm_year = year-1900;
for (tmstamp.tm_mon = 0; tmstamp.tm_mon < 12; tmstamp.tm_mon++)
{
if (strncmpi(stamp+3, month[tmstamp.tm_mon], 3) == 0)
{
break;
}
}
tmstamp.tm_mday = atoi(stamp);
tmstamp.tm_hour = atoi(stamp+7);
tmstamp.tm_min = atoi(stamp+10);
tmstamp.tm_sec = atoi(stamp+13);
return mktime(&tmstamp);
}
long date_to_secs(struct date *datep, struct time *timep)
{
struct tm tmstamp;
timezone = 0L;
daylight = 0;
memset(&tmstamp, 0, sizeof(struct tm));
tmstamp.tm_year = datep->da_year-1900;
tmstamp.tm_mon = datep->da_mon-1;
tmstamp.tm_mday = datep->da_day;
tmstamp.tm_hour = timep->ti_hour;
tmstamp.tm_min = timep->ti_min;
tmstamp.tm_sec = timep->ti_sec;
return mktime(&tmstamp);
}
int draw_graph(histories *h, char colors[], int military, char *usefile,
char *title)
{
int max_rows;
int numrows;
int numcols;
int height[COLS];
char out[BOTTOMROW - TOPROW][RIGHTCOL + 1];
char color[BOTTOMROW - TOPROW][RIGHTCOL + 1];
char curcolor = 0;
long total_time = 0L;
int i,j,k;
FILE *usefile_fp;
numrows = BOTTOMROW-TOPROW;
numcols = RIGHTCOL+1;
/* Initialize virtual screen */
for (i=0; i<numrows; i++)
for (j=0; j<numcols; j++)
{
out[i][j] = ' ';
color[i][j] = colors[default_color];
}
/* Draw frame */
for (i=RIGHTCOL-COLS; i<RIGHTCOL; i++)
{
out[BOTTOMROW-2][i] = out[TOPROW][i] = DOUBLEHORIZ;
color[BOTTOMROW-2][i] = color[TOPROW][i] = colors[frame_color];
}
for (i=1; i<BOTTOMROW-2; i++)
{
out[i][RIGHTCOL] = out[i][RIGHTCOL-1-COLS] = DOUBLEVERT;
color[i][RIGHTCOL] = color[i][RIGHTCOL-1-COLS] = colors[frame_color];
}
/* Draw corners */
out[TOPROW][LEFTCOL] = TOPLEFT;
out[TOPROW][RIGHTCOL] = TOPRIGHT;
out[BOTTOMROW-2][LEFTCOL] = BOTTOMLEFT;
out[BOTTOMROW-2][RIGHTCOL] = BOTTOMRIGHT;
color[BOTTOMROW-2][RIGHTCOL] =
color[BOTTOMROW-2][LEFTCOL] =
color[TOPROW][RIGHTCOL] =
color[TOPROW][LEFTCOL] = colors[frame_color];
/* Draw 20% references */
for (i=1; i<5; i++)
{
/* Draw reference text */
out[i*4][RIGHTCOL-4-COLS] = ((5-i)*2)+'0';
out[i*4][RIGHTCOL-3-COLS] = '0';
out[i*4][RIGHTCOL-2-COLS] = '%';
color[i*4][RIGHTCOL-4-COLS] =
color[i*4][RIGHTCOL-3-COLS] =
color[i*4][RIGHTCOL-2-COLS] = colors[reference_text_color];
/* Draw reference lines */
for (j=RIGHTCOL-COLS; j<RIGHTCOL; j++)
{
out[i*4][j] = HORIZLINE;
color[i*4][j] = colors[reference_lines_color];
}
}
/* Draw hour references */
for (i=0; i<24; i++)
{
if (i%2)
{
/* Draw dots for odd hours */
out[BOTTOMROW-1][RIGHTCOL-COLS+(COLS/24*i)] = DOT;
color[BOTTOMROW-1][RIGHTCOL-COLS+(COLS/24*i)] =
colors[hour_dots_color];
}
else
{
/* Draw numbers for even hours */
k = (military) ? i : ((i+11)%12)+1;
/* Check for one or two digits */
if (k > 9)
{
out[BOTTOMROW-1][RIGHTCOL-COLS+(COLS/24*i)] = (k/10)+'0';
out[BOTTOMROW-1][RIGHTCOL+1-COLS+(COLS/24*i)] = (k%10)+'0';
color[BOTTOMROW-1][RIGHTCOL-COLS+(COLS/24*i)] =
color[BOTTOMROW-1][RIGHTCOL+1-COLS+(COLS/24*i)] =
colors[hour_text_color];
}
else
{
out[BOTTOMROW-1][RIGHTCOL-COLS+(COLS/24*i)] = (k%10)+'0';
color[BOTTOMROW-1][RIGHTCOL-COLS+(COLS/24*i)] =
colors[hour_text_color];
}
}
/* Draw hour lines */
if ((i%4 == 0) && (i > 0))
for (j=1; j<BOTTOMROW-2; j++)
{
out[j][RIGHTCOL-COLS+(COLS/24*i)] = VERTLINE;
color[j][RIGHTCOL-COLS+(COLS/24*i)] = colors[hour_lines_color];
}
}
/* Graph data */
max_rows = ((BOTTOMROW - TOPROW - 2 - 1) * 2) + 1;
for (i=0; i<COLS; i++)
{
/* Divide use counts by total time (for all logs) and minutes per column */
if (h->totals[i])
{
height[i] = (int)(h->uses[i]*(long)(max_rows)/h->totals[i]);
}
else
{
height[i] = 0;
}
/* Accumulate total time */
total_time += h->totals[i];
/* Clip to maximum height (just in case) */
if (height[i] > max_rows - 1)
height[i] = max_rows-1;
j = 0;
while (j*2 < height[i])
{
/* 219 = full height block, 220 = half height block */
out[BOTTOMROW-2-1-j][RIGHTCOL-COLS+i] =
(j*2+1 < height[i] ? FULLHEIGHT : HALFHEIGHT);
color[BOTTOMROW-2-1-j][RIGHTCOL-COLS+i] = colors[data_bars_color];
j++;
}
}
/* Open use (output) file */
if ((usefile_fp = _fsopen(usefile, "w+b", SH_DENYRW)) == NULL)
{
fprintf(stderr, "USAGE: Unable to open %s\n", usefile);
exit(1);
}
/* Write [cls] and [moreoff] tokens */
fprintf(usefile_fp, "\x0c\x0b");
/* Write centered title */
fprintf(usefile_fp, "\x16\x01%c ", colors[title_color]);
j = 39-(strlen(title))/2;
for (i = 0; i < j; i++)
putc(' ', usefile_fp);
fprintf(usefile_fp, "%s\r\n", title);
/* Add a switch to enable this option?
fprintf(usefile_fp, "%s (%d day%s)\r\n", title,
(int)((h->lasts-h->firsts)/DAYSECS+1),
((h->lasts-h->firsts)/DAYSECS+1) > 1 ? "s" : "");
*/
/* Write data */
for (i=0; i<BOTTOMROW-TOPROW; i++)
{
for (j=0; j<RIGHTCOL+1; j++)
{
/* Write color change tokens only when necessary */
if (color[i][j] != curcolor)
{
curcolor = color[i][j];
fprintf(usefile_fp, "\x16\x01%c", curcolor);
}
putc(out[i][j], usefile_fp);
}
fprintf(usefile_fp, "\r\n");
}
/* Add color and [enter] tokens */
fprintf(usefile_fp, "\x16\x01%c \x01",
colors[enter_prompt_color]);
/* Close use file */
fclose(usefile_fp);
return 0;
}