cstringparsingstrsep

Strange character replacement where it makes no sense (to me)


Sorry for the long sections of code, but I am stumped and need a hand!

My specific problem is that when I use my parse method in order to call a "del" event, I get that odd character replacement going on in a line that, as far as I can tell, isn't being touched. This does not happen when I directly call the function "del." I've spent far too long poring over this code to no avail, attempting to implement a different way of tokenizing strings; you name it. I included ALL my code because I don't know where the issue is, as I am starting to think it does not lie within the function "parse."

I'll hang around and give as much information as is requested, for now I don't know what else to add.

P.S. The compiler links against the gnu windows libraries, that's where strsep comes from.

LibGW32C for Windows

P.P.S. Only compiler warning is it whining about me not using char* buf

db.h

#define MAX_ITEMS 80

typedef struct item {
    long id;
    char* name;
    char* desc;
    float price;
} item_t;

extern char *strsep (char **restrict stringp, const char *restrict delim);

int isLong (char* str);

int isFloat (char* str);

void add (item_t* item, long id, char* name, char* desc, float price);

void del (item_t* item, long id);

void modify (item_t* item, long id, char* name, char* desc, float price);

void disp (item_t* item, long id);

void itemCopy (item_t* from, item_t* to);

void parse (item_t* item, char* buf);

int findLastElement (item_t* item);

db.c

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "db.h"

int isLong (char* str) {
    if (str == NULL) {
        return 0;
    }
    char* pEnd;
    strtol (str, &pEnd, 10);
    if (isalpha (*pEnd) || *pEnd == ' ') {
        return 0;
    } else {
        return 1;
    }
}

int isFloat (char* str) {
    if (str == NULL) {
        return 0;
    }
    char* pEnd;
    strtod (str, &pEnd);
    if (isalpha (*pEnd) || *pEnd == ' ') {
        return 0;
    } else {
        return 1;
    }
}


void add (item_t *item, long id, char* name, char* desc, float price) {
    int i = 0;
    while (1) {
        if (item[i].id == id) {
            printf ("Item \"%s\" with ID %lu already exists.\n", item[i].name, id);
            break;
        } else if (item[i].id == 0) {
            item[i].id = id;
            item[i].name = name;
            item[i].desc = desc;
            item[i].price = price;
            break;
        } else {
            i++;
        }
    }
}

void del (item_t* item, long id) {
    int i = 0;
    int end = findLastElement (item);
    while (1) {
        if (item[i].id == id) {
            item[i].id = 0;
            item[i].name = "";
            item[i].desc = "";
            item[i].price = 0;
            while (i < end) {
                itemCopy (&item [i + 1], &item [i]);
                i++;
            }
            break;
        } else {
            if (i == MAX_ITEMS) {
                printf ("Item with ID %lu does not exist.\n", id);
                break;
            }
            i++;
        }
    }
}

void modify (item_t *item, long id, char* name, char* desc, float price) {
    int i = 0;
    while (1) {
        if (item[i].id == id) {
            item[i].name = name;
            item[i].desc = desc;
            item[i].price = price;
            break;
        } else {
            if (i == MAX_ITEMS) {
                printf ("Item with ID %lu does not exist.\n", id);
                break;
            }
            i++;
        }
    }
}

void disp (item_t* item, long id) {
    int end = findLastElement (item);
    printf ("\nID\tNAME\tDESCRIPTION\tPRICE\n--\t----\t-----------\t-----\n");
    if (id == -1) {
        for (int i = 0; i < end; i++) {
            printf ("%lu\t%s\t%s\t$%2.2f\n", item[i].id, item[i].name, item[i].desc, item[i].price);
        }
    } else {
        for (int i = 0; i < end; i++) {
            if (item[i].id == id) {
                printf ("%lu\t%s\t%s\t$%2.2f\n", item[i].id, item[i].name, item[i].desc, item[i].price);
                break;
            }
        }
    }
}

void itemCopy (item_t* from, item_t* to) {
    to -> id = from -> id;
    to -> name = from -> name;
    to -> desc = from -> desc;
    to -> price = from -> price;
}

void parse (item_t* item, char* str) {
    char **ap, *argv[10], *inputstr = malloc (sizeof(str)), *ptr;
    strcpy (inputstr, str);
    memset (argv, 0, sizeof (argv));
    for (ap = argv; (*ap = strsep (&inputstr, ",\n")) != NULL;) {
        if (**ap != '\0') {
            if (++ap >= &argv[10]) {
                break;
            }
        }
    }

    if (strcmp (argv[0], "add\0") == 0) {
        if (!isLong (argv[1]) || argv[1] == NULL) {
            printf ("\nInvalid/Missing ID\n");
            return;
        } else if (argv[2] == NULL) {
            printf ("\nInvalid/Missing Product Name.\n");
            return;
        } else if (argv[3] == NULL) {
            printf ("\nInvalid/Missing Product Description.\n");
            return;
        } else if (!isFloat (argv[4]) || argv[4] == NULL) {
            printf ("\nInvalid/Missing Price\n");
            return;
        } else {
            add (item, strtol (argv[1], &ptr, 10), argv[2], argv[3], strtod (argv[4], &ptr));
        }
    } else if (strcmp (argv[0], "del\0") == 0) {
        if (!isLong (argv[1]) || argv[1] == NULL) {
            printf ("\nInvalid/Missing ID\n");
            return;
        } else {
            del (item, strtol (argv[1], &ptr, 10));
        }
    }
} 

int findLastElement (item_t* item) {
    for (int i = 0; i < MAX_ITEMS; i++) {
        if (item[i].id == 0) return i;
    }
    return -1;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "db.h"


int main (int argc, char* argv[]) {
    item_t item [MAX_ITEMS];
    memset (item, 0, sizeof (item));
    char* buf = malloc (255);
    int exit = 0;

    while (!exit) {
        printf ("Adding 3 test items.\n");
        add (item, 1234, "Pizza", "Tasty Pizza", 9.99F);
        add (item, 5678, "Pasta", "Tasty Pasta", 19.99F);
        add (item, 9012, "Ribs", "Tasty Ribs", 29.99F);
        disp (item, -1);
        printf ("\nDeleting Item with ID 5678.\n");
        del (item, 5678);
        disp (item, -1);
        printf ("\nModifying item with ID 1234.\n");
        modify (item, 1234, "Soup", "Tasty Soup", 4.99F);
        disp (item, -1);
        printf ("\nAdding another item with id 5678.");
        add (item, 5678, "Pasta", "Tasty Pasta", 19.99F);
        printf ("\nAdding item \"Pizza\" with the same ID as \"Soup\".\n");
        add (item, 1234, "Pizza", "Tasty Pizza", 9.99F);
        disp (item, -1);
        printf ("\nDeleting item \"Soup\" and re-adding \"Pizza\".\n");
        del (item, 1234);
        add (item, 1234, "Pizza", "Tasty Pizza", 9.99F);
        disp (item, -1);
        printf ("\nDisplaying only the item with id 5678.\n");
        disp (item, 5678);
        printf ("\nAdding item \"Wings\" using the parsing function.\n");
        parse (item, "add,9898,Wings,Tasty Wings,14.99\n");
        disp (item, -1);
        printf ("\nAttempting to Delete non-existent item with ID 9999.\n");
        //del (item, 9999);
        parse (item, "del,9999\n"); <--- This is the problem line
        printf ("\nAttempting to Modify non-existent item with ID 9999.\n");
        modify (item, 9999, "Test", "Test", 0.0F);
        disp (item, -1);
        exit = 1;
    }
}

And finally, my output:

Adding 3 test items.

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
1234    Pizza   Tasty Pizza     $9.99
5678    Pasta   Tasty Pasta     $19.99
9012    Ribs    Tasty Ribs      $29.99

Deleting Item with ID 5678.

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
1234    Pizza   Tasty Pizza     $9.99
9012    Ribs    Tasty Ribs      $29.99

Modifying item with ID 1234.

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
1234    Soup    Tasty Soup      $4.99
9012    Ribs    Tasty Ribs      $29.99

Adding another item with id 5678.
Adding item "Pizza" with the same ID as "Soup".
Item "Soup" with ID 1234 already exists.

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
1234    Soup    Tasty Soup      $4.99
9012    Ribs    Tasty Ribs      $29.99
5678    Pasta   Tasty Pasta     $19.99

Deleting item "Soup" and re-adding "Pizza".

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
9012    Ribs    Tasty Ribs      $29.99
5678    Pasta   Tasty Pasta     $19.99
1234    Pizza   Tasty Pizza     $9.99

Displaying only the item with id 5678.

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
5678    Pasta   Tasty Pasta     $19.99

Adding item "Wings" using the parsing function.

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
9012    Ribs    Tasty Ribs      $29.99
5678    Pasta   Tasty Pasta     $19.99
1234    Pizza   Tasty Pizza     $9.99
9898    Wings   Tasty Wings     $14.99

Attempting to Delete non-existent item with ID 9999.
Item with ID 9999 does not exist.

Attempting to Modify non-existent item with ID 9999.
Item with ID 9999 does not exist.

ID      NAME    DESCRIPTION     PRICE
--      ----    -----------     -----
9012    Ribs    Tasty Ribs      $29.99
5678    Pasta   Tasty Pasta     $19.99
1234    Pizza   Tasty Pizza     $9.99
9898    Win♦    Çdel    $14.99 <--- THIS IS WHAT'S DOING MY HEAD IN

Thanks for your help in advance!

I really hope this doesn't end up being some trivially stupid rookie error.


Solution

  • I would have thought that the compiler would complain about parse taking a char* as its second argument, since you actually provide a const char* (i.e. a string constant). However, you actually try to copy the string, so you could have (and should have) made the prototype const char*.

    Let's focus on "try to copy". Leaving aside your obfuscated initialization, what you do is:

     inputstr = malloc (sizeof(str));
     strcpy(inputstr, str);
    

    Before I go any further, I suggest you look at strdup, which does exactly what you want, only correctly:

     inputstr = strdup(str);
    

    No fuss, no errors. But anyway,

    str is a char*; that is, a pointer to a character. (It should have been const char*, but it would still be a pointer to a character.) So sizeof(str) is the size of a pointer, which is likely to be four bytes on your platform, although it might be eight. Either way, it's not close to long enough to hold the string "add,9898,Wings,Tasty Wings,14.99\n", so when you copy that into inputstr, you'll end up overwriting random memory. (And probably again with the del string).

    sizeof(*str) would be the size of a character, which is one byte. What I think you might have meant was strlen(str), which is the length of the string str in bytes, but that wouldn't be right either because it leaves out the terminating NUL byte.

    In short, what you wanted was the strdup call above, which is equivalent to:

    char* inputstr = malloc(strlen(str) + 1);
    strcpy(inputstr, str);