Merge branch 'vendor/BZIP'
[games.git] / games / hack / hack.tty.c
1 /*-
2  * Copyright (c) 1988, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)hack.tty.c       8.1 (Berkeley) 5/31/93
30  * $FreeBSD: src/games/hack/hack.tty.c,v 1.6.2.1 2000/07/20 10:35:07 kris Exp $
31  * $DragonFly: src/games/hack/hack.tty.c,v 1.4 2006/08/21 19:45:32 pavalos Exp $
32  */
33
34 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
35 /* hack.tty.c - version 1.0.3 */
36 /*
37  * With thanks to the people who sent code for SYSV - hpscdi!jon,
38  * arnold@ucsf-cgl, wcs@bo95b, cbcephus!pds and others.
39  */
40
41 #include <termios.h>
42 #include "hack.h"
43
44 /*
45  * Some systems may have getchar() return EOF for various reasons, and
46  * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs.
47  * FIXME: is this still valid nowadays?
48  */
49 #define NR_OF_EOFS      20
50
51 static char erase_char, kill_char;
52 static boolean settty_needed = FALSE;
53 struct termios inittyb, curttyb;
54
55 static void setctty(void);
56
57 /*
58  * Get initial state of terminal, set ospeed (for termcap routines)
59  * and switch off tab expansion if necessary.
60  * Called by startup() in termcap.c and after returning from ! or ^Z
61  */
62 void
63 gettty(void)
64 {
65         if (tcgetattr(fileno(stdin), &inittyb) < 0)
66                 perror("Hack (gettty)");
67         curttyb = inittyb;
68         erase_char = inittyb.c_cc[VERASE];
69         kill_char = inittyb.c_cc[VKILL];
70         getioctls();
71
72         /* do not expand tabs - they might be needed inside a cm sequence */
73         if (curttyb.c_oflag & OXTABS) {
74                 curttyb.c_oflag &= ~OXTABS;
75                 setctty();
76         }
77         settty_needed = TRUE;
78 }
79
80 /* reset terminal to original state */
81 void
82 settty(const char *s)
83 {
84         clear_screen();
85         end_screen();
86         if (s)
87                 printf("%s", s);
88         fflush(stdout);
89         if (tcsetattr(fileno(stdin), TCSANOW, &inittyb) < 0)
90                 perror("Hack (settty)");
91         flags.echo = (inittyb.c_lflag & ECHO) ? ON : OFF;
92         flags.cbreak = (inittyb.c_lflag & ICANON) ? OFF : ON;
93         setioctls();
94 }
95
96 static void
97 setctty(void)
98 {
99         if (tcsetattr(fileno(stdin), TCSANOW, &curttyb) < 0)
100                 perror("Hack (setctty)");
101 }
102
103 void
104 setftty(void)
105 {
106         u_long ef = 0;          /* desired value of flags & ECHO */
107         u_long cf = !(ICANON);  /* desired value of flags & CBREAK */
108         int change = 0;
109
110         flags.cbreak = ON;
111         flags.echo = OFF;
112         /* Should use (ECHO|CRMOD) here instead of ECHO */
113         if ((curttyb.c_lflag & ECHO) != ef) {
114                 curttyb.c_lflag &= ~ECHO;
115                 change++;
116         }
117         if ((curttyb.c_lflag & ICANON) != cf) {
118                 curttyb.c_lflag &= ~ICANON;
119                 curttyb.c_lflag |= cf;
120                 /* be satisfied with one character; no timeout */
121                 curttyb.c_cc[VMIN] = 1;         /* was VEOF */
122                 curttyb.c_cc[VTIME] = 0;        /* was VEOL */
123                 change++;
124         }
125         if (change)
126                 setctty();
127         start_screen();
128 }
129
130 /* fatal error */
131 /* VARARGS1 */
132 void
133 error(const char *s, ...)
134 {
135         va_list ap;
136
137         if (settty_needed)
138                 settty(NULL);
139         va_start(ap, s);
140         vprintf(s, ap);
141         va_end(ap);
142         putchar('\n');
143         exit(1);
144 }
145
146 /*
147  * Read a line closed with '\n' into the array char bufp[BUFSZ].
148  * (The '\n' is not stored. The string is closed with a '\0'.)
149  * Reading can be interrupted by an escape ('\033') - now the
150  * resulting string is "\033".
151  */
152 void
153 getlin(char *bufp)
154 {
155         char *obufp = bufp;
156         int c;
157
158         flags.toplin = 2;       /* nonempty, no --More-- required */
159         for (;;) {
160                 fflush(stdout);
161                 if ((c = getchar()) == EOF) {
162                         *bufp = 0;
163                         return;
164                 }
165                 if (c == '\033') {
166                         *obufp = c;
167                         obufp[1] = 0;
168                         return;
169                 }
170                 if (c == erase_char || c == '\b') {
171                         if (bufp != obufp) {
172                                 bufp--;
173                                 putstr("\b \b");        /* putsym converts \b */
174                         } else
175                                 bell();
176                 } else if (c == '\n') {
177                         *bufp = 0;
178                         return;
179                 } else if (' ' <= c && c < '\177') {
180                         /*
181                          * avoid isprint() - some people don't have it ' ' is
182                          * not always a printing char
183                          */
184                         *bufp = c;
185                         bufp[1] = 0;
186                         putstr(bufp);
187                         if (bufp - obufp < BUFSZ - 1 && bufp - obufp < COLNO)
188                                 bufp++;
189                 } else if (c == kill_char || c == '\177') {     /* Robert Viduya */
190                         /* this test last - @ might be the kill_char */
191                         while (bufp != obufp) {
192                                 bufp--;
193                                 putstr("\b \b");
194                         }
195                 } else
196                         bell();
197         }
198 }
199
200 void
201 getret(void)
202 {
203         cgetret("");
204 }
205
206 void
207 cgetret(const char *s)
208 {
209         putsym('\n');
210         if (flags.standout)
211                 standoutbeg();
212         putstr("Hit ");
213         putstr(flags.cbreak ? "space" : "return");
214         putstr(" to continue: ");
215         if (flags.standout)
216                 standoutend();
217         xwaitforspace(s);
218 }
219
220 char morc;              /* tell the outside world what char he used */
221
222 void
223 xwaitforspace(const char *s)    /* chars allowed besides space or return */
224 {
225         int c;
226
227         morc = 0;
228         while ((c = readchar()) != '\n') {
229                 if (flags.cbreak) {
230                         if (c == ' ')
231                                 break;
232                         if (s && strchr(s, c)) {
233                                 morc = c;
234                                 break;
235                         }
236                         bell();
237                 }
238         }
239 }
240
241 char *
242 parse(void)
243 {
244         static char inputline[COLNO];
245         int foo;
246
247         flags.move = 1;
248         if (!Invisible)
249                 curs_on_u();
250         else
251                 home();
252         while ((foo = readchar()) >= '0' && foo <= '9')
253                 multi = 10 * multi + foo - '0';
254         if (multi) {
255                 multi--;
256                 save_cm = inputline;
257         }
258         inputline[0] = foo;
259         inputline[1] = 0;
260         if (foo == 'f' || foo == 'F') {
261                 inputline[1] = getchar();
262 #ifdef QUEST
263                 if (inputline[1] == foo)
264                         inputline[2] = getchar();
265                 else
266 #endif /* QUEST */
267                         inputline[2] = 0;
268         }
269         if (foo == 'm' || foo == 'M') {
270                 inputline[1] = getchar();
271                 inputline[2] = 0;
272         }
273         clrlin();
274         return (inputline);
275 }
276
277 char
278 readchar(void)
279 {
280         int sym;
281
282         fflush(stdout);
283         if ((sym = getchar()) == EOF)
284 #ifdef NR_OF_EOFS
285         { /*
286            * Some SYSV systems seem to return EOFs for various reasons
287            * (?like when one hits break or for interrupted systemcalls?),
288            * and we must see several before we quit.
289            */
290                 int cnt = NR_OF_EOFS;
291                 while (cnt--) {
292                         clearerr(stdin); /* omit if clearerr is undefined */
293                         if ((sym = getchar()) != EOF)
294                                 goto noteof;
295                 }
296                 end_of_input();
297 noteof:;
298         }
299 #else
300                 end_of_input();
301 #endif /* NR_OF_EOFS */
302         if (flags.toplin == 1)
303                 flags.toplin = 2;
304         return ((char)sym);
305 }
306
307 void
308 end_of_input(void)
309 {
310         settty("End of input?\n");
311         clearlocks();
312         exit(0);
313 }