From 9b919149af3a2e02d1e56b1f8d890d8f48ae7d32 Mon Sep 17 00:00:00 2001
From: Brandon Rodriguez <brodriguez8774@gmail.com>
Date: Sun, 24 Sep 2017 17:08:14 -0400
Subject: [PATCH] Add full handling of user input and song searching

Now includes searching by album name
---
 BuildDataBase.c   |  35 ++------
 HelperFunctions.c |  56 +++++++++++-
 HelperHeader.h    |   2 +
 UseDataBase.c     | 219 ++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 245 insertions(+), 67 deletions(-)

diff --git a/BuildDataBase.c b/BuildDataBase.c
index 8b05edf..3f1627e 100644
--- a/BuildDataBase.c
+++ b/BuildDataBase.c
@@ -56,7 +56,6 @@ void read_line();               // Separates file chunk into lines.
 void tokenize_line();           // Separates lines into fields.
 void populate_array();          // Populates array with fields.
 void resize_array();            // Reallocates memory for array.
-char* remove_quotes();          // Removes quotes from string value.
 void print_all_songs();         // Prints info for all songs in array.
 void print_song_info();         // Prints all info for provided song.
 void sort_array();              // Sorts array by song name.
@@ -71,15 +70,16 @@ void exit_program();            // Frees memory and closes program.
  * Initializes and runs program.
  */
 int main(int argc, char* argv[]) {
+    write(1, "Reading csv...\n", 16);
     songs_array = calloc(songs_array_max, sizeof(songs_struct *));
     int file_descriptor = open_file("Data/SongCSV_Small.csv", O_RDONLY);
     read_file(file_descriptor);
-    write(1, "\n\n\n\n\n", 6);
-    //print_all_songs();
-    //write(1, "\nSorting...\n\n\n", 15);
     sort_array();
-    print_all_songs();
+    //print_all_songs();
+    write(1, "Saving to file...\n", 19);
     save_array();
+    write(1, "Finished.\n", 11);
+    write(1, "Closing program...\n", 19);
     close(file_descriptor);
     exit_program();
 }
@@ -284,31 +284,6 @@ void populate_array(int field_number, char *field_buffer) {
 }
 
 
-/**
- * Removes quotes from provided string.
- */
-char* remove_quotes(char* a_string) {
-    size_t string_length = strlen(a_string);
-    int orig_index;
-    int replace_index = 0;
-
-    for (orig_index = 0; orig_index < string_length; orig_index++) {
-        if (a_string[orig_index] != '\"') {
-            a_string[replace_index] = a_string[orig_index];
-            replace_index++;
-        }
-    }
-
-    // Fill rest of values with null terminators.
-    while (replace_index < string_length) {
-        a_string[replace_index] = '\0';
-        replace_index++;
-    }
-
-    return a_string;
-}
-
-
 /**
  * Sorts array by song name.
  */
diff --git a/HelperFunctions.c b/HelperFunctions.c
index 9ab8f67..e36edb6 100644
--- a/HelperFunctions.c
+++ b/HelperFunctions.c
@@ -113,7 +113,7 @@ double* copy_double(double *source_ptr) {
  */
 char* to_lower_case(char* input_string) {
     int index = 0;
-    char* return_string = calloc(strlen((input_string) + 1), sizeof(char));
+    char* return_string = calloc((strlen(input_string) + 1), sizeof(char));
     while (input_string[index]) {
         return_string[index] = tolower(input_string[index]);
         index++;
@@ -162,3 +162,57 @@ char* first_letter_upper(char* input_string) {
     }
     return return_string;
 }
+
+
+/**
+ * Removes quotes from provided string.
+ *
+ * Return: Quote-less version of initial string.
+ */
+char* remove_quotes(char* input_string) {
+    size_t string_length = strlen(input_string);
+    int orig_index;
+    int replace_index = 0;
+
+    for (orig_index = 0; orig_index < string_length; orig_index++) {
+        if (input_string[orig_index] != '\"') {
+            input_string[replace_index] = input_string[orig_index];
+            replace_index++;
+        }
+    }
+
+    // Fill rest of values with null terminators.
+    while (replace_index < string_length) {
+        input_string[replace_index] = '\0';
+        replace_index++;
+    }
+
+    return input_string;
+}
+
+
+/**
+ * Removes newline character from string.
+ *
+ * Return: Newline-less version of initial string.
+ */
+char* remove_newline(char* input_string) {
+    size_t string_length = strlen(input_string);
+    int orig_index;
+    int replace_index = 0;
+
+    for (orig_index = 0; orig_index < string_length; orig_index++) {
+        if (input_string[orig_index] != '\n') {
+            input_string[replace_index] = input_string[orig_index];
+            replace_index++;
+        }
+    }
+
+    // Fill rest of values with null terminators.
+    while (replace_index < string_length) {
+        input_string[replace_index] = '\0';
+        replace_index++;
+    }
+
+    return input_string;
+}
diff --git a/HelperHeader.h b/HelperHeader.h
index 9f74301..3d52d6f 100644
--- a/HelperHeader.h
+++ b/HelperHeader.h
@@ -20,3 +20,5 @@ double* copy_double(double* source_ptr);
 char* to_lower_case(char* input_string);
 char* to_upper_case(char* input_string);
 char* first_letter_upper(char* input_string);
+char* remove_quotes(char* input_string);
+char* remove_newline(char* input_string);
diff --git a/UseDataBase.c b/UseDataBase.c
index f512207..d9f0f7e 100644
--- a/UseDataBase.c
+++ b/UseDataBase.c
@@ -58,6 +58,10 @@ void populate_array();          // Populates array with fields.
 void resize_array();            // Reallocates memory for array.
 void print_all_songs();         // Prints info for all songs in array.
 void print_song_info();         // Prints all info for provided song.
+void print_help_text();         // Prints helper text for user.
+void find_song();               // Find song with given name.
+songs_struct* search_array_by_song();   // Binary search for name.
+songs_struct* search_array_by_album();  // Linear search for album.
 void exit_program();            // Frees memory and closes program.
 
 
@@ -66,13 +70,23 @@ void exit_program();            // Frees memory and closes program.
  * Initializes and runs program.
  */
 int main(int argc, char* argv[]) {
+    int run_program_bool;       // Bool to keep program running.
+    int song_directory_descriptor;  // File descriptor for directory data.
+    int song_binary_descriptor; // File descriptor for binary song data.
+    int* song_size;             // "Directory" size of current song.
+    char* user_input_string;    // String of user's input.
+    char* input_to_lower;       // User input as all lower.
+    off_t read_value;           // Return value of read attempt.
+    off_t offset_size;          // Return value of lseek attempt.
+
+    // Initialize vars.
+    run_program_bool = 1;
+    song_directory_descriptor = open_file("Data/SongDirectory_Small", O_RDONLY);
+    song_binary_descriptor = open_file("Data/BinarySongData_Small", O_RDONLY);
     songs_array = calloc(songs_array_max, sizeof(songs_struct *));
-    int song_directory_descriptor = open_file("Data/SongDirectory_Small", O_RDONLY);
-    int song_binary_descriptor = open_file("Data/BinarySongData_Small", O_RDONLY);
-    int* song_size = calloc(1, (sizeof(int) + 1));
-    off_t read_value;
-    off_t offset_size;
+    song_size = calloc(1, (sizeof(int) + 1));
 
+    // Read songs until no more directories are present.
     while ((read_value = read(song_directory_descriptor, song_size, sizeof(int))) != 0) {
         if (read_value < 0) {
             err_sys("Failed to read line.");
@@ -86,12 +100,48 @@ int main(int argc, char* argv[]) {
         read_songs(song_binary_descriptor, song_size);
     }
 
-    print_all_songs();
+    print_help_text();
+
+    while (run_program_bool) {
+        user_input_string = calloc(1, BUFFER_SIZE);
+
+        // Read input from console.
+        write(1, "Enter input: ", 14);
+        read_value = read(0, user_input_string, BUFFER_SIZE);
+        write(1, "\n", 2);
+
+        user_input_string = remove_newline(user_input_string);
+
+        // Read user input and handle accordingly.
+        if ((user_input_string != NULL) && (strcmp(user_input_string, "") != 0)) {
+            input_to_lower = to_lower_case(user_input_string);
+
+            // Check if user needs reprinting of help text.
+            if (strcmp(input_to_lower, "help") == 0) {
+                print_help_text();
+            }
+            // Check if user has requested to exit program.
+            else if (strcmp(input_to_lower, "zzz") == 0) {
+                run_program_bool = 0;
+                printf("Exiting Program...\n");
+            }
+            // Check if user has requested to print all songs.
+            else if (strcmp(input_to_lower, "all") == 0) {
+                print_all_songs();
+            }
+            else { // Attempt to find song with given name.
+                //printf("User Input: %s", input_to_lower);
+                find_song(input_to_lower);
+            }
+            free(input_to_lower);
+        }
+        free(user_input_string);
+    }
 
+    // Free memory and close program.
     free(song_size);
     close(song_directory_descriptor);
     close(song_binary_descriptor);
-
     exit_program();
 }
 
@@ -117,8 +167,6 @@ void read_songs(int binary_descriptor, int* song_size) {
     void* read_buffer;
     read_buffer = calloc(1, BUFFER_SIZE);
 
-    printf("Song Struct Size: %d\n", *song_size);
-
     ssize_t read_value = read(binary_descriptor, read_buffer, *song_size);
     if (read_value < 0) {
         err_sys("Failed to read in song.");
@@ -130,7 +178,6 @@ void read_songs(int binary_descriptor, int* song_size) {
     }
 
     populate_array(song_size, read_buffer);
-
     free(read_buffer);
 }
 
@@ -146,95 +193,69 @@ void populate_array(int song_size, ssize_t read_buffer) {
     double* double_buffer;
     double* double_pointer;
 
-
     songs_array[song_index] = calloc(1, sizeof(songs_struct));
 
     // Get Song Name.
     char_pointer = ((char*)read_buffer);
     char_buffer = calloc(1, BUFFER_SIZE);
-
     index = 0;
     while (char_pointer[0] != '\0') {
         char_buffer[index] = *char_pointer;
         ++char_pointer;
         index++;
     }
-    printf("Name: %s\n", char_buffer);
     songs_array[song_index]->song_name = copy_string(char_buffer);
-    printf("Struct Value: %s\n", songs_array[song_index]->song_name);
     ++char_pointer;
     free(char_buffer);
 
-
     // Get Album.
     char_buffer = calloc(1, BUFFER_SIZE);
-
     index = 0;
     while (char_pointer[0] != '\0') {
         char_buffer[index] = *char_pointer;
         ++char_pointer;
         index++;
     }
-    printf("Album: %s\n", char_buffer);
     songs_array[song_index]->album_name = copy_string(char_buffer);
-    printf("Struct Value: %s\n", songs_array[song_index]->album_name);
     ++char_pointer;
     free(char_buffer);
 
-
     // Get Artist.
     char_buffer = calloc(1, BUFFER_SIZE);
-
     index = 0;
     while (char_pointer[0] != '\0') {
         char_buffer[index] = *char_pointer;
         ++char_pointer;
         index++;
     }
-    printf("Artist: %s\n", char_buffer);
     songs_array[song_index]->artist = copy_string(char_buffer);
-    printf("Struct Value: %s\n", songs_array[song_index]->artist);
     ++char_pointer;
     free(char_buffer);
 
-
     // Get Duration.
     float_buffer = calloc(1, (sizeof(float) + 1));
     float_pointer = (float*)char_pointer;
     *float_buffer = *float_pointer;
-
-    printf("Duraton: %.2f\n", *float_buffer);
     songs_array[song_index]->duration = copy_float(float_buffer);
-    printf("Struct Value: %.2f\n", *songs_array[song_index]->duration);
     ++float_pointer;
     free(float_buffer);
 
-
     // Get Hotttnesss.
     double_buffer = calloc(1, (sizeof(double) + 1));
     double_pointer = (double*)float_pointer;
     *double_buffer = *double_pointer;
-
-    printf("Hotttnesss: %.2f\n", *double_buffer);
     songs_array[song_index]->hotttnesss = copy_double(double_buffer);
-    printf("Struct Value: %.2f\n", *songs_array[song_index]->hotttnesss);
     ++double_pointer;
     free(double_buffer);
 
-
     // Get Year.
     int_buffer = calloc(1, (sizeof(int) + 1));
     int_pointer = (int*)double_pointer;
     *int_buffer = *int_pointer;
-
-    printf("Year: %d\n", *int_buffer);
     songs_array[song_index]->year = copy_int(int_buffer);
-    printf("Struct Value: %d\n", *songs_array[song_index]->year);
     ++int_pointer;
     free(int_buffer);
 
-    printf("\n\n");
-
     song_index++;
 
 }
@@ -271,6 +292,132 @@ void print_song_info(songs_struct* song) {
 }
 
 
+/**
+ * Prints helper text.
+ * Called on program start and again if user types "Help".
+ */
+void print_help_text() {
+    printf("To display help text again, type 'Help'.\n");
+    printf("To print all songs, type 'All'\n");
+    printf("To exit program, type 'ZZZ'.\n");
+    printf("Otherwise, type name of song you wish to locate.\n\n");
+}
+
+
+/**
+ * Searches through array and finds song based on user input.
+ */
+void find_song(char* user_input_string) {
+    int song_counter = -1; // To account for arrays starting at 0.
+    int index = 0;
+    char* temp_string;
+    songs_struct* song;
+    songs_struct* temp_song = calloc(1, sizeof(songs_struct*));
+
+    // Count current number of songs in array.
+    while (songs_array[index] != NULL) {
+        song_counter++;
+        index++;
+    }
+
+    // Search for user's input, first by song_name, then by album name.
+    song = search_array_by_song(temp_song, 0, song_counter, user_input_string);
+
+    // Check returned song.
+    if (song != NULL) {
+        print_song_info(song);
+        free(temp_song);
+    } else { // Not found. Search again by album instead of song_name.
+        song = search_array_by_album(0, song_counter, user_input_string);
+        if (song != NULL) {
+            print_song_info(song);
+        } else {
+            // Could not find song or album.
+            temp_string = first_letter_upper(user_input_string);
+            err_msg("Could not find song or album with name: %s\n", temp_string);
+            free(temp_string);
+        }
+    }
+    //free(song);
+}
+
+
+/**
+ * Uses binary search to locate song with desired name.
+ *
+ * Return: Either valid song or NULL, depending on if record is found or not.
+ */
+songs_struct* search_array_by_song(songs_struct* return_song,
+                int first_index, int last_index, char* desired_record) {
+
+    //songs_struct* return_song = calloc(1, sizeof(songs_struct*));  // Song to return.
+    char* temp_string;
+
+    // First check if this is the end of search or not.
+    if (first_index >= last_index) {
+
+        // End of search. Handle accordingly.
+        temp_string = to_lower_case(songs_array[first_index]->song_name);
+        if ((strcmp(desired_record, temp_string)) == 0 ) {
+            // Match found. Use given struct.
+            free(temp_string);
+            return_song = songs_array[first_index];
+        } else {
+            // End of search and no match found. Use NULL.
+            free(temp_string);
+            free(return_song);
+            return_song = NULL;
+        }
+
+    } else { // Still more values to search. Handle accordingly.
+        int mid_index = (first_index + last_index) / 2;
+        temp_string = to_lower_case(songs_array[mid_index]->song_name);
+        if ((strcmp(desired_record, temp_string)) == 0 ) {
+            // Match found. Use given struct.
+            free(temp_string);
+            return_song = songs_array[mid_index];
+        } else {
+            if ((strcmp(desired_record, temp_string)) < 0 ) {
+                // Current struct song name is higher letter than desired.
+                // Searching first half of provided structs.
+                free(temp_string);
+                return_song = search_array_by_song(return_song, first_index, mid_index, desired_record);
+            } else {
+                // Current struct song name is lower letter than desired.
+                // Searching later half of provided structs.
+                free(temp_string);
+                return_song = search_array_by_song(return_song, mid_index + 1, last_index, desired_record);
+            }
+        }
+    }
+
+    return return_song;
+}
+
+
+/**
+ * Songs Array is not sorted by album, thus uses basic linear search.
+ * This is called far less often so that should be acceptable.
+ *
+ * Return either valid album or NULL, depending on if record is found or not.
+ */
+songs_struct* search_array_by_album(int first_index, int last_index, char* desired_record) {
+    int index = 0;
+
+    while (songs_array[index] != NULL) {
+        char* temp_string = to_lower_case(songs_array[index]->album_name);
+        if (strcmp(temp_string, desired_record) == 0) {
+            free(temp_string);
+            return songs_array[index];
+        }
+        index++;
+        free(temp_string);
+    }
+
+    return NULL;
+}
+
+
 /**
  * Frees remaining memory and so program can close cleanly.
  */
-- 
GitLab