1 /**
2  * termdata.cpp - Store terminal screen data and parse
3  *                ANSI escape sequence.
4  *
5  * Copyright (c) 2004-2005 PCMan <pcman.tw@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (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 Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #ifdef __GNUG__
23   #pragma implementation "termdata.h"
24 #endif
25 
26 
27 #include "termdata.h" // class's header file
28 #include "termview.h" // class's header file
29 #include "termsel.h" // class's header file
30 #include <cstring>
31 #include <stdint.h>
32 
33 #include <algorithm>
34 
35 using std::swap;
36 
37 /////////////////////////////////////////////////////////////////////////////
38 //The functions section of CTermAttr class.
39 //
40 /////////////////////////////////////////////////////////////////////////////
41 
42 //GdkColor = (red, green, blue)
43 
44 GdkColor
45 CTermCharAttr::m_DefaultColorTable[SIZE_OF_COLOR_TABLE] = {
46 	//Darker color
47 	{0,0,0,0}, 		//0;30m		Black
48 	{0,65536/2,0,0},		//0;31m		Dark red
49 	{0,0,65536/2,0},		//0;32m		Dark green
50 	{0,65536/2,65536/2,0},	//0;33m		Brown
51 	{0,0,0,65536/2},		//0;34m		Dark blue
52 	{0,65536/2,0,65536/2},	//0;35m		Dark magenta
53 	{0,0,65536/2,65536/2},	//0;36m		Dark cyan
54 	{0,65536*192/256,65536*192/256,65536*192/256},	//0;37m		Light gray
55 	//Bright color
56 	{0,65536/2,65536/2,65536/2},	//1;30m		Gray
57 	{0,65535,0,0},		//1;31m		Red
58 	{0,0,65535,0},		//1;32m		Green
59 	{0,65535,65535,0},	//1;33m		Yellow
60 	{0,0,0,65535},		//1;34m		Blue
61 	{0,65535,0,65535},	//1;35m		Magenta
62 	{0,0,65535,65535},	//1;36m		Cyan
63 	{0,65535,65535,65535}	//1;37m		White
64 };
65 
66 //Update this old attribute of character with new one which filter by [flags].
67 //e.g., when [flags] = STA_FG | STA_BRIGHT, the old attribute updates
68 //attributes Foreground and Bright only.
69 //After updating, set Need Update flag as true.
70 void
SetTextAttr(CTermCharAttr attr,int flags)71 CTermCharAttr::SetTextAttr( CTermCharAttr attr, int flags )
72 {
73 	if( flags & STA_FG )
74 		m_Fg = attr.m_Fg;
75 	if( flags & STA_BG )
76 		m_Bg = attr.m_Bg;
77 	if( flags & STA_BRIGHT )
78 		m_Bright = attr.m_Bright;
79 	if( flags & STA_BLINK )
80 		m_Blink = attr.m_Blink;
81 	if( flags & STA_UNDERLINE )
82 		m_UnderLine = attr.m_UnderLine;
83 	if( flags & STA_INVERSE )
84 		m_Inverse = attr.m_Inverse;
85 	if( flags & STA_INVISIBLE )
86 		m_Invisible = attr.m_Invisible;
87 	m_NeedUpdate = 1;
88 }
89 
90 // I don't know whether assign an 'short' to this 'object' directly will cause
91 // problems or not hence preventing using it.  Otherwise I can return 7 directly;
92 CTermCharAttr::AttrType
GetDefVal()93 CTermCharAttr::GetDefVal(){
94 	CTermCharAttr attr;
95 	*(AttrType*)&attr=0;
96 	attr.m_Fg = 7;
97 	return *(AttrType*)&attr;
98 }
99 
100 
101 void
SetToDefault()102 CTermCharAttr::SetToDefault(){ *(AttrType*)this = 0;	m_Fg=7;}
103 bool
operator ==(CTermCharAttr & attr)104 CTermCharAttr::operator==(CTermCharAttr& attr){
105 	return ((m_Fg == attr.m_Fg) &&
106 			(m_Bg == attr.m_Bg) &&
107 			(m_Bright == attr.m_Bright) &&
108 			(m_Blink == attr.m_Blink) &&
109 			(m_UnderLine == attr.m_UnderLine) &&
110 			(m_Inverse == attr.m_Inverse) &&
111 			(m_Invisible == attr.m_Invisible));
112 }
113 
114 
115 /////////////////////////////////////////////////////////////////////////////
116 //The functions section of CTermData class.
117 //
118 /////////////////////////////////////////////////////////////////////////////
119 
120 // class constructor
CTermData(CTermView * pView)121 CTermData::CTermData(CTermView* pView) :
122 	m_pView(pView),
123 	m_pCmdLine(NULL),
124 	m_Screen(NULL)
125 {
126 	m_CaretPos.x = m_CaretPos.y = 0;
127 	m_OldCaretPos = m_CaretPos;
128 	m_FirstLine = 0;
129 	m_RowCount = 0;
130 	m_RowsPerPage = m_ColsPerPage = 0;
131 	m_ScrollRegionBottom = m_ScrollRegionTop = 0;
132 	m_CurAttr.SetToDefault();
133 	m_SavedAttr.SetToDefault();
134 	m_CmdLine[0] = '\0';
135 	m_WaitUpdateDisplay = false;
136 	m_NeedDelayedUpdate = false;
137 	m_DelayedUpdateTimeout = 0;
138 	m_Sel = new CTermSelection(this);
139 #ifdef USE_IPLOOKUP
140 	regcomp( &m_RegIp, "([0-9]{1,3}\\.){3}([0-9]{1,3}|\\*)", REG_EXTENDED );
141 #endif
142 	m_LineCounter = 0;
143 }
144 
145 // class destructor
~CTermData()146 CTermData::~CTermData()
147 {
148 	delete m_Sel;
149 
150 	if( m_DelayedUpdateTimeout )
151 		g_source_remove(m_DelayedUpdateTimeout);
152 
153 	if( m_Screen )
154 	{
155 		for(int i=0; i<m_RowCount; i++)
156 			delete []m_Screen[i];
157 		delete []m_Screen;
158 	}
159 
160 #ifdef USE_IPLOOKUP
161 	regfree(&m_RegIp);
162 #endif
163 }
164 
165 
166 // Set sizes of screen buffer, reallocate buffer automatically when needed.
SetScreenSize(int RowCount,unsigned short RowsPerPage,unsigned short ColsPerPage)167 void CTermData::SetScreenSize( int RowCount, unsigned short RowsPerPage,
168      unsigned short ColsPerPage)
169 {
170     m_RowsPerPage = RowsPerPage;
171     // if cols per page change, reallocate all existing rows.
172     if( m_ColsPerPage != ColsPerPage )
173     {
174         for(int i=0; i < m_RowCount; i++)
175         {
176             char* NewLine = AllocNewLine(ColsPerPage);
177             unsigned short Cols = (ColsPerPage < m_ColsPerPage)?ColsPerPage:m_ColsPerPage;
178             //Copy context of old into new one.
179             memcpy(NewLine, m_Screen[i], Cols);
180             memcpy(GetLineAttr(NewLine, ColsPerPage), GetLineAttr(m_Screen[i]), sizeof(CTermCharAttr::AttrType)*Cols);
181             delete []m_Screen[i];
182             m_Screen[i] = NewLine;
183         }
184         m_ColsPerPage = ColsPerPage;
185     }
186     SetRowCount(RowCount);
187 }
188 
189 // Change row count of screen buffer
SetRowCount(int RowCount)190 void CTermData::SetRowCount(int RowCount)
191 {
192     if( RowCount == m_RowCount )
193         return;
194 
195     char** NewScreen = new char* [RowCount];
196     if( RowCount > m_RowCount )    // increase row count
197     {
198         memcpy(NewScreen, m_Screen, sizeof(char**)*m_RowCount);
199         for( int i = m_RowCount; i < RowCount; i++ )
200                 NewScreen[i] = AllocNewLine(m_ColsPerPage);
201     }
202     else        // decrease row count
203     {
204         memcpy(NewScreen, m_Screen, sizeof(char**)*RowCount);
205         for( int i = RowCount; i < m_RowCount; i++ )
206                 delete []m_Screen[i];
207     }
208     delete []m_Screen;
209     m_Screen = NewScreen;
210     m_RowCount = RowCount;
211 }
212 
213 // Allocate screen buffer.
AllocScreenBuf(int RowCount,unsigned short RowsPerPage,unsigned short ColsPerPage)214 void CTermData::AllocScreenBuf(int RowCount, unsigned short RowsPerPage,unsigned short ColsPerPage){
215     m_RowCount = RowCount;
216     m_RowsPerPage = RowsPerPage;
217     m_ColsPerPage = ColsPerPage;
218 
219     m_Screen = new char* [m_RowCount];
220     for(int i=0; i < m_RowCount; i++)
221         m_Screen[i] = AllocNewLine(m_ColsPerPage);
222 
223     m_FirstLine = m_RowCount - m_RowsPerPage;
224     m_ScrollRegionTop = 0;
225     m_ScrollRegionBottom = m_RowsPerPage-1;
226 }
227 
228 // Initialize new lines
InitNewLine(char * NewLine,const int ColsPerPage)229 void CTermData::InitNewLine(char* NewLine, const int ColsPerPage){
230 		memset( NewLine, ' ', ColsPerPage);
231 		NewLine[ColsPerPage] = '\0';
232 		CTermCharAttr DefAttr;	DefAttr.SetToDefault();	DefAttr.SetNeedUpdate(true);
233 		memset16( GetLineAttr(NewLine, ColsPerPage), DefAttr.AsType(), ColsPerPage);
234 }
235 
236 // LF handler
LineFeed()237 void CTermData::LineFeed()
238 {
239 	int top;
240 	int bottom = m_FirstLine + m_ScrollRegionBottom;
241 
242 	m_LineCounter++;
243 
244 	if(m_CaretPos.y < bottom)
245 	{
246 		m_CaretPos.y++;
247 		return;
248 	}
249 	else if( m_ScrollRegionBottom != (m_RowsPerPage-1) || m_ScrollRegionTop != 0 )
250 	{
251 		top = m_FirstLine + m_ScrollRegionTop;
252 	//	bottom = m_FirsLine + m_ScrollRegionBottom-1;
253 	}
254 	else
255 	{
256 		top = 0;
257 		bottom = m_RowCount-1;
258 	}
259 
260 	char* tmp = m_Screen[top];
261 	InitNewLine(tmp, m_ColsPerPage);
262 	for( int i = top; i < bottom; i++ )
263 	{
264 		m_Screen[i] = m_Screen[i+1];
265 		SetWholeLineUpdate(m_Screen[i]);
266 	}
267 	m_Screen[bottom] = tmp;
268 
269 	m_NeedDelayedUpdate = true;
270 }
271 
272 // BS handler
Back()273 void CTermData::Back()
274 {
275 	if(m_CaretPos.x >0 )
276 		m_CaretPos.x-- ;
277 }
278 
279 // BEL handler
Bell()280 void CTermData::Bell()	//	virtual
281 {
282 	//	Call virtual function in dirived class to determine
283 	//	whether to beep or show a visual indication instead.
284 }
285 
286 // CR handler
CarriageReturn()287 void CTermData::CarriageReturn()
288 {
289 	m_CaretPos.x = 0;
290 }
291 
292 // TAB handler
Tab()293 void CTermData::Tab()
294 {
295 //	m_CaretPos.x += ((m_CaretPos.x/8)*8)+8;
296 	m_CaretPos.x += ((m_CaretPos.x/4)*4)+4;
297 }
298 
PutChar(unsigned char ch)299 void CTermData::PutChar(unsigned char ch)
300 {
301 	// C0 control charcters have higher precedence than ANSI escape sequences.
302 	// interpret them first whether in ANSI escape sequence or not
303 	if( ch < ' ' )    // if this is a control character
304 	{
305 		switch( ch )
306 		{
307 		case '\x1b':      // ESC
308 			m_CmdLine[0] = '\x1b';
309 			m_pCmdLine = &m_CmdLine[1];
310 			break;
311 		case '\n':        // LF, line feed
312 			LineFeed();
313 			break;
314 		case '\r':        // CR, carriage return
315 			CarriageReturn();
316 			break;
317 		case '\b':        // BS, backspace
318 			Back();
319 			break;
320 		case '\a':        // BEL, bell
321 			Bell();
322 			break;
323 		case '\t':        // HT, horizontal tab
324 			Tab();
325 			break;
326 		}
327     }
328 	else	// not C0 control characters, check if we're in control sequence.
329 	{
330 		switch( m_CmdLine[0] )
331 		{
332 		// m_CmdLine[0] == '\0' means "not in control sequence," and *m_pBuf
333 		// is normal text, write to screen buffer directly.
334 		case '\0':
335 			{
336 				if( m_CaretPos.x >= m_ColsPerPage )	// if we are at the bottom of screen
337 				{
338 					//LineFeed();
339 					//CarriageReturn();	//	scroll up and move to a new line
340 					break;
341 				}
342 
343 				m_Screen[m_CaretPos.y][m_CaretPos.x] = ch;
344 
345 				CTermCharAttr* pAttr = GetLineAttr( m_Screen[m_CaretPos.y] );
346 
347 				// Check if we've changed a character which is part of a URL.
348 				bool bHyperLink = pAttr[m_CaretPos.x].IsHyperLink();
349 				pAttr[m_CaretPos.x] = m_CurAttr;
350 				// Set char attributes of current character out put to screen
351 
352 				// Important:
353 				// Set the update flag to indicate "redrawing needed."
354 				pAttr[m_CaretPos.x].SetNeedUpdate(true);
355 				// 2004.08.07 Added by PCMan:
356 				// If we've changed a character which is originally part of a URL,
357 				// whole URL must be redrawn or underline won't be updated correctly.
358 				if( bHyperLink )
359 				{
360 					int col;
361 					for( col = m_CaretPos.x-1; col > 0 && pAttr[col].IsHyperLink(); col-- )
362 						pAttr[col].SetNeedUpdate(true);
363 					for( col = m_CaretPos.x+1; col < m_ColsPerPage && pAttr[col].IsHyperLink(); col++ )
364 						pAttr[col].SetNeedUpdate(true);
365 				}
366 				//	Advance the caret after character input.
367 				m_CaretPos.x++;
368 			break;
369 			}
370 		// m_CmdLine[0] == '\0' means we're currently in ANSI control sequence.
371 		// Store ch to CmdLine, and parse ANSI escape sequence when ready.
372 		case '\x1b':        // ESC, in ANSI escape mode
373 			if( m_pCmdLine < (m_CmdLine +sizeof(m_CmdLine)) )
374 			{
375 				*m_pCmdLine = ch;
376 				m_pCmdLine++;
377 			}
378 
379 			if( m_CmdLine[1] == '[' )
380 			{
381 				if( ch < '@' || ch == '[' || ch > '~' )
382 					break;
383 			}
384 			else
385 			{
386 				if( ch < '0' || ch > '_' )
387 					break;
388 			}
389 
390 			if( m_pCmdLine < (m_CmdLine +sizeof(m_CmdLine)) )
391 				*m_pCmdLine = '\0';
392 			// Current ANSI escape type is stored in *m_pBuf.
393 			ParseAnsiEscapeSequence( (const char*)m_CmdLine, ch);
394 			m_CmdLine[0] = '\0';
395 			m_pCmdLine = m_CmdLine;
396 		} // end switch( m_CmdLine[0] )
397 	}
398 }
399 
InsertNewLine(int y,int count)400 void CTermData::InsertNewLine(int y, int count)
401 {
402 	short tmp = m_ScrollRegionTop;
403 	m_ScrollRegionTop = y;
404 	ScrollDown( count );
405 	m_ScrollRegionTop = tmp;
406 }
407 
ScrollUp(int n)408 void CTermData::ScrollUp(int n /*=1*/)
409 {
410 	int maxn = m_ScrollRegionBottom - m_ScrollRegionTop +1;
411 	if( n > maxn )
412 		n = maxn;
413 
414 	int start = m_FirstLine + m_ScrollRegionTop;
415 	int end = m_FirstLine + m_ScrollRegionBottom - n;
416 	int i;
417 	for( i = start; i <= end; i++ )
418 	{
419 		// Swap two lines to prevent memmory reallocation.
420 		swap(m_Screen[i], m_Screen[i + n]);
421 		SetWholeLineUpdate(m_Screen[i]);
422 	}
423 	for( i = 1; i <= n; i++ )
424 	{
425 		memset( m_Screen[end+i], ' ', m_ColsPerPage-1 );
426 		memset16( GetLineAttr(m_Screen[end+i]), m_CurAttr.AsType(), m_ColsPerPage-1 );
427 		SetWholeLineUpdate(m_Screen[end+i]);
428 	}
429 }
430 
ScrollDown(int n)431 void CTermData::ScrollDown(int n /*=1*/)
432 {
433 	int maxn = m_ScrollRegionBottom - m_ScrollRegionTop +1;
434 	if( n > maxn )
435 		n = maxn;
436 
437 	int start = m_FirstLine + m_ScrollRegionBottom;
438 	int end = m_FirstLine + m_ScrollRegionTop + n;
439 	int i;
440 	for( i = start; i >= end; i-- )
441 	{
442 		// Swap two lines to prevent memmory reallocation.
443 		swap(m_Screen[i], m_Screen[i - n]);
444 		SetWholeLineUpdate(m_Screen[i]);
445 	}
446 	for( i = 1; i <= n; i++ )
447 	{
448 		memset( m_Screen[end-i], ' ', m_ColsPerPage-1 );
449 		memset16( GetLineAttr(m_Screen[end-i]), m_CurAttr.AsType(), m_ColsPerPage-1 );
450 		SetWholeLineUpdate(m_Screen[end-i]);
451 	}
452 }
453 
454 // Parse ANSI escape sequence
ParseAnsiEscapeSequence(const char * CmdLine,char type)455 void CTermData::ParseAnsiEscapeSequence(const char* CmdLine, char type)
456 {
457 	// Current ANSI escape type is stored in *m_pBuf.
458 	if( m_CmdLine[1] == '[' )	// ESC[, CSI: control sequence introducer
459 	{
460 		//	CmdLine[] = {'\x1b', '[', ...'\0'};
461 		const char* pParam = &CmdLine[2];
462 		if(type == 'm' )	// multiple parameters, view as a special case.
463 			ParseAnsiColor(pParam);
464 		else
465 		{
466 			int p1=0, p2=0;
467 			int n = sscanf(pParam, "%d;%d",&p1,&p2);
468 			if( p1 < 0 )	p1 = 0;
469 			if( p2 < 0 )	p2 = 0;
470 			switch(type)
471 			{
472 			case 'K':	//	Clear Line
473 				EraseLine(p1);
474 				break;
475 			case 'H':	// Set Caret Pos
476 			case 'f':
477 				GoToXY(p2-1, p1-1);
478 				break;
479 			case 'J':	// Clear Screen
480 				ClearScreen(p1);
481 				break;
482 			case 'E':
483 				break;
484 			case 'L':
485 				InsertNewLine(m_CaretPos.y, p1);
486 				break;
487 			case 'A':
488 				if(p1 <= 0)
489 					p1=1;
490 				GoToXY( m_CaretPos.x, m_CaretPos.y - p1 );
491 				break;
492 			case 'B':
493 				if( p1<=0 )
494 					p1=1;
495 				GoToXY( m_CaretPos.x, m_CaretPos.y + p1 );
496 				break;
497 			case 'C':
498 				if( p1<=0 )
499 					p1=1;
500 				GoToXY( m_CaretPos.x + p1, m_CaretPos.y );
501 				break;
502 			case 'D':
503 				if( p1<=0 )
504 					p1=1;
505 				GoToXY( m_CaretPos.x - p1, m_CaretPos.y );
506 				break;
507 			case 'r':	// Set Scroll Region
508 				switch(n)
509 				{
510 				case 0:	// Turn off scroll region
511 					m_ScrollRegionTop = 0; m_ScrollRegionBottom = m_RowsPerPage-1;
512 					break;
513 				case 2:
514 					p2--;
515 					if( p2 > 0 && p2 < m_RowsPerPage && p2 >= m_ScrollRegionTop )
516 						m_ScrollRegionBottom = p2;
517 				case 1:
518 					p1--;
519 					if( p1 <= m_ScrollRegionBottom )
520 						m_ScrollRegionTop = p1;
521 					break;
522 				}
523 //				printf("scroll region: %d, %d\n", m_ScrollRegionTop, m_ScrollRegionBottom );
524 				break;
525 			case 's':	//save cursor pos
526 				break;
527 			case 'u':	//restore cursor pos
528 				break;
529 			case '@':	//insert char
530 				break;
531 			case 'M':	//delete n line
532 				break;
533 			case 'P':	//delete char
534 				break;
535 /*
536 			case 'U':	//next n page
537 				break;
538 			case 'V':	//previous n page
539 				break;
540 */
541 			case 'Z':	//cursor back tab
542 				break;
543 			case 'h':	//set mode
544 				break;
545 			case 'l':	//reset mode
546 				break;
547 			case 'n':	//Device Status Report
548 				break;
549 			}
550 		}
551 	}
552 	else
553 	{
554 		switch(type)
555 		{
556  		case 'D':	//	scroll up
557 			ScrollUp();
558  			break;
559  		case 'M':	//	scroll down
560 			ScrollDown();
561  			break;
562  		case 'E':
563  			break;
564 		case '7':
565 			m_OldCaretPos = m_CaretPos;
566 //			printf("save cursor: %d, %d\n", (int)m_CaretPos.x, (int)m_CaretPos.y );
567 			m_SavedAttr = m_CurAttr;
568 			break;
569 		case '8':
570 			m_CaretPos = m_OldCaretPos;
571 //			printf("restored cursor: %d, %d\n", m_CaretPos.x, m_CaretPos.y );
572 			m_CurAttr = m_SavedAttr;
573 			m_pView->UpdateCaretPos();
574 			break;
575  		}	//end switch
576 	}
577 }
578 
579 
GoToXY(int x,int y)580 void CTermData::GoToXY(int x, int y)
581 {
582 	if( x < 0)
583 		x = 0;
584 	else if( x >= m_ColsPerPage )
585 		x= m_ColsPerPage-1;
586 
587 	if( y < 0 )
588 		y = 0;
589 	else if( y >= m_RowsPerPage )
590 		y= m_RowsPerPage-1;
591 
592 	m_CaretPos.x = x;
593 	m_CaretPos.y = m_FirstLine + y;
594 }
595 
ClearScreen(int p)596 void CTermData::ClearScreen(int p)
597 {
598 	m_NeedDelayedUpdate = true;
599 
600 	// Scroll down a page
601 	int bottom = m_RowCount-m_RowsPerPage;
602 	int i;
603 	char* tmp;
604 	for( i = 0; i < bottom; i++ )
605 	{
606 		int src = i+m_RowsPerPage;
607 		swap(m_Screen[i], m_Screen[src]);
608 	}
609 	for( i = bottom; i< m_RowCount; i++ )
610 		InitNewLine( m_Screen[i], m_ColsPerPage);
611 
612 	switch(p)
613 	{
614 //	case 2:	// Erase entire display
615 //		break;
616 	case 1:	// Erase from beginning to current position (inclusive)
617 		tmp = m_Screen[m_CaretPos.y];
618 		if( m_CaretPos.x < m_ColsPerPage && m_CaretPos.y > m_RowsPerPage )
619 		{
620 			memcpy( &tmp[m_CaretPos.x],
621 				&m_Screen[m_CaretPos.y-m_RowsPerPage][m_CaretPos.x],
622 				m_ColsPerPage-m_CaretPos.x);
623 			memcpy( &GetLineAttr(tmp)[m_CaretPos.x],
624 				&GetLineAttr(m_Screen[m_CaretPos.y-m_RowsPerPage])[m_CaretPos.x],
625 				m_ColsPerPage-m_CaretPos.x);
626 		}
627 		for( i = m_CaretPos.y + 1; i < m_RowCount; i++)
628 		{
629 			tmp = m_Screen[i];
630 			if( i < m_RowsPerPage)
631 				break;
632 			memcpy( tmp, m_Screen[i-m_RowsPerPage],m_ColsPerPage);
633 			memcpy( GetLineAttr(tmp),GetLineAttr(m_Screen[i-m_RowsPerPage]),m_ColsPerPage);		}
634 		break;
635 	case 0:	// Erase from current position to end (inclusive)
636 	default:
637 		tmp = m_Screen[m_CaretPos.y];
638 		if( m_CaretPos.x > 0 && m_CaretPos.y > m_RowsPerPage )
639 		{
640 			memcpy( tmp, &m_Screen[m_CaretPos.y-m_RowsPerPage],m_CaretPos.x-1);
641 			memcpy( GetLineAttr(tmp), GetLineAttr(m_Screen[m_CaretPos.y-m_RowsPerPage]),
642 				m_CaretPos.x-1);
643 		}
644 		for( i = bottom; i < m_CaretPos.y; i++)
645 		{
646 			tmp = m_Screen[i];
647 			if( i < m_RowsPerPage)
648 				break;
649 			memcpy( tmp, m_Screen[i-m_RowsPerPage],m_ColsPerPage);
650 			memcpy( GetLineAttr(tmp),GetLineAttr(m_Screen[i-m_RowsPerPage]),m_ColsPerPage);		}
651 		break;
652 	}
653 }
654 
EraseLine(int p)655 void CTermData::EraseLine(int p)
656 {
657 	char* pLine = m_Screen[m_CaretPos.y];
658 	CTermCharAttr* pAttr = GetLineAttr(pLine);
659 	switch(p)
660 	{
661 	case 0:		// Clear from current position to end of line.
662 		memset(&pLine[m_CaretPos.x],' ',m_ColsPerPage-m_CaretPos.x);
663 		//memset16(&pAttr[m_CaretPos.x],CTermCharAttr::GetDefVal(),m_ColsPerPage-m_CaretPos.x);
664 		memset16(&pAttr[m_CaretPos.x],*(short*)&m_CurAttr,m_ColsPerPage-m_CaretPos.x);
665 		SetLineUpdate(pLine, m_CaretPos.x, m_ColsPerPage );
666 		break;
667 	case 1:	// Clear from head of line to current position.
668 		memset(pLine, ' ',m_CaretPos.x);
669 		memset16(pAttr ,CTermCharAttr::GetDefVal(),m_CaretPos.x);
670 		SetLineUpdate(pLine, 0, m_CaretPos.x+1);
671 		break;
672 	default:
673 	case 2:	// Clear whole line.
674 		InitNewLine( pLine, m_ColsPerPage);
675 		break;
676 	}
677 }
678 
ParseAnsiColor(const char * pParam)679 void CTermData::ParseAnsiColor(const char *pParam)
680 {
681 	while(*pParam)
682 	{
683 		int param = 0;
684 		while( isdigit(*pParam) )
685 		{
686 			param *= 10;
687 			param += *pParam - '0';
688 			pParam++;
689 		}
690 		if( param < 30 )	// property code
691 		{
692 			switch(param)
693 			{
694 			case 0:		// normal
695 				m_CurAttr.SetToDefault();
696 				break;
697 			case 1:		// bright foreground
698 				m_CurAttr.SetBright(true);
699 				break;
700 			case 4:		// underscore
701 				m_CurAttr.SetUnderLine(true);
702 				break;
703 			case 5:		// blink
704 			case 6:
705 				m_CurAttr.SetBlink(true);
706 				break;
707 			case 7:		// reverse
708 				m_CurAttr.SetInverse(true);
709 				break;
710 			case 8:		// invisible text (fore=back)
711 				m_CurAttr.SetInvisible(true);
712 				break;
713 			}
714 		}
715 		else	// color code
716 		{
717 			if( param >= 40 && param <= 47)	//	background
718 				m_CurAttr.SetBackground(param-40);
719 			else if(param <= 37)	// foreground
720 				m_CurAttr.SetForeground(param-30);
721 			// else
722 				// Undefined parameter!
723 		}
724 		pParam++;
725 	}
726 }
727 
memset16(void * dest,short val,size_t n)728 void CTermData::memset16(void *dest, short val, size_t n)
729 {
730 	uint16_t *dest16 = (uint16_t *) dest;
731 	while (n--)
732 		*dest16++ = val;
733 }
734 
735 
update_view(CTermData * _this)736 static gboolean update_view(CTermData* _this)
737 {
738 	if(_this->m_pView)
739 		_this->DoUpdateDisplay();
740 	INFO("do update");
741 	_this->m_DelayedUpdateTimeout = 0;	// Simply returning false will remove the source.
742 	return false;	// remove timeout source
743 }
744 
UpdateDisplay()745 void CTermData::UpdateDisplay()
746 {
747 	DetectCharSets();
748 	DetectHyperLinks();
749 #ifdef USE_IPLOOKUP
750 	DetectIpAddrs();
751 #endif
752 
753  	if( m_pView && m_pView->IsVisible() && !m_WaitUpdateDisplay )
754 	{
755 		INFO("waiting update");
756 		m_WaitUpdateDisplay = true;
757 
758 		if( m_NeedDelayedUpdate )
759 		{
760 			if( m_DelayedUpdateTimeout )
761 				g_source_remove(m_DelayedUpdateTimeout);
762 			m_DelayedUpdateTimeout = g_timeout_add( 80, (GSourceFunc)&update_view, this);
763 		}
764 		else
765 			DoUpdateDisplay();
766 	}
767 	m_NeedDelayedUpdate = false;
768 }
769 
DoUpdateDisplay()770 void CTermData::DoUpdateDisplay()
771 {
772 	m_WaitUpdateDisplay = false;
773 
774 	m_pView->m_Caret.Hide();
775 	for( int row = 0; row < m_RowsPerPage; row++ )
776 	{
777 		int col = 0;
778 		CTermCharAttr* attr = GetLineAttr( m_Screen[m_FirstLine + row] );
779 		bool callback_has_been_called = false;
780 		for( ; col < m_ColsPerPage; col++  )
781 		{
782 			if( attr[col].IsNeedUpdate() )
783 			{
784 				if( ! callback_has_been_called )
785 				{
786 					OnLineModified( m_FirstLine + row );
787 					callback_has_been_called = true;
788 				}
789 
790 				if( col>0 && attr[col].GetCharSet()==CTermCharAttr::CS_MBCS2 )
791 					col--;
792 				m_pView->DrawChar( row, col );
793 				attr[col].SetNeedUpdate(false);
794 				// Check if this is a MBCS char.
795 				if( attr[col].GetCharSet()==CTermCharAttr::CS_MBCS1 )
796 				{
797 					attr[col+1].SetNeedUpdate(false);
798 					col ++;
799 				}
800 			}
801 		}
802 	}
803 	m_pView->UpdateCaretPos();
804 	m_pView->m_Caret.Show();
805 }
806 
807 // Detect character sets here.  This is for MBCS support.
DetectCharSets()808 void CTermData::DetectCharSets()
809 {
810 	int iline = m_FirstLine;
811 	int ilast_line = iline + m_RowsPerPage;
812 	for( ; iline < ilast_line; iline++ )
813 	{
814 		char* line = m_Screen[iline];
815 		CTermCharAttr* attr = GetLineAttr( line );
816 		int col = 0;
817 		while( col < m_ColsPerPage )
818 		{
819 			if( ((unsigned char)line[col]) > 128 && (col+1)< m_ColsPerPage)
820 			{
821 				if( attr[col].IsNeedUpdate() != attr[col+1].IsNeedUpdate() ) {
822 					attr[col].SetNeedUpdate(true);
823 					attr[col+1].SetNeedUpdate(true);
824 				}
825 
826 				attr[col].SetCharSet(CTermCharAttr::CS_MBCS1);
827 				col++;
828 				attr[col].SetCharSet(CTermCharAttr::CS_MBCS2);
829 			}
830 			else
831 				attr[col].SetCharSet(CTermCharAttr::CS_ASCII);
832 			col++;
833 		}
834 	}
835 }
836 
837 
838 // 2004/08/03 modified by PCMan
839 // Check if 'ch' is an valid character in URL.
isurl(int ch)840 inline bool isurl(int ch)
841 {	return isalnum(ch) || strchr("!$&'*+,-./:;=?@_|~%#", ch);	}
842 // Though '(' ,')', '<', and '>' are legal characters in URLs, I ignore them because
843 // they are frequently used to enclose URLs. ex: (http://code.google.com/p/pcmanx-gtk2/)
isurlscheme(int ch)844 inline bool isurlscheme(int ch)
845 {	return isalnum(ch) || strchr("+-.", ch);	}
846 
847 
848 #define ARRAY_SIZE(arr) \
849 	(sizeof(arr) / sizeof(arr[0]))
850 static const char *valid_protocol[] = {
851 	"http",
852 	"telnet",
853 	"https",
854 	"ftp",
855 };
isValidURLScheme(const char * line,int schemeStart,int schemeEnd)856 static inline bool isValidURLScheme(const char *line, int schemeStart, int schemeEnd)
857 {
858 	char protocol_buffer[16];
859 	int strLen = schemeEnd - schemeStart + 1; // index starts from 0
860 
861 	for (unsigned int i = 0; i < ARRAY_SIZE(valid_protocol); i++) {
862 		strcpy(protocol_buffer, valid_protocol[i]);
863 		if (!strncmp(line + schemeStart,
864 		             strcat(protocol_buffer, "://"), strLen)) {
865 			return true;
866 		}
867 	}
868 	return false;
869 }
870 
871 // 2004/08/06 modified by PCMan
872 // This function is used to detect E-mails and called from UpdateDisplay().
DetectEMails(const char * line,CTermCharAttr * attr,int len)873 inline void DetectEMails( const char *line, CTermCharAttr *attr, int len )
874 {
875 	int ilink = 0, stage = 0;
876 	for( int col = 0; col < len; col += (CTermCharAttr::CS_ASCII==attr[col].GetCharSet()?1:2) )
877 	{
878 		unsigned char ch = line[col];
879 		switch( stage )
880 		{
881 		case 0:	// a URL character is found, beginning of URL.
882 			if( isurl(ch) )
883 			{
884 				stage = 1;
885 				ilink = col;
886 			}
887 			break;
888 		case 1:	// '@' is found.
889 			if( !isurl(ch) )
890 				stage = 0;
891 			else if( '@' == ch )
892 				stage = 2;
893 			break;
894 		case 2:	// URL characters are found after '@'.
895 			if( !isurl(ch) )
896 				stage = 0;
897 			else if( '.' == ch )
898 				stage = 3;
899 			break;
900 		case 3:	//  This is a valid URL.
901 			if( !isurl(ch) )
902 			{
903 				for( ; ilink < col; ilink++ )
904 				{
905 					attr[ilink].SetHyperLink(true);
906 					attr[ilink].SetNeedUpdate(true);
907 				}
908 				stage = 0;
909 			}
910 		}
911 	}
912 }
913 
914 // 2004/08/06 added by PCMan
915 // This function is used to detect URLs other than E-mails and called from UpdateDisplay().
DetectCommonURLs(const char * line,CTermCharAttr * attr,int len)916 inline void DetectCommonURLs( const char *line, CTermCharAttr *attr, int len )
917 {
918 	int ilink = 0, stage = 0;
919 	for( int col = 0; col < len; col += (CTermCharAttr::CS_ASCII==attr[col].GetCharSet()?1:2) )
920 	{
921 		unsigned char ch = line[col];
922 		switch( stage )
923 		{
924 		case 0:	// a URL scheme character is found, beginning of URL.
925 			if( isurlscheme(ch) )
926 			{
927 				stage = 1;
928 				ilink = col;
929 			}
930 			break;
931 		case 1:	// "://" is found.
932 			if (((col + 3) <= len) &&
933 			    (0 == strncmp(line + col, "://", 3)) &&
934 			    isurl(line[col + 3]) &&
935 			    (isValidURLScheme(line, ilink, col + 2))) {
936 				stage = 2;
937 				col += 3;
938 			}
939 			else if( !isurlscheme(ch) )
940 				stage = 0;
941 			break;
942 		case 2:	// This is a valid URL.
943 			if( !isurl(ch) )
944 			{
945 				for( ; ilink < col; ilink++ )
946 				{
947 					attr[ilink].SetHyperLink(true);
948 					attr[ilink].SetNeedUpdate(true);
949 				}
950 				stage = 0;
951 			}
952 		}
953 	}
954 }
955 
956 // 2004/08/03 modified by PCMan
957 // This function is used to detect hyperlinks and called from UpdateDisplay().
DetectHyperLinks()958 void CTermData::DetectHyperLinks()
959 {
960 	int iline = m_FirstLine;
961 	int ilast_line = iline + m_RowsPerPage;
962 	for( ; iline < ilast_line; iline++ )
963 	{
964 		char* line = m_Screen[iline];
965 		CTermCharAttr* attr = GetLineAttr( line );
966 		// Clear all marks.
967 		for( int col = 0; col < m_ColsPerPage; col ++ )
968 			attr[col].SetHyperLink(false);
969 		DetectEMails( line, attr, m_ColsPerPage );	// Search for E-mails.
970 		DetectCommonURLs( line, attr, m_ColsPerPage );	// Search for URLs other than E-mail.
971 	}
972 }
973 
974 #ifdef USE_IPLOOKUP
975 
976 /* detect ipv4 addresses. */
DetectIpPatterns(const char * line,CTermCharAttr * attr,int len,const regex_t * regip)977 inline void DetectIpPatterns( const char *line, CTermCharAttr *attr, int len, const regex_t *regip)
978 {
979 	regmatch_t match;
980 	const char *p = line;
981 
982 	while ( p < line + len && regexec( regip, p, 1, &match, 0 ) == 0 )
983 	{
984 		int offset = p - line;
985 		if ( CTermCharAttr::CS_ASCII == attr[offset + match.rm_so].GetCharSet()
986 		  && CTermCharAttr::CS_ASCII == attr[offset + match.rm_eo - 1].GetCharSet() )
987 			for ( int i = match.rm_so; i < match.rm_eo; i++ )
988 			{
989 				attr[offset + i].SetIpAddr(true);
990 				attr[offset + i].SetNeedUpdate(true);
991 			}
992 		p += match.rm_eo + 1;
993 	}
994 }
995 
996 /* Detect IP addresses (called from UpdateDisplay()) */
DetectIpAddrs()997 void CTermData::DetectIpAddrs()
998 {
999 	int iline = m_FirstLine;
1000 	int ilast_line = iline + m_RowsPerPage;
1001 	for( ; iline < ilast_line; iline++ )
1002 	{
1003 		char* line = m_Screen[iline];
1004 		CTermCharAttr* attr = GetLineAttr( line );
1005 		// Clear all marks.
1006 		for( int col = 0; col < m_ColsPerPage; col ++ )
1007 			attr[col].SetIpAddr(false);
1008 		DetectIpPatterns( line, attr, m_ColsPerPage, &m_RegIp );
1009 	}
1010 }
1011 
1012 #endif
1013 
1014 typedef struct {
1015 	CTermData* pTermData;
1016 	string*    text;
1017 	int        lines;
1018 	const char*      eol;
1019 } ReadStatus;
1020 
GetChangedAttrStr(CTermCharAttr oldattr,CTermCharAttr newattr)1021 string GetChangedAttrStr(CTermCharAttr oldattr, CTermCharAttr newattr)
1022 {
1023 	string text = "\x1b[";	// Control sequence introducer.
1024 	bool reset = false;
1025 	 // If we want to cancel bright attribute, we must reset all attributes.
1026 	bool bright_changed = (newattr.IsBright() != oldattr.IsBright());
1027 	if( bright_changed && 1 == oldattr.IsBright() )
1028 		reset = true;
1029 	// Blink attribute changed.
1030 	// We must reset all attributes to remove 'blink' attribute.
1031 	bool blink_changed = (newattr.IsBlink() != oldattr.IsBlink());
1032 	if( blink_changed && 1 == oldattr.IsBlink() )
1033 		reset = true;
1034 	// Underline attribute changed.
1035 	// We must reset all attributes to remove 'underline' attribute.
1036 	bool underline_changed = (newattr.IsUnderLine() != oldattr.IsUnderLine());
1037 	if( underline_changed && 1 == oldattr.IsUnderLine() )
1038 		reset = true;
1039 	// Inverse attribute changed.
1040 	// We must reset all attributes to remove 'inverse' attribute.
1041 	bool inverse_changed = (newattr.IsInverse() != oldattr.IsInverse());
1042 	if( inverse_changed && 1 == oldattr.IsInverse() )
1043 		reset = true;
1044 
1045 	if(reset)
1046 		text += ';';	// remove all attributes
1047 	if( bright_changed && newattr.IsBright() )	// Add bright attribute
1048 		text += "1;";
1049 	if( blink_changed && newattr.IsBlink() )	// Add blink attribute
1050 		text += "5;";
1051 	if( underline_changed && newattr.IsUnderLine() ) // Add underline attributes.
1052 		text += "4;";
1053 	if( inverse_changed && newattr.IsInverse() ) // Add inverse attributes.
1054 		text += "7;";
1055 	if( reset || newattr.GetBackground() != oldattr.GetBackground())	// If reset or color changed.
1056 	{
1057 		char color[] = {'4', static_cast<char>('0'+ newattr.GetBackground()), ';', '\0' };
1058 		text += color;
1059 	}
1060 	if( reset || newattr.GetForeground() != oldattr.GetForeground() )
1061 	{
1062 		char color[] = {'3', static_cast<char>('0' + newattr.GetForeground()), ';', '\0' };
1063 		text += color;
1064 	}
1065 	if( ';' == text[ text.length()-1 ] )	// Don't worry about access violation because text.Len() always > 1.
1066 		text = text.substr(0, text.length()-1);
1067 	text += 'm';	// Terminate ANSI escape sequence
1068 	return text;
1069 }
1070 
read_line_with_color(int row,int col1,int col2,void * data)1071 static void read_line_with_color( int row, int col1, int col2, void* data )
1072 {
1073 	ReadStatus* rs = (ReadStatus*)data;
1074 	string* text = rs->text;
1075 
1076 	if ( rs->lines )
1077 	{
1078 		*text += rs->eol;
1079 
1080 		if ( col1 == col2 )
1081 			return;
1082 	}
1083 
1084 	CTermData* td = rs->pTermData;
1085 	char* pLine = td->m_Screen[row];
1086 	CTermCharAttr* pAttr = td->GetLineAttr( pLine );
1087 	CTermCharAttr attr;
1088 	attr.SetToDefault();
1089 
1090 	// GetLineWithColor
1091 	{
1092 		string line;
1093 		for ( int i = col1; i < col2; i++ )
1094 		{
1095 			if( !(attr == pAttr[i]) )
1096 			{
1097 				// Here we've got a characters with different attributes.
1098 				line += GetChangedAttrStr( attr, pAttr[i] );
1099 				attr = pAttr[i];
1100 			}
1101 			// Append characters with the same attributes.
1102 			if ( pLine[i] )
1103 				line += pLine[i];
1104 		}
1105 
1106 		// if current background is black,
1107 		if ( attr.GetBackground() == 0 && line.length() )
1108 		{
1109 			size_t n = line.find_last_not_of( ' ' );
1110 			if( n != text->npos )
1111 				line = line.substr( 0, n + 1 );
1112 		}
1113 
1114 		*text += line;
1115 	}
1116 
1117 
1118 	rs->lines++;
1119 }
1120 
read_line(int row,int col1,int col2,void * data)1121 static void read_line( int row, int col1, int col2, void* data )
1122 {
1123 	ReadStatus* rs = (ReadStatus*)data;
1124 	string* text = rs->text;
1125 
1126 	CTermData* td = rs->pTermData;
1127 
1128 	if ( rs->lines )
1129 	{
1130 		if ( rs->lines == 1 && text->length() > 0 )
1131 		{
1132 			// the old code does this
1133 			//if ( !td->m_Sel->m_BlockMode )
1134 				//*text = text->substr( 0, text->length() - 1 );
1135 
1136 			size_t n = text->find_last_not_of( ' ' );
1137 			if ( n != text->npos )
1138 				*text = text->substr( 0, n + 1 );
1139 		}
1140 
1141 		*text += rs->eol;
1142 	}
1143 
1144 	string line( td->m_Screen[row] + col1, col2 - col1 );
1145 
1146 	if ( line.length() )
1147 	{
1148 		// first line is handled in next call
1149 		if ( rs->lines )
1150 		{
1151 			size_t n = line.find_last_not_of( ' ' );
1152 			if ( n != line.npos )
1153 				line = line.substr( 0, n + 1 );
1154 			else if ( !td->m_Sel->m_BlockMode )
1155 				line = "";
1156 		}
1157 
1158 		*text += line;
1159 	}
1160 	rs->lines++;
1161 }
1162 
GetText(CTermSelection * sel,bool trim,bool color)1163 string CTermData::GetText( CTermSelection* sel, bool trim, bool color )
1164 {
1165 	string text;
1166 	int endrow;
1167 	ReadStatus rs = { this, &text, 0, "\n" };
1168 
1169 	if ( trim )
1170 	{
1171 		endrow = sel->m_End.row;
1172 
1173 		for ( ; sel->m_End.row > sel->m_Start.row; sel->m_End.row-- )
1174 		{
1175 			if( !IsLineEmpty( sel->m_End.row ) )
1176 				break;
1177 		}
1178 	}
1179 
1180 	if ( color )
1181 	{
1182 		text = "\x1b[m";
1183 		sel->ForEachLine( read_line_with_color, (void*)&rs );
1184 		if ( rs.lines > 1 && m_Sel->m_BlockMode )
1185 			text += rs.eol;
1186 		text += "\x1b[m";
1187 	}
1188 	else
1189 	{
1190 		sel->ForEachLine( read_line, (void*)&rs );
1191 		if ( rs.lines == 1 )
1192 		{
1193 			size_t n = text.find_last_not_of( ' ' );
1194 			if ( n != text.npos )
1195 				text = text.substr( 0, n + 1 );
1196 		}
1197 		else if ( rs.lines > 1 && m_Sel->m_BlockMode )
1198 			text += rs.eol;
1199 	}
1200 
1201 	if ( trim )
1202 		sel->m_End.row = endrow;
1203 
1204 	return text;
1205 }
1206 
GetSelectedText(bool trim)1207 string CTermData::GetSelectedText(bool trim)
1208 {
1209 	return GetText( m_Sel, trim, false );
1210 }
1211 
GetSelectedTextWithColor(bool trim)1212 string CTermData::GetSelectedTextWithColor(bool trim)
1213 {
1214 	return GetText( m_Sel, trim, true );
1215 }
1216 
1217 // 2004/08/03  Modified by PCMan
1218 // If line[col] is a character in a hyperlink, return the start index of whole hyperlink,
1219 // and store its length in 'len' which is optional and can be NULL.
1220 // If there is no hyperlink found at specified index 'col', the return value will be -1.
HyperLinkHitTest(const char * line,int col,int * len)1221 int CTermData::HyperLinkHitTest(const char *line, int col, int *len/*= NULL*/ )
1222 {
1223 	CTermCharAttr* pattr = GetLineAttr( line );
1224 	if( !pattr[col].IsHyperLink() )
1225 		return -1;
1226 	int start = col;
1227 	while( pattr[start].IsHyperLink() && start > 0 )
1228 		start--;
1229 	if( len )
1230 	{
1231 		while( pattr[col].IsHyperLink() && col < m_ColsPerPage )
1232 			col++;
1233 		*len = col-start;
1234 	}
1235 	return start;
1236 }
1237 
1238 // 2004/08/12	Modified by PCMan
1239 // Delete n characters at specified position (line, col).
DeleteChar(int line,int col,int n)1240 void CTermData::DeleteChar(int line, int col, int n)
1241 {
1242 	if( col > m_ColsPerPage || col < 0 )	return;
1243 	if( line < 0 || line >= m_RowCount )	return;
1244 	if( (col + n) > m_ColsPerPage )
1245 		n = (m_ColsPerPage - col);
1246 
1247 	char* pline = m_Screen[line];
1248 	CTermCharAttr* pattr = GetLineAttr( pline );
1249 	int col2 = m_ColsPerPage - n;
1250 	for( ; col < col2; col++ )
1251 	{
1252 		pline[ col ] = pline[ col + n ];
1253 		pattr[ col ] = pattr[ col + n ];
1254 		pattr[ col ].SetNeedUpdate(true);
1255 	}
1256 	for( ; col < m_ColsPerPage; col++ )
1257 	{
1258 		pline[ col ] = ' ';
1259 		pattr[ col ].SetToDefault();
1260 		pattr[ col ].SetNeedUpdate(true);
1261 	}
1262 }
1263 
1264 // 2004/08/12	Modified by PCMan
1265 // Insert n space characters at specified position (line, col).
InsertChar(int line,int col,int n)1266 void CTermData::InsertChar( int line, int col, int n )
1267 {
1268 	if( col > m_ColsPerPage || col < 0 )	return;
1269 	if( line < 0 || line >= m_RowCount )	return;
1270 	if( (col + n) > m_ColsPerPage )
1271 		n = (m_ColsPerPage - col);
1272 	char* pline = m_Screen[line];
1273 	CTermCharAttr* pattr = GetLineAttr( pline );
1274 
1275 	int coln = col + n;
1276 	for( int end = m_ColsPerPage; end >= coln; end-- )
1277 	{
1278 		pline[ end ] = pline[ end - n ];
1279 		pattr[ end ] = pattr[ end - n ];
1280 		pattr[ end ].SetNeedUpdate(true);
1281 	}
1282 	for( int x = col; x < coln; x++ )
1283 	{
1284 		pline[ x ] = ' ';
1285 		pattr[ x ] = m_CurAttr;
1286 		pattr[ x ].SetNeedUpdate(true);
1287 	}
1288 }
1289 
1290 #define CHAR_CLASS_WORD		0x1
1291 #define CHAR_CLASS_DELIMITER	0x2
1292 #define CHAR_CLASS_ASCII	0x80
GetCharClass(int line,int col)1293 unsigned char CTermData::GetCharClass( int line, int col )
1294 {
1295 	if( col >= m_ColsPerPage || col < 0 )
1296 		return 0;
1297 	if( line >= m_RowCount || line < 0 )
1298 		return 0;
1299 
1300 	const char* pLine = m_Screen[line];
1301 	CTermCharAttr* pAttr = GetLineAttr( pLine );
1302 	unsigned char ret = 0;
1303 	bool ascii;
1304 
1305 	switch( pAttr[col].GetCharSet() )
1306 	{
1307 	case CTermCharAttr::CS_MBCS2:
1308 		col--;
1309 	case CTermCharAttr::CS_MBCS1:
1310 		ascii = false;
1311 		break;
1312 	case CTermCharAttr::CS_ASCII:
1313 		ascii = true;
1314 		ret |= CHAR_CLASS_ASCII;
1315 		break;
1316 	}
1317 
1318 	if( ascii )
1319 	{
1320 		if( ( 'A' <= pLine[col] && pLine[col] <= 'Z' ) ||
1321 		    ( 'a' <= pLine[col] && pLine[col] <= 'z' ) ||
1322 		    ( '0' <= pLine[col] && pLine[col] <= '9' ) )
1323 			ret |= CHAR_CLASS_WORD;
1324 		else
1325 		{
1326 			switch(pLine[col])
1327 			{
1328 			case '#':
1329 			case '$':
1330 			case '%':
1331 			case '-':
1332 			case '+':
1333 			case '_':
1334 			case '.':
1335 			case '/':
1336 				ret |= CHAR_CLASS_WORD;
1337 				break;
1338 			case ' ':
1339 				ret |= CHAR_CLASS_DELIMITER;
1340 				break;
1341 			default:
1342 				break;
1343 			}
1344 		}
1345 	}
1346 	else
1347 	{
1348 		ret |= CHAR_CLASS_WORD;
1349 	}
1350 
1351 	return ret;
1352 }
1353 
1354 // 2004/08/25	Added by PCMan
1355 // Set attributes of all text in specified range.
SetTextAttr(CTermCharAttr attr,int flags,GdkPoint start,GdkPoint end,bool block)1356 void CTermData::SetTextAttr( CTermCharAttr attr, int flags, GdkPoint start, GdkPoint end, bool block)
1357 {
1358 	if( block || start.y == end.y )
1359 	{
1360 		if( end.x < start.x )
1361 		    swap(end.y, end.x);
1362 
1363 		for( int iline = start.y; iline <= end.y; iline++ )
1364 		{
1365 			CTermCharAttr* pattr = GetLineAttr( m_Screen[iline]);
1366 			for( int col = start.x; col < end.x; col++ )
1367 				pattr[col].SetTextAttr(attr, flags);
1368 		}
1369 	}
1370 	else
1371 	{
1372 		CTermCharAttr* pattr = GetLineAttr( m_Screen[start.y]);
1373 		for( int col = start.x; col < m_ColsPerPage; col++ )
1374 			pattr[col].SetTextAttr(attr, flags);
1375 		for( int iline = start.y+1; iline < end.y; iline++ )
1376 		{
1377 			pattr = GetLineAttr( m_Screen[iline]);
1378 			for( int col = 0; col < m_ColsPerPage; col++ )
1379 				pattr[col].SetTextAttr(attr, flags);
1380 		}
1381 		if( start.y != end.y )
1382 		{
1383 			pattr = GetLineAttr( m_Screen[end.y]);
1384 			for( int col = 0; col < end.x; col++ )
1385 				pattr[col].SetTextAttr(attr, flags);
1386 		}
1387 	}
1388 }
1389 
1390 
1391 //If the specified line on screen has a non space and
1392 //a non '\0' character or its background is not color 0
1393 //(usually black), return false.
1394 //Question: how about selected block which color is inversed?
IsLineEmpty(int iLine)1395 bool CTermData::IsLineEmpty(int iLine)
1396 {
1397 	const char* pLine = m_Screen[iLine];
1398 	CTermCharAttr* pAttr = GetLineAttr( pLine );
1399 	for( int col = 0; col < m_ColsPerPage; col++ )
1400 		if( (pLine[col] && pLine[col] != ' ') || pAttr[col].GetBackground() )
1401 			return false;
1402 	return true;
1403 }
1404 
1405 // This is a callback function called from CTermData::DoUpdateDisplay().
1406 // When new characters are written to a line in the screen buffer,
1407 // this function will be called with the line number passed in 'row'.
OnLineModified(int row UNUSED)1408 void CTermData::OnLineModified(int row UNUSED)
1409 {
1410     // This function can be overriden in derived class.
1411 }
1412 
1413