Contact Management System in C

it’s only my second code in C so far. I’ve some knowledge about this language, dynamic memory allocation etc. but I’m still far to be good in it. Earlier I used to program in Java, so I could have passed some habits from there. In general – what should I change to make this code somehow more perfect, what I shouldn’t do and what practices should be cultivated by me?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <time.h>

#define BASE_DECIMAL 10
#define ID_LENGTH 5
#define MAX_FNAME_LENGTH 16
#define MAX_SNAME_LENGTH 32
#define MAX_PHONENUMBER_LENGTH 15
#define BUFFER_SIZE 256
//colors
#define ANSI_COLOR_RED     "x1b(31m"
#define ANSI_COLOR_MAGENTA "x1b(35m"
#define ANSI_COLOR_CYAN    "x1b(36m"
#define ANSI_COLOR_RESET   "x1b(0m"

//prototypes
char* inputFirstName();
char* inputSurname();
char* inputPhoneNumber();
bool searchDuplicatedNumber( FILE *fp, char* search_Number );
char* checkConditions( FILE *fp );
char* ordinals( int i );
char* generateID();
FILE *checkInAllContacts();
void addNewContact( char* path, FILE *allContacts );
void addToAllContacts( char* path );
void showContactBasedOnPath( char* path );

int main( int argC, char* argV() ) {
    
    //declare
    char* id;
    char option;
    char* path_to_specific_file;

    //allocate
    id = malloc(ID_LENGTH * sizeof(char));
    path_to_specific_file = malloc(ID_LENGTH * sizeof(char));

    //check
    if( id == NULL || path_to_specific_file == NULL) {
        puts( "Memory allocation failed" );
        return 0;
    }
    
    do{

    printf(     "ntt***Contact management system***n1. Add new contactn2. Show all contactsn3. Show specific contactn*Press 'q' to exit*n"      );
    fgets( &option, 3, stdin );
    
    //assign
    id = generateID();
    
    //MENU
    switch(option) {
        
        case '1':
            addNewContact(id, checkInAllContacts());
            fclose(checkInAllContacts());
            addToAllContacts(id);
            free(id);
            break;

        case '2':
            showContactBasedOnPath("All_Contacts.txt");
            break;

        case '3': //I know it can be probably achieved in different way
            do{
                puts( "Input path to file(based on given id!): ");
                fgets( path_to_specific_file, (ID_LENGTH + 1), stdin );
                showContactBasedOnPath(path_to_specific_file);
            }while((getchar()) != 'n');
            
            free(path_to_specific_file);
            break;
        
        case 'q':
            printf(ANSI_COLOR_RED   "nExiting...n"   ANSI_COLOR_RESET);
            exit(0);

        default:
            puts( "Undefined option!" );
            break;
    }
    }while(1);
    return 0;
}

//functions-----------------------

char* inputFirstName() {
    
    char* name;
    name = (char*) malloc( MAX_FNAME_LENGTH * sizeof(char) );
    
    if( name == NULL ) {
        puts( "Memory allocation failed - inputFirstName" );
        exit( EXIT_FAILURE );
    }


    printf( "nn%s: ", "First name" );
    fgets( name, MAX_FNAME_LENGTH, stdin );
    return name;
}

char* inputSurname() {
        
    char* sName;
    sName = (char*) malloc( MAX_SNAME_LENGTH * sizeof(char) );

    if( sName == NULL ) {
        puts( "Memory allocation failed - inputSurname" );
        exit( EXIT_FAILURE );
    }


    printf( "n%s: ", "Surname");
    fgets( sName, MAX_SNAME_LENGTH, stdin );
    return sName;
}


//phone number functions
char* inputPhoneNumber() {
    
    char* phoneNumber;
    phoneNumber = (char*) malloc( MAX_PHONENUMBER_LENGTH * sizeof(char) );
    
    if( phoneNumber == NULL ) {
        puts( "Memory allocation failed - inputPhoneNumber" );
        exit( EXIT_FAILURE );
    }

    printf( "n%s: ", "Phone number" );
    fgets( phoneNumber, MAX_PHONENUMBER_LENGTH, stdin );
    return phoneNumber;
}
//search for duplicates
bool searchDuplicatedNumber(FILE *fp, char* search_Number) {
    
    char* properties = (char*) malloc( BUFFER_SIZE * sizeof(char) );

    if( properties == NULL ) {
        puts( "Memory allocation failed - searchDuplicatedNumber" );
        exit( EXIT_FAILURE );
    }
    //point to character
    char *ptc;
    while( ( fgets(properties, BUFFER_SIZE, fp) ) != NULL ) {
        
        ptc = strstr(properties, search_Number);
        if( ptc != NULL) {
            return true;
        }
    }
    free(properties);
    return false;
}
//check if p.number meets all conditions
char* checkConditions(FILE *fp) {
    
    char continue_Question;
    char* phone_Number;
    char* which_Ordinal;

    phone_Number = (char*) malloc( MAX_PHONENUMBER_LENGTH * sizeof(char) );
    which_Ordinal = (char*) malloc( 2 * sizeof(char) );
    if( phone_Number == NULL || which_Ordinal == NULL ) {
        puts( "Memory allocation failed - checkConditions" );
        exit( EXIT_FAILURE );
    }
    phone_Number = inputPhoneNumber();
    
    //check if it has character
    for(int i = 0; i < strlen(phone_Number) - 1; ++i) {
        if( !isdigit( phone_Number(i) ) ){
            
            which_Ordinal = ordinals(i + 1);
            printf( "nDetected error in input at %d%s positionnTry againn"
                    , i + 1, which_Ordinal);
            //use recursion to make user provide correct input
            return checkConditions(fp);
        }
    }
    free(which_Ordinal);
    //check for duplicated number 
    if( searchDuplicatedNumber(fp, phone_Number) ) {
        printf( "nPhone number -> %s is assigned to another contact!n", phone_Number );
        //if user accidentally provided phone number that already exist in contacts
        //ask him if he wants to continue
        printf( "nWould you like to continue?(Y/N) " );
        fgets(&continue_Question, 3, stdin);
        if(continue_Question == 'Y' || continue_Question == 'y') {
            return checkConditions(fp);
        }
        else {
            exit(0);
        }
    }
    return phone_Number;
}
//add ordinals to the number so it'd be grammatically correct 
char* ordinals( int i ) {

    switch(i) {
        case 1:
            return "st";
            break;
        case 2:
            return "nd";
            break;
        case 3:
            return "rd";
            break;
        default:
            return "th";
            break;
    }
}

//generate unique id for each contact
char* generateID() {
    srand(time(NULL));
    char* str_id = malloc(4 * sizeof(char));
    if( str_id == NULL ) {
        puts( "Memory allocation failed - generateID" );
        exit( EXIT_FAILURE );
    }
    sprintf(str_id, "%d", (rand() % (99999 - 10000 + 1)) + 10000);
    return str_id;
}

//files handling functions

//needed to look for duplicates
FILE *checkInAllContacts() {
    
    FILE *fp;
    fp = fopen( "All_Contacts.txt", "r");
    if( fp == NULL ) {
        printf( "nCould not open filen" );
        exit( EXIT_FAILURE );
    }
    return fp;
}

//add new contact
void addNewContact( char* path, FILE *allContacts ) {
        
    FILE *fp;
    fp = fopen(path, "w");
    if(fp == NULL) {
        printf( "nUnable to create filen" );
        exit( EXIT_FAILURE );
    }
    //get user input
    char* f_Name; 
    char* surname;
    char* ph_Number;
    //allocate memory
    f_Name = (char*) malloc(MAX_FNAME_LENGTH * sizeof(char));
    surname = (char*) malloc(MAX_SNAME_LENGTH * sizeof(char));
    ph_Number = malloc(MAX_PHONENUMBER_LENGTH * sizeof(ph_Number));
    //check allocation
    if(f_Name == NULL || surname == NULL || ph_Number == NULL) {
        puts( "Memory allocation failed - addNewContact" );
        exit( EXIT_FAILURE );
    }
    //assign functions to variables
    f_Name = inputFirstName();
    surname = inputSurname();
    ph_Number = checkConditions(allContacts);
    printf( "nGenerated id: %sn", path );
    
    //add them to file
    fprintf( fp, "nFirst name: %s", f_Name );
    fprintf( fp, "nSurname: %s", surname );
    fprintf( fp, "nPhone number: %s", ph_Number );
    fprintf( fp, "nid: %sn", path );

    //free memory and close file
    free(f_Name);
    free(surname);
    free(ph_Number);
    fclose(fp);
}

void addToAllContacts( char* path ) {
    
    FILE *fpS, *fpD;
    //buffer
    char putIn;
    
    fpS = fopen(path, "r");
    fpD = fopen("All_Contacts.txt", "a");

    if( fpS == NULL || fpD == NULL ) {
        puts( "File not found - appending" );
        exit( EXIT_FAILURE );
    }
    
    //add line to make it more readable
    fprintf(fpD, "n----------------------------------------n");
    
    //get text from source
    while ( (putIn = fgetc( fpS )) != EOF ) {
        //put it to destination file
        fputc(putIn, fpD);
    }
    fclose(fpS);
    fclose(fpD);
}

void showContactBasedOnPath( char* path ) {
    
    FILE* fp;
    char char_from_file;

    fp = fopen(path, "r");
    //check if exist
    if( !fp ) {
        puts( "n**File not found**n" );
        return;
    }

    while( (char_from_file = fgetc(fp)) != EOF ) {
        printf(ANSI_COLOR_CYAN   "%c"   ANSI_COLOR_RESET , char_from_file );
    }
    fclose(fp);
}

Probably some functions could be scaled into one, but tbh structure of this code is quite easy to get with, so that’s why I did not change it. Also I’m aware of fact that if memory won’t be allocated or file would be NULL then program is gonna exit-crush. I’ll change it later.