C tutorial Chapter 14 - Example Programs [End]
Chapter 14 - Example Programs
WHY THIS CHAPTER?
Although every program in this tutorial has been a
complete program, each one has also been a very small
program intended to teach you some principle of programming
in C. It would do you a disservice to leave you at that
point without introducing you to a few larger programs to
illustrate how to put together the constructs you have
learned to create a major program. This chapter contains
four programs of increasing complexity, each designed to
take you into a higher plateau of programming, and each
designed to be useful to you in some way.
DOSEX will illustrate how to make DOS system calls and
will teach you, through self-study, how the system responds
to the keyboard. WHATNEXT reads commands input on the
command line and will aid you in setting up a variable batch
file, one that requests an operator input and responds to
the input by branching to a different part of the batch
file.
LIST is the source code for the program you used to
print out the C source files when you began studying C with
the aid of this tutorial. Finally we come to VC, the Visual
Calculator, which you should find to be a useful program
even if you don't study its source code. VC uses most of
the programming techniques we have studied in this course
and a few that we never even mentioned such as separately
compiled subroutines.
We will take a look at the example programs one at a
time but without a complete explanation of any of them
because you have been studying C for some time now and
should be able to read and understand most of these programs
on your own.
/******************************************************************/
/* This is an example program to illustrate how to; */
/* 1. Get the time and date from DOS */
/* 2. Set the cursor to any position on the screen */
/* 3. Read characters from the keyboard and display their codes */
/* 4. How to scroll a window up on the monitor */
/* 5. Format a program for ease of reading and understanding */
/* 6. How to do proper prototyping */
/******************************************************************/
void draw_box(void);
void disp_char(int inchar);
void get_time(int *hour,int *minute,int *second);
void disp_time_date(void);
void pos_cursor(char row,char column);
void scroll_window(void);
#include "stdio.h"
#include "dos.h"
#include "conio.h"
int main()
{
int hour, minute, sec, old_sec;
int character;
draw_box(); /* draw the boxes around the fields */
old_sec = 0; /* this variable stores the old time
so we can look for a change */
do {
if (kbhit()) { /* has a key been hit? */
character = getch(); /* read it in */
disp_char(character); /* display it */
}
get_time(&hour,&minute,&sec); /* get the time of day */
if (sec != old_sec) { /* if it has changed, */
disp_time_date(); /* update the display */
old_sec = sec; /* save new time */
}
} while (character != 'Q'); /* Quit when a Q is found */
pos_cursor(0,0); /* put cursor at top of screen */
}
/* **************************************************** drawbox */
/* This routine draws a box on the screen. The keys hit, and */
/* the time and date are displayed in these boxes. There is */
/* nothing special about these boxes, they are simply output */
/* using the printf function. */
/* ************************************************************ */
void draw_box(void)
{
int index;
char line[81];
for (index = 0;index < 80;index++) /* three blank rows */
line[index] = ' ';
line[80] = NULL; /* end of string */
for (index = 0;index < 3;index++)
printf("%s",line);
line[8] = 201; /* draw top line of box */
for (index = 9;index < 70;index++)
line[index] = 205;
line[70] = 187;
printf("%s",line);
line[8] = 186; /* draw sides of large box */
for (index = 9;index < 70;index++)
line[index] = ' ';
line[70] = 186;
for (index = 0;index < 15;index++)
printf("%s",line);
line[8] = 204; /* draw line between boxes */
for (index = 9;index < 70;index++)
line[index] = 205;
line[70] = 185;
printf("%s",line);
line[8] = 186; /* sides for time/date box */
for (index = 9;index < 70;index++)
line[index] = ' ';
line[70] = 186;
printf("%s",line);
line[8] = 200; /* bottom line of the box */
for (index = 9;index < 70;index++)
line[index] = 205;
line[70] = 188;
printf("%s",line);
for (index = 0;index < 80;index++) /* three blank rows */
line[index] = ' ';
for (index = 0;index < 3;index++)
printf("%s",line);
}
/* ************************************************** disp_char */
/* This routine displays the characters hit on the monitor. If */
/* the first character is a zero, a special character has been */
/* hit, and the zero is displayed. The next character is read, */
/* and it is displayed on the monitor. */
/* ************************************************************ */
void disp_char(int inchar)
{
scroll_window();
pos_cursor(17,15); /* position of message on screen */
if(inchar == 0) {
printf(" 00 "); /* a special character was hit */
inchar = getch(); /* get the next part of it */
switch (inchar) {
case 59 :
case 60 :
case 61 :
case 62 :
case 63 : /* these are the function keys */
case 64 :
case 65 :
case 66 :
case 67 :
case 68 : printf("%4d Function key F%d\n",inchar,inchar-58);
break;
case 94 :
case 95 :
case 96 :
case 97 :
case 98 : /* these are the ctrl-function keys */
case 99 :
case 100 :
case 101 :
case 102 :
case 103 : printf("%4d Function key Ctrl-F%d\n",inchar,
inchar-93);
break;
case 84 :
case 85 :
case 86 :
case 87 : /* these are the upper-function keys */
case 88 :
case 89 :
case 90 :
case 91 :
case 92 :
case 93 : printf("%4d Function key Upper-F%d\n",inchar,
inchar-83);
break;
case 104 :
case 105 :
case 106 :
case 107 :
case 108 : /* these are the alt-function keys */
case 109 :
case 110 :
case 111 :
case 112 :
case 113 : printf("%4d Function key Alt-F%d\n",inchar,
inchar-103);
break;
default : printf("%4d Special key hit\n",inchar);
}
} else /* a regular character was hit */
printf(" %4d (%c) Character Hit.\n",inchar,inchar);
pos_cursor(25,1); /* hide the cursor on the 26th line */
}
/* *************************************************** get_time */
/* This routine calls the DOS function call for time of day. It */
/* returns the time of day to the calling program in the three */
/* pointers used in the call. */
/* ************************************************************ */
void get_time(int *hour,int *minute,int *second)
{
union REGS inregs;
union REGS outregs;
inregs.h.ah = 44; /* Hex 2C - Get current time */
int86(0x21,&inregs,&outregs);
*hour = outregs.h.ch;
*minute = outregs.h.cl;
*second = outregs.h.dh;
}
/* ********************************************* disp_time_date */
/* This routine displays the time and date on the monitor in a */
/* fixed position. It gets the time from the get_time function, */
/* and gets the date from its own built in DOS call. Good */
/* programming practice would move the date to another function */
/* but this is an illustrative example to display methods of */
/* doing things. This routine also calls the cursor positioning */
/* function to put the time and date where we want them. */
/* ************************************************************ */
void disp_time_date(void)
{
int hour, minute, second;
union REGS inregs;
union REGS outregs;
pos_cursor(19,19); /* position the cursor for date and time */
inregs.h.ah = 42; /* hex 2A - What is the date? */
int86(0x21,&inregs,&outregs); /* interrupt 21 */
printf("Date = %2d/%2d/%2d ",
outregs.h.dh, /* month - 1 to 12 */
outregs.h.dl, /* day - 1 to 31 */
outregs.x.cx); /* year - 1980 to 2099 */
get_time(&hour, &minute, &second);
printf("Time = %2d:%2d:%2d\n",hour, minute, second);
pos_cursor(25,1); /* hide the cursor on the 26th line */
}
/* ************************************************* pos_cursor */
/* This routine positions the cursor at the requested row and */
/* column. The upper left corner is row 0 and column 0 */
/* ************************************************************ */
void pos_cursor(char row,char column)
{
union REGS inregs;
union REGS outregs;
inregs.h.ah = 2; /* service 2 - position the cursor */
inregs.h.dh = row;
inregs.h.dl = column;
inregs.h.bh = 0;
int86(0x10,&inregs,&outregs); /* interrupt 10 */
}
/* ********************************************** scroll_window */
/* This routine scrolls all of the material in the key hit */
/* window up one space leaving room for another entry. */
/* ************************************************************ */
void scroll_window(void)
{
union REGS inregs;
union REGS outregs;
inregs.h.ah = 6; /* service 6 - scroll window */
inregs.h.al = 1; /* number of lines to scroll */
inregs.h.ch = 3; /* top row of window */
inregs.h.cl = 9; /* left column of window */
inregs.h.dh = 17; /* bottom row of window */
inregs.h.dl = 69; /* right column of window */
inregs.h.bh = 7; /* attribute of blank line */
int86(0x10,&inregs,&outregs); /* interrupt 10 */
}
DOSEX.C - The DOS Example Program
The copy of DOS that you received with your IBM-PC or
compatible has about 80 internal DOS calls that you can use
as a programmer to control your peripheral devices and read
information or status from them. Some of the earlier IBM
DOS manuals, DOS 2.0 and earlier, have these calls listed in
the back of the manual along with how to use them. Most of
the manuals supplied with compatible computers make no
mention of these calls even though they are extremely
useful. These calls can be accessed from nearly any
programming language but they do require some initial study
to learn how to use them. This program is intended to aid
you in this study.
Page 99
Chapter 14 - Example Programs
Display the program on your monitor or print it out for
reference. It is merely a loop watching for a keyboard
input or a change in the time. If either happens, it reacts
accordingly. In line 32, the function "kbhit()" returns a
value of 1 if a key has been hit but not yet read from the
input buffer by the program.
Look at the function named "get_time" for an example of
a DOS call. An interrupt 21(hex) is called after setting
the AH register to 2C(hex) = 44(decimal). The time is
returned in the CH, CL, and DH registers. Refer to the DOS
call definitions in your copy of DOS. If the definitions
are not included there, Peter Nortons book, "rogrammers
Guide to the IBM PC" is recommended as a good reference
manual for these calls and many other programming
techniques. Your compiler may have a built in function to
do this. If you read your documentation, you will probably
find many useful functions available with your compiler that
are included as a convenience for you by your compiler
writer.
Another useful function is the "pos_cursor()" function
that positions the cursor anywhere on the monitor that you
desire by using a DOS interrupt. In this case, the
interrupt used is 10(hex) which is the general monitor
interrupt. This particular service is number 2 of about 10
different monitor services available. This function is
included here as another example to you.
The next function, service number 6 of interrupt
10(hex) is the window scroll service. It should be self
explanatory.
In this program, the cursor is positioned and some data
is output to the monitor, then the cursor is "hidden" by
moving it to line 26 which is not displayed. After you
compile and run the program, you will notice that the cursor
is not visible on the monitor. This is possible in any
program, but be sure to put the cursor in view before
returning to DOS because DOS does not like to have a
"hidden" cursor and may do some strange things.
Some time spent studying this program will be valuable
to you as it will reveal how the keyboard data is input to
the computer. Especially of importance is how the special
keys such as function keys, arrows, etc. are handled. Also
note that this program uses full prototype checking and is a
good example of how to use it. Since it also uses the
"modern" method of function definitions, it is a good
example of that also.
Page 100
Chapter 14 - Example Programs
/* *************************************************************** */
/* This program reads a series of words from the command line, */
/* and displays all but the last on the monitor. The last is a */
/* series of characters which are used as input comparisons. One */
/* character is read from the keyboard. If it is one of the */
/* characters in the comparison list, its number is returned to */
/* DOS as the errorlevel command. If the character does not exist */
/* in the list, a zero is returned. Example follows; */
/* */
/* WHATNEXT What model do you want? ALR%3T */
/* */
/* What model do you want? <---- displayed on monitor */
/* If key a or A is hit, errorlevel 1 is returned. */
/* If key l or L is hit, errorlevel 2 is returned. */
/* If key r or R is hit, errorlevel 3 is returned. */
/* If key % is hit, errorlevel 4 is returned. */
/* If key 3 is hit, errorlevel 5 is returned. */
/* If key t or T is hit, errorlevel 6 is returned. */
/* If any other key is hit, errorlevel 0 is returned. */
/* */
/* The question must be on one line. */
/* Up to nine different keys can be used. */
/* The errorlevel can be interpreted in a batchfile. */
/* *************************************************************** */
#include "stdio.h"
#include "ctype.h"
#include "conio.h"
#include "process.h"
main(int number,char *name[])
{
int index; /* a counter and incrementing variable */
int c; /* the character read in for comparison */
int code; /* the resulting errorlevel returned to */
char next_char; /* used for the comparison loop */
char *point; /* a dummy pointer used for convenience */
/* At least one group must be used for this */
/* filename, and one group used for the */
/* required fields, so less than three allows */
/* for no question. */
if (number < 3) {
printf("No question given on command line\n");
exit(0);
}
/* print out words 2 to n-1, the question */
number--;
for(index = 1;index < number;index++) {
printf("%s ",name[index]);
}
/* get the users response and make it uppercase */
c = getch();
printf("%c\n",c);
if (islower(c))
c = toupper(c);
point = name[number];/* point to the last pointer on the inputs */
code = 0;
index = 0;
do { /* search across allowed responses in last word */
next_char = *(point + index);
if (islower(next_char))
next_char = toupper(next_char); /* make it uppercase */
if(next_char == c) /* if a match is found */
code = index + 1; /* save the number of the match */
index++;
} while (*(point + index)); /* until NULL terminator found */
exit(code); /* return the errorcode to the system */
}
WHATNEXT.C - The Batch File Interrogator
This is an example of how to read the data on the
command line following the function call. Notice that there
are two variables listed within the parentheses following
the main() call. The first variable is a count of words in
the entire command line including the command itself and the
second variable is a pointer to an array of pointers
defining the actual words on the command line.
First the question on the command line, made up of some
number of words, is displayed on the monitor and the program
waits for the operator to hit a key. If the key hit is one
of those in the last "word" of the group of words on the
command line, the number of the character within the group
is returned to the program where it can be tested with the
"errorlevel" command in the batch file. You could use this
technique to create a variable AUTOEXEC.BAT file or any
other batch file can use this for a many way branch.
Compile and run this file with TEST.BAT for an example of
how it works in practice. You may find this technique
useful in one of your batch files and you will almost
certainly need to read in the command line parameters
someday.
An interesting alternative would be for you to write a
program named "WOULD.C" that would return a 1 if a "Y" or
"y" were typed and a zero if any other key were hit. Then
your batch file could have a line such as;
WOULD YOU LIKE TO USE THE ALTERNATIVE METHOD (Y/N)
Dos would use "WOULD" as the program name, ignore the
rest of the statement except for displaying it on the
screen. You would then respond to the question on the
monitor with a single keyhit. Your batch file would then
respond to the 1 or 0 returned and either run the
alternative part of the batch file or the primary part
whatever each part was.
WOULD YOU LIKE PRIMARY (Y/N)
IF ERRORLEVEL 1 GOTO PRIMARY
(secondary commands)
GOTO DONE
RIMARY
(primary commands)
ONE
Page 101
Chapter 14 - Example Programs
/* *************************************************************** */
/* This program will read in any text file and list it on the */
/* monitor with line numbers and with page numbers. */
/* *************************************************************** */
#include "stdio.h" /* standard I/O header file */
#include "io.h" /* file I/O prototypes */
void open_file(int no,char *name);
void open_print_file(void);
void print_a_line(void);
void top_of_page(void);
#define MAXCHARS 255 /* maximum size of a line */
FILE *file_point; /* pointer to file to be read */
FILE *print_file_point; /* pointer to pronter */
char oneline[256]; /* input string buffer area */
main(number,name)
int number; /* number of arguments on command line */
char *name[]; /* arguments on the command line */
{
char *c; /* variable to indicate end of file */
char *point;
point = name[1];
open_file(number,point); /* open the file to read and print */
open_print_file();
do {
c = fgets(oneline,MAXCHARS,file_point); /* read one line */
if (c != NULL)
print_a_line(); /* print the line */
} while (c != NULL); /* continue until EOF */
top_of_page(); /* move paper to top of page */
fclose(file_point); /* close read file */
fclose(print_file_point); /* close printer file */
}
LIST.C - The Program Lister
This program is actually composed of two files, LIST.C
and LISTF.C that must be separately compiled and linked
together with your linker. There is nothing new here and
you should have no trouble compiling and linking this
program by reading the documentation supplied with your
C compiler.
The only thing that is new in this program is the
inclusion of three "extern" variables in the LISTF.C
listing. The only purpose for this is to tie these global
variables to the main program and tell the compiler that
these are not new variables. The compiler will therefore
not generate any new storage space for them but simply use
their names during the compile process. At link time, the
linker will get their actual storage locations from the
LIST.OBJ file and use those locations for the variables in
the LISTF part of the memory map also. The variables of
those names in both files are therefore the same identical
variables and can be used just as any other global variables
could be used if both parts of the program were in one file.
/* *************************************************************** */
/* This module contains the functions called by the list.c program */
/* program. If this were a program to be used for some specific */
/* purpose, it would probablly not be wise to break it up into two */
/* separately compiled modules. It is only done here for purposes */
/* of illustration. It is a useful program. */
/* *************************************************************** */
#define MAXLINES 54 /* maximum number of lines per page */
#include "stdio.h" /* standard I/O header file */
#include "string.h" /* prototypes for strings */
void open_file(int no,char *name);
void open_print_file(void);
void print_a_line(void);
void header(void);
void top_of_page(void);
extern FILE *file_point; /* pointer to the file to be read */
extern FILE *print_file_point; /* pointer to the printer */
extern char oneline[]; /* input string buffer area */
char filename[15]; /* filename from header or prompt */
int line_number = 0; /* line number initialized to one */
int page_number = 1; /* page number initialized to one */
int lines_this_page = 0; /* lines on this page so far */
/* ***************************************************** open_file */
/* This function opens the input file named on the command line, */
/* if there was one defined. Otherwise, it requests a file name to */
/* open and opens the requested file. */
/* *************************************************************** */
void open_file(int no,char *name)
{
strcpy(filename,name); /* copy name for printing header */
file_point = NULL; /* if no name was given in command */
if (no == 2) { /* 2nd field in command is filename */
file_point = fopen(name,"r"); /* open requested file */
if (file_point == NULL) /* NULL if file doesn't exist */
printf("Filename on command line doesn't exist!\n");
}
do {
if (file_point == NULL) { /* no filename yet */
printf("Enter filename -> ");
scanf("%s",filename);
file_point = fopen(filename,"r"); /* open file */
if (file_point == NULL) /* NULL if file no exist */
printf("Filename doesn't exist, try again.\n");
}
} while (file_point == NULL); /* continue until good filename */
}
/* *********************************************** open_print_file */
/* This function opens the printer file to the standard printer. */
/* *************************************************************** */
void open_print_file(void)
{
print_file_point = fopen("RN","w"); /* open printer file */
}
/* ************************************************** print_a_line */
/* This routine prints a line of text and checks to see if there */
/* is room for another line on the page. If not, it starts a new */
/* page with a new header. This routine calls several other local */
/* routines. */
/* *************************************************************** */
void print_a_line(void)
{
int index;
header();
printf("%5d %s",line_number,oneline);
/* This prints a line of less than 72 chars */
if (strlen(oneline) < 72)
fprintf(print_file_point,"%5d %s",line_number,oneline);
/* This prints a line of 72 to 143 chars */
else if (strlen(oneline) < 144) {
fprintf(print_file_point,"%5d ",line_number);
for (index = 0;index < 72;index++)
fprintf(print_file_point,"%c",oneline[index]);
fprintf(print_file_point,"<\n ");
for (index = 72;index < strlen(oneline);index++)
fprintf(print_file_point,"%c",oneline[index]);
lines_this_page++;
}
/* This prints a line of 144 to 235 chars */
else if (strlen(oneline) < 235) {
fprintf(print_file_point,"%5d ",line_number);
for (index = 0;index < 72;index++)
fprintf(print_file_point,"%c",oneline[index]);
fprintf(print_file_point,"<\n ");
for (index = 72;index < 144;index++)
fprintf(print_file_point,"%c",oneline[index]);
fprintf(print_file_point,"<\n ");
for (index = 144;index < strlen(oneline);index++)
fprintf(print_file_point,"%c",oneline[index]);
lines_this_page += 2;
}
/* the following line outputs a newline if there is none
at the end of the last line */
if (oneline[strlen(oneline)-1] != '\n')
fprintf(print_file_point,"%c",'\n');
line_number++;
lines_this_page++;
}
/* ******************************************************** header */
/* This routine checks to see if a header needs to be printed. It */
/* also checks for the end of a page. and spaces the paper up. */
/* *************************************************************** */
void header(void)
{
int index;
/* first see if we are at the bottom of the page */
if (lines_this_page > MAXLINES) { /* space paper up for bottom */
for (index = lines_this_page;index < 61;index++)
fprintf(print_file_point,"\n");
lines_this_page = 0;
}
/* put a monitor header out only at the very beginning */
if (line_number == 0) { /* display monitor header */
printf(" Source file %s\n",filename);
line_number = 1;
}
/* check to see if we are at the top of the page either */
/* through starting a file, or following a bottom of page */
if (lines_this_page == 0) { /* top of every printer page */
fprintf(print_file_point,"\n\n\n ");
fprintf(print_file_point," Source file - %s ",filename);
fprintf(print_file_point," Page %d\n\n", page_number);
page_number++;
}
}
/* *************************************************** top_of_page */
/* This function spaces the paper to the top of the next page so */
/* that another call to this function will start correctly. This */
/* is used only at the end of a complete printout. */
/* *************************************************************** */
void top_of_page(void)
{
int index;
for (index = lines_this_page;index < 61;index++)
fprintf(print_file_point,"\n");
}
There is no reason why the variables couldn't have been
defined in the LISTF.C part of the program and declared as
"extern" in the LIST.C part. Some of the variables could
have been defined in one and some in the other. It is
merely a matter of personal taste. Carried to an extreme,
all of the variables could have been defined in a third file
and named "extern" in both of these files. The third file
would then be compiled and included in the linking process.
It would be to your advantage to compile, link, and run
this program to prepare you for the next program which is
composed of 6 separate files which must all work together.
/* VC.C VC.C VC.C VC.C VC.C VC.C VC.C
VISUAL CALCULATOR X X XXX
MAIN PROGRAM X X X X
X X X
July 1, 1987 X X X
X X X
X X X
X XXX
This program will evaluate single value expressions in a
manner similar to those evaluated by a hand-held calculator,
hence its name, the Visual Calculator. It was never intended
to be programmable, so no loop constructs are included in
its design. It is possible to write a series of statements,
store them in a file, and recall them while using them to
calculate with new values of input variables in the six
variable storage registers. The input variables can be
changed, and the entire series recalculated.
Although this is a potentially useful program in its own
right, it was originally written as an illustration of a
rather large C program. It is especially useful because
the student of C can run the program to determine its
operating characteristics, then study the code needed to
perform the various operations. For that reason, the entire
program is heavily commented. An actual production program
would probably not have as many comments as this example
but it would not be bad practice to comment all of your
programs to this extent.
*/
#include "ctype.h"
#include "stdio.h"
#include "string.h"
#include "conio.h"
#include "process.h"
#include "struct.def"
#include "defin.h"
struct vars allvars[12]; /* this is the main variable storage */
int varinuse = 0; /* which variable is being used now */
char inline[200]; /* input line area */
int col; /* used for searching across the input */
int errcode; /* error code number */
int colerr; /* column where error occurred */
int printit = 0; /* 1 = print a transcript */
int ignore; /* 1 = ignore calculations for line */
extern char strngout[]; /* output message area */
extern int valattr; /* value and variable attribute */
extern int helpattr; /* help box attribute */
FILE *prtfile; /* file pointers */
struct lines *top, *bot, *q, *p, *arrow, *trnsend;
/* *********************************************************** main */
/* This is the main control loop for the program. It initializes */
/* everything and reads statements until no errors are found. It */
/* continues reading until an F10 is detected in a subordinate */
/* function where control is returned to DOS */
main()
{
top = bot = q = p = arrow = trnsend = NULL;
monitor(); /* initialize video attributes */
initdata(&allvars[0]); /* initialize all data */
bkgndvid(); /* display video background - double lines */
valusvid(); /* display starting values of all variables */
strtrans("Welcome to the Visual Calculator - Version 1.10",0);
transout();
do{
poscurs(23,7);
printf(" input > ");
printf(" ");
do { /* repeat input until no errors */
readline(); /* get an input line */
errdis(" "); /* clear error msg */
parse(); /* parse the line */
if (errcode) errout();/* output error message */
} while (errcode);
if (ignore == 1)
strtrans(inline,0); /* store comment in transcript */
else
strtrans(inline,1); /* store "inline" in transcript */
transout();
} while (1); /* continuous loop */
}
/* ******************************************************* readline */
/* This function reads a line by inputting one character at a time */
/* and deciding what to do with it if it is a special character, or */
/* adding it to the input line if it is a special character. The */
/* routine takes care of such things as backspace, cursor movement */
/* and delete keys. The final result is a single line of text stored*/
/* in the buffer "inline" and the line displayed on the monitor. */
void readline(void)
{
int index;
int c,temp;
int row = 23,col = 17;
int attr;
if (errcode) { /* error recovery allow reenter */
index = colerr;
errcode = 0;
}
else { /* normal input routine */
index = 0;
for (temp = 0;temp < 80;temp++)
inline[temp] = 0; /* clear input buffer */
}
poscurs(row,col+index); /* starting location of cursor */
do { /* repeat this do loop until a return is hit */
while ((c = getch()) == EOF); /* get a keystroke */
if (c == 0) { /* a zero here says a special key was hit */
/* get the key and act on it as needed */
int spec;
spec = getch(); /* this is the special code found */
switch (spec) {
case 59 : helpm(); /* F1 - Help math */
transout();
break;
case 60 : helps(); /* F2 - Help system */
transout();
break;
case 61 : if (printit) { /* F3 - Print on/off */
printit = 0; /* print off */
fprintf(prtfile,"%s\n\n","rint off");
fclose(prtfile);
strcpy(strngout,"-----");
attr = helpattr;
} else {
prtfile = fopen("RN","w"); /* print on */
if (prtprblm()) {
errcode = 12; /* printer is not ready */
errout();
break;
}
printit = 1;
fprintf(prtfile,"%s\n","rint On");
strcpy(strngout,"rint");
attr = valattr;
}
strngdis(1,73,attr);
break;
case 62 : /* F4 - Mark transcript */
arrow->marked = (arrow->marked?0:1);
transout();
break;
case 63 : fileout(); /* F5 - Store transcript */
break;
case 64 : filein(); /* F6 - Retrieve trans */
errcode = 0;
break;
case 65 : /* F7 - */
break;
case 66 : /* F8 - */
break;
case 67 : /* F9 - Edit a line */
strcpy(inline,arrow->lineloc);
poscurs(23,17);
printf("%s",inline);
break;
case 68 : poscurs(23,17); /* F10 - Quit to DOS */
printf("Quit? (Y/N) ");
c = getch();
if ((c == 'Y') || (c == 'y')){
clrscrn();
exit(0);
}
poscurs(23,17);
printf(" ");
break;
case 75 : if (index) { /* left arrow */
index = index -1; /* back up cursor */
}
break;
case 77 : if (index < 65) { /* right arrow */
if (inline[index] == 0) /* zero found */
inline[index] = ' '; /* blank over 0 */
index = index + 1; /* cursor forward */
}
break;
case 72 : movarrow(-1); /* up arrow */
break;
case 80 : movarrow(1); /* down arrow */
break;
case 73 : movarrow(-8); /* page up */
break;
case 81 : movarrow(8); /* page down */
break;
case 71 : movarrow(-1000); /* home */
break;
case 79 : movarrow(1000); /* end */
break;
case 83 : temp = index; /* delete key */
/* move all characters left one space */
while (inline[temp]) {
inline[temp] = inline[temp+1];
putchar(inline[temp++]);
}
putchar(0); /* zero in last place */
break;
default : poscurs(15,5);
printf(" S%3d",spec);
}
poscurs(row,col+index); /* actually put cursor in position */
}
else { /* normal letter or char hit */
int curr,next;
if (islower(c)) c = toupper(c); /* convert to upper case */
if ((c >= '\40') && (c <= '\176')) { /* printable char */
poscurs(row,col+index);
putchar(c);
next = inline[index];
inline[index++] = c;
curr = index;
while((next != 0) && (curr <= 65)) { /* move remainder */
temp = next; /* line right */
next = inline[curr];
inline[curr++] = temp;
putchar(temp);
}
}
else {
if ((c == 8) && index){ /* backspace */
index--;
poscurs(row,col+index); /* back up cursor */
temp = index;
while (inline[temp]) {
inline[temp] = inline[temp+1];
putchar(inline[temp++]);
}
putchar(0);
}
}
poscurs(row,col+index);
}
if (c == 3) exit(0); /* ctrl-break, out to DOS */
} while (c != 13); /* newline found, line input is complete */
}
/* ********************************************************** parse */
/* This function does a lot of checking of the input line for */
/* logical errors in construction, then turns control over to the */
/* function "calcdata" for the actual calculations. */
void parse(void)
{
int index,parcol;
double newval;
char name[7];
varinuse = -1;
errcode = 0;
col = 0;
ignore = 1; /* ignore this line */
if (inline[0] == '#') { /* get list of variable names */
getnames();
return;
}
while (inline[col] == ' ') col++; /* ignore leading blanks */
if (inline[col] == '$') return; /* ignore a comment line */
if (inline[col] == 0) return; /* ignore a blank line */
ignore = 0; /* don't ignore this line */
name[0] = inline[col++]; /* find variable name */
index = 1;
while ((((inline[col] >= 'A') && (inline[col] <= 'Z')) ||
((inline[col] >= '0') && (inline[col] <= '9'))) &&
(index <= 5)) { /* continue var or function name */
name[index++] = inline[col++];
}
name[index] = 0; /* name found */
for (index = 0;index < 12;index++) {
if ((strcmp(name,allvars[index].varname)) == 0)
varinuse = index; /* variable name found */
}
if (varinuse < 0) errchk(3); /* unknown variable name */
while (inline[col] == ' ') col++; /* ignore leading blanks */
if (inline[col] == '=') col++;
else errchk(8); /* missing equal sign */
parcol = 0; /* now check for correct parenthesis matchup */
index = col;
do {
if (inline[col] == '(') parcol++;
if (inline[col++] == ')') parcol--;
if (parcol < 0) errchk(1); /* paren count went negative */
} while (inline[col]);
if (parcol) errchk(2); /* left over parentheses */
col = index;
calcdata(&newval); /* now go evaluate the full expression */
if (errcode == 0) { /* don't update value if error found */
allvars[varinuse].value = newval;
disnew(varinuse); /* display the changed value */
}
}
/* ********************************************************* errout */
/* This is the function that displays the blinking error message on */
/* the monitor. Note the extra errors for expansion of the table. */
void errout(void)
{
switch (errcode) {
case 1 : errdis("extra right parentheses ");
break;
case 2 : errdis("missing right parentheses");
break;
case 3 : errdis("unknown variable name ");
break;
case 4 : errdis("invalid math operator ");
break;
case 5 : errdis("negative value for SQRT ");
break;
case 6 : errdis("function not found ");
break;
case 7 : errdis("negative value for LOG ");
break;
case 8 : errdis("equal sign missing ");
break;
case 9 : errdis("invalid data field ");
break;
case 10 : errdis("division by zero ");
break;
case 11 : errdis("File doesn't exist ");
break;
case 12 : errdis("rinter not ready ");
break;
case 13 : errdis("Out of memory ");
break;
case 14 : errdis("Dash expected ");
break;
case 15 : errdis("Invalid format code ");
break;
case 16 : errdis("Neg value for FACTORIAL ");
break;
case 17 : errdis("Err 17 ");
break;
default : errdis("unknown error ");
poscurs(21,70);
printf("%d",errcode);
}
poscurs(23,12+colerr);
}
VC.C - The Visual Calculator
This program finally ties nearly everything together
because it uses nearly every concept covered in the entire
tutorial. It is so big that I will not even try to cover
the finer points of its operation. Only a few of the more
important points will be discussed.
The first thing you should do is go through the
tutorial for VC included in the file VC.DOC. There are
several dozen steps for you to execute, with each step
illustrating some aspect of the Visual Calculator. You will
get a good feel for what it is capable of doing and make
your study of the source code very profitable. In addition,
Page 102
Chapter 14 - Example Programs
you will probably find many ways to use the Visual
Calculator to solve problems involving calculations where
the simplicity of the problem at hand does not warrant
writing a program.
Notice that the structure definitions, used in all of
the separate parts of the program, are defined in the file
STRUCT.DEF. During program development, when it became
necessary to change one of the structures slightly, it was
not necessary to change it in all of the files, only one
file required modification which was then "included" in the
source files. Notice that the transcript data is stored in
a doubly linked list with the data itself being stored in a
separate dynamically allocated char string. This line is
pointed to by the pointer "lineloc".
For ease of development, the similar functions were
grouped together and compiled separately. Thus, all of the
functions involving the monitor were included in the file
named VIDEO.C, and all of the functions involving the data
storage were grouped into the FILE.C collection. Dividing
your program in a way similar to this should simplify
debugging and future modifications.
/* FILE.C FILE.C FILE.C FILE.C FILE.C FILE.C
FILE INPUT AND OUTPUT XXXXX XXX X XXXXX
X X X X
July 1,1987 X X X X
XXX X X XXX
X X X X
X X X X
X XXX XXXXX XXXXX
*/
#include "stdio.h"
#include "string.h"
#include "alloc.h"
#include "struct.def"
#include "defin.h"
int lastline = 0;
int arrowln = 0;
extern struct lines *top, *bot, *q, *p, *arrow, *trnsend;
extern struct vars allvars[];
extern int varinuse;
extern int printit;
extern char inline[];
extern int errcode;
extern int ignore;
extern int trnsattr;
/* ******************************************************** fileout */
/* This routine opens a disk file and writes the marked lines in the*/
/* transcript to that file. */
void fileout(void)
{
char *lpt, fileout[25];
struct lines *pt;
int i;
FILE *fp2;
poscurs(23,1); /* read in filename to output to */
printf(" filename > <");
poscurs(23,17);
for (i = 0;(fileout = getchar()) != '\n';++i);
fileout = 0; /* filename read in, ready to use */
fp2 = fopen(fileout,"w"); /* open file */
pt = top; /* start at top of llinked list */
do {
lpt = pt->lineloc; /* line of text stored */
if (pt->marked){ /* only output marked lines */
fputs(lpt,fp2); /* output a line */
fputs("\n",fp2); /* and a linefeed */
}
pt = pt->dn; /* get the next line */
} while (pt != NULL);
fflush(fp2); /* flush the file to disk */
fclose(fp2); /* close the file */
poscurs(23,7);
printf(" input > ");
}
/* ********************************************************* filein */
/* A diskfile is opened and read into the transcript window while */
/* all calculations are done as the file is input. If any errors are*/
/* found, the calculations are not done, zero is returned as a */
/* result and the remainder of the file is read in. It is assumed */
/* that the equations are correct before the file was originally */
/* written. */
char filenam[25] = "help"; /* default filename to start with */
void filein(void)
{
char filein[25];
char *fc;
int i;
FILE *fp2;
poscurs(23,1); /*read in filename for input */
printf(" filename > <");
poscurs(23,17);
for (i = 0; (filein = getchar()) != '\n';++i);
filein = 0; /* filename read in , ready to use */
if (filein[0] == 0) /* if no filename was input */
strcpy(filein,filenam); /* use last valid filemane */
else
strcpy(filenam,filein); /* save for later use */
fp2 = fopen(filein,"r"); /* open file */
if (fp2 == NULL) { /* file doesn't exist */
errcode = 11;
errout();
}
else {
do {
fc = (fgets(inline,62,fp2));
if (fc == NULL) break;
for (i=0;inline;++i);
inline[i-1] = 0;
parse();
if ((ignore == 1) || errcode)
strtrans(inline,0);
else
strtrans(inline,1);
transout();
} while (i != NULL);
}
for (i = 0;i < 200;++i) inline = 0; /* clear input area */
poscurs(23,7);
printf(" input > ");
}
/* ******************************************************* strtrans */
/* A line from the input area or from a file input is stored in the */
/* transcript area. It is stored in the transcript array here, and */
/* output to the transcript window in the "transout" function. */
/* This function uses a linked list to store the lines of data. */
void strtrans(char line[],int type)
{
int i;
long int temp;
char *pt;
char buffer[25]; /* this is long enough to include an overwrite */
double xx; /* temporary variable */
extern FILE *prtfile; /* print file output */
p = (struct lines *)malloc(sizeof(struct lines));
pt = (char *)malloc(1 + strlen(line));
if ((p == NULL) || (pt == NULL)) { /* out of memory */
errcode = 13;
errout();
}
else { /* there is enough memory for this entry */
if (top == NULL){ /* first entry */
top = bot = p;
p->dn = NULL;
p->up = NULL;
}
else { /* additional entries */
bot->dn = p;
p->up = bot;
p->dn = NULL;
bot = p;
}
p->lineloc = pt;
i = strlen(line);
p->isvalue = type;
p->marked = type;
p->linelngt = i;
if (type) {
xx = allvars[varinuse].value;
if (xx < 0.0) xx = -xx;
if ((xx > 9999999.0) || (xx < .001))
sprintf(buffer,"%12.5e",allvars[varinuse].value);
else
sprintf(buffer,"%12.6f",allvars[varinuse].value);
buffer[12] = 0;
if (varinuse > 5) { /* variable I through N */
temp = allvars[varinuse].value;
temp = temp & 077777777;
if (allvars[varinuse].outtype == 'D')
sprintf(buffer,"(D) %8ld",temp);
if (allvars[varinuse].outtype == 'O')
sprintf(buffer,"(O) %8lo",temp);
if ((allvars[varinuse].outtype == 'X') ||
(allvars[varinuse].outtype == 'H'))
sprintf(buffer,"(H) %8lx",temp);
}
strcpy(p->strval,buffer);
}
else
strcpy(p->strval," ");
line = '\0'; /* line terminator */
strcpy(pt,line);
if (type && printit){
fprintf(prtfile,"%13s %-62s\n",buffer,line);
}
arrow = p;
trnsend = p;
lastline++;
arrowln = lastline;
}
}
/* ******************************************************* transout */
/* This function outputs the transcript to the transcript window */
extern char strngout[];
void transout(void)
{
int i;
int maxm = 13; /* number of lines to output to the trans wind */
char *pt;
p = trnsend;
for (i = 0;i < maxm;++i){ /* count up max from trnsend */
if (p->up == NULL) break; /* stop if top found */
p = p->up;
}
for (i = 0;i <= maxm;++i){ /* output max fields to viddisp */
pt = p->lineloc; /* pt now points to the line */
strcpy(strngout,p->strval);
strngdis(8+i,1,trnsattr); /* output the formatted value */
strcpy(strngout,pt);
blnkline(8+i,16); /* write blanks to line */
if (p->marked)
chardis(8+i,15,trnsattr,'*'); /* marked indicator */
else
chardis(8+i,15,trnsattr,' '); /* blank */
strngdis(8+i,17,trnsattr);
if (arrow == p)
chardis(8+i,14,trnsattr,16); /* arrow char */
else
chardis(8+i,14,trnsattr,' '); /* blank */
if (p->dn == NULL) break; /* stop if bottom found */
p = p->dn;
}
poscurs(23,3);
printf("%4d",arrowln);
}
/* ******************************************************* movarrow */
/* This function is used to move the arrow up or down in the window */
/* and to control where the window begins and ends in the transcript*/
/* data. The arrow is always two lines from the top or bottom if it */
/* is possible to do so. */
void movarrow(int where)
{
int index;
struct lines *temp;
int iend, iarrow, itrnsend;
iend = iarrow = itrnsend = 0;
if (where > 0) {
for (index = where;index && (arrow != bot);--index)
arrow = arrow->dn; /* move arrow down one */
for (temp = top,index = 0;temp != bot;index++) {
if (temp == arrow) iarrow = index; /* locate arrow */
if (temp == trnsend) itrnsend = index; /* loc display end */
temp = temp->dn;
}
if (temp == arrow) iarrow = index; /* if they are at */
if (temp == trnsend) itrnsend = index; /* the bottom end */
iend = index;
/* now trnsend must be >= arrow, but not by more than 10 */
if (iarrow == iend) index = iend - itrnsend;
else if (itrnsend < (iarrow+1)) index = iarrow - itrnsend + 1;
else index = 0;
}
else {
for (index = -where;index && (arrow != top);--index)
arrow = arrow->up; /* move arrow up one */
/* if (arrow == top) arrow = arrow->dn; move one field down */
for (temp = top,index = 0;temp != bot;index++) {
if (temp == arrow) iarrow = index; /* locate arrow */
if (temp == trnsend) itrnsend = index; /* loc display end */
temp = temp->dn;
}
if (temp == arrow) iarrow = index; /* if they are at */
if (temp == trnsend) itrnsend = index; /* the bottom end */
iend = index;
/* now trnsend must be >= arrow, but not by more than 12 */
if (iarrow == 0) index = (iend > 13?13:iend) - itrnsend;
else if ((itrnsend - iarrow) > 12) index = iarrow-itrnsend+12;
else index = 0;
}
if (index > 0)
for (;index > 0;--index)
trnsend = trnsend->dn;
else if (index < 0)
for (;index < 0;++index)
trnsend = trnsend->up;
arrowln = iarrow;
transout();
}
Of special interest is the "monitor()" function. This
function examines the video mode through use of a DOS
command and if it is a 7, it assumes it is a monochrome
monitor, otherwise it assumes a color monitor. The colors
of the various fields are established at this time and used
throughout the program. Most of the data is written
directly to the video memory, but some is written through
the standard BIOS routines.
/*
FILENAME FUNCTION DESCRIPTION OF FUNCTION
DEFIN.C -----------> Text file - definition only - this file
VC.C main() primarily a menu
readline() read an input line from the keyboard
parse() parse the input line
errout() output error message to display
DATA.C initdata() initialize data and fields
getnames() get new variable names
calcdata() do the calculations
calcdat() do the four function calculations
getnum() get the value of the number
getop() get the math operator
errchk() check for error storage
FILE.C fileout() store transcript to a file
filein() retrieve transcript from a file
strtrans() store a transcript message
transout() display the current transcript
movarrow() move the arrow up or down
VIDEO.C monitor() determine type of monitor
bkgndvid() display video background
valusvid() display all values
disnew() display the new changed variable
helpm() display mathematics help messages
helps() display system help messages
linedisp() display a line to video (with attr)
strngdis() display a line (add attr)
blnkline() outputs blanks to a video line
chardis() display a single character
errdis() display error message to screen
clrscrn() clear the video monitor
poscurs() position the cursor on the monitor
prtprblm() printer problem check
*/
/* Prototype definitions for all functions */
void readline(void);
void parse(void);
void errout(void);
void initdata(struct vars *pnt);
void getnames(void);
void calcdata(double *newval);
void calcdat(double *number1,char *op1,double *number2);
double getnum(void);
char getop(void);
void errchk(int err);
void fileout(void);
void filein(void);
void strtrans(char line[],int type);
void transout(void);
void movarrow(int where);
void monitor(void);
void bkgndvid(void);
void valusvid(void);
void disnew(int varinuse);
void helpm(void);
void helps(void);
void linedisp(int line);
void strngdis(int row,int col,int attr);
void blnkline(int row,int col);
void chardis(int row,int col,int attr,int ch);
void errdis(char str[]);
void clrscrn(void);
void poscurs(int row,int col);
int prtprblm(void);
The file DEFIN.H is a catalogue of the functions to aid
in finding the functions. This file was generated as one of
the first files and was maintained and updated for use
during the entire design and coding lifetime. It also
contains all of the prototype definitions for the functions
in all of the source files, and is "included" in every
source file to do prototype checking.
Page 103
---- END --- |