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