1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2009 Kris Bell. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         version:        rewritten: Nov 2009
8 
9         Various low-level console oriented utilities
10 
11 *******************************************************************************/
12 
13 module tango.core.util.console;
14 
15 private import tango.core.util.string;
16 
17 /*******************************************************************************
18         
19         External functions
20 
21 *******************************************************************************/
22 
23 version (Windows) 
24         {
25         enum {UTF8 = 65001};
26         private extern (Windows) int GetStdHandle (int);
27         private extern (Windows) int WriteFile (int, in char*, int, int*, void*);
28         private extern (Windows) bool GetConsoleMode (int, int*);
29         private extern (Windows) bool WriteConsoleW (int, in wchar*, int, int*, void*);
30         private extern (Windows) int MultiByteToWideChar (int, int, in char*, int, wchar*, int);
31         } 
32 else 
33 version (Posix)
34          extern(C) ptrdiff_t write (int, in void*, size_t);
35 
36 
37 /*******************************************************************************
38         
39         Emit an integer to the console
40 
41 *******************************************************************************/
42 
43 extern(C) void consoleInteger (ulong i)
44 {
45         char[25] tmp = void;
46 
47         consoleString (ulongToUtf8 (tmp, i));
48 }
49 
50 /*******************************************************************************
51 
52         Emit a utf8 string to the console. Codepages are not supported
53 
54 *******************************************************************************/
55 
56 extern(C) void consoleString (const(char)[] s)
57 {
58         version (Windows)
59                 {
60                 int  mode, count;
61                 auto handle = GetStdHandle (0xfffffff5);
62 
63                 if (handle != -1 && GetConsoleMode (handle, &mode))
64                    {
65                    wchar[512] utf;
66                    while (s.length)
67                          {
68                          // crop to last complete utf8 sequence
69                          auto t = crop (s[0 .. (s.length > utf.length) ? utf.length : s.length]);
70 
71                          // convert into output buffer and emit
72                          auto i = MultiByteToWideChar (UTF8, 0, s.ptr, t.length, utf.ptr, utf.length);
73                          WriteConsoleW (handle, utf.ptr, i, &count, null);
74 
75                          // process next chunk
76                          s = s [t.length .. $];
77                          }
78                    }
79                 else
80                    // output is probably redirected (we assume utf8 output)
81                    WriteFile (handle, s.ptr, s.length, &count, null);
82                 }
83 
84         version (Posix)
85                  write (2, s.ptr, s.length);
86 }
87 
88 /*******************************************************************************
89 
90         Support for chained console (pseudo formatting) output
91 
92 *******************************************************************************/
93 
94 struct Console
95 {
96         alias newline opCall;
97         alias emit    opCall;
98     
99         /// emit a utf8 string to the console
100         Console emit (const(char)[] s)
101         {
102                 consoleString (s);
103                 return this;
104         }
105 
106         /// emit an unsigned integer to the console
107         Console emit (ulong i)
108         {
109                 consoleInteger (i);
110                 return this;
111         }
112 
113         /// emit a newline to the console
114         Console newline ()
115         {
116                 version (Windows)
117                          const eol = "\r\n";
118                 version (Posix)
119                          const eol = "\n";
120 
121                 return emit (eol);
122         }
123 }
124 
125 public Console console;
126 
127 
128 version (Windows)
129 {
130 /*******************************************************************************
131 
132         Adjust the content such that no partial encodings exist on the 
133         right side of the provided text.
134 
135         Returns a slice of the input
136 
137 *******************************************************************************/
138 
139 private inout(char)[] crop (inout(char)[] s)
140 {
141         if (s.length)
142            {
143            auto i = s.length - 1;
144            while (i && (s[i] & 0x80))
145                   if ((s[i] & 0xc0) is 0xc0)
146                      {
147                      // located the first byte of a sequence
148                      ubyte b = s[i];
149                      int d = s.length - i;
150 
151                      // is it a 3 byte sequence?
152                      if (b & 0x20)
153                          --d;
154    
155                      // or a four byte sequence?
156                      if (b & 0x10)
157                          --d;
158 
159                      // is the sequence complete?
160                      if (d is 2)
161                          i = s.length;
162                      return s [0..i];
163                      }
164                   else 
165                      --i;
166            }
167         return s;
168 }
169 }
170 
171 
172 /*******************************************************************************
173         
174 *******************************************************************************/
175 
176 debug (console)
177 {
178         void main()
179         {
180                 console ("hello world \u263a")();
181         }
182 }