I've got a basic program that's designed to copy the functionality of bash's cp command. I'm developing a copy for UNIX and for Windows. My UNIX version works fine, however, I'm finding that Windows doesn't have support for the "wx" mode option for fopen(), as in the following line:
file2 = fopen(argv[2], "wx");
Is there an alternative way to mirror the wx functionality mode for fopen here?
(wx allows for opening a file with write access, but will return an error if a file with the same filename already exists--meaning you won't override the existing file. See here.
note: attempting to run the program in Developer Command Prompt for VS2013
The short answer is that you cannot pass "wx" or any equivalent to fopen that will yield a CreateFile with CREATE_NEW. fopen simply does not accept any parameter combination to yield that - it's very limited. You can see the source code yourself for fopen in the Visual Studio CRT code base!
However you can instead call CreateFile directly. This is probably the best approach.
Alternatively you can call _open (http://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx) which will take the parameter _O_EXCL which can yield CREATE_NEW and thus will cause it to fail if the file exists as you want.
From the CRT:
case _O_CREAT | _O_EXCL:
case _O_CREAT | _O_TRUNC | _O_EXCL:
filecreate = CREATE_NEW;
break;
Related
I'm trying to learn File I/O concepts in C programming language. I'm using GNU / Linux ( Ubuntu 16.04 LTS ) and my IDE is eclipse 3.8. when I try to write in a file through fprintf() method, it doesn't create any files or if the file is even created, it doesn't write in it. I tried to fix the problem by using fflush() or setbuf(file_pointer, NULL) methods as is suggested here but still no change. I guess I'm writing the address of the file in a wrong way.
Here is the code:
#include <stdio.h>
int main(void){
FILE *file_pointer;
file_pointer=fopen("~/.textsfiless/test.txt","w+");
setbuf(file_pointer,NULL);
fprintf(file_pointer,"Testing...\n");
fclose(file_pointer);
return EXIT_SUCCESS;
}
Can someone explain what's wrong here?
On Linux, the ~ in ~/.textsfiless/test.txt is not expanded by the C library fopen... When you use ~ on the command line, it is expanded by your shell (but not by the program using it, started by the shell doing some execve(2)...) into your home directory; the expansion is called globbing. Read glob(7). You are very unlikely to have a directory named ~.
You should read Advanced Linux Programming
So you should check if fopen failed (it is very likely that it did fail). If you want to get a file in the home directory, you'll better use getenv(3) with "HOME" (or perhaps getpwuid(3) & getuid(2)...). See environ(7)
Perhaps a better code might be:
char*homedir = getenv("HOME");
if (!homedir) { perror("getenv HOME"); exit(EXIT_FAILURE); };
char pathbuf[512]; /// or perhaps PATH_MAX instead of 512
snprintf(pathbuf, sizeof(pathbuf),
"%s/.textsfiless/test.txt", homedir);
FILE *file_pointer = fopen(pathbuf, "r");
if (!file_pointer) { perror(pathbuf); exit(EXIT_FAILURE); };
and so on.
Notice that you should check against failures most C standard library (& POSIX) functions. The perror(3) function is useful to report errors to the user on stderr.
(pedantically, we should even test that snprintf(3) returns a length below sizeof(pathbuf) or use and test against failure asprintf(3) instead; I leave that test as an exercise to the reader)
More generally, read the documentation of every external function that you are using.
Beware of undefined behavior (your code is probably having some, e.g. fprintf to a NULL stream). Compile your code with all warnings & debug info (so gcc -Wall -g) and use the gdb debugger. Read What every C programmer should know about undefined behavior.
BTW, look into strace(1) and try it on your original (faulty) program. You'll learn a lot about the system calls used in it.
Most likely your call to fopen() fails. You don't have any checking in your program to ensure fopen even worked. It may not have, and this could be due to a variety of things, like you spelling the path wrong, wrong file or process permissions, etc.
To see what really happened, you should check fopen's return value:
#include <stdio.h>
int main(void){
FILE *file_pointer;
file_pointer=fopen("~/.textsfiless/test.txt","w+");
if (file_pointer == NULL) {
printf("Opening the file failed.");
return EXIT_FAILURE;
}
setbuf(file_pointer,NULL);
fprintf(file_pointer,"Testing...\n");
fclose(file_pointer);
return EXIT_SUCCESS;
}
Edit: Since your comment, you getting the path wrong is most certainly what happened. If you're executing your program from the current directory, and your file is in a folder called "textfiless" in your current directory and your file is called "test.txt", then you'd call fopen like this:
file_pointer=fopen("/textsfiless/test.txt","w+");
I am trying to open a couple different files via their absolute path (determined elsewhere, programmatically), so I can get their SHA1 hash*, some of which are core windows files. fopen() is returning NULL on some (but not all) files when I attempt to open them as follows (normally the filename is gotten via QueryFullProcessImageName but I hardcoded it just in case):
char * filename = "c:\\windows\\system32\\spoolsv.exe";
FILE * currFileRead = fopen(filename, "rb");
if (currFileRead == NULL)
{
printf("Failed to open %s, error %s\n", filename, strerror(errno) );
}
else
{
//hashing code
}
The reported error is 2: "No such file or directory", but obviously they're there. It also only fails for some processes, like spoolsv.exe or winlogon.exe, while svchost.exe and wininint.exe seem to open just fine.
My program has administrative privileges, and I can't figure why some processes would fail while others opened without trouble?
*I'm using a method from LibTomCrypt (http://libtom.org/?page=features) which is open source with a permissive license. The call to sha1_process takes in a hash_state (internal to the library), an unsigned char buffer, and the length of the buffer. I need to read the file with fopen to get the file into memory for hashing.
Because your program is a 32-bit process, when you try to open c:\windows\system32 you actually get c:\windows\syswow64 which does not contain all of the same files.
You can use IsWow64Process to determine whether you are running on a 64-bit system. If you are, you can replace system32 with sysnative in the path to open the actual file, unless you need to support Windows 2003 or Windows XP. Depending on your circumstances, you might need to cope with the possibility that the Windows folder is not c:\windows and/or the possibility that there are other folders named system32.
On the whole it would be more robust to have separate 32-bit and 64-bit versions of your application, or perhaps just the particular part of it that is exhibiting the problem. If you can't leave it up to the user to install the appropriate version, the installer could decide which to install, or you could always install both and have the 32-bit version automatically launch the 64-bit version when running on a 64-bit system.
Having administrative privileges is not always enough, because if the file you want to open is in use and the program that is using it has locked it, then you can't open and read that file.
It looks like this question is pretty simple but I can't find the clear solution for copying files in C without platform dependency.
I used a system() call in my open source project for creating a directory, copying files and run external programs. It works very well in Mac OS X and other Unix-ish systems, but it fails on Windows. The problem was:
system( "cp a.txt destination/b.txt" );
Windows uses backslashes for path separator. (vs slashes in Unix-ish)
Windows uses 'copy' for the internal copy command. (vs cp in Unix-ish)
How can I write a copying code without dependency?
( Actually, I wrote macros to solve this problems, but it's not cool. http://code.google.com/p/npk/source/browse/trunk/npk/cli/tests/testutil.h, L22-56 )
The system() function is a lot more trouble than it's worth; it invokes the shell in a seperate proccess, and should usually be avoided.
Instead fopen() a.txt and dest/b.text, and use getc()/putc() to do the copying (because the standard library is more likely to do page-aligned buffering than you)
FILE *src = fopen("a.txt", "rb");
FILE *dst = fopen("dest/b.txt", "wb");
int i;
for (i = getc(src); i != EOF; i = getc(src))
{
putc(i, dst);
}
fclose(dst);
fclose(src);
You need to use the C standard library functions in stdio.h.
In particular, fopen, fread, fwrite, and fclose will be sufficient.
Be sure to include the b ("binary") option in the flags to fopen.
[edit]
Unfortunately, the file names themselves (forward-slashes vs. back-slashes) are still platform dependent. So you will need some sort of #ifdef or similar to deal with that.
Or you can use a cross-platform toolkit.
Use the standard C library stdio.h. First open input file for reading using fopen(inputFilename, "rb") and open output file for writing using fopen(outputFilename, "wb"), copy the content using fread and fwrire. Then close both files using fclose.
Maybe I just have another black out but, this one line is giving me a lot of troubles:
FILE *fp = fopen("data/world.data", "rb");
This works fine under Linux when compiled with GCC. But when I compile it with Visual Studio, it crashes. fp is always NULL. Both the BIN and the EXE are in the exact same directory. Now, to make things even crazier, when I run the EXE using Wine under Linux... it... works...
I have absolutely not a god damn clue what's going on here. Maybe it's some insanely stupid mistake on my side, but I cannot get this thing to run under Windows :/
Also, I have another program which works just fine, there the data files are also contained in a sub directory named data.
EDIT:
To make it clear neither / NOR `\ * do work.
EDIT 2:
OK I've given up on this, maybe someone has fun trying to figure it out, here's ZIP containing the EXE, Debug Data for VS etc.:
https://dl.dropbox.com/u/2332843/Leaf.zip
EDIT 3:
Compiled it with CodeBlocks and MinGW, works like a charm. Guess it has to do something with MSVC or the Project Settings in VS.
It sounds like data isn't a subdirectory of your current directory when you run the program. By default (for x86 targets) VS will build and run your program from either a DEBUG or RELEASE subdirectory of the base directory you've created for the project. You can modify the directory that will be "current" when it runs though (e.g., project | properties | configuration properties | debugging for VS 2008).
Edit: While Windows requires that you use a backslash as a directory separator at the command line, a forward slash works fine in code -- this is not the source of your problem.
In windows you have to write the following:
FILE *fp = fopen("data\\world.data", "rb");
This is like that because the backslash is a special character (so a backslash in a string is written using \ and a quotation symbol is \" and so with other special characters).
Since this issue happens only on windows. I doubt whether the file is really named "world.data". As you know, the default setting for windows hides the file extention. Is its real name world.data.xxx?
Include a line to GetCurrentDirectory(), to see if you are running from the directory you expected.
When I develop in C#/ C++ on visual studio, I normally get to run it from the debug folder. I don't think it matters if forward slash is used in place of backslash in .net.
I happened to have the same problem, and suddenly i figured it out.
That should be your windows fault.
Let's say, FILE *fp = fopen("data/world.data", "rb"); in windows, if you hide the extensions, then you can see the file data/world.data, but actually it maybe /data/world.dat.txt or somewhat.
So please check the extensions.
Hope it helps!
I ran into this today, and it happened because I used "br" instead of "rb" on that mode argument.
The underlying fopen is throwing an exception of some kind, which only registers as a crash. It's not bothering to return the standard NULL response or set the associated error values.
I'm not sure but it may be because you're using slash instead of (an escaped) backslash in the path?
How can one create a new file descriptor from an existing file descriptor such that the new descriptor does not share the same internal file structure/entry in the file table? Specifically attributes such as file offset (and preferably permissions, sharing and modes) should not be shared between the new and old file descriptors.
Under both Windows and Linux, dup() will duplicate the file descriptor, but both descriptors still point to the same file structure in the process' file table. Any seeking on either descriptor will adjust the position for the other descriptors as well.
Note
I've since received answers for both Windows and Linux and adjusted the question a little too often, which has made it difficult for people to answer. I'll adjust my votes and accept the cleanest answer which covers both Windows and Linux. Apologies to all, I'm still new to the SO paradigm. Thanks for the great answers!
So basically, what you really want is to be given a file descriptor, and basically open the same file over again, to get a separate position, sharing, mode, etc. And you want to do this on Windows (where the "file descriptor" is basically a foreign object, not something used directly by the OS or the run-time library at all.
Amazingly enough, there is a way to do that, at least with MS VC++. All but two steps of it use only the Win32 API so porting to other compilers/libraries should be fairly reasonable (I think most supply versions of those two functions). Those are for converting a Unix-style file descriptor to a native Win32 file handle, and converting a native Win32 file handle back to a Unix-style file descriptor.
Convert file-descriptor to native file handle with _get_osfhandle()
Get a name for the file with GetFileInformationByHandleEx(FILE_NAME_INFO)1
Use CreateFile to open a new handle to that file
Create a file descriptor for that handle with _open_osfhandle()
Et voilĂ , we have a new file descriptor referring to the same file, but with its own permissions, position, etc.
Toward the end of your question, you make it sound like you also want the "permissions", but that doesn't seem to make any real sense -- the permissions attach to the file itself, not to how the file is opened, so opening or reopening the file has no effect on the file's permissions. If you really want to know the, you can get it with GetFileInformationByHandle, but be aware that file permissions in Windows are quite a bit different from the (traditional) file permissions in Unix. Unix has owner/group/world permissions on all files, and most systems also have ACLs (though there's more variation in how they work). Windows either has no permissions at all (e.g., files on FAT or FAT32) or else uses ACLs (e.g., files on NTFS), but nothing that's really equivalent to the traditional owner/group/world permissions most people are accustomed to on Unix.
Perhaps you're using "permissions" to refer to whether the file was open for reading, writing, or both. Getting that is considerably uglier than any of the preceding. The problem is that most of it is in the library, not Win32, so there's probably no way to do it that will be even close to portable between compilers. With MS VC++ 9.0 SP1 (not guaranteed for any other compiler) you can do this:
#include <stdio.h>
int get_perms(int fd) {
int i;
FILE * base = __iob_func();
for (i=0; i<_IOB_ENTRIES; i++)
if (base[i]._file == fd)
return base[i]._flag; // we've found our file
return 0; // file wasn't found.
}
Since this involved some spelunking, I wrote a quick test to verify that it might actually work:
#ifdef TEST
#include <io.h>
void show_perms(int perms, char const *caption) {
printf("File opened for %s\n", caption);
printf("Read permission = %d\n", (perms & _IOREAD)!=0);
printf("Write permission = %d\n", (perms & _IOWRT)!=0);
}
int main(int argc, char **argv) {
FILE *file1, *file2;
int perms1, perms2;
file1=fopen(argv[1], "w");
perms1 = get_perms(_fileno(file1));
fclose(file1);
file2=fopen(argv[1], "r");
perms2 = get_perms(_fileno(file2));
fclose(file2);
show_perms(perms1, "writing");
show_perms(perms2, "reading");
return 0;
}
#endif
And the results seem to indicate success:
File opened for writing
Read permission = 0
Write permission = 1
File opened for reading
Read permission = 1
Write permission = 0
You can then test that returned flag against _IOREAD, _IOWRT, and _IORW, which are defined in stdio.h. Despite my previous warnings, I should probably point out that I suspect (though I certainly can't guarantee) that this part of the library is fairly stable, so the real chances of major changes are probably fairly minimal.
In the other direction, however, there's basically no chance at all that it'll work with any other library. It could (but certainly isn't guaranteed to) work with the other compilers that use the MS library, such as Intel, MinGW or Comeau using MS VC++ as its back-end. Of those, I'd say the most likely to work would be Comeau, and the least likely MinGW (but that's only a guess; there's a good chance it won't work with any of them).
Requires the redistributable Win32 FileID API Library
So, I recommend reading up on this a little more. The dup() and related functions serve to create a duplicate value in the file descriptor table pointing to the same entry in the open file table. This is intended to have the same offset. If you call open(), you will create a new entry the open file table.
It doesn't make any sense to create a duplicate of a file descriptor and that new file descriptor have a different offset in the open file table (this seems to contradict what the word "duplicate" means).
I'm not sure what your question is actually. I mean, it isn't the same thing as a duplicate. You could read:
/proc/self/fd/[descriptor]
and get the string that was used to open that file descriptor; bear in mind this may provide some pitfalls, some of which you actually noted in your observation of calling open() again.
Maybe you can explain a little more and I can try to update to help.
Why don't you just open the file a second time with open() or CreateFile() on windows? This gives you all freedom of different access rights and separate offset.
This of course has the drawback that you you can not open the file exclusively, but it solves your problem very simply.