-1

I have this program that extracts book data(title, isbn, authors and publisher) from a file and then populates 3 structs. The structs are all correctly populated and the command "t" that prints all the books alphabetically in order is working correctly. So everything up until that point works but then if I try to use the "i 'isbn'" (ex.: i 9780321942050) command it doesn't work. It prints some weird characters and I've tried everything in the range of function of my brain to try and fix it but nothing worked. From what I could understand the problem is in the vecRefSearchIsbn function and what it returns because everything is being passed correctly up until that point. I don't have that much knowledge about pointers so I may be missing something but I've tried a lot of things so I'm not going to list them all. I provided the main function and the vecRefSearchIsbn function because that is where I think the problem resides but if someones thinks that the problem is elsewhere I won't hesitate to provide any other functions.

This is what I get in the output:

Digite um comando (t, i + isbn, q): i 9780321942050

Title:p▬┼%7☻
ISBN: 0z┼%7☻
Authors: ░É┼%7☻
Publisher: `¡┼%7☻
typedef struct {
 char *title;
 char isbn[MAX_ISBN];
 char *authors;
 char *publisher;
} Book;
typedef struct{
 Book **refs;
 int size;
 int space;
} VecBookRef;
typedef struct{
 VecBookRef *titleVec;
 VecBookRef *isbnVec;
} DynCollection; 

#include <stdio.h>
#include <stdlib.h>
#include "vector.h"
#include "book.h"
#include "collection.h"
#include "splitfield.h"


int main(){
    char input[100];
    DynCollection *col;

    FILE *f=fopen("dados.csv", "r");
    if(f==NULL){
        printf("Erro ao abrir ficheiro");
        return 0;
    }
    vecRefCreate();
    col=collecBuild(f);
    if(col==NULL){
        printf("Erro ao criar coleção");
        return 0;
    }
    fclose(f);

    do{
        printf("\nDigite um comando (t, i + isbn, q): ");
        fgets(input, sizeof(input), stdin);
        int inputLen=strlen(input);

        if(inputLen==2 && input[0]=='t'){
            vecRefScan(col->titleVec, NULL, printBook);
        }
        else if(inputLen>2 && input[0]=='i' && input[1]==' '){ 
            char isbn[MAX_ISBN];
            sscanf(input+2, "%s", isbn);
            if(strlen(isbn)!=13){
                printf("\nISBN invalido\n");
                continue;
            }
            Book *b=vecRefSearchIsbn(col->isbnVec, isbn);
            if(b!=NULL){
                printBook(b, NULL);
            }else{
                printf("\nNao foi encontrado um livro com o ISBN inserido\n");
                continue;
            }
        }
        if(inputLen==2 && input[0]=='q'){
            collecFree(col);
            printf("Memoria limpa");
        }
    }while(input[0]!='q');

    return 0;
}

Book *vecRefSearchIsbn(VecBookRef *vec, char *isbn){

int comparator(const void *key, const void *book){
    const char *isbn=(const char *)key;
    const Book *b=*(const Book **)book;
    return strcmp(isbn, b->isbn);
}
Book *b=(Book*)bsearch(isbn, vec->refs, vec->size, sizeof(Book *), comparator);
if(b!=NULL){
    return b;
}else return NULL;

}

void printBook(Book *b, void *context){
    printf("\nTitle:%s\nISBN: %s\nAuthors: %s\nPublisher: %s\n", b->title, b->isbn, b->authors, b->publisher);
}

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 1
    Almost always accessing memory out of bounds, failure to NUL terminate strings. Is `MAX_ISBN` big enough to hold the required text *and* the NUL? You should specify the length in `scanf` if practical so it doesn't overflow that buffer. – tadman May 17 '23 at 22:23
  • Where do you actually print this output? Is it in `printBook`? That code needs to be shown if that's the case. – tadman May 17 '23 at 22:24
  • @tadman yes, the printBook function only has a line where it prints the data by accessing the fields from the struct(ex.: b->title). Only that nothing more. – Zé António May 17 '23 at 22:28
  • Only that's where your bug is, so it'll be illuminating to see that code. You should also **step through in a debugger** to see what the captured values are. There's a lot of reading code here that could have gone hugely awry. – tadman May 17 '23 at 22:28
  • @tadman but the vecRefSearchIsbn is only returning a point to a book object that is already populated and if the "t" command works it means that the strings are all well terminated because it can print all the books by accessing that data, right? Atleast that's what I thought. – Zé António May 17 '23 at 22:31
  • @tadman I provided the printBook() function and yes MAX_ISBN is set to 14 that is sufficient to accomodate the 13 numbers that make the ISBN and the null character at the end – Zé António May 17 '23 at 22:35
  • Next step is to check your reading code as you may have mangled data long before you get to the printing part. The alternative is the pointer you're passing in to print is not valid. – tadman May 17 '23 at 22:36
  • For what it's worth, checking that something is NULL, and then returning NULL, otherwise that thing, is just the same as returning that thing. You could just `return bsearch(...)` and be done with it. – tadman May 17 '23 at 22:37
  • It's also hard to tell here but do you have a function defined in your function? You *can* do that, but it's also really weird and frowned on by a lot of C coding conventions. It's easy enough to bump that up to the top-level right alongside your search function. – tadman May 17 '23 at 22:39
  • @tadman I put the comparator function inside the vecRefSearchIsbn function just so it would be more compact for the question. I don't think any data is mangled because I tried to print all the data from the books just before calling the vecRefSearchIsbn function and it printed everything correctly and vecRefSearchIsbn is just supposed to return a pointer to something already existent. – Zé António May 17 '23 at 22:45
  • Re nested functions [for what it's worth](https://stackoverflow.com/q/2608158/631266) – Avi Berger May 18 '23 at 01:08
  • We need a [mre] to help you. Incomplete snippets means that we have do work just to figure if you supplied enough information to debug the issue. – Allan Wind May 18 '23 at 05:53
  • You leak resources on failure. For instance if col is NULL then you don't close the file. Consider using a goto chain for resource cleanup. It works particular well if you initialize values so safety free all of them irregardless of where it failed. – Allan Wind May 18 '23 at 05:59
  • Always use a maximum field width when reading strings with the `sscanf()`. I.e. `sscanf(input+2, "%s", isbn)` will overflow with invalid input. It's too large to check after the fact. – Allan Wind May 18 '23 at 06:10

1 Answers1

0

Always use a maximum field width when reading a string with *scanf() as this will cause a buffer overflow if user enters too much data. It's too late to check after the fact:

            char isbn[MAX_ISBN];
            sscanf(input+2, "%s", isbn);
            if(strlen(isbn)!=13){

Otherwise the corruption/error is in code you haven't shown us. I made the following changes:

  1. Changed MAX_ISBN from 14 to 13 (required for the str() macro).
  2. Changed isbn definition to use MAX_ISBN+1.
  3. Define INPUT_LEN instead of the magic number 100 inline.
  4. (Not fixed) Check if fgets() returns NULL.
  5. Strip the trailing newline from input as you probably don't care about it. If you do then you check that overflow is \n.
  6. Don't call strlen() just use functions that can deal with input that is possible too small, i.e. strcmp(), strncmp(). That said, I might be tempted to switch on input[0] even if means you now need to use a goto to exit the loop instead of the break.
  7. Changed error handling for too large a isbn, and used a maximum field width to read it.
  8. Use break/goto instead of testing the exit condition twice.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_ISBN 13
#define INPUT_LEN 100
#define str(s) str2(s)
#define str2(s) #s

typedef struct {
    char *title;
    char isbn[MAX_ISBN+1];
    char *authors;
    char *publisher;
} Book;

int main(void) {
    char input[INPUT_LEN];
    for(;;) {
        printf("\nDigite um comando (t, i + isbn, q): ");

        fgets(input, sizeof(input), stdin);
        input[strcspn(input, "\n")] = '\0';
        if(!strncmp(input, "i ", 2)) {
            char isbn[MAX_ISBN+1];
            char overflow;
            if(sscanf(input+2, "%" str(MAX_ISBN) "s%c", isbn, &overflow) != 1) {
                printf("\nISBN invalido\n");
                continue;
            }
            printf("%s\n", isbn);
        } else if(!strcmp(input, "q")) {
            printf("Memoria limpa");
            break;
        }
    }
}

and example session:

Digite um comando (t, i + isbn, q): i 9780321942050
9780321942050

Digite um comando (t, i + isbn, q): i 9780321942050x

ISBN invalido

Digite um comando (t, i + isbn, q): q
Allan Wind
  • 23,068
  • 5
  • 28
  • 38