From 9fc68e1b72adb77e3746807f13cee0e73deaef78 Mon Sep 17 00:00:00 2001 From: SuperNovaa41 Date: Sat, 13 Jan 2024 21:29:52 -0500 Subject: [PATCH] Cleans up the code Separates some functions into their own files separates some big functions in to smaller ones to help improve the code cleans up some stuff --- Makefile | 2 +- curl.c | 67 ++++++++++++++++++ curl.h | 40 +++++++++++ json.c | 70 +++++++++++++++++++ json.h | 40 +++++++++++ main.c | 206 ++++++++----------------------------------------------- 6 files changed, 247 insertions(+), 178 deletions(-) create mode 100644 curl.c create mode 100644 curl.h create mode 100644 json.c create mode 100644 json.h diff --git a/Makefile b/Makefile index 8d8b0ce..4b40e34 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ all: main.c - gcc main.c -lcurl -lcjson -o isbn -Wall + gcc main.c json.c curl.c -lcurl -lcjson -o isbn -Wall clean: rm isbn diff --git a/curl.c b/curl.c new file mode 100644 index 0000000..984dc15 --- /dev/null +++ b/curl.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#include "curl.h" + +#define BASE_URL_SIZE 38 + +void init_string(string* s) +{ + s->len = 0; + s->buf = malloc(s->len + 1); + + if (NULL == s->buf) { + fprintf(stderr, "malloc() failed!\n"); + exit(EXIT_FAILURE); + } + s->buf[0] = '\0'; +} + +size_t writefunc(void* ptr, size_t size, size_t nmemb, string* s) +{ + size_t new_len = s->len + (size * nmemb); + s->buf = realloc(s->buf, new_len + 1); + if (NULL == s->buf) { + fprintf(stderr, "realloc() failed!\n"); + exit(EXIT_FAILURE); + } + memcpy(s->buf + s->len, ptr, size * nmemb); + s->buf[new_len] = '\0'; + s->len = new_len; + return size * nmemb; +} + +CURLcode perform_book_get(char* isbn, string* s) +{ + CURL* handler; + char* base_url; + size_t finalurl_size = BASE_URL_SIZE + 15; + char finalurl[finalurl_size]; + CURLcode result; + + handler = curl_easy_init(); + if (!handler) { + fprintf(stderr, "cURL failed to initialize!\n"); + exit(EXIT_FAILURE); + } + + base_url = "https://openlibrary.org/search.json?q="; + + // Setup the URL for the request + snprintf(finalurl, finalurl_size, "%s%s", base_url, isbn); + + curl_easy_setopt(handler, CURLOPT_URL, finalurl); + curl_easy_setopt(handler, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(handler, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(handler, CURLOPT_WRITEDATA, s); + + result = curl_easy_perform(handler); + + // Always clean up + curl_easy_cleanup(handler); + + return result; + +} diff --git a/curl.h b/curl.h new file mode 100644 index 0000000..5f5261e --- /dev/null +++ b/curl.h @@ -0,0 +1,40 @@ +#ifndef CURL_H +#define CURL_H + +/** + * struct string_t + * char* buf - The buffer containing the string data + * size_t len - The length of the buffer + */ +typedef struct string_t { + char* buf; + size_t len; +} string; + +/** + * void init_string + * string* s - The string struct + * + * Initializes the string struct + */ +void init_string(string* s); + +/** + * size_t writefunc + * + * The arguments from CURLOPT_WRITEFUNCTION + * + * Writes the cURL get into the string struct + */ +size_t writefunc(void* ptr, size_t size, size_t nmemb, string* s); + +/** + * CURLcode perform_book_get + * char* isbn - The ISBN + * string* s - The string struct to place the GET output in + * + * Performs a GET request with the ISBN + */ +CURLcode perform_book_get(char* isbn, string* s); + +#endif diff --git a/json.c b/json.c new file mode 100644 index 0000000..dd74d2b --- /dev/null +++ b/json.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +#include "curl.h" +#include "json.h" + +#define MAX_BUF_LEN 1024 + +void check_valid_query(cJSON* numfound) +{ + if (0 != numfound->valueint) + return; + fprintf(stderr, "No ISBN found!\n"); + exit(EXIT_FAILURE); +} + +void get_authors(cJSON* bookinfo, char authors[256]) +{ + char* temp_author; + size_t new_len; + cJSON* author_arr = cJSON_GetObjectItemCaseSensitive(bookinfo, "author_name")->child; + + snprintf(authors, strlen(author_arr->valuestring) + 1, "%s", author_arr->valuestring); + author_arr = author_arr->next; + + while (NULL != author_arr) { + // The plus 1 is for the \0, the plus 2 is for the ", " + new_len = strlen(authors) + strlen(author_arr->valuestring) + 1 + 2; + + temp_author = malloc(sizeof(char) * new_len); + snprintf(temp_author, new_len, "%s, %s", authors, author_arr->valuestring); + memcpy(authors, temp_author, new_len); + + free(temp_author); + + author_arr = author_arr->next; + } +} + +void parse_json(string* s, char* isbn, book_t* book) +{ + char authors[MAX_BUF_LEN]; + + cJSON* json = cJSON_Parse(s->buf); + if (NULL == json) { + const char* error_ptr = cJSON_GetErrorPtr(); + if (NULL != error_ptr) + fprintf(stderr, "JSON error: %s\n", error_ptr); + cJSON_Delete(json); + exit(EXIT_FAILURE); + } + + check_valid_query(cJSON_GetObjectItemCaseSensitive(json, "numFound")); + + cJSON* bookinfo = cJSON_GetObjectItemCaseSensitive(json, "docs")->child; + + book->isbn = isbn; + book->title = cJSON_GetObjectItemCaseSensitive(bookinfo, "title")->valuestring; + book->year_of_publication = cJSON_GetObjectItemCaseSensitive(bookinfo, "first_publish_year")->valueint; + book->page_len = cJSON_GetObjectItemCaseSensitive(bookinfo, "number_of_pages_median")->valueint; + + get_authors(bookinfo, authors); + + // Need to malloc, because we need to copy authors into the book struct + book->authors = (char*) malloc(sizeof(char) * (strlen(authors) + 1)); + memcpy(book->authors, authors, strlen(authors) + 1); +} diff --git a/json.h b/json.h new file mode 100644 index 0000000..5d76cc7 --- /dev/null +++ b/json.h @@ -0,0 +1,40 @@ +#ifndef JSON_H +#define JSON_H + +#define MAX_BUF_LEN 1024 + +typedef struct book_t { + char* isbn; + char* title; + char* authors; + int year_of_publication; + int page_len; +} book_t; + +/** + * void check_valid_query + * cJSON* numfound - The json entry that contains the numFound value + * + * Checks if there are any search results in this json entry + */ +void check_valid_query(cJSON* numfound); + +/** + * void get_authors + * cJSON* bookinfo - The JSON entry that has all of the information about the book + * char* authors - The string to fill + * + * Takes the JSON entry from bookinfo and combines all of the authors into one final string + */ +void get_authors(cJSON* bookinfo, char* authors); + +/** + * void parse_json + * string* s - The string struct + * char* isbn - The ISBN + * book_t* book - The struct to fill with book information + * + * Parses through the given JSON entry and gathers all of the information we need + */ +void parse_json(string* s, char* isbn, book_t* book); +#endif diff --git a/main.c b/main.c index 416826f..a1a4600 100644 --- a/main.c +++ b/main.c @@ -1,115 +1,27 @@ #include +#include #include #include #include #include -#include +#include "curl.h" +#include "json.h" -#define BASE_URL_SIZE 38 #define FILE_NAME "books.csv" #define MAX_BUF_LEN 1024 -/** - * struct string_t - * char* buf - The buffer containing the string data - * size_t len - The length of the buffer - */ -typedef struct string_t { - char* buf; - size_t len; -} string; - -/** - * void init_string - * string* s - The string struct - * - * Initializes the string struct */ -void init_string (string* s) +void print_book(book_t* book) { - s->len = 0; - s->buf = malloc(s->len + 1); - - if (NULL == s->buf) { - fprintf(stderr, "malloc() failed\n"); exit(EXIT_FAILURE); - } - s->buf[0] = '\0'; + printf("ISBN: %s\n", book->isbn); + printf("Title: %s\n", book->title); + printf("Author(s): %s\n", book->authors); + printf("(First) Year of Publication: %d\n", book->year_of_publication); + printf("Page length: %d\n", book->page_len); } -/** - * size_t writefunc - * - * The arguments come from CURLOPT_WRITEFUNCTION - * - * Writes the cURL get into the string struct - */ -size_t writefunc(void* ptr, size_t size, size_t nmemb, string* s) -{ - size_t new_len = s->len + (size * nmemb); - s->buf = realloc(s->buf, new_len + 1); - if (NULL == s->buf) { - fprintf(stderr, "realloc() failed\n"); - exit(EXIT_FAILURE); - } - - memcpy(s->buf + s->len, ptr, size * nmemb); - s->buf[new_len] = '\0'; - s->len = new_len; - - return size * nmemb; -} - -/** - * CURLcode perform_book_get - * char isbn_buf[14] - The ISBN - * string* s - The string struct to place the GET output in - * - * Perforrms a GET request with the ISBN - */ -CURLcode perform_book_get(char isbn_buf[14], string* s) -{ - CURL* handler; - char *base_url; - size_t finalurl_size = BASE_URL_SIZE + 15; - char finalurl[finalurl_size]; - CURLcode result; - - handler = curl_easy_init(); - if (!handler) { - fprintf(stderr, "cURL failed to initialize!\n"); - exit(EXIT_FAILURE); - } - - base_url = "https://openlibrary.org/search.json?q="; - /** - * Setup the URL for the request - */ - snprintf(finalurl, finalurl_size, "%s%s", base_url, isbn_buf); - - curl_easy_setopt(handler, CURLOPT_URL, finalurl); - curl_easy_setopt(handler, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(handler, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(handler, CURLOPT_WRITEDATA, s); - - result = curl_easy_perform(handler); - - // Always clean up - curl_easy_cleanup(handler); - - return result; -} - -void print_book(char* isbn, char* title, char* authors, int year, int page_len) -{ - printf("ISBN: %s\n", isbn); - printf("Title: %s\n", title); - printf("Author(s): %s\n", authors); - printf("(First) Year of Publication: %d\n", year); - printf("Page length: %d\n", page_len); -} - -void write_to_file(char* isbn, char* title, char* authors, int year, int page_len) +void write_to_file(book_t* book) { FILE* file; int file_exists; @@ -128,93 +40,18 @@ void write_to_file(char* isbn, char* title, char* authors, int year, int page_le } // now we write the information - fprintf(file, "\"%s\",\"%s\",\"%s\",%d,%d\n", isbn, title, authors, year, page_len); + fprintf(file, "\"%s\",\"%s\",\"%s\",%d,%d\n", + book->isbn, book->title, book->authors, book->year_of_publication, book->page_len); fclose(file); } -/** - * void parse_json - * string* s - Pointer to the string struct - * - * Parses the JSON inside of s.buf and prints the information we're looking for - */ -void parse_json(string* s, char* options, char* isbn_buf) -{ - char *title, *temp_author; - int year, page_len, new_len; - char authors[MAX_BUF_LEN]; - - cJSON* json = cJSON_Parse(s->buf); - if (NULL == json) { - const char* error_ptr = cJSON_GetErrorPtr(); - if (NULL != error_ptr) - fprintf(stderr, "JSON error: %s\n", error_ptr); - cJSON_Delete(json); - exit(EXIT_FAILURE); - } - - /** - * if there are no results, we should exit - * if theres too many, we should just treat it normally - */ - cJSON* numFound = cJSON_GetObjectItemCaseSensitive(json, "numFound"); - if (0 == numFound->valueint) { - fprintf(stderr, "No ISBN found!\n"); - exit(EXIT_FAILURE); - } - - cJSON* docs = cJSON_GetObjectItemCaseSensitive(json, "docs"); - cJSON* child = docs->child; // this is the JSON object that stores all of the books information - - title = cJSON_GetObjectItemCaseSensitive(child, "title")->valuestring; - year = cJSON_GetObjectItemCaseSensitive(child, "first_publish_year")->valueint; - page_len = cJSON_GetObjectItemCaseSensitive(child, "number_of_pages_median")->valueint; - - /** - * The author value is a linked list, so we want to loop through each value - */ - - cJSON* authors_json = cJSON_GetObjectItemCaseSensitive(child, "author_name"); - cJSON* authorarr = authors_json->child; - - // we want to do this first out of the loop, because of formatting - snprintf(authors, strlen(authorarr->valuestring) + 1, "%s", authorarr->valuestring); - authorarr = authorarr->next; - while (NULL != authorarr) { - // The plus one is for the \0, the plus two is for the ", " - new_len = strlen(authors) + strlen(authorarr->valuestring) + 1 + 2; - - /** - * So first we make a new string to hold the new string temporarily - * - * Then we want to add formatting by using the existing authors string - * along with the next string - * - * Then we memcpy the temp string into the original string - */ - temp_author = malloc(sizeof(char) * new_len); - snprintf(temp_author, new_len, "%s, %s", authors, authorarr->valuestring); - memcpy(authors, temp_author, new_len); - - // Free since we malloc'd - free(temp_author); - authorarr = authorarr->next; - } - - if (0 == strcmp(options, "r")) - print_book(isbn_buf, title, authors, year, page_len); - else if (0 == strcmp(options, "w")) - write_to_file(isbn_buf, title, authors, year, page_len); - else - fprintf(stderr, "Improper option provided!"); -} - int main(int argc, char* argv[]) { char isbn_buf[14]; // want to hold a max of 14 so we can hold up to ISBN13s char options[2]; CURLcode res; + book_t new_book; if (3 != argc) { printf("Usage: isbn [isbn] [options]\n"); @@ -227,6 +64,7 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } + /** * We must initialize cURL */ @@ -241,6 +79,11 @@ int main(int argc, char* argv[]) * Grab the formatting options from argv */ snprintf(options, 2, "%s", argv[2]); + if (!(('w' == options[0]) || ('r' == options[0]))) { + fprintf(stderr, "Invalid option submitted!\n"); + return EXIT_FAILURE; + } + /** * Setup the output string @@ -257,16 +100,25 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } + /** * Now we want to parse the JSON input */ - parse_json(&get_output, options, isbn_buf); + parse_json(&get_output, isbn_buf, &new_book); /** * We need to free this string */ free(get_output.buf); + /** + * NOW we either print or save the book + */ + if (options[0] == 'w') + write_to_file(&new_book); + else if (options[0] == 'r') + print_book(&new_book); + return 0; }