1 // Written in the D programming language.
2 
3 /**
4  * $(RED Deprecated: This module is considered out-dated and not up to Phobos'
5  *       current standards.)
6  *
7  * The std.cstream module bridges core.stdc.stdio (or std.stdio) and std.stream.
8  * Both core.stdc.stdio and std.stream are publicly imported by std.cstream.
9  *
10  * Macros:
11  *      WIKI=Phobos/StdCstream
12  *
13  * Copyright: Copyright Ben Hinkle 2007 - 2009.
14  * License:   $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
15  * Authors:   Ben Hinkle
16  * Source:    $(PHOBOSSRC std/_cstream.d)
17  */
18 /*          Copyright Ben Hinkle 2007 - 2009.
19  * Distributed under the Boost Software License, Version 1.0.
20  *    (See accompanying file LICENSE_1_0.txt or copy at
21  *          http://www.boost.org/LICENSE_1_0.txt)
22  */
23 module undead.cstream;
24 
25 public import core.stdc.stdio;
26 public import undead.stream;
27 version(unittest) import std.stdio;
28 
29 import std.algorithm;
30 
31 /**
32  * A Stream wrapper for a C file of type FILE*.
33  */
34 class CFile : Stream {
35   protected FILE* cfile;
36 
37   /**
38    * Create the stream wrapper for the given C file.
39    * Params:
40    *   cfile = a valid C $(B FILE) pointer to wrap.
41    *   mode = a bitwise combination of $(B FileMode.In) for a readable file
42    *          and $(B FileMode.Out) for a writeable file.
43    *   seekable = indicates if the stream should be _seekable.
44    */
45   this(FILE* cfile, FileMode mode, bool seekable = false) {
46     super();
47     this.file = cfile;
48     readable = cast(bool)(mode & FileMode.In);
49     writeable = cast(bool)(mode & FileMode.Out);
50     this.seekable = seekable;
51   }
52 
53   /**
54    * Closes the stream.
55    */
56   ~this() { close(); }
57 
58   /**
59    * Property to get or set the underlying file for this stream.
60    * Setting the file marks the stream as open.
61    */
62   @property FILE* file() { return cfile; }
63 
64   /**
65    * Ditto
66    */
67   @property void file(FILE* cfile) {
68     this.cfile = cfile;
69     isopen = true;
70   }
71 
72   /**
73    * Overrides of the $(B Stream) methods to call the underlying $(B FILE*)
74    * C functions.
75    */
76   override void flush() { fflush(cfile); }
77 
78   /**
79    * Ditto
80    */
81   override void close() {
82     if (isopen)
83       fclose(cfile);
84     isopen = readable = writeable = seekable = false;
85   }
86 
87   /**
88    * Ditto
89    */
90   override bool eof() {
91     return cast(bool)(readEOF || feof(cfile));
92   }
93 
94   /**
95    * Ditto
96    */
97   override char getc() {
98     return cast(char)fgetc(cfile);
99   }
100 
101   /**
102    * Ditto
103    */
104   override char ungetc(char c) {
105     return cast(char)core.stdc.stdio.ungetc(c,cfile);
106   }
107 
108   /**
109    * Ditto
110    */
111   override size_t readBlock(void* buffer, size_t size) {
112     size_t n = fread(buffer,1,size,cfile);
113     readEOF = cast(bool)(n == 0);
114     return n;
115   }
116 
117   /**
118    * Ditto
119    */
120   override size_t writeBlock(const void* buffer, size_t size) {
121     return fwrite(buffer,1,size,cfile);
122   }
123 
124   /**
125    * Ditto
126    */
127   override ulong seek(long offset, SeekPos rel) {
128     readEOF = false;
129     if (fseek(cfile,cast(int)offset,rel) != 0)
130       throw new SeekException("unable to move file pointer");
131     return ftell(cfile);
132   }
133 
134   /**
135    * Ditto
136    */
137   override void writeLine(const(char)[] s) {
138     writeString(s);
139     writeString("\n");
140   }
141 
142   /**
143    * Ditto
144    */
145   override void writeLineW(const(wchar)[] s) {
146     writeStringW(s);
147     writeStringW("\n");
148   }
149 
150   // run a few tests
151   unittest {
152     import undead.internal.file;
153     import std.internal.cstring : tempCString;
154 
155     auto stream_file = (undead.internal.file.deleteme ~ "-stream.txt").tempCString();
156     FILE* f = fopen(stream_file,"w");
157     assert(f !is null);
158     CFile file = new CFile(f,FileMode.Out);
159     int i = 666;
160     // should be ok to write
161     assert(file.writeable);
162     file.writeLine("Testing stream.d:");
163     file.writeString("Hello, world!");
164     file.write(i);
165     // string#1 + string#2 + int should give exacly that
166     version (Windows)
167         assert(file.position == 19 + 13 + 4);
168     version (Posix)
169         assert(file.position == 18 + 13 + 4);
170     file.close();
171     // no operations are allowed when file is closed
172     assert(!file.readable && !file.writeable && !file.seekable);
173     f = fopen(stream_file,"r");
174     file = new CFile(f,FileMode.In,true);
175     // should be ok to read
176     assert(file.readable);
177     auto line = file.readLine();
178     auto exp = "Testing stream.d:";
179     assert(line[0] == 'T');
180     assert(line.length == exp.length);
181     assert(!std.algorithm.cmp(line, "Testing stream.d:"));
182     // jump over "Hello, "
183     file.seek(7, SeekPos.Current);
184     version (Windows)
185       assert(file.position == 19 + 7);
186     version (Posix)
187       assert(file.position == 18 + 7);
188     assert(!std.algorithm.cmp(file.readString(6), "world!"));
189     i = 0; file.read(i);
190     assert(i == 666);
191     // string#1 + string#2 + int should give exacly that
192     version (Windows)
193       assert(file.position == 19 + 13 + 4);
194     version (Posix)
195       assert(file.position == 18 + 13 + 4);
196     // we must be at the end of file
197     file.close();
198     f = fopen(stream_file,"w+");
199     file = new CFile(f,FileMode.In|FileMode.Out,true);
200     file.writeLine("Testing stream.d:");
201     file.writeLine("Another line");
202     file.writeLine("");
203     file.writeLine("That was blank");
204     file.position = 0;
205     char[][] lines;
206     foreach(char[] fileLine; file) {
207       lines ~= fileLine.dup;
208     }
209     assert( lines.length == 5 );
210     assert( lines[0] == "Testing stream.d:");
211     assert( lines[1] == "Another line");
212     assert( lines[2] == "");
213     assert( lines[3] == "That was blank");
214     file.position = 0;
215     lines = new char[][5];
216     foreach(ulong n, char[] fileLine; file) {
217       lines[cast(size_t)(n-1)] = fileLine.dup;
218     }
219     assert( lines[0] == "Testing stream.d:");
220     assert( lines[1] == "Another line");
221     assert( lines[2] == "");
222     assert( lines[3] == "That was blank");
223     file.close();
224     remove(stream_file);
225   }
226 }
227 
228 /**
229  * CFile wrapper of core.stdc.stdio.stdin (not seekable).
230  */
231 __gshared CFile din;
232 
233 /**
234  * CFile wrapper of core.stdc.stdio.stdout (not seekable).
235  */
236 __gshared CFile dout;
237 
238 /**
239  * CFile wrapper of core.stdc.stdio.stderr (not seekable).
240  */
241 __gshared CFile derr;
242 
243 shared static this() {
244   // open standard I/O devices
245   din = new CFile(core.stdc.stdio.stdin,FileMode.In);
246   dout = new CFile(core.stdc.stdio.stdout,FileMode.Out);
247   derr = new CFile(core.stdc.stdio.stderr,FileMode.Out);
248 }
249