xref: /haiku/src/apps/terminal/TermParse.cpp (revision 688acf41a377f25d4048dc6092edb86ac9179677)
1  /*
2   * Copyright 2001-2013, 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   * Authors:
8   *		Kian Duffy, myob@users.sourceforge.net
9   *		Simon South, simon@simonsouth.net
10   *		Siarzhuk Zharski, zharik@gmx.li
11   */
12  
13  
14  //! Escape sequence parse and character encoding.
15  
16  
17  #include "TermParse.h"
18  
19  #include <ctype.h>
20  #include <errno.h>
21  #include <stdio.h>
22  #include <stdlib.h>
23  #include <signal.h>
24  #include <string.h>
25  #include <unistd.h>
26  
27  #include <Autolock.h>
28  #include <Beep.h>
29  #include <Catalog.h>
30  #include <Locale.h>
31  #include <Message.h>
32  #include <UTF8.h>
33  
34  #include "Colors.h"
35  #include "TermConst.h"
36  #include "TerminalBuffer.h"
37  #include "VTparse.h"
38  
39  
40  extern int gUTF8GroundTable[];		/* UTF8 Ground table */
41  extern int gISO8859GroundTable[];	/* ISO8859 & EUC Ground table */
42  extern int gWinCPGroundTable[];		/* Windows cp1252, cp1251, koi-8r */
43  extern int gSJISGroundTable[];		/* Shift-JIS Ground table */
44  
45  extern int gEscTable[];				/* ESC */
46  extern int gCsiTable[];				/* ESC [ */
47  extern int gDecTable[];				/* ESC [ ? */
48  extern int gScrTable[];				/* ESC # */
49  extern int gIgnoreTable[];			/* ignore table */
50  extern int gIesTable[];				/* ignore ESC table */
51  extern int gEscIgnoreTable[];		/* ESC ignore table */
52  
53  extern const char* gLineDrawGraphSet[]; /* may be used for G0, G1, G2, G3 */
54  
55  #define DEFAULT -1
56  #define NPARAM 10		// Max parameters
57  
58  
59  //! Get char from pty reader buffer.
60  inline uchar
61  TermParse::_NextParseChar()
62  {
63  	if (fParserBufferOffset >= fParserBufferSize) {
64  		// parser buffer empty
65  		status_t error = _ReadParserBuffer();
66  		if (error != B_OK)
67  			throw error;
68  	}
69  
70  #ifdef USE_DEBUG_SNAPSHOTS
71  	fBuffer->CaptureChar(fParserBuffer[fParserBufferOffset]);
72  #endif
73  
74  	return fParserBuffer[fParserBufferOffset++];
75  }
76  
77  
78  TermParse::TermParse(int fd)
79  	:
80  	fFd(fd),
81  	fParseThread(-1),
82  	fReaderThread(-1),
83  	fReaderSem(-1),
84  	fReaderLocker(-1),
85  	fBufferPosition(0),
86  	fReadBufferSize(0),
87  	fParserBufferSize(0),
88  	fParserBufferOffset(0),
89  	fBuffer(NULL),
90  	fQuitting(true)
91  {
92  	memset(fReadBuffer, 0, READ_BUF_SIZE);
93  	memset(fParserBuffer, 0, ESC_PARSER_BUFFER_SIZE);
94  }
95  
96  
97  TermParse::~TermParse()
98  {
99  	StopThreads();
100  }
101  
102  
103  status_t
104  TermParse::StartThreads(TerminalBuffer *buffer)
105  {
106  	if (fBuffer != NULL)
107  		return B_ERROR;
108  
109  	fQuitting = false;
110  	fBuffer = buffer;
111  
112  	status_t status = _InitPtyReader();
113  	if (status < B_OK) {
114  		fBuffer = NULL;
115  		return status;
116  	}
117  
118  	status = _InitTermParse();
119  	if (status < B_OK) {
120  		_StopPtyReader();
121  		fBuffer = NULL;
122  		return status;
123  	}
124  
125  	return B_OK;
126  }
127  
128  
129  status_t
130  TermParse::StopThreads()
131  {
132  	if (fBuffer == NULL)
133  		return B_ERROR;
134  
135  	fQuitting = true;
136  
137  	_StopPtyReader();
138  	_StopTermParse();
139  
140  	fBuffer = NULL;
141  
142  	return B_OK;
143  }
144  
145  
146  //! Initialize and spawn EscParse thread.
147  status_t
148  TermParse::_InitTermParse()
149  {
150  	if (fParseThread >= 0)
151  		return B_ERROR; // we might want to return B_OK instead ?
152  
153  	fParseThread = spawn_thread(_escparse_thread, "EscParse",
154  		B_DISPLAY_PRIORITY, this);
155  
156  	if (fParseThread < 0)
157  		return fParseThread;
158  
159  	resume_thread(fParseThread);
160  
161  	return B_OK;
162  }
163  
164  
165  //! Initialize and spawn PtyReader thread.
166  status_t
167  TermParse::_InitPtyReader()
168  {
169  	if (fReaderThread >= 0)
170  		return B_ERROR; // same as above
171  
172  	fReaderSem = create_sem(0, "pty_reader_sem");
173  	if (fReaderSem < 0)
174  		return fReaderSem;
175  
176  	fReaderLocker = create_sem(0, "pty_locker_sem");
177  	if (fReaderLocker < 0) {
178  		delete_sem(fReaderSem);
179  		fReaderSem = -1;
180  		return fReaderLocker;
181  	}
182  
183  	fReaderThread = spawn_thread(_ptyreader_thread, "PtyReader",
184  		B_NORMAL_PRIORITY, this);
185  	if (fReaderThread < 0) {
186  		delete_sem(fReaderSem);
187  		fReaderSem = -1;
188  		delete_sem(fReaderLocker);
189  		fReaderLocker = -1;
190  		return fReaderThread;
191  	}
192  
193  	resume_thread(fReaderThread);
194  
195  	return B_OK;
196  }
197  
198  
199  void
200  TermParse::_StopTermParse()
201  {
202  	if (fParseThread >= 0) {
203  		status_t dummy;
204  		wait_for_thread(fParseThread, &dummy);
205  		fParseThread = -1;
206  	}
207  }
208  
209  
210  void
211  TermParse::_StopPtyReader()
212  {
213  	if (fReaderSem >= 0) {
214  		delete_sem(fReaderSem);
215  		fReaderSem = -1;
216  	}
217  	if (fReaderLocker >= 0) {
218  		delete_sem(fReaderLocker);
219  		fReaderLocker = -1;
220  	}
221  
222  	if (fReaderThread >= 0) {
223  		suspend_thread(fReaderThread);
224  
225  		status_t status;
226  		wait_for_thread(fReaderThread, &status);
227  
228  		fReaderThread = -1;
229  	}
230  }
231  
232  
233  //! Get data from pty device.
234  int32
235  TermParse::PtyReader()
236  {
237  	int32 bufferSize = 0;
238  	int32 readPos = 0;
239  	while (!fQuitting) {
240  		// If Pty Buffer nearly full, snooze this thread, and continue.
241  		while (READ_BUF_SIZE - bufferSize < MIN_PTY_BUFFER_SPACE) {
242  			status_t status;
243  			do {
244  				status = acquire_sem(fReaderLocker);
245  			} while (status == B_INTERRUPTED);
246  			if (status < B_OK)
247  				return status;
248  
249  			bufferSize = fReadBufferSize;
250  		}
251  
252  		// Read PTY
253  		uchar buf[READ_BUF_SIZE];
254  		ssize_t nread = read(fFd, buf, READ_BUF_SIZE - bufferSize);
255  		if (nread <= 0) {
256  			fBuffer->NotifyQuit(errno);
257  			return B_OK;
258  		}
259  
260  		// Copy read string to PtyBuffer.
261  
262  		int32 left = READ_BUF_SIZE - readPos;
263  
264  		if (nread >= left) {
265  			memcpy(fReadBuffer + readPos, buf, left);
266  			memcpy(fReadBuffer, buf + left, nread - left);
267  		} else
268  			memcpy(fReadBuffer + readPos, buf, nread);
269  
270  		bufferSize = atomic_add(&fReadBufferSize, nread);
271  		if (bufferSize == 0)
272  			release_sem(fReaderSem);
273  
274  		bufferSize += nread;
275  		readPos = (readPos + nread) % READ_BUF_SIZE;
276  	}
277  
278  	return B_OK;
279  }
280  
281  
282  void
283  TermParse::DumpState(int *groundtable, int *parsestate, uchar c)
284  {
285  	static const struct {
286  		int *p;
287  		const char *name;
288  	} tables[] = {
289  #define T(t) \
290  	{ t, #t }
291  		T(gUTF8GroundTable),
292  		T(gISO8859GroundTable),
293  		T(gWinCPGroundTable),
294  		T(gSJISGroundTable),
295  		T(gEscTable),
296  		T(gCsiTable),
297  		T(gDecTable),
298  		T(gScrTable),
299  		T(gIgnoreTable),
300  		T(gIesTable),
301  		T(gEscIgnoreTable),
302  		{ NULL, NULL }
303  	};
304  	int i;
305  	fprintf(stderr, "groundtable: ");
306  	for (i = 0; tables[i].p; i++) {
307  		if (tables[i].p == groundtable)
308  			fprintf(stderr, "%s\t", tables[i].name);
309  	}
310  	fprintf(stderr, "parsestate: ");
311  	for (i = 0; tables[i].p; i++) {
312  		if (tables[i].p == parsestate)
313  			fprintf(stderr, "%s\t", tables[i].name);
314  	}
315  	fprintf(stderr, "char: 0x%02x (%d)\n", c, c);
316  }
317  
318  
319  int *
320  TermParse::_GuessGroundTable(int encoding)
321  {
322  	switch (encoding) {
323  		case B_ISO1_CONVERSION:
324  		case B_ISO2_CONVERSION:
325  		case B_ISO3_CONVERSION:
326  		case B_ISO4_CONVERSION:
327  		case B_ISO5_CONVERSION:
328  		case B_ISO6_CONVERSION:
329  		case B_ISO7_CONVERSION:
330  		case B_ISO8_CONVERSION:
331  		case B_ISO9_CONVERSION:
332  		case B_ISO10_CONVERSION:
333  		case B_ISO13_CONVERSION:
334  		case B_ISO14_CONVERSION:
335  		case B_ISO15_CONVERSION:
336  		case B_EUC_CONVERSION:
337  		case B_EUC_KR_CONVERSION:
338  		case B_JIS_CONVERSION:
339  		case B_BIG5_CONVERSION:
340  			return gISO8859GroundTable;
341  
342  		case B_KOI8R_CONVERSION:
343  		case B_MS_WINDOWS_1251_CONVERSION:
344  		case B_MS_WINDOWS_CONVERSION:
345  		case B_MAC_ROMAN_CONVERSION:
346  		case B_MS_DOS_866_CONVERSION:
347  		case B_GBK_CONVERSION:
348  		case B_MS_DOS_CONVERSION:
349  			return gWinCPGroundTable;
350  
351  		case B_SJIS_CONVERSION:
352  			return gSJISGroundTable;
353  
354  		case M_UTF8:
355  		default:
356  			break;
357  	}
358  
359  	return gUTF8GroundTable;
360  }
361  
362  
363  int32
364  TermParse::EscParse()
365  {
366  	int top = 0;
367  	int bottom = 0;
368  
369  	char cbuf[4] = { 0 };
370  	char dstbuf[4] = { 0 };
371  
372  	int currentEncoding = -1;
373  
374  	int param[NPARAM];
375  	int nparam = 1;
376  	for (int i = 0; i < NPARAM; i++)
377  		param[i] = DEFAULT;
378  
379  	int row = 0;
380  	int column = 0;
381  
382  	// default encoding system is UTF8
383  	int *groundtable = gUTF8GroundTable;
384  	int *parsestate = gUTF8GroundTable;
385  
386  	// handle alternative character sets G0 - G4
387  	const char** graphSets[4] = { NULL, NULL, NULL, NULL };
388  	int curGL = 0;
389  	int curGR = 0;
390  
391  	BAutolock locker(fBuffer);
392  
393  	while (!fQuitting) {
394  		try {
395  			uchar c = _NextParseChar();
396  
397  			//DumpState(groundtable, parsestate, c);
398  
399  			if (currentEncoding != fBuffer->Encoding()) {
400  				// Change coding, change parse table.
401  				groundtable = _GuessGroundTable(fBuffer->Encoding());
402  				parsestate = groundtable;
403  				currentEncoding = fBuffer->Encoding();
404  			}
405  
406  	//debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]);
407  			int32 srcLen = 0;
408  			int32 dstLen = sizeof(dstbuf);
409  			int32 dummyState = 0;
410  
411  			switch (parsestate[c]) {
412  				case CASE_PRINT:
413  				{
414  					int curGS = c < 128 ? curGL : curGR;
415  					const char** curGraphSet = graphSets[curGS];
416  					if (curGraphSet != NULL) {
417  						int offset = c - (c < 128 ? 0x20 : 0xA0);
418  						if (offset >= 0 && offset < 96
419  							&& curGraphSet[offset] != 0) {
420  							fBuffer->InsertChar(curGraphSet[offset]);
421  							break;
422  						}
423  					}
424  					fBuffer->InsertChar((char)c);
425  					break;
426  				}
427  				case CASE_PRINT_GR:
428  				{
429  					/* case iso8859 gr character, or euc */
430  					switch (currentEncoding) {
431  						case B_EUC_CONVERSION:
432  						case B_EUC_KR_CONVERSION:
433  						case B_JIS_CONVERSION:
434  						case B_BIG5_CONVERSION:
435  							cbuf[srcLen++] = c;
436  							c = _NextParseChar();
437  							cbuf[srcLen++] = c;
438  							break;
439  
440  						case B_GBK_CONVERSION:
441  							cbuf[srcLen++] = c;
442  							do {
443  								// GBK-compatible codepoints are 2-bytes long
444  								c = _NextParseChar();
445  								cbuf[srcLen++] = c;
446  
447  								// GB18030 extends GBK with 4-byte codepoints
448  								// using 2nd byte from range 0x30...0x39
449  								if (srcLen == 2 && (c < 0x30 || c > 0x39))
450  									break;
451  							} while (srcLen < 4);
452  							break;
453  
454  						default: // ISO-8859-1...10 and MacRoman
455  							cbuf[srcLen++] = c;
456  							break;
457  					}
458  
459  					if (srcLen > 0) {
460  						int encoding = currentEncoding == B_JIS_CONVERSION
461  							? B_EUC_CONVERSION : currentEncoding;
462  
463  						convert_to_utf8(encoding, cbuf, &srcLen,
464  								dstbuf, &dstLen, &dummyState, '?');
465  
466  						fBuffer->InsertChar(UTF8Char(dstbuf, dstLen));
467  					}
468  					break;
469  				}
470  
471  				case CASE_LF:
472  					fBuffer->InsertLF();
473  					break;
474  
475  				case CASE_CR:
476  					fBuffer->InsertCR();
477  					break;
478  
479  				case CASE_INDEX:
480  					fBuffer->InsertLF();
481  					parsestate = groundtable;
482  					break;
483  
484  				case CASE_NEXT_LINE:
485  					fBuffer->NextLine();
486  					parsestate = groundtable;
487  					break;
488  
489  				case CASE_SJIS_KANA:
490  					cbuf[srcLen++] = c;
491  					convert_to_utf8(currentEncoding, cbuf, &srcLen,
492  							dstbuf, &dstLen, &dummyState, '?');
493  					fBuffer->InsertChar(UTF8Char(dstbuf, dstLen));
494  					break;
495  
496  				case CASE_SJIS_INSTRING:
497  					cbuf[srcLen++] = c;
498  					c = _NextParseChar();
499  					cbuf[srcLen++] = c;
500  
501  					convert_to_utf8(currentEncoding, cbuf, &srcLen,
502  							dstbuf, &dstLen, &dummyState, '?');
503  					fBuffer->InsertChar(UTF8Char(dstbuf, dstLen));
504  					break;
505  
506  				case CASE_UTF8_2BYTE:
507  					cbuf[srcLen++] = c;
508  					c = _NextParseChar();
509  					if (groundtable[c] != CASE_UTF8_INSTRING)
510  						break;
511  					cbuf[srcLen++] = c;
512  
513  					fBuffer->InsertChar(UTF8Char(cbuf, srcLen));
514  					break;
515  
516  				case CASE_UTF8_3BYTE:
517  					cbuf[srcLen++] = c;
518  
519  					do {
520  						c = _NextParseChar();
521  						if (groundtable[c] != CASE_UTF8_INSTRING) {
522  							srcLen = 0;
523  							break;
524  						}
525  						cbuf[srcLen++] = c;
526  
527  					} while (srcLen != 3);
528  
529  					if (srcLen > 0)
530  						fBuffer->InsertChar(UTF8Char(cbuf, srcLen));
531  					break;
532  
533  				case CASE_SCS_STATE:
534  				{
535  					int set = -1;
536  					switch (c) {
537  						case '(':
538  							set = 0;
539  							break;
540  						case ')':
541  						case '-':
542  							set = 1;
543  							break;
544  						case '*':
545  						case '.':
546  							set = 2;
547  							break;
548  						case '+':
549  						case '/':
550  							set = 3;
551  							break;
552  						default:
553  							break;
554  					}
555  
556  					if (set > -1) {
557  						char page = _NextParseChar();
558  						switch (page) {
559  							case '0':
560  								graphSets[set] = gLineDrawGraphSet;
561  								break;
562  							default:
563  								graphSets[set] = NULL;
564  								break;
565  						}
566  					}
567  
568  					parsestate = groundtable;
569  					break;
570  				}
571  
572  				case CASE_GROUND_STATE:
573  					/* exit ignore mode */
574  					parsestate = groundtable;
575  					break;
576  
577  				case CASE_BELL:
578  					beep();
579  					break;
580  
581  				case CASE_BS:
582  					fBuffer->MoveCursorLeft(1);
583  					break;
584  
585  				case CASE_TAB:
586  					fBuffer->InsertTab();
587  					break;
588  
589  				case CASE_ESC:
590  					/* escape */
591  					parsestate = gEscTable;
592  					break;
593  
594  				case CASE_IGNORE_STATE:
595  					/* Ies: ignore anything else */
596  					parsestate = gIgnoreTable;
597  					break;
598  
599  				case CASE_IGNORE_ESC:
600  					/* Ign: escape */
601  					parsestate = gIesTable;
602  					break;
603  
604  				case CASE_IGNORE:
605  					/* Ignore character */
606  					break;
607  
608  				case CASE_LS1:
609  					/* select G1 into GL */
610  					curGL = 1;
611  					parsestate = groundtable;
612  					break;
613  
614  				case CASE_LS0:
615  					/* select G0 into GL */
616  					curGL = 0;
617  					parsestate = groundtable;
618  					break;
619  
620  				case CASE_SCR_STATE:	// ESC #
621  					/* enter scr state */
622  					parsestate = gScrTable;
623  					break;
624  
625  				case CASE_ESC_IGNORE:
626  					/* unknown escape sequence */
627  					parsestate = gEscIgnoreTable;
628  					break;
629  
630  				case CASE_ESC_DIGIT:	// ESC [ number
631  					/* digit in csi or dec mode */
632  					if ((row = param[nparam - 1]) == DEFAULT)
633  						row = 0;
634  					param[nparam - 1] = 10 * row + (c - '0');
635  					break;
636  
637  				case CASE_ESC_SEMI:		// ESC ;
638  					/* semicolon in csi or dec mode */
639  					if (nparam < NPARAM)
640  						param[nparam++] = DEFAULT;
641  					break;
642  
643  				case CASE_CSI_SP: // ESC [N q
644  					// part of change cursor style DECSCUSR
645  					if (nparam < NPARAM)
646  						param[nparam++] = ' ';
647  					break;
648  
649  				case CASE_DEC_STATE:
650  					/* enter dec mode */
651  					parsestate = gDecTable;
652  					break;
653  
654  				case CASE_ICH:		// ESC [@ insert charactor
655  					/* ICH */
656  					if ((row = param[0]) < 1)
657  						row = 1;
658  					fBuffer->InsertSpace(row);
659  					parsestate = groundtable;
660  					break;
661  
662  				case CASE_CUU:		// ESC [A cursor up, up arrow key.
663  					/* CUU */
664  					if ((row = param[0]) < 1)
665  						row = 1;
666  					fBuffer->MoveCursorUp(row);
667  					parsestate = groundtable;
668  					break;
669  
670  				case CASE_CUD:		// ESC [B cursor down, down arrow key.
671  					/* CUD */
672  					if ((row = param[0]) < 1)
673  						row = 1;
674  					fBuffer->MoveCursorDown(row);
675  					parsestate = groundtable;
676  					break;
677  
678  				case CASE_CUF:		// ESC [C cursor forword
679  					/* CUF */
680  					if ((row = param[0]) < 1)
681  						row = 1;
682  					fBuffer->MoveCursorRight(row);
683  					parsestate = groundtable;
684  					break;
685  
686  				case CASE_CUB:		// ESC [D cursor backword
687  					/* CUB */
688  					if ((row = param[0]) < 1)
689  						row = 1;
690  					fBuffer->MoveCursorLeft(row);
691  					parsestate = groundtable;
692  					break;
693  
694  				case CASE_CUP:		// ESC [...H move cursor
695  					/* CUP | HVP */
696  					if ((row = param[0]) < 1)
697  						row = 1;
698  					if (nparam < 2 || (column = param[1]) < 1)
699  						column = 1;
700  
701  					fBuffer->SetCursor(column - 1, row - 1 );
702  					parsestate = groundtable;
703  					break;
704  
705  				case CASE_ED:		// ESC [ ...J clear screen
706  					/* ED */
707  					switch (param[0]) {
708  						case DEFAULT:
709  						case 0:
710  							fBuffer->EraseBelow();
711  							break;
712  
713  						case 1:
714  							fBuffer->EraseAbove();
715  							break;
716  
717  						case 2:
718  							fBuffer->EraseAll();
719  							break;
720  					}
721  					parsestate = groundtable;
722  					break;
723  
724  				case CASE_EL:		// ESC [ ...K delete line
725  					/* EL */
726  					switch (param[0]) {
727  						case DEFAULT:
728  						case 0:
729  							fBuffer->DeleteColumns();
730  							break;
731  
732  						case 1:
733  							fBuffer->EraseCharsFrom(0, fBuffer->Cursor().x + 1);
734  							break;
735  
736  						case 2:
737  							fBuffer->DeleteColumnsFrom(0);
738  							break;
739  					}
740  					parsestate = groundtable;
741  					break;
742  
743  				case CASE_IL:
744  					/* IL */
745  					if ((row = param[0]) < 1)
746  						row = 1;
747  					fBuffer->InsertLines(row);
748  					parsestate = groundtable;
749  					break;
750  
751  				case CASE_DL:
752  					/* DL */
753  					if ((row = param[0]) < 1)
754  						row = 1;
755  					fBuffer->DeleteLines(row);
756  					parsestate = groundtable;
757  					break;
758  
759  				case CASE_DCH:
760  					/* DCH */
761  					if ((row = param[0]) < 1)
762  						row = 1;
763  					fBuffer->DeleteChars(row);
764  					parsestate = groundtable;
765  					break;
766  
767  				case CASE_SET:
768  					/* SET */
769  					if (param[0] == 4)
770  						fBuffer->SetInsertMode(MODE_INSERT);
771  					parsestate = groundtable;
772  					break;
773  
774  				case CASE_RST:
775  					/* RST */
776  					if (param[0] == 4)
777  						fBuffer->SetInsertMode(MODE_OVER);
778  					parsestate = groundtable;
779  					break;
780  
781  				case CASE_SGR:
782  				{
783  					/* SGR */
784  					Attributes attributes = fBuffer->GetAttributes();
785  					for (row = 0; row < nparam; ++row) {
786  						switch (param[row]) {
787  							case DEFAULT:
788  							case 0: /* Reset attribute */
789  								attributes.Reset();
790  								break;
791  
792  							case 1: /* Bold     */
793  							case 5:
794  								attributes |= BOLD;
795  								break;
796  
797  							case 4:	/* Underline	*/
798  								attributes |= UNDERLINE;
799  								break;
800  
801  							case 7:	/* Inverse	*/
802  								attributes |= INVERSE;
803  								break;
804  
805  							case 22:	/* Not Bold	*/
806  								attributes &= ~BOLD;
807  								break;
808  
809  							case 24:	/* Not Underline	*/
810  								attributes &= ~UNDERLINE;
811  								break;
812  
813  							case 27:	/* Not Inverse	*/
814  								attributes &= ~INVERSE;
815  								break;
816  
817  							case 90:
818  							case 91:
819  							case 92:
820  							case 93:
821  							case 94:
822  							case 95:
823  							case 96:
824  							case 97:
825  								param[row] -= 60;
826  							case 30:
827  							case 31:
828  							case 32:
829  							case 33:
830  							case 34:
831  							case 35:
832  							case 36:
833  							case 37:
834  								attributes.SetIndexedForeground(param[row] - 30);
835  								break;
836  
837  							case 38:
838  							{
839  								if (nparam >= 3 && param[row+1] == 5) {
840  									attributes.SetIndexedForeground(param[row+2]);
841  									row += 2;
842  								} else if (nparam >= 5 && param[row+1] == 2) {
843  									attributes.SetDirectForeground(param[row+2], param[row+3], param[row+4]);
844  									row += 4;
845  								} else {
846  									row = nparam; // force exit of the parsing
847  								}
848  
849  								break;
850  							}
851  
852  							case 39:
853  								attributes.UnsetForeground();
854  								break;
855  
856  							case 100:
857  							case 101:
858  							case 102:
859  							case 103:
860  							case 104:
861  							case 105:
862  							case 106:
863  							case 107:
864  								param[row] -= 60;
865  							case 40:
866  							case 41:
867  							case 42:
868  							case 43:
869  							case 44:
870  							case 45:
871  							case 46:
872  							case 47:
873  								attributes.SetIndexedBackground(param[row] - 40);
874  								break;
875  
876  							case 48:
877  							{
878  								if (nparam >= 3 && param[row+1] == 5) {
879  									attributes.SetIndexedBackground(param[row+2]);
880  									row += 2;
881  								} else if (nparam >= 5 && param[row+1] == 2) {
882  									attributes.SetDirectBackground(param[row+2], param[row+3], param[row+4]);
883  									row += 4;
884  								} else {
885  									row = nparam; // force exit of the parsing
886  								}
887  
888  								break;
889  							}
890  
891  							case 49:
892  								attributes.UnsetBackground();
893  								break;
894  						}
895  					}
896  					fBuffer->SetAttributes(attributes);
897  					parsestate = groundtable;
898  					break;
899  				}
900  
901  				case CASE_CPR:
902  				// Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp)
903  				// 21-JUL-99
904  				_DeviceStatusReport(param[0]);
905  				parsestate = groundtable;
906  				break;
907  
908  				case CASE_DA1:
909  				// DA - report device attributes
910  				if (param[0] < 1) {
911  					// claim to be a VT102
912  					write(fFd, "\033[?6c", 5);
913  				}
914  				parsestate = groundtable;
915  				break;
916  
917  				case CASE_DECSTBM:
918  				/* DECSTBM - set scrolling region */
919  
920  				if ((top = param[0]) < 1)
921  					top = 1;
922  
923  				if (nparam < 2)
924  					bottom = fBuffer->Height();
925  				else
926  					bottom = param[1];
927  
928  				top--;
929  					bottom--;
930  
931  					if (bottom > top)
932  						fBuffer->SetScrollRegion(top, bottom);
933  
934  					parsestate = groundtable;
935  					break;
936  
937  				case CASE_DECSCUSR_ETC:
938  				// DECSCUSR - set cursor style VT520
939  					if (nparam == 2 && param[1] == ' ') {
940  						bool blinking = (param[0] & 0x01) != 0;
941  						int style = -1;
942  						switch (param[0]) {
943  							case 0:
944  								blinking = true;
945  							case 1:
946  							case 2:
947  								style = BLOCK_CURSOR;
948  								break;
949  							case 3:
950  							case 4:
951  								style = UNDERLINE_CURSOR;
952  								break;
953  							case 5:
954  							case 6:
955  								style = IBEAM_CURSOR;
956  								break;
957  						}
958  
959  						if (style != -1)
960  							fBuffer->SetCursorStyle(style, blinking);
961  					}
962  					parsestate = groundtable;
963  					break;
964  
965  				case CASE_DECREQTPARM:
966  					// DEXREQTPARM - request terminal parameters
967  					_DecReqTermParms(param[0]);
968  					parsestate = groundtable;
969  					break;
970  
971  				case CASE_DECSET:
972  					/* DECSET */
973  					for (int i = 0; i < nparam; i++)
974  						_DecPrivateModeSet(param[i]);
975  					parsestate = groundtable;
976  					break;
977  
978  				case CASE_DECRST:
979  					/* DECRST */
980  					for (int i = 0; i < nparam; i++)
981  						_DecPrivateModeReset(param[i]);
982  					parsestate = groundtable;
983  					break;
984  
985  				case CASE_DECALN:
986  					/* DECALN */
987  					{
988  						Attributes attr;
989  						fBuffer->FillScreen(UTF8Char('E'), attr);
990  						parsestate = groundtable;
991  					}
992  					break;
993  
994  					//	case CASE_GSETS:
995  					//		screen->gsets[scstype] = GSET(c) | cs96;
996  					//		parsestate = groundtable;
997  					//		break;
998  
999  				case CASE_DECSC:
1000  					/* DECSC */
1001  					fBuffer->SaveCursor();
1002  					parsestate = groundtable;
1003  					break;
1004  
1005  				case CASE_DECRC:
1006  					/* DECRC */
1007  					fBuffer->RestoreCursor();
1008  					parsestate = groundtable;
1009  					break;
1010  
1011  				case CASE_HTS:
1012  					/* HTS */
1013  					fBuffer->SetTabStop(fBuffer->Cursor().x);
1014  					parsestate = groundtable;
1015  					break;
1016  
1017  				case CASE_TBC:
1018  					/* TBC */
1019  					if (param[0] < 1)
1020  						fBuffer->ClearTabStop(fBuffer->Cursor().x);
1021  					else if (param[0] == 3)
1022  						fBuffer->ClearAllTabStops();
1023  					parsestate = groundtable;
1024  					break;
1025  
1026  				case CASE_RI:
1027  					/* RI */
1028  					fBuffer->InsertRI();
1029  					parsestate = groundtable;
1030  					break;
1031  
1032  				case CASE_SS2:
1033  					/* SS2 */
1034  					parsestate = groundtable;
1035  					break;
1036  
1037  				case CASE_SS3:
1038  					/* SS3 */
1039  					parsestate = groundtable;
1040  					break;
1041  
1042  				case CASE_CSI_STATE:
1043  					/* enter csi state */
1044  					nparam = 1;
1045  					param[0] = DEFAULT;
1046  					parsestate = gCsiTable;
1047  					break;
1048  
1049  				case CASE_OSC:
1050  					{
1051  						/* Operating System Command: ESC ] */
1052  						uchar params[512];
1053  						// fill the buffer until BEL, ST or something else.
1054  						bool isParsed = false;
1055  						int32 skipCount = 0; // take care about UTF-8 characters
1056  						for (uint i = 0; !isParsed && i < sizeof(params); i++) {
1057  							params[i] = _NextParseChar();
1058  
1059  							if (skipCount > 0) {
1060  								skipCount--;
1061  								continue;
1062  							}
1063  
1064  							skipCount = UTF8Char::ByteCount(params[i]) - 1;
1065  							if (skipCount > 0)
1066  								continue;
1067  
1068  							switch (params[i]) {
1069  								// BEL
1070  								case 0x07:
1071  									isParsed = true;
1072  									break;
1073  								// 8-bit ST
1074  								case 0x9c:
1075  									isParsed = true;
1076  									break;
1077  								// 7-bit ST is "ESC \"
1078  								case '\\':
1079  								// hm... Was \x1b replaced by 0 during parsing?
1080  									if (i > 0 && params[i - 1] == 0) {
1081  										isParsed = true;
1082  										break;
1083  									}
1084  								default:
1085  									if (!isprint(params[i] & 0x7f))
1086  										break;
1087  									continue;
1088  							}
1089  							params[i] = '\0';
1090  						}
1091  
1092  						// watchdog for the 'end of buffer' case
1093  						params[sizeof(params) - 1] = '\0';
1094  
1095  						if (isParsed)
1096  							_ProcessOperatingSystemControls(params);
1097  
1098  						parsestate = groundtable;
1099  						break;
1100  					}
1101  
1102  				case CASE_RIS:		// ESC c ... Reset terminal.
1103  					break;
1104  
1105  				case CASE_LS2:
1106  					/* select G2 into GL */
1107  					curGL = 2;
1108  					parsestate = groundtable;
1109  					break;
1110  
1111  				case CASE_LS3:
1112  					/* select G3 into GL */
1113  					curGL = 3;
1114  					parsestate = groundtable;
1115  					break;
1116  
1117  				case CASE_LS3R:
1118  					/* select G3 into GR */
1119  					curGR = 3;
1120  					parsestate = groundtable;
1121  					break;
1122  
1123  				case CASE_LS2R:
1124  					/* select G2 into GR */
1125  					curGR = 2;
1126  					parsestate = groundtable;
1127  					break;
1128  
1129  				case CASE_LS1R:
1130  					/* select G1 into GR */
1131  					curGR = 1;
1132  					parsestate = groundtable;
1133  					break;
1134  
1135  				case CASE_VPA:		// ESC [...d move cursor absolute vertical
1136  					/* VPA (CV) */
1137  					if ((row = param[0]) < 1)
1138  						row = 1;
1139  
1140  					// note beterm wants it 1-based unlike usual terminals
1141  					fBuffer->SetCursorY(row - 1);
1142  					parsestate = groundtable;
1143  					break;
1144  
1145  				case CASE_HPA:		// ESC [...G move cursor absolute horizontal
1146  					/* HPA (CH) */
1147  					if ((column = param[0]) < 1)
1148  						column = 1;
1149  
1150  					// note beterm wants it 1-based unlike usual terminals
1151  					fBuffer->SetCursorX(column - 1);
1152  					parsestate = groundtable;
1153  					break;
1154  
1155  				case CASE_SU:	// scroll screen up
1156  					if ((row = param[0]) < 1)
1157  						row = 1;
1158  					fBuffer->ScrollBy(row);
1159  					parsestate = groundtable;
1160  					break;
1161  
1162  				case CASE_SD:	// scroll screen down
1163  					if ((row = param[0]) < 1)
1164  						row = 1;
1165  					fBuffer->ScrollBy(-row);
1166  					parsestate = groundtable;
1167  					break;
1168  
1169  
1170  				case CASE_ECH:	// erase characters
1171  					if ((column = param[0]) < 1)
1172  						column = 1;
1173  					fBuffer->EraseChars(column);
1174  					parsestate = groundtable;
1175  					break;
1176  
1177  				case CASE_CBT:	// cursor back tab
1178  					if ((column = param[0]) < 1)
1179  						column = 1;
1180  					fBuffer->InsertCursorBackTab(column);
1181  					parsestate = groundtable;
1182  					break;
1183  
1184  				case CASE_CFT:	// cursor forward tab
1185  					if ((column= param[0]) < 1)
1186  						column = 1;
1187  					for (int32 i = 0; i < column; ++i)
1188  						fBuffer->InsertTab();
1189  					parsestate = groundtable;
1190  					break;
1191  
1192  				case CASE_CNL:	// cursor next line
1193  					if ((row= param[0]) < 1)
1194  						row = 1;
1195  					fBuffer->SetCursorX(0);
1196  					fBuffer->MoveCursorDown(row);
1197  					parsestate = groundtable;
1198  					break;
1199  
1200  				case CASE_CPL:	// cursor previous line
1201  					if ((row= param[0]) < 1)
1202  						row = 1;
1203  					fBuffer->SetCursorX(0);
1204  					fBuffer->MoveCursorUp(row);
1205  					parsestate = groundtable;
1206  					break;
1207  
1208  				case CASE_REP:		// ESC [...b repeat last graphic char
1209  				{
1210  					int repetitions = param[0];
1211  					int maxRepetitions = fBuffer->Width() * fBuffer->Height();
1212  					if (repetitions > maxRepetitions)
1213  						repetitions = maxRepetitions;
1214  					for (int i = 0; i < repetitions; i++)
1215  						fBuffer->InsertLastChar();
1216  					parsestate = groundtable;
1217  					break;
1218  				}
1219  				default:
1220  					break;
1221  			}
1222  		} catch (...) {
1223  			break;
1224  		}
1225  	}
1226  
1227  	return B_OK;
1228  }
1229  
1230  
1231  /*static*/ int32
1232  TermParse::_ptyreader_thread(void *data)
1233  {
1234  	return reinterpret_cast<TermParse *>(data)->PtyReader();
1235  }
1236  
1237  
1238  /*static*/ int32
1239  TermParse::_escparse_thread(void *data)
1240  {
1241  	return reinterpret_cast<TermParse *>(data)->EscParse();
1242  }
1243  
1244  
1245  status_t
1246  TermParse::_ReadParserBuffer()
1247  {
1248  	// We have to unlock the terminal buffer while waiting for data from the
1249  	// PTY. We don't have to unlock when we don't need to wait, but we do it
1250  	// anyway, so that TermView won't be starved when trying to synchronize.
1251  	fBuffer->Unlock();
1252  
1253  	// wait for new input from pty
1254  	if (atomic_get(&fReadBufferSize) == 0) {
1255  		status_t status = B_OK;
1256  		while (atomic_get(&fReadBufferSize) == 0 && status == B_OK) {
1257  			do {
1258  				status = acquire_sem(fReaderSem);
1259  			} while (status == B_INTERRUPTED);
1260  
1261  			// eat any sems that were released unconditionally
1262  			int32 semCount;
1263  			if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0)
1264  				acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0);
1265  		}
1266  
1267  		if (status < B_OK) {
1268  			fBuffer->Lock();
1269  			return status;
1270  		}
1271  	}
1272  
1273  	int32 toRead = atomic_get(&fReadBufferSize);
1274  	if (toRead > ESC_PARSER_BUFFER_SIZE)
1275  		toRead = ESC_PARSER_BUFFER_SIZE;
1276  
1277  	for (int32 i = 0; i < toRead; i++) {
1278  		// TODO: This could be optimized using memcpy instead and
1279  		// calculating space left as in the PtyReader().
1280  		fParserBuffer[i] = fReadBuffer[fBufferPosition];
1281  		fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE;
1282  	}
1283  
1284  	int32 bufferSize = atomic_add(&fReadBufferSize, -toRead);
1285  
1286  	// If the pty reader thread waits and we have made enough space in the
1287  	// buffer now, let it run again.
1288  	if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE
1289  			&& bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) {
1290  		release_sem(fReaderLocker);
1291  	}
1292  
1293  	fParserBufferSize = toRead;
1294  	fParserBufferOffset = 0;
1295  
1296  	fBuffer->Lock();
1297  	return B_OK;
1298  }
1299  
1300  
1301  void
1302  TermParse::_DeviceStatusReport(int n)
1303  {
1304  	char sbuf[16] ;
1305  	int len;
1306  
1307  	switch (n) {
1308  		case 5:
1309  			{
1310  				// Device status report requested
1311  				// reply with "no malfunction detected"
1312  				const char* toWrite = "\033[0n";
1313  				write(fFd, toWrite, strlen(toWrite));
1314  				break ;
1315  			}
1316  		case 6:
1317  			// Cursor position report requested
1318  			len = snprintf(sbuf, sizeof(sbuf),
1319  				"\033[%" B_PRId32 ";%" B_PRId32 "R",
1320  				fBuffer->Cursor().y + 1,
1321  				fBuffer->Cursor().x + 1);
1322  			write(fFd, sbuf, len);
1323  			break ;
1324  		default:
1325  			return;
1326  	}
1327  }
1328  
1329  
1330  void
1331  TermParse::_DecReqTermParms(int value)
1332  {
1333  	// Terminal parameters report:
1334  	//   type (2 or 3);
1335  	//   no parity (1);
1336  	//   8 bits per character (1);
1337  	//   transmit speed 38400bps (128);
1338  	//   receive speed 38400bps (128);
1339  	//   bit rate multiplier 16 (1);
1340  	//   no flags (0)
1341  	char parms[] = "\033[?;1;1;128;128;1;0x";
1342  
1343  	if (value < 1)
1344  		parms[2] = '2';
1345  	else if (value == 1)
1346  		parms[2] = '3';
1347  	else
1348  		return;
1349  
1350  	write(fFd, parms, strlen(parms));
1351  }
1352  
1353  
1354  void
1355  TermParse::_DecPrivateModeSet(int value)
1356  {
1357  	switch (value) {
1358  		case 1:
1359  			// Application Cursor Keys (whatever that means).
1360  			// Not supported yet.
1361  			break;
1362  		case 5:
1363  			// Reverse Video (inverses colors for the complete screen
1364  			// -- when followed by normal video, that's shortly flashes the
1365  			// screen).
1366  			// Not supported yet.
1367  			break;
1368  		case 6:
1369  			// Set Origin Mode.
1370  			fBuffer->SetOriginMode(true);
1371  			break;
1372  		case 9:
1373  			// Set Mouse X and Y on button press.
1374  			fBuffer->ReportX10MouseEvent(true);
1375  			break;
1376  		case 12:
1377  			// Start Blinking Cursor.
1378  			fBuffer->SetCursorBlinking(true);
1379  			break;
1380  		case 25:
1381  			// Show Cursor.
1382  			fBuffer->SetCursorHidden(false);
1383  			break;
1384  		case 47:
1385  			// Use Alternate Screen Buffer.
1386  			fBuffer->UseAlternateScreenBuffer(false);
1387  			break;
1388  		case 1000:
1389  			// Send Mouse X & Y on button press and release.
1390  			fBuffer->ReportNormalMouseEvent(true);
1391  			break;
1392  		case 1002:
1393  			// Send Mouse X and Y on button press and release, and on motion
1394  			// when the mouse enter a new cell
1395  			fBuffer->ReportButtonMouseEvent(true);
1396  			break;
1397  		case 1003:
1398  			// Use All Motion Mouse Tracking
1399  			fBuffer->ReportAnyMouseEvent(true);
1400  			break;
1401  		case 1006:
1402  			// Enable extended mouse coordinates with SGR scheme
1403  			fBuffer->EnableExtendedMouseCoordinates(true);
1404  			break;
1405  		case 1034:
1406  			// Interpret "meta" key, sets eighth bit.
1407  			fBuffer->EnableInterpretMetaKey(true);
1408  			break;
1409  		case 1036:
1410  			// Send ESC when Meta modifies a key
1411  			fBuffer->EnableMetaKeySendsEscape(true);
1412  			break;
1413  		case 1039:
1414  			// TODO: Send ESC when Alt modifies a key
1415  			// Not supported yet.
1416  			break;
1417  		case 1049:
1418  			// Save cursor as in DECSC and use Alternate Screen Buffer, clearing
1419  			// it first.
1420  			fBuffer->SaveCursor();
1421  			fBuffer->UseAlternateScreenBuffer(true);
1422  			break;
1423  	}
1424  }
1425  
1426  
1427  void
1428  TermParse::_DecPrivateModeReset(int value)
1429  {
1430  	switch (value) {
1431  		case 1:
1432  			// Normal Cursor Keys (whatever that means).
1433  			// Not supported yet.
1434  			break;
1435  		case 3:
1436  			// 80 Column Mode.
1437  			// Not supported yet.
1438  			break;
1439  		case 4:
1440  			// Jump (Fast) Scroll.
1441  			// Not supported yet.
1442  			break;
1443  		case 5:
1444  			// Normal Video (Leaves Reverse Video, cf. there).
1445  			// Not supported yet.
1446  			break;
1447  		case 6:
1448  			// Reset Origin Mode.
1449  			fBuffer->SetOriginMode(false);
1450  			break;
1451  		case 9:
1452  			// Disable Mouse X and Y on button press.
1453  			fBuffer->ReportX10MouseEvent(false);
1454  			break;
1455  		case 12:
1456  			// Stop Blinking Cursor.
1457  			fBuffer->SetCursorBlinking(false);
1458  			break;
1459  		case 25:
1460  			// Hide Cursor
1461  			fBuffer->SetCursorHidden(true);
1462  			break;
1463  		case 47:
1464  			// Use Normal Screen Buffer.
1465  			fBuffer->UseNormalScreenBuffer();
1466  			break;
1467  		case 1000:
1468  			// Don't send Mouse X & Y on button press and release.
1469  			fBuffer->ReportNormalMouseEvent(false);
1470  			break;
1471  		case 1002:
1472  			// Don't send Mouse X and Y on button press and release, and on motion
1473  			// when the mouse enter a new cell
1474  			fBuffer->ReportButtonMouseEvent(false);
1475  			break;
1476  		case 1003:
1477  			// Disable All Motion Mouse Tracking.
1478  			fBuffer->ReportAnyMouseEvent(false);
1479  			break;
1480  		case 1006:
1481  			// Disable extended mouse coordinates with SGR scheme
1482  			fBuffer->EnableExtendedMouseCoordinates(false);
1483  			break;
1484  		case 1034:
1485  			// Don't interpret "meta" key.
1486  			fBuffer->EnableInterpretMetaKey(false);
1487  			break;
1488  		case 1036:
1489  			// Don't send ESC when Meta modifies a key
1490  			fBuffer->EnableMetaKeySendsEscape(false);
1491  			break;
1492  		case 1039:
1493  			// TODO: Don't send ESC when Alt modifies a key
1494  			// Not supported yet.
1495  			break;
1496  		case 1049:
1497  			// Use Normal Screen Buffer and restore cursor as in DECRC.
1498  			fBuffer->UseNormalScreenBuffer();
1499  			fBuffer->RestoreCursor();
1500  			break;
1501  	}
1502  }
1503  
1504  
1505  void
1506  TermParse::_ProcessOperatingSystemControls(uchar* params)
1507  {
1508  	int mode = 0;
1509  	for (uchar c = *params; c != ';' && c != '\0'; c = *(++params)) {
1510  		mode *= 10;
1511  		mode += c - '0';
1512  	}
1513  
1514  	// eat the separator
1515  	if (*params == ';')
1516  		params++;
1517  
1518  	static uint8 indexes[kTermColorCount];
1519  	static rgb_color colors[kTermColorCount];
1520  
1521  	switch (mode) {
1522  		case 0: // icon name and window title
1523  		case 2: // window title
1524  			fBuffer->SetTitle((const char*)params);
1525  			break;
1526  		case 4: // set colors (0 - 255)
1527  		case 104: // reset colors (0 - 255)
1528  			{
1529  				bool reset = (mode / 100) == 1;
1530  
1531  				// colors can be in "idx1:name1;...;idxN:nameN;" sequence too!
1532  				uint32 count = 0;
1533  				char* p = strtok((char*)params, ";");
1534  				while (p != NULL && count < kTermColorCount) {
1535  					indexes[count] = atoi(p);
1536  
1537  					if (!reset) {
1538  						p = strtok(NULL, ";");
1539  						if (p == NULL)
1540  							break;
1541  
1542  						if (gXColorsTable.LookUpColor(p, &colors[count]) == B_OK)
1543  							count++;
1544  					} else
1545  						count++;
1546  
1547  					p = strtok(NULL, ";");
1548  				};
1549  
1550  				if (count > 0) {
1551  					if (!reset)
1552  						fBuffer->SetColors(indexes, colors, count);
1553  					else
1554  						fBuffer->ResetColors(indexes, count);
1555  				}
1556  			}
1557  			break;
1558  		// set dynamic colors (10 - 19)
1559  		case 10: // text foreground
1560  		case 11: // text background
1561  		case 12: // cursor back
1562  			{
1563  				int32 offset = mode - 10;
1564  				int32 count = 0;
1565  				if (strcmp((char*)params, "?") == 0) {
1566  					fBuffer->GetColor(mode);
1567  					break;
1568  				}
1569  				char* p = strtok((char*)params, ";");
1570  				do {
1571  					if (gXColorsTable.LookUpColor(p, &colors[count]) != B_OK) {
1572  						// dyna-colors are pos-sensitive - no chance to continue
1573  						break;
1574  					}
1575  
1576  					indexes[count] = 10 + offset + count;
1577  					count++;
1578  					p = strtok(NULL, ";");
1579  
1580  				} while (p != NULL && (offset + count) < 10);
1581  
1582  				if (count > 0) {
1583  					fBuffer->SetColors(indexes, colors, count, true);
1584  				}
1585  			}
1586  			break;
1587  		// reset dynamic colors (10 - 19)
1588  		case 110: // text foreground
1589  		case 111: // text background
1590  		case 112: // cursor back
1591  			{
1592  				indexes[0] = mode;
1593  				fBuffer->ResetColors(indexes, 1, true);
1594  			}
1595  			break;
1596  		default:
1597  		//	printf("%d -> %s\n", mode, params);
1598  			break;
1599  	}
1600  }
1601