0

Apparently fdopen requires a POSIX/gnu99 using compilation standard. I am hoping to stay on C99.

I am running into this problem because I use open() to create a file descriptor and then (because I use fgets()) I need a FILE pointer which is generated via fdopen(). Unfortunately the fdopen() is not available in C99.

Is there a way to get the FILE structure from the descriptor that is C99 compliant?

Tyler Durden
  • 11,156
  • 9
  • 64
  • 126
  • 2
    *... I use `open()` ...* That's not C99 compliant, so why can't you use `fdopen()`? – Andrew Henle Mar 18 '23 at 14:19
  • @AndrewHenle I have no problem compiling open() with C99, but when I try compiling with fdopen() I get an error. – Tyler Durden Mar 18 '23 at 14:54
  • @TylerDurden, your particular compiler and compilation settings may result in calls to `open()` being accepted without any diagnostics, but the fact remains that `open()` is not specified by any version of the C language specification. It comes from the same place as `fdopen()`, POSIX. Indeed, the whole point of `fdopen()` is to bridge between POSIX `open()` and standard C streams. If it is an objective to avoid relying on POSIX, then you must avoid `open()`. And in that case, you have no use for `fdopen()`. – John Bollinger Mar 18 '23 at 14:59
  • 2
    Both use of `open` and use of `fdopen` are *conforming* to the C standard. Neither is *strictly conforming*. There is no use of `open` that conforms to the C standard in a way that `fdopen` does not. In other words, if you are using `open`, then using `fdopen` does not make your program any less conforming to the C standard. So the question is flawed. Either you choose to have a program that is not strictly conforming, and you can use both `open` and `fdopen`, or you choose to have a program that is strictly conforming, and you cannot use either… – Eric Postpischil Mar 18 '23 at 15:06
  • … The issue you have with getting some unstated “error” when compiling with `fdopen` that you do not with `open` is some issue of using the right headers with the right feature-test macro or of your compiling or linking configuration. It is not an issue of C99 compliance. – Eric Postpischil Mar 18 '23 at 15:07

3 Answers3

3

Is there a way to get the FILE structure from the descriptor that is C99 compliant?

If by "C99 compliant" you mean relying only on features of the C standard library specified by C99 then no. But that's ok, because there is no way to get or use a file descriptor in the first place if you rely only on the standard library of C99. Streams, managed via C FILE objects, are the only representation of files defined by C99.

On the other hand, if you're talking about limiting yourself to the syntax and semantics of C99, but not necessarily to standard library functions it specifies, then yes: use fdopen(). It serves exactly the purpose you describe.

Since you appear to be using gcc, perhaps the question arises because you want to compile with the -std=c99 option to disable language extensions, and you find that this also disables (or at least, does not enable) declaration of some functions, including fdopen(). This is because the -std option affects the default values of some of the standard feature test macros. The documentation for fdopen() says that it requires one of a variety feature-test macros: either _POSIX_C_SOURCE with a value greater than 0, or _XOPEN_SOURCE, or _POSIX_SOURCE. With -std=gnu99 you get settings of all of those that serve the purpose, but with -std=c99 you don't get definitions of any of them that will do the job.

If you want all the library features of the latest version of POSIX (that are provided by your glibc) then you should ensure that _POSIX_C_SOURCE is defined with value 200809L (as of POSIX.1-2017). Since such dependence is a characteristic of your source code, not of the compilation conditions, you should put an appropriate macro definition at the very beginning of your source files (that is, don't depend on compilation options for this). For example:

#ifdef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#define _POSIX_C_SOURCE 200809L

#include <stdio.h>
#include <fcntl.h>

// ...

The macro should be defined before any headers are #included.

You should then be able to compile with gcc -std=c99 and also have a declaration of fdopen().

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
2

The fdopen() function is part of POSIX, not part of C99. If you compile your code in strict C99 mode, like with -std=c99, you will not get it.

This is, of course, expected—the int file descriptors are not part of C99 either. The point of fdopen() is interoperability with POSIX file descriptors.

The way you get the function is by defining the correct “feature test macros” before including your headers. Do it at the top of your file:

#define _POSIX_C_SOURCE 1

#include <stdio.h>

// use fdopen() here

You can find information about these requirements in the man page:

https://linux.die.net/man/3/fdopen

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

fdopen(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

“C99-compliance” is a funny term. If you are on POSIX or a POSIX-like system (Linux, WSL), then you have int file descriptors and fdopen(), and everything will work fine. If you trying to write code that runs on any conforming C99 implementation, then you obviously can’t use fdopen()—and open() is not part of C99 either.

The reason you don’t need to do this for open() is because open() is exposed through <fcntl.h>, which is not part of C99.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • This doesn't answer the question. I will repeat the question: Is there a way to get the FILE structure from the descriptor that is C99 compliant? – Tyler Durden Mar 18 '23 at 14:58
  • @TylerDurden: The answer to that question is simple—it’s impossible. It is absolutely impossible to do what you are asking, in the precise way you’re asking it. – Dietrich Epp Mar 18 '23 at 15:02
  • 1
    @TylerDurden: The problem has a contradiction. You can’t `open()` in a portable way, and you can’t `fdopen()` either. If you want to stick to the C standard library, you have to open your files with `fopen()`, and you cannot use `open()` or `fdopen()`. If you use `open()`, then there’s no justification for avoiding `fdopen()`, since they’re part of the same API—the POSIX API. – Dietrich Epp Mar 18 '23 at 15:08
  • I don't want to necessarily make it portable, I just want it to compile under C99. As far as I can tell open() does compile under C99. – Tyler Durden Mar 18 '23 at 15:16
  • @TylerDurden, `open()` is not declared by any header defined by C99. Presumably you are getting its declaration from `fcntl.h`, which is not part of C99. – John Bollinger Mar 18 '23 at 15:38
  • @JohnBollinger That is fine. If it compiles, it compiles. As I said, I don't care about technical portability issues. I just want it to compile when I run the c99 command, and open() is doing that and fdopen() is not. – Tyler Durden Mar 18 '23 at 16:40
  • @TylerDurden: Yes, the way you do that is by using the feature check macros. Put `#define _POSIX_C_SOURCE 1` above your `#include` directives, and it will compile. – Dietrich Epp Mar 18 '23 at 17:07
  • Hmm, ok, I guess I need to study that further to understand how that works. Thanks. – Tyler Durden Mar 18 '23 at 17:35
0

Unfortunately the fdopen() is not available in C99.

Neither is open(). The question is self-contradictory. The system call open() is not specified in any version of the C standard (fopen() is), nor does C have the concept of file descriptors, it only deals with streams.

From the man page:

CONFORMING TO         
       open(), creat() SVr4, 4.3BSD, POSIX.1-2001, POSIX.1-2008.

So if your application is already using open(), there is no justification for avoiding fdopen(). — @Dietrich Epp

/* See @John's answer for the rationale. */
#define _POSIX_C_SOURCE 200809L

#include <stdio.h>                                                                                    │
#include <stdlib.h>                                                                                   │
#include <unistd.h>                                                                                   │
#include <fcntl.h>                                                                                    │
                                                                                                      │
int main (int argc, char **argv)                                                                      │
{                                                                                                     │
        if (argv[1]) {                                                                                │
                int fd = open (argv[1], O_RDONLY);                                                    │
                FILE *stream = fdopen (fd, "r");                                                      │
        }                                                                                             │
                                                                                                      │
        return EXIT_SUCCESS;                                                                          │
}

Compiled with:

gcc-10 -std=c99 open.c -o open

produces no more warnings.

Harith
  • 4,663
  • 1
  • 5
  • 20