This page does not represent the most current semester of this course; it is present merely as an archive.
When you log into a department server you have access to a large public shared space called /bigtemp
. I have placed in that directory a folder called /bigtemp/coa1
which we will use like a wall of mail cubbyholes for this assignment.
You will write a program that will do the following:
If there is a file named /bigtemp/coa1/mst3k.chat
, where mst3k
is replaced by your ID, the program will show that file’s contents to the user and then empty the file.
The program will ask the user the computing ID of the person they want to send a message to.
The program will ask the user what message to send them.
The program will append your ID, followed by a colon, followed by the message, to the file /bigtemp/coa1/mst3k.chat
, where mst3k
is replaced by the recipient’s ID, and change its permissions so others can read and write it.
You are welcome to assume that no string will be bigger than 4096 characters and use a global char buffer[4096]
instead of trying to handle malloc
, etc, in this lab.
You’ll probably find it useful to work in pairs so you can send each other messages.
Linux provides access to several “environment variables”. One of them (USER
) has, as its value, your current login ID. You can retrieve this using the getenv
function in stdlib.h
:
The following will print your ID to the terminal, duplicating the behavior of the built-in whoami
command.
int main() {
char *me = getenv("USER");
(me);
putsreturn 0;
}
There are two basic tools for building a string out of component parts. Either one will work fine for this task, and both Both of them require a pre-allocated destination buffer.
strcat
The strcat
function from string.h
is similar to +=
for strings in Java or Python.
After running the following
[0] = '\0';
buffer(buffer, "/bigtemp/coa1/");
strcat(buffer, "mst3k");
strcat(buffer, ".chat"); strcat
the array buffer
contains "/bigtemp/coa1/mst3k.chat"
sprintf
The snprintf
function from stdio.h
does a formatted string construction, like the string’s .format
method in Java or Python. It uses the same formatting specifiers as printf
, with both the power and complexity that that entails.
After running the following
(buffer, 4096, "/bigtemp/%s/%s.chat",
snprintf"coa1",
"mst3k",
);
the array buffer
contains "/bigtemp/coa1/mst3k.chat"
. It will not overrun 4096
characters, even if it were given very large strings to format.
To work with files, you want to fopen
them, access their contents, and then fclose
them. The following components will help:
Opening:
FILE *inbox = fopen(filepath, "r");
returns NULL
if filepath
is not the path to a real file, and a pointer to a FILE
structure opened in read-only mode otherwise.
There is a special FILE *
named stdin
that is always open and reads from what the user types into the terminal window.
FILE *outbox = fopen(filepath, "a");
returns NULL
if filepath
exists but you are not allowed to write to it, and a pointer to a FILE
structure opened in append-only mode otherwise.
There is a special FILE *
named stdout
that is always open and displays to the terminal window.
Writing:
fwrite(buffer, sizeof(char), 23, outbox);
writes 23 char
-sized values from buffer
to FILE *outbox
. Use it if you know how many bytes you want to write.
fprintf(outbox, "%s: %d\n", "mst3k", 2501);
writes mst3k 2501
and a newline to FILE *outbox
. Like snprintf
, this gives a lot of power with corresponding complexity.
Reading:
size_t got = fread(buffer, sizeof(char), 4096, inbox);
reads up to 4096 char
-sized values from FILE *inbox
into buffer
, and returns the number read (which is often less than 4096 if inbox
did not have that many characters).
This does not work well for stdin
as the inbox
parameter, as you don’t generally know how many characters the user will type. fgets
is better for most user interactions.
char *line = fgets(buffer, 4096, inbox);
reads one line of text from FILE *inbox
, or 4096 characters, whichever is less. It returns buffer
on success and NULL
on failure. The returned string includes the newline, as e.g. "mst3k\n"
This works well for stdin
as the inbox
parameter, as users generally type one line at a time.
Permissions, etc:
chmod("/bigtemp/coa1/mst3k.chat", 0666);
sets the permissions for mst3k.chat
based on a bit-vector of flags, specified in octal (hence the leading 0
):
07
to 1112), the bits mean may-read, may-write, and may-execute, in order.0666
is a reasonable permission set.truncate("/bigtemp/coa1/mst3k.chat", 0);
truncates a file so its new size is 0 bytes. This is useful in re-setting a chat file after the user has read its contents.
You can manually check for the existance and permissions of a file by running
ls -l /bigtemp/coa1/mst3k.chat
Note that many bugs end up creating the wrong file name; the following will list all files in the directory with the newest file last
ls -ltr /bigtemp/coa1/
You can force-add a message to a mailbox by running
echo "This is a message" >> /bigtemp/coa1/mst3k.chat
chmod 666 /bigtemp/coa1/mst3k.chat
You can read all messages by running
less /bigtemp/coa1/mst3k.chat
These can be useful in testing different aspects of your program independently. But don’t mess with other student’s mailboxes in this way without their permission.
If you try to read from a non-existent file, open a file in a non-existent directory, or access a file for which you do not have permissions, the functions you use will fail (and set errno
).
The following shows how a pair of students, aa1a
and tj0uva
might chat with one another. What the user types is shown like this.
User aa1a does |
User tj0uva does |
---|---|
$ ./a.out You have no new messages Who would you like to message? tj0uva What do you want to say? Hello, is this working? |
|
$ ./a.out Your messages: aa1a: Hello, is this working? Who would you like to message? aa1a What do you want to say? Yes, works well! |
|
$ ./a.out You have no new messages Who would you like to message? aa1a What do you want to say? How are you, by the way? |
|
$ ./a.out Your messages: tj0uva: Yes, works well! tj0uva: How are you, by the way? Who would you like to message? |
Some notes for the ideal implementation:
getenv
to figure out the name of the user running the program. Don’t hard-code it or ask them.
argc
is 2, use argv[1]
instead so users can run as e.g. ./a.out tj0uva
to pretend to be TJ. If you do that, don’t go erasing other student’s mailboxes…chmod
each file you touch to be 0666
so that others can work with it too.": "
to each message you put in a mailbox so the recipient can see who wrote it.You don’t need to do all of that to check off (showing the TA a basic implementation is enough), but we encourage you to work on all of the above features to better prepare you for future assignments.