2
\$\begingroup\$

I noticed that I have a lot of duplication in my codes, but I really don't know how to solve duplication that's why I decided to seek for advice and help from Stack Overflow. I also notice that I have been using more than 1 loop in more cases so I wonder can you all check on my following code snippet and tell me how do I reduce the amount of loop usage and remove the duplication.

My code is a bit long so I decide to put it in an online compiler so that you guys can see it clearly, I'm also a bit sorry for asking this ridiculous question, but I really wanted to know how can I reduce the number of lines and memory usage in my program, I'm pretty bad at this..

The following code snippet is basically a program that captures the record about sanitizing a venue; the data will be written in a binary file.

Infection Level Check

This function is mainly for prompting the user and collecting the response from user. It is for tracking their level of infection based on their response. I only allow the user to answer yes or no which is a method of validation I believe.

Table Header / Footer

This is just for printing the table header and footer which will be used in most of the functions. I decided to make it a function to reduce duplication.

Sanitize Menu

This is basically the main menu of this program.

It allows user to navigate to any other feature that he or she wishes to use.

I use a nested dowhile for doing validation. I feel like this is not the best way of doing it, but I really don't know how can I improve it.

Add Record

It is mainly for adding a new sanitization record. It prompts and asks the user a few things and all the data will be written in a binary file call Sanitization Record.dat. I did some validation for date and time, but I feel like it is a little bit too long. I wonder if I can reduce the amount of lines for the validation?

Search Record

This is just a menu for searching a record based on their ID, the company name and etc. Yes, I did use a nested while loop in here for doing validation, which I feel is definitely not the right way of doing it. Maybe you guys can give me any suggestions on it?

The Rest of the Search Function

All of these are the same, just that the searching method is different. If it's a string, I will be using strcmp, else I will just compare it with a normal if condition. Once the record is found in the binary file, it will print out the record for the user to see.

Modify Function

It allows the user to modify everything inside the binary file except the venue ID. It will prompt and ask the user to enter the venue id he wish to modify, and it will print out the relevant detail to that venue ID and ask for confirmation. If the user answers "Y", it will allow the user to modify those records. Once modified, all the records will be rewritten into the binary file.

Delete Function

It allows the user to delete any record inside the binary file. It will first read the binary file and capture all the data into an array structure, then it will ask the user to enter the venue ID for deleting the record. If a record is found, it will prompt and ask whether the user wants to delete; if the user answers "Y" the record will be deleted through array shifting method. Then everything will be rewritten into the binary file.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#pragma warning (disable:4996)
#define MAX 20
typedef struct {
    char companyName[41], personInCharge[41], gender, contactNum[13];
    float temperature;
    int infectionLevel;
}Company;

typedef struct {
    int hours, minute;
}Time;

typedef struct {
    int day, month, year;
}Date;

struct SanitizeData {
    char venueID[10];
    Company Details;
    Time Start;
    Time End;
    Date sanitizeDate;
};

// Function Prototype Declaration
int infectionLevelCheck();
void tableHeader();
void tableFooter(int, int);
void sanitizeMenu();
void addRecord();
void searchRecord();
void searchVenueID();
void searchDate();
void searchCompany();
void searchName();
void searchContactNumber();
void searchGender();
void searchTemperature();
void modifyRecord();
void displayRecord();
void deleteRecord();

int infectionLevelCheck() {
    char userResponse;
    int infectionLevel;
    int invalidCounter;

    do {
        invalidCounter = 0;
        infectionLevel = 0;
        printf("\n\nRisk Of Infection Check: \n");
        printf("-----------------------\n");
        printf("Are you having the 2 or more of the following symptoms listed?\n");
        printf("- Fever\n");
        printf("- Sore Throat\n");
        printf("- Runny Nose\n");
        printf("- Diarrhea\n");
        printf("- Headache\n");
        printf("- Body Ache\n");
        printf("(Y = Yes, N = No): ");
        scanf("%c", &userResponse);
        rewind(stdin);

        if (toupper(userResponse) != 'Y' && toupper(userResponse) != 'N') {
            invalidCounter++;
        }
        else if (toupper(userResponse) == 'Y') {
            infectionLevel++;
        }

        printf("\n\nBesides the Symptoms listed above, are you having the following symptoms: \n");
        printf("- Cough\n");
        printf("- Difficulty breathing\n");
        printf("- Loss of smell\n");
        printf("- Loss of taste\n");
        printf("(Y = Yes, N = No): ");
        scanf("%c", &userResponse);
        rewind(stdin);

        if (toupper(userResponse) != 'Y' && toupper(userResponse) != 'N') {
            invalidCounter++;
        }
        else if (toupper(userResponse) == 'Y') {
            infectionLevel += 4;
        }

        printf("\n\nHave you travelled to any country outside Malaysia\nwithin the past 14 days?\n");
        printf("(Y = Yes, N = No): ");
        scanf("%c", &userResponse);
        rewind(stdin);

        if (toupper(userResponse) != 'Y' && toupper(userResponse) != 'N') {
            invalidCounter++;
        }

        else if (toupper(userResponse) == 'Y') {
            infectionLevel += 2;
        }

        printf("\n\nHave you had closed contact with anyone who \nconfirmed or suspected case of COVID 19 within the 14 days?");
        printf("(Y = Yes, N = No): ");
        scanf("%c", &userResponse);
        rewind(stdin);

        if (toupper(userResponse) != 'Y' && toupper(userResponse) != 'N') {
            invalidCounter++;
        }

        else if (toupper(userResponse) == 'Y') {
            infectionLevel += 4;
        }

        if (invalidCounter > 0) {
            printf("\n- Error Found: Invalid Response Entered - \n\n");
        }

    } while (invalidCounter != 0);
    return infectionLevel;
}

void tableHeader() {
    printf("------------------------------------------------------------------------------------------------------------------------\n");
    printf("| Venue |   Sanitization   |  Ending  |    Company    |    Person   | Gender |    Contact   | Temperature | Infection  |\n");
    printf("|  ID   |       Time       |   Time   |   In-Charge   |  In-Charge  |        |              |             |   Risk     |\n");
    printf("|-------|------------------|----------|---------------|-------------|--------|--------------|-------------|------------|\n");
}

void tableFooter(int i, int emptyRecord) {
    printf("------------------------------------------------------------------------------------------------------------------------\n");
    if (emptyRecord == MAX) {
        printf("No Such Record Found\n");
    }
    else {
        printf("%d Record Found\n", i);
    }
}

void addRecord() {
    struct SanitizeData newData;
    FILE* addPtr;
    char userResponse;
    int validate, invalidCounter, counter = 0, infectionlevel = 0;

    addPtr = fopen("Sanitization Record.dat", "ab");
    if (addPtr == NULL) {
        printf("Unable to open Sanitization Record.dat\n");
        exit(-1);
    }
    else {

        do {
            system("cls");
            do {
                invalidCounter = 0;
                printf("Add Sanitization Record\n");
                printf("-----------------------\n");
                printf("Enter Venue ID: ");
                scanf("%s", &newData.venueID);
                rewind(stdin);

                printf("Enter Sanitized Date in [DD/MM/YYYY Format Eg: 19/02/2021] : ");
                validate = scanf("%d/%d/%d", &newData.sanitizeDate.day, &newData.sanitizeDate.month, &newData.sanitizeDate.year);
                rewind(stdin);

                printf("Enter Sanitized Time in [HH:MM Format Eg: 12:51] - \n");
                printf("Starting: ");
                validate = scanf("%d:%d", &newData.Start.hours, &newData.Start.minute);
                rewind(stdin);

                printf("Ending: ");
                validate = scanf("%d:%d", &newData.End.hours, &newData.End.minute);
                rewind(stdin);

                printf("Enter Santizie Handling Company Details: \n");
                printf("Company Name: ");
                scanf("%[^\n]", &newData.Details.companyName);
                rewind(stdin);

                printf("Person In-Charge Name:  ");
                scanf("%[^\n]", &newData.Details.personInCharge);
                rewind(stdin);

                printf("Person In-Charge Gender: ");
                scanf("%c", &newData.Details.gender);
                rewind(stdin);

                printf("Person In-Charge Contact Numer: ");
                scanf("%s", &newData.Details.contactNum);
                rewind(stdin);

                printf("Person In-Charge Temperature: ");
                validate = scanf("%f", &newData.Details.temperature);
                rewind(stdin);

                newData.Details.infectionLevel = infectionLevelCheck();

                if (newData.Details.temperature > 37.8) newData.Details.infectionLevel++;


                if (validate == 0
                    || newData.sanitizeDate.day < 1 || newData.sanitizeDate.day > 31
                    || newData.sanitizeDate.month > 12 || newData.sanitizeDate.month < 1
                    || newData.Start.hours > 24 || newData.Start.hours < 1
                    || newData.Start.minute > 60 || newData.Start.minute < 1
                    || newData.End.hours > 24 || newData.End.hours < 1
                    || newData.End.minute > 60 || newData.End.minute < 1
                    || toupper(newData.Details.gender) != 'M' && toupper(newData.Details.gender) != 'F')
                {
                    if (newData.sanitizeDate.day < 1 || newData.sanitizeDate.day > 31 || newData.sanitizeDate.month > 12 || newData.sanitizeDate.month < 1) {
                        printf("- Error Found: Invalid Month or Days Entered. -\n");
                    }
                    if (newData.Start.hours > 24 || newData.Start.hours < 1 || newData.Start.minute > 60 || newData.Start.minute < 1) {
                        printf("- Error Found: Invalid Starting Hours or Minutes Entered. - \n");
                    }

                    if (newData.End.hours > 24 || newData.End.hours < 1 || newData.End.minute > 60 || newData.End.minute < 1) {
                        printf("- Error Found: Invalid Ending Hours or Minutes Entered. - \n");
                    }

                    if (toupper(newData.Details.gender) != 'M' || toupper(newData.Details.gender) != 'F') {
                        printf("- Error Found: Invalid Gender Entered. -\n");
                    }

                    if (validate == 0) {
                        printf("- Error Found: Kindly Enter Valid Input Only. - \n");
                    }

                    invalidCounter++;
                    system("pause");
                    system("cls");
                }
                else {
                    fwrite(&newData, sizeof(newData), 1, addPtr);
                    counter++;
                }
            } while (invalidCounter != 0);

            printf("Add Another Record? (N = No): ");
            scanf("%c", &userResponse);
            rewind(stdin);
        } while (toupper(userResponse) != 'N');
        printf("%d Record Added.....\n", counter);
        fclose(addPtr);
    }
}

void searchVenueID() {
    struct SanitizeData data[20];
    FILE* searchPtr;
    int i = 0, emptyRecord = 0;
    int counter = 0;
    char venueID[10];

    searchPtr = fopen("Sanitization Record.dat", "rb");
    if (searchPtr == NULL) {
        printf("Unable to open Santization Record.dat\n");
        exit(-1);
    }

    while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {
        i++;
    }

    printf("Enter Venue ID to Search: ");
    scanf("%s", &venueID);

    tableHeader();
    for (int i = 0; i < 20; i++) {
        if (strcmp(venueID, data[i].venueID) == 0) {
            counter++;
            printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data[i].venueID, data[i].sanitizeDate.day, data[i].sanitizeDate.month, data[i].sanitizeDate.year,
                data[i].Start.hours, data[i].Start.minute, data[i].End.hours, data[i].End.minute, data[i].Details.companyName,
                data[i].Details.personInCharge);

            switch (toupper(data[i].Details.gender)) {
            case 'F':
                printf("Female ");
                break;
            case 'M':
                printf(" Male  ");
                break;
            }

            printf("| %-12s |    %.2f    | ", data[i].Details.contactNum, data[i].Details.temperature);

            if (data[i].Details.infectionLevel <= 1) {
                printf(" Low Risk  |\n");
            }
            else if (data[i].Details.infectionLevel <= 3) {
                printf(" Mid Risk  |\n");
            }
            else if (data[i].Details.infectionLevel >= 4) {
                printf(" High Risk |\n");
            }
        }
        else {
            emptyRecord++;
        }
    }
    tableFooter(counter, emptyRecord);
    fclose(searchPtr);
}

void searchDate() {
    struct SanitizeData data[20];
    FILE* searchPtr;
    int i = 0, emptyRecord = 0;
    int counter = 0;
    int day, month, year;
    searchPtr = fopen("Sanitization Record.dat", "rb");
    if (searchPtr == NULL) {
        printf("Unable to open Santization Record.dat\n");
        exit(-1);
    }

    while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {
        i++;
    }

    printf("Enter Date to Search [Format: 19/02/2021] : ");
    scanf("%d/%d/%d", &day, &month, &year);

    tableHeader();
    for (i = 0; i < 20; i++) {
        if (day == data[i].sanitizeDate.day && month == data[i].sanitizeDate.month && year == data[i].sanitizeDate.year) {
            counter++;
            printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data[i].venueID, data[i].sanitizeDate.day, data[i].sanitizeDate.month, data[i].sanitizeDate.year,
                data[i].Start.hours, data[i].Start.minute, data[i].End.hours, data[i].End.minute, data[i].Details.companyName,
                data[i].Details.personInCharge);

            switch (toupper(data[i].Details.gender)) {
            case 'F':
                printf("Female ");
                break;
            case 'M':
                printf(" Male  ");
                break;
            }

            printf("| %-12s |    %.2f    | ", data[i].Details.contactNum, data[i].Details.temperature);

            if (data[i].Details.infectionLevel <= 1) {
                printf(" Low Risk  |\n");
            }
            else if (data[i].Details.infectionLevel <= 3) {
                printf(" Mid Risk  |\n");
            }
            else if (data[i].Details.infectionLevel >= 4) {
                printf(" High Risk |\n");
            }
        }
        else {
            emptyRecord++;
        }
    }
    tableFooter(counter, emptyRecord);
    fclose(searchPtr);
}

void searchCompany() {
    struct SanitizeData data[20];
    FILE* searchPtr;
    int i = 0, emptyRecord = 0;
    char companyName[41];
    int counter = 0;
    searchPtr = fopen("Sanitization Record.dat", "rb");
    if (searchPtr == NULL) {
        printf("Unable to open Santization Record.dat\n");
        exit(-1);
    }

    while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {
        i++;
    }

    printf("Enter Company Name to Search: ");
    scanf("%[^\n]", &companyName);

    tableHeader();
    for (i = 0; i < 20; i++) {
        if (strcmp(companyName, data[i].Details.companyName) == 0) {
            counter++;
            printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data[i].venueID, data[i].sanitizeDate.day, data[i].sanitizeDate.month, data[i].sanitizeDate.year,
                data[i].Start.hours, data[i].Start.minute, data[i].End.hours, data[i].End.minute, data[i].Details.companyName,
                data[i].Details.personInCharge);

            switch (toupper(data[i].Details.gender)) {
            case 'F':
                printf("Female ");
                break;
            case 'M':
                printf(" Male  ");
                break;
            }

            printf("| %-12s |    %.2f    | ", data[i].Details.contactNum, data[i].Details.temperature);

            if (data[i].Details.infectionLevel <= 1) {
                printf(" Low Risk  |\n");
            }
            else if (data[i].Details.infectionLevel <= 3) {
                printf(" Mid Risk  |\n");
            }
            else if (data[i].Details.infectionLevel >= 4) {
                printf(" High Risk |\n");
            }
        }
        else {
            emptyRecord++;
        }
    }
    tableFooter(counter, emptyRecord);
    fclose(searchPtr);
}

void searchName() {
    struct SanitizeData data[20];
    FILE* searchPtr;
    int i = 0, emptyRecord = 0;
    char personInCharge[41];
    int counter = 0;
    searchPtr = fopen("Sanitization Record.dat", "rb");
    if (searchPtr == NULL) {
        printf("Unable to open Santization Record.dat\n");
        exit(-1);
    }

    while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {
        i++;
    }

    printf("Enter Person In-Charge Name to Search: ");
    scanf("%[^\n]", &personInCharge);

    tableHeader();
    for (i = 0; i < 20; i++) {
        if (strcmp(personInCharge, data[i].Details.personInCharge) == 0) {
            counter++;
            printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data[i].venueID, data[i].sanitizeDate.day, data[i].sanitizeDate.month, data[i].sanitizeDate.year,
                data[i].Start.hours, data[i].Start.minute, data[i].End.hours, data[i].End.minute, data[i].Details.companyName,
                data[i].Details.personInCharge);

            switch (toupper(data[i].Details.gender)) {
            case 'F':
                printf("Female ");
                break;
            case 'M':
                printf(" Male  ");
                break;
            }

            printf("| %-12s |    %.2f    | ", data[i].Details.contactNum, data[i].Details.temperature);

            if (data[i].Details.infectionLevel <= 1) {
                printf(" Low Risk  |\n");
            }
            else if (data[i].Details.infectionLevel <= 3) {
                printf(" Mid Risk  |\n");
            }
            else if (data[i].Details.infectionLevel >= 4) {
                printf(" High Risk |\n");
            }
        }
        else {
            emptyRecord++;
        }
    }
    tableFooter(counter, emptyRecord);
    fclose(searchPtr);
}

void searchContactNumber() {
    struct SanitizeData data[20];
    FILE* searchPtr;
    int i = 0, emptyRecord = 0;
    char contactNum[13];
    int counter = 0;
    searchPtr = fopen("Sanitization Record.dat", "rb");
    if (searchPtr == NULL) {
        printf("Unable to open Santization Record.dat\n");
        exit(-1);
    }

    while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {
        i++;
    }

    printf("Enter Contact Number to Search [Format: 010-2012687]: ");
    scanf("%s", &contactNum);

    tableHeader();
    for (i = 0; i < 20; i++) {
        if (strcmp(contactNum, data[i].Details.contactNum) == 0) {
            counter++;
            printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data[i].venueID, data[i].sanitizeDate.day, data[i].sanitizeDate.month, data[i].sanitizeDate.year,
                data[i].Start.hours, data[i].Start.minute, data[i].End.hours, data[i].End.minute, data[i].Details.companyName,
                data[i].Details.personInCharge);

            switch (toupper(data[i].Details.gender)) {
            case 'F':
                printf("Female ");
                break;
            case 'M':
                printf(" Male  ");
                break;
            }

            printf("| %-12s |    %.2f    | ", data[i].Details.contactNum, data[i].Details.temperature);

            if (data[i].Details.infectionLevel <= 1) {
                printf(" Low Risk  |\n");
            }
            else if (data[i].Details.infectionLevel <= 3) {
                printf(" Mid Risk  |\n");
            }
            else if (data[i].Details.infectionLevel >= 4) {
                printf(" High Risk |\n");
            }
        }
        else {
            emptyRecord++;
        }
    }
    tableFooter(counter, emptyRecord);
    fclose(searchPtr);
}

void searchGender() {
    struct SanitizeData data[20];
    FILE* searchPtr;
    int i = 0, emptyRecord = 0;
    char gender;
    int counter = 0;
    searchPtr = fopen("Sanitization Record.dat", "rb");
    if (searchPtr == NULL) {
        printf("Unable to open Santization Record.dat\n");
        exit(-1);
    }

    while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {
        i++;
    }

    printf("Enter Person In-Charge Gender to Search [F = Female, M = Male]: ");
    scanf("%c", &gender);

    tableHeader();
    for (i = 0; i < 20; i++) {
        if (gender == data[i].Details.gender) {
            counter++;
            printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data[i].venueID, data[i].sanitizeDate.day, data[i].sanitizeDate.month, data[i].sanitizeDate.year,
                data[i].Start.hours, data[i].Start.minute, data[i].End.hours, data[i].End.minute, data[i].Details.companyName,
                data[i].Details.personInCharge);

            switch (toupper(data[i].Details.gender)) {
            case 'F':
                printf("Female ");
                break;
            case 'M':
                printf(" Male  ");
                break;
            }

            printf("| %-12s |    %.2f    | ", data[i].Details.contactNum, data[i].Details.temperature);

            if (data[i].Details.infectionLevel <= 1) {
                printf(" Low Risk  |\n");
            }
            else if (data[i].Details.infectionLevel <= 3) {
                printf(" Mid Risk  |\n");
            }
            else if (data[i].Details.infectionLevel >= 4) {
                printf(" High Risk |\n");
            }
        }
        else {
            emptyRecord++;
        }
    }
    tableFooter(counter, emptyRecord);
    fclose(searchPtr);
}

void searchTemperature() {
    struct SanitizeData data[20];
    FILE* searchPtr;
    int i = 0, emptyRecord = 0;
    float temperature;
    int counter = 0;
    searchPtr = fopen("Sanitization Record.dat", "rb");
    if (searchPtr == NULL) {
        printf("Unable to open Santization Record.dat\n");
        exit(-1);
    }

    while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {
        i++;
    }

    printf("Enter Temperature To Search [Eg: 36.6]: ");
    scanf("%f", &temperature);

    tableHeader();
    for (i = 0; i < 20; i++) {
        if (temperature == data[i].Details.temperature) {
            counter++;
            printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data[i].venueID, data[i].sanitizeDate.day, data[i].sanitizeDate.month, data[i].sanitizeDate.year,
                data[i].Start.hours, data[i].Start.minute, data[i].End.hours, data[i].End.minute, data[i].Details.companyName,
                data[i].Details.personInCharge);

            switch (toupper(data[i].Details.gender)) {
            case 'F':
                printf("Female ");
                break;
            case 'M':
                printf(" Male  ");
                break;
            }

            printf("| %-12s |    %.2f    | ", data[i].Details.contactNum, data[i].Details.temperature);

            if (data[i].Details.infectionLevel <= 1) {
                printf(" Low Risk  |\n");
            }
            else if (data[i].Details.infectionLevel <= 3) {
                printf(" Mid Risk  |\n");
            }
            else if (data[i].Details.infectionLevel >= 4) {
                printf(" High Risk |\n");
            }
        }
        else {
            emptyRecord++;
        }
    }
    tableFooter(counter, emptyRecord);
    fclose(searchPtr);
}

void searchRecord() {
    int selection, invalidCounter;
    char userResponse;
    do {
        system("cls");
        do {
            invalidCounter = 0;
            printf("Search Record Based On Following Criteria: \n");
            printf("1. Venue ID\n");
            printf("2. Sanitization Date\n");
            printf("3. Company Name\n");
            printf("4. Person In-Charge\n");
            printf("5. Contact Number\n");
            printf("6. Gender\n");
            printf("7. Temperature\n");
            printf("8. Back to Sanitize Menu\n");
            printf("Enter Your Choice > ");
            scanf("%d", &selection);
            if (selection < 1 || selection > 8) {
                invalidCounter++;
                printf("- Error Found: Enter 1 - 8 ONLY - \n");
                system("pause");
                system("cls");
            }
        } while (invalidCounter != 0);
        switch (selection) {
        case 1:
            searchVenueID();
            break;
        case 2:
            searchDate();
            break;
        case 3:
            searchCompany();
            break;
        case 4:
            searchName();
            break;
        case 5:
            searchContactNumber();
            break;
        case 6:
            searchGender();
            break;
        case 7:
            searchTemperature();
        case 8:
            sanitizeMenu();
            break;
        }
        printf("Anymore to Search? (N = No): ");
        scanf("%c", &userResponse);
        rewind(stdin);

    } while (toupper(userResponse) != 'N');
}

void modifyRecord() {
    struct SanitizeData data;
    struct SanitizeData modify[20];
    FILE* modifyPtr;
    int i = 0, counter = 0, validate, invalidCounter, emptyRecord = 0, modifyCounter = 0;
    char venueID[10], userResponse;
    modifyPtr = fopen("Sanitization Record.dat", "rb");
    if (modifyPtr == NULL) {
        printf("Unable to open Sanitization Record.dat\n");
        exit(-1);
    }

    while (fread(&data, sizeof(data), 1, modifyPtr) != 0) {
        modify[modifyCounter] = data;
        modifyCounter++;
    }

    modifyPtr = fopen("Sanitization Record.dat", "wb");
    if (modifyPtr == NULL) {
        printf("Unable to open Sanitization Record.dat\n");
        exit(-1);
    }
    do {
        system("cls");
        printf("Modify Sanitize Record\n");
        printf("----------------------\n\n");
        printf("Enter Venue ID To Modify: ");
        scanf("%s", &venueID);
        rewind(stdin);
        for (i = 0; i < modifyCounter; i++) {
            if (strcmp(venueID, modify[i].venueID) == 0) {

                printf("Venue ID                       : %s\n", modify[i].venueID);
                printf("Sanitization Date              : %02d-%02d-%4d\n", modify[i].sanitizeDate.day, modify[i].sanitizeDate.month, modify[i].sanitizeDate.year);
                printf("Starting Time                  : %02d:%02d\n", modify[i].Start.hours, modify[i].Start.minute);
                printf("Ending Time                    : %02d:%02d\n", modify[i].End.hours, modify[i].End.minute);
                printf("Sanitized Company Name         : %s\n", modify[i].Details.companyName);
                printf("Person In-Charge  Name         : %s\n", modify[i].Details.personInCharge);
                printf("Person In-Charge Gender        : %c\n", modify[i].Details.gender);
                printf("Person In-Charge Contact Number: %s\n", modify[i].Details.contactNum);
                printf("Person In-Charge Temperature   : %.2f\n\n", modify[i].Details.temperature);
                printf("Sure to Modify? (Y = Yes): ");
                scanf("%c", &userResponse);
                if (toupper(userResponse) == 'Y') {
                    counter++;
                    do {
                        invalidCounter = 0;

                        printf("Enter Sanitized Date in [DD/MM/YYYY Format Eg: 19/02/2021] : ");
                        validate = scanf("%d/%d/%d", &modify[i].sanitizeDate.day, &modify[i].sanitizeDate.month, &modify[i].sanitizeDate.year);
                        rewind(stdin);

                        printf("Enter Sanitized Time in [HH:MM Format Eg: 12:51] - \n");
                        printf("Starting: ");
                        validate = scanf("%d:%d", &modify[i].Start.hours, &modify[i].Start.minute);
                        rewind(stdin);

                        printf("Ending: ");
                        validate = scanf("%d:%d", &modify[i].End.hours, &modify[i].End.minute);
                        rewind(stdin);

                        printf("Enter Santizie Handling Company Details: \n");
                        printf("Company Name: ");
                        scanf("%[^\n]", &modify[i].Details.companyName);
                        rewind(stdin);

                        printf("Person In-Charge Name:  ");
                        scanf("%[^\n]", &modify[i].Details.personInCharge);
                        rewind(stdin);

                        printf("Person In-Charge Gender: ");
                        scanf("%c", &modify[i].Details.gender);
                        rewind(stdin);

                        printf("Person In-Charge Contact Numer: ");
                        scanf("%s", &modify[i].Details.contactNum);
                        rewind(stdin);

                        printf("Person In-Charge Temperature: ");
                        validate = scanf("%f", &modify[i].Details.temperature);
                        rewind(stdin);

                        modify[i].Details.infectionLevel = infectionLevelCheck();

                        if (modify[i].Details.temperature > 37.8) modify[i].Details.infectionLevel++;


                        if (validate == 0
                            || modify[i].sanitizeDate.day < 1 || modify[i].sanitizeDate.day > 31
                            || modify[i].sanitizeDate.month > 12 || modify[i].sanitizeDate.month < 1
                            || modify[i].Start.hours > 24 || modify[i].Start.hours < 1
                            || modify[i].Start.minute > 60 || modify[i].Start.minute < 1
                            || modify[i].End.hours > 24 || modify[i].End.hours < 1
                            || modify[i].End.minute > 60 || modify[i].End.minute < 1
                            || toupper(modify[i].Details.gender) != 'M' && toupper(modify[i].Details.gender) != 'F')
                        {
                            if (modify[i].sanitizeDate.day < 1 || modify[i].sanitizeDate.day > 31 || modify[i].sanitizeDate.month > 12 || modify[i].sanitizeDate.month < 1) {
                                printf("- Error Found: Invalid Month or Days Entered. -\n");
                            }
                            if (modify[i].Start.hours > 24 || modify[i].Start.hours < 1 || modify[i].Start.minute > 60 || modify[i].Start.minute < 1) {
                                printf("- Error Found: Invalid Starting Hours or Minutes Entered. - \n");
                            }

                            if (modify[i].End.hours > 24 || modify[i].End.hours < 1 || modify[i].End.minute > 60 || modify[i].End.minute < 1) {
                                printf("- Error Found: Invalid Ending Hours or Minutes Entered. - \n");
                            }

                            if (toupper(modify[i].Details.gender) != 'M' || toupper(modify[i].Details.gender) != 'F') {
                                printf("- Error Found: Invalid Gender Entered. -\n");
                            }

                            if (validate == 0) {
                                printf("- Error Found: Kindly Enter Valid Input Only. - \n");
                            }

                            invalidCounter++;
                            system("pause");
                            system("cls");
                        }

                    } while (invalidCounter != 0);

                }
                else {
                    break;
                }
                fwrite(&modify[i], sizeof(data), 1, modifyPtr);
            }
            else {
                emptyRecord++;
            }
        }
        if (emptyRecord == 20) {
            printf("No Record Found\n");
        }
        printf("Continue Modify Record? (N = No): ");
        scanf("%c", &userResponse);
    } while (userResponse != 'N');
    printf("%d Record Modified\n", counter);
    fclose(modifyPtr);
}

void displayRecord() {
    struct SanitizeData data;
    FILE* displayPtr;
    int i = 0;
    displayPtr = fopen("Sanitization Record.dat", "rb");

    if (displayPtr == NULL) {
        printf("Unable to open Sanitization Record.dat\n");
        exit(-1);
    }

    system("cls");

    printf("Display All Sanitization Record\n");
    printf("-------------------------------\n");
    tableHeader();
    while (fread(&data, sizeof(data), 1, displayPtr) != 0) {
        printf("| %-5s | %02d-%02d-%4d %02d:%02d |   %02d:%02d  | %-13s | %-11s | ", data.venueID, data.sanitizeDate.day, data.sanitizeDate.month, data.sanitizeDate.year,
            data.Start.hours, data.Start.minute, data.End.hours, data.End.minute, data.Details.companyName,
            data.Details.personInCharge);

        switch (toupper(data.Details.gender)) {
        case 'F':
            printf("Female ");
            break;
        case 'M':
            printf(" Male  ");
            break;
        }
        printf("| %-12s |    %.2f    | ", data.Details.contactNum, data.Details.temperature);
        if (data.Details.infectionLevel <= 1) {
            printf(" Low Risk  |\n");
        }
        else if (data.Details.infectionLevel <= 3) {
            printf(" Mid Risk  |\n");
        }
        else if (data.Details.infectionLevel >= 4) {
            printf(" High Risk |\n");
        }
        i++;
    }
    printf("------------------------------------------------------------------------------------------------------------------------\n");
    printf("%d Record Found\n", i);
    fclose(displayPtr);
}

void deleteRecord() {
    struct SanitizeData data;
    struct SanitizeData delData[20];
    FILE* deletePtr;

    int i = 0, delCount = 0, position, emptyRecord = 0;
    char userResponse;
    char venueID[10];

    deletePtr = fopen("Sanitization Record.dat", "rb");
    if (deletePtr == NULL) {
        printf("Unable to open Sanitization.dat\n");
        exit(-1);
    }
    else {
        while (fread(&data, sizeof(data), 1, deletePtr) != 0) {
            delData[i] = data;
            i++;
            delCount++;
        }
    }
    fclose(deletePtr);

    deletePtr = fopen("Sanitization Record.dat", "wb");
    if(deletePtr == NULL) {
        printf("Unable to open Sanitization.dat\n");
        exit(-1);
    }
    else {

        do {
            printf("Delete Record Based On: ");
            printf("1. Venue ID\n");
            printf("Enter Venue ID: ");
            scanf("%s", venueID);
            rewind(stdin);

            for (i = 0; i < delCount; i++) {
                if (strcmp(venueID, delData[i].venueID) == 0) {
                    printf("Venue ID                       : %s\n", delData[i].venueID);
                    printf("Sanitization Date              : %02d-%02d-%4d\n", delData[i].sanitizeDate.day, delData[i].sanitizeDate.month, delData[i].sanitizeDate.year);
                    printf("Starting Time                  : %02d:%02d\n", delData[i].Start.hours, delData[i].Start.minute);
                    printf("Ending Time                    : %02d:%02d\n", delData[i].End.hours, delData[i].End.minute);
                    printf("Sanitized Company Name         : %s\n", delData[i].Details.companyName);
                    printf("Person In-Charge  Name         : %s\n", delData[i].Details.personInCharge);
                    printf("Person In-Charge Gender        : %c\n", delData[i].Details.gender);
                    printf("Person In-Charge Contact Number: %s\n", delData[i].Details.contactNum);
                    printf("Person In-Charge Temperature   : %.2f\n\n", delData[i].Details.temperature);
                    position = i;
                    printf("Confirm to Delete? (Y = Yes): ");
                    scanf("%c", &userResponse);
                    rewind(stdin);
                    if (toupper(userResponse) == 'Y') {
                        for (i = position; i < delCount; i++) {
                            delData[i] = delData[i + 1];
                        }
                    }
                }
                else {
                    emptyRecord++;
                }
            }

            if (emptyRecord == 20) {
                printf("-Error Found: No Record - \n");
            }

            printf("Anymore to Delete? (Y = Yes): ");
            scanf("%c", &userResponse);
            rewind(stdin);

            if (toupper(userResponse) == 'N') {
                for (int i = 0; i < delCount--; i++) {
                    fwrite(&delData[i], sizeof(delData[i]), 1, deletePtr);
                }
            }

        } while (toupper(userResponse) != 'N');
    }
    fclose(deletePtr);
}

void sanitizeMenu() {
    int validate, invalidCounter, selection;
    char userResponse;
    do {
        system("cls");
        do {
            invalidCounter = 0;
            printf("     Sanitization Record Module\n");
            printf("-------------------------------------\n");
            printf("            MENU\n");
            printf("[1] - Add Sanitization Record\n");
            printf("[2] - Search Sanitization Record\n");
            printf("[3] - Modify Sanitization Record\n");
            printf("[4] - Display Sanitization Record\n");
            printf("[5] - Delete Sanitization Record\n");
            printf("[6] - Record \n");
            printf("[7] - Quit This Menu\n");
            printf("[8] - Exit The Program\n\n");
            printf("Enter Your Selection: ");
            validate = scanf("%d", &selection);
            rewind(stdin);
            if (selection < 1 || selection > 8 || validate == 0) {
                invalidCounter++;
                printf("- Error Found: Please Enter 1 - 7 Only -\n");
                system("pause");
                system("cls");
            }

        } while (invalidCounter != 0);

        switch (selection) {
        case 1:
            addRecord();
            break;
        case 2:
            searchRecord();
            break;
        case 3:
            modifyRecord();
            break;
        case 4:
            displayRecord();
            break;
        case 5:
            deleteRecord();
            break;
        case 6:
            menu();
            break;
        case 7:
            footer();
            exit(0);
        }

        printf("Back to Sanitize Menu? (N = No): ");
        rewind(stdin);
        scanf("%c", &userResponse);
        rewind(stdin);
    } while (toupper(userResponse) != 'N');
}
\$\endgroup\$
1
  • 3
    \$\begingroup\$ To anyone in the Close Vote Queue, this question now meets Code Review Standards. \$\endgroup\$
    – pacmaninbw
    Mar 6, 2021 at 17:44

2 Answers 2

4
\$\begingroup\$

General Observations

The function names are descriptive and this is good. The functions themselves are overly complex (do too much) and many of them can be broken up into smaller functions that do specific tasks. (See Error Checking below)

Two functions are not defined within the scope of the code but are called by the code, menu() and footer(). These are both called in sanitizeMenu(). This can make the question off-topic due to the Missing Code Review Context rule it can also make it harder to do a good review of the code.

It is not clear why the following pre-processor directive is included in the code since it doesn't seem to affect the warning messages from the gcc compiler:

#pragma warning (disable:4996)

If you are compiling this on Windows using Visual Studio be aware that Visual Studio doesn't necessarily follow the C standards, and therefore the code might not be portable to Linux systems or other platforms.

It is not clear why there is no typedef for the SanitizeData struct and the Company, Time and Date do have typedef, this seems inconsistent. Leave spaces between operators and symbol or type names, for example there should be a space after } for all the typedefs.

Don't use the rewind() function on stdin, there is no way to reset to the beginning of stdin since it is a special FILE pointer that you can't open or close. It is recommended to use fseek() rather than rewind() to return to the first record in a file. The function fseek() returns success or failure where the rewind() function does not. The fseek() function also will not work on stdin.

DRY Code or Reducing Code Repetition

There are several ways to reduce code repetition in this code, one of the best might be to write some utility functions that implement opening and closing files, this would be applying the Single Responsibility Principle. (DRY means Don't Repeat Yourself)

The Single Responsibility Principle states:

that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by that module, class or function.

Have functions specific to each type of data structure that does a certain thing such as reading or writing that data structure. For example for the struct Company :

  • getCompanyName()
  • getPersonInCharge()
  • getGender()
  • getContactNumber()

And for the struct SanitizeData :

  • getVenueID()
  • getCompanyDetails()
  • getDate()

A second method that would probably improve the performance of the program would be to open the file only once, read all they data into a linked list of SanitizeData structures, close the data file, make all necessary edits to the data and then write out the updated data to the file. Among other things this could allow the data to be sorted. This would improve performance because opening, closing, reading and writing data to files are very expensive operations that make system calls. This would also remove the necessity for opening and closing the file in each function.

Recommendations

Magic Numbers

There are Magic Numbers throughout the code, it might be better to create symbolic constants for them to make the code more readable and easier to maintain. These numbers may be used in many places and being able to change them by editing only one line makes maintenance easier.

Numeric constants in code are sometimes referred to as Magic Numbers, because there is no obvious meaning for them. There is a discussion of this on stackoverflow.

There is one symbolic constant (MAX) which is good, but there should be at lease 5 symbolic constants to improve maintainability. One symbolic constant that is clearly missing is VENUEID_SIZE since the number 10 is used in multiple places for this (SanitizeData structure and in the function searchVenueID(). The symbolic constant MAX is only used once, but it seems that many of the for loops could use it.

Error Checking

There is a distinct lack of error checking on system supplied functions such as scanf(). The function scanf() returns an integer value that indicates the number of characters read.

This code in infectionLevelCheck() can be made into a function that will force the user to input the correct response.

        printf("\n\nRisk Of Infection Check: \n");
        printf("-----------------------\n");
        printf("Are you having the 2 or more of the following symptoms listed?\n");
        printf("- Fever\n");
        printf("- Sore Throat\n");
        printf("- Runny Nose\n");
        printf("- Diarrhea\n");
        printf("- Headache\n");
        printf("- Body Ache\n");
        printf("(Y = Yes, N = No): ");
        scanf("%c", &userResponse);
        rewind(stdin);

        if (toupper(userResponse) != 'Y' && toupper(userResponse) != 'N') {
            invalidCounter++;
        }
        else if (toupper(userResponse) == 'Y') {
            infectionLevel++;
        }

Have a loop in the previous code that doesn't return until the response is Y or N. One of the benefits of this is that the variable invalidCounter may not be necessary in the function.

This code in the same function could also be moved to a smaller function that just returns the response:

        printf("\n\nBesides the Symptoms listed above, are you having the following symptoms: \n");
        printf("- Cough\n");
        printf("- Difficulty breathing\n");
        printf("- Loss of smell\n");
        printf("- Loss of taste\n");
        printf("(Y = Yes, N = No): ");
        scanf("%c", &userResponse);
        rewind(stdin);

        if (toupper(userResponse) != 'Y' && toupper(userResponse) != 'N') {
            invalidCounter++;
        }
        else if (toupper(userResponse) == 'Y') {
            infectionLevel += 4;
        }

Initialize Variables When They are Declared

The code in infectionLevelCheck() contains these variable declarations:

    char userResponse;
    int infectionLevel;
    int invalidCounter;

This allows the variables to be used without being initialized which can cause undefined behavior (bugs). Always initialize variables when you declare them, the C programming language does not assign a default value to variables when they are declared.

\$\endgroup\$
3
  • \$\begingroup\$ Hi Packmainbw, Thank for your time in reviewing my code. The reason that i decides to put the pragma warning is that I'm doing it on the visual studio, whenever i use scanf it will prompt and tell me that scanf is not safe therefore I'm using it to bypass those warnings. It was definitely my fault for not putting the entire code here, I totally forgot that I have those functions in another .c file, I'm so sorry for not pasting the entire code here. I have read all your suggestion and definitely, i will make good use of it by turning all the capturing user's input into multiple functions. \$\endgroup\$
    – Ah Kang
    Mar 7, 2021 at 4:20
  • \$\begingroup\$ So that the function can focus on doing one thing at a time and duplication can be removed. Thanks for telling me that I should always initialized my variable whenever they are declared, I never knew that I need to initialize them and I always encountered some bug due to variable not initialized @_@. \$\endgroup\$
    – Ah Kang
    Mar 7, 2021 at 4:22
  • \$\begingroup\$ Oh ya also, the reason i wanna use rewind(stdin) is just to clear the input buffer of the enter key so that the user can keep on continue entering the response, but I've seen both of the answer from two, do i like replace scanf with something else like gets / getchar something like this so that i don't have to use rewind(stdin) anymore? \$\endgroup\$
    – Ah Kang
    Mar 7, 2021 at 8:43
3
\$\begingroup\$

You have not given us all the code. This makes it hard to try out the program, and it makes it hard to check the online compiler link you helpfully provided.

(It's not a requirement to provide all the code -- reviews don't depend on it. But it seems like you provided 90%, so why not go the extra 10%?)

Eliminate all the compiler warnings!

You provided a link to an online compiler service that uses GCC for its compilation backend. But this code appears to be aimed at Windows, and at Microsoft's compiler in particular. You might do well to use a Microsoft-oriented online compiler service.

Regardless, though, the very first rule for junior C programmers is this one: ELIMINATE ALL THE COMPILER WARNINGS!

Right now, the compiler is smarter than you are. If it prints a warning, no matter how incomprehensible the warning message is, there's a reason. Figure out what it is telling you, and take whatever steps are required to get rid of the warning.

You can post small snippets, plus the warning, in questions on Stack Overflow or Reddit or whatever-website, and you'll probably get a solid answer in less than a day. (In many cases, less than an hour.)

Improve your layout with vertical spacing

Identify "sections" or "paragraphs" and use vertical spacing (aka: blank lines) to separate them from each other. Consider:

#include <stdio.h>                                                               
#include <stdlib.h>                                                              
#include <ctype.h>                                                               
#include <string.h>                                                              
#pragma warning (disable:4996)                                                   
#define MAX 20                                                                   
typedef struct {                                                                 
    char companyName[41], personInCharge[41], gender, contactNum[13];            
    float temperature;                                                           
    int infectionLevel;                                                          
}Company;                                                                        

This would look better, and be slightly more readable, with a few extra newlines:

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

#pragma warning (disable:4996)                                                   

#define MAX 20                                                                   

typedef struct {                                                                 
    char companyName[41], personInCharge[41], gender, contactNum[13];            
    float temperature;                                                           
    int infectionLevel;                                                          
}Company;                                                                        

Adopt a coding standard

Your naming and code style is very unpredictable. Find a coding standard that suits you, and adopt it. Be aware that once you've adopted this standard, you will become a dogmatic, unflexible jerkwad, advocating your particular style in preference to all other styles, and you'll likely spend the next 10 years trying to convince all your peers and various strangers you meet on the bus or in airports that your coding style is the right one. So it pays to pick one that has a sensible rationale attached to it.

Regardless, find a style you like, adopt it, and rewrite all your code in that style.

Style: one declarator per declarator-list

Yes, I know that C supports multiple declarator names separated by commas.

But I'm not aware of any coding standard that encourages this, nor am I aware of any experienced coder who thinks it's a good style.

Rewrite code like this:

typedef struct {                                                              
    int day, month, year;                                                     
}Date;                                                                        

to look like this:

typedef struct {
    int day;
    int month;
    int year;
} Date;

No 'magic' numbers!

Magic numbers, or magic values (in the case of non-numerics) are so universally reviled that there is not even the usual wishy-washy "Discussion" associated with them on Wikipedia. Everybody agrees that magic values are bad. Just don't do it!

Train yourself to replace all magic values with constants, before you even write them the first time!

The only two numeric digits that should appear anywhere in your code other than as character/string literals, constant definitions, or as part of an identifier, are 0 and 1.

Replace this:

typedef struct {                                                              
    char companyName[41], personInCharge[41], gender, contactNum[13];         

with something like this:

typedef struct {
    char companyName[MAX_NAME_LEN + 1];
    char personInCharge[MAX_NAME_LEN + 1];
    char gender;
    char contactNum[MAX_CONTACT_LEN + 1];

Note: There are two lengths when dealing with C strings: the length of the string as reported by strlen() and the size of the buffer you will need to store a string. Because of the trailing NUL delimiter, these two numbers differ by 1: to store a string with strlen of 0 requires a buffer with 1 character: [ '\0' ]

As a result of this, you are faced with declaring constants that are 1-too-big or 1-too-small. If your constants are the string's max length, they are 1-too-small for buffer size. If they are the buffer size, then they are 1-too-big for string length. It is my opinion (this is one of those dogmatic coding-standard things) that using _LEN + 1 is the better way because:

  • this is what you have to do when dynamically allocating strings using malloc (consistency);
  • the _LEN + 1 model is intuitive for even junior coders who are aware of how C strings work;
  • the _LEN constant is more likely to be useful at runtime (comparing against strlen for example).

You could certainly choose to do something like:

typedef struct {
    char companyName[MAX_NAME_BUF];
    char personInCharge[MAX_NAME_BUF];
    char gender;
    char contactNum[MAX_CONTACT_BUF];

and it would also work.

Use typedef to save typing!

You have this typedef:

typedef struct { ... } Date;

You have this struct with no typedef:

struct SanitizeData { ... };                                                                            

I run these commands and get these results:

$ grep '\<Date' sanitization.c  | grep -v 'printf' | wc -l
2

$ grep '\<SanitizeData' sanitization.c  | grep -v 'printf' | wc -l
14

If you're not unix-savvy, those lines say "find every line containing start-of-word followed by Date/SanitizeData and print them, then filter out any printed line that also contains the word 'printf', then count the number of resulting lines."

The two results for Date are for the typedef itself (obviously the word appears where the word is defined) and for where Date is the type of a member inside the SanitizeData structure.

Technically, you would be better off declaring struct Date and using that inside SanitizeData.

On the other hand, there are 13 places where SanitizeData occurs after you declare the structure. Each of those places would be shorter if struct SanitizeData were replaced by SanitizeData.

Go ahead and use typedef on SanitizeData.

Use C prototypes for C functions

C requires (void) to indicate an empty function parameter list. Declaring

int infectionLevelCheck();

is actually an "old-style" function declaration, that tells the compiler there is a function out there, but doesn't declare any parameters and doesn't declare an empty parameter list.

If you want to create a prototype, use

int infectionLevelCheck(void);

Don't rewind(stdin)

This is a Microsoft-ism, and it's doing you a huge disservice. It claims to "clear the keyboard buffer" but that would only be true if stdin was connected to a keyboard.

If you want to do some unit tests, a simple way would be to create a text file full of inputs, and feed that to your program. Except that if you have stdin pointing to a file and call rewind(stdin) it would just start the input over again. D'oh!

I suspect you're doing this because you are using scanf and you haven't figure out how scanf deals with all the various input character (like the newline at the end of an input).

I'd suggest that you stop using scanf for input, and use something more useful. Then you can use sscanf or something else for processing the input you get -- see below.

Write an input module

Much of your code consists of printing messages/prompts/menus to the user, and then reading responses back from the user.

Instead of doing the same thing over and over and over and over and over, why not write some functions to do it for you?

Write functions like Date get_date(), int get_number(void), and int get_yn() that loop until they receive a valid input. Then just call the functions.

You might want to add some output functions as well, and make it a text_ui module.

These functions can initially just be placed at the top of your program (before other functions that use them) and then you can migrate them to a separate library if you end up re-using them in another program.

Change your search model

Your search routines presently have an array of 20 struct SanitizeData objects that they try to read in from the data file.

I suspect you have crashes when you run this code with lots of records, since it seems like your read-in-the-file code overwrites the stack.

In particular:

  while (fread(&data[i], sizeof(data), 1, searchPtr) != 0) {                

In the code above, data is an array of 20 records. So sizeof(data) is 20 * sizeof(record). You're reading 20-records in 1 time, then looping back to do it again at an offset of +1 record in the buffer.

The first time you do this, you can read up to 20 records no problem. The second time you do this, you will write 20-records worth of data at location data[1], which only has 19 records worth of space before the data hits the stack.

So I think if the first read succeeds, and the second read succeeds, you'll get stack corruption. This should happen with 40 or more records in the file.

There's also the bug that you're overwriting the last 19 records that were read in by the read at data[0].

To fix this, just read one record worth of data:

fread(&data[i], sizeof(data[i]), 1, searchPtr)

The change is from sizeof(data) to sizeof(data[i]). You could use other expressions, like sizeof(struct SanitizeData). But data is 20 times bigger than you need.

Regardless of all that, there is still the fact that you are trying to read in "all the data" and then search it. To quote a famous squirrel: "That trick never works!"

Instead, put the fread into the loop and just read one record, then check it. This way you won't have to worry about running out of space in memory, even if you have millions of records.

Centralize your formatting

You have a lot of printf() format strings. Instead of copying those all over the place, write a function or macro that does it for you!

For macros, you can rely on C's adjacent string literal concatenation to join everything together:

#define TBL_VENUE_FMT  "| %-5s "
#define TBL_DATETIME_FMT "| %02d-%02d-%4d %02d:%02d "
#define TBL_TEMP_FMT   "|    %.2f    "

Then

printf(TBL_VENUE_FMT 
       TBL_DATETIME_FMT
       ...,
       data[i].venueID,
       ...);

For functions, you could have them return strings and just format everything use %s.

printf("| %s | %s | %s ...",
       format_venue(data[i].venueID,
       format_datetime(data[i].sanitizeDate, data[i].Start),
        ...);

Replace system("cls")

There's a lot of people in the world who won't have any idea what that does. So wrap it in a function:

void 
clear_screen(void)
{
    system("cls");
}

Then you can call:

do {
    clear_screen();
    do {

and everybody knows the score!

\$\endgroup\$
4
  • \$\begingroup\$ First off, I would like to say that I'm really sorry that It was my mistake I didn't paste the entire code here because I totally forgot about it, it was in another .c file...., I really appreciate your time in reviewing my code and giving me a bunch of suggestion, I will definitely take note on this suggestion by making all of the capturing user's input in a function so that one function only required to do one single task. You have a really good point that I can make use of the macro to do the formatting, I really didn't know that I can do it that way... Thanks for pointing it out to me \$\endgroup\$
    – Ah Kang
    Mar 7, 2021 at 4:15
  • \$\begingroup\$ Also, I really didn't know that I should always put a void inside the parameter of my function prototype @_@, thanks for letting me know. Yes I will listen to your suggestion and try to be more consistent and adapt to one coding standard, I didn't know that rewind(stdin) is a bad thing and yes i probably will start using like fgets, gets and sscanf i believe for getting user's input. \$\endgroup\$
    – Ah Kang
    Mar 7, 2021 at 4:27
  • \$\begingroup\$ Is "appears to target Windows" comment based on the #pragma? If that were inside an #ifdef, it's not far from being portable. Oh, we'd have to fix the assumptions where system() is called, too. \$\endgroup\$ Mar 7, 2021 at 11:48
  • \$\begingroup\$ I'm not quite sure on this, all I know is it will just stop me from using scanf or all sorts of commands that he thinks it is unsafe.... so that's why I have to use #pragma warning (disable:4996). \$\endgroup\$
    – Ah Kang
    Mar 7, 2021 at 12:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.