The text file "text.txt", is used by the program as the source of data for the project. Compile and run the program, as given, to ensure that it works.
The "project.c" source program consits of the following four(4) subprograms:
- int read_text(char *p_text[], FIle *fp):
This function is called by the main program to read the input "text.txt" file, line by line, and return the entire text in the array of pointers: char *p_text[MAX_NUM_LINES]. Please read the documentation in the source code of the function for details. The code for this function is given and no modifications are required by you.
- void write_text_inorder(char *p_text[], nlines):
This subroutine takes the "p_text" array of pointers as argument, and the number of lines it contains (nlines). The subroutine prints the contents of the pointer array in the same order that it was input by the "read_text" function. Please read the documentation in the source code of the subroutine for details.
Task: The subroutine header is given, you are to write the body. Note: If there are no lines of text to print, you must output a message to that affect using the "MSG_INFO" enumerated message type and the report_message(...) routine.
- void write_text_reverse(char *p_text[], nlines):
This subroutine takes the "p_text" array of pointers as argument, and the number of lines it contains (nlines). The subroutine prints the contents of the pointer array in the REVERSE order that it was input by the "read_text" function. That is, the last input line of text is printed first, the second-last input line is printed second, and so on. Please read the documentation in the source code of the subroutine for details.
The subroutine header is given, you are to write the body. Note: If there are no lines of text to print, you must output a message to that affect using the "MSG_INFO" enumerated message type and the report_message(...) routine.
- void report_message(char msg[MAX_MSG_NUM][MAX_MSG_LNG], enum msg_kind_type msg_kind ):
This routine prints a given message, "msg", in a standardized format with each message type having a unique message prefix to identify the message on output. For example, an information message (MSG_INFO) has a prefix of "::> MSG: ", while an error message (MSG_ERROR) has a prefix of ">>> ERR: ". Please read the documentation in the source code of the subroutine for details.
Your task is to create a NEW enumerated message type/kind called "MSG_END", and use it to print the "Program Terminated. Have a great day." message in a standardized fashion using the report_message(...) routine.
The output message MUST look like this:
==> END: Program Terminated. : Have a great day.
Follow the example of the other enumerated message types, and how they are use to print a message.
text.txt
Life is a challenge - meet it, Life is a gift - accept it, Life is a sorrow - overcome it, Life is a tragedy - face it, Life is a mystery - unfold it, Life is an opportunity - take it, Life is a promise - complete it, Life is a struggle - fight it, Life is a goal - achieve it, Life is love - love it, Life is adventurous - have fun, Life is a duty - perform it, Life is a game - out smart it, Life is a beauty - praise it, Life is great - make something good of it. So life is a celebration - eat, laugh and do meditation.
project.c
#include
#include #include #include 49 // Define machine-independent TRUE and FALSE values 51 54 #ifdef TRUE #undef TRUE #undef FALSE #endif #define TRUE (1==1) #define FALSE (O==1) ' ' //===== GLOBAL MACRO DEFINITIONS === #define NL " " // New Line string #define NL ' ' // New Line char #define LF " " // Line Feed string #define cLF // Line Feed char #define CR " " // Carriage Return string #define CCR '\ // Carriage Return char #define CRLF " " // Carriage Return/Line Feed #define LFCR " " // Line Feed/Carriage Return #define FF // Form Feed #define NUL "10" // Null string #define CNUL // Null character #define BLK // Single Blank string #define CBLK // Single Blank character "\f" #define cuScore ! #define UScore " #define CZERO TO' // Underscore character // Underscore string. // Single ZERO character #define HTAB #define cHTAB #define VTAB #define CVTAB #define ESC "\t" "It' "\v" "Iv' "\x1B" // Horizontal Tab string // Horizontal Tab character // Vertical Tab string // Vertical Tab character // Escape string [hex(1B) = dec(27)] 84 // ===== GLOBAL STATEMENT FUNCTION DEFINITIONS === #define F_MIN (V1, V2) (((v1) (v2))? (v1): (v2)) #define ZF_MIN (v1, V2) (F_MAX(@, (F_MIN( (v1), (v2))))) #define ZF(v) (F_MAX(0, (v))) #define F_NOT(v) (((v) == TRUE) ? FALSE: TRUE), #define F_ABS(v) (((V) >= 0 )? (v):(-(v))), // Return the less of v1 and v2 // Return the greater of v1 and v2 // Like F_MIN, but lower bounds the result at ZERO // Lower bounds the value "V" at ZERO. // Logical Negation. // Absolute value //===== GLOBAL Constants ======== #define MAX_NUM_LINES 500 #define MAX_LINE_LNG 81 // Max. number of lines. // Max. length of line. One extra byte for terminating null. 96 #define MAX_MSG_NUM 10 #define MAX_MSG_LNG 133 // Number of message lines. // Length of a message string: Max of 132 chars +1 for terminating null byte. //===== GLOBAL VARIABLES and STRUCTURE DEFINITIONS = char msa IMAX MSG NUM) (MAX MSG LNG]; / Message buffer. Can hold MAX_MSG_NUM messages, each MAX MSG_LNG chars long. 98 99 100 101 102 103 104 105 106 enum msg kind_type(MSG_INFO, MSG_WARNING, MSG_ERROR}; // MESSAGE type or kind of message. (Enumerated Type) // Information. // Warning. Error. //===== GLOBAL FUNCTION PROTOTYPES === int_read_text (char *p_text[], FILE *fp ); 108 109 void write_text inorder(char *p_text(), int nlines); 112 void write_text_reverse(char *p_textil, i nt nlines); 114 115 116 117 118 void report_message (char enum msg [MAX_MSG_NUM] [MAX_MSG_LNG), msg_kind_type msg_kind ); 120 121 int main(int argc, char *argv[]) { //=== setbuf(stdout, NULL); 122 123 int nlines; int return_val; // No. of lines read from input file. // Main program return value to os (Operating System). 125 126 127 char *p_text [MAX_NUM_LINES]; 128 129 // Array of pointers to "char" // Have a total number of MAX_NUM_LINES pointers. // Input file name. // Input file pointer. 130 char filename_in[] = "text.txt"; FILE *fp_in = NULL; 131 return_val = @; // Assume all is well. 132 133 134 135 // Initialize "msg" message buffer by filling entire buffer with NULLS. for (int i=0; i 0) { 147 write_text_inorder(p_text, nlines); write_text_reverse(p_text, nlines); // Call subprogram to print in order. // Call subprogram to print in reverse order. 149 }else { // Handle excessive number of input lines. Indicated by "nlines = -1" sprintf(msg[0], "%s", "Input file too large to process."); sprintf(msg[1], "s[d]", "Max. number of input lines allowed = ", MAX_NUM_LINES); sprintf(msg[2], "%s", "Program Execution Aborted."); report_message(msg, MSG_ERROR); return_val = -1; // Set up to return ERROR condition (-1) to os. 157 158 fclose(fp_in); // Close input file "here" since it was opened successfully. 159 160 161 162 printf("Program Terminated. Have a great day."); 163 return (return_val); // Return to os. 164 165 166 167 168 169 170 171 172 173 174 int read_text(char *p_text[], // Array of pointers to char. FILE *fp ). // Input file pointer. //=========================== // This function reads consecutive lines of text from the input file "fp", until either the end of file is reached, or // the end of the array of pointers is reached. // Each input line is saved/stored in dynamic storage and it is accessed/referenced by a pointer of the p_text array. // That is, p_text[0] points to the FIRST input line, 11 p_text[1] points to the SECOND input line, and so on. // The function returns: 1) The array of pointers "p_text" that point to each input line. 112 ) The number of lines read, if all went well, otherwise 1/ 3) Return -1, if an error has occurred. 175 176 177 178 int int int char char int i; nlines; line_lng; line [MAX_LINE_LNG); *p; return_val; // Loop index. // Line counter of lines read. // Length of current input line. // Local storage for the current line of text just read. // Pointer to dynamically allocated storage. // Value returned by the function. 179 180 181 182 183 184 185 186 187 188 189 190 191 192 nlines return_val = 0; // So far, all is well. while ( (fgets(line, MAX_LINE_LNG, fp) != NULL) && (return_val >= 0)) { i = 0; while linelul != NUL) && (i 232 233 return; void write_text_reverse(char *p_text[], // Array of pointers to lines of input. int nlines) // Number of input lines pointed to/referenced by array of pointers. //===================================== // This subroutine prints all input lines of text referenced by the "p_text" array of pointers. // Input lines are printed in the REVERSE order that they were read in. // A suitable heading is printed to indicate the nature of the output. // The report_message() routine is called to output an appropriate "MSG_INFO" message if there are no lines of text to print. { // 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 return; void report_message(char msg [MAX_MSG_NUM (MAX_MSG_LNG), // Message buffer enum msg_kind_type msg_kind ) // Message kind, i.e. kind of message: INFO, WARNING, or ERROR. //=EEEEEEEEEEEEEEEEEEEEEEE ============== ========= // Given the 2D message buffer msg [] [] and the type/kind of message to print, this routine prints the message in a "standardized" format. // Each message type has a unique prefix which is printed with the first message line to serve as a "label" for the message, and each // subsequent message line, if present, is indented accordingly. // Each line of the message buffer is printed sequentially, starting with the first line (index = 0), and ending when either the // last line of the buffer is printed, or the first NULL line is encountered. { static char pname'] = "report_message"; // sub-program name. 261 263 264 register int i; static char info_prefix] static char warn_prefix] static char err_prefix] static char indent[] = "::> MSG: "; = "*> WRN: "; = ">>> ERR: ": printf(NL); // Print a blank line to separate the message from previous output. 266 267 268 269 270 // Switch on "msg_kind" to determine which message prefix to output! 271 272 273 switch (msg_kind) { case MSG_INFO: printf(info_prefix); break; D case MSG_WARNING: printf(warn_prefix); break; // A "Switch" statement is the ONLY place where a "break" is allowed because it // is part of the "Switch Syntax". 278 279 case MSG_ERROR: printf(err_prefix); break; 280 281 282 283 284 285 286 287 288 default: // Always check for an "un-handled" msg_kind! printf(">>>>>>: INTERNAL Error in function: %s ", pname ); printf(">>>>>>: Invalid msg_kind : [%d] ", msg_kind); break; // Output the contents of the "msg" buffer, starting with the first message line. // NOTE: The first message line is ALWAYS output because message buffer is // guaranteed to contain at least one message line. 289 290 msg [0] [MAX_MSG_LNG -1) = (NUL; 291 292 293 294 // Null last position of message, in case message is longer than buffer, or // message is not nulled. Can never be too trustworthy when it comes to handling // strings in "C". 295 printf("%s ", msg[0]); // Always print first message! 295 296 297 printf("%s ", msg[0]); // Always print first message! for (int j=0; j #include #include #include 49 // Define machine-independent TRUE and FALSE values 51 54 #ifdef TRUE #undef TRUE #undef FALSE #endif #define TRUE (1==1) #define FALSE (O==1) ' ' //===== GLOBAL MACRO DEFINITIONS === #define NL " " // New Line string #define NL ' ' // New Line char #define LF " " // Line Feed string #define cLF // Line Feed char #define CR " " // Carriage Return string #define CCR '\ // Carriage Return char #define CRLF " " // Carriage Return/Line Feed #define LFCR " " // Line Feed/Carriage Return #define FF // Form Feed #define NUL "10" // Null string #define CNUL // Null character #define BLK // Single Blank string #define CBLK // Single Blank character "\f" #define cuScore ! #define UScore " #define CZERO TO' // Underscore character // Underscore string. // Single ZERO character #define HTAB #define cHTAB #define VTAB #define CVTAB #define ESC "\t" "It' "\v" "Iv' "\x1B" // Horizontal Tab string // Horizontal Tab character // Vertical Tab string // Vertical Tab character // Escape string [hex(1B) = dec(27)] 84 // ===== GLOBAL STATEMENT FUNCTION DEFINITIONS === #define F_MIN (V1, V2) (((v1) (v2))? (v1): (v2)) #define ZF_MIN (v1, V2) (F_MAX(@, (F_MIN( (v1), (v2))))) #define ZF(v) (F_MAX(0, (v))) #define F_NOT(v) (((v) == TRUE) ? FALSE: TRUE), #define F_ABS(v) (((V) >= 0 )? (v):(-(v))), // Return the less of v1 and v2 // Return the greater of v1 and v2 // Like F_MIN, but lower bounds the result at ZERO // Lower bounds the value "V" at ZERO. // Logical Negation. // Absolute value //===== GLOBAL Constants ======== #define MAX_NUM_LINES 500 #define MAX_LINE_LNG 81 // Max. number of lines. // Max. length of line. One extra byte for terminating null. 96 #define MAX_MSG_NUM 10 #define MAX_MSG_LNG 133 // Number of message lines. // Length of a message string: Max of 132 chars +1 for terminating null byte. //===== GLOBAL VARIABLES and STRUCTURE DEFINITIONS = char msa IMAX MSG NUM) (MAX MSG LNG]; / Message buffer. Can hold MAX_MSG_NUM messages, each MAX MSG_LNG chars long. 98 99 100 101 102 103 104 105 106 enum msg kind_type(MSG_INFO, MSG_WARNING, MSG_ERROR}; // MESSAGE type or kind of message. (Enumerated Type) // Information. // Warning. Error. //===== GLOBAL FUNCTION PROTOTYPES === int_read_text (char *p_text[], FILE *fp ); 108 109 void write_text inorder(char *p_text(), int nlines); 112 void write_text_reverse(char *p_textil, i nt nlines); 114 115 116 117 118 void report_message (char enum msg [MAX_MSG_NUM] [MAX_MSG_LNG), msg_kind_type msg_kind ); 120 121 int main(int argc, char *argv[]) { //=== setbuf(stdout, NULL); 122 123 int nlines; int return_val; // No. of lines read from input file. // Main program return value to os (Operating System). 125 126 127 char *p_text [MAX_NUM_LINES]; 128 129 // Array of pointers to "char" // Have a total number of MAX_NUM_LINES pointers. // Input file name. // Input file pointer. 130 char filename_in[] = "text.txt"; FILE *fp_in = NULL; 131 return_val = @; // Assume all is well. 132 133 134 135 // Initialize "msg" message buffer by filling entire buffer with NULLS. for (int i=0; i 0) { 147 write_text_inorder(p_text, nlines); write_text_reverse(p_text, nlines); // Call subprogram to print in order. // Call subprogram to print in reverse order. 149 }else { // Handle excessive number of input lines. Indicated by "nlines = -1" sprintf(msg[0], "%s", "Input file too large to process."); sprintf(msg[1], "s[d]", "Max. number of input lines allowed = ", MAX_NUM_LINES); sprintf(msg[2], "%s", "Program Execution Aborted."); report_message(msg, MSG_ERROR); return_val = -1; // Set up to return ERROR condition (-1) to os. 157 158 fclose(fp_in); // Close input file "here" since it was opened successfully. 159 160 161 162 printf("Program Terminated. Have a great day."); 163 return (return_val); // Return to os. 164 165 166 167 168 169 170 171 172 173 174 int read_text(char *p_text[], // Array of pointers to char. FILE *fp ). // Input file pointer. //=========================== // This function reads consecutive lines of text from the input file "fp", until either the end of file is reached, or // the end of the array of pointers is reached. // Each input line is saved/stored in dynamic storage and it is accessed/referenced by a pointer of the p_text array. // That is, p_text[0] points to the FIRST input line, 11 p_text[1] points to the SECOND input line, and so on. // The function returns: 1) The array of pointers "p_text" that point to each input line. 112 ) The number of lines read, if all went well, otherwise 1/ 3) Return -1, if an error has occurred. 175 176 177 178 int int int char char int i; nlines; line_lng; line [MAX_LINE_LNG); *p; return_val; // Loop index. // Line counter of lines read. // Length of current input line. // Local storage for the current line of text just read. // Pointer to dynamically allocated storage. // Value returned by the function. 179 180 181 182 183 184 185 186 187 188 189 190 191 192 nlines return_val = 0; // So far, all is well. while ( (fgets(line, MAX_LINE_LNG, fp) != NULL) && (return_val >= 0)) { i = 0; while linelul != NUL) && (i 232 233 return; void write_text_reverse(char *p_text[], // Array of pointers to lines of input. int nlines) // Number of input lines pointed to/referenced by array of pointers. //===================================== // This subroutine prints all input lines of text referenced by the "p_text" array of pointers. // Input lines are printed in the REVERSE order that they were read in. // A suitable heading is printed to indicate the nature of the output. // The report_message() routine is called to output an appropriate "MSG_INFO" message if there are no lines of text to print. { // 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 return; void report_message(char msg [MAX_MSG_NUM (MAX_MSG_LNG), // Message buffer enum msg_kind_type msg_kind ) // Message kind, i.e. kind of message: INFO, WARNING, or ERROR. //=EEEEEEEEEEEEEEEEEEEEEEE ============== ========= // Given the 2D message buffer msg [] [] and the type/kind of message to print, this routine prints the message in a "standardized" format. // Each message type has a unique prefix which is printed with the first message line to serve as a "label" for the message, and each // subsequent message line, if present, is indented accordingly. // Each line of the message buffer is printed sequentially, starting with the first line (index = 0), and ending when either the // last line of the buffer is printed, or the first NULL line is encountered. { static char pname'] = "report_message"; // sub-program name. 261 263 264 register int i; static char info_prefix] static char warn_prefix] static char err_prefix] static char indent[] = "::> MSG: "; = "*> WRN: "; = ">>> ERR: ": printf(NL); // Print a blank line to separate the message from previous output. 266 267 268 269 270 // Switch on "msg_kind" to determine which message prefix to output! 271 272 273 switch (msg_kind) { case MSG_INFO: printf(info_prefix); break; D case MSG_WARNING: printf(warn_prefix); break; // A "Switch" statement is the ONLY place where a "break" is allowed because it // is part of the "Switch Syntax". 278 279 case MSG_ERROR: printf(err_prefix); break; 280 281 282 283 284 285 286 287 288 default: // Always check for an "un-handled" msg_kind! printf(">>>>>>: INTERNAL Error in function: %s ", pname ); printf(">>>>>>: Invalid msg_kind : [%d] ", msg_kind); break; // Output the contents of the "msg" buffer, starting with the first message line. // NOTE: The first message line is ALWAYS output because message buffer is // guaranteed to contain at least one message line. 289 290 msg [0] [MAX_MSG_LNG -1) = (NUL; 291 292 293 294 // Null last position of message, in case message is longer than buffer, or // message is not nulled. Can never be too trustworthy when it comes to handling // strings in "C". 295 printf("%s ", msg[0]); // Always print first message! 295 296 297 printf("%s ", msg[0]); // Always print first message! for (int j=0; j