Source code:
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <conio.h>
#include <windows.h>
#include "addon.h"
static book* archivelib;
static book* noticelib;
const int MAX_LINE_LENGTH = (255);
void fileCheck(file sourcefile) { //Checks if a file exists
if (access(sourcefile.filename, F_OK) != false) {
textcolour(RED);
fprintf(stderr, "Error: %s does not exist.\n", sourcefile.filename);
exit(EXIT_FAILURE);
};
};
size_t recordcount(file source) { //Finds how many records are in a file
source.fp = fopen(source.filename, "r");
char ch;
size_t records = 0;
fseek(source.fp, START_OF_LINE, SEEK_SET);
while ((ch = fgetc(source.fp)) != EOF)
if (ch == '\n')
records++;
fclose(source.fp);
return records;
};
void textcolour(int colour) {
static int __BACKGROUND;
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
GetConsoleScreenBufferInfo(h, &csbiInfo);
SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE),
colour + (__BACKGROUND << 4));
}
void cls(HANDLE hConsole) {
CONSOLE_SCREEN_BUFFER_INFO csbi;
SMALL_RECT scrollRect;
COORD scrollTarget;
CHAR_INFO fill;
// Get the number of character cells in the current buffer.
if (!GetConsoleScreenBufferInfo(hConsole, &csbi)) {
return;
}
// Scroll the rectangle of the entire buffer.
scrollRect.Left = 0;
scrollRect.Top = 0;
scrollRect.Right = csbi.dwSize.X;
scrollRect.Bottom = csbi.dwSize.Y;
// Scroll it upwards off the top of the buffer with a magnitude of the entire height.
scrollTarget.X = 0;
scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);
// Fill with empty spaces with the buffer's default text attribute.
fill.Char.UnicodeChar = TEXT(' ');
fill.Attributes = csbi.wAttributes;
// Do the scroll
ScrollConsoleScreenBuffer(hConsole, &scrollRect, NULL, scrollTarget, &fill);
// Move the cursor to the top left corner too.
csbi.dwCursorPosition.X = 0;
csbi.dwCursorPosition.Y = 0;
SetConsoleCursorPosition(hConsole, csbi.dwCursorPosition);
}
void clearscreen() {
HANDLE hStdout;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
cls(hStdout);
}
const book emptybook = {
.checkdigit = (0),
.ISBN = "empty",
.title = "empty",
.DOR = "empty",
.PersonDetails = { .firstname = "empty", .lastname = "empty", .fullname = "empty"},
.status = "empty",
};
static file notice = {
.filename = "NOTICE.txt",
.sizeofline = MAX_LINE_LENGTH,
.field = { .format = "%17[^|]|%55s|%10s|%10s|%21s|%10[^|]|%26[^\n]\n", .num = (7)},
};
static file archive = {
.filename = "ARCHIVE.txt",
.sizeofline = MAX_LINE_LENGTH,
.field = { .format = "%17[^|]|%55s|%10s|%10s|%21s|%10[^|]|%26[^\n]\n", .num = (7)},
};
void printBook(book bk) { //Print information on book bk
if (strcmp(bk.title, bk.PersonDetails.firstname) == 0) {
textcolour(RED);
fprintf(stderr, "Error: The book could not be printed\n");
sleep(2);
clearscreen();
return;
};
printf("|ISBN: %s\n", bk.ISBN);
printf("|Book: %s\n", bk.title);
printf("|Authour: %s\n", bk.PersonDetails.fullname);
printf("|Date of Release: %s\n", bk.DOR);
};
book* alloclib(file libfile) {
fileCheck(libfile); //check the file
libfile.records = recordcount(libfile); //find the number of records in file
book* lib = malloc(libfile.records * sizeof *lib);
if (lib == NULL) {
textcolour(RED);
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
};
return(lib);
}
int CmpBookRecs(const void* recA, const void* recB) { //Compare book records alphabetically
int result = 0;
book* bkRecA = (book*)recA;
book* bkRecB = (book*)recB;
//Compare the titles
result = strcmp(bkRecA->title,
bkRecB->title);
//If (titles match) compare the names
if (!result)
result = strcmp(bkRecA->PersonDetails.lastname,
bkRecB->PersonDetails.lastname);
//If (names match) compare the ISBN
if (!result)
result = strcmp(bkRecA->ISBN,
bkRecB->ISBN);
return (result); //Return the the result
};
book* loadlibrary(file libsource) { //Loads all the books in "libarchive"
printf("Loading library...\n");
sleep(2);
libsource.fp = fopen(libsource.filename, "r");
if (libsource.fp == NULL) {
textcolour(RED);
fprintf(stderr, "Error opening file.\n"); //Return an error if the file cannot be opened
exit(EXIT_FAILURE);
};
int read = 0; //read will be used to ensure each line/record is read correctly
int records = 0; //records will keep track of the number of Student records read from the file
char* token; //smaller char* broken from line
const char* del = "-"; //the delimiter the line is broken into a series of tokens by
//Read all records from the file and store them into the lib
book* lib = alloclib(libsource);
do {
//Check how many fields are in the file
read = fscanf(libsource.fp, libsource.field.format, lib[records].ISBN,
lib[records].title,
lib[records].PersonDetails.firstname,
lib[records].PersonDetails.lastname,
lib[records].PersonDetails.fullname,
lib[records].DOR);
//If fscanf read values from the file, assign values then successfully add an another record
if (read == libsource.field.num)
records++;
//If read is not equal the field size and it is not the end of file
if (read != libsource.field.num && !feof(libsource.fp)) {
textcolour(RED);
fprintf(stderr, "File format incorrect. %d fields read instead of %d\n", read, libsource.field.num);
sleep(2);
exit(EXIT_FAILURE);
};
//If there was an error reading from the file exit with an error message and status
if (ferror(libsource.fp)) {
textcolour(RED);
fprintf(stderr, "Error in reading file.\n");
exit(EXIT_FAILURE);
};
lib[records].checkdigit = atoi(strrchr(lib[records].ISBN, '-'));
} while (!feof(libsource.fp) && records < libsource.records);
textcolour(LIGHTGREEN);
printf("Sorting Library...\n");
sleep(2);
qsort(lib, records, sizeof(*lib), CmpBookRecs);
textcolour(GREEN);
printf("Library was sorted!\n\n");
//free(line);
fclose(libsource.fp);
printf("Library was successfully loaded...\n");
sleep(2);
return (lib);
};
int CheckISBN(char* ISBN) { //Calculates the check digit for 13 digit International Standard Book Number
int x[13];
int count = 0;
int i = 0;
while (count < strlen(ISBN) && ISBN[i] != '\0') {
if (isdigit(ISBN[i])) {
x[count] = ISBN[i] - '0'; // Convert character to integer
count++;
} i++;
}
int s = 0,
t = 0;
//Calculation
for (i = 0; i < 12; ++i) {
if (i % 2 == 0) {
t += (x[i] * 3);
} else {
t += x[i];
} s += t;
};
int r = s % 10;
return (10 - r);
};
book booksearch(file libsource, book* lib) { //Allows the user to search for a specify book based on its title or ISBN
book foundbook; //book to be found
char* search = malloc((MAX_LINE_LENGTH + 1) * sizeof *search); //search title
char information[MAX_LINE_LENGTH]; //book title in record
char* option;
bool flag = true;
do { //Infinite loop until input
printf("\n");
textcolour(BLUE);
printf(" | _____ ___ ____ ____ __ __ __ |\n");
printf(" | / ___/ / _] / || ` / ]| | | |\n");
printf(" | ( |_ / [_ | o || D ) / / | | | |\n");
printf(" | |__ || _]| || / / / | _ | |\n");
printf(" | / ` || [_ | _ || ` / |_ | | | |\n");
printf(" | ` || || | || . `` || | | |\n");
printf(" | |___||_____||__|__||__||_| |____||__|__| |\n");
printf(" | ___ ____ ______ ____ ___ ____ _____ |\n");
printf(" | / ` | ` | || | / ` | ` / ___/ |\n");
printf(" | | || o )| | | | | || _ |( |_ |\n");
printf(" | | O || _/ |_| |_| | | | O || | | |__ | |\n");
printf(" | | || | | | | | | || | | / ` | |\n");
printf(" | | || | | | | | | || | | ` | |\n");
printf(" | |___/ |__| |__| |____| |___/ |__|__| |___| |\n");
printf(" | |\n");
printf(" |- [");
textcolour(LIGHTBLUE);
printf("1");
textcolour(BLUE);
printf("] ISBN |\n");
printf(" |- [");
textcolour(LIGHTBLUE);
printf("2");
textcolour(BLUE);
printf("] Title |\n");
printf(" |- [");
textcolour(LIGHTBLUE);
printf("3");
textcolour(BLUE);
printf("] Exit |\n\n");
//User option
switch (getc(stdin)) {
case '1': option = "ISBN";
flag = false;
break;
case '2': option = "Title";
flag = false;
break;
case '3': return(emptybook); //Exit
break;
default : textcolour(RED); //Red
fprintf(stderr, "\nInvalid option. Try again.");
clearscreen();
break;
};
} while (flag == true);
//Prompt user for the book they want to search
textcolour(YELLOW);
clearscreen();
sleep(2);
printf("\nPlease enter the %s of the book you are searching for:\n", option);
scanf("%s", search);
//fgets(search, strlen(search), stdin);
//Remove newline character if exists
if ((strlen(search) > 0) && (search[strlen(search) - 1] == '\n'))
search[strlen(search) - 1] = '\0';
//Check if title is proper
if (strcmp(option, "Title") != 0) {
//Convert to search to all caps
for (int i = 0; search[i] != '\0'; i++) {
//If any character in the char* is not a character or char* is empty
if (isalpha(search[i]) == 0 || search == NULL)
goto impromperformat;
search[i] = toupper(search[i]);
};
};
//Check if ISBN is correct
if (strcmp(option, "ISBN") == 0) {
int q[5];
int read = sscanf(search, "%3d-%2d-%4d-%3d-%1d", q[0],q[1],q[2],q[3],q[4]);
if (read != 5) {
impromperformat:
textcolour(RED);
fprintf(stderr, "This not a %s. Returning...\n", option);
return(emptybook);
}
if (read == 5 && q[4] != CheckISBN(search)) {
textcolour(RED);
fprintf(stderr, "This not a proper ISBN. Returning...\n", option);
return(emptybook);
}
};
int records = 0; //the number of lines in the record
bool found = false; //if the the book was found
int linenumber = 0;
const char* del = "|"; //character that seperates each field
//While there are book records that have not been read
while (fgets(information, libsource.sizeofline, libsource.fp) != NULL && linenumber != notice.records) {
for (int i = 0; information[i] != '\n'; i++)
information[i] = toupper(information[i]);
//If the an occurrence of the substring, (search) in the string, (line) is found
if (strstr(information, search) != NULL) {
int read = sscanf(information, libsource.field.format, lib[records].ISBN,
lib[records].title,
lib[records].PersonDetails.firstname,
lib[records].PersonDetails.lastname,
lib[records].PersonDetails.fullname,
lib[records].DOR);
if (read != 5)
goto notfound;
foundbook.checkdigit = atoi(strrchr(foundbook.ISBN, '-'));
if (foundbook.checkdigit != CheckISBN(foundbook.ISBN))
goto notfound;
if (strcmp(foundbook.status, "Available") != 0) {
textcolour(RED);
fprintf(stderr, "This book is currently unavailable.\n");
return(emptybook);
};
textcolour(LIGHTGREEN);
printf("A match was found for %s!\n", search); //Book is found
found = true;
} linenumber++;
};
notfound:
//free(token); //Free up book title
free(search); //Free up search
if (found == false) {
textcolour(LIGHTRED);
printf("Sorry, %s is currently not available. Better add one to the bookshelf!\n", search);
sleep(2);
return(emptybook); //return an empty book if found is still false
};
};
void titleview(book* lib, size_t libsize) {
lib = alloclib(notice);
lib = loadlibrary(notice);
textcolour(CYAN);
printf("| These are all records of the book currently at the library: \n");
for (int records = 0; records < libsize; records++) {
while (lib[records].checkdigit != CheckISBN(lib[records].ISBN))
records++; //Go to next record skipping the incorrect record
textcolour(BLUE);
printf("%d) ", records);
textcolour(CYAN);
if (records % 2 == 0)
textcolour(LIGHTBLUE);
printBook(lib[records]);
sleep(2);
printf("\n");
};
};
void menu(void) { //Program Menu
book found;
do { //Infinite loop until input
printf("\n");
textcolour(YELLOW);
printf(" ___________________________________________________________________________________________________________________________________\n");
printf(" |- __ ___ __ __ _____ _ __ -|\n");
printf(" |- / |/ / ___ ___ _ ___/ / ___ _ __ _ __ ___ _ / / ___ / ___/ ___ __ _ __ _ __ __ ___ (_) / /_ __ __ -|\n");
printf(" |- / /|_/ / / -_)/ _ `// _ / / _ `| |/|/ /| |/ // _ `/ / / / -_) / /__ / _ ` / ' ` / ' `/ // / / _ ` / / / __/ / // / -|\n");
printf(" |- /_/ /_/ |__/ |_,_/ |_,_/ |___/|__,__/ |___/ |_,_/ /_/ |__/ |___/ |___//_/_/_//_/_/_/|_,_/ /_//_//_/ |__/ |_, / -|\n");
printf(" |- /___/ -|\n");
printf(" |- __ _ __ ____ __ -|\n");
printf(" |- / / (_) / / ____ ___ _ ____ __ __ / __/ __ __ ___ / /_ ___ __ _ -|\n");
printf(" |- / /__ / / / _ ` / __// _ `/ / __/ / // / _` ` / // / (_-</ __// -_) / ' ` -|\n");
printf(" |- /____//_/ /_.__//_/ |_,_/ /_/ |_, / /___/ |_, / /___/|__/ |__/ /_/_/_/ -|\n");
printf(" |- /___/ /___/ -|\n");
printf(" |- -|\n");
printf(" | [1] View Upcoming Titles |\n");
printf(" | [2] Book Search |\n");
printf(" | [3] Exit |\n");
//User option
switch (getc(stdin)) {
case '1': clearscreen();
sleep(2);
titleview(noticelib, notice.records);
break;
case '2': clearscreen();
sleep(2);
found = booksearch(archive, archivelib); //Allows user to search a book
printBook(found);
break;
case '3': exit(0); //Exit
break;
default : textcolour(RED);
fprintf(stderr, "Invalid option. Try again.");
sleep(2);
clearscreen();
};
} while(1);
};
int main() {
noticelib = alloclib(notice);
archivelib = alloclib(archive);
menu();
free(archivelib); //free the archivelib
free(noticelib); //free the noticelib
return 0;
};
And my custom header:
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <conio.h>
#include <windows.h>
#include <stdio.h>
#ifndef __ADDON_H__
#define __ADDON_H__
#define MAX_PATH_LENGTH (255)
const int FIRST_LINE = 1;
const int START_OF_LINE = 0;
typedef struct {
size_t records;
size_t sizeofline;
char* filename;
FILE* fp;
struct {
int num;
char* format;
} field;
} file;
typedef struct {
int checkdigit; //permits mathematical checking for accuracy
char ISBN[18]; //ISBN number
char title[76]; //Title of material
char DOR[11]; //Date of release for the item
struct {
char firstname[11]; //first name
char lastname[11]; //last name
char fullname[22]; //First + Middle + Last name of Authour
} PersonDetails; //details about a person
char status[27];
} book; //structure for a book
enum {
BLACK = 0,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
LIGHTGRAY,
DARKGRAY,
LIGHTBLUE,
LIGHTGREEN,
LIGHTCYAN,
LIGHTRED,
LIGHTMAGENTA,
YELLOW,
WHITE
};
#endif //__ADDON_H__
I've been trying to get the number of fields that could successfully be scanned in my file as read in the function loadlibrary. However, it only reads two fields out of the intended 7.
Any corrections on how to fix the error or improve my code are welcome. Thank you!
Here is the data I am currently testing it on: ARCHIVE.txt
ISBN|Title|First Name|Last Name|Full Name|DOR|Status
978-51-7947-772-3|"Bleed Magic"|Orville|Baker|Orville Baker|15/04/2014|Available
978-05-0191-800-4|"Solar Waltz"|Alejandra|Bauer|Alejandra Bauer|30/07/1985|Unavailable - Rented
978-43-8687-725-8|"From Halsey to Hal's Island"|Jacklyn|Bruce|Jacklyn Bruce|09/05/1996|Available
978-36-2955-426-0|"Witch With Honor"|Miriam|Bryant|Miriam Bryant|14/07/1992|Unavailable - Damaged
978-95-2736-473-4|"Point A to Z"|Sang|Cherry|Sang Cherry|28/07/1987|Unavailable - Library use only
978-45-0742-523-8|"Priest named Sins"|Teddy|Decker|Teddy Decker|15/09/1989|Unavailable - Missing
978-27-1385-463-7|"Success of the Solstice"|Sandra|Duncan|Sandra Duncan|24/04/1989|Unavailable - In Process
978-31-8143-556-4|"What’s Over There?"|Leonel|Espinoza|Leonel Espinoza|25/03/2003|Available
978-95-5238-240-6|"The Amazing Adventures of Ice-Boy: Robots of Everest"|Ingrid|Guerra|Ingrid Guerra|24/06/2004|Available
978-85-7786-850-6|"One Boy And The World!"|Ezequiel|Hall|Ezequiel Hall|01/11/1994|Unavailable - Rented
978-28-5593-358-0|"Origin Of The Fog"|Nicky|Hancock|Nicky Hancock|23/08/2013|Available
978-33-2798-076-7|"Signs of the Past"|Lynwood|Hardin|Lynwood Hardin|27/02/1992|Available
978-23-8499-409-0|"Bleeding At The Past"|Renee|Johnston|Renee Johnston|07/05/1993|Available
978-32-9910-416-1|"Vision of Evil"|Jon|Kirby|Jon Kirby|16/02/2007|Unavailable - Rented
NOTICE.txt
ISBN|Title|First Name|Last Name|Full Name|DOR|Status
978-89-8818-596-4|"London Titans: As We Still Exist"|Kieth|Kirk|Kieth Kirk|08/08/1996|Unavailable - Coming Soon
978-30-7350-796-6|"Linger Longer"|Ester|Li|Ester Li|05/07/2022|Unavailable - Coming Soon
978-87-6109-805-4|"Till You, My Wallflower, Are Blooming"|Penny|Lucero|Penny Lucero|06/08/2018|Unavailable - Coming Soon
978-44-5008-783-5|"Fin and Tail, Claw and Tooth"|Bernadine|Mckinney|Bernadine Mckinney|18/10/2023|Unavailable - Coming Soon
978-60-3893-332-9|"Yes... Maybe? No!"|Vicky|Mills|Vicky Mills|05/02/2008|Unavailable - Coming Soon
978-55-4205-071-3|"Mermaids and Sirens"|Genevieve|Ochoa|Genevieve Ochoa|01/07/1987|Unavailable - Coming Soon
978-66-8758-265-7|"O Death, Where Is Thy Sting?"|Mohamed|Oconnell|Mohamed Oconnell|16/02/2021|Unavailable - Coming Soon
978-68-4196-147-2|"Behind the Door"|Hunter|Payne|Hunter Payne|19/05/2010|Unavailable - Coming Soon
978-60-6519-219-5|"Stay Hidden"|Saul|Powers|Saul Powers|22/12/2004|Unavailable - Coming Soon
978-90-3065-807-8|"The Things that I Never Will Learn"|Eva|Robertson|Eva Robertson|18/06/2007|Unavailable - Coming Soon
978-74-8013-934-5|"Humans With Us Untold Gods"|Suzette|Rosales|Suzette Rosales|25/02/1987|Unavailable - Coming Soon
978-87-4206-938-7|"Whispers of a Ghost"|Diego|Russo|Diego Russo|03/06/1996|Unavailable - Coming Soon
978-66-2724-397-6|"When Love Lasts"|Barbra|Sanders|Barbra Sanders|29/11/2018|Unavailable - Coming Soon
978-78-4619-100-0|"Pelicans We"|Sheila|Whitehead|Sheila Whitehead|18/11/1982|Unavailable - Coming Soon
978-09-4189-874-4|"Obsidian Leviathan"|Elwood|Wise|Elwood Wise|10/05/2023|Unavailable - Coming Soon
978-20-7104-860-0|"Of Fire and Silk"|Reyes|Wolf|Reyes Wolf|27/02/2001|Unavailable - Coming Soon
Answers
It seems like there are a few issues in your code that might be causing the problem with reading only two fields instead of seven in the loadlibrary
function. Here are some potential corrections and improvements:
-
Correct the
recordcount
function: Therecordcount
function seems to count the number of lines in a file to determine the number of records. However, it's reading the file character by character, which might not be efficient or accurate, especially if the file contains multiline records. Instead, you can use a line-based approach to count the number of records. -
Correct the
fileCheck
function: In thefileCheck
function, you're checking if the file exists using theaccess
function, but you're comparing the result tofalse
, which might not work as expected. Theaccess
function returns 0 on success and -1 on failure. So, you should compare the result to -1. -
Check the file format: Ensure that the format specifier in the
fscanf
function matches the format of the lines in your file exactly. Any discrepancies between the format specifier and the actual format of the lines can cause issues with reading the fields correctly. -
Use dynamic memory allocation for strings: Instead of defining fixed-size character arrays for strings like ISBN, title, etc., consider using dynamic memory allocation (e.g.,
malloc
) to allocate memory based on the length of the input strings. This will ensure that you can handle strings of varying lengths without overflow issues. -
Check for memory allocation failures: Whenever you allocate memory dynamically using
malloc
, make sure to check if the allocation was successful before using the allocated memory. Ifmalloc
returnsNULL
, it means that the allocation failed, and you should handle this error condition appropriately. -
Handle file errors gracefully: When opening files using
fopen
, check if the file was opened successfully before performing any operations on it. Iffopen
returnsNULL
, it means that the file opening failed, and you should handle this error condition appropriately. -
Use meaningful variable names: Use meaningful variable names that accurately describe their purpose and usage. For example, instead of
lib
, you can uselibrary
to represent a library of books. -
Avoid using
goto
: The use ofgoto
is generally discouraged in modern programming practices because it can make the code harder to understand and maintain. Consider using structured control flow constructs like loops and conditional statements instead.
By addressing these issues and making the necessary corrections, you should be able to improve the functionality and reliability of your code. Let me know if you need further assistance with any specific part of the code!