Weird bug in c in parking lot code, when trying to read string between ""

71 views Asked by At

Okay so basically as you can tell by the code, I'm programming a terminal that reads commands given by the user, and one of the comands is the comand "p" that creates a parking lot, with the arguments name,capacity, cost15 (cost of staying in the park for 15 minutes), cost15_after 1 hour (cost of each 15 minutes after the first hour), and max_daily_cost. The thing is that when inputing the name of the parking lot in the command, when the name is separated by a space , per example "park number one" , the input should be between "" , and when the name is only given by one word, per example park1, it does not come with "". This is not a choice of mine, this is for a college project, so removing the "" is not an option. However, the weird thing is that it works perfectly, but only if I first input a parking lot with a one word name, and then assign a parking lot with a name separated by spaces, because If I try to assign first the parking lot with a name separated by spaces, it doesn't work! By the way the command p alone with no arguments, shows the existing parking lots. This is the output:


p "park name" 20 1 2 3
Error: invalid capacity.

p park 20 1 2 3
Parking lot park successfully created!

p "park name" 20 1 2 3
Parking lot park name successfully created!

p
Existing parking lots:
park 20 20
park name 20 20
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 100

// Definition of the structure for a parking lot
typedef struct {
    char name[50];
    int capacity;
    float cost15;
    float cost15_after1hour;
    float max_daily_cost;

    int available_spaces;
} ParkingLot;

// Global variable to store the created parking lots
ParkingLot parkingLots[10];
int num_parkingLots = 0;

// Function to create a new parking lot
void createParkingLot(char name[], int capacity, float cost15, float cost15_after1hour, float max_daily_cost) {
    if (num_parkingLots >= 10) {
        printf("Error: too many parking lots.\n");
        return;
    }

    // Check if the parking lot name already exists
    for (int i = 0; i < num_parkingLots; i++) {
        if (strcmp(parkingLots[i].name, name) == 0) {
            printf("Error: %s already exists.\n", name);
            return;
        }
    }

    // Check if the capacity is valid
    if (capacity <= 0) {
        printf("Error: invalid capacity.\n");
        return;
    }

    // Check if the tariff values are increasing
    if (cost15 <= 0 || cost15_after1hour <= cost15 || max_daily_cost <= cost15_after1hour) {
        printf("Error: invalid cost.\n");
        return;
    }

    // Create the new parking lot
    strcpy(parkingLots[num_parkingLots].name, name);
    parkingLots[num_parkingLots].capacity = capacity;
    parkingLots[num_parkingLots].cost15 = cost15;
    parkingLots[num_parkingLots].cost15_after1hour = cost15_after1hour;
    parkingLots[num_parkingLots].max_daily_cost = max_daily_cost;
    parkingLots[num_parkingLots].available_spaces = capacity;
    num_parkingLots++;

    printf("Parking lot %s successfully created!\n", name);
}

// Function to list existing parking lots
void listParkingLots() {
    printf("Existing parking lots:\n");
    for (int i = 0; i < num_parkingLots; i++) {
        printf("%s %d %d\n", parkingLots[i].name, parkingLots[i].capacity, parkingLots[i].available_spaces);
    }
}

int main() {
    char command[20];
    char name[50];
    int capacity;
    float cost15, cost15_after1hour, max_daily_cost;

   
    printf("p [ <park-name> <capacity> <cost-15> <cost-15-after-1hour> <max-daily-cost> ] - Create parking lot\n");
    
    
    while (1) {
        
        scanf("%s", command);
        

        if (strcmp(command, "p") == 0) {
            char nextChar = getchar(); // Reads the next character after the "p"
            if (nextChar == '\n') {
                listParkingLots();
            } else {
                ungetc(nextChar, stdin); // Returns the unread character to the buffer
                char str[MAXSIZE];
                fgets(str, MAXSIZE, stdin);
                char *p = strchr(str, '\"');

                if (p){
                    char str_between_quotes[MAXSIZE];
                    int i = 0;
                    p++; // Moves the pointer to the next character after the first quote
                    while (*p != '\"' && *p != '\n' && *p != '\0') {
                        str_between_quotes[i] = *p;
                        i++;
                        p++;
                    }
                    str_between_quotes[i] = '\0'; // Ends the string
                    strcpy(name,str_between_quotes);
                } else {
                    sscanf(str, "%s %d %f %f %f", name, &capacity, &cost15, &cost15_after1hour, &max_daily_cost);
                }
                createParkingLot(name,capacity,cost15,cost15_after1hour,max_daily_cost);
            }
        } else if (strcmp(command, "q") == 0) {
            break;
        } else {
            printf("Invalid command.\n");
        }
    }

    return 0;
}

I've tried debugging it using the gdb tool in linux, but I've came to no conclusion. I think it might be something about the pointers or somethign, I'm not really a guru in C yet. And I would also apreciate some tips for this code, is there a better way to do what I'm trying to do?? Hope someone can help me out, thanks

2

There are 2 answers

0
Vlad from Moscow On

Within this if statement

if (p){
    char str_between_quotes[MAXSIZE];
    int i = 0;
    p++; // Moves the pointer to the next character after the first quote
    while (*p != '\"' && *p != '\n' && *p != '\0') {
        str_between_quotes[i] = *p;
        i++;
        p++;
    }
    str_between_quotes[i] = '\0'; // Ends the string
    strcpy(name,str_between_quotes);
} 

you forgot to extract these items: capacity, cost15, cost15_after1hour, max_daily_cost. So they have indeterminate values provided that this if statement was the first that got the control for the input in the while loop.

You could add for example this call

if ( *p == '\"' ) 
{
    sscanf( ++p, "%d %f %f %f", &capacity, &cost15, &cost15_after1hour, &max_daily_cost)
}
else
{
    // some error message
}

Also pay attention to that you should check the return value of the call of sscanf.

0
Schwern On

To add to Vlad from Moscow's answer...

name, capacity, cost15, cost15_after1hour, max_daily_cost are all declared outside the loop, which means their values will persist for each iteration.

This made the code harder to debug.

Instead, declare variables in the narrowest possible scope. In this case, inside the loop.

int main(void)
{
    printf("p [ <park-name> <capacity> <cost-15> <cost-15-after-1hour> <max-daily-cost> ] - Create parking lot\n");

    while (1)
    {
        char command[20];
        char name[50];
        int capacity;
        float cost15, cost15_after1hour, max_daily_cost;

And now the compiler can warn you that you're using uninitialized variables.


There are other problems.

You're reading up to 100 bytes, but you only allocate 50 bytes for the name and 20 for the command and this will lead to a buffer overflow. Avoid static allocations for input and instead store a pointer to the input.

Avoid scanf. Instead, read the whole line with fgets and then parse it. There are many tools to parse strings including byte by byte, sscanf, and many more.

Your code, reworked. I've gotten rid of the intermediate variables and am using the struct directly; this is possible because ParkingLots are now stored as pointers making them easier to create and then add to the array.

I'm also using pointer math to keep my place in parsing the line so I can parse it a piece at a time.