1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998-2000 Paul Brannan
4 //Copyright (C) 1998 I.Ioannou
5 //Copyright (C) 1997 Brad Johnson
6 //
7 //This program is free software; you can redistribute it and/or
8 //modify it under the terms of the GNU General Public License
9 //as published by the Free Software Foundation; either version 2
10 //of the License, or (at your option) any later version.
11 //
12 //This program is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with this program; if not, write to the Free Software
19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //I.Ioannou
22 //roryt@hol.gr
23 //
24 ///////////////////////////////////////////////////////////////////////////
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 //
28 // Module:		tscroll.cpp
29 //
30 // Contents:	Telnet Handler
31 //
32 // Product:		telnet
33 //
34 // Revisions: Dec. 5, 1998	Paul Brannan <pbranna@clemson.edu>
35 //            June 15, 1998 Paul Brannan
36 //
37 //            This is code originally from tnclass.cpp and ansiprsr.cpp
38 //
39 ///////////////////////////////////////////////////////////////////////////////
40 
41 #include "precomp.h"
42 
43 enum {
44 	HEX,
45 	DUMP,
46 	DUMPB,
47 	TEXTB,
48 };
49 
50 int DummyStripBuffer(char *start, char *end, int width) {return 0;}
51 
52 TScroller::TScroller(TMouse &M, int size) : Mouse(M) {
53 	iScrollSize = size;
54 	pcScrollData = new char[iScrollSize];
55 	iScrollEnd = 0;
56 	iPastEnd = 0;
57 	memset(pcScrollData, ' ', iScrollSize);
58 
59 	if(stricmp(ini.get_scroll_mode(), "hex") == 0) iDisplay = HEX;
60 	else if(stricmp(ini.get_scroll_mode(), "dump") == 0) iDisplay = DUMP;
61 	else if(stricmp(ini.get_scroll_mode(), "dumpb") == 0) iDisplay = DUMPB;
62 	else if(stricmp(ini.get_scroll_mode(), "text") == 0) iDisplay = TEXTB;
63 	else iDisplay = DUMP;
64 
65 	strip = &DummyStripBuffer;
66 }
67 
68 TScroller::~TScroller() {
69 	delete[] pcScrollData;
70 }
71 
72 void TScroller::init(stripfunc *s) {
73 	strip = s;
74 }
75 
76 // Fixed update of circular buffer (Paul Brannan 12/4/98)
77 // Note: iScrollEnd is one character beyond the end
78 void TScroller::update(const char *pszHead, const char *pszTail) {
79 	if ((iScrollEnd)+(pszTail-pszHead) < iScrollSize) {
80 		memcpy(&pcScrollData[iScrollEnd], pszHead, pszTail-pszHead);
81 	} else if (pszTail-pszHead > iScrollSize) {
82 		memcpy(pcScrollData, pszTail-iScrollSize, iScrollSize);
83 		iScrollEnd = 0;
84 	} else {
85 		memcpy(&pcScrollData[iScrollEnd], pszHead, iScrollSize-iScrollEnd);
86 		memcpy(&pcScrollData[0], pszHead + (iScrollSize-iScrollEnd),
87 			pszTail-pszHead-(iScrollSize-iScrollEnd));
88 	}
89 
90 	// This could probably be optimized better, but it's probably not worth it
91 	int temp = iScrollEnd;
92 	iScrollEnd = ((iScrollEnd)+(pszTail-pszHead))%iScrollSize;
93 	if(iScrollEnd < temp) iPastEnd = 1;
94 }
95 
96 // Perhaps this should be moved to Tconsole.cpp? (Paul Brannan 6/12/98)
97 static BOOL WriteConsoleOutputCharAndAttribute(
98 										HANDLE  hConsoleOutput,	// handle of a console screen buffer
99 										CHAR * lpWriteBuffer,
100 										WORD wAttrib,
101 										SHORT sX,
102 										SHORT sY ){
103 	// we ought to allocate memory before writing to an address (PB 5/12/98)
104 	DWORD cWritten;
105 	const LPDWORD lpcWritten = &cWritten;
106 
107 	DWORD  cWriteCells = strlen(lpWriteBuffer);
108 	COORD  coordWrite = {sX,sY};
109 	LPWORD lpwAttribute = new WORD[cWriteCells];
110 	for (unsigned int i = 0; i < cWriteCells; i++)
111 		lpwAttribute[i] = wAttrib;
112 	WriteConsoleOutputAttribute(
113 		hConsoleOutput,			// handle of a console screen buffer
114 		lpwAttribute,			// address of buffer to write attributes from
115 		cWriteCells,			// number of character cells to write to
116 		coordWrite,				// coordinates of first cell to write to
117 		lpcWritten				// address of number of cells written to
118 		);
119 	WriteConsoleOutputCharacter(
120 		hConsoleOutput,			// handle of a console screen buffer
121 		lpWriteBuffer,			// address of buffer to write characters from
122 		cWriteCells,			// number of character cells to write to
123 		coordWrite,				// coordinates of first cell to write to
124 		lpcWritten				// address of number of cells written to
125 		);
126 	delete [] lpwAttribute;
127 	return 1;
128 }
129 
130 static void hexify(int x, char *str, int len) {
131 	for(int j = len - 1; j >= 0; j--) {
132 		str[j] = x % 16;
133 		if(str[j] > 9) str[j] += 'A' - 10;
134 		else str[j] += '0';
135 		x /= 16;
136 	}
137 }
138 
139 static int setmaxlines(int iDisplay, int iScrollSize, int strippedlines,
140 				int con_width) {
141 	switch(iDisplay) {
142 		case HEX: return(iScrollSize / 16); break;
143 		case DUMP:
144 		case DUMPB: return(iScrollSize / con_width); break;
145 		case TEXTB: return(strippedlines); break;
146 	}
147 	return 0;
148 }
149 
150 static void setstatusline(char *szStatusLine, int len, int iDisplay) {
151 	memset(szStatusLine, ' ', len);
152 	memcpy(&szStatusLine[1], "Scrollback Mode", 15);
153 	switch(iDisplay) {
154 	case HEX: memcpy(&szStatusLine[len / 2 - 1], "HEX", 3); break;
155 	case DUMP: memcpy(&szStatusLine[len / 2 - 2], "DUMP", 4); break;
156 	case DUMPB: memcpy(&szStatusLine[len / 2 - 5], "BINARY DUMP", 11); break;
157 	case TEXTB: memcpy(&szStatusLine[len / 2 - 2], "TEXT", 4); break;
158 	}
159 	memcpy(&szStatusLine[len - 6], "READY", 5);
160 	szStatusLine[len] = 0;
161 }
162 
163 void TScroller::ScrollBack(){
164 	char p;
165 	int r,c;
166 
167 	// define colors (Paul Brannan 7/5/98)
168 	int normal = (ini.get_scroll_bg() << 4) | ini.get_scroll_fg();
169 	// int inverse = (ini.get_scroll_fg() << 4) | ini.get_scroll_bg();
170 	int status = (ini.get_status_bg() << 4) | ini.get_status_fg();
171 
172 	CHAR_INFO* chiBuffer;
173 	chiBuffer = newBuffer();
174 	saveScreen(chiBuffer);
175 
176 	HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
177 	CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
178 	GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo);
179 
180 	// Update iScrollBegin -- necessary in case the buffer isn't full yet
181 	long iScrollBegin, iScrollLast;
182 	if(iPastEnd == 0) {
183 		iScrollBegin = 0;
184 		iScrollLast = iScrollEnd - 1;
185 	} else {
186 		iScrollBegin = iScrollEnd;
187 		iScrollLast = iScrollSize - 1;
188 	}
189 
190 	// Create buffer with ANSI codes stripped
191 	// Fixed this to work properly with a circular buffer (PB 12/4/98)
192 	char *stripped = new char[iScrollSize];
193 	memcpy(stripped, pcScrollData + iScrollBegin, iScrollSize -
194 		iScrollBegin);
195 	if(iScrollBegin != 0) memcpy(stripped + (iScrollSize - iScrollBegin),
196 		pcScrollData, iScrollBegin - 1);
197 	int strippedlines = (*strip)(stripped, stripped + iScrollLast,
198 		CON_COLS);
199 
200 	// Calculate the last line of the scroll buffer (Paul Brannan 12/4/98)
201 	int maxlines = setmaxlines(iDisplay, iScrollLast + 1, strippedlines,
202 		CON_COLS);
203 
204 	// init scroll position
205 	int current = maxlines - CON_HEIGHT + 1;
206 	if(current < 0) current = 0;
207 
208 	// paint border and info
209     // paint last two lines black on white
210     char * szStatusLine;
211     szStatusLine = new char[CON_WIDTH+2];
212 	setstatusline(szStatusLine, CON_COLS, iDisplay);
213     WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, status,
214 		CON_LEFT, CON_BOTTOM);
215 
216 	// loop while not done
217 	BOOL done = FALSE;
218 	while (!done){
219 		switch (iDisplay){
220 		case HEX:
221 			memset(szStatusLine, ' ', CON_COLS);
222 			szStatusLine[8] = ':';
223 			szStatusLine[34] = '-';
224             for (r = 0; r < CON_HEIGHT; r++) {
225 				hexify((r + current) * 16, &szStatusLine[2], 6);
226 				for (c = 0; c < 16; c++){
227 					if (c+(16*(r+current)) >= iScrollLast)
228 						p = 0;
229 					else
230 						p = pcScrollData[(c+16*(r+current) + iScrollBegin) %
231 							iScrollSize];
232 					hexify((char)p, &szStatusLine[11 + 3*c], 2);
233 					if (!iscntrl(p)) {
234 						szStatusLine[60 + c] = (char)p;
235 					} else {
236 						szStatusLine[60 + c] = '.';
237 					}
238 				}
239 				for(int j = 0; j < 16; j++) {
240 				}
241 				szStatusLine[CON_COLS] = '\0';
242 				WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine,
243 					normal, CON_LEFT, r+CON_TOP);
244             }
245             break;
246 		case DUMP:
247             for (r = 0; r < CON_HEIGHT; r++) {
248 				for (c = 0; c <= CON_WIDTH; c++) {
249 					if (c+((CON_COLS)*(r+current)) >= iScrollLast) p = ' ';
250 					else p = pcScrollData[(c+((CON_COLS)*(r+current))
251 						+ iScrollBegin)	% iScrollSize];
252 					if (!iscntrl(p))
253 						szStatusLine[c] = p;
254 					else
255 						szStatusLine[c] = '.';
256 				}
257 				szStatusLine[c] = '\0';
258 				WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine,
259 					normal, CON_LEFT, r+CON_TOP);
260             }
261             break;
262 		case DUMPB:
263 			for (r = 0; r < CON_HEIGHT; r++) {
264 				for (c = 0; c <= CON_WIDTH; c++) {
265 					if (c+((CON_COLS)*(r+current)) >= iScrollLast) p = ' ';
266 					else p = pcScrollData[  (c+((CON_COLS)*(r+current))
267 						+ iScrollBegin) % iScrollSize];
268 					if (p != 0)
269 						szStatusLine[c] = p;
270 					else
271 						szStatusLine[c] = ' ';
272 				}
273 				szStatusLine[c] = '\0';
274 				WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine,
275 					normal, CON_LEFT, r+CON_TOP);
276             }
277             break;
278 		case TEXTB: {
279 			int ch, lines, x;
280 			// Find the starting position
281 			for(ch = 0, lines = 0, x = 1; ch < iScrollSize &&
282 				lines < current; ch++, x++) {
283 
284 				if(stripped[ch] == '\n') lines++;
285 				if(stripped[ch] == '\r') x = 1;
286 			}
287 
288 			for (r = 0; r < CON_HEIGHT; r++) {
289 				memset(szStatusLine, ' ', CON_COLS);
290 				for(c = 0; c <= CON_WIDTH; c++) {
291 					done = FALSE;
292 					if (ch >= iScrollSize) p = ' ';
293 					else p = stripped[ch];
294 					switch(p) {
295 					case 10: done = TRUE; break;
296 					case 13: c = 0; break;
297 					default: szStatusLine[c] = p;
298 					}
299 					ch++;
300 					if(done) break;
301 				}
302 				szStatusLine[CON_COLS] = '\0';
303 				WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine,
304 					normal, CON_LEFT, r+CON_TOP);
305 			}
306 					}
307             break;
308 		}
309 
310 		setstatusline(szStatusLine, CON_COLS, iDisplay);
311 		WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, status,
312 			CON_LEFT, CON_BOTTOM);
313 
314 		// paint scroll back data
315 		// get key input
316 		switch(scrollkeys()){
317 		case VK_ESCAPE:
318 			done = TRUE;
319 			break;
320 		case VK_PRIOR:
321 			if ( current > CON_HEIGHT)
322 				current-= CON_HEIGHT;
323 			else
324 				current = 0;
325 			break;
326 		case VK_NEXT:
327 			if ( current < maxlines - 2*CON_HEIGHT + 2)
328 				current += CON_HEIGHT;
329 			else
330 				current = maxlines - CON_HEIGHT + 1;
331 			break;
332 		case VK_DOWN:
333 			if (current <= maxlines - CON_HEIGHT) current++;
334 			break;
335 		case VK_UP:
336 			if ( current > 0) current--;
337 			break;
338 		case VK_TAB:
339 			iDisplay = (iDisplay+1)%4;
340 			maxlines = setmaxlines(iDisplay, iScrollLast + 1, strippedlines,
341 				CON_COLS);
342 			if(current > maxlines) current = maxlines - 1;
343 			if(current < 0) current = 0;
344 			break;
345 		case VK_END:
346 			current = maxlines - CON_HEIGHT + 1;
347 			if(current < 0) current = 0;
348 			break;
349 		case VK_HOME:
350 			current = 0;
351 			break;
352 		case SC_MOUSE:
353 			Mouse.scrollMouse();
354 			break;
355 		}
356 	}
357 
358 	// Clean up
359 	restoreScreen(chiBuffer);
360 	delete[] szStatusLine;
361 	delete[] chiBuffer;
362 	delete[] stripped;
363 }
364