CS 3113: Project 1
Objectives
The objectives of this project are for you to:
- Gain experience in the representation of chars, ints, floats
and strings at the byte level, including translation between
these representations.
- Implement low-level file reading and writing, as well as
positioning of the file offset at a specified location.
Overview
- Your program will create a 128-byte buffer that it stores in
memory. This buffer should be declared as an unsigned
char array.
- Through a text interface, the user is able to:
- Store characters, strings, ints, floats into specified
locations in the buffer
- Read characters, strings, ints, floats from specified
locations in the buffer
- File storage: the user is able to:
- Store all or part of the buffer to a specified location
in the file
- Read a portion of the file into the buffer
Proper Academic Conduct
The code solution to this project must be done individually. Do not
copy solutions to this problem from the network and do not look at or
copy solutions from others. However, you may use the net for
inspiration and you may discuss your general approach with others.
Specification
Storage API
You are to implement a Storage API that provides the functions
for opening/closing a file, as well as reading/writing a specified
number of bytes from/to a specified location within the file. Your
implementation must follow this specification exactly.
- Implement storage.c
- Note that storage.h is provided in the tar file
below and must not be altered.
The STORAGE structure for now stores just the file descriptor
of the open file:
typedef struct
{
int fd;
} STORAGE;
init_storage()
STORAGE * init_storage(char * name);
- Opens the specified storage file for reading and
writing (and file should be created if it does not exist).
- If the file is opened successfully, then a dynamically created
STORAGE object is returned.
- If there is an error, this function must return NULL.
close_storage()
int close_storage(STORAGE *storage);
- Closes the already opened file.
- Frees the dynamically allocated structure.
- Returns 0 on success and -1 on error.
get_bytes()
int get_bytes(STORAGE *storage, unsigned char *buf, int location, int len);
- Reads len bytes into buf starting at the file
offset location. For example, the byte at location is
copied to buf[0]; the next byte is copied to buf[1], etc.
- Returns the number of bytes read, zero if at end-of-file, or a
negative number on error.
put_bytes()
int put_bytes(STORAGE *storage, unsigned char *buf, int location, int len);
- Writes the first len bytes in buf to the storage
file, starting at location.
- Returns the number of bytes written, or a negative number on error.
Main Program
Your main program maintains a 128-byte buffer in memory. The user can
interact with this buffer by placing integer, floating point,
character, string and hexadecimal values at a specified location in
the buffer. In addition, the user may request that the values in the
buffer be placed in a specified location in the storage file, or that
the buffer values can be read from a specific location in the file.
The command-line interface to your program is as follows (note that
you must respect the casing):
./project1
This program does the following:
- Ignores any arguments that it is given.
- Opens the storage file storage.bin.
- Sitting in a loop, it accepts one of several commands from the
user and executes these commands.
- Each command is exactly one input line, terminated by a newline
character.
- The commands manipulate or read the contents of the buffer, or
they transfer values between the buffer and the storage file.
- It is an error if a command has an incorrect number of
arguments or incorrect arguments.
- When there are no more commands (EOF), then the program closes
the file and exits.
- All errors are written to STDERR.
Parsing Lines
There are lots of ways to parse a string into the individual tokens
(tokens are symbols that are separated by white space). Here, we will
use the string tokenizer provided in the string library in C.
The local data structures that you need are:
const char SEPARATORS[] = " \t\n"; // Declare as a global constant
char in_buffer[INBUFSIZE]; // Input buffer from STDIN
char * args[MAX_ARGS]; // pointers to arg strings
char ** arg; // working pointer that steps through the args
After receiving an input line into in_buffer (e.g., using
fgets()), you can split the characters in the buffer into distinct
tokens as follows:
arg = args;
*arg++ = strtok(in_buffer,SEPARATORS); // tokenize input
while ((*arg++ = strtok(NULL,SEPARATORS)));
Here, SEPARATORS is a string that contains all of the characters that
are valid indicators for splitting the input string up. At the end, of
the loop, arg will be an array of strings (technically, it is
an array of pointers to characters). The arg in the array after the
last argument will be set to NULL.
Commands
There will be exactly one command per line of input from STDIN.
- List the contents of the buffer.
l
Print each byte in hexadecimal format. There are 16 bytes per
output line, for a total of 8 lines. Each byte has exactly
two characters, and bytes are separated by a space.
- Zero out the contents of the buffer.
z
Set all of the byte values in the buffer to zero.
- Write a byte value to a buffer location.
b LOC VAL
LOC is the location in the buffer to write to (LOC is decimal). VAL is the byte
value (in decimal) to be written
- Read a byte value from a buffer location.
B LOC
LOC is the location in the buffer to read from. Prints the
value of the byte (in decimal)
- Write a hexadecimal byte value to a buffer location.
h LOC VAL
LOC is the location in the buffer to write to. VAL is the byte
value (in hexadecimal) to be written
- Read a byte value from a buffer location.
H LOC
LOC is the location in the buffer to read from. Prints the
value of the byte (in hexadecimal)
- Write a character to a buffer location.
c LOC VAL
LOC is the location in the buffer to write to. VAL is the
character to be written
- Read a character value from a buffer location.
C LOC
LOC is the location in the buffer to read from. Prints the
value of the character (i.e., the glyph)
- Write an integer value to a buffer location.
i LOC VAL
LOC is the location in the buffer to write to (remember that
integers require multiple bytes to store). VAL is the integer
value (in decimal) to be written
- Read an integer value from a buffer location.
I LOC
LOC is the location in the buffer to read from. Prints the
value of the integer (in decimal)
- Write a floating point value to a buffer location.
f LOC VAL
LOC is the location in the buffer to write to (floats require
multiple bytes). VAL is the floating point
value to be written
- Read a floating point value from a buffer location.
F LOC
LOC is the location in the buffer to read from. Prints the
value of the floating point value.
- Write a string to a buffer location (may be multiple characters).
s LOC VAL
LOC is the location in the buffer to write to. VAL is the
string value to be written. Note that it will contain no
spaces (due to the tokenization process). Make sure that the
NULL terminator is also written to the buffer.
- Read a string from a buffer location.
S LOC
LOC is the location in the buffer to read from. Prints the
value of the string.
- Write the contents of the buffer to the file.
w OFFSET LEN
OFFSET is the offset in the file to start writing to. LEN is
the number of bytes in the buffer to write (bytes 0 ... LEN-1
in the buffer are written).
- Read part of the file into the buffer.
r OFFSET LEN
OFFSET is the offset in the file to start reading from. LEN is
the number of bytes in the buffer to read.
Notes
- You may assume that the user will not specify a buffer location
that is outside the 128-byte buffer.
- You may assume that the user will provide arguments in the
correct numerical range.
- All program input is from STDIN.
- All normal program output is to be placed in STDOUT.
- You must address the case of incorrect numbers of arguments or
of parse errors by writing an error to STDERR.
Examples
Program Invocation (for all examples)
./project1
Input Text
z
l
Output Text
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Input Text
z
b 42 67
l
B 42
H 42
Output Text
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 43 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
67
43
C
Input Text
z
s 10 HelloWorld
S 10
S 15
C 15
H 15
l
Output Text
HelloWorld
World
W
57
00 00 00 00 00 00 00 00 00 00 48 65 6c 6c 6f 57
6f 72 6c 64 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Input Text
z
i 32 1138
I 32
I 33
I 34
H 32
H 33
H 34
l
Output Text
1138
4
0
72
4
0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
72 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Input Text
z
s 16 Hello
h 21 20
s 22 World!
S 16
S 20
l
Output Text
Hello World!
o World!
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
48 65 6c 6c 6f 20 57 6f 72 6c 64 21 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Input Text
z
w 0 128
w 128 128
w 256 128
i 30 1138
s 40 HelloWorld!
l
w 0 128
z
r 128 128
H 0
l
r 0 128
H 0
l
S 40
Output Text
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 72 04
00 00 00 00 00 00 00 00 48 65 6c 6c 6f 57 6f 72
6c 64 21 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 72 04
00 00 00 00 00 00 00 00 48 65 6c 6c 6f 57 6f 72
6c 64 21 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
HelloWorld!
Submitting Your Program
- Your submission is composed of the following files, named exactly as shown (including casing):
- project1.c: your source file
- Include your name and project number at the top of the file
- storage.c/h: your library file.
- Include your name and project number at the top of the file
- Makefile: the make file that:
- all: compiles your program (this is the default behavior)
- clean: deletes the executable file (project1) and any intermediate files (.o, specifically)
- README.txt: documentation
- Include your name and project number at the top of the file
- Document any Internet resources that you consulted (URLs)
- Document any peer class members that you discussed your solution with. Note that you may not look at or share code that solves this specific problem
- Documentation requirements: function-level and inline
documentation are important components of writing code. They help
you to organize your thoughts while programming and help to
communicate the method and the intent of the code to your future
self or to collaborators (of course, you will not have collaborators
in this class). While we will not be specifically grading
documentation in this class, we will not be able to comment on your
code unless it is sufficiently documented. This will be true whether
you are asking for help before the submission deadline or looking
for feedback on your solution after the deadline. In short, you
should take the time to properly document your code as you develop
it.
- Submit your files:
- Create a zip file named project1.zip containing
your source code (c and h files), Makefile and README.txt file. The
following Linux shell command will do this for you:
zip project1.zip *.c *.h Makefile README.txt
Note I: this is a good thing to add to your Makefile.
Note II: do not include object files, executables or
temporary files in your zip file.
- Submit your zip file to Gradescope (access via Canvas).
Shortly after your submission, you will receive
automated feedback and a grade for the
correctness component of your grade.
- You may submit multiple times until the deadline.
- To be counted as on time, your solution must be submitted to
the Gradescope server by 11:45:00
pm on Tuesday, Sept. 24th. Grade penalties will be imposed for
late submissions (see the syllabus for the details).
Grading Criteria
- README.txt (10%): contains the required information
- Makefile (10%): satisfies the make file requirements
- Correctness (80%): passes all of the (hidden) unit tests
- Bonus: if you have your l, z, b, B, i and I commands working
by Wednesday, Sept. 18th (11:45pm), then you can receive up to 5%
additional points. To have this count, you must submit your
project by the early deadline to the project1-bonus
Gradescope area.
Downloads
The following file contains storage.h and a number of example tests: project1_dist.tar
Testing
The testing procedure is the same as in Project 0.
Hints
- Remember that characters use the ASCII encoding. You can take
advantage of this to make your implementation simple.
- Don't implement everything at once. Instead, get the first few
commands written and tested before moving on to the more
complicated commands.
- Any debugging code should only print to STDERR. This way, it
won't interfere with our testing procedures.
- Useful C functions:
- fgets()
Example:
char buf[100];
fgets(buf, 100, stdin);
This call will read at most 100 characters from the STDIN pipe (it stops when 100 characters are read, a newline is found or the end-of-file is reached). fgets() returns buf if something is read and NULL if the end-of-file marker is reached.
- fprintf()
This function behaves like printf(), except you can specify which stream will receive the output (first parameter). stdout and stderr are valid choices here.
- free()
- malloc()
- memset()
- memcpy()
- read()
- write()
- open()
- strcmp()
- strcpy()
- You can ask your VM about any of the above functions. For example, type the following into your shell:
man fgets
- Useful libraries (which define the above functions):
- stdio
- ctype
- string
- stdlib
- You can simulate an end-of-file marker (EOF) at the terminal by typing CTRL-d
Addenda
andrewhfagg at gmail.com
Last modified: Fri Sep 20 14:48:10 2019