1

I am writing a command line tool in C to control UNDERTALE save files (cuz why not) and I keep getting a segmentation fault whenever I take input from the user.

Here is all my code:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#define KILOBYTE 1024

/* The 'm()' is so i know what line the fault happens on */

void m(void) {
        printf("X");
}

int main(int argc, char**argv) {
    FILE *file;

    if (file = fopen("~/.undersave", "r")) {
        // Do some code that I haven't written yet
    }
    else {
        char path[KILOBYTE] = "";
        printf("It seems that this is your fist time using UNDERSAVE.\nWe just need to go through some quick installation steps.\nWhere do you want your UNDERTALE save folder to be located? >>> ");
        m();
        fgets(path, KILOBYTE, stdin);
        /*
              The fault happens here, when it asks me, 
              i put in the input like normal, but
              when I press enter, I get a fault before
              it prints 'X'
         */
        m();
        mkdir(path, 0777);
        m();
        file = fopen("~/.undersave", "w");
        m();
        fprintf(file, "%s", path);
        m();
        fclose(file);
    }
      
    return 0;
}
Chris
  • 26,361
  • 5
  • 21
  • 42
TheMystZ
  • 80
  • 1
  • 8
  • Re "*The 'm()' is so i know what line the fault happens on*", oof. Learn to use a debugger. `gdb` can easily give you a stack trace (the command is `bt` IIRC). But also, you should find `-fsanitize=address` very useful if you're using `gcc` or `clang`. – ikegami Dec 10 '21 at 05:35
  • 1
    `printf` uses buffered output, so you won't see anything unless you flush the output buffer. Typically, you just use a newline (`'\n'`) at the end of the printf to do that. I'd recommend `void m(int location) { printf("X%d\n", location); }` and then change the calls to `m(1)`, `m(2)`, etc. – user3386109 Dec 10 '21 at 05:36
  • 1
    Also see [how to remove the newline that fgets puts in the buffer](https://stackoverflow.com/questions/2693776). – user3386109 Dec 10 '21 at 05:47

1 Answers1

4

Most likely both calls to fopen fail, because on Unix-like systems ~-expansion is a feature of the shell, and isn't applied to a program opening a file directly with fopen or open. If you want to open a file relative to your home directory, either include the full path, or use getenv to read the HOME environment variable and construct the path within your program. Or, better still, make the filename a command-line argument; those arguments are expanded by the shell before your program sees them, so then ~ expansion would just work.

So the fopen for writing returns NULL, which you don't test for (a more serious bug). And you then try to fprintf to a null file pointer, which naturally crashes.

Also, your printf("X") is not a good diagostic, because output to a terminal is usually buffered. The X's won't actually be written to the screen until a new line is printed, or until you try to read from stdin, or some other special conditions occur. So I suspect the program actually crashes later than you think, on the fprintf instead of the fgets. See Why does printf not flush after the call unless a newline is in the format string? and then put fflush(stdout) after the printf in your m() function.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • If it exists, do some code that I haven't written yet, better add that in. – TheMystZ Dec 10 '21 at 05:42
  • Oh wait, I understand now. It's not fgets, because 'm();' has not been flushed. The real problem is on 'fprintf(file,"%s",path);', the reason the folder was not created is that the path does not exist. (~ not existing) so i just assumed it was fgets stopping it. – TheMystZ Dec 10 '21 at 06:04