For each of these exercises, you will implement command line utilities that perform file I/O. Though we provide you with skeleton code for each exercise, we recommend that you first attempt them on paper, and convince yourself of your implementations’ correctness before compiling and running it.
For any code completed on paper, assume that any necessary header files are included for you. Make sure to consult the man pages for documentation about standard library functions.
myhead
Obtain the skeleton code from:
git clone ~j-hui/cs3157-pub/examples/myhead
Your task is to implement a simplified version of the head
program, myhead
.
Running myhead N
reads the first N
lines from stdin, and outputs to stdout;
if you run myhead
without arguments, it defaults to the first 10 lines.
Here is a sample shell session using the program:
$ echo "this file is one line" >one-line
$ ./myhead <one-line
this file is one line
$ touch empty-file
$ ./myhead <empty-file
$ ./myhead </usr/share/dict/words
A
AA
AAA
AA's
AB
ABC
ABC's
ABCs
ABM
ABM's
$ ./myhead 5 </usr/share/dict/words
A
AA
AAA
AA's
AB
$ ./myhead 0 </usr/share/dict/words
Here are some additional things to keep in mind:
You do not need to handle arbitrary binary input; only expect regular text.
Your implementation should support arbitrarily long lines.
We have already written the argument-parsing part for you, in myhead.c
:
int main(int argc, char **argv) {
/*
* Parse arguments
*/
if (argc > 2) {
fprintf(stderr, "%s\n", "usage: myhead [<num-lines>]");
exit(1);
}
// Default to 10 lines
int lines = 10;
if (argc == 2) {
lines = atoi(argv[1]);
}
/*
* YOUR SOLUTION HERE
*/
return 0;
}
myarchive
Adapted from midterm 2, Spring 2019
Obtain the skeleton code from:
git clone ~j-hui/cs3157-pub/examples/myarchive
Consider myarchive.c
, which takes a list of file names in the current
directory as arguments and combines them into a single stream to stdout,
and the program myexpand
, which reads a file produced by myarchive
from
stdin and extracts each file contained within it.
Both programs run without memory leaks or errors. Here is a sample session, where I archive and expand a pair files containing lyrics to popular songs:
$ gcc myarchive.c -o myarchive
$ gcc myexpand.c -o myexpand
$ cat all-star
Somebody once told me
$ cat 1612
Ooh, it's a tzimmes
But you need to t'set it
Sometimes I write a little song
So you don't forget it
Sometimes I write a little song
To remember the lyrics
$ ./myarchive all-star 1612 > sample.ar
$ mkdir testdir
$ cd testdir
$ ../myexpand < ../sample.ar
$ ls
all-star 1612
$ cat all-star
Somebody once told me
$ cat 1612
Ooh, it's a tzimmes
But you need to t'set it
Sometimes I write a little song
So you don't forget it
Sometimes I write a little song
To remember the lyrics
(7.2.1) Fill in the blanks to complete the implementation of myarchive
:
// From myarchive.c
int main(int argc, char **argv) {
FILE *fp;
char *fname;
char buf[4096];
struct stat st;
int n1, n2;
if (argc < 2) {
fprintf(stderr, "Usage: %s <filenames...>\n", argv[0]);
return 1;
}
while (*++argv) {
fname = *argv;
fp = ________7_2_1_1________;
/* Include null terminator in filename */
n1 = strlen(fname) + 1;
n2 = htonl(n1);
fwrite(&n2, 1, sizeof(n2), stdout);
fwrite(fname, 1, n1, stdout);
/* Get the size of the file in bytes */
stat(fname, &st);
n1 = htonl(st.st_size);
fwrite(&n1, 1, sizeof(n1), stdout);
while ( ________7_2_1_2________ )
fwrite( ________7_2_1_3________ );
fclose(fp);
}
return 0;
}
Hint: htonl()
is used to convert an integer from host byte order (on CLAC,
little endian) to network byte order (big endian); you should read its man
pages for details.
(7.2.2) Complete the implementation of myexpand
:
int main(int argc, char **argv) {
FILE *fp1;
char *p1;
int n1, n1_net;
FILE *fp2;
char *p2;
int n2, n2_net;
/*
* YOUR SOLUTION HERE
*
* Do NOT declare additional variables.
*
* You don’t have to use every variable declared above.
*
* For brevity, please omit error checking code; assume that all
* library function calls will succeed when invoked correctly.
*/
return 0;
}
Here are some things to keep in mind for myexpand
:
Assume that the input files are small so that the entire contents of each file can be read into memory.
Do not declare any new variables other than the ones already declared in the skeleton code.
Your implementation should handle arbitrary binary data.
Make an effort to write this out on paper first, and get the syntax right. At this point, I expect you can write correct C code without a compiler.
Hint: you should read the man pages for ntohl()
, which will help you
reverse the effects of htonl()
and convert from network byte order (big
endian) back to host byte order (little endian).