COMS 3157 Advanced Programming

Recitation 7: File I/O

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.

7.1 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:

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;
}

7.2 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: