Assignment 2 - Bookshelf Manager!
Welcome to CS Bookshelf! Your task is to make a bookshelf manager that can be used to organise your personal reading lists, easily track your reading progress and help you meet your reading goals!
Overview
Assignment Structure
This assignment will test your ability to create, use, manipulate and solve problems using linked lists. To do this, you will be implementing a bookshelf manager using linked lists of books and bookshelves.
We have defined some structs in the provided code to get you started. You may add fields to any of the structs if you wish, but you should not need to.
struct shelf
- Purpose:
- To store all the information of a shelf
- Fields:
char name[MAX_STR_LEN]
- The name of the bookshelf
struct book *books
- A pointer to the start of the list of books on this bookshelf
struct shelf *next
- A pointer to the next bookshelf
struct book
- Purpose:
- To store all the information of a book on your bookshelf
- Fields:
char title[MAX_STR_LEN]
- The title of the book
char author[MAX_STR_LEN]
- The author of the book
enum book_genre genre
- The genre of the book as an enum
- Can be either
CLASSICS
,FANTASY
,MYSTERY
,NON_FICTION
,SCI_FI
orINVALID
int rating
- The rating of the book out of 5
int pages_count
- The number of pages in the book
int pages_read
- The number of pages that have been read so far
- This initially will be 0 when a book is added to a shelf
struct book *next
- The next book on the bookshelf
The following enum definition is also provided for you. You can create your own enums if you would like, but you should not modify the genre enum.
enum book_genre {CLASSICS, FANTASY, MYSTERY, NON_FICTION, SCI_FI, INVALID};
- Purpose:
- To represent the different types of genres of book that we will deal with in this assignment
- Values:
CLASSICS
FANTASY
MYSTERY
NON_FICTION
SCI_FI
INVALID
- Not an actual genre
- This is used to represent if some error has occurred when trying to scan in a
enum book_genre
.
Your Tasks
This assignment consists of four stages. Each stage builds on the work of the previous stage, and each stage has a higher complexity than its predecessor. You should complete the stages in order.
Stage 1.1 - Basic setup: Implement code to create a single struct shelf and a single struct book.
Stage 1.2 - Setup Command loop: Implement the Command loop.
In this stage, you will be setting up the program to read commands continuously until
CTRL-D
(similar to Assignment 1).Each command will start with a single unique character, and depending on the command, may be followed by some specific number of arguments relevant to that command.
Stage 1.3 onwards - Implement the commands:
You will need to implement some commands, which will allow you to add, modify and display information on the list of books and different bookshelves.
How to get started
There are a few steps to getting started with Bookshelf Manager.
Create a new folder for your assignment work and move into it.
mkdir ass2 cd ass2
Download cs_bookshelf.c here, or copy it to your CSE account using the following command:
cp -n /import/reed/A/dp1091/public_html/23T3/activities/cs_bookshelf/cs_bookshelf.c .
Run
1091 autotest cs_bookshelf
to make sure you have correctly downloaded the file.
1091 autotest cs_bookshelf
- Read through Stage 1.
Reference Implementation
To help you understand the proper behaviour of the Bookshelf Manager, we have provided a reference implementation. If you have any questions about the behaviour of your assignment, you can check and compare it to the reference implementation.
To run the reference implementation, use the following command:
1091 cs_bookshelf
You might want to start by running the ?
command:
1091 cs_bookshelf Welcome to Bookshelf Manager! Enter command: ? =====================[ Bookshelf Manager ]===================== ===============[ Usage Info ]=============== ? Show help a [title] [author] [genre] [rating] [pages_count] Add a book to the shelf p Print out all of the books on the shelf c Count all of the books on the shelf i [n] [title] [author] [genre] [rating] [page_count] Insert a book into the shelf at position `n` r [title] [author] [n] Read `n` pages of the book with matching `title` and `author` s Display the reading stats A [name] Add a new empty shelf in alphabetical order > Select the next shelf in the shelf list. < Select the previous shelf in the shelf list. P Print out the shelves list d [title] [author] Delete book with matching `title` and `author` from the selected shelf. D Remove the selected shelf. G [genre] Create a new shelf with all the books of `genre` S [pattern] Search for titles that match `pattern` across all shelves ================================================================
About the starter code
The starter code contains some provided functions to help simplify some stages of the assignment. These functions have been fully implemented for you and should not need to be modified to complete the assignment.
These provided functions will be explained in the relevant stages of the assignment. \ Please read the comments and the spec as we will suggest certain provided functions for you to use.
It also contains two function stubs, create_shelf
and create_book
, which you will need to complete in Stage 1.1.
Finally, the main
function contains some comments to help guide you through Stage 1.1, as well as some printf
messages which run when the program starts and ends.
On program start:
Welcome to Bookshelf Manager!
On program end:
Goodbye
Allowed C Features
In this assignment, you cannot use arrays, other than char
arrays, and cannot use the features explicitly banned in the Style Guide.
We strongly encourage you to complete the assessment using only features taught in lectures up to and including Weeks 8 and 9. The only C features you will need to get full marks in the assignment are:
int
,char
, anddouble
variables.- Enums.
- Structs.
- If statements.
- While and for loops.
- Your own functions.
- Pointers.
char
arrays/strings (you are not allowed to use arrays that are notchar
arrays).- Linked lists.
- Standard libraries:
stdio.h
,stdlib.h
,ctype.h
andstring.h
. - Good Code Style! \ (Header comments, function comments, constants (
#define
's), and whitespace and indentation.)
Using any other features will not increase your marks (and will make it more likely you make style mistakes that cost you marks).
If you choose to disregard this advice, you must still follow the Style Guide. You also may be unable to get help from course staff if you use features not taught in DPST1091.
Features that the Style Guide strongly discourages or bans will be penalised during marking.
Your Tasks
This assignment consists of four stages. Each stage builds on the work of the previous stage, and each stage has a higher complexity than its predecessor. You should complete the stages in order.
Stage 1
For Stage 1 of this assignment, you will be implementing the command loop, as well as the commands to add books to a bookshelf.
Specifically, this will include:
- Implementing the
create_shelf
andcreate_book
functions. - Implementing the command loop, to scan commands until
CTRL-D
. - Adding books to the end of the shelf
- Printing out all books on the shelf
- Counting the number of books on the shelf
By the end of this stage, your linked list of books will look something like:
Stage 1.1 - Creating a shelf and book
As you might have found by now, it can be really useful to have a function that takes the input for a linked list node, calls malloc
and initialises all the fields of the node. So, in Stage 1.1, we will be implementing functions that do exactly that for a struct shelf
, and for a struct book
.
You'll find the following unimplemented functions in the starter code:
struct shelf *create_shelf(char name[MAX_STR_LEN]) {
// STAGE 1.1
// TODO: malloc, initialise, and return a new shelf.
// hint: you will have to replace NULL in this return statement.
return NULL;
}
struct book *create_book(
char title[MAX_STR_LEN],
char author[MAX_STR_LEN],
enum book_genre genre,
int rating,
int pages_count
) {
// STAGE 1.1
// TODO: malloc, initialise, and return a new book.
// hint: you will have to replace NULL in this return statement.
return NULL;
}
Your task is to complete the create_shelf
function, so that it:
- Malloc's a new
struct shelf
. - Copies the
name
into the corresponding struct field. - Initialises all other fields to some reasonable value.
- Returns a pointer to the malloc'd struct.
Your also then need to complete the create_book
function, so that it:
- Malloc's a new
struct book
. - Copies the
title
,author
,genre
,rating
andpages_count
into the corresponding struct fields. - Initialises all other fields to some reasonable value.
- Returns a pointer to the malloc'd struct.
Assumptions
- No error handling is required for this stage
Testing
There are no autotests for Stage 1.1.
Instead, you may want to double check your work by compiling your code using dcc
and making sure there are no warnings or errors.
You could also write some temporary testing code to check your create_shelf
and create_book
functions work properly.
For example, you could copy the following testing code into your main function:
///////////////////////////// TESTING CODE /////////////////////////////
// create a struct book with
// title : "emma"
// author : "jane_austen"
// genre : CLASSICS
// rating : 4
// pages_read : 0
// pages_count : 200
struct book *test_book = create_book("emma","jane_austen",
CLASSICS, 4, 200);
// print out all it's fields.
printf("title: %s\n", test_book->title);
printf("author: %s\n", test_book->author);
printf("rating: %d\n", test_book->rating);
printf("pages_read: %d\n", test_book->pages_read);
printf("pages_count: %d\n", test_book->pages_count);
printf("next field: %p\n", test_book->next);
if (test_book->genre == CLASSICS) {
printf("genre: CLASSICS\n");
} else {
printf("not in CLASSICS genre\n");
}
///////////////////////////// TESTING CODE /////////////////////////////
This code just calls create_book
to malloc and initialise a struct book
, and then prints out all of its fields.
If you run it, it should print out something like:
title: emma author: jane_austen rating: 4 pages_read: 0 pages_count: 200 next field: (nil) genre: CLASSICS
Stage 1.2 - Implement the command loop
Now we'll be implementing the command loop, allowing your program to take in and perform different operations on the shelf.
From this stage onwards, your program should run in a loop, scanning in and executing commands until CTRL-D
. You should implement this command loop between the existing welcome and goodbye messages in the starter code.
On each iteration of the loop, your program should:
- Print the prompt
Enter command:
- Scan in a command character
- Scan in any arguments following the command character and execute the command
Each command will start with a single unique character, and may be followed by a variable number of arguments depending on the command.
The unique character for each different command and the number and type of the command arguments are specified in the relevant stages of this assignment.
The first command you have to implement is the Help command, which is described below.
Command: Help
?
Description
When the character '?'
is entered into your program, it should run the Help command. This command doesn't read in any arguments following the initial command and should display a message which lists the different commands and their arguments.
The purpose of this command is to allow anyone using our program to easily look up the different commands.
A helper function: void print_usage(void)
has been provided to help you print and format this message.
You can find more information about it here: Provided helper function.
This is what it should look like when you run the Help command:
At this point in the assignment, the ONLY command you have to implement is the Help command, which prints this message.
None of the other commands listed here should be implemented yet.
Instead, you will be adding them over the course of the whole assignment.
Because of the command loop, after the Help command has finished running, your program should print out
Enter command:
and then wait for the user to either enter another command, or press CTRL-D
to end the program.
Provided helper functions
A procedure, print_usage
, has been provided for you in the starter code which prints out the help message.
So all you need to do for this command is call the print_usage
procedure when the ?
character is scanned in.
Assumptions
- All commands will begin with a single unique character. Some will be followed by a number of arguments, depending on the command.
- You can assume that commands will always be given the correct number of arguments, so you do NOT need to check for this.
- You do not need to handle unknown commands.
- No error handling is required for this stage.
Examples
Stage 1.3 - Add books
Now it is time to create a shelf and start adding books to it!
When you run your program, a new shelf should be created in main with the name "tbr"
(an acronym for to be read). You should use the create_shelf
function to do this.
This shelf will start out with an empty list of books.
It should look something like this:
To make your shelf more useful, we need a way of adding a book to the end of the list of books.
Command: Append book to shelf
a [title] [author] [genre] [rating] [pages_count]
Description
The a
command takes in 5 arguments:
- a string called
title
, - a string called
author
, - an
enum genre
calledgenre
, - an integer called
rating
, and - an integer called
pages_count
.
Some helper functions have been provided to help you scan in these arguments:
void scan_title_author(char title[MAX_STR_LEN], char author[MAX_STR_LEN])
enum book_genre scan_genre(void)
You can find more information on these here: Provided helper functions.
When the a
command is entered, your program should create a new book containing the title
, author
, genre
, rating
and pages_count
, then append it to the end of the list of books inside the shelf (in other words, insert the new book at the end of the shelf's books
list).
Finally, it should out a message to confirm the command was successful:
Book: '[title]' added!
You should replace [title]
with the title
you scanned in.
For example, if we have just started the program and we use the a
command once, it would look like:
Welcome to Bookshelf Manager! Enter command: a emma jane_austen classics 5 474 Book: 'emma' added!
Our linked list should now look like this:
If we then run the a
command again:
Enter command: a win_friends dale_carnegie non_fiction 4 288 Book: 'win_friends' added!
then our linked list should now look like:
Provided helper functions
Two helper functions have been provided for this stage:
void scan_title_author(char title[MAX_STR_LEN], char author[MAX_STR_LEN])
:- Scans the
title
intochar title[MAX_STR_LEN]
and - Scans the
author
intochar author[MAX_STR_LEN]
.
- Scans the
enum book_genre scan_genre(void)
:- Scans the
genre
and returns it as anenum book_genre
. - Returns
INVALID
if thegenre
did not correspond to one of the valid genres.
- Scans the
You should use these functions to help you scan in the title
, author
and genre
arguments.
Remember that the arguments must be scanned in the correct order, so your code to scan arguments will look something like the following:
// Create variables to scan arguments into
char title[MAX_STR_LEN];
char author[MAX_STR_LEN];
enum book_genre genre;
int rating, pages_count;
// Arguments are in order: [title] [author] [genre] [rating] [pages_count]
// 1. Scan title and author first
scan_title_author(title, author);
// 2. Then scan the genre
genre = scan_genre();
// 3. Then scan the rating and pages_count. We can just use scanf() for this
scanf("%d %d", &rating, &pages_count);
// We've scanned in all the arguments! Now we can use them in our code
Errors
There is no error handling required for this stage. You'll be adding this late in Stage 2.1.
Assumptions / Restrictions
title
andauthor
will always be less thanMAX_STR_LEN
including their null terminator at the end.title
andauthor
will not contain any whitespace. For examplepride_and_prejudice
is a valid title butpride and prejudice
is not. We will not test any invalidtitle
orauthor
inputs so you do not need to account for this in your program.You can assume that the combination of a
title
andauthor
will be unique in this stage.genre
will be entered as a lowercase string and automatically converted to the correctenum genre
for you by thescan_genre
function, when the function is used.Corresponding string enum genre "classics" CLASSICS
"fantasy" FANTASY
"mystery" MYSTERY
"non_fiction" NON_FICTION
"sci_fi" SCI_FI
any invalid string INVALID
Note that you don't need to worry about
INVALID
until Stage 2.1.
Until then, you can assume that the returnedenum genre
will never beINVALID
.
Examples
Stage 1.4 - Printing books on the shelf
Now we want a way to display the shelf and all its books.
Command: Print books
p
Description
The p
command takes no arguments.
When the p
command is run, your program should print out all books on the current shelf, from head to tail.
The print_book
function has been provided for you to format and print a single book. More information on its usage can be found below.
If there are no books on the shelf, you should print the following message instead:
There are no books on this shelf!
Provided helper functions
One helper function has been provided for this stage:
void print_book(struct book *book)
- This takes in a pointer to a single struct book and prints in the correct format.
This means you don't need to worry about copying the exact output format for the p
command. To match the autotests exactly, you should loop through the list of books and call this function for each of them.
Errors/Assumptions: N/A
Examples
Stage 1.5 - Count books on the shelf
It would be nice to have a way to check how many books are on the shelf. As such, we need to implement the c
command.
Command: Count books on shelf
c
Description
This command should print a different message, depending on the number of books on the shelf:
- For zero books:
There are no books on this shelf!
- For one book:
There is 1 book on this shelf!
- Otherwise:
There are [num] books on this shelf!
(replacing[num]
with the number of books on the shelf)
Errors/Assumptions: N/A
Examples
Testing and Submission
Remember to do your own testing
Are you finished with this stage? If so, you should make sure to do the following:
- Run
1091 style
, and clean up any issues a human may have reading your code. Don't forget -- 20% of your mark in the assignment is based on style and readability! - Autotest for this stage of the assignment by running the
autotest-stage
command as shown below. - Remember -- give early, and give often. Only your last submission counts, but why not be safe and submit right now?
1091 style cs_bookshelf.c 1091 autotest-stage 01 cs_bookshelf give dp1091 ass2_cs_bookshelf cs_bookshelf.c