4

I am trying to write a .txt file to the user's HOME directory.

I have tried:

char str[] = "TEST";
char* file = strcat(getenv("HOME"), "/dataNumbers.txt"); //I am concatenating the user's home directory and the filename to be created.
myFile = fopen(file,"w");
fwrite(str , 1 , sizeof(str) , myFile );

However, this does not create the file.

Hani Al-shafei
  • 149
  • 1
  • 2
  • 13
  • 1. What is the value of the `file` variable? Is it the expected file name? 2. Does the user running this code have permissions to write to the `$HOME`? You'd think this should be safe to assume, but you should still double check the permissions for that directory. – Code-Apprentice Feb 02 '16 at 23:22
  • If it doesn't create the file you shouldn't be calling `fwrite()` either. There is a distinct lack of error checking here. You need to test `myFile` for null and call `perror()` or its friends if found, and not proceed as though it wasn't. – user207421 Feb 03 '16 at 00:02

3 Answers3

6

You are using strcat incorrectly.

 char *file;
 char *fileName = "/dataNumbers.txt";

 file = malloc(strlen(getenv("HOME") + strlen(fileName) + 1); // to account for NULL terminator
 strcpy(file, getenv("HOME"));
 strcat(file, fileName);

file will now contain the concatenated path and filename.

Obviously, this could be written much more cleanly. I'm just trying to be very straightforward.

David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
5

You can't strcat() to an environment variable. You need another buffer:

char file[256]; // or whatever, I think there is a #define for this, something like PATH_MAX
strcat(strcpy(file, getenv("HOME")), "/dataNumbers.txt");
myFile = fopen(file,"w");

EDIT To address one of the comments below, you should first ensure that the data to be concatenated doesn't overflow the file buffer, or allocate it dynamically - not forgetting to free it afterwards.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • After he fixes that, it will probably fail because of permissions. Make sure you have permission to create a file in the user's home directory. – Lee Daniel Crocker Feb 02 '16 at 23:24
  • @LeeDanielCrocker Presumably `$HOME` is the home directory of the current user who is running the shown code. Most likely there should be no permission issues...but it's probably a good idea to double check. – Code-Apprentice Feb 02 '16 at 23:25
  • Risky if the combination is more than 255 characters. – David Hoelzer Feb 02 '16 at 23:25
  • @LeeDanielCrocker I find it difficult if not impossible to attach any meaning to 'user's home directory' if he can't write to it. This is surely a non-issue. – user207421 Feb 02 '16 at 23:25
  • @DavidHoelzer Agreed, but then if the combination is too long the file can't be opened anyway. My main point is not to `strcat` to an environment variable. – user207421 Feb 02 '16 at 23:26
  • @EJP That's assuming the program is being run on *his* home directory and not someone else's. It also assumes the existence of a home directory, which might not be a good assumption on all OSs. – Lee Daniel Crocker Feb 02 '16 at 23:30
  • 1
    I should clarify that this is for a school project and that these precautions can be overlooked. – Hani Al-shafei Feb 02 '16 at 23:34
  • 1
    @LeeDanielCrocker `getenv("HOME")` returns you your own home directory. – user207421 Feb 02 '16 at 23:35
  • Well, technically, it returns the value of the HOME environment variable, which is *supposed* to point to your home directory if your OS and installation are sane. I don't always assume that, but point taken. – Lee Daniel Crocker Feb 02 '16 at 23:42
  • I do think it would be more reliable to simply prepend a `~` onto the filename, to be honest. – David Hoelzer Feb 02 '16 at 23:45
  • @DavidHoelzer That won't work. `~` is understood by the shell, not the file system. – user207421 Feb 02 '16 at 23:48
  • @LeeDanielCrocker In any case the best way to ensure you have permission is to try to open the file. Any prior checking is either redundant or prone to timing-window problems. – user207421 Aug 31 '20 at 23:59
2

Strings in C are not like other strings in other high-level languages; that is, You have to allocate all the space for them yourself. When using c-string functions, you need to ensure that you have enough memory allocated for the resultant strings, by either reserving enough space on the stack (i.e. char filename[HOPEFULLY_ENOUGH]) or via the heap (malloc() and friends). You cannot count on getenv to return a string with enough space for you to concatenate to;

Here's a complete program from start to finish that does what you want to do (minus some pedantic error checking):

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

int main(void)
{
    char *filename = "/dataNumbers.txt";
    char *writeString = "TEST";
    char *home_dir = getenv("HOME");
    char *filepath = malloc(strlen(home_dir) + strlen(filename) + 1);
    strncpy(filepath, home_dir, strlen(home_dir) + 1);
    strncat(filepath, filename, strlen(filename) + 1);
    printf("%s\n", filepath);

    FILE *myFile = fopen(filepath, "w");
    fwrite(writeString, 1, strlen(writeString), myFile);
    fclose(myFile);
    free(filepath);
    return 0;
}

strncpy will copy the string plus the null terminator (that's the reason for the +1). strncat will begin concatenation at the null terminator and the resultant concatenated string itself must be null-terminated (hence the +1 in the third argument to strncat).

I use malloc and free because it's hard to know exactly the length of the full path to your file. You could go with something enormous (like char[4096]) but that seems wasteful, doesn't it? malloc() allows you to reserve exactly the amount of memory you need. No more, and no less.

You can also do the string concatenation using the snprintf function. I include this only for your personal enrichment, and sometimes it's nice to have multiple ways to do the same thing, even though strcpy and strcat will make your intentions much more clear to readers of your code:

char *filename = "/dataNumbers.txt";
char *writeString = "TEST";
char *home_dir = getenv("HOME");
size_t size = strlen(home_dir) + strlen(filename) + 1;
char *filepath = malloc(size);
snprintf(filepath, size, "%s%s", home_dir, filename);

Hope this helps!

bpm
  • 105
  • 4