IC221: Systems Programming (SP14)


Home Policy Calendar Syllabus Resources Piazza

Lab 03: C Programming, mycp, mycat and mywc (SOLUTIONS)

Table of Contents

Task 1

Work in the file src/swap.c.

  1. Complete the function swap() that takes pointers to two int's and swaps their values via pointer derferencing. Test your function in main.
  2. Create a function swap_pair() which takes a pointer to the struct pair and swaps the order of the pair using your swap() function.

When correct, the output of the program should be as follows:

#>./swap 
BEFORE SWAP: a: 10 b: 20
AFTER SWAP: a: 20 b: 10
----------------------
BEFORE SWAP: p.first: 50 p.second: 60
AFTER SWAP: p.first: 60 p.second: 50

(HELPFUL HINTS) Recall that when you have a pointer to a structure, to access it's element you use the -> operator. For example, a print_pair function would look like this:

print_pair(pair * p){
  printf("first: %d second: %d\n", p->first, p->second);
}

However, if you are working with the data type itself, then you use the . operator to access items, like in:

pair p;
p.first = 50;
p.second = 60;
printf("first: %d second: %d\n", p.first, p.second);

Solution

#include <stdio.h>
#include <stdlib.h>

struct pair{
  int first;
  int second;
};


void swap(int *, int*);
void swap_pair(struct pair *);


//TODO: complete swap and swap pair;
void swap(int *a, int *b){
  int tmp;
  tmp = *a;
  *a = *b;
  *b = tmp;
}


void swap_pair(struct pair * p){
  swap(&(p->first), &(p->second));
}

int main(int argc, char * argv[]){

  int a,b;
  struct pair p;

  a = 10;
  b =20;

  printf("BEFORE SWAP: a: %d b: %d\n", a,b);
  swap(&a,&b);
  printf("AFTER SWAP: a: %d b: %d\n", a,b);

  printf("----------------------\n");

  p.first = 50;
  p.second = 60;

  printf("BEFORE SWAP: p.first: %d p.second: %d\n", p.first,p.second);
  swap_pair(&p);
  printf("AFTER SWAP: p.first: %d p.second: %d\n", p.first,p.second);

}

Task 2

Work in the file src/stirngex.c.

  1. Complete the function mystrcpy() which takes as input two char *, to and from, and copies the string at from to the string at to.
  2. Complete the function reverse() which takes a char * as input and reverses the order of the string.

For both of these functions you may use array access to perform string operations. To assist, you may use the string library function strlen() which will give the length of a string, not including the terminating NULL.

For 5 point Extra Credit, complete your mystrcpy() and reverse() methods using only pointer arithmetic. Indicate this in your README file.

Solution

#include <stdio.h>
#include <stdlib.h>
#include <string.h> //for strlen()

void mystrcpy(char * from, char * to){
  while( (*to++ = *from++));
}

void reverse(char * str){
  char *p = NULL;
  char tmp;

  for(p=str+strlen(str)-1; p != str && p > str; p--, str++){
    tmp = *str;
    *str = *p;
    *p = tmp;
  }

}

int main(int argc, char * argv[]){

  char hello[] = "Hello World!";
  char hello_cp[100];

  mystrcpy(hello, hello_cp);

  printf("%s\n", hello_cp);

  printf("-----------\n");

  reverse(hello);
  printf("%s\n",hello_cp);
  printf("%s\n", hello);

}

Task 3

You will complete two small programs, save.c and recall.c found in the src directory of the lab folder.

  1. The program save will read from stdin and save what is read to a file called saved.out. It should report an error if it can't open saved.out for writing.
  2. The program will try and open the saved.out file and write it's contents to the screen. It will report an error if saved.out does not exist.

Here is some sample usage to compare against:

#> cat hemmingway.txt | ./save 
#> ls saved.out 
sved.out
#> diff hemmingway.txt saved.out 
#> diff hemmingway.txt saved.out #will show nothing if the same
#> ./recall 
One hot evening in Padua they carried him up onto the roof and he could look out over the top of the town. There were chimney swifts in the sky. After a while it got dark and the searchlights came out. The others went down and took the bottles with them. He and Luz could hear them below on the balcony. Luz sat on the bed. She was cool and fresh in the hot night.
(...)
#> rm saved.out
#> ./recall
ERROR: Cannot open output file for reading

Solution

#include <stdio.h>
#include <stdlib.h>


int main(int argc, char * argv[]){

  int c;
  FILE * output;

  if((output = fopen("saved.out", "w")) == NULL){
    fprintf(stderr, "ERROR: Cannot open output file for writing");
    return 1;
  }

  while((c = fgetc(stdin)) != EOF){
    fputc(c, output);
  }

  fclose(output);

  return 0;
}
#include <stdio.h>
#include <stdlib.h>


int main(int argc, char * argv[]){

  int c;
  FILE * input;

  if((input = fopen("saved.out", "r")) == NULL){
    fprintf(stderr, "ERROR: Cannot open output file for reading\n");
    return 1;
  }

  while((c = fgetc(input)) != EOF){
    fputc(c, stdout);
  }

  fclose(input);

  return 0;
}

Task 4

  1. Write your own version of the cp function, mycp which takes two command line arguments, from and to, and copies a file byte-by-byte using fgetc() and fputc(). Your verision of mycp should report an error on the following two conditions:
    • The from file cannot be opened with read permission
    • The to file cannot be opened with write permission
    • An invalid number of arguments was provided.

    Here is some sample usage of mycp to compare against:

    #> ./mycp BeatArmy.txt CopyArmy.txt
    #> cat BeatArmy.txt 
    Go Navy!
    #> cat CopyArmy.txt 
    Go Navy!
    #> ./mycp BADFILE copy
    ERROR: Cannot open input file for reading
    #> ./mycp copy BADFILE
    ERROR: Cannot open output file for reading
    #> ./mycp copy 
    ERROR: Invalid number of arguments
    #> ./mycp 
    ERROR: Invalid number of arguments
    
  2. Write your own version of cat function, mycat which has the same operations as cat.
    • Without arguments, it should read from stdin and write to stdout
    • With file arguments, it should attempt to open those files and write them to stdout in the order provided
    • If an argument is - then it should read from stdin for that argument. To do this comparison of the argument to "-" use strcmp(), look it up in the manual for details.

    Here is some sample usage of mycat to compare against:

    #> cat BeatArmy.txt | ./mycat 
    Go Navy!
    #> ./mycat BeatArmy.txt
    Go Navy!
    #> ./mycat BeatArmy.txt GoNavy.txt 
    Go Navy!
    Beat Army!
    #> ./mycat BeatArmy.txt - < GoNavy.txt 
    Go Navy!
    Beat Army!
    #> ./mycat BeatArmy.txt - BeatArmy.txt < GoNavy.txt 
    Go Navy!
    Beat Army!
    Go Navy!
    #> ./mycat BeatArmy.txt BADFILE GoNavy.txt 
    Go Navy!
    mycat : Cannot open file 'BADFILE' for reading
    Beat Army!
    #> ./mycat BeatArmy.txt BADFILE GoNavy.txt 2> /dev/null 
    Go Navy!
    Beat Army!
    

Solution

#include <stdio.h>
#include <stdlib.h>


int main(int argc, char * argv[]){

  int c;
  FILE * from, * to;


  if (argc != 3){
    fprintf(stderr, "ERROR: Require two arguments: myco from to\n");
    return 1;
  }

  if((from = fopen(argv[1], "r")) == NULL){
    fprintf(stderr, "ERROR: Cannot open input file for reading\n");
    return 1;
  }

  if((to = fopen(argv[2], "w")) == NULL){
    fprintf(stderr, "ERROR: Cannot open destination file for writing\n");
    return 1;
  }

  while((c = fgetc(from)) != EOF){
    fputc(c, to);
  }

  fclose(from);
  fclose(to);

  return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char * argv[]){

  int c,i;
  FILE * file;

  if(argc == 1){
    //just read from stdin

    while((c = fgetc(stdin)) != EOF){
      fputc(c, stdout);
    }


  }else{
    for(i=1;i < argc; i++){

      if(strcmp(argv[i], "-") == 0){
        while((c = fgetc(stdin)) != EOF){
          fputc(c, stdout);
        }


      }else{

          if((file = fopen(argv[i], "r")) == NULL){
            fprintf(stderr, "mycat : Cannot open file '%s' for reading\n", argv[i]);
            continue;
          }

          while((c = fgetc(file)) != EOF){
            fputc(c, stdout);
          }

          fclose(file);
      }

    }
  }
}

Task 5

Implement the word count program mywc in the file mywc.c. In addition to complete the program logic in main(), you are require to fill in the following functions:

  1. count() : Given a a file pointer and a pointer to a mywc_res_t structure, it will read the file and fill in the results in the structure, counting lines, words, and bytes.
  2. print_res() : Given options setting in mywc_opts_t, results in mywc_res_t, and the name of file read as a char *, print the results to stdout.

Here is some sample output to compare against.

#> ./mywc GoNavy.txt 
    1       2       11      GoNavy.txt
#> ./mywc hemmingway.txt
    13      633     3235    hemmingway.txt
#> ./mywc hemmingway.txt GoNavy.txt 
    13      633     3235    hemmingway.txt
    1       2       11      GoNavy.txt
    14      635     3246    TOTAL
#> ./mywc hemmingway.txt - GoNavy.txt < dickens.txt 
    13      633     3235    hemmingway.txt
    19202   161009  936251  -
    1       2       11      GoNavy.txt
    19216   161644  939497  TOTAL
#> ./mywc < dickens.txt 
    19202   161009  936251  
#> ./mywc hemmingway.txt BADFILE
    13      633     3235    hemmingway.txt
mywc: File not found: BADFILE
#> ./mywc hemmingway.txt BADFILE 2> /dev/null 
    13      633     3235    hemmingway.txt
#> ./mywc -l hemmingway.txt - GoNavy.txt < dickens.txt 
    13      hemmingway.txt
    19202   -
    1       GoNavy.txt
    19216   TOTAL
#> ./mywc -l -w hemmingway.txt - GoNavy.txt < dickens.txt 
    13      633     hemmingway.txt
    19202   161009  -
    1       2       GoNavy.txt
    19216   161644  TOTAL
#> ./mywc -c hemmingway.txt - GoNavy.txt < dickens.txt 
    3235    hemmingway.txt
    936251  -
    11      GoNavy.txt
    939497  TOTAL
#> ./mywc -w hemmingway.txt - GoNavy.txt < dickens.txt 
    633     hemmingway.txt
    161009  -
    2       GoNavy.txt
    161644  TOTAL

Solution

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>


/* USAGE Information */
char USAGE[] = "mywc [OPTIONS] [file] [...]\n"
  "Print the number lines, words, and bytes of each file or from standard input\n"
  "\n"
  "All count information is written to standard out when no options are provided, and\n"
  "by default data is read from standard input if no file is provided. To read from \n"
  "standard input and a file, indicate standard input using '-'\n"
  "\n"
  "OPTIONS:\n"
  "\t -h \t print USAGE\n"
  "\t -l \t print line count\n"
  "\t -w \t print word count\n"
  "\t -c \t print byte count\n";

/**
 * Data type for mywc options
 **/
struct mywc_opt_t{
  int all; 
  int lines; //== 1 if counting lines
  int words; //== 1 if counting words
  int bytes; //== 1 if countying bytes
  int f_index; //index into argv for start of file arguments
};

/**
 * Data type for mywc results
 **/
struct mywc_res_t{
  int lines; //number of lines
  int words; //number of words
  int bytes; //number of bytes
};

/*Function Delceartions*/
void parse_args(int argc, char * argv[], struct mywc_opt_t * opts);
void print_opts(int argc, char * argv[], struct mywc_opt_t * opts);
void count(FILE * f, struct mywc_res_t * res);
void print_res(struct mywc_opt_t * opts, struct mywc_res_t * res, char * fname);


///////////////////////////////////////////////////////////////////////////////


/**
 * parse_args(int argc, char * argv[], struct mywc_opt_t * opts);
 * returns: index of remaining argv[]  values
 *
 * Set the opts structure with apropriate flags and return
 **/
void parse_args(int argc, char * argv[], struct mywc_opt_t * opts){
  int c;

  /*default settings*/
  opts->all = 1;
  opts->lines = 0;
  opts->bytes = 0;
  opts->words = 0;
  opts->f_index = -1;

  /*parse each flag*/
  while ((c = getopt(argc, argv, "wlch")) != -1){

    switch (c)
      {
      case 'h':
        printf("%s", USAGE);
        exit(0);
        break;
      case 'w':
        opts->all = 0;
        opts->words = 1;
        break;
      case 'l':
        opts->all = 0;
        opts->lines = 1;
        break;
      case 'c':
        opts->all = 0;
        opts->bytes = 1;
        break;
      default:
        fprintf(stderr, "mywc: Unknown option '%c' \n", c);
        exit(0);
        break;
      }
  }

  /*if all is set, set all to 1*/
  if (opts->all){
    opts->lines = 1;
    opts->words = 1;
    opts->bytes = 1;
  }

  /*store inde of last arguemnt*/
  opts->f_index = optind;

  return;
}

/**
 * print_opts(int argc, char * argv[], struct mywc_opt_t * opts)
 *
 * Debug function to print the settings for mywc
 **/
void print_opts(int argc, char * argv[], struct mywc_opt_t * opts){
  int i;

  printf("all -> %d\n", opts->all);
  printf("lines -> %d\n", opts->lines);
  printf("words -> %d\n", opts->words);
  printf("bytes -> %d\n", opts->bytes);
  printf("f_index -> %d\n", opts->f_index);

  if(opts->f_index == argc){
    printf("file: - (default)\n");
  }else{
    for(i = opts->f_index; i < argc; i++){
      printf("file: %s\n", argv[i]);
    }
  }
}

/**
 * count(FILE * f, struct mywc_res_t * res)
 *
 * Count the number lines, words, and bytes in the file opned at the
 * file pointer f. Set the results in res.
 *
 **/
void count(FILE * f, struct mywc_res_t * res){
  //TODO: Complete this function

  char c;
  int last_whitespace = 0;

  res->words = res->lines = res->bytes = 0;

  while( (c=fgetc(f)) != EOF){
    res->bytes++;

    if(!last_whitespace && isspace(c)){
      res->words++;
    }

    if(c == '\n'){
      res->lines++;
    }

    if(isspace(c)){
      last_whitespace = 1;
    }else{
      last_whitespace = 0;
    }

  }
}

/**
 * print_res(                    )
 *
 * Print the output for a results. The order of the output is always
 * prefered lines, words, bytes and then file name, dependent on which
 * options are provided. All results are tab deliminated with a
 * leadding tab. 
 *
 **/
void print_res(struct mywc_opt_t * opts, struct mywc_res_t * res, char * fname){
  //TODO: Complete the function decleration and code

  printf("\t");
  if(opts->lines){
    printf("%d\t", res->lines);
  }
  if(opts->words){
    printf("%d\t", res->words);
  }
  if(opts->bytes){
    printf("%d\t", res->bytes);
  }
  printf("%s\n",fname);
}

int main( int argc, char *argv[] ){


  struct mywc_opt_t opts; //options structure
  struct mywc_res_t res, total; //results struct and total

  int i; //iterator
  char * fname; //filename
  FILE * file; //file pointer


  parse_args(argc, argv, &opts); // parse the options
  //  print_opts(argc, argv, &opts); // debug print, remove before submission


  //TODO

  if(opts.f_index == argc){
    count(stdin, &res);
    print_res(&opts, &res, "");
  }else{

    total.lines = total.words = total.bytes = 0;

    for(i = opts.f_index; i < argc; i++){
      fname = argv[i];

      if(strcmp(fname, "-") == 0){
        count(stdin, &res);
      }else{

        if((file = fopen(fname, "r")) == NULL){
            fprintf(stderr,"mywc: File not found: %s\n", fname);
            exit(1);
        }

        count(file, &res);
        fclose(file);
      }

      total.lines += res.lines;
      total.words += res.words;
      total.bytes += res.bytes;


      print_res(&opts, &res, fname);
    }

    if(argc - opts.f_index> 1){
      print_res(&opts, &total, "TOTAL");
    }
  }

  return 0;
}