asdfffffffff
asdfffffffff

Reputation: 11

Print last few lines of a text file

Currently, I am trying to create a C program that prints the last few lines of a text file, read in through the command line. However, it is currently causing a segmentation error when I try to copy the strings from fgets into the main array. I have been unable to fix this, and so have not been able to test the rest of my code. How would I begin to fix the segmentation error? I have posted the code below:

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

int main(int argc, char *argv[])
{
   int i=1,j,printNumber;
   char **arr = (char **) malloc (100 * sizeof(char *));
   char *line = (char *) malloc (80 * sizeof(char));

   if (argc == 1) {
      printNumber = 10;
   }
   else {
      printNumber = atoi(argv[1]);
   }

   while (fgets(line,80,stdin) != NULL) {
      if (line != NULL) {
         line[strlen(line)-1] = '\0';
         strcpy(arr[i],line);  //SEGMENTATION ERROR!!!!
      }
      else {
         free(line);
         strcpy(arr[i],NULL);
      }
      i++;
      printf("%d ",i);
   }
   free(arr);

   for (j = i-printNumber-1; j < i-1; j++) {
      printf("%s ", arr[j]);
   }
   printf("\n");
   return 0;
}

Upvotes: 1

Views: 101

Answers (2)

Bo R
Bo R

Reputation: 2371

You don't always know the length of the longest line (not until you try to read) OR how many last lines you are expected to keep track of (but is given at runtime). Thus, both of these values need to be known before you allocate memory or delegated to a function that does it for you.

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

struct Line {
    char *line; // content
    size_t storage_sz; // allocation size of line memory
    ssize_t sz; // size of line, not including terminating null byte ('\0')
};

int main(int argc, char *argv[]) {
    int max_lines = 10;
    if (argc > 1) {
        max_lines = atoi(argv[1]);
    }
    if (max_lines < 0) {
        fprintf(stderr, "%s\n", "Sorry, no defined behaviour of negative values (yet)\n");
        return EXIT_FAILURE;
    }

    // keep an extra slot for the last failed read at EOF
    struct Line *lines = (struct Line *) calloc(max_lines + 1, sizeof(struct Line));
    int end = 0;
    int size = 0;

    // only keep track of the last couple of lines
    while ((lines[end].sz = getline(&lines[end].line, &lines[end].storage_sz, stdin)) != -1) {
        end++;
        if (end > max_lines) {
            end = 0;
        }
        if (size < max_lines) {
            size++;
        }
    }

    // time to print them back
    int first = end - size;
    if (first < 0) {
        first += size + 1;
    }
    for (int count = size; count; count--) {
        // lines might contain null bytes we can't use printf("%s", lines[first].line);
        fwrite(lines[first].line, lines[first].sz, 1u, stdout);
        first++;
        if (first > size) {
            first = 0;
        }
    }

    // clear up memory after use
    for (int idx = 0; idx <= max_lines; idx++) {
        free(lines[idx].line);
    }
    free(lines);
    return EXIT_SUCCESS;
}

Upvotes: 0

Govind Parmar
Govind Parmar

Reputation: 21542

You are allocating space for arr, which is a pointer to a pointer to char, but not allocating any individual char * pointers within arr.

Since you allocated arr with the size of 100 * sizeof(char *), I assume you want 100 sub-entries in arr. Sure:

for(i = 0; i < 100; i++)
     arr[i] = malloc(80 * sizeof(char));

Then, when you free arr:

for(i = 0; i < 100; i++)
    free(arr[i]);

free(arr);

Note that it is good practice to always check malloc for failure (return value of NULL) and handle it, and to set pointers to NULL after freeing them once to avoid double-free bugs.

Upvotes: 2

Related Questions