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