-2

As part of an exercise, I am developing a small grep-like program in C which is supposed to receive a string and some file names in order to look for that string in the referred files.

I got it working by hardcoding the name of the file and the string:

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

int searchString(char *str1, char *str2) {
    if (strstr(str1, str2) == NULL) {
        return 0;
    } else {
        return 1;
    }
}

void searchInFile(char *filename, char *str) {
    FILE *f;
    int lineno =0;
    size_t len;
    char *buffer;
    f = fopen(filename, "r");
    if (f != NULL) {
        while (getline(&buffer, &len, f) != -1) {
            lineno++;
            if (searchString(buffer, str)) {
                printf("[%s:%d] %s", filename, lineno, buffer);
            } 
        }
    }
    fclose(f);
}

int main() {
    searchInFile("loremipsum.txt", "dolor");
    return 0;
}

But as soon as I modify the code to chage the int main() line to add argc and argv, compile it and then run it, the script returns an error message. The modification is:

int main(int argc, char *argv[]) {
    searchInFile("loremipsum.txt", "dolor");
    return 0;
}

The received error:

* Error in `./a.out': realloc(): invalid pointer: 0x000056424999394d *

Anyone knows why am I getting this error?

Alfonso Jiménez
  • 1,235
  • 1
  • 13
  • 24
  • 4
    Yep, you never allocate memory for buffer, and so your code might do anything. – Lee Daniel Crocker Apr 09 '18 at 19:32
  • 3
    `man 3 getline`, `ssize_t getline(char **lineptr, size_t *n, FILE *stream);` "*If `*lineptr` is set to `NULL` and `*n` is set `0` before the call, then `getline()` will allocate a buffer for storing the line.*" Also, you should open and validate `f` in `main()` and then pass a `FILE*` parameter to `searchInFile` instead of `char *`. – David C. Rankin Apr 09 '18 at 19:52
  • 3
    buffer isn't set to NULL either. – Lee Daniel Crocker Apr 09 '18 at 20:35
  • I got used to easier languages such as Python and now my C is a bit rusty. As soon as I set buffer to NULL and len to 0, the code worked again. Thanks! About opening the file in main, the exercise gives us the function prototypes so I am required to receive the filename as a parameter. – Alfonso Jiménez Apr 10 '18 at 04:30
  • @DavidC.Rankin Post an answer so I can mark it as solved – Alfonso Jiménez Apr 10 '18 at 04:33
  • 1
    Sure. Give me a few minutes and I'm happy to oblige. – David C. Rankin Apr 10 '18 at 04:37

1 Answers1

1

Your primary problem with getline() was failing to initialize len = 0; and buffer = NULL; before passing buffer as the lineptr parameter and len as the n parameter. man 3 getline, explains:

ssize_t getline(char **lineptr, size_t *n, FILE *stream); 
...
If *lineptr is set to NULL and *n is set 0 before the call, then 
getline() will allocate a buffer for storing the line."

Unless you initialize the pointer for lineptr and value for n, those will contain indeterminate (garbage) values that will prevent getline from automatically handling allocation properly.

Fixing those issues, providing a couple of minor cleanups, and enabling passing the filename and searchterm as the first and second arguments to your program, you could do something similar to the following:

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

/* consider adding parameter for case-insensitive search */
int searchstring (const char *str1, const char *str2)
{
    if (strstr (str1, str2) == NULL)
        return 0;
    else
        return 1;
}

int searchinfile (const char *filename, char *str)
{
    int lineno = 0;
    size_t len = 0;
    char *buffer = NULL;
    FILE *f = fopen (filename, "r");

    if (!f) {   /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", filename);
        return 1;
    }

    /* read each line into buffer, search for searchstring */
    while (getline (&buffer, &len, f) != -1) {
        lineno++;
        if (searchstring (buffer, str))
            printf("[%s:%d] %s", filename, lineno, buffer);
    }
    fclose (f);     /* close file */

    return 0;
}

int main (int argc, char **argv) {

    if (argc < 3) { /* validate at least 2 arguments given */
        fprintf (stderr, "error: insufficient input.\n"
                "usage: %s <file> <searchstring>\n", argv[0]);
        return 1;
    }

    /* search file argv[1] for string argv[2] */
    if (searchinfile (argv[1], argv[2]))
        fprintf (stderr, "unable to search '%s'.\n", argv[1]);

    return 0;
}

Example Input File

$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.

Example Use/Output

$ /tmp/tmp-david/greplite dat/captnjack.txt Sea
[dat/captnjack.txt:4] On the Seven Seas.

Note the comment in the code -- it would be a neat trick to pass an additional parameter to indicate whether a case-insensitive search should be performed (and then converting both the line and search-term to lower-case before the search).

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85