C & POSIX

Assigned: Tuesday, Jan 26, 2016

Due: Monday, Feb 1, 2016 Tuesday, Feb 2, 2016 at 10:30pm

Collaboration: You should work with your assigned partner for this lab. You may use your classmates and their code as a resource, but please cite them. Sharing of complete or nearly-complete answers is not permitted.

Many of you haven’t used the C programming language since 161, so there’s a good chance you’ll feel a little rusty. After working with more modern languages like Java or Ruby, it’s easy to forget what it was like to live in the anything-goes, hoist yourself by your bootstraps world of C programming. By modern standards, the functionality built into C and its standard library is minimal and a bit cumbersome. This lack of built-in functionality often means you’ll be implementing your own basic functionality, which gives you an incredible degree of control over what your program does and how it does it. At the risk of perpetuating a cliche, I’ll emphasize that with great power comes great responsibility.

You aren’t completely on your own with C, though. The C language has been closely matched with the POSIX standard library, which provides quite a few handy utilities that are certainly worth using. The purpose of this lab is to help you regain some familiarity with the C programming language and get a sense of what’s available for reuse rather than reimplementation.

Getting Started

No pun this time, but this section really could have been called “gitting started.”

Yesterday, you used git entirely through GitHub’s web interface. From now on, we’ll mostly use git using the command line git tool. Go to the GitHub project for this lab, github.com/grinnell-cs/213-warmup, create a fork, and add your lab partner as a collaborator just as you did yesterday.

On your fork (not the main repository page), click the “SSH” drop-down button just above the list of files and select “HTTPS”. Copy the contents of the text field to the right and open a terminal. In your terminal, use the cd and mkdir commands to create/move to a directory where you would like to make a local copy of the repository. Once you’re there, run the command git clone <paste the URL you copied here>. Git will ask for your GitHub username and password, work for a few seconds, and then you’ll have a local copy of the repository.

While this local copy is different from a fork (GitHub doesn’t have a page for it) it works in much the same way. You can make changes to your code locally and commit them to the repository. Once you are ready to publish these changes, you can push them up to GitHub. If your partner has made changes and pushed them, you can pull them from GitHub to your local copy. Finally, once you have committed and pushed all your changes, you can create a pull request from your GitHub fork to send me a list of changes you would like me to accept (or, in this case, to grade and not accept into my starter code).

Git Reference

The git project has good documentation ranging from basic git commands to very complex issues that come up in large projects. There is also a useful guide for GitHub’s workflow, which extends the basic functionality of git with some good practices and a handy web interface. You’ve already seen this with yesterday’s mini-lab, but there are plenty of details we skipped over.

There are many other git guides in GitHub’s bootcamp, which are of varying quality. If you find yourself really stuck using git, odds are I can help you figure it out; don’t spend more than a few minutes resolving a git-related issue before you ask for help.

Part A: Shuffling a Deck of Cards

The code you checked out should have three directories, one for each part of this lab, along with some Makefiles, a README, and a file called common.mk. Take a look at the contents of each Makefile, including the one in the top directory. This is a pattern that makes it easy to build multi-directory and multi-source file projects. You can ignore the “magic” in common.mk for now, but I’d be happy to tell you about it if you’re interested.

To build your code, type make in the partA directory (or at the top level to build everything). This will create an executable in each directory, along with an obj directory to hold the intermediate files used for compilation. Type make clean if you want to get rid of all the intermediate files; this is a common step before you commit things to git because you generally won’t want to commit generated files, only source code.

One key thing to remember about C is that types are more of a suggestion than a rule; chars are just numbers that, by convention, correspond to specific characters. There are quite a few nice built-in characters like the following suits of cards: ♥, ♦, ♣, and ♠. Use these characters to print out all of the cards in a deck (2–10, Jack, Queen, King, and Ace of each suit). Hint: Assign a number to each unique card so you can print it easily. You don’t need to use a lookup table here if you can come up with a nice formula to turn a number between 0 and 51 into a specific card.

Once you’ve changed at least one file, run the command git status in your copy of the repository. This will tell you what has changed, and if there are any unrecognized files. The command git add <filename> will move changes up into the “changes staged for commit” section, which means those changes will be committed to your local repository when you run the command git commit -m "informative commit message here". Instead of staging changes, you can just commit everything that has changed with the command git commit -a -m "informative commit message here" (the -a flag is the only difference).

Be sure to commit frequently, and periodically run git push to send your changes back up to GitHub. You’ll probably be asked to specify your name and email address before you do this the first time; just follow git’s directions and this should all work out.

Once you can print the entire deck, change your program so it will print a shuffled deck, which should be different on every run. Use the rand function to generate random numbers. Use the command man 3 rand to read about it, or go to this web-based manpage viewer: linux.die.net.

Part B: Using a Timer

For this part, you will be writing a procedure called every that takes three arguments: the number of times to run a function, the number of milliseconds between runs of that function, and the function itself. You can use either nanosleep and/or gettimeofday to implement this; it’s possible to solve the problem with one or the other, but the “best” solution will use both.

Depending on how you implement every, you may see different behavior with your implementation. What happens if the function you have every run takes a long time? What if that runtime is larger than the interval between executions? Ideally, your implementation of every should start the specified function at the given interval, regardless of how long it takes to run; if the function takes one second and the interval is two seconds, you should just pause one second between runs. You are not required to pause for the exact amount of time if the function takes longer than the specified interval.

If you are looking for an additional challenge, implement a version of every that runs in the background. You’ll need to use timer_create and signal to do this. One nice perk of this approach is that you can have multiple “tasks” running with every at any given time.

Part C: Counting Words

Use the fgets function to read a string from user input (a file named stdin), and count the number of words in the input. For simplicity, a “word” is any sequence of characters bounded by whitespace (space, tab, or line break) or the beginning/end of the input. Use strtok or a similar POSIX function to implement your word counting utility; solutions that do not use POSIX functions for at least some of the word counting process will not receive full credit!

Once you have it working, you should have your word counting utility print out the number of words using printf. You can test it with a command line invocation like this one:

> echo "the quick brown fox jumps over the lazy dog" | ./mywc
9

You should not assume any upper limit on the length of the input. You can use fgets to read in some amount of the input, count the words in that input fragment, then add it to the word count for the rest of the input. Be careful when combining word counts for fragments, as the fragment boundary may fall in the middle of a word.

Wrapping Up

Once you’re done, double check to make sure you’ve committed all your changes and pushed those commits to GitHub. Once everything is in your repository on GitHub, submit a pull request just like you did with the first mini-lab.