Mockup of my production code:
/* version 1 */
#include <stdio.h>
FILE** fout = &stdout;
int main() {
fprintf( *fout, "hello\n" );
}
Works fine under gcc, but reportedly fails to compile under mingw (lvalue required as unary '&' operand).
I have seen Is setting a FILE* equal to stdout portable?; I understand that
/* version 2 */
#include <stdio.h>
int main() {
FILE* fout = stdout;
fprintf( fout, "hello\n" );
}
would be perfectly valid. However, I need to preset a global variable. Unfortunately,
/* version 3 */
#include <stdio.h>
FILE* fout = stdout;
int main() {
fprintf( fout, "hello\n" );
}
is not suitable to replace version 1; it does not even compile under gcc (line 2: initializer element is not constant).
Any idea how to get stdout into a variable that is initialized before main() starts?
In Linux (glibc), stdout is defined like this:
extern struct _IO_FILE *stdout;
So, you can do whatever you need with that.
However, on MinGW, stdout is defined like this, in stdio.h:
#define stdout (&_iob[STDOUT_FILENO])
Alas, that's not something you can take the address of, and, as you discovered, it's not something you can use in a global initializer. :-(
The root of the problem is that the C standard says that these should be macros, which means any portable program should make no assumptions about what's inside. So, I'm afraid, there is no easy way to avoid doing reading stdout programmatically. This sort of thing is why many libraries require a lib_initialize() function that must be called before anything else.
C++ does permit constructors for global variables, and these are automatically called before main, even for libraries. It is possible, with gcc, to hack a C program to do the same, but that's an evil trick and I can't remember how to do it off the top of my head.
I'd just do this:
#include <stdio.h>
FILE* fout = NULL;
int my_library_function() {
if (!fout)
fout = stdout;
fprintf( fout, "hello\n" );
}
That isn't a big efficiency problem: you'd have to load fout anyway, and a compare with zero is pretty cheap.
According to the C standard (7.21.1), stdout is a macro which is an expression of type "pointer to FILE". It is not necessarily a global variable. It is not portable C to take its address --- it works in gcc but not in mingw, as you saw.
Use the second version of your code --- this is portable.
The third would be OK too if you moved the initialization of fout inside main:
/* version 3 */
#include <stdio.h>
FILE* fout;
int main() {
fout = stdout;
fprintf( fout, "hello\n" );
}
This initialization cannot be combined with the declaration of fout, as stdout is not (at least, not necessarily) a constant expression.
If you want to have a FILE ** pointer, use:
/* version 4 */
#include <stdio.h>
FILE* mystdout;
FILE** fout = &mystdout;
int main() {
mystdout = stdout;
fprintf( *fout, "hello\n" );
}
but again the initialization of mystdout cannot be at its declaration, for the same reason.
I don't think that this is a very good idea, as file descriptors are not generally portable. Also, it makes the implementation of library functions quite low-level, or you'll have a hard time synchronizing file descriptors and FILE pointers.
However, as #JoachimWuttke explicitly asked, here's what I had in mind in my previous comment:
/* version 5 */
#include <stdio.h>
#include <unistd.h>
int fdout = 1;
void use(FILE *fout)
{
fdout = fileno(fout);
}
void printing() {
const char msg[] = "hello\n";
write(fdout, msg, sizeof(msg)-1);
}
int main() {
printing(); // this one goes to stdout
FILE *f = fopen("output.txt", "wt");
use(f);
printing(); // this one goes to "output.txt"
printing(); // this one too
fclose(f);
use(stdout);
printing(); // this one goes to stdout too
return 0;
}
Related
THis is the Dll code
#ifdef HELLO_EXPORTS
#define CLASS_DECLSPEC __declspec(dllexport)
#else
#define CLASS_DECLSPEC __declspec(dllimport)
#endif
CLASS_DECLSPEC FILE* GetStdout() {
return stdout;
}
CLASS_DECLSPEC void dump_code(FILE* fd, const void* data, size_t len)
{
unsigned char* p = (unsigned char*)data;
size_t i;
for(i = 1; i < len + 1; ++i){
fprintf(fd, "0x%.2x, ", p[i-1]);
if((i%16)==0) fprintf(fd, "\n");
}
fprintf(fd, "\n");
}
This is the test code:
#include <stdio.h>
#include <stdlib.h>
#include "dump.h"
int main(){
char data[] = {
0x1f,0xc2,0x8b,0x08,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xc3,0x8d,0xc2,0x90,0x3d
};
dump_code(GetStdout(), data, 16);
//dump_code(stdout, data, 16);
}
If I directly use stdout: dump_code(stdout, data, 16);, the program will crash in _lock_file.
But it will ok to use GetStdout() instead of stdout.
Don't pass FILE * pointers between two copies of libc. You need to link the exe and the dll against the exact same version of libc as a dynamic link library or it won't work.
The general tradition on Windows is don't even try to make this kind of thing work. For every export that would return allocated memory there is another export to free that memory, and more complex things either aren't done at all or done using COM to provide cleanup routines.
OP has now commented that he built with /MTd, which decodes to multi-threaded, static library, and switching to /MDd, which decodes to multi-threaded dll-version-specific library (which is in a .dll) fixed the problem, as expected. When switching to release, /MD must be used instead of /MT or the same problem will reoccur.
Can anyone tell me how to get the path of the current working directory on Windows?
The Windows API function GetCurrentDirectory will give you the current directory for the current process.
Alternatively, you may want to use the function getcwd or _getcwd, especially if you seek compatibility with POSIX platforms such as Linux.
Here is an example for using the function GetCurrentDirectory:
#include <windows.h>
int main( void )
{
TCHAR tszBuffer[MAX_PATH];
DWORD dwRet;
dwRet = GetCurrentDirectory( MAX_PATH, tszBuffer );
if ( dwRet == 0 )
{
//TODO: handle error
}
// The buffer now contains the path of the
// current directory and can be inspected.
}
MAX_PATH is defined when you #include <windows.h>.
TCHAR is just a typedef for char if you are compiling in ASCII mode, or for a wide-character WCHAR if you are compiling in UNICODE mode. DWORD is just a typedef for an unsigned long. These typedefs are declared when you #include <windows.h>.
Here is an example for using the function getcwd:
#include <stdio.h>
#include <direct.h>
// Microsoft wants us to use _getcwd instead of getcwd, which breaks POSIX
// compatibility. See the following link for more information:
// https://stackoverflow.com/questions/7582394/strdup-or-strdup
// Therefore we must disable the compiler warning if we want to use getcwd
// to maintain POSIX compatibility. This is accomplished with the following
// line.
#pragma warning(disable : 4996)
// We can't use the constant MAX_PATH in this program because it is not
// defined. This is because we have not included windows.h. Since MAX_PATH
// has the value 260, we will use that value.
#define BUF_SIZE 260
int main()
{
char buffer[BUF_SIZE];
char *p;
p = getcwd( buffer, BUF_SIZE );
if ( p == NULL )
{
//TODO: handle error
}
printf( "The current directory is: %s", buffer );
}
In contrast to the function GetCurrentDirectory, the functions getcwd and _getcwd allow you to pass NULL as the buffer parameter. In that case, it will allocate the memory for you with malloc and return a pointer to that memory buffer. Therefore, you must call free when you are finished with the buffer, to prevent a memory leak. Here is an example:
#include <stdio.h>
#include <stdlib.h>
#include <direct.h>
// Microsoft wants us to use _getcwd instead of getcwd, which breaks POSIX
// compatibility. See the following link for more information:
// https://stackoverflow.com/questions/7582394/strdup-or-strdup
// Therefore we must disable the compiler warning if we want to use getcwd
// to maintain POSIX compatibility. This is accomplished with the following
// line.
#pragma warning(disable : 4996)
int main()
{
char *p;
p = getcwd( NULL, 0 );
if ( p == NULL )
{
//TODO: handle error
}
printf( "The current directory is: %s", p );
free( p );
}
I'm trying to use the snprintf function which based on the manual I've read is apart of the <stdio.h> header however I'm getting an error that it's been implicitly declared. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct users {
char* user_id;
};
typedef struct users users_t;
int save_user_detail(user_t);
int main() {
users_t users;
save_user_detail(users);
return 0;
}
int save_user_detail(users_t users)
{
printf("Type the filename = ");
scanf("%s", users.user_id);
char* extension = ".txt";
char fileSpec[strlen(users.user_id)+strlen(extension)+1];
FILE *file;
snprintf(fileSpec, sizeof(fileSpec), "%s%s", users.user_id, extension);
file = fopen(fileSpec, "w");
if(file==NULL)
{
printf("Error: can't open file.\n");
return 1;
}
else
{
printf("File written successfully.\n");
fprintf(file, "WORKS!\r\n");
}
fclose(file);
return 0;
}
You seem to be using gcc, but this compiler does not necessarily use the glibc, which is compliant with the C Standard and supports snprintf.
On the Windows architecture, you may be using the Microsoft C library, which in older versions did not have snprintf or renamed it as _snprintf.
Here are 2 ways you can try a work around your problem:
try using _snprintf instead of snprintf.
define snprintf manually after including <stdio.h> as
int snprintf(char *buf, size_t size, const char *fmt, ...);
The compiler should stop complaining about the missing prototype and if the runtime library does have a symbol for snprintf with a matching calling convention, it will link to it and the program should behave as expected.
I have read a little and learned a little about syestem function in c.
So, assuming I have a bash file ./some.sh that takes three arguments how should I make this code work? It will not compile with an error about the buffer.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
snprintf(buffer, sizeof(buffer), "/bin/bash ./some.sh %s %s %s", target1, target2, target3);
system(buffer)
}
Slightly more complicated, but safer, would be to avoid system and explicitly call fork and some version of exec (below, I use execl). (Error handling omitted for simplicity.) This avoids the need to ensure that each argument is correctly quoted for creating a shell command line.
int main(void) {
// ...
if (fork() == 0) {
execl("/bin/bash", "./some.sh", target1, target2, target3, (char *)0);
}
}
(Note: the approach is sound; my actual C implementation may leave something to be desired.)
You've used the buffer variable without actually declaring it. This works in Bash, but not in C. If you wanted buffer to be an array of characters, say, 1024 characters long, you could write:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char buffer[1024];
snprintf(buffer, sizeof(buffer), "/bin/bash ./some.sh %s %s %s", target1, target2, target3);
system(buffer)
}
(Of course, target1, target2 and target3 have to exist as well, but I assume those are placeholder names.)
I have attempted to make a script that creates a file and then sets it as immutable similar to the chattr +i command for linux. The script compiles (with gcc), runs and the file is created. However the file itself is not immutable and can be removed with a simple rm -f. I have attempted to stacktrace where chattr is called and I found a function called ioctl. I then used what little information I could gather and came up with what I have below. I narrowed it down from ext2_fs.h but it just doesn't seem to work. I've clearly overlooked something.
Updates to previous entry: Compiles but returns -1 on ioctl() function. Bad address shown with perror().
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
fp = fopen("/shovel.txt", "w+");
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
ioctl(fileno(fp), FS_IOC_SETFLAGS, 0x00000010);
fclose(fp);
}
Any help appreciated.
You are using the right ioctl command, but you're passing it the wrong arguments.
The manpage for ioctl_list(2) shows that FS_IOC_SETFLAGS expects to receive a pointer to int (an int *), yet you're passing it an integer literal (hence the Bad Address error).
The fact that you don't to any error checking whatsoever is also not helping.
The correct flag to pass to FS_IOC_SETFLAGS is a pointer holding the value EXT2_IMMUTABLE_FL, which is defined in ext2fs/ext2_fs.h (some older / different Linux distributions seem to have it under linux/ext2_fs.h), so you'll need to #include <ext2fs/etx2_fs.h>. Make sure to install e2fslibs-dev (and probably you'll need linux-headers too).
This code is working:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <ext2fs/ext2_fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
if ((fp = fopen("shovel.txt", "w+")) == NULL) {
perror("fopen(3) error");
exit(EXIT_FAILURE);
}
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
int val = EXT2_IMMUTABLE_FL;
if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
perror("ioctl(2) error");
fclose(fp);
return 0;
}
Remember to run this as root.
UPDATE:
As Giuseppe Guerrini suggests in his answer, you might want to use FS_IMMUTABLE_FL instead, and you won't need to include ext2_fs.h:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
int main()
{
FILE *fp;
char shovel[16] = "I have a shovel!";
if ((fp = fopen("shovel.txt", "w+")) == NULL) {
perror("fopen(3) error");
exit(EXIT_FAILURE);
}
fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
int val = FS_IMMUTABLE_FL;
if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
perror("ioctl(2) error");
fclose(fp);
return 0;
}
The main problem is that the ioctl wants a pointer to the mask, not a direct constant. You have to define a int variable, store the mask (0x10) in it and pass its address as third argument of ioctl.
Also, I'd add some hints:
other programs to change attributes are used to use low-level I/O directly (open, close...). Also, the file is usually opened with O_RDONLY.
Use FS_IMMUTABLE_FL istead the raw constant.
Get the current attribute mask first (FS_IOC_SETFLAGS) and mask it with the new flag, so other settings are not lost by the service.