1 /**
2  * Consoleur: a package for interaction with character-oriented terminal emulators
3  *
4  * Copyright: Maxim Freck, 2017.
5  * Authors:   Maxim Freck <maxim@freck.pp.ru>
6  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  */
8 module consoleur.terminfo.io;
9 version(Posix) {
10 
11 import std.stdio: File;
12 import std.traits: isScalarType;
13 
14 /*******
15  * Determines terminfo file name
16  * Returns: the path to the file or an empty string if the file is not found
17  */
18 string determineTermFile() @safe
19 {
20 	import std.process: environment, get;
21 
22 	auto fname = environment.get("TERM", "unknown");
23 	fname = cast(char)(fname[0])~"/"~fname;
24 
25 	auto home = getHomeDir() ~ "/.terminfo/" ~ fname;
26 
27 	if (fileExists(home)) {
28 		return home;
29 	}
30 
31 	if (fileExists("/etc/terminfo/"~fname)) {
32 		return "/etc/terminfo/"~fname;
33 	}
34 
35 	if (fileExists("/lib/terminfo/"~fname)) {
36 		return "/lib/terminfo/"~fname;
37 	}
38 
39 	return "";
40 }
41 
42 private bool fileExists(string name) @trusted
43 {
44 	import core.sys.posix.sys.stat: stat, stat_t, S_ISREG;
45 
46 	stat_t path_stat;
47 	stat((name~"\0").ptr, &path_stat);
48 	return S_ISREG(path_stat.st_mode);
49 }
50 
51 private string getHomeDir() @trusted
52 {
53 	import core.sys.posix.pwd: getpwuid;
54 	import core.sys.posix.unistd: getuid;
55 	import std.conv: to;
56 
57 	auto passwdEnt = *getpwuid(getuid());
58 	return to!string(passwdEnt.pw_dir);
59 }
60 
61 /*******
62  * Determines whether the system is big endian
63  * Returns: true if the system is big endan, false otherwise
64  */
65 pure nothrow immutable(bool) isBigEndian() @trusted @nogc
66 {
67 	union E {ushort s; ubyte[2] b; }
68 	E e = {s: 0xdead};
69 	return (e.b[0] == 0xad) ? false : true;
70 }
71 
72 /*******
73  * Performes inplace reverse of a static array
74  */
75 private pure nothrow void reverse(T, size_t n)(ref T[n] a) @safe @nogc
76 {
77 	foreach (i; 0 .. n/2) {
78 		immutable temp = a[i];
79 		a[i] = a[n - 1 - i];
80 		a[n - 1 - i] = temp;
81 	}
82 }
83 
84 /*******
85  * Reads variable of type T from a file in the little endian format.
86  * Returns: variable of type T in the little endian format.
87  */
88 auto get(T)(File f) @safe if (isScalarType!(T))
89 {
90 	static if (T.sizeof == 1) {
91 		T[1] b = [0];
92 		f.rawRead(b);
93 		return b[0];
94 
95 	} else {
96 
97 		union buffer {ubyte[T.sizeof] b; T v;}
98 		buffer buf;
99 		f.rawRead(buf.b);
100 
101 		if(isBigEndian()) {
102 			reverse(buf.b);
103 		}
104 
105 		return buf.v;
106 	}
107 }
108 
109 /*******
110  * Reads array of variables of type T from a file in the little endian format.
111  * Returns: array of variables of type T in the little endian format.
112  */
113 T[] get(T)(File f, size_t size) @safe if (isScalarType!(T))
114 {
115 	if (size < 1) return [];
116 
117 	auto buf = new T[size];
118 	f.rawRead(buf);
119 
120 	if(isBigEndian()) {
121 		union item {ubyte[T.sizeof] b; T v;}
122 		foreach (ref i; buf) {
123 			item itm;
124 			itm.v = i;
125 			reverse(itm.b);
126 			i = itm.v;
127 		}
128 	}
129 
130 	return buf;
131 }
132 
133 }