Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Copy of Console.d

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003         @file Console.d
00004         
00005         Copyright (c) 2004 Kris Bell
00006         
00007         This software is provided 'as-is', without any express or implied
00008         warranty. In no event will the authors be held liable for damages
00009         of any kind arising from the use of this software.
00010         
00011         Permission is hereby granted to anyone to use this software for any 
00012         purpose, including commercial applications, and to alter it and/or 
00013         redistribute it freely, subject to the following restrictions:
00014         
00015         1. The origin of this software must not be misrepresented; you must 
00016            not claim that you wrote the original software. If you use this 
00017            software in a product, an acknowledgment within documentation of 
00018            said product would be appreciated but is not required.
00019 
00020         2. Altered source versions must be plainly marked as such, and must 
00021            not be misrepresented as being the original software.
00022 
00023         3. This notice may not be removed or altered from any distribution
00024            of the source.
00025 
00026         4. Derivative works are permitted, but they must carry this notice
00027            in full and credit the original source.
00028 
00029 
00030                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00031 
00032        
00033         @version        Initial version, Feb 2005      
00034         @author         Kris
00035 
00036 
00037 *******************************************************************************/
00038 
00039 module mango.io.Console;
00040 
00041 private import  mango.sys.OS;
00042 
00043 private import  mango.convert.Type;
00044 
00045 private import  mango.io.Buffer,
00046                 mango.io.ConduitStyle,
00047                 mango.io.DeviceConduit;
00048 
00049 
00050 /*******************************************************************************
00051 
00052         Bring in native Windows functions
00053 
00054 *******************************************************************************/
00055 
00056 version (Win32)
00057 {
00058         private extern (Windows) 
00059         {
00060                 HANDLE GetStdHandle        (DWORD);
00061                 DWORD  GetConsoleMode      (HANDLE, LPDWORD);
00062                 BOOL   WriteConsoleW       (HANDLE, VOID*, DWORD, LPDWORD, LPVOID);
00063                 int    MultiByteToWideChar (UINT, DWORD, LPCSTR, int, LPWSTR, int);
00064         }
00065 }
00066 
00067 
00068 
00069 /*******************************************************************************
00070 
00071         low level console IO support. 
00072         
00073         Redirecting the standard IO handles (via a shell) operates as one 
00074         would expect.
00075 
00076 *******************************************************************************/
00077 
00078 struct Console 
00079 {
00080         /**********************************************************************
00081 
00082         **********************************************************************/
00083 
00084         class Input : Buffer
00085         {
00086                 ConsoleConduit conduit;
00087 
00088                 this (FileDevice device)
00089                 {
00090                         super (1024);
00091                         setConduit (conduit = new ConsoleConduit (device));
00092                             
00093                 }
00094 
00095                 int opCall(inout char[] x)
00096                 {
00097                         get (0);
00098                         x = cast(char[]) get (readable());
00099                         return x.length;
00100                 }
00101         }
00102 
00103         /**********************************************************************
00104 
00105         **********************************************************************/
00106 
00107         class Output : Buffer
00108         {
00109                 ConsoleConduit conduit;
00110 
00111                 this (FileDevice device)
00112                 {
00113                         super (1024 * 8);
00114                         setConduit (conduit = new ConsoleConduit (device));        
00115                 }
00116 
00117                 int opCall (char[] x)
00118                 {
00119                         int ret = sink (x, Type.Utf8);
00120                         flush();
00121                         return ret;
00122                 }
00123 
00124                 protected int sink (void[] x, int type)
00125                 {
00126                         return conduit.write (x);
00127                 }
00128         }
00129 
00130 
00131         /***********************************************************************
00132 
00133                 Conduit for specifically handling the console devices. This 
00134                 takes care of certain implementation details on the Win32 
00135                 platform.
00136 
00137         ***********************************************************************/
00138 
00139         class ConsoleConduit : DeviceConduit
00140         {
00141                 // expose conduit.copy() methods also 
00142                 alias DeviceConduit.copy  copy;
00143                 alias DeviceConduit.read  read;
00144                 alias DeviceConduit.write write;
00145 
00146                 // should we transcode output? 
00147                 private bool transcode = false;
00148 
00149                 /***************************************************************
00150 
00151                         Create a FileConduit on the provided FileDevice. 
00152 
00153                         This is strictly for adapting existing devices such 
00154                         as Stdout and friends.
00155 
00156                 ***************************************************************/
00157 
00158                 package this (FileDevice device)
00159                 {
00160                         super (device);
00161 
00162                         version (Win32)
00163                                 {
00164                                 buffer = new wchar[128];
00165                                 setTranscode (true);
00166                                 }
00167                 }    
00168 
00169                 /***************************************************************
00170 
00171                         Enable or disable transcoding for Win32 console. This 
00172                         is not required on linux, since it is utf8 throughout.
00173 
00174                         When Win32 stdout is redirected, the content is *not*
00175                         transcoded, since the unicode functions fail in such
00176                         cases. Instead, the content is emitted raw.
00177 
00178                 ***************************************************************/
00179 
00180                 public void setTranscode (bool enabled)
00181                 {
00182                         this.transcode = enabled;
00183                 }
00184 
00185                 /***************************************************************
00186 
00187                         Return the name of this device
00188 
00189                 ***************************************************************/
00190 
00191                 protected char[] getName()
00192                 {
00193                         return "console device";
00194                 }
00195 
00196                 /***************************************************************
00197 
00198                         Windows-specific code
00199 
00200                 ***************************************************************/
00201 
00202                 version(Win32)
00203                 {
00204                         private wchar[] buffer;
00205 
00206                         enum {FILE_TYPE_CHAR=2, CP_UTF8=65001};
00207 
00208                         /*******************************************************
00209 
00210                                 Gain access to the standard IO handles 
00211 
00212                         *******************************************************/
00213 
00214                         protected override void reopen (FileDevice device)
00215                         {
00216                                 static const DWORD[] id = [
00217                                                           cast(DWORD) -10, 
00218                                                           cast(DWORD) -11, 
00219                                                           cast(DWORD) -12
00220                                                           ];
00221                                 static const char[][] f = [
00222                                                           "CONIN$\0", 
00223                                                           "CONOUT$\0", 
00224                                                           "CONOUT$\0"
00225                                                           ];
00226 
00227                                 assert (device.id < 3);
00228                                 handle = GetStdHandle (id[device.id]);
00229                                 if (! handle)
00230                                       handle = CreateFileA (f[device.id], 
00231                                                GENERIC_READ | GENERIC_WRITE,  
00232                                                FILE_SHARE_READ | FILE_SHARE_WRITE, 
00233                                                null, OPEN_EXISTING, 0, null);
00234                                 if (! handle)
00235                                       error ();
00236 
00237                                 // turn off transcoding if redirecting
00238                                 DWORD mode;
00239                                 if (! GetConsoleMode (handle, &mode))
00240                                       setTranscode (false);
00241                         }
00242 
00243                         /*******************************************************
00244 
00245                                 Write a chunk of bytes to the file from the 
00246                                 provided array (typically that belonging to 
00247                                 an IBuffer)
00248 
00249                         *******************************************************/
00250 
00251                         override int write (void[] src)
00252                         {
00253                                 void* p = src.ptr;
00254                                 DWORD len = src.length;
00255                                 const int Max = 1024 * 8;
00256 
00257                                 // skip if nothing to do; avoids toWideChar() failure
00258                                 if (len is 0)
00259                                     return 0;
00260 
00261                                 // Win32 console has problems with content over
00262                                 // 32KB in length, so handle it specifically. We
00263                                 // may also need to transcode on the fly
00264                                 do {
00265                                    DWORD i = len;
00266                                    if (i > Max)
00267                                        i = Max;
00268 
00269                                    version (Win32SansUnicode)
00270                                    {
00271                                    // just emit the content as it is
00272                                    if (! WriteFile (handle, p, i, &i, null))
00273                                          error ();
00274                                    } 
00275                                    else
00276                                    {
00277                                    if (transcode)
00278                                       {
00279                                       // grow buffer as required
00280                                       if (buffer.length < i)
00281                                           buffer.length = i;
00282 
00283                                       // convert to utf16 ...
00284                                       uint j = MultiByteToWideChar (CP_UTF8, 0, cast(char*)p, i, 
00285                                                                     buffer.ptr, buffer.length);
00286                                       if (j is 0)
00287                                           error();
00288 
00289                                       // ensure all utf16 is emitted
00290                                       uint k;
00291                                       do {
00292                                          if (! WriteConsoleW (handle, &buffer[k], j, &k, null))
00293                                                error();
00294                                          } while (j -= k);
00295 
00296                                       }
00297                                    else
00298                                       // just emit the content as it is
00299                                       if (! WriteFile (handle, p, i, &i, null))
00300                                             error ();
00301                                    }
00302 
00303                                    len -= i;
00304                                    p += i;
00305                                    } while (len);
00306 
00307                                 return src.length;
00308                         }
00309                 }
00310         }
00311 }
00312 
00313 /******************************************************************************
00314 
00315 ******************************************************************************/
00316 
00317 static Console.Input    Cin;
00318 static Console.Output   Cout, 
00319                         Cerr;
00320 
00321 static this ()
00322 {
00323         Cin  = new Console.Input  (new FileDevice (0, ConduitStyle.Access.Read));
00324         Cout = new Console.Output (new FileDevice (1, ConduitStyle.Access.Write));
00325         Cerr = new Console.Output (new FileDevice (2, ConduitStyle.Access.Write));
00326 }

Generated on Sat Dec 24 17:28:32 2005 for Mango by  doxygen 1.4.0