mardi 8 octobre 2013

C++ Command Line using standard input/output.

As programmer you may be familiar with a lot a program design to use input file and/or input stream using the standard input. It’s one of the powerful aspect of tools like grep, sed, etc… And if you want to implement such command-line tool by yourself you may meet one big issue.

Windows vs. Linux.

As often, we would write our code in a portable way and just create a simple while loop like
while(!feof(stdin)) 
  {
    size_t bytes = fread(bufferInput, 1, m_blocksize, stdin);

    // ....
  }

but here you are face to a piece of code dependent of the platform you target, let me explain.


  • On Linux stdin and stdout are by default open using the binary mode.

  • On the other side on Windows they will be open using the text mode.

because of that, the feof and all the fread, fgets function can have different output on the 2 systems. For example, on Windows, reading using the normal mode will convert \r\n (Windows newline) to the single character ASCII 10, instead of returning 2 bytes under Linux !

To avoid that pitfall, you can use 2 approach:


  • freopen, portable and easy to put before using stdin

  • or _setmode/setmode available under Windows and also Linux/GCC under certain condition.

freopen:


Basically, the best thing you should do is this:

freopen(NULL, "rb", stdin);

This will reopen stdin using the binary mode, so under Linux it will change nothing, and on Windows, 
it will avoid abnormal character interpretation (premature end-of-stream, etc…). But take care of the 
compiler you use because depending of the freopen spec you read the behavior when passing path 
equal to NULL may be different... see the 2 following description:

setmode:

That method is less portable and may require some addition to your GCC environment.
With VS for example, you can call _setmode (require fcntl.h and io.h)

  _setmode(_fileno(stdin), _O_BINARY);
  _setmode(_fileno(stdout), _O_BINARY);

But under linux, the setmode isn’t a standard lib function, it’s a libbsd function. So you have to include <bsd/unistd.h> and link with –lbsd.

Conclusion


If you have to write a multi-platform command line tool using stdin/stdout the most portable way to do it is the freopen solution!

Aucun commentaire :

Enregistrer un commentaire