123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
/*******************************************************************************

        copyright:      Copyright (c) 2009 Kris Bell. All rights reserved

        license:        BSD style: $(LICENSE)

        version:        rewritten: Nov 2009

        Various low-level console oriented utilities

*******************************************************************************/

module tango.core.util.console;

private import tango.core.util.string;

/*******************************************************************************
        
        External functions

*******************************************************************************/

version (Windows) 
        {
        enum {UTF8 = 65001};
        private extern (Windows) int GetStdHandle (int);
        private extern (Windows) int WriteFile (int, in char*, int, int*, void*);
        private extern (Windows) bool GetConsoleMode (int, int*);
        private extern (Windows) bool WriteConsoleW (int, in wchar*, int, int*, void*);
        private extern (Windows) int MultiByteToWideChar (int, int, in char*, int, wchar*, int);
        } 
else 
version (Posix)
         extern(C) ptrdiff_t write (int, in void*, size_t);


/*******************************************************************************
        
        Emit an integer to the console

*******************************************************************************/

extern(C) void consoleInteger (ulong i)
{
        char[25] tmp = void;

        consoleString (ulongToUtf8 (tmp, i));
}

/*******************************************************************************

        Emit a utf8 string to the console. Codepages are not supported

*******************************************************************************/

extern(C) void consoleString (const(char)[] s)
{
        version (Windows)
                {
                int  mode, count;
                auto handle = GetStdHandle (0xfffffff5);

                if (handle != -1 && GetConsoleMode (handle, &mode))
                   {
                   wchar[512] utf;
                   while (s.length)
                         {
                         // crop to last complete utf8 sequence
                         auto t = crop (s[0 .. (s.length > utf.length) ? utf.length : s.length]);

                         // convert into output buffer and emit
                         auto i = MultiByteToWideChar (UTF8, 0, s.ptr, t.length, utf.ptr, utf.length);
                         WriteConsoleW (handle, utf.ptr, i, &count, null);

                         // process next chunk
                         s = s [t.length .. $];
                         }
                   }
                else
                   // output is probably redirected (we assume utf8 output)
                   WriteFile (handle, s.ptr, s.length, &count, null);
                }

        version (Posix)
                 write (2, s.ptr, s.length);
}

/*******************************************************************************

        Support for chained console (pseudo formatting) output

*******************************************************************************/

struct Console
{
        alias newline opCall;
        alias emit    opCall;
    
        /// emit a utf8 string to the console
        Console emit (const(char)[] s)
        {
                consoleString (s);
                return this;
        }

        /// emit an unsigned integer to the console
        Console emit (ulong i)
        {
                consoleInteger (i);
                return this;
        }

        /// emit a newline to the console
        Console newline ()
        {
                version (Windows)
                         const eol = "\r\n";
                version (Posix)
                         const eol = "\n";

                return emit (eol);
        }
}

public Console console;


version (Windows)
{
/*******************************************************************************

        Adjust the content such that no partial encodings exist on the 
        right side of the provided text.

        Returns a slice of the input

*******************************************************************************/

private inout(char)[] crop (inout(char)[] s)
{
        if (s.length)
           {
           auto i = s.length - 1;
           while (i && (s[i] & 0x80))
                  if ((s[i] & 0xc0) is 0xc0)
                     {
                     // located the first byte of a sequence
                     ubyte b = s[i];
                     int d = s.length - i;

                     // is it a 3 byte sequence?
                     if (b & 0x20)
                         --d;
   
                     // or a four byte sequence?
                     if (b & 0x10)
                         --d;

                     // is the sequence complete?
                     if (d is 2)
                         i = s.length;
                     return s [0..i];
                     }
                  else 
                     --i;
           }
        return s;
}
}


/*******************************************************************************
        
*******************************************************************************/

debug (console)
{
        void main()
        {
                console ("hello world \u263a")();
        }
}