Portable way to put stdout in binary mode

2019-04-10 05:06发布

I'm writing C programs used as cgi's that generate and output gif images. They're used in HTML pages with tags of the form <img src="/cgi-bin/gifprogram.cgi?param=val&etc">, where the query string params describe the image to be generated by the cgi.

C program cgi's invoked like this emit their output to stdout. And in this case the output's a gif image containing lots of binary (non-printable) bytes. That's already working fine on Unix/Linux. But Windows apparently requires a separate non-portable _setmode(_fileno(stdout),_O_BINARY) call (and some Windows-specific #include's). Otherwise you get the usual cr/lf problem, i.e., every 0x0A is preceded by a spurious 0x0D. And that doesn't look good in a gif :(

Is there any portable way around this problem, using ANSI-standard C without any platform-specific syntax, and without a lot of #ifdef stuff to try to detect a Windows compile environment? Moreover, some Windows compilers apparently use _setmode(_fileno(stdout),_O_BINARY) while others use setmode(fileno(stdout),O_BINARY), which I also have to try to detect.

>>Edit<< In reply to comments...

Thanks, guys. I'll apparently have to take some otherwise completely posix-compliant/portable programs, and goof around with them just for windows [n.b., JoNaThAn: really, no need to edit to capitalize that:), -- my personal writing style is intentionally grammatically loose].

Below is a first cut at some of the goofiness I'm considering introducing. Seems to work, as far as I can tell, using mingw. Is it sufficient, i.e., works for all-or-most other compilers? And is it necessary, i.e., any short-and-neat/quick-and-dirty way to accomplish the same thing with fewer/more-readable lines of code?

/* ---
 * windows-specific header info
 * ---------------------------- */
#ifndef WINDOWS                 /* -DWINDOWS not supplied by user */
  #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
  ||  defined(DJGPP)            /* try to recognize windows compilers */ \
  ||  defined(_USRDLL)          /* must be WINDOWS if compiling for DLL */
    #define WINDOWS             /* signal windows */
  #endif
#endif
#ifdef WINDOWS                  /* Windows opens stdout in char mode, and */
  #include <fcntl.h>            /* precedes every 0x0A with spurious 0x0D.*/
  #include <io.h>               /* So emitcache() issues a Win _setmode() */
                                /* call to put stdout in binary mode. */
  #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
    #define O_BINARY _O_BINARY  /* make O_BINARY available, etc... */
    #define setmode  _setmode
    #define fileno   _fileno
  #endif
  #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
    #define HAVE_SETMODE        /* so we'll use setmode() */
  #endif
  #if defined(_MSC_VER) && defined(_DEBUG) /* MS VC++ in debug mode */
    /* to show source file and line numbers where memory leaks occur... */
    #define _CRTDBG_MAP_ALLOC   /* ...include this debug macro */
    #include <crtdbg.h>         /* and this debug library */
  #endif
  #define ISWINDOWS 1
#else
  #define ISWINDOWS 0
#endif

And the subsequent corresponding code to make sure stdout is in binary mode is

    #if ISWINDOWS                           /* compiling for win... */
      #ifdef HAVE_SETMODE                   /* try to use setmode()*/
        if ( setmode ( fileno (stdout), O_BINARY) /* to set stdout */
        == -1 ) /* handle error here*/ ;    /* to binary mode */
      #else                                 /* setmode not available */
        #if 1                               /* so try this...*/
          freopen ("CON", "wb", stdout);    /* freopen stdout binary */
        #else                               /* or maybe this... */
          stdout = fdopen (STDOUT_FILENO, "wb"); /*fdopen stdout binary*/
        #endif                              /* done */
      #endif                                /* " */
    #endif                                  /* " */

You'll note I'm trying to suggest setmode() alternatives. Though my alternative testing hasn't been successful, I've left the syntax in place for future reference, just in case some of it ultimately proves useful.

0条回答
登录 后发表回答