egra: post rebuild event on minimisation (because minimised windows won't do it)
[iv.d.git] / _obsolete_dont_use / file.d
blobd0c90725c2bd77f0069d18d09a2737f171fe69b7
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // severely outdated case-insensitive filesystem interface
18 // use iv.vfs instead
19 module iv.file /*is aliced*/;
21 import iv.alice;
22 private import std.stdio : File;
23 private import iv.strex : indexOf; // rdmd sux!
26 /**
27 * get path to real disk file, do case-insensitive search if necessary.
28 * assuming that disk names are in koi8-u.
29 * expands tilde too.
31 * Params:
32 * path = source path
33 * allOk = success flag
35 * Returns:
36 * found path (full or partial if `allOk` is not null)
37 * null path (if there is no such path and `allOk` is null)
39 * Throws:
40 * it shouldn't, but expandTilde() is not nothrow, for example
42 string getPathCI (string path, bool* allOk=null) @trusted {
43 import std.file : dirEntries, exists, SpanMode;
44 import std.path : baseName, buildNormalizedPath, buildPath, CaseSensitive;
45 import std.path : dirName, expandTilde, filenameCmp, isRooted, pathSplitter;
47 if (allOk !is null) *allOk = false;
48 path = path.expandTilde;
50 // try 'as is'
51 if (path.exists) {
52 if (allOk !is null) *allOk = true;
53 return path;
56 // alas, traverse dirs
57 string dir;
58 foreach (/*auto*/ d; path.dirName.expandTilde.buildNormalizedPath.pathSplitter) {
59 if (dir.length == 0 && d.isRooted) {
60 dir = d.idup;
61 } else {
62 string realDir = findNameCI(dir, d, true);
63 if (realDir is null) return (allOk !is null ? dir : null);
64 dir = buildPath(dir, realDir);
68 // now try the last part, filename
69 string pn = path.baseName;
70 foreach (string fn; dirEntries((dir.length > 0 ? dir : "."), SpanMode.shallow)) {
71 string n = fn.baseName;
72 if (filenameCmp!(CaseSensitive.no)(n, pn) == 0) {
73 if (allOk !is null) *allOk = true;
74 return (dir.length == 0 ? n : fn);
78 // no filename was found
79 return (allOk !is null ? dir : null);
83 // ////////////////////////////////////////////////////////////////////////// //
84 // returns only file name, without path
85 private string findNameCI (string dir, in char[] name, bool wantDir) @trusted {
86 import std.file : exists, dirEntries, SpanMode, isFile, isDir;
87 import std.path : baseName, buildPath, filenameCmp, CaseSensitive;
88 if (dir.length == 0) dir = ".";
89 string fullName = buildPath(dir, name);
90 if (fullName.exists) {
91 if ((wantDir && fullName.isDir) || (!wantDir && fullName.isFile)) return name.idup;
93 foreach (string fn; dirEntries(dir, SpanMode.shallow)) {
94 string n = fn.baseName;
95 if (filenameCmp!(CaseSensitive.no)(n, name) == 0) {
96 if ((wantDir && fn.isDir) || (!wantDir && fn.isFile)) return n;
99 return null;
104 * open file using case-insensitive name in read-only mode.
106 * Params:
107 * fileName = file name
108 * diskName = found disk file name on success (can be relative or absolute)
110 * Returns:
111 * std.stdio.File
113 * Throws:
114 * Exception on 'file not found'
116 File openCI (string fileName, out string diskName) @trusted {
117 import std.file : exists, isFile;
118 import std.path;
120 // try 'as is'
121 diskName = fileName;
122 if (fileName.exists && fileName.isFile) {
123 try return File(fileName); catch (Exception) {}
126 // traverse dirs
127 string dir;
128 foreach (/*auto*/ d; fileName.dirName.expandTilde.buildNormalizedPath.pathSplitter) {
129 if (dir.length == 0 && d.isRooted) {
130 dir = d.idup;
131 } else {
132 string realDir = findNameCI(dir, d, true);
133 if (realDir is null) return File(fileName); // throw error
134 dir = buildPath(dir, realDir);
138 string name = findNameCI(dir, fileName.baseName, false);
139 if (name is null) return File(fileName); // throw error
141 diskName = buildPath(dir, name);
142 return File(diskName);
147 * open file using case-insensitive name in read-only mode.
149 * Params:
150 * fileName = file name
152 * Returns:
153 * std.stdio.File
155 * Throws:
156 * Exception on 'file not found'
158 File openCI (string fileName) @trusted {
159 string dn;
160 return openCI(fileName, dn);
164 version(test_file) unittest {
165 import std.file, std.path, std.stdio;
166 string md = getcwd().dirName;
167 writeln(md);
168 bool ok;
169 string r;
170 r = getPathCI(md~"/IV/file.D", &ok);
171 writeln(ok, " ", r);
172 r = getPathCI(md~"/IV/filez.D", &ok);
173 writeln(ok, " ", r);
174 r = getPathCI(md~"/IVz/file.D", &ok);
175 writeln(ok, " ", r);
176 writeln(getPathCI(md~"/IV/file.D"));
177 writeln(getPathCI(md~"/IV/filez.D"));
178 writeln(getPathCI(md~"/IVz/file.D"));
182 // the following code was taken from https://github.com/nordlow/justd/blob/master/bylinefast.d
184 * Reads by line in an efficient way (10 times faster than File.byLine from
185 * std.stdio). This is accomplished by reading entire buffers (fgetc() is not
186 * used), and allocating as little as possible.
188 * The char \n is considered as default separator, removing the previous \r if
189 * it exists.
191 * The \n is never returned. The \r is not returned if it was
192 * part of a \r\n (but it is returned if it was by itself).
194 * The returned string is always a substring of a temporary buffer, that must
195 * not be stored. If necessary, you must use str[] or .dup or .idup to copy to
196 * another string. DIP-25 return qualifier is used in front() to add extra
197 * checks in @safe callers of front().
199 * Example:
201 * File f = File("file.txt");
202 * foreach (string line; ByLineFast(f)) {
203 * ...process line...
204 * //Make a copy:
205 * string copy = line[];
208 * The file isn't closed when done iterating, unless it was the only reference to
209 * the file (same as std.stdio.byLine). (example: ByLineFast(File("file.txt"))).
211 struct ByLineFast(Char, Terminator) {
212 File file;
213 char[] line;
214 bool firstCall = true;
215 char[] buffer;
216 char[] strBuffer;
217 const string separator;
218 bool keepTerminator;
220 this (File f, bool keepTerminator=false, string separator="\n", uint bufferSize=4096) @safe {
221 assert(bufferSize > 0);
222 file = f;
223 this.separator = separator;
224 this.keepTerminator = keepTerminator;
225 buffer.length = bufferSize;
228 @property bool empty () const @trusted {
229 import std.stdio : fgetc, ungetc;
230 // Its important to check "line !is null" instead of
231 // "line.length != 0", otherwise, no empty lines can
232 // be returned, the iteration would be closed.
233 if (line !is null) return false;
234 if (!file.isOpen) {
235 // Clean the buffer to avoid pointer false positives:
236 (cast(char[])buffer)[] = 0;
237 return true;
239 // First read. Determine if it's empty and put the char back.
240 auto mutableFP = (cast(File*)&file).getFP();
241 const c = fgetc(mutableFP);
242 if (c == -1) {
243 // Clean the buffer to avoid pointer false positives:
244 (cast(char[])buffer)[] = 0;
245 return true;
247 if (ungetc(c, mutableFP) != c) assert(false, "Bug in cstdlib implementation");
248 return false;
251 @property char[] front() @safe /*return*//*DIP-25*/ {
252 if (firstCall) {
253 popFront();
254 firstCall = false;
256 return line;
259 void popFront() @trusted {
260 import iv.strex : indexOf;
262 if (strBuffer.length == 0) {
263 strBuffer = file.rawRead(buffer);
264 if (strBuffer.length == 0) {
265 file.detach();
266 line = null;
267 return;
271 const pos = indexOf(strBuffer, this.separator);
272 if (pos != -1) {
273 if (pos != 0 && strBuffer[pos-1] == '\r') {
274 line = strBuffer[0..pos-1];
275 } else {
276 line = strBuffer[0..pos];
278 // Pop the line, skipping the terminator:
279 strBuffer = strBuffer[pos+1..$];
280 } else {
281 // More needs to be read here. Copy the tail of the buffer
282 // to the beginning, and try to read with the empty part of
283 // the buffer.
284 // If no buffer was left, extend the size of the buffer before
285 // reading. If the file has ended, then the line is the entire
286 // buffer.
287 if (strBuffer.ptr != buffer.ptr) {
288 import core.stdc.string: memmove;
289 // Must use memmove because there might be overlap
290 memmove(buffer.ptr, strBuffer.ptr, strBuffer.length*char.sizeof);
292 const spaceBegin = strBuffer.length;
293 if (strBuffer.length == buffer.length) {
294 // Must extend the buffer to keep reading.
295 assumeSafeAppend(buffer);
296 buffer.length = buffer.length*2;
298 const readPart = file.rawRead(buffer[spaceBegin..$]);
299 if (readPart.length == 0) {
300 // End of the file. Return whats in the buffer.
301 // The next popFront() will try to read again, and then
302 // mark empty condition.
303 if (spaceBegin != 0 && buffer[spaceBegin-1] == '\r') {
304 line = buffer[0..spaceBegin-1];
305 } else {
306 line = buffer[0..spaceBegin];
308 strBuffer = null;
309 return;
311 strBuffer = buffer[0..spaceBegin+readPart.length];
312 // Now that we have new data in strBuffer, we can go on.
313 // If a line isn't found, the buffer will be extended again to read more.
314 popFront();
319 auto byLineFast(Terminator=char, Char=char) (File f,
320 bool keepTerminator=false,
321 string separator="\n",
322 uint bufferSize=4096) @safe // TODO lookup preferred block type
324 return ByLineFast!(Char, Terminator)(f, keepTerminator, separator, bufferSize);
328 version(test_file) unittest {
329 import std.stdio: File, writeln;
330 import std.algorithm.searching: count;
331 const path = "/etc/passwd";
332 assert(File(path).byLineFast.count == File(path).byLine.count);
335 version(test_file) @safe unittest {
336 import std.stdio: File;
337 const path = "/etc/passwd";
338 char[] mutable_line;
339 foreach (line; File(path).byLineFast) {
340 mutable_line = line; // TODO this should fail
343 auto byline = File(path).byLineFast;
344 mutable_line = byline.front; // TODO this should fail
348 version(none) unittest {
349 import std.stdio: File, writeln;
350 import std.algorithm.searching: count;
351 const path = "/home/ketmar/muldict.txt";
352 import std.datetime: StopWatch;
353 double d1, d2;
355 StopWatch sw;
356 sw.start;
357 const c1 = File(path).byLine.count;
358 sw.stop;
359 d1 = sw.peek.msecs;
360 writeln("byLine: ", d1, "msecs");
363 StopWatch sw;
364 sw.start;
365 const c2 = File(path).byLineFast.count;
366 sw.stop;
367 d2 = sw.peek.msecs;
368 writeln("byLineFast: ", d2, "msecs");
370 writeln("Speed-Up: ", d1 / d2);