关于Cygwin 1.7.28中的c:mkstemp()和fdopen()

 2021-04-09 

mkstemp() and fdopen() in Cygwin 1.7.28

在使用GNU GCC 4.8.2在Cygwin(1.7.28-2,64位)下构建一些基于C的代码的过程中,遇到了以下错误:

1
2
3
4
5
6
7
8
...
SortDetails.cpp: In function a€?FILE* create_tmpfile(const char*, char**)a€?:
SortDetails.cpp:127:20: error: a€?mkstempa€? was not declared in this scope
   fd = mkstemp(tmpl);
                    ^
SortDetails.cpp:133:24: error: a€?fdopena€? was not declared in this scope
   fp = fdopen(fd,"wb+");
...

无法编译的特定代码块是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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!\
"
);
          return NULL;
      }
  fp = fdopen(fd,"wb+");                    /* <----- ...and here */
  *fileName = (char*)malloc(strlen(tmpl) + 1);
  strcpy(*fileName, tmpl);
  free(tmpl);
  return fp;
}

(正在强制转换malloc的结果,因为此代码在基于C的较大项目中。)

回归

此代码可在Linux主机上的GNU GCC 4.8.x以及OS X下的Clang / 5.0编译并成功使用。

环境

我正在使用以下版本的Cygwin:

1
2
$ uname -a
CYGWIN_NT-6.1 CygFoo-PC 1.7.28(0.271/5/3) 2014-02-09 21:06 x86_64 Cygwin

这是我使用的GCC版本:

1
2
3
4
5
6
7
8
$ 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)

问题

  • 在GCC 4.8.2中是否为Cygwin支持mkstemp()fdopen()

  • 如果没有,是否有我可以添加的程序包或我可以相对容易地编译以添加对这些功能的支持的库?

  • 如果没有,我可以使用mkstemp()fdopen()的替代品来复制其在Cygwin下的功能吗?

  • 可能的解决方法

    这是此功能的修改版本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    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!\
    "
    );
              return NULL;
          }
      fp = fdopen(fd,"wb+");
    #endif
      *fileName = (char*)malloc(strlen(tmpl) + 1);
      strcpy(*fileName, tmpl);
      free(tmpl);
      return fp;
    }

    这很丑。如果可以使用POSIX函数,请尽量使用它们。感谢您的任何建议。


    在Cygwin上使用g++ 4.8.2进行编译时,在三种情况下记录了宏的扩展:

    1
    2
    3
    $ 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

    比较日志很有用。在-std=c++11-ansi情况下存在"漏洞",而在"无旗标"情况下出现包含mkstemp()声明的块。这让我可以对标头中经过不同处理的部分归零。

    在文件/usr/include/stdlib.h中,如果定义了__STRICT_ANSI__,则拒绝mkstemp()的声明和其他一些函数,例如当我们使用编译时标志-ansi-std=c++11时。 >

    同样,在文件/usr/include/stdio.h中,出于相同的原因也会跳过fdopen()的声明。

    C头文件<cstdlib><cstdio>都包含stdlib.hstdio.h头文件,并将这两个函数(以及其他函数)的声明保留为这两个头文件。因此,如果我们使用-ansi和/或-std=c++11,则不会声明这两个函数,并且会出现编译错误。

    似乎适用于玩具代码示例的解决方案是在编译之前取消定义__STRICT_ANSI__

    1
    $ g++ -std=c++11 -U__STRICT_ANSI__ foo.cpp

    目前尚不清楚它的副作用是什么,但是从谷歌搜索来看,这似乎是一个常见问题,并且是其他需要针对Cygwin的开发人员所应用的常见修复程序。


    Cygwin具有一组类似Linux的功能测试宏。但是,在具有C的Linux上,_GNU_SOURCE是无条件定义的,实质上消除了所有此类防护。在Cygwin上,我们不这样做,这意味着您实际上还必须尊重C上各种标志的含义。

    如前所述,使用任何-std=c++*标志将定义__STRICT_ANSI__,这些宏可以识别。在命令行上取消定义不正确。而是,为要使用的功能定义正确的文档标记(在这种情况下,-D_POSIX_C_SOURCE=200809L应该涵盖这两个标记),或者改用-std=gnu++*标志(顺便说一句,不要定义_GNU_SOURCE)不声明符合ANSI。


    最近我在编译git-crypt并遇到相同的问题。上面答案中的解决方案有效,除了必须通过" make"而不是" g"进行部署,如下所示:

    1
    make CXXFLAGS="-U__STRICT_ANSI__ -std=c++11"

    我的朋友Vincent制作了一个与Cygwin一起使用的简单版本。 http://code.google.com/p/xpost/source/browse/src/lib/xpost_compat.c#113

    它可能无法涵盖所有??情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/stat.h>

    # include <windows.h>
    # include <io.h>

    int mkstemp(char *template)
    {
        char *temp;

        temp = _mktemp(template);
        if (!temp)
            return -1;

        return _open(temp, _O_CREAT | _O_TEMPORARY | _O_EXCL | _O_RDWR, _S_IREAD | _S_IWRITE);
    }

    对于fdopen,我不我必须做一些研究/思考。