mkstemp() and fdopen() in Cygwin 1.7.28
c, c++, cygwin, gcc
Solution
When compiling with `g++` 4.8.2 on Cygwin, I logged the expansion of macros in three cases:
$ g++ -std=c++11 -E -Dd foo.cpp > foo.log.c++11
$ g++ -ansi -E -Dd foo.cpp > foo.log.ansi
$ g++ -E -Dd foo.cpp > foo.log.noFlag
Comparing the logs was useful. There were "holes" in the `-std=c++11` and `-ansi` cases, while a block containing the `mkstemp()` declaration shows up in the "flagless" case. This let me zero in on the parts of the headers that were processed differently.
In the file `/usr/include/stdlib.h`, declarations of `mkstemp()` and some other functions are rejected if `__STRICT_ANSI__` is defined — such as when we use the compile-time flags `-ansi` and `-std=c++11`.
Likewise, in the file `/usr/include/stdio.h`, declarations of `fdopen()` will get skipped for the same reason.
The C++ headers `<cstdlib>` and `<cstdio>` both include the `stdlib.h` and `stdio.h` headers and leave declaration of those two functions (among others) up to those two headers. So if we use `-ansi` and/or `-std=c++11` then those two functions will not be declared and we get the compile errors.
The solution that seems to work for the toy code samples is to undefine `__STRICT_ANSI__` before compiling:
$ g++ -std=c++11 -U__STRICT_ANSI__ foo.cpp
It's not clear what the side effects of this will be, but from googling, it seems like this is a common problem and a common fix applied by other developers who need to target Cygwin.
Problem
In the course of building some C++-based code under Cygwin (1.7.28-2, 64-bit) with GNU GCC 4.8.2, I ran into the following errors: ``` ... SortDetails.cpp: In function ‘FILE* create_tmpfile(const char*, char**)’: SortDetails.cpp:127:20: error: ‘mkstemp’ was not declared in this scope fd = mkstemp(tmpl); ^ SortDetails.cpp:133:24: error: ‘fdopen’ was not declared in this scope fp = fdopen(fd, "wb+"); ... ``` The specific chunk of code that fails to compile is: ``` FILE * create_tmpfile(char const* path, char** fileName) { FILE* fp; int fd; char* tmpl; if ( path == NULL ) { fileName = NULL; return tmpfile(); } tmpl = (char*)malloc(1 + strlen(path) + L_tmpnam); strcpy(tmpl, path); strcpy(tmpl+strlen(path), "/sb.XXXXXX"); fd = mkstemp(tmpl); /* <----- here... */ if(fd == -1) { fprintf(stderr, "unable to create temp file!\n"); return NULL; } fp = fdopen(fd, "wb+"); /* <----- ...and here */ *fileName = (char*)malloc(strlen(tmpl) + 1); strcpy(*fileName, tmpl); free(tmpl); return fp; } ``` (The results of `malloc` are being cast because this code is within a larger C++-based project.) Regression This code compiles and works successfully with GNU GCC 4.8.x on Linux hosts and with Clang/++ 5.0 under OS X. Environment I am using the following version of Cygwin: ``` $ uname -a CYGWIN_NT-6.1 CygFoo-PC 1.7.28(0.271/5/3) 2014-02-09 21:06 x86_64 Cygwin ``` Here is the version of GCC I am using: ``` $ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-pc-cygwin/4.8.2/lto-wrapper.exe Target: x86_64-pc-cygwin Configured with: /cygdrive/i/szsz/tmpp/cygwin64/gcc/gcc-4.8.2-2/src/gcc-4.8.2/configure --srcdir=/cygdrive/i/szsz/tmpp/cygwin64/gcc/gcc-4.8.2-2/src/gcc-4.8.2 --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/libexec --datadir=/usr/share --localstatedir=/var --sysconfdir=/etc --libdir=/usr/lib --datarootdir=/usr/share --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --disable-__cxa_atexit --with-dwarf2 --with-tune=generic --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-graphite --enable-threads=posix --enable-libatomic --enable-libgomp --disable-libitm --enable-libquadmath --enable-math-support --enable-libssp --enable-libada --enable-libgcj-sublibs --disable-java-awt --disable-symvers --with-ecj-jar=/usr/share/java/ecj.jar --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib Thread model: posix gcc version 4.8.2 (GCC) ``` Questions Is there support for `mkstemp()` and `fdopen()` in GCC 4.8.2 for Cygwin? If not, is there a package I can add or a library I can relatively easily compile to add support for these functions? If not, are there alternatives to `mkstemp()` and `fdopen()` that I can make use of to replicate their functionality under Cygwin? Possible fix Here's a modified version of this function: ``` FILE * create_tmpfile(char const* path, char** fileName) { FILE* fp; char* tmpl; if ( path == NULL ) { fileName = NULL; return tmpfile(); } #if defined(__CYGWIN__) && !defined(_WIN32) const char *cygwinPrefix = "/sb."; const char *cygwinTmpDir = "/tmp"; char *cygwinTmplSuffix = (char *)malloc(1 + L_tmpnam); tmpnam(cygwinTmplSuffix); tmpl = (char *)malloc(1 + strlen(path) + strlen(cygwinPrefix) + strlen(cygwinTmplSuffix + strlen(cygwinTmpDir) + 1)); strcpy(tmpl, path); strcpy(tmpl+strlen(path), cygwinPrefix); strcpy(tmpl+strlen(path)+strlen(cygwinPrefix), cygwinTmplSuffix + strlen(cygwinTmpDir) + 1); fp = fopen(tmpl, "wbx+"); /* we add the 'x' extension to apply the O_EXCL flag, to avoid a security hole described in the GNU C library docs */ free(cygwinTmplSuffix); #else tmpl = (char*)malloc(1 + strlen(path) + L_tmpnam); strcpy(tmpl, path); strcpy(tmpl+strlen(path), "/sb.XXXXXX"); int fd = mkstemp(tmpl); if(fd == -1) { fprintf(stderr, "unable to create temp file!\n"); return NULL; } fp = fdopen(fd, "wb+"); #endif *fileName = (char*)malloc(strlen(tmpl) + 1); strcpy(*fileName, tmpl); free(tmpl); return fp; } ``` This is pretty ugly. If there is a way to use POSIX functions, I'd like to use them, if I can. Thanks for any advice.