Lab overview
Lab 01
Lab 02
Lab 03
Lab 04
Lab 05
Lab 06
Lab 07
Lab 08
Lab 09
Lab 10
Lab 11
Lab 12
Lab 13
Review

[Course home page]

CSC 209 lab 06 exercises, week of 14 June 2022

[solutions are available (requires teach.cs authentication)]

Background

To do this lab you will need to be familiar with the files and strings material, and also "argv".

Attendance

As usual, please either run /u/csc209h/summer/present on the console of a lab workstation, or get the TA to record your attendance. Please do this first so that you don't forget.

Remember that you can check that your attendance has been recorded properly at https://wwwcgi.teach.cs.toronto.edu/~ajr/cgi-bin/auth/present


1. Struct

1) Please see the python3 program at /u/csc209h/summer/pub/lab/06/3n1.py

This uses a procedure (function) to return two values.

You can't do this in C (or, indeed, in hardly any traditional programming language).

But you can return a struct of two values. Use this technique to translate that 3n1.py program to C.

2) Consider the struct type

	struct asdf {
	    int x;
	    struct asdf *other;
	};

Declare two of these, set their 'x' values to different things, and set their 'other' values each to point to the other.

Write a function into which you can pass a pointer to just one of these two objects and it will printf whether you've passed in the one with the greater or the lesser value of x.


2. Files

1) Take your rot13 program from two weeks ago, or my version from /u/csc209h/summer/pub/lab/04/soln/rot13.c , and modify it to take a mandatory command-line argument which is a file name, and to read from that file instead of from stdin.

As always, you must check argc, and output an appropriate usage error if applicable.

Reminder: To read from a file other than stdin, instead of calling "getchar()" you call "getc(fp)".

2) Write a C program which reads all of the integers in the file /u/csc209h/summer/pub/lab/06/t3 and adds them together. (Output the sum.)

3) Write a C program named "incr.c" which increments a number in a file in the current directory named "count". (If the file doesn't exist, that's a fatal error.)
That is, read the contents, which must be an integer; and then overwrite the file with that number plus one.

4) Write an sh script which uses your "incr" (in a silly way) to add two numbers specified on the command-line ($1 and $2). To do this, put zero in "count", execute incr $1 times, then execute incr $2 times, then output the count. See if you can come up with a way to use our unix tools in a pipeline to execute incr the specified number of times, rather than writing a loop manually in sh.


3. Demonstrations of selected system calls and similar

Please look at the sample programs in /u/csc209h/summer/pub/lab/06.

They are all compiled there in that directory for you, but you can also copy the .c files to your own directory to modify them to test variations.

1) Examine the program /u/csc209h/summer/pub/lab/06/stat.c
The stat() and/or lstat() system calls will be needed for assignment two.

stat.c illustrates calling stat() on an argument file name and extracting various information about the file. The data returned from stat() is basically an "ls −l". This data is returned in a "struct" because there are too many values for it to be feasible to do it any other way.

See how when you run stat (or my stat.c) on a symlink, you get information about the pointed-to file (try it! — e.g. create a file named "foo" and then create a symlink to it with "ln −s foo bar"). This is the difference between stat() and lstat(). Examine and run lstat.c. (And try "diff stat.c lstat.c"!)

Note: If you just type "stat" as the command name, intending to run a file "stat" out of the current directory, you may instead get /usr/bin/stat, depending on how your PATH is set up. I suggest getting into the habit of typing "./file" to run a program in the current directory, and omitting dot from your PATH.

Normally we want to "follow" symlinks in the way that stat() does. But for some purposes we need to see the actual symlink. For example, to write "ls −l". For another example, your assignment two directory-traversing code (part 3 of assignment two) will need to use lstat() rather than stat() to traverse a directory tree properly, so that symlinks don't send it off in strange directions (possibly even infinite loops).

Three of the data values in the "struct stat" are time values. A time value in unix/linux is just a single integer which is the number of seconds since 00:00 1 Jan 1970 UTC, so it's particularly easy to work with, although difficult to convert into a string suitable for output to the user, but we have library routines for the latter.

2) Please see /u/csc209h/summer/pub/lab/06/time.c for some examples of manipulating time values. Note in particular how we achieve a time twelve hours later by simply adding the appropriate number of seconds; it's easy to work with time information when it's just an integer. But also note the conversion for display to the user.
In general, note that you often don't need to do even this when working with time values — they're just integers, and can just be passed around as such, and you can compare two of them to see which time is later, and so on.

3) Strings in C are represented as arrays of characters. Strings can be compared with strcmp(), which is used in assignment two (although it's not a system call). Please see /u/csc209h/summer/pub/lab/06/strcmp.c, which is just a command-line front end to strcmp(). Run this program with various strings in argv[1] and argv[2]. Note that strcmp() returns zero when the strings are equal.

4) For directory traversal in assignment two you will need to read a directory. The program /u/csc209h/summer/pub/lab/06/readdir.c illustrates using opendir(), readdir(), and closedir(). The struct returned by readdir() contains all of the information in one entry in the directory — which is merely an inode number and a name.

For stat() and lstat(), we pass in a pointer to a struct which is then filled in by the library routine; in contrast, readdir() declares its own struct and returns a pointer to it. Thus unlike in stat.c, with readdir() we're usually dealing with a variable which is a pointer to a struct, rather than being the struct itself.

So we could write things like "(*p).d_name", first dereferencing the pointer-to-struct, then accessing a member of the struct. This notation is cumbersome, though, and the parentheses are necessary because of the operator precedence rules. So there's a special notation in C which looks like a right-pointing arrow and is formed from the two characters '−' and '>'. "a−>b" is simply defined as "(*a).b". Thus instead of the cumbersome "(*p).d_name", we can write "p−>d_name". (It makes more of a difference in typing than in reading it! It also makes more of a difference when there are multiple levels of pointers to structs. And we'll see all of this more in future.)


4. To submit for credit

Write a C program whose command-line arguments are file path names. Call stat() on each of them and output the file path name which is the largest file, in accordance with "st_size". For the purposes of this exercise, let's assume that all st_size values will fit into an int.

(There is, of course, no output other than this file path name (exactly as specified in argv) and a newline.)

Warning: You want to take care in copying any portions of the supplied stat.c. In general, it's asking for trouble to copy code you don't want or understand. Write your own program, and make sure you know what's in there.

Call your program lab06.c, and submit it with

        submit -c csc209h -a lab06 lab06.c
(Other 'submit' commands are described at the end of the notes for lab one.)
And don't forget to run /u/csc209h/summer/present during the tutorial time, on the console of a tutorial lab workstation, or get the TA to record your attendance.

Your program must compile on the teach.cs machines with "gcc −Wall" with no errors or warning messages.


Q: Do we need to check argc and output a usage message?

A: Yes! Always! And the usage message must be in the standard format, and to stderr, and you must exit with exit status 1 in case of error or 0 in the normal case.

Q: Do we need to check stat's return value?

A: Yes! And call perror() properly if it fails, and exit with exit status 1 (do not print a maximum-sized file name!).