xref: /haiku/src/apps/terminal/TermParse.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2  * Copyright (c) 2001-2005, Haiku, Inc.
3  * Copyright (c) 2003-4 Kian Duffy <myob@users.sourceforge.net>
4  * Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai.
5  * Distributed under the terms of the MIT license.
6  */
7 
8 
9 #include <errno.h>
10 #include <stdio.h>
11 #include <signal.h>
12 #include <unistd.h>
13 
14 #include <app/Application.h>
15 #include <support/Beep.h>
16 #include <Message.h>
17 #include <MessageRunner.h>
18 
19 
20 #include "TermParse.h"
21 #include "TermView.h"
22 #include "VTparse.h"
23 #include "TermConst.h"
24 #include "CodeConv.h"
25 
26 extern int pfd; // defined Muterminal.cpp
27 
28 /////////////////////////////////////////////////////////////////////////////
29 // PtyReader ... Get character from pty device.
30 //
31 /////////////////////////////////////////////////////////////////////////////
32 int32
33 TermParse::PtyReader(void *data)
34 {
35   int nread;
36   uint read_p = 0;
37 
38   TermParse *theObj = (TermParse *) data;
39 
40   uchar buf [READ_BUF_SIZE];
41 
42   while (theObj->fQuitting) {
43     /*
44      * If Pty Buffer nearly full, snooze this thread, and continue.
45      */
46     if ((read_p - theObj->fParser_p) > READ_BUF_SIZE - 16) {
47       theObj->fLockFlag = READ_BUF_SIZE / 2;
48       acquire_sem (theObj->fReaderLocker);
49     }
50 
51     /*
52      * Read PTY.
53      */
54     nread = read (pfd, buf,
55 		  READ_BUF_SIZE - (read_p - theObj->fParser_p));
56     if (nread <= 0) {
57       be_app->PostMessage(B_QUIT_REQUESTED);
58       exit_thread (B_ERROR);
59 
60     }
61 
62 
63     int left = READ_BUF_SIZE - (read_p % READ_BUF_SIZE);
64     int mod = read_p % READ_BUF_SIZE;
65 
66     /*
67      * Copy read string to PtyBuffer.
68      */
69     if (nread >= left) {
70       memcpy (theObj->fReadBuf + mod, buf, left);
71       memcpy (theObj->fReadBuf, buf + left, nread - left);
72     }
73     else {
74       memcpy (theObj->fReadBuf + mod, buf, nread);
75     }
76     read_p += nread;
77 
78     /*
79      * Release semaphore. Number of semaphore counter is nread.
80      */
81     release_sem_etc (theObj->fReaderSem, nread, 0);
82 
83   }
84   theObj->fReaderThread = -1;
85   exit_thread (B_OK);
86   return B_OK;
87 }
88 ///////////////////////////////////////////////////////////////////////////
89 // GetReaderBuf ... Get char pty reader buffer.
90 //
91 ///////////////////////////////////////////////////////////////////////////
92 
93 uchar
94 TermParse::GetReaderBuf (void)
95 {
96   int c;
97 
98   switch (acquire_sem_etc (fReaderSem, 1, B_TIMEOUT, (bigtime_t)10000.0)) {
99     case B_NO_ERROR:
100       break;
101     case B_TIMED_OUT:
102     default:
103       fViewObj->ScrollAtCursor();
104       fViewObj->UpdateLine();
105 
106       // Reset cursor blinking time and turn on cursor blinking.
107       fCursorUpdate->SetInterval (1000000);
108       fViewObj->SetCurDraw (CURON);
109 
110       // wait new input from pty.
111       acquire_sem (fReaderSem);
112       break;
113   }
114 
115   c = fReadBuf[fParser_p % READ_BUF_SIZE];
116   fParser_p++;
117   /*
118    * If PtyReader thread locked, decliment counter and unlock thread.
119    */
120   if (fLockFlag != 0) {
121     fLockFlag--;
122     if (fLockFlag == 0) {
123       release_sem (fReaderLocker);
124     }
125   }
126 
127   fViewObj->SetCurDraw (CUROFF);
128 
129   return c;
130 }
131 
132 ///////////////////////////////////////////////////////////////////////////
133 // InitTermParse ... Initialize and spawn EscParse thread.
134 //
135 ///////////////////////////////////////////////////////////////////////////
136 status_t
137 TermParse::InitTermParse (TermView *inViewObj, CodeConv *inConvObj)
138 {
139 
140   fViewObj = inViewObj;
141   fConvObj = inConvObj;
142 
143   fCursorUpdate = new BMessageRunner (BMessenger (fViewObj),
144 				      new BMessage (MSGRUN_CURSOR),
145 				      1000000);
146 
147   if (fParseThread < 0)
148     fParseThread =
149       spawn_thread (EscParse, "EscParse", B_DISPLAY_PRIORITY, this);
150   else
151     return B_BAD_THREAD_ID;
152 
153   return (resume_thread ( fParseThread));
154 
155 }
156 ///////////////////////////////////////////////////////////////////////////
157 // InitPtyReader ... Initialize and spawn PtyReader thread.
158 //
159 ///////////////////////////////////////////////////////////////////////////
160 thread_id
161 TermParse::InitPtyReader (TermWindow *inWinObj)
162 {
163 
164   fWinObj = inWinObj;
165 
166   if (fReaderThread < 0)
167     fReaderThread =
168       spawn_thread (PtyReader, "PtyReader", B_NORMAL_PRIORITY, this);
169   else
170     return B_BAD_THREAD_ID;
171 
172   fReaderSem = create_sem (0, "pty_reader_sem");
173   fReaderLocker = create_sem (0, "pty_locker_sem");
174 
175   resume_thread (fReaderThread);
176 
177   return (fReaderThread);
178 }
179 
180 /*
181  *  Constructer and Destructer.
182  */
183 
184 TermParse::TermParse (void)
185 {
186 
187   fParseThread = -1;
188   fReaderThread = -1;
189   fLockFlag = 0;
190   fParser_p = 0;
191   fQuitting = 1;
192 
193 }
194 
195 TermParse::~TermParse (void)
196 {
197   //status_t sts;
198 
199   fQuitting = 0;
200   kill_thread(fParseThread);
201 
202   kill_thread(fReaderThread);
203   delete_sem (fReaderSem);
204   delete_sem (fReaderLocker);
205 
206 }
207 
208 //////////////////////////////////////////////////////////////////////////////
209 // EscParse ... Escape sequence parse and character encoding.
210 //
211 //////////////////////////////////////////////////////////////////////////////
212 
213 extern int utf8_groundtable[];		/* UTF8 Ground table */
214 extern int cs96_groundtable[];		/* CS96 Ground table */
215 extern int iso8859_groundtable[];	/* ISO8859 & EUC Ground table */
216 extern int sjis_groundtable[];		/* Shift-JIS Ground table */
217 
218 extern int esctable[];		/* ESC */
219 extern int csitable[];		/* ESC [ */
220 extern int dectable[];		/* ESC [ ? */
221 extern int scrtable[];		/* ESC # */
222 extern int igntable[];		/* ignore table */
223 extern int iestable[];		/* ignore ESC table */
224 extern int eigtable[];		/* ESC ignore table */
225 extern int mbcstable[];		/* ESC $ */
226 
227 
228 /* MuTerminal coding system (global varriable) */
229 int gNowCoding = M_UTF8;
230 
231 #define DEFAULT -1
232 #define NPARAM 10		// Max parameters
233 
234 
235 int32
236 TermParse::EscParse(void *data)
237 {
238 	int tmp;
239 	int top, bot;
240 	int cs96;
241 	uchar curess = 0;
242 
243 	TermParse *theObj = (TermParse *)data;
244 
245 	TermView *viewObj = theObj->fViewObj;
246 	CodeConv *convObj = theObj->fConvObj;
247 
248 	uchar cbuf[4], dstbuf[4];
249 	uchar *ptr;
250 
251 	int *parsestate, *groundtable;
252 	int now_coding = -1;
253 
254 	uchar c;
255 	ushort attr = BACKCOLOR;
256 
257 	int param[NPARAM];
258 	int nparam = 1;
259 
260 	int row, col;
261 	int width;
262 
263 	/* default coding system is UTF8 */
264 	groundtable = utf8_groundtable;
265 	parsestate = groundtable;
266 
267 	while (theObj->fQuitting) {
268 		c = theObj->GetReaderBuf();
269 
270 		if (now_coding != gNowCoding) {
271 			/*
272 			 * Change coding, change parse table.
273 			 */
274 			switch (gNowCoding) {
275 				case M_UTF8:
276 					groundtable = utf8_groundtable;
277 					break;
278 				case M_ISO_8859_1:
279 				case M_ISO_8859_2:
280 				case M_ISO_8859_3:
281 				case M_ISO_8859_4:
282 				case M_ISO_8859_5:
283 				case M_ISO_8859_6:
284 				case M_ISO_8859_7:
285 				case M_ISO_8859_8:
286 				case M_ISO_8859_9:
287 				case M_ISO_8859_10:
288 					groundtable = iso8859_groundtable;
289 					break;
290 				case M_SJIS:
291 					groundtable = sjis_groundtable;
292 					break;
293 				case M_EUC_JP:
294 				case M_EUC_KR:
295 				case M_ISO_2022_JP:
296 					groundtable = iso8859_groundtable;
297 					break;
298 			}
299 			parsestate = groundtable;
300 			now_coding = gNowCoding;
301     	}
302 
303 		switch (parsestate[c]) {
304 			case CASE_PRINT:
305 				cbuf[0] = c;
306 				cbuf[1] = '\0';
307 				width = HALF_WIDTH;
308 				viewObj->PutChar(cbuf, attr, width);
309 				break;
310 
311 			case CASE_PRINT_GR:
312 				/* case iso8859 gr character, or euc */
313 				ptr = cbuf;
314 				if (now_coding == M_EUC_JP || now_coding == M_EUC_KR
315 					|| now_coding == M_ISO_2022_JP) {
316 					switch (parsestate[curess]) {
317 						case CASE_SS2:		/* JIS X 0201 */
318 							*ptr++ = curess;
319 							*ptr++ = c;
320 							*ptr = 0;
321 							width = 1;
322 							curess = 0;
323 							break;
324 
325 						case CASE_SS3:		/* JIS X 0212 */
326 							*ptr++ = curess;
327 							*ptr++ = c;
328 							*ptr++ = theObj->GetReaderBuf ();
329 							*ptr = 0;
330 							width = 2;
331 							curess = 0;
332 							break;
333 
334 						default:		/* JIS X 0208 */
335 							*ptr++ = c;
336 							*ptr++ = theObj->GetReaderBuf ();
337 							*ptr = 0;
338 							width = 2;
339 							break;
340 					}
341 				} else {
342 					/* ISO-8859-1...10 and MacRoman */
343 					*ptr++ = c;
344 					*ptr = 0;
345 					width = 1;
346 				}
347 
348 				if (now_coding != M_ISO_2022_JP)
349 					convObj->ConvertToInternal((char*)cbuf, -1, (char*)dstbuf, now_coding);
350 				else
351 					convObj->ConvertToInternal((char*)cbuf, -1, (char*)dstbuf, M_EUC_JP);
352 
353 				viewObj->PutChar(dstbuf, attr, width);
354 				break;
355 
356 			case CASE_PRINT_CS96:
357 				cbuf[0] = c | 0x80;
358 				cbuf[1] = theObj->GetReaderBuf() | 0x80;
359 				cbuf[2] = 0;
360 				width = 2;
361 				convObj->ConvertToInternal((char*)cbuf, 2, (char*)dstbuf, M_EUC_JP);
362 				viewObj->PutChar(dstbuf, attr, width);
363 				break;
364 
365 			case CASE_LF:
366 				viewObj->PutLF();
367 				break;
368 
369 			case CASE_CR:
370 				viewObj->PutCR();
371 				break;
372 
373 			case CASE_SJIS_KANA:
374 				cbuf[0] = (uchar)c;
375 				cbuf[1] = '\0';
376 				convObj->ConvertToInternal((char*)cbuf, 1, (char*)dstbuf, now_coding);
377 				width = 1;
378 				viewObj->PutChar(dstbuf, attr, width);
379 				break;
380 
381 			case CASE_SJIS_INSTRING:
382 				cbuf[0] = (uchar)c;
383 				cbuf[1] = theObj->GetReaderBuf();
384 				cbuf[2] = '\0';
385 				convObj->ConvertToInternal((char*)cbuf, 2, (char*)dstbuf, now_coding);
386 				width = 2;
387 				viewObj->PutChar(dstbuf, attr, width);
388 				break;
389 
390 			case CASE_UTF8_2BYTE:
391 				cbuf[0] = (uchar)c;
392 				c = theObj->GetReaderBuf();
393 				if (groundtable[c] != CASE_UTF8_INSTRING)
394 					break;
395 				cbuf[1] = (uchar)c;
396 				cbuf[2] = '\0';
397 				width = convObj->UTF8GetFontWidth((char*)cbuf);
398 				viewObj->PutChar(cbuf, attr, width);
399 				break;
400 
401 			case CASE_UTF8_3BYTE:
402 				cbuf[0] = c;
403 				c = theObj->GetReaderBuf();
404 				if (groundtable[c] != CASE_UTF8_INSTRING)
405 					break;
406 				cbuf[1] = c;
407 
408 				c = theObj->GetReaderBuf();
409 				if (groundtable[c] != CASE_UTF8_INSTRING)
410 					break;
411 				cbuf[2] = c;
412 				cbuf[3] = '\0';
413 				width = convObj->UTF8GetFontWidth((char*)cbuf);
414 				viewObj->PutChar (cbuf, attr, width);
415 				break;
416 
417 			case CASE_MBCS:
418 				/* ESC $ */
419 				parsestate = mbcstable;
420 				break;
421 
422 			case CASE_GSETS:
423 				/* ESC $ ? */
424 				parsestate = cs96_groundtable;
425 				cs96 = 1;
426 				break;
427 
428 			case CASE_SCS_STATE:
429 				cs96 = 0;
430 				theObj->GetReaderBuf ();
431 				parsestate = groundtable;
432 				break;
433 
434 			case CASE_GROUND_STATE:
435 				/* exit ignore mode */
436 				parsestate = groundtable;
437 				break;
438 
439 			case CASE_BELL:
440 				beep();
441 				break;
442 
443 			case CASE_BS:
444 				viewObj->MoveCurLeft(1);
445 				break;
446 
447 			case CASE_TAB:
448 				tmp = viewObj->GetCurX();
449 				tmp %= 8;
450 				viewObj->MoveCurRight(8 - tmp);
451 				break;
452 
453 			case CASE_ESC:
454 				/* escape */
455 				parsestate = esctable;
456 				break;
457 
458 			case CASE_IGNORE_STATE:
459 				/* Ies: ignore anything else */
460 				parsestate = igntable;
461 				break;
462 
463 			case CASE_IGNORE_ESC:
464 				/* Ign: escape */
465 				parsestate = iestable;
466 				break;
467 
468 			case CASE_IGNORE:
469 				/* Ignore character */
470 				break;
471 
472 			case CASE_SI:
473 				break;
474 
475 			case CASE_SO:
476 				break;
477 
478 			case CASE_SCR_STATE:	// ESC #
479 				/* enter scr state */
480 				parsestate = scrtable;
481 				break;
482 
483 			case CASE_ESC_IGNORE:
484 				/* unknown escape sequence */
485 				parsestate = eigtable;
486 				break;
487 
488 			case CASE_ESC_DIGIT:	// ESC [ number
489 				/* digit in csi or dec mode */
490 				if ((row = param[nparam - 1]) == DEFAULT)
491 					row = 0;
492 				param[nparam - 1] = 10 * row + (c - '0');
493 				break;
494 
495 			case CASE_ESC_SEMI:		// ESC ;
496 				/* semicolon in csi or dec mode */
497 				if (nparam < NPARAM)
498 					param[nparam++] = DEFAULT;
499 				break;
500 
501 			case CASE_DEC_STATE:
502 				/* enter dec mode */
503 				parsestate = dectable;
504 				break;
505 
506 			case CASE_ICH:		// ESC [@ insert charactor
507 				/* ICH */
508 				if ((row = param[0]) < 1)
509 					row = 1;
510 				viewObj->InsertSpace(row);
511 				parsestate = groundtable;
512 				break;
513 
514 			case CASE_CUU:		// ESC [A cursor up, up arrow key.
515 				/* CUU */
516 				if ((row = param[0]) < 1)
517 					row = 1;
518 				viewObj->MoveCurUp(row);
519 				parsestate = groundtable;
520 				break;
521 
522 			case CASE_CUD:		// ESC [B cursor down, down arrow key.
523 				/* CUD */
524 				if ((row = param[0]) < 1)
525 					row = 1;
526 				viewObj->MoveCurDown(row);
527 				parsestate = groundtable;
528 				break;
529 
530 			case CASE_CUF:		// ESC [C cursor forword
531 				/* CUF */
532 				if ((row = param[0]) < 1)
533 					row = 1;
534 				viewObj->MoveCurRight(row);
535 				parsestate = groundtable;
536 				break;
537 
538 			case CASE_CUB:		// ESC [D cursor backword
539 				/* CUB */
540 				if ((row = param[0]) < 1)
541 					row = 1;
542 				viewObj->MoveCurLeft(row);
543 				parsestate = groundtable;
544 				break;
545 
546 			case CASE_CUP:		// ESC [...H move cursor
547 				/* CUP | HVP */
548 				if ((row = param[0]) < 1)
549 					row = 1;
550 				if (nparam < 2 || (col = param[1]) < 1)
551 					col = 1;
552 
553 				viewObj->SetCurPos(col - 1, row - 1 );
554 				parsestate = groundtable;
555 				break;
556 
557 			case CASE_ED:		// ESC [ ...J clear screen
558 				/* ED */
559 				switch (param[0]) {
560 					case DEFAULT:
561 					case 0:
562 						viewObj->EraseBelow();
563 						break;
564 
565 					case 1:
566 						break;
567 
568 					case 2:
569 						viewObj->SetCurPos(0, 0);
570 						viewObj->EraseBelow();
571 						break;
572 				}
573 				parsestate = groundtable;
574 				break;
575 
576 			case CASE_EL:		// delete line
577 				/* EL */
578 				viewObj->DeleteColumns();
579 				parsestate = groundtable;
580 				break;
581 
582 			case CASE_IL:
583 				/* IL */
584 				if ((row = param[0]) < 1)
585 					row = 1;
586 				viewObj->PutNL(row);
587 				parsestate = groundtable;
588 				break;
589 
590 			case CASE_DL:
591 				/* DL */
592 				if ((row = param[0]) < 1)
593 					row = 1;
594 				viewObj->DeleteLine(row);
595 				parsestate = groundtable;
596 				break;
597 
598 			case CASE_DCH:
599 				/* DCH */
600 				if ((row = param[0]) < 1)
601 					row = 1;
602 				viewObj->DeleteChar(row);
603 				parsestate = groundtable;
604 				break;
605 
606 			case CASE_SET:
607 				/* SET */
608 				viewObj->SetInsertMode(MODE_INSERT);
609 				parsestate = groundtable;
610 				break;
611 
612 			case CASE_RST:
613 				/* RST */
614 				viewObj->SetInsertMode(MODE_OVER);
615 				parsestate = groundtable;
616 				break;
617 
618 			case CASE_SGR:
619 				/* SGR */
620 				for (row = 0; row < nparam; ++row) {
621 					switch (param[row]) {
622 						case DEFAULT:
623 						case 0: /* Reset attribute */
624 							attr = 0;
625 							break;
626 
627 						case 1:
628 						case 5:	/* Bold		*/
629 							attr |= BOLD;
630 							break;
631 
632 						case 4:	/* Underline	*/
633 							attr |= UNDERLINE;
634 							break;
635 
636 						case 7:	/* Inverse	*/
637 							attr |= INVERSE;
638 							break;
639 
640 						case 30:
641 						case 31:
642 						case 32:
643 						case 33:
644 						case 34:
645 						case 35:
646 						case 36:
647 						case 37:
648 							attr &= ~FORECOLOR;
649 							attr |= FORECOLORED(param[row] - 30);
650 							attr |= FORESET;
651 							break;
652 
653 						case 39:
654 							attr &= ~FORESET;
655 							break;
656 
657 						case 40:
658 						case 41:
659 						case 42:
660 						case 43:
661 						case 44:
662 						case 45:
663 						case 46:
664 						case 47:
665 							attr &= ~BACKCOLOR;
666 							attr |= BACKCOLORED(param[row] - 40);
667 							attr |= BACKSET;
668 							break;
669 
670 						case 49:
671 							attr &= ~BACKSET;
672 							break;
673 					}
674 				}
675 				parsestate = groundtable;
676 				break;
677 
678 				case CASE_CPR:
679 					// Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp)
680 					// 21-JUL-99
681 					viewObj->DeviceStatusReport(param[0]);
682 					parsestate = groundtable;
683 					break;
684 
685 				case CASE_DECSTBM:
686 					/* DECSTBM - set scrolling region */
687 
688 					if ((top = param[0]) < 1)
689 						top = 1;
690 
691 					if (nparam < 2)
692 						bot = -1;
693 					else
694 						bot = param[1];
695 
696 					top--;
697 					bot--;
698 
699 					if (bot > top)
700 						viewObj->SetScrollRegion(top, bot);
701 
702 					parsestate = groundtable;
703 					break;
704 
705 				case CASE_DECREQTPARM:
706 					parsestate = groundtable;
707 					break;
708 
709 				case CASE_DECSET:
710 					/* DECSET */
711 					//      dpmodes(term, bitset);
712 					parsestate = groundtable;
713 					break;
714 
715 				case CASE_DECRST:
716 					/* DECRST */
717 					//      dpmodes(term, bitclr);
718 					parsestate = groundtable;
719 					break;
720 
721 				case CASE_DECALN:
722 					/* DECALN */
723 					//      if(screen->cursor_state)
724 					//	HideCursor();
725 					//      ScrnRefresh(screen, 0, 0, screen->max_row + 1,
726 					//		  screen->max_col + 1, False);
727 					parsestate = groundtable;
728 					break;
729 
730 			//	case CASE_GSETS:
731 			//		screen->gsets[scstype] = GSET(c) | cs96;
732 			//		parsestate = groundtable;
733 			//		break;
734 
735 				case CASE_DECSC:
736 					/* DECSC */
737 					viewObj->SaveCursor();
738 					parsestate = groundtable;
739 					break;
740 
741 				case CASE_DECRC:
742 					/* DECRC */
743 					viewObj->RestoreCursor();
744 					parsestate = groundtable;
745 					break;
746 
747 				case CASE_HTS:
748 					/* HTS */
749 					//      TabSet(term->tabs, screen->cur_col);
750 					parsestate = groundtable;
751 					break;
752 
753 				case CASE_RI:
754 					/* RI */
755 					viewObj->ScrollRegion(-1, -1, SCRDOWN, 1);
756 					parsestate = groundtable;
757 					break;
758 
759 				case CASE_SS2:
760 					/* SS2 */
761 					curess = c;
762 					parsestate = groundtable;
763 					break;
764 
765 				case CASE_SS3:
766 					/* SS3 */
767 					curess = c;
768 					parsestate = groundtable;
769 					break;
770 
771 				case CASE_CSI_STATE:
772 					/* enter csi state */
773 					nparam = 1;
774 					param[0] = DEFAULT;
775 					parsestate = csitable;
776 					break;
777 
778 				case CASE_OSC:
779 					/* Operating System Command: ESC ] */
780 					//      do_osc(finput);
781 					parsestate = groundtable;
782 					break;
783 
784 				case CASE_RIS:		// ESC c ... Reset terminal.
785 					break;
786 
787 				case CASE_LS2:
788 					/* LS2 */
789 					//      screen->curgl = 2;
790 					parsestate = groundtable;
791 					break;
792 
793 				case CASE_LS3:
794 					/* LS3 */
795 					//      screen->curgl = 3;
796 					parsestate = groundtable;
797 					break;
798 
799 				case CASE_LS3R:
800 					/* LS3R */
801 					//      screen->curgr = 3;
802 					parsestate = groundtable;
803 					break;
804 
805 				case CASE_LS2R:
806 					/* LS2R */
807 					//      screen->curgr = 2;
808 					parsestate = groundtable;
809 					break;
810 
811 				case CASE_LS1R:
812 					/* LS1R */
813 					//      screen->curgr = 1;
814 					parsestate = groundtable;
815 					break;
816 
817 				default:
818 					break;
819 		}
820 	}
821 	theObj->fParseThread = -1;
822 	exit_thread(B_OK);
823 	return B_OK;
824 }
825 
826