There are nine instruction blocks here - I\'m really stuck with this and don\'t
ID: 3673438 • Letter: T
Question
There are nine instruction blocks here - I'm really stuck with this and don't know where to begin...
/ * This program implements a simple calculator application.
* It demonstrates a concepts where multiple users submitting tasks, then the
* application serially loads, computes and then generates a report.
* This is accomplished using multiple processes:
* loader (input command processor)
* ALU (arithmetic logic unit, or doing the actual calculations)
* printer (report generator)
*
* Howto compile & run:
* In the same directory, have the following file:
* p1calc.c
* calc.dat (or other data file)
* $ gcc p1calc.c -o p1calc.out
* $ p1calc.out calc.dat
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
// MACROS //////////////////////////////////////////////////////////////////////
#define MAX_OPS_PER_JOB 10
#define NUM_CMD_FIELDS 4
#define TRUE 1
#define FALSE 0
// Data structures /////////////////////////////////////////////////////////////
// Calculator Command Operation
typedef struct _cmd_rcd {
char operator; // operator
int operand1; // operand1
int operand2; // operand2
int result; // result
} cmd_rcd_t;
// Global variables ////////////////////////////////////////////////////////////
// Shared Memory pointers
int gNumCmds; // range: 0-done to MAX_OPS_PER_JOB
cmd_rcd_t gCmdTbl[MAX_OPS_PER_JOB]; // all of one job's operations
// Global variables
FILE *fpIn; // file handle pointer of input job data file
int fdOut; // file handle pointer of output report file
// Function prototypes /////////////////////////////////////////////////////////
// [In later projects these effectively become the child processes]
int load_job(int job); // load a user's job
void perform_arithmetic(int job); // process the math for a job
void print_job_report(int job); // prints a job's report
#define RPT_FILENAME "calc.rpt"
/*******************************************************************************
* main function
* inputs (standard)
* argc - number of command line arguments
* argv - array of pointers to strings for each command line arguments
* return - none
*
* This program application implements a simple calculator batch system
* to load jobs, compute and then produce a report. It reads a series of jobs
* each job being a set of commands, performing the calculator operations per
* job, and finally producing a printed output report per job.
*
* To accomplish the program it uses several function (loader, ALU and printer)
* sequentially per job.
*/
int main (int argc, char *argv[]) /* Calculator main program */
{
char c;
int job;
int more_jobs; // boolean
int done; // boolean
setbuf(stdout, NULL); // use standard output, flush buffer
/* read and open command file name */
if (argc != 2) { // arg 1 = program executable_name, arg 2 - data filename
if (argc == 1)
printf("No data filename provided "
"Example $ calc_semshr.out calc.dat ");
else
printf("No filename provided, or too many arguments "
"Example $ calc_semshr.out calc.dat ");
exit(EXIT_FAILURE);
}
else if ((fpIn = fopen(argv[1], "r")) == 0) {
puts ("Unable to open job command file for reading. ");
exit(EXIT_FAILURE);
}
printf( " "
"Choose an output option "
" F or f Print the output to the file calc.rpt "
" S or s Print the output to the screen "
"Press [F|f|S|s] key and then press [ENTER] key >> " );
done = FALSE;
while (!done) {
c = getchar(); // blocks until information (keypress) is available
switch(c) {
case 'F':
case 'f':
if (access(RPT_FILENAME, F_OK) == 0) // check for an old report file
unlink(RPT_FILENAME); // remove old report file
/* The access() function is a low-level file IO command
* we did not discuss in MOS Ch 1 or Lab #2. [see Ch 10] */
// create a new report file
/* XXX INSTRUCTION START
* Convert the report file from using a
* Standard IO stdio.h file handle pointer (fp) method to using the
* low-level IO fcntl.h file descriptor (fd) method
* Replace fpOut with fdOut
* Replace fopen(), two methods possible
* Method #1:
* Replace fopen() with creat()
* 1st argument is the same
* 2nd argument use user RWX, everyone else read-only
* Big Hint: (S_IRWXU|S_IRGRP|S_IROTH)
* Method #2:
* Replace fopen() with open()
* 1st argument is the same
* 2nd argument use user create write-only
* Big Hint: (O_CREAT|O_WRONLY)
* 3rd argument use user RWX, everyone else read-only
* Big Hint: (S_IRWXU|S_IRGRP|S_IROTH)
*
* Note: The old C method would use 0755 for (S_IRWXU|S_IRGRP|S_IROTH)
*
* Then delete this entire comment block once program is working.
*
* Use man or linux.die.net for more info on creat/open/fopen (or a textbook)
* XXX INSTRUCTION END */
fdOut = creat(RPT_FILENAME, (S_IRWXU|S_IRGRP|S_IROTH)); // open new report file
/* XXX INSTRUCTION START
* Convert the report file from using a
* Standard IO stdio.h file handle pointer (fp) method to using the
* low-level IO fcntl.h file descriptor (fd) method
* Update the if condition from the fp to the fd method
* fp returns 0 on error (zero indicates a NULL pointer, or undefined)
* fd returns -1 on error (indicates failed, an error code is stored in errno)
* Then delete this entire comment block once program is working.
* XXX INSTRUCTION END */
if (fpOut == 0) { // error opening new report file
puts ("Unable to open job report file for writing. ");
exit(EXIT_FAILURE);
}
done = TRUE;
break;
case 'S':
case 's':
/* XXX INSTRUCTION START
* Convert the report file from using a
* Standard IO stdio.h file handle pointer (fp) method to using the
* low-level IO fcntl.h file descriptor (fd) method
* Background info:
* Equivalents stdio.h fcntl.h
* standard device fp * fd
* inputs keyboard stdin 0
* outputs console stdout 1
* errors console stderr 2
* Then delete this entire comment block once program is working.
*
* FYI: You can change the default device in Unix/C,
* In Unix shell scripts use re-direction arrows < << > >>
* In C programs manipulating the file descriptors and pointers,
* its a long discussion, but the dup() function is used to do this.
* XXX INSTRUCTION END */
fdout = 1;
done = TRUE;
break;
default:
printf("INVALID ENTRY - RETRY "
"Press [F|f|S|s] key and then press [ENTER] key >> " );
break;
}
}
job = 0;
more_jobs = TRUE;
while (more_jobs) {
gNumCmds = load_job(job);
if ( gNumCmds > 0 ) {
perform_arithmetic(job);
print_job_report(job);
job++;
}
else
more_jobs = FALSE;
}
// cleanup
fclose(fpIn);
/* XXX INSTRUCTION START
* Convert the report file from using a
* Standard IO stdio.h file handle pointer (fp) method to using the
* low-level IO fcntl.h file descriptor (fd) method
* Replace fclose() with close()
* Replace argument fpOut with fdOut
* Then delete this entire comment block once program is working.
*
* Use man or linux.die.net for more info on close/fclose (or a textbook)
* XXX INSTRUCTION END */
fclose(fpOut);
printf("normal exit ");
exit(EXIT_SUCCESS);
} /* end of Calculator main */
/*******************************************************************************
* load_job function
* inputs
* job - job used as index for global array
* return
* 1-10 - number of operations successfully, stored in gCmdTbl
* 0 - no more jobs to read
*
* This function will load one job's operations from an input file.
* It reads one line at a time from the file, formatted as described below.
* It loads the set of operations (a job) into an array of structures.
*
* Input file calculation command format (Postfix ie. Reverse Polish Notation):
* three required fields separated by a space & ending with a carriage return
* field 1 - operand 1, legal integer values, use 0 with ? terminator
* field 2 - operand 2, legal integer values, use 0 with ? terminator
* field 3 - one character operator, valid values are:
* + addition
* - subtraction
* * multiplication
* / division
* % modulus
* ? terminator
* field 4 - EOL - end of line character
* Examples:
* 1 2 + for 1 + 2 = 3
* 3 6 * for 3 * 6 = 18
* -2 4 - for -2 - 4 = -6
* 0 0 ? job done
* This is what is referred to as Reverse Polish Notation (PN)/postfix notation.
* notation. RPN is commonly used in computer science for logic
* representations that are stack dependent implementations, including
* early hand-held scientific calculators and many software calculators,
* plus stack-oriented languages like Forth, Factor & PostScript.
* Trivia: Infix Polish Notation is used with AI language Lisp, some CS
*
* A job (terminated by a ?) can have up to MAX_OPS_PER_JOB jobs.
* Jobs in excess will result in job being split and the remainder passed to the
* next job.
*
* Note: For the purposes of not adding distracting additional code to this
* assignment, the validation of the calculation commands input line
* is modest, basically it assumes correct input, it will only detect
* the simple input errors.
*/
int load_job(int job)
{
cmd_rcd_t command; // command record read from data file
char eol; // data entry carriage return at EOL
int num_fields; // number of fields read
int end_of_job = FALSE;
int cmd;
cmd = 0; // reset for new job to read
do // do-while so you always attempt a read from input file at least once
{
// Set local command record fields to known values before read
// (helps to identify data file format errors)
command = (cmd_rcd_t) {0, 0, '#', 0 };
// Try to read a job's commands, 4 required fields per line
// Reverse Polish Notation operator, number, number, EOL
num_fields = fscanf(fpIn, "%d %d %c%c",
&command.operand1, &command.operand2, &command.operator, &eol);
if (num_fields == NUM_CMD_FIELDS) {
switch (command.operator) {
// accept valid operands
case '+': // addition operand
case '-': // subtraction operand
case '*': // multiplication operand
case '/': // division operand
if (cmd < MAX_OPS_PER_JOB) {
gCmdTbl[cmd++] = command; // copy record
}
else { // max number of commands reached with no terminator
fprintf(stderr,
">>> MAX OPERATIONS PER JOB EXCEEDED-IGNORE COMMAND "
" job#=%d fields-read=%d op=%c op1=%d op2=%d eol=%#04x ",
job, num_fields,
command.operator, command.operand1, command.operand2, eol);
}
break;
case '?': // terminator operand
end_of_job = TRUE;
break;
default:
fprintf(stderr,
">>> ILLEGAL OPERATION COMMANDED-IGNORE "
" job#=%d fields-read=%d op=%c op1=%d op2=%d eol=%#04x ",
job, num_fields,
command.operator, command.operand1, command.operand2, eol);
} // end switch
}
else if (num_fields == EOF) { // last job read
end_of_job = TRUE;
if (cmd != 0) // EOF without job terminator
printf(">>> EOF WITHOUT JOB TERMINATOR ");
}
else // other formatting error
fprintf(stderr,
">>> IGNORE ILLEGAL COMMAND FORMAT "
" job#=%d fields-read=%d op=%c op1=%d op2=%d eol=%#04x ",
job, num_fields,
command.operator, command.operand1, command.operand2, eol);
} while (!end_of_job); // end loop */
return(cmd);
} /* end of load_job */
/*******************************************************************************
* perform_calcuations function
* inputs
* job - job used as index for global array
* return - none
*
* This function actually performs the arithmetic computations of all the
* calculation operations for a specific job.
* The operations commands have previously been placed in a global array.
*/
void perform_arithmetic(int job)
{
char op; // operation
int op1, op2, answer; // operands & result
int cmd; // command operation index
for (cmd=0; cmd < gNumCmds; cmd++) {
op = gCmdTbl[cmd].operator;
op1 = gCmdTbl[cmd].operand1;
op2 = gCmdTbl[cmd].operand2;
if ((op == '/') && (op2 == 0)) { // check for divide by zero
gCmdTbl[cmd].result = INT_MAX;
fprintf(stderr, ">>> DIVIDE BY ZERO op1=%d op2=%d op=%c ", op1, op2, op);
}
else {
switch (gCmdTbl[cmd].operator) {
case '+': answer = op1 + op2; break;
case '-': answer = op1 - op2; break;
case '*': answer = op1 * op2; break;
case '/': answer = op1 / op2; break;
default: answer = INT_MAX; break;
}
gCmdTbl[cmd].result = answer;
}
}
return;
} /* end of perform_calcuations */
/*******************************************************************************
* print_memory function
* inputs
* job - job used as index for global array
* return - none
*
* This function prints a report of all the calculation operations records for a
* specific job using a global array.
*/
void print_job_report(int job)
{
int cmd; // command operation index
/* XXX INSTRUCTION START
* Create a character array named 'outstr' of size 100 after the prior line.
* Then delete this entire comment block once program is working.
*
* Don't remember how to create an arrays, see Quickstart #5 C Arrays lecture
*
* Background on Strings:
* A string is simply an array of characters terminated with a zero ''
* The printf family of functions automatically add the ''
*
* FYI: There is a whole family of string manipulation functions in the
* C String library , which for the most part I have tried to avoid
* using in this course to keep things simple. These are efficient & capable
* but somewhat cryptic functions heavily dependent upon pointer management,
* and both dynamics (heap) and static (data) memory management, and are
* frequently the source of run-time errors in the hands of newbies or lazy
* programmers. This is why contemporary languages have IO and string
* manipulation rules built into the language definition, not libraries.
* Some examples include strlen (length), strcpy, strchr (string search),
* strcat (as in Unix cat command), and many many more.
* XXX INSTRUCTION END */
/* XXX INSTRUCTION START
* Background:
* The stdio has several formated output functions
* printf screen
* fprintf file at fp
* sprintf string (array) at char *
* When using file descriptor IO read/write commands for bytes of data,
* but you need to reformat between ASCII and binary numbers.
* Example: the integer number 100 decimal
* 16-bit integer x0064 or b0000000001100100
* formatted 2 ASCII character x3535
* Humans prefer the ASCII format, computer processors can only perform math
* on the numeric integers. [I'm ignoring real numbers for this discussion.]
* The easiest method is to use the string sprintf and scanf functions to
* convert your binary information to/from strings,
* which you can then read/write one byte at a time.
* There is a string.h function we will use, strlen which provides the number
* of character up to but not including the terminator ''
* Use man or linux.die.net for more info on these functions (or a textbook)
*
* Convert the fprintf() statement into two statements
* Replace fprintf with sprintf
* Replace fp with string pointer previously declared 'outstr'
* Other arguments remain unchanged.
* Add a new write() function call with three arguments
* first - the fd
* second - a void cast of the string char pointer '(void *)outstr'
* third - the byte count, using 'strlen(outstr)'
* Then delete this entire comment block once program is working.
*
* Note: No error checking on write is done to keep this simple.
* XXX INSTRUCTION END */
fprintf(fpOut, "REPORT FOR JOB# %d ", job);
for (cmd=0; cmd < gNumCmds; cmd++) { // loop one command at a time
/* The calculation operations reside in the shared memory output/print
* buffer at the index provided. This data is then formated to the desired
* report layout/format for printing.
* It prints the report to the standard output device i.e the screen.
* for a 2 -4 * command, a line of the report will look like:
* "INPUT> * 2 -4 CALCULATION 2*-4=-8" */
/* XXX INSTRUCTION START
* Repeat instruction from prior XXX step for this fprintf function.
* Then delete this entire comment block once program is working.
* XXX INSTRUCTION END */
fprintf(fpOut, "Input command> %c %d %d Calculation> %d%c%d=%d ",
gCmdTbl[cmd].operator,
gCmdTbl[cmd].operand1,
gCmdTbl[cmd].operand2,
gCmdTbl[cmd].operand1,
gCmdTbl[cmd].operator,
gCmdTbl[cmd].operand2,
gCmdTbl[cmd].result );
}
/* XXX INSTRUCTION START
* Repeat instruction from prior XXX step in this fprintf function.
* Then delete this entire comment block once program is working.
* XXX INSTRUCTION END */
fprintf(fpOut, " Number of job computations: %d ", gNumCmds );
return;
} /* end of print_job_report */
Explanation / Answer
The program would start executing from the main function. It will call the required functions from within the main.
#include<stdio.h>
#include<conio.h>
#include<cstdlib.h>
#include<string.h>
// MACROS //////////////////////////////////////////////////////////////////////
#define MAX_OPS_PER_JOB 10
#define NUM_CMD_FIELDS 4
#define TRUE 1
#define FALSE 0
// Data structures /////////////////////////////////////////////////////////////
// Calculator Command Operation
typedef struct _cmd_rcd {
char operator; // operator
int operand1; // operand1
int operand2; // operand2
int result; // result
} cmd_rcd_t;
// Global variables ////////////////////////////////////////////////////////////
// Shared Memory pointers
int gNumCmds; // range: 0-done to MAX_OPS_PER_JOB
cmd_rcd_t gCmdTbl[MAX_OPS_PER_JOB]; // all of one job's operations
// Global variables
FILE *fpIn; // file handle pointer of input job data file
int fdOut; // file handle pointer of output report file
// Function prototypes /////////////////////////////////////////////////////////
// [In later projects these effectively become the child processes]
int load_job(int job); // load a user's job
void perform_arithmetic(int job); // process the math for a job
void print_job_report(int job); // prints a job's report
#define RPT_FILENAME "calc.rpt"
int main (int argc, char *argv[]) /* Calculator main program */
{
char c;
int job;
int more_jobs; // boolean
int done; // boolean
setbuf(stdout, NULL); // use standard output, flush buffer
/* read and open command file name */
if (argc != 2) { // arg 1 = program executable_name, arg 2 - data filename
if (argc == 1)
printf("No data filename provided "
"Example $ calc_semshr.out calc.dat ");
else
printf("No filename provided, or too many arguments "
"Example $ calc_semshr.out calc.dat ");
exit(EXIT_FAILURE);
}
else if ((fpIn = fopen(argv[1], "r")) == 0) {
puts ("Unable to open job command file for reading. ");
exit(EXIT_FAILURE);
}
printf( " "
"Choose an output option "
" F or f Print the output to the file calc.rpt "
" S or s Print the output to the screen "
"Press [F|f|S|s] key and then press [ENTER] key >> " );
done = FALSE;
while (!done) {
c = getchar(); // blocks until information (keypress) is available
switch(c) {
case 'F':
case 'f':
if (access(RPT_FILENAME, F_OK) == 0) // check for an old report file
unlink(RPT_FILENAME); // remove old report file
fdOut = creat(RPT_FILENAME, (S_IRWXU|S_IRGRP|S_IROTH)); // open new report file
if (fpOut == 0) { // error opening new report file
puts ("Unable to open job report file for writing. ");
exit(EXIT_FAILURE);
}
done = TRUE;
break;
case 'S':
case 's':
fdout = 1;
done = TRUE;
break;
default:
printf("INVALID ENTRY - RETRY "
"Press [F|f|S|s] key and then press [ENTER] key >> " );
break;
}
}
job = 0;
more_jobs = TRUE;
while (more_jobs) {
gNumCmds = load_job(job);
if ( gNumCmds > 0 ) {
perform_arithmetic(job);
print_job_report(job);
job++;
}
else
more_jobs = FALSE;
}
// cleanup
fclose(fpIn);
fclose(fpOut);
printf("normal exit ");
exit(EXIT_SUCCESS);
} /* end of Calculator main */
int load_job(int job)
{
cmd_rcd_t command; // command record read from data file
char eol; // data entry carriage return at EOL
int num_fields; // number of fields read
int end_of_job = FALSE;
int cmd;
cmd = 0; // reset for new job to read
do // do-while so you always attempt a read from input file at least once
{
// Set local command record fields to known values before read
// (helps to identify data file format errors)
command = (cmd_rcd_t) {0, 0, '#', 0 };
// Try to read a job's commands, 4 required fields per line
// Reverse Polish Notation operator, number, number, EOL
num_fields = fscanf(fpIn, "%d %d %c%c",
&command.operand1, &command.operand2, &command.operator, &eol);
if (num_fields == NUM_CMD_FIELDS) {
switch (command.operator) {
// accept valid operands
case '+': // addition operand
case '-': // subtraction operand
case '*': // multiplication operand
case '/': // division operand
if (cmd < MAX_OPS_PER_JOB) {
gCmdTbl[cmd++] = command; // copy record
}
else { // max number of commands reached with no terminator
fprintf(stderr,
">>> MAX OPERATIONS PER JOB EXCEEDED-IGNORE COMMAND "
" job#=%d fields-read=%d op=%c op1=%d op2=%d eol=%#04x ",
job, num_fields,
command.operator, command.operand1, command.operand2, eol);
}
break;
case '?': // terminator operand
end_of_job = TRUE;
break;
default:
fprintf(stderr,
">>> ILLEGAL OPERATION COMMANDED-IGNORE "
" job#=%d fields-read=%d op=%c op1=%d op2=%d eol=%#04x ",
job, num_fields,
command.operator, command.operand1, command.operand2, eol);
} // end switch
}
else if (num_fields == EOF) { // last job read
end_of_job = TRUE;
if (cmd != 0) // EOF without job terminator
printf(">>> EOF WITHOUT JOB TERMINATOR ");
}
else // other formatting error
fprintf(stderr,
">>> IGNORE ILLEGAL COMMAND FORMAT "
" job#=%d fields-read=%d op=%c op1=%d op2=%d eol=%#04x ",
job, num_fields,
command.operator, command.operand1, command.operand2, eol);
} while (!end_of_job); // end loop */
return(cmd);
} /* end of load_job */
void perform_arithmetic(int job)
{
char op; // operation
int op1, op2, answer; // operands & result
int cmd; // command operation index
for (cmd=0; cmd < gNumCmds; cmd++) {
op = gCmdTbl[cmd].operator;
op1 = gCmdTbl[cmd].operand1;
op2 = gCmdTbl[cmd].operand2;
if ((op == '/') && (op2 == 0)) { // check for divide by zero
gCmdTbl[cmd].result = INT_MAX;
fprintf(stderr, ">>> DIVIDE BY ZERO op1=%d op2=%d op=%c ", op1, op2, op);
}
else {
switch (gCmdTbl[cmd].operator) {
case '+': answer = op1 + op2; break;
case '-': answer = op1 - op2; break;
case '*': answer = op1 * op2; break;
case '/': answer = op1 / op2; break;
default: answer = INT_MAX; break;
}
gCmdTbl[cmd].result = answer;
}
}
return;
} /* end of perform_calcuations */
void print_job_report(int job)
{
int cmd; // command operation index
fprintf(fpOut, "REPORT FOR JOB# %d ", job);
for (cmd=0; cmd < gNumCmds; cmd++) { // loop one command at a time
fprintf(fpOut, "Input command> %c %d %d Calculation> %d%c%d=%d ",
gCmdTbl[cmd].operator,
gCmdTbl[cmd].operand1,
gCmdTbl[cmd].operand2,
gCmdTbl[cmd].operand1,
gCmdTbl[cmd].operator,
gCmdTbl[cmd].operand2,
gCmdTbl[cmd].result );
}
fprintf(fpOut, " Number of job computations: %d ", gNumCmds );
return;
}
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.