1 module unde.lib;
2 
3 import derelict.sdl2.sdl;
4 import std.container.dlist;
5 import std.stdio;
6 import std.format;
7 import std..string;
8 import std.process;
9 import std.regex;
10 import std.conv;
11 import std.utf;
12 import core.exception;
13 
14 import derelict.sdl2.sdl;
15 
16 import unde.global_state;
17 import unde.slash;
18 
19 import core.sys.posix.sys.stat;
20 import core.sys.posix.pwd;
21 import core.sys.posix.grp;
22 version(Windows)
23 {
24 import core.stdc.time;
25 alias ulong ulong_t;
26 }
27 
28 enum DOUBLE_DELAY=750;
29 
30 enum PATH_MAX=4096; //from linux/limits.h
31 enum UUID_MAX=36;
32 enum MARKS_PATH_MAX=PATH_MAX+UUID_MAX;
33 
34 struct Gradient
35 {
36     struct ColorPoint
37     {
38         SDL_Color color;
39         double  point;
40     }
41 
42     DList!ColorPoint grad;
43 
44     void add(SDL_Color color, double point)
45     {
46         auto range = grad[];
47 
48         foreach(a; grad[])
49         {
50             if (point < a.point)
51             {
52                 grad.insertBefore(range, ColorPoint(color, point));
53                 return;
54             }
55             range.popFront();
56         }
57         grad.insertBack(ColorPoint(color, point));
58     }
59 
60     SDL_Color getColor(double point)
61     {
62         auto range = grad[];
63 
64         ColorPoint *a;
65         ColorPoint *b;
66 
67         foreach(g; grad[])
68         {
69             if (point < g.point)
70             {
71                 b = &g;
72                 if (!a) return b.color;
73 
74                 SDL_Color ret;
75                 double k = (point - a.point) / (b.point - a.point);
76                 ret.r = cast(ubyte)(a.color.r * (1.0-k) + b.color.r * k);
77                 ret.g = cast(ubyte)(a.color.g * (1.0-k) + b.color.g * k);
78                 ret.b = cast(ubyte)(a.color.b * (1.0-k) + b.color.b * k);
79                 ret.a = cast(ubyte)(a.color.a * (1.0-k) + b.color.a * k);
80                 return ret;
81             }
82             a = &range.front();
83             range.popFront();
84         }
85 
86         return grad[].back.color;
87     }
88 }
89 
90 unittest
91 {
92     import std.algorithm: equal;
93     Gradient grad;
94     grad.add(SDL_Color(0, 0, 0, 1), 1);
95     grad.add(SDL_Color(0, 0, 0, 3), 3);
96     grad.add(SDL_Color(0, 0, 0, 2), 2);
97     grad.add(SDL_Color(0, 0, 0, 0), 0);
98 
99     /*foreach(a; grad.grad[])
100     {
101         writefln("%d - %.2f", a.color.a, a.point);
102     }*/
103     assert(equal(grad.grad[], [
104                              Gradient.ColorPoint(SDL_Color(0,0,0,0), 0),
105                              Gradient.ColorPoint(SDL_Color(0,0,0,1), 1),
106                              Gradient.ColorPoint(SDL_Color(0,0,0,2), 2),
107                              Gradient.ColorPoint(SDL_Color(0,0,0,3), 3),]));
108 }
109 
110 struct DRect
111 {
112     double x,y,w,h;
113 
114     bool In(in ref DRect b) const
115     {
116         return x >= b.x && (x+w) <= (b.x+b.w) &&
117                y >= b.y && (y+h) <= (b.y+b.h);
118     }
119     bool NotIntersect(in ref DRect b) const
120     {
121         return ((b.x+b.w) < x || b.x > (x+w) ||
122                 (b.y+b.h) < y || b.y > (y+h));
123     }
124     DRect apply(in ref DRect b) const
125     {
126         DRect res;
127         res.x = b.x + x*b.w/(1024*1024);
128         res.y = b.y + y*b.h/(1024*1024);
129         res.w = b.w * w / (1024*1024);
130         res.h = b.h * h / (1024*1024);
131         //assert(res.In(b), format("apply must make rectangle which lays in b.\nthis=%s, b=%s, res=%s", this, b, res));
132         return res;
133     }
134     SDL_Rect to_screen(in ref CoordinatesPlusScale screen)
135     {
136         SDL_Rect rect;
137         rect.x = cast(int)((x - screen.x)/screen.scale);
138         rect.y = cast(int)((y - screen.y)/screen.scale);
139         rect.w = cast(int)(w/screen.scale);
140         rect.h = cast(int)(h/screen.scale);
141         return rect;
142     }
143 
144     void rescale_screen(ref CoordinatesPlusScale screen, SDL_Rect rect)
145     {
146         screen.scale = w/rect.w;
147         screen.x = x - rect.x*screen.scale;
148         screen.y = y - rect.y*screen.scale;
149     }
150 }
151 
152 struct CoordinatesPlusScale
153 {
154     double x, y;
155     int w, h;
156     double scale;
157 
158     DRect getRect()
159     {
160         DRect rect;
161         rect.x = x;
162         rect.y = y;
163         rect.w = w*scale;
164         rect.h = h*scale;
165         return rect;
166     }
167 }
168 
169 enum SortType
170 {
171     ByName = 0,
172     BySize,
173     ByTime
174 }
175 
176 enum FileType
177 {
178     Directory = 0,
179     File,
180     Image,
181     Text
182 }
183 
184 enum InfoType
185 {
186     None = 0,
187     CreateDirectory,
188     Copy,
189     Move,
190     FileInfo,
191     Progress
192 }
193 
194 char[i] to_char_array(int i)(string str)
195 {
196     char[i] ret;
197     size_t l = str.length;
198     if (l > i) l = i;
199     ret[0..l] = str[0..l];
200     return ret;
201 }
202 
203 char[i] to_char_array_z(int i)(string str)
204 {
205     char[i] ret;
206     size_t l = str.length;
207     if (l > i) l = i;
208     ret[0..l] = str[0..l];
209     ret[l..$] = '\0';
210     return ret;
211 }
212 
213 string from_char_array(const char[] str)
214 {
215     int i;
216     foreach (c; str)
217     {
218         if (c == char.init) break;
219         i++;
220     }
221     return str[0..i].idup();
222 }
223 
224 wstring from_char_array(const wchar[] str)
225 {
226     int i;
227     foreach (c; str)
228     {
229         if (c == wchar.init) break;
230         i++;
231     }
232     return str[0..i].idup();
233 }
234 
235 string strip_error(string str)
236 {
237     return str[str.indexOf(":")+2..$];
238 }
239 
240 struct RectSize
241 {
242     DRect rect_by_name;
243     DRect rect_by_size;
244     DRect rect_by_time;
245 
246     long size;
247     long disk_usage;
248     time_t mtime;
249     ulong_t mtime_nsec;
250     long files;
251     InfoType show_info;
252 
253     union{
254         struct
255         {
256             // Error Message
257             char[80] msg;
258             long msg_time;
259             uint msg_color;
260         };
261         struct
262         {
263             // Progress Info
264             char[80] path;
265             long estimate_end;
266             int progress; // from 0 tlll 10000
267         };
268     }
269 
270     FileType type;
271 
272     union
273     {
274         struct {
275         // Directory
276             SortType sort;
277             long newest_msg_time;
278         };
279         struct {
280         // Image
281             double angle;
282         };
283         struct {
284         // Text
285             long offset;
286             char[32] charset;
287         };
288     }
289 
290     ref inout(DRect) rect(const SortType sort) inout
291     {
292         final switch(sort)
293         {
294             case SortType.ByName:
295                 return rect_by_name;
296             case SortType.BySize:
297                 return rect_by_size;
298             case SortType.ByTime:
299                 return rect_by_time;
300         }
301     }
302 }
303 
304 struct Mark
305 {
306     char[MARKS_PATH_MAX] path;
307     SDL_Rect screen_rect;
308     State state;
309     union{
310         struct
311         { // Text
312             long offset;
313         }
314     }
315 }
316 
317 struct DirEntryRect
318 {
319     string path;
320     DRect rect;
321     long size;
322     alias rect this;
323 
324     this (string path, long x, long y, long w, long h, long size)
325     {
326         this.path = path;
327         rect = DRect(x, y, w, h);
328         this.size = size;
329     }
330 
331     this (string path, DRect rect, long size)
332     {
333         this.path = path;
334         this.rect = rect;
335         this.size = size;
336     }
337 }
338 
339 string
340 subpath(string path, string mnt)
341 {
342     if (mnt == SL) return path;
343     if (mnt == path) return SL;
344     return path[mnt.length..$];
345 }
346 
347 void
348 calculate_selection_sub(GlobalState gs)
349 {
350     gs.selection_sub = null;
351     foreach(path, rectsize; gs.selection_hash)
352     {
353         path = getParent(path);
354         while (path > "")
355         {
356             gs.selection_sub[path] = true;
357             path = getParent(path);
358         }
359         gs.selection_sub[SL] = true;
360     }
361 }
362 
363 struct Selection
364 {
365     string from;
366     string to;
367 
368     long size_from;
369     long size_to;
370 
371     time_t mtime_from;
372     time_t mtime_to;
373 
374     SortType sort;
375 }
376 
377 version (Posix)
378 {
379 import core.sys.posix.sys.types;
380 import core.sys.posix.sys.stat;
381 string mode_to_string(mode_t mode)
382 {
383     char[12] res;
384     res[ 0] = mode & S_ISUID ? 'U' : '-';
385     res[ 1] = mode & S_ISGID ? 'G' : '-';
386     res[ 2] = mode & S_ISVTX ? 'S' : '-';
387     res[ 3] = mode & S_IRUSR ? 'r' : '-';
388     res[ 4] = mode & S_IWUSR ? 'w' : '-';
389     res[ 5] = mode & S_IXUSR ? 'x' : '-';
390     res[ 6] = mode & S_IRGRP ? 'r' : '-';
391     res[ 7] = mode & S_IWGRP ? 'w' : '-';
392     res[ 8] = mode & S_IXGRP ? 'x' : '-';
393     res[ 9] = mode & S_IROTH ? 'r' : '-';
394     res[10] = mode & S_IWOTH ? 'w' : '-';
395     res[11] = mode & S_IXOTH ? 'x' : '-';
396     return res.idup();
397 }
398 
399 string uid_to_name(uid_t uid)
400 {
401     auto pwd = getpwuid(uid);
402     return fromStringz(pwd.pw_name).idup();
403 }
404 
405 string gid_to_name(gid_t gid)
406 {
407     auto pwd = getgrgid(gid);
408     return fromStringz(pwd.gr_name).idup();
409 }
410 }
411 
412 string
413 mime(string path)
414 {
415     auto df_pipes = pipeProcess(["file", "-bi", path], Redirect.stdout);
416     scope(exit) wait(df_pipes.pid);
417 
418     int l=0;
419     foreach (df_line; df_pipes.stdout.byLine)
420     {
421         if (l == 0)
422         {
423             auto match = matchFirst(df_line, regex(`(.*); charset=.*`));
424             if (match)
425             {
426                 return match[1].idup();
427             }
428         }
429         l++;
430     }
431     return "error/none";
432 }
433 
434 int
435 db_recover(string path)
436 {
437     auto df_pipes = pipeProcess(["db_recover", "-h", path], Redirect.stdout | Redirect.stderrToStdout);
438     scope(exit) wait(df_pipes.pid);
439 
440     foreach (df_line; df_pipes.stdout.byLine)
441     {
442         writefln(df_line);
443     }
444 
445     return 0;
446 }
447 
448 void
449 unDE_RenderFillRect(SDL_Renderer *renderer, SDL_Rect *rect, uint color)
450 {
451     int r = SDL_SetRenderDrawColor(renderer, 
452             (color&0xFF0000) >> 16,
453             (color&0xFF00) >> 8,
454             color&0xFF,
455             (color&0xFF000000) >> 24);
456     if (r < 0)
457     {
458         writefln("Can't SDL_SetRenderDrawColor: %s",
459                 fromStringz(SDL_GetError()));
460     }
461 
462     r = SDL_RenderFillRect(renderer, rect);
463     if (r < 0)
464     {
465         writefln("Can't SDL_RenderFillRect: %s",
466                 fromStringz(SDL_GetError()));
467     }
468 }
469 
470 version(Windows)
471 {
472 import core.sys.windows.winbase;
473 
474 string GetErrorMessage()
475 {
476 	wchar[1024] pBuffer;
477 
478 	auto res = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
479 			null, 
480 			GetLastError(),
481 			0,
482 			pBuffer.ptr, 
483 			pBuffer.length, 
484 			null);
485 	if (!res)
486 	{
487 		writefln("FormatMessage error %s", GetLastError());
488 	}
489 
490 	return to!string(from_char_array(pBuffer));
491 }
492 }
493 
494 string getParent(string path)
495 {
496 	version (Windows)
497 	{
498 	if (path.lastIndexOf(SL) < 0)
499 		return SL;
500 	}
501         return path[0..path.lastIndexOf(SL)];
502 }
503 
504 size_t
505 mystride(T)(ref T str, size_t pos, size_t len = 0)
506 {
507     /* stride falls with OutOfMemoryError Sometimes
508        on not correct symbols, so we will use out stride */
509 
510     if ((str[pos] & 0b1000_0000) == 0)
511         return 1;
512 
513     if (len == 0) len = str.length;
514 
515     size_t i;
516     for (i=pos+1; i < len && (str[i] & 0b1100_0000) == 0b1000_0000; i++)
517     {
518     }
519     return i-pos;
520 }
521 
522 size_t
523 mystrideBack(T)(ref T str, size_t pos)
524 {
525     try
526     {
527         return str.strideBack(pos);
528     }
529     catch (UnicodeException e)
530     {
531     }
532     catch (UTFException e)
533     {
534     }
535     catch (OutOfMemoryError e)
536     {
537     }
538     return 1;
539 }
540 
541 size_t
542 myWalkLength(char[] str)
543 {
544     size_t n = 0;
545     for (size_t i = 0; i < str.length; i+=mystride(str, i))
546         n++;
547     return n;
548 }
549 
550 // TODO Move to unde.file_manager.lib
551 import unde.viewers.image_viewer.lib;
552 import unde.viewers.text_viewer.lib;
553 import unde.path_mnt;
554 void
555 openFile(GlobalState gs, PathMnt path)
556 {
557     string mime_type = mime(path);
558     if (mime_type == "inode/directory" ||
559             mime_type == "error/none")
560     {
561         // Do nothing
562     }
563     else if (mime_type.startsWith("image/"))
564     {
565         image_viewer(gs, path);
566     }
567     else if (mime_type.startsWith("text/"))
568     {
569         text_viewer(gs, path);
570     }
571     else
572     {
573         string msg = format("No viewer for '%s' mime type", mime_type);
574         gs.messages ~= ConsoleMessage(
575                 SDL_Color(0xFF, 0x00, 0x00, 0xFF),
576                 msg,
577                 SDL_GetTicks()
578                 );
579         writeln(msg);
580     }
581 }
582 
583 // TODO Move to unde.file_manager.lib
584 void
585 openFileByMime(GlobalState gs, string path)
586 {
587     string mime_type = mime(path);
588     if ( mime_type in gs.mime_applications )
589     {
590         auto pid = spawnProcess([gs.mime_applications[mime_type], path]);
591         gs.pids ~= pid;
592     }
593     else
594     {
595         string msg = format("No mime application for '%s' mime type in ~/.unde/mime", mime_type);
596         gs.messages ~= ConsoleMessage(
597                 SDL_Color(0xFF, 0x00, 0x00, 0xFF),
598                 msg,
599                 SDL_GetTicks()
600                 );
601         writeln(msg);
602     }
603 }
604 
605 static byte[byte] clear_check;
606 
607 static if (!__traits(hasMember, clear_check, "clear"))
608 {
609 	void clear(A, B) (A[B] a)
610 	{
611 		foreach (key; a.keys)
612 		{
613 			a.remove(key);
614 		}
615 	}
616 }