Lab 03: C Programming, mycp
, mycat
and mywc
(SOLUTIONS)
Task 1
Work in the file src/swap.c
.
- Complete the function
swap()
that takes pointers to twoint
's and swaps their values via pointer derferencing. Test your function in main. - Create a function
swap_pair()
which takes a pointer to thestruct pair
and swaps the order of the pair using yourswap()
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.
- Complete the function
mystrcpy()
which takes as input twochar *
,to
andfrom
, and copies the string atfrom
to the string atto
. - Complete the function
reverse()
which takes achar *
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.
- The program
save
will read fromstdin
and save what is read to a file calledsaved.out
. It should report an error if it can't opensaved.out
for writing. - The program will try and open the
saved.out
file and write it's contents to the screen. It will report an error ifsaved.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
- Write your own version of the
cp
function,mycp
which takes two command line arguments,from
andto
, and copies a file byte-by-byte usingfgetc()
andfputc()
. Your verision ofmycp
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
- Write your own version of
cat
function,mycat
which has the same operations ascat
.- Without arguments, it should read from
stdin
and write tostdout
- 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 "-" usestrcmp()
, 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!
- Without arguments, it should read from
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:
count()
: Given a a file pointer and a pointer to amywc_res_t
structure, it will read the file and fill in the results in the structure, counting lines, words, and bytes.print_res()
: Given options setting inmywc_opts_t
, results inmywc_res_t
, and the name of file read as achar *
, print the results tostdout
.
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; }