00001 /******************************************************************************* 00002 00003 @file DeviceConduit.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; May 2005 00034 00035 @author Kris 00036 00037 *******************************************************************************/ 00038 00039 module mango.io.DeviceConduit; 00040 00041 private import mango.sys.OS, 00042 mango.sys.System; 00043 00044 public import mango.io.Buffer, 00045 mango.io.FileStyle; 00046 00047 private import mango.io.Conduit, 00048 mango.io.Exception, 00049 mango.io.ConduitStyle; 00050 00051 /******************************************************************************* 00052 00053 Bring in native Windows functions 00054 00055 *******************************************************************************/ 00056 00057 version (Win32) 00058 { 00059 private extern (Windows) 00060 { 00061 HANDLE GetStdHandle (DWORD); 00062 } 00063 } 00064 00065 00066 /******************************************************************************* 00067 00068 Implements a means of reading and writing a file device. Conduits 00069 are the primary means of accessing external data, and are usually 00070 routed through a Buffer. 00071 00072 *******************************************************************************/ 00073 00074 class DeviceConduit : Conduit 00075 { 00076 // expose conduit.copy() methods also 00077 alias Conduit.copy copy; 00078 alias Conduit.read read; 00079 alias Conduit.write write; 00080 00081 /*********************************************************************** 00082 00083 Construct a conduit with the given style and seek abilities. 00084 Conduits are either seekable or non-seekable. 00085 00086 ***********************************************************************/ 00087 00088 this (ConduitStyle style, bool seekable) 00089 { 00090 super (style, seekable); 00091 } 00092 00093 /*********************************************************************** 00094 00095 Create a FileConduit on the provided FileDevice. This is 00096 strictly for adapting existing devices such as Stdout and 00097 friends. 00098 00099 ***********************************************************************/ 00100 00101 this (FileDevice device) 00102 { 00103 // say we're not seekable 00104 super (device, false); 00105 00106 // open the file 00107 reopen (device); 00108 00109 // bump lock count 00110 acquire(); 00111 } 00112 00113 /*********************************************************************** 00114 00115 Callback to close the file. This is invoked from the Resource 00116 base-class when the resource is being closed. 00117 00118 ***********************************************************************/ 00119 00120 protected override void closure () 00121 { 00122 super.closure (); 00123 _close (); 00124 } 00125 00126 /*********************************************************************** 00127 00128 Create a Buffer of the default FileConduit size, and 00129 associate it with this conduit 00130 00131 ***********************************************************************/ 00132 00133 override IBuffer createBuffer() 00134 { 00135 const int DefaultFileBufferSize = 1024 * 16; 00136 00137 IBuffer buffer = new Buffer (DefaultFileBufferSize); 00138 00139 buffer.setConduit (this); 00140 return buffer; 00141 } 00142 00143 /*********************************************************************** 00144 00145 Return the name of this device 00146 00147 ***********************************************************************/ 00148 00149 protected char[] getName() 00150 { 00151 return "<device>"; 00152 } 00153 00154 00155 /*********************************************************************** 00156 00157 Windows-specific code 00158 00159 ***********************************************************************/ 00160 00161 version (Win32) 00162 { 00163 protected HANDLE handle; 00164 00165 /*************************************************************** 00166 00167 Throw an IOException noting the last error 00168 00169 ***************************************************************/ 00170 00171 protected void error () 00172 { 00173 throw new IOException (getName() ~ ": " ~ System.error); 00174 } 00175 00176 /*************************************************************** 00177 00178 Gain access to the standard IO handles (console etc). 00179 00180 ***************************************************************/ 00181 00182 protected void reopen (FileDevice device) 00183 { 00184 handle = cast(HANDLE) device.id; 00185 } 00186 00187 /*************************************************************** 00188 00189 Close the underlying file 00190 00191 ***************************************************************/ 00192 00193 protected void _close () 00194 { 00195 if (! CloseHandle (handle)) 00196 error (); 00197 } 00198 00199 /*************************************************************** 00200 00201 Read a chunk of bytes from the file into the provided 00202 array (typically that belonging to an IBuffer) 00203 00204 ***************************************************************/ 00205 00206 override int read (void[] dst) 00207 { 00208 DWORD read; 00209 void *p = dst; 00210 00211 if (! ReadFile (handle, p, dst.length, &read, null)) 00212 error (); 00213 00214 if (read == 0 && dst.length > 0) 00215 return Eof; 00216 return read; 00217 } 00218 00219 /*************************************************************** 00220 00221 Write a chunk of bytes to the file from the provided 00222 array (typically that belonging to an IBuffer) 00223 00224 ***************************************************************/ 00225 00226 override int write (void[] src) 00227 { 00228 DWORD written; 00229 00230 if (! WriteFile (handle, src, src.length, &written, null)) 00231 error (); 00232 00233 return written; 00234 } 00235 } 00236 00237 00238 /*********************************************************************** 00239 00240 Unix-specific code. 00241 00242 ***********************************************************************/ 00243 00244 version (Posix) 00245 { 00246 protected int handle = -1; 00247 00248 /*************************************************************** 00249 00250 Throw an IOException noting the last error 00251 00252 ***************************************************************/ 00253 00254 protected void error () 00255 { 00256 throw new IOException (getName() ~ ": " ~ 00257 System.error); 00258 } 00259 00260 /*************************************************************** 00261 00262 Gain access to the standard IO handles (console etc). 00263 00264 ***************************************************************/ 00265 00266 protected void reopen (FileDevice device) 00267 { 00268 handle = device.id; 00269 } 00270 00271 /*************************************************************** 00272 00273 Close the underlying file 00274 00275 ***************************************************************/ 00276 00277 protected void _close () 00278 { 00279 if (posix.close (handle) == -1) 00280 error (); 00281 } 00282 00283 /*************************************************************** 00284 00285 Read a chunk of bytes from the file into the provided 00286 array (typically that belonging to an IBuffer) 00287 00288 ***************************************************************/ 00289 00290 override int read (void[] dst) 00291 { 00292 int read = posix.read (handle, dst, dst.length); 00293 if (read == -1) 00294 error (); 00295 else 00296 if (read == 0 && dst.length > 0) 00297 return Eof; 00298 return read; 00299 } 00300 00301 /*************************************************************** 00302 00303 Write a chunk of bytes to the file from the provided 00304 array (typically that belonging to an IBuffer) 00305 00306 ***************************************************************/ 00307 00308 override int write (void[] src) 00309 { 00310 int written = posix.write (handle, src, src.length); 00311 if (written == -1) 00312 error (); 00313 return written; 00314 } 00315 } 00316 } 00317 00318 00319 /******************************************************************************* 00320 00321 Class used to wrap an existing file-oriented handle, such as Stdout 00322 and its cohorts. 00323 00324 *******************************************************************************/ 00325 00326 class FileDevice : ConduitStyle 00327 { 00328 private uint id; 00329 00330 this (uint id, Access access) 00331 { 00332 super (access); 00333 this.id = id; 00334 } 00335 } 00336 00337 00338 /******************************************************************************* 00339 00340 Conduit for specifically handling the console devices. This takes 00341 care of certain implementation details on the Win32 platform. 00342 00343 *******************************************************************************/ 00344 00345 class ConsoleConduit : DeviceConduit 00346 { 00347 // expose conduit.copy() methods also 00348 alias DeviceConduit.copy copy; 00349 alias DeviceConduit.read read; 00350 alias DeviceConduit.write write; 00351 00352 /*********************************************************************** 00353 00354 Create a FileConduit on the provided FileDevice. This is 00355 strictly for adapting existing devices such as Stdout and 00356 friends. 00357 00358 ***********************************************************************/ 00359 00360 package this (FileDevice device) 00361 { 00362 super (device); 00363 } 00364 00365 /*********************************************************************** 00366 00367 Return the name of this device 00368 00369 ***********************************************************************/ 00370 00371 protected char[] getName() 00372 { 00373 return "console device"; 00374 } 00375 00376 /*********************************************************************** 00377 00378 Windows-specific code 00379 00380 ***********************************************************************/ 00381 00382 version(Win32) 00383 { 00384 /*************************************************************** 00385 00386 Gain access to the standard IO handles (console etc). 00387 00388 ***************************************************************/ 00389 00390 protected override void reopen (FileDevice device) 00391 { 00392 static const DWORD[] id = [ 00393 cast(DWORD) -10, 00394 cast(DWORD) -11, 00395 cast(DWORD) -12 00396 ]; 00397 static const char[][] f = [ 00398 "CONIN$\0", 00399 "CONOUT$\0", 00400 "CONOUT$\0" 00401 ]; 00402 00403 assert (device.id < 3); 00404 handle = GetStdHandle (id[device.id]); 00405 if (! handle) 00406 handle = CreateFileA (f[device.id], 00407 GENERIC_READ | GENERIC_WRITE, 00408 FILE_SHARE_READ | FILE_SHARE_WRITE, 00409 null, OPEN_EXISTING, 0, null); 00410 if (! handle) 00411 error (); 00412 } 00413 00414 /*************************************************************** 00415 00416 Write a chunk of bytes to the file from the provided 00417 array (typically that belonging to an IBuffer) 00418 00419 ***************************************************************/ 00420 00421 override int write (void[] src) 00422 { 00423 void* p = src.ptr; 00424 DWORD len = src.length; 00425 const int Max = 0x7fff; 00426 00427 // Win32 console has problems with content over 00428 // 32KB in length, so handle it specifically 00429 do { 00430 DWORD i = len; 00431 if (i > Max) 00432 i = Max; 00433 if (! WriteFile (handle, p, i, &i, null)) 00434 error (); 00435 len -= i; 00436 p += i; 00437 } while (len); 00438 00439 return src.length; 00440 } 00441 } 00442 } 00443 00444