1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 void PrintHelp()
10 {
11 Console.WriteLine("Usage: less [options] [FILE]...");
12 Console.WriteLine("View paginated output.");
13 Console.WriteLine("Options:");
14 Console.WriteLine("--help | -h");
15 Console.WriteLine(" Print help and exit.");
16 }
17
18 int MaxWidth(const List<ustring>& lines)
19 {
20 int maxWidth = 0;
21 for (const ustring& line : lines)
22 {
23 if (line.Length() > maxWidth)
24 {
25 maxWidth = cast<int>(line.Length());
26 }
27 }
28 return maxWidth;
29 }
30
31 void ViewScreen(const List<ustring>& lines, int offsetY, int screenWidth, int screenHeight)
32 {
33 ClearScreen(screenWidth, screenHeight);
34 int n = Min(cast<int>(lines.Count()), screenHeight - 1);
35 for (int i = 0; i < n; ++i;)
36 {
37 SetCursorPos(0, i);
38 const ustring& line = lines[i + offsetY];
39 Terminal.Out() << line;
40 }
41 SetCursorPos(0, screenHeight - 1);
42 Terminal.Out() << ":";
43 }
44
45 void Start(int& offsetY)
46 {
47 offsetY = 0;
48 }
49
50 void End(const List<ustring>& lines, int& offsetY, int screenHeight)
51 {
52 offsetY = Max(cast<int>(0), cast<int>(lines.Count()) - screenHeight);
53 }
54
55 void LineUp(int& offsetY)
56 {
57 if (offsetY > 0)
58 {
59 --offsetY;
60 }
61 }
62
63 void LineDown(const List<ustring>& lines, int& offsetY, int screenHeight)
64 {
65 if (offsetY < lines.Count() - screenHeight)
66 {
67 ++offsetY;
68 }
69 }
70
71 void PageUp(int& offsetY, int screenHeight)
72 {
73 offsetY = Max(cast<int>(0), offsetY - screenHeight);
74 }
75
76 void PageDown(const List<ustring>& lines, int& offsetY, int screenHeight)
77 {
78 offsetY = Max(cast<int>(0), Min(offsetY + screenHeight, cast<int>(lines.Count()) - screenHeight));
79 }
80
81 void View(List<ustring>& lines)
82 {
83 SetRaw(Terminal.Descriptor());
84 SetEcho(Terminal.Descriptor(), false);
85 int maxWidth = MaxWidth(lines);
86 int screenWidth = TerminalWindowWidth();
87 int screenHeight = TerminalWindowHeight();
88 List<ustring> lns;
89 if (maxWidth > screenWidth)
90 {
91 for (const ustring& line : lines)
92 {
93 if (line.Length() > screenWidth)
94 {
95 lns.Add(line.Substring(0, screenWidth));
96 lns.Add(line.Substring(screenWidth));
97 }
98 else
99 {
100 lns.Add(line);
101 }
102 }
103 Swap(lines, lns);
104 }
105 int offsetY = 0;
106 ViewScreen(lines, offsetY, screenWidth, screenHeight);
107 uchar key = ReadKey(Terminal.Descriptor());
108 while (key != keyEscape)
109 {
110 int prevOffsetY = offsetY;
111 switch (key)
112 {
113 case keyHome:
114 {
115 Start(offsetY);
116 break;
117 }
118 case keyEnd:
119 {
120 End(lines, offsetY, screenHeight - 1);
121 break;
122 }
123 case keyUp:
124 {
125 LineUp(offsetY);
126 break;
127 }
128 case keyDown:
129 {
130 LineDown(lines, offsetY, screenHeight - 1);
131 break;
132 }
133 case keyPgUp:
134 {
135 PageUp(offsetY, screenHeight - 1);
136 break;
137 }
138 case keyPgDown:
139 {
140 PageDown(lines, offsetY, screenHeight - 1);
141 break;
142 }
143 }
144 if (prevOffsetY != offsetY)
145 {
146 ViewScreen(lines, offsetY, screenWidth, screenHeight);
147 }
148 key = ReadKey(Terminal.Descriptor());
149 }
150 ClearScreen(screenWidth, screenHeight);
151 SetCursorPos(0, 0);
152 }
153
154 void ViewFiles(const List<string>& files)
155 {
156 List<ustring> lines;
157 for (const string& file : files)
158 {
159 StreamReader reader(SharedPtr<Stream>());
160 if (!file.IsEmpty())
161 {
162 reader = File.OpenRead(file);
163 }
164 else
165 {
166 if (IsConsole(0))
167 {
168 reader = StreamReader(SharedPtr<Stream>(new FileStream(0)));
169 }
170 else
171 {
172 reader = StreamReader(SharedPtr<Stream>(new BufferedStream(SharedPtr<Stream>(new FileStream(0)))));
173 }
174 }
175 while (!reader.EndOfStream())
176 {
177 string line = reader.ReadLine();
178 lines.Add(ToUtf32(line));
179 }
180 }
181 if (!IsConsole(1))
182 {
183 for (const ustring& line : lines)
184 {
185 Console.Out() << line << endl();
186 }
187 }
188 else
189 {
190 View(lines);
191 }
192 }
193
194 int main(int argc, const char** argv)
195 {
196 try
197 {
198 List<string> files;
199 for (int i = 1; i < argc; ++i;)
200 {
201 string arg = argv[i];
202 if (arg.StartsWith("--"))
203 {
204 if (arg == "--help")
205 {
206 PrintHelp();
207 return 1;
208 }
209 else
210 {
211 throw Exception("unknown option '" + arg + "'");
212 }
213 }
214 else if (arg.StartsWith("-"))
215 {
216 string options = arg.Substring(1);
217 for (char o : options)
218 {
219 bool unknown = false;
220 string uo;
221 switch (o)
222 {
223 case 'h':
224 {
225 PrintHelp();
226 return 1;
227 }
228 default:
229 {
230 uo.Append(o);
231 unknown = true;
232 break;
233 }
234 }
235 if (unknown)
236 {
237 throw Exception("unknown option '-" + uo + "'");
238 }
239 }
240 }
241 else
242 {
243 files.Add(arg);
244 }
245 }
246 if (files.IsEmpty())
247 {
248 files.Add(string());
249 }
250 ViewFiles(files);
251 }
252 catch (const Exception& ex)
253 {
254 Console.Error() << ex.ToString() << endl();
255 return 1;
256 }
257 SetCooked(Terminal.Descriptor());
258 SetEcho(Terminal.Descriptor(), true);
259 return 0;
260 }