1 /* $Id: ttyio.c,v 1.2.2.4 2003/02/23 14:32:58 amura Exp $ */
2 /*
3  * Name:	MicroEMACS
4  *		System V terminal I/O.
5  * Version:	0
6  * Last edit:	Tue Aug 26 23:57:57 PDT 1986
7  * By:		gonzo!daveb
8  *		{sun, amdahl, mtxinu}!rtech!gonzo!daveb
9  *
10  * The functions in this file
11  * negotiate with the operating system for
12  * keyboard characters, and write characters to
13  * the display in a barely buffered fashion.
14  *
15  * This version goes along with tty/termcap/tty.c.
16  * Terminal size is determined there, rather than here, and
17  * this does not open the termcap file
18  */
19 
20 /*
21  * $Log: ttyio.c,v $
22  * Revision 1.2.2.4  2003/02/23 14:32:58  amura
23  * canna_init() must be called after setttysize() on ttyio.c
24  *
25  * Revision 1.2.2.3  2001/01/23 08:50:29  amura
26  * reset terminal polling mode in ttwait()
27  *
28  * Revision 1.2.2.2  2001/01/17 18:36:45  amura
29  * fix typo POSIXTTY to POSIX_TTY
30  *
31  * Revision 1.2.2.1  2000/12/01 10:04:12  amura
32  * fix ttraw() with termios
33  * unset IEXTEN flag on c_lflag
34  *
35  * Revision 1.2  2000/11/16 14:32:03  amura
36  * fix some typos which cause compile error when using
37  * strict ANSI-C compiler (ex ACK, gcc-1.x)
38  *
39  * Revision 1.1.1.1  2000/06/27 01:47:59  amura
40  * import to CVS
41  *
42  */
43 /* 90.02.05	Modified for Ng 1.0 by S.Yoshida */
44 
45 #include	"config.h"	/* 90.12.20  by S.Yoshida */
46 #include	"def.h"
47 
48 #include	<sys/types.h>
49 #include	<fcntl.h>
50 #ifdef	POSIXTTY		/* by S.Okamoto 93/03/16 */
51 #include	<termios.h>
52 #else
53 #include	<termio.h>
54 #endif
55 #ifdef	ADDFUNC	/* 93.07.08  by S.Yoshida */
56 #include	<signal.h>		/* 93.07.08: For SIGWINCH.	*/
57 #include	<sys/ioctl.h>		/* 00.04.03: amura		*/
58 #endif	/* ADDFUNC */
59 
60 #define	NOBUF	512			/* Output buffer size.		*/
61 
62 char	obuf[NOBUF];			/* Output buffer.		*/
63 int	nobuf;				/* buffer count			*/
64 
65 #ifdef POSIXTTY			/* by S.Okamoto 93/03/16 */
66 static struct termios	ot;		/* entry state of the terminal	*/
67 static struct termios	nt;		/* editor's terminal state	*/
68 #else
69 static struct termio	ot;		/* entry state of the terminal	*/
70 static struct termio	nt;		/* editor's terminal state	*/
71 #endif
72 
73 static int ttyactivep = FALSE;		/* terminal in editor mode?	*/
74 static int ttysavedp = FALSE;		/* terminal state saved?	*/
75 
76 #ifdef	BUGFIX	/* 91.01.14  by S.Yoshida */
77 #ifdef	TIOCGWINSZ
78 struct	winsize winsize;		/* 4.3 BSD window sizing	*/
79 #endif
80 #endif	/* BUGFIX */
81 
82 int	nrow;				/* Terminal size, rows.		*/
83 int	ncol;				/* Terminal size, columns.	*/
84 
85 #ifdef	ADDFUNC		/* 93.07.08  by S.Yoshida */
86 #ifdef	SIGWINCH	/* 93.07.08  by S.Yoshida */
87 VOID	ttwinch();
88 #endif
89 #endif
90 #ifdef HAVE_GETSID
91 int	job_control;
92 #endif
93 
94 /* These are used to implement typeahead on System V */
95 
96 int kbdflgs;			/* saved keyboard fd flags	*/
97 int kbdpoll;			/* in O_NDELAY mode			*/
98 int kbdqp;			/* there is a char in kbdq	*/
99 char kbdq;			/* char we've already read	*/
100 
101 /*
102  * This function gets called once, to set up
103  * the terminal channel.  This version turns off flow
104  * control.  This may be wrong for your system, but no
105  * good solution has really been found (daveb).
106  */
ttopen()107 ttopen()
108 {
109 	register char	*cp;
110 	extern char	*getenv();
111 
112 	if (ttyactivep)
113 		return;
114 
115 	if( !ttysavedp )
116 	{
117 #ifdef POSIXTTY		/* by S.Okamoto 93/03/16 */
118 		if (tcgetattr(0, &ot) < 0)
119 #else
120 		if (ioctl(0, TCGETA, &ot) < 0)
121 #endif
122 			abort();
123 		nt = ot;		/* save entry state		*/
124 		nt.c_cc[VMIN] = 1;	/* one character read is OK	*/
125 		nt.c_cc[VTIME] = 0;	/* Never time out.		*/
126 		nt.c_iflag |= IGNBRK;
127 		nt.c_iflag &= ~( ICRNL | INLCR | ISTRIP | IXON | IXOFF );
128 		nt.c_oflag &= ~OPOST;
129 		nt.c_cflag |= CS8;	/* allow 8th bit on input	*/
130 		nt.c_cflag &= ~PARENB;	/* Don't check parity		*/
131 		nt.c_lflag &= ~( ECHO | ICANON | ISIG );
132 #ifdef	POSIXTTY
133 		nt.c_lflag &= ~IEXTEN;
134 #endif
135 		kbdpoll = (((kbdflgs = fcntl(0, F_GETFL, 0)) & O_NDELAY) != 0);
136 #ifdef HAVE_GETSID
137 		{
138 		pid_t pid,pgid,sid;
139 
140 		pid = getpid();
141 		pgid = getpgrp();
142 		sid = getsid(0);
143 		if (pid == pgid && pgid != sid)
144 			job_control = TRUE;
145 		else
146 			job_control = FALSE;
147 		}
148 #endif /* HAVE_GETSID */
149 
150 		ttysavedp = TRUE;
151 	}
152 #ifdef BUGFIX
153 	else
154 		kbdpoll = ((fcntl(0, F_GETFL, 0) & O_NDELAY) != 0);
155 #endif
156 
157 #ifdef POSIXTTY		/* by S.Okamoto 93/03/16 */
158 	if (tcsetattr(0, TCSAFLUSH, &nt) < 0)
159 #else
160 	if (ioctl(0, TCSETAF, &nt) < 0)
161 #endif
162 		abort();
163 
164 #ifdef	TIOCGWINSZ
165 	if (ioctl(0, TIOCGWINSZ, (char *) &winsize) != -1) {
166 		nrow = winsize . ws_row;
167 		ncol = winsize . ws_col;
168 	} else
169 	nrow = 0;
170 	if (nrow <= 0 || ncol <= 0)
171 #endif	/* TIOCGWINSZ */
172 	/* This really belongs in tty/termcap... */
173 
174 	if ((cp=getenv("TERMCAP")) == NULL
175 	|| (nrow=getvalue(cp, "li")) <= 0
176 	|| (ncol=getvalue(cp, "co")) <= 0) {
177 		nrow = 24;
178 		ncol = 80;
179 	}
180 	if (nrow > NROW)			/* Don't crash if the	*/
181 		nrow = NROW;			/* termcap entry is	*/
182 	if (ncol > NCOL)			/* too big.		*/
183 		ncol = NCOL;
184 #ifdef  ADDFUNC		/* 93.07.08  by S.Yoshida */
185 #ifdef	SIGWINCH	/* 93.07.08  by S.Yoshida */
186 	(void) signal(SIGWINCH, ttwinch);
187 #endif
188 #endif
189 #ifdef CANNA
190 	canna_init();
191 #endif
192 	ttyactivep = TRUE;
193 }
194 
195 /*
196  * This routine scans a string, which is
197  * actually the return value of a getenv call for the TERMCAP
198  * variable, looking for numeric parameter "name". Return the value
199  * if found. Return -1 if not there. Assume that "name" is 2
200  * characters long. This limited use of the TERMCAP lets us find
201  * out the size of a window on the X display.
202  */
getvalue(cp,name)203 getvalue(cp, name)
204 register char	*cp;
205 register char	*name;
206 {
207 	for (;;) {
208 		while (*cp!=0 && *cp!=':')
209 			++cp;
210 		if (*cp++ == 0)			/* Not found.		*/
211 			return (-1);
212 		if (cp[0]==name[0] && cp[1]==name[1] && cp[2]=='#')
213 			return (atoi(cp+3));	/* Stops on ":".	*/
214 	}
215 }
216 
217 /*
218  * This function gets called just
219  * before we go back home to the shell. Put all of
220  * the terminal parameters back.
221  */
ttclose()222 ttclose()
223 {
224 	if(!ttysavedp || !ttyactivep)
225 		return;
226 	ttflush();
227 #ifdef POSIXTTY
228 	if (tcsetattr(0, TCSAFLUSH, &ot) < 0 ||
229 	    fcntl( 0, F_SETFL, kbdflgs ) < 0)
230 #else
231 	if (ioctl(0, TCSETAF, &ot) < 0 || fcntl( 0, F_SETFL, kbdflgs ) < 0)
232 #endif
233 		abort();
234 	ttyactivep = FALSE;
235 #ifdef CANNA
236 	canna_end();
237 #endif
238 }
239 
240 /*
241  * Write character to the display.
242  * Characters are buffered up, to make things
243  * a little bit more efficient.
244  */
ttputc(c)245 ttputc(c)
246 {
247 	if (nobuf >= NOBUF)
248 		ttflush();
249 	obuf[nobuf++] = c;
250 }
251 
252 /*
253  * Flush output.
254  */
ttflush()255 ttflush()
256 {
257 	if (nobuf != 0) {
258 		write(1, obuf, nobuf);
259 		nobuf = 0;
260 	}
261 }
262 
263 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
264 static	int	nkey = 0;		/* The number of ungetc charactor. */
265 static	int	keybuf[4];		/* Ungetc charactors.		*/
266 #endif	/* KANJI */
267 
268 /*
269  * Read character from terminal.
270  * All 8 bits are returned, so that you can use
271  * a multi-national terminal.
272  *
273  * If keyboard 'queue' already has typeahead from a typeahead() call,
274  * just return it.  Otherwise, make sure we are in blocking i/o mode
275  * and read a character.
276  */
ttgetc()277 ttgetc()
278 {
279 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
280 	if (nkey > 0) {
281 		return (keybuf[--nkey]);
282 	}
283 #endif	/* KANJI */
284 	if( kbdqp )
285 		kbdqp = FALSE;
286 	else
287 	{
288 		if( kbdpoll && fcntl( 0, F_SETFL, kbdflgs ) < 0 )
289 			abort();
290 		kbdpoll = FALSE;
291 		while (read(0, &kbdq, 1) != 1)
292 			;
293 	}
294 	return ( kbdq & 0xff );
295 }
296 
297 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
298 /*
299  * Save pre-readed char to read again.
300  */
ttungetc(c)301 ttungetc(c)
302 int	c;
303 {
304 	keybuf[nkey++] = c;
305 }
306 #endif	/* KANJI */
307 
308 /*
309  * Return non-FALSE if typeahead is pending.
310  *
311  * If already got unread typeahead, do nothing.
312  * Otherwise, set keyboard to O_NDELAY if not already, and try
313  * a one character read.
314  */
typeahead()315 typeahead()
316 {
317 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
318 	if (nkey > 0) {
319 		return (TRUE);
320 	}
321 #endif	/* KANJI */
322 	if( !kbdqp )
323 	{
324 		if( !kbdpoll && fcntl( 0, F_SETFL, kbdflgs | O_NDELAY ) < 0 )
325 			abort();
326 		kbdpoll = TRUE;
327 		kbdqp = (1 == read( 0, &kbdq, 1 ));
328 	}
329 	return ( kbdqp );
330 }
331 
332 
333 /*
334  * panic:  print error and die, leaving core file.
335  * Don't know why this is needed (daveb).
336  */
panic(s)337 panic(s)
338 char *s;
339 {
340 	fprintf(stderr, "%s\r\n", s);
341 	abort();
342 }
343 
344 
345 /*
346 ** This should check the size of the window, and reset if needed.
347 */
348 
setttysize()349 setttysize()
350 {
351 #ifdef	TIOCGWINSZ
352 	if (ioctl(0, TIOCGWINSZ, (char *) &winsize) != -1) {
353 		nrow = winsize . ws_row;
354 		ncol = winsize . ws_col;
355 	} else
356 #ifdef	BUGFIX	/* 93.07.08  by S.Yoshida */
357 	nrow = 0;
358 	if (nrow <= 0 || ncol <= 0)
359 #endif	/* BUGFIX */
360 #endif
361 	if ((nrow=tgetnum ("li")) <= 0
362 	|| (ncol=tgetnum ("co")) <= 0) {
363 		nrow = 24;
364 		ncol = 80;
365 	}
366 	if (nrow > NROW)			/* Don't crash if the	*/
367 		nrow = NROW;			/* termcap entry is	*/
368 	if (ncol > NCOL)			/* too big.		*/
369 		ncol = NCOL;
370 }
371 
372 #ifdef	ADDFUNC		/* 93.07.08  by S.Yoshida */
373 #ifdef	SIGWINCH	/* 93.07.08  by S.Yoshida */
374 /*
375  * Signal handler when window size has changed.
376  */
377 VOID
ttwinch()378 ttwinch()
379 {
380 	refresh(FFRAND, 0);			/* Very easy way... */
381 #ifdef	CANNA
382 	canna_width();
383 #endif	/* CANNA */
384 	(void) signal(SIGWINCH, ttwinch);
385 }
386 #endif	/* ADDFUNC */
387 #endif	/* SIGWINCH */
388 
389 
390 #ifndef NO_DPROMPT
391 #include <signal.h>
392 #include <setjmp.h>
393 
394 static jmp_buf tohere;
395 
alrm()396 static VOID alrm()
397 {
398 	longjmp(tohere, -1);
399 }
400 
401 /*
402  * Return TRUE if we wait without doing anything, else return FALSE.
403  */
404 
ttwait()405 ttwait()
406 {
407 	VOID alrm();
408 
409 #ifdef	KANJI	/* 90.02.05  by S.Yoshida */
410 	if (nkey > 0) {
411 		return (FALSE);
412 	}
413 #endif	/* KANJI */
414 	if (kbdqp)
415 		return FALSE;		/* already pending input	*/
416 	if (setjmp(tohere))
417 		return TRUE;		/* timeout on read if here	*/
418 #ifdef	BUGFIX	/* 90.02.07  by S.Yoshida */
419 	signal(SIGALRM, alrm); alarm(1);
420 #else	/* NOT BUGFIX */
421 	signal(SIGALRM, alrm); alarm(2);
422 #endif	/* BUGFIX */
423 	if (kbdpoll && fcntl( 0, F_SETFL, kbdflgs ) < 0)
424 		abort();
425 	kbdpoll = FALSE;
426 	kbdqp = (1 == read(0, &kbdq, 1));
427 	alarm(0);
428 	return FALSE;			/* successful read if here	*/
429 }
430 #endif /* NO_DPROMPT */
431