Entering the Echo Chamber
The first command line utility we will study is called echo
. What does echo
do? Well, it echoes. Yep, that’s right:
[mmalensek@mmalensek-vm ~]$ echo
[mmalensek@mmalensek-vm ~]$
Hmmm?
[mmalensek@mmalensek-vm ~]$ echo Hello World!
Hello World!
[mmalensek@mmalensek-vm ~]$
Okay, yeah. You get it. This might not seem like a useful program, but it does come in handy when you want to print something to the terminal from a script – a bunch of terminal commands used to automate something. Or, even more useful, you can print environment variables (or really any shell variable) with it:
[mmalensek@mmalensek-vm ~]$ echo $HOME
/home/mmmalensek
[mmalensek@mmalensek-vm ~]$ echo $?
0
I can use it to find out my home directory, or the exit status of the last command. Neat!
What can echo do?
If we want to know exactly what echo
is capable of, we can use the manual pages: run man echo
to find out all about it. If you run the command on your Linux VM, you’ll notice that echo supports a LOT of different command line flags: things that set options that will effect how the program runs. For example, echo -n Hello World!
does not print a newline character (\n
) at the end of the string.
The BSD version of echo
is a bit simpler:
NAME
echo – write arguments to the standard output
SYNOPSIS
echo [-n] [string ...]
DESCRIPTION
The echo utility writes any specified operands, separated by single blank (‘ ’) characters and followed
by a newline (‘\n’) character, to the standard output.
The following option is available:
-n Do not print the trailing newline character. This may also be achieved by appending ‘\c’ to the
end of the string, as is done by iBCS2 compatible systems. Note that this option as well as the
effect of ‘\c’ are implementation-defined in IEEE Std 1003.1-2001 (“POSIX.1”) as amended by Cor.
1-2002. Applications aiming for maximum portability are strongly encouraged to use printf(1) to
suppress the newline character.
Some shells may provide a builtin echo command which is similar or identical to this utility. Most
notably, the builtin echo in sh(1) does not accept the -n option. Consult the builtin(1) manual page.
EXIT STATUS
The echo utility exits 0 on success, and >0 if an error occurs.
Part 1: echo
For the first part of this lab assignment, you’ll build your own version of echo
called sfecho
. Because yours is from San Francisco, where everything’s better. Like Rice-A-Roni… and Folger’s Coffee.
Requirements
- Read the command line arguments passed to your program and print them out. When finished, print a trailing newline (
\n
) character - If you encounter the
-n
flag, don’t print the newline - Exit. You’re done!
If you want to go the extra mile, implement the rule described in the man
page above: if the last argument ends in \c
then also disable the trailing newline.
Reminder: Building your program
Make sure that your source file is called sfecho.c
and stored under a directory called lab2
in your lab repository. To compile it, use:
cc -Wall sfecho.c -o sfecho
The -Wall
flag tells the compiler to turn on all warnings.
Run with:
./sfecho Hello world
Hello world
Testing echo
Here are some test cases to verify that everything is working properly. You’ll notice that removing the newline at the end of the output makes the shell prompt display in a somewhat weird way – that’s normal.
[mmalensek@mmalensek-vm ~]$ ./sfecho Hello World!
Hello World!
[mmalensek@mmalensek-vm ~]$ ./sfecho -n Testing testing
Testing testing[mmalensek@mmalensek-vm ~]$ ./sfecho $SHELL
/bin/bash
[mmalensek@mmalensek-vm ~]$ # Arguments are separated by any amount of space,
[mmalensek@mmalensek-vm ~]$ # So our program won't see these:
[mmalensek@mmalensek-vm ~]$ ./sfecho This is a TEST
This is a TEST
[mmalensek@mmalensek-vm ~]$ # Quotes will make this appear as a single argument:
[mmalensek@mmalensek-vm ~]$ ./sfecho 'Hello World!'
Hello World!
[mmalensek@mmalensek-vm ~]$ ./sfecho
[mmalensek@mmalensek-vm ~]$ ./sfecho -n all done.
all done.[mmalensek@mmalensek-vm ~]$
Part 2: Handling Command Line Options
Handling the -n
flag above is harder than it seems: should it take effect if it appears as any of the arguments? Only the first argument? And if it appears in the middle, then do we need to find a way to remove it from the argv
array? Further, from a maintainability perspective, hard-coding options seems like a bad idea. A better way to do this is to use a standard library so our program will behave similarly to other Unix utilities… we need getopt!
For Part 2 of this lab, integrate getopt
into your utility. You should support the -n
option as well as -h
to print a help message explaining how to use your program.
Part 3: Encryption
Everyone knows that computer security is so hot right now, so of course you want in on the action. For the final part of this lab, you will add encryption support to sfecho
using the ultra-secure and super-safe ROT-13 as inspiration.
Our main goal here is to get more familiar with pointers. You’ll create a function called rotate that looks like this:
/**
* Rotates a given character in place within the printable ASCII range.
*
* @param ch The character to rotate
* @param places Number of character places to rotate
* @return 0 if the rotation was successful, -1 otherwise. E.g.,
* if the character is not valid or not within the printable
* range.
*/
int rotate(int *ch, int places);
Which takes an original character and rotates it within the printable ASCII range. Check out an ASCII table to get an idea of the numeric representation of each letter. That’s how the characters are represented in memory. ‘a’, for instance, is 97 in ASCII, and ‘z’ is 122. To rotate a character, you are adding to its value. For example, rotating A
by 1
would change the character to B
, or rotating A
by 2
changes it to C
.
HINT: We’ll leave spaces out of the ASCII “printable range.” That means the range starts with !
and ends with ~
, for a total of 94 characters. If a character is outside the printable ASCII range and isn’t a space, your function should return -1
.
After rotating, the neat thing is that we can rotate again to get our original string back (as long as we rotate enough to get back to the original character). Since we have 94 printable ASCII characters to consider, if you rotate a string by 47
and then by 47
again, you should get the original back.
Add a new -r
flag that rotates the output string by a given amount, e.g., ./sfecho -r 13 Testing
would output ar"#v{t
. Here’s a few more examples:
[mmalensek@mmalensek-vm ~]$ ./sfecho -r 1 Testing Testing 123
Uftujoh Uftujoh 234
[mmalensek@mmalensek-vm ~]$ ./sfecho -r 22 CS 521
Yi KHG
[mmalensek@mmalensek-vm ~]$ ./sfecho -r 72 Yi KHG
CS 521
[mmalensek@mmalensek-vm ~]$ # And of course, we can combine command line options:
[mmalensek@mmalensek-vm ~]$ ./sfecho -r 3 -n ABC HELLO
DEF KHOOR[mmalensek@mmalensek-vm ~]$
Grading and Submission
You should provide a Makefile
with your lab that builds sfecho
. It should also include clean
and install
recipes (install to /usr/local/bin
). When we test your code, we’ll assume sfecho
has already been installed on your VM and we can run it from any directory simply by entering sfecho
.
Once you are finished with the lab, check your code into a directory called lab2
in your lab repository.
To receive 80% credit:
- Implement the basic echo command (Part 1)
To receive 90% credit:
- Complete all previous requirements
- Use the
getopt
library for handling command line arguments - Add a help function that prints basic usage information when the
-h
flag is passed in.
To receive full credit for this lab:
- Complete all previous requirements
- Implement the rotation-based encryption option for
sfecho
(-r
option) - Makefile that builds, cleans, and installs your program.