Homework • Introduction to HW7 – Complexity similar to HW6 – Don’t wait until last minute to start on it • File Access will be needed in HW8 1 Intro to Homework 7 • Data Structures behind the “Mapping” Maplist L char *key int length char *data Block *head Block *next Block *tail char *key char *data Block *next “” “” All “real” Blocks Are linked in here “” “” NULL 2 Intro to Homework 7 • Why include the fake head and tail Blocks? • So that we can use a pointer to the Block previous to the Block that we are accessing • For example: Deleting a Block from the list: if(strcmp(bptr->next->key, key) == 0) { sptr = bptr->next; bptr->next = bptr->next->next; free((void *)sptr->key); free((void *)sptr->data); free((void *)sptr); } 3 Intro to Homework 7 • If we didn’t do that, we would need to either: – Use a doubly linked list so that we could back up to remove a Block from the linked list OR – Save a pointer to the previous Block as we moved down the linked list so that we could back up to remove a Block from the linked list • Both of these solutions can be made to work, but are a bit “messy” by comparison 4 File Access, K&R 7.5 • Dealing with named files is surprisingly similar to dealing with stdin and stdout. • Start by declaring a "file pointer": FILE *fp; /* See Appendix B1.1, pg. 242 */ • <stdio.h> header contains a structure definition with typedef name FILE that contains component variables (buffer, etc.) used in file I/O • You don't need to know the details of these structs to use simple file I/O • Use standard file access functions such as fopen() 5 File Access • To open a file • Function prototype fopen : FILE * fopen(const char *name, const char * mode); • To ask fopen to open file (character string "name") in a particular "use mode". fp = fopen(name, mode) • fp is the return value: set to NULL if fopen fails • Legal “use mode” values: "r" for read, "w" for write, and "a" for append 6 File Access • An "r" mode file can be used like stdin • Function prototype getc: int getc(FILE *stream); • To get a char from an "r" file c = getc(fp); • c is the return value: Return value is the character read from the file OR Set to EOF if getc fails 7 File Access • A “w“ or “a" mode file can be used like stdout • Function prototype putc: int putc(int c, FILE *stream); • To put a character to a “w” or “a” file status = putc(c, fp) • Return value: set to EOF if putc fails 8 File Access • When we fopen a file in "w" or "a" mode: • If the file does not already exist, it will be created (like the vi editor creates a new file). • If the file does already exist, then "w" mode fopen will destroy the old contents (like the command mv) and "a" mode will append new contents to the end of the existing file (like "save" command in mail). 9 File Access • When you have finished reading from a file or writing to a file, call fclose to close the file status = fclose(fp); • Function prototype: int fclose(FILE *stream);) • Function fclose( ) returns: Zero for success OR EOF if an error occurs 10 File Access • Every file open requires resources and there is a limit on number of files open at any one time – close each fp when done using it – close all files before program termination • FILE structure has a buffer for disk data in memory • When putc returns data may not get written to file THE DATA IS NOT SAFELY ON DISK YET! • Functions fclose( ) and fflush( ) flush buffer to disk 11 File Access • When a C program is started, the operating system opens three files and provides file pointers (FILE *) to them: stdin, stdout, and stderr • We can now define getchar and putchar as macros: #define getchar( ) getc(stdin) #define putchar(c) putc((c), stdout) /* why (c) in parens? */ • Other file oriented analogs for input / output functions: int fscanf(FILE *fp, char *format, . . .); /* mode must be "r" int fprintf(FILE *fp, char *format, . . .); /* mode "w" or "a" 12 Error Handling • Trying to read a file that does not exist is an error, • There are other errors as well: reading or writing a file without appropriate permission • There are three streams opened by the O/S when a program begins execution, stdin, stdout, and stderr. • stderr usually goes to the screen even if stdout is redirected to a file prog . . . >outfile prog . . . >&outfile prog . . . >>outfile /* redirect stdout; overwrite */ /* redirect stdout and stderr */ /* redirect stdout; append */ 13 Error Handling • How to write a program so error msgs go to stderr? char *prog = argv[0]; /* pick up command name */ if ((fp = fopen(*++argv, "r")) == NULL) { fprintf(stderr, "%s: can't open %s\n", prog, *argv); exit(1); } • Variable prog is a pointer to char array containing the command name used to invoke this program and *argv is a pointer to char array containing the file name that couldn’t be opened 14 Error Handling • The exit(int) function (arg: 0-255) terminates program execution and returns argument to invoking process (debugger, shell, fork parent) • Of course, a "return value" from main program would do this as well, but exit() will terminate execution as if we executed a return from main(), and can be called from anywhere in program! • A zero returned by a program means no error • You can use conventions for meaning of non-zero values - best to keep the values positive 15 Error Handling • To show how the return value may be used: • UNIX conditional sequence && % gcc myprog.c && a.out Second program executes only if first returns == 0 • UNIX conditional sequence || % gcc myprog.c || echo compilation failed Second program executes only if first returns != 0 16 Error Handling • Note a problem with program on pg. 163 • To handle errors, should use: #include <errno.h> • The function ferror() tells us the last error that occurred for a stream if (ferror(stdout)) { fprintf(stderr, “%s: error writing stdout\n”, prog); exit(2); } 17 Error Handling • If not exiting, to avoid retaining a stale error value from a previous failed operation, use: void clearerr(FILE *stream); • Example: clearerr(stdout); 18 Error Handling • More generally, errno.h contains a macro "errno" that can be tested; it is zero if there is no problem and non-zero otherwise • Text in B1.7 says errno "may" contain an error number; it will contain one if there has been an error—any error, not just in a stream—unless the error is so serious it corrupted the error structs • We can use the function perror to write out the error msg associated with errno, but we have to test for error right after it occurs to get right one 19 Error Handling • We can use the function perror to write out standard error message associated with errno, but we have to test for error right after it occurs to get correct one if (errno != 0) { perror("Error at myprog: exiting."); exit(2); } • perror will print out a standard error message corresponding to integer in errno, as if by: fprintf(stderr, "%s: %s\n", s, "error message"); 20