CS 537 Spring 2008 Section 1: Programming Homework 0
Due: Tuesday, January 27th at 9 pm
You are to do this project BY YOURSELF
This project must be implemented in C (and not C++ or anything else)
Notes:
Monday, January 26
Hint 2: you can assume that the input is a well-formed text file, meaning that it contains no null characters.
Thursday, January 22
Hint 1: here is sample code from this web site showing how to allocate a two dimensional array:
int **array1 = (int **)malloc(nrows * sizeof(int *));
for(i = 0; i < nrows; i++)
array1[i] = (int *)malloc(ncolumns * sizeof(int));
array1[1][2] = 0;
Hint 2 (student generated): it may be easier to test your code for correctness by reducing the maximum length of the file and length of the line. Once that works, you can experiment on larger files.
Purpose
The purpose of this assignment is to get familiar with the C
programming language, the gcc compiler, and the gdb
debugger.
Part 0: a picture
We want to get to know you better, and what better way than to be able
to associate a name with a face? You need to turn in a digital
picture of yourself so I can learn who you are. Put this in your
handin directory (a jpg or a gif is fine), in the form
Firstname.Lastname.jpg (or whatever). If you don't turn in a picture,
your project will not be graded!
Part 1: Writing C code
You will write a program to read in a text file and print the lines in reverse order (last line first, first line last) and also print out each line in reverse order (last non-newline character first, first character last).
Specification:
Program input: read a file specified on the command line:
reverse-lines filename.txt
Program output: every line of the file, in reverse order.
Note that the lines of the file may be very short (blank) or very long
(up to hundreds of characters).
This input:
<!--#include file="base-head.html" -->
<title>CS 537: Operating Systems</title>
<!--#include file="base-top.html" -->
<h2>Overview</h2>
<p> Welcome to your first the Wisconsin Operating Systems course. This
course will describe a number of topicsincluding basic operating
system structure, process and thread synchronization and concurrency,
file systems and storage servers, memory management techniques,
process scheduling and resource management, system security, and a few
other "hot" topics.
should produce this output:
.scipot "toh" rehto
wef a dna ,ytiruces metsys ,tnemeganam ecruoser dna gniludehcs ssecorp
,seuqinhcet tnemeganam yromem ,srevres egarots dna smetsys elif
,ycnerrucnoc dna noitazinorhcnys daerht dna ssecorp ,erutcurts metsys
gnitarepo cisab gnidulcniscipot fo rebmun a ebircsed lliw esruoc
sihT .esruoc smetsyS gnitarepO nisnocsiW eht tsrif ruoy ot emocleW >p<
>2h/<weivrevO>2h<
>-- "lmth.pot-esab"=elif edulcni#--!<
>eltit/<smetsyS gnitarepO :735 SC>eltit<
>-- "lmth.daeh-esab"=elif edulcni#--!<
Assumptions
String length: You may assume no line in the input file is longer than
512 bytes. If you encounter a line that is too long, you should print error
message LINE_TOO_LONG (as detailed below) and skip the rest of this line.
Let's say the max size of an input line is 8
characters (and not 512). Which of these lines should be in the final
output?
abcdef
abcdefg
abcdefgh
The first seemingly has 6 characters (abcdef), the second has
7, and the third 8, and thus you might naively think all should be
accepted. However, you are forgetting the newline character (\n)
which is at the end of each input line. Thus, the first two should be
accepted (as they have 7 and 8 characters including the newline). For
the third line, you should only accept 'abcdefg' and put a \n, and
skip the rest of the line, and don't forget to print the LINE_TOO_LONG
message.
File length: You may assume the input file has 1024 valid lines (not too long) of input
or fewer. If you encounter more lines, print the FILE_TOO_LONG error message once
(as detailed below)
and reverse whatever input you currently have read in.
Error Messages
All error messages encountered while reading the input file should be of this format:
Error in line XXX: specific error message
where the specific error messages are:
FILE_TOO_LONG: You should print the following message: File too long
LINE_TOO_LONG: You should print the following message: Line too long
INVALID_FILE: If the user specifies exactly one file (as
desired) but it can't be opened (for whatever reason), you should
print the following message:
Error: Cannot open file FILE
where FILE is what the user passed in.
If you encounter a line that has both of these errors, you should print both error messages.
There are some other possible errors too. For example, if the user doesn't
properly specify the input file on the command line (by say, not giving one,
or by giving too many files), you should print:
usage: reverse <file>
and then exit.
Important: On any error code, you should print the error to the screen
using fprintf() , and send the error message to stderr (standard
error) and not stdout (standard output). This is accomplished in your C
code as follows:
fprintf(stderr, “usage: ...
”);
Implementation tips:
To do this, use the C standard I/O "f" functions:
- fgetc, fputc
- fgets, fgets
- fscanf, fprintf
- fopen, fclose
Documentation for these functions are available through the Linux man
pages:
emperor01(1)% man fgets
GETS(3) BSD Library Functions Manual
FGETS(3)
NAME
fgets, gets -- get a line from a stream
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include <stdio.h>
char *
fgets(char *restrict s, int n, FILE *restrict stream);
char *
gets(char *s);
To invoke the compiler, run the command line:
emperor01(1) gcc -g -o programname filename.c
This will compile the file "filename.c" and produce the output program
"filename" that you can then run with the command "./filename" (the
"./" is to tell Linux in which directory to find the program. The "-g"
flag indicates that the compiler should produce extra data to enable
source-level debugging, and the "-o" options specifies to put the
program in a specific file as compared to the standard name "a.out".
Other Tips
Start small, and get things working incrementally. For example, first
get a program that simply reads in the input file, one line at a time, and
prints out what it reads in. Then, slowly add features and test them as you
go.
Testing is critical. One great programmer I once knew said you have to
write 5-10 lines of test code for every line of code you produce; testing your
code to make sure it works is crucial. Write tests to see if your code handles
all the cases you think it should. Be as comprehensive as you can be. Of
course, when grading your projects, we will be. Thus, it is better if you find
your bugs first, before we do.
Keep old versions around. Keep copies of older versions of your program
around, as you may introduce bugs and not be able to easily undo them. A
simple way to do this is to keep copies around, by explicitly making copies of
the file at various points during development. For example, let's say you get
a simple version of myreverse.c working (say, that just reads in the
file); type cp myreverse.c myreverse.v1.c to make a copy into the file
myreverse.v1.c . More sophisticated developers use version control systems like
CVS , but we'll not get into that here (yet).
Keep your source code in a private directory. An easy way to do this is
to log into your account and first change directories into private/ and
then make a directory therein (say p1 , by typing mkdir p1 after
you've typed cd private/ to change into the private
directory). However, you can always check who can read the contents of your
AFS directory by using the fs command. For example, by typing in
fs listacl . you will see who can access files in your current directory. If
you see that system:anyuser can read (r) files, your directory contents
are readable by anybody. To fix this, you would type fs setacl
. system:anyuser “” in the directory you wish to make private. The
dot “.” referred to in both of these examples is just shorthand for
the current working directory.
Part 2: Debugging C code
The gdb debugger allows you to stop your program at any point
and investigate program variables.
Specification:
Start your program under the debugger. Put a breakpoint on your main
loop reading a line from the file. Single step through input of a
single line. Print out the values of at least two different
variables. Submit a transcript of your debugging session.
Implementation tips:
You start gdb by invoking it with the program name:
emperor01(5)% gdb programname
GNU gdb Red Hat Linux (6.5-37.el5_2.2rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host
libthread_db library "/lib/libthread_db.so.1".
(gdb)
Start running your program with the "run" command any input or output
redirection:
(gdb) run < filename.c
You can set breakpoints on a function with the "break" command and
pass it either a function name or a file and line number:
(gdb) break main
Breakpoint 1 at 0x8048419: file filename.c, line 8.
(gdb) break filename.c:11
Breakpoint 3 at 0x804841e: file filename.c, line 11.
(gdb)
You can print variables with the "print" command:
gdb) print stdin
$3 = (struct _IO_FILE *) 0x2ae420
(gdb)
You can list code with the "list" command, which takes a function name
or a filename:linenumber (like break):
(gdb) list main
1 #include <stdio.h>
2 #include <string.h>
3
4 int main(int argc, char * argv[])
5 {
6 char buffer[200];
7 char * tmp;
8 while (!feof(stdin)) {
9 int len;
10 int i;
(gdb)
After execution stops at a breakpoint, you can continue exection with
the "cont" command:
(gdb) run < filename.c
Starting program: /afs/cs.wisc.edu/u/s/w/swift/tmp/programname <
filename.c
Breakpoint 1, main () at filename.c:8
8 while (!feof(stdin)) {
(gdb) cont
Continuing.
One execution stops, you can step forward one source code line at a
time with the "next" command (abbreviated "n"), which jumps over function calls, or the
"step" command (abbreviated "s"), which goes into the function and stops:
Breakpoint 1, main () at filename.c:12
12 while (!feof(stdin)) {
(gdb) next
15 tmp = fgets(buffer, 200, stdin);
(gdb) next
16 if (tmp == 0){
(gdb) next
19 len = strlen(tmp);
(gdb) next
20 for (i = len-2; i >= 0; i--) {
(gdb) next
21 printf("%c",myfunction(tmp[i]));
(gdb) step
myfunction (c=62 '>') at filename.c:5
5 return((int)c);
(gdb)
You can quit gdb with the "quit" command.
What to turn in:
- A digital picture of yourself
- Please turn in your source code as a single file named
"assignment0.c" (there is no need for multiple files or a makefile for this assignment)
- Please turn in a transcript from your debugging session as a text file
name "debug-trace.txt"
Please put all files in the directory: ~cs537-1/handin/<your-login>/p0
What we will look for:
- Does the code work on simple files?
- Does the code work on more complex files (zero-length lines,
very long lines)
- Did you get the debugger to work?
- Did you turn in a picture?
|