-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.c
285 lines (246 loc) · 8.97 KB
/
utils.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/***************************************************************************\
|* *|
|* utils.c: A version of Tetris to run on ordinary terminals, *|
|* (ie., not needing a workstation, so should available *|
|* to peasant Newwords+ users. This module supplies all *|
|* the function that I will need, but which are not *|
|* intrinsically a part of Tetris itself. *|
|* *|
|* Author: Mike Taylor ([email protected]) *|
|* Started: Fri May 26 12:26:05 BST 1989 *|
|* *|
|* Modifications: *|
|* *|
|* 7th Feb 1991 [email protected] M001 *|
|* - Checked SYSV version for SCO Unix, flush_keyboard can be *|
|* simplified to an ioctl(), and checked usleep() worked. *|
|* It didn't - no clear solution yet. *|
|* 1st May 1991 [email protected] M002 *|
|* - Rewrote usleep() to use nap() *|
|* - Added sysv_select() which emulates BSD select using poll() *|
|* *|
\***************************************************************************/
#include <string.h>
#include <errno.h>
#ifdef SYSV
# undef SYSV /* SysV curses actually defines SYSV */
/* We'll use poll() instead of select */
# include <stropts.h>
# include <poll.h>
#endif
#include <curses.h>
#include <term.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h> /* M001 */
#include "tt.h"
#include "utils.h"
#include "screen.h"
/*-------------------------------------------------------------------------*/
extern char *getenv ();
/***************************************************************************\
|* *|
|* The function basename() acts like the UNIX(tm) command of the same *|
|* name. It takes as its argument a string, and returns the last path- *|
|* component of that string, (ie, the section after the last '/'), or *|
|* the whole string if it has no '/'. *|
|* *|
\***************************************************************************/
char *basename (name)
char *name;
{
char *slash;
if ((slash = strrchr (name, '/')) != 0)
return (slash+1);
else
return (name);
}
/***************************************************************************\
|* *|
|* This is my home-made varargs-based version of C++'s neat function of *|
|* the same name. It saves all that tedious mucking about with static *|
|* buffers which you sprintf into, then forget all about, by basically *|
|* being a sprintf with its own static storage. It takes as arguments *|
|* exactly the same things as printf(3), and returns a pointer to the *|
|* resultant string. *|
|* *|
\***************************************************************************/
/*VARARGS*/
char *form (char *fmt, ...)
{
va_list ap;
static char result[LINELEN];
va_start (ap, fmt);
(void) vsprintf (result, fmt, ap);
va_end (ap);
return (result);
}
/***************************************************************************\
|* *|
|* This is yer bog-standard "print a message and quit" function, except *|
|* that it checks to see if we are in curses(3x), and if so, takes us *|
|* out before doing its stuff. The arguments are an integer, the exit *|
|* status, and a string containing an error report, to which will be *|
|* prepended the program name when it is printed. If the status code *|
|* is LE_OK, (ie. nothing went wrong), no message is printed. *|
|* *|
\***************************************************************************/
void die (status, line)
int status;
char *line;
{
if (in_curses) {
myrefresh ();
#ifndef lint
nocrmode ();
echo ();
#endif /*lint*/
endwin ();
}
if (status != LE_OK)
(void) printf ("%s: %s\n", prog_name, line);
#ifndef LOCKF
(void) unlink (LOCK_FILE); /* Just in case :-) */
#endif /* LOCKF */
exit (status);
}
/***************************************************************************\
|* *|
|* The function get_termcap extracts the information we need from the *|
|* UNIX terminal capability database. It has been (correctly) pointed *|
|* out that most of what I do in this function is already done for me *|
|* by curses(3x), but (A) curses behaves stupidly on terminals with *|
|* termcap's "sg" non-zero, (ie. refuses to enter standout mode, so *|
|* you *have* to do it manually), and (B) I wanted to prove a point to *|
|* Sunny and others, who said it was impossible. Tgets() sucks :-P *|
|* *|
\***************************************************************************/
void get_termcap ()
{
static char bp[1024]; /* Seems odd to hardwire the 1024, but ... */
char *term_name; /* ... the manual entry told me to do it! */
int status; /* Success/failure of tgetent here */
char *tmp_ptr = tc_string; /* Pointer to be advanced by tgetstr() */
if ((term_name = getenv ("TERM")) == NULL)
die (LE_ENV, "couldn't get TERM from environment");
if ((status = tgetent (bp, term_name)) == -1)
die (LE_TERMCAP, "couldn't open TERMCAP file");
if (status == 0)
die (LE_TERMCAP, "couldn't find your terminal in TERMCAP file");
if ((screen_depth = tgetnum ("li")) == -1)
die (LE_TERMCAP, "couldn't get screen-depth from termcap");
if ((screen_width = tgetnum ("co")) == -1)
die (LE_TERMCAP, "couldn't get screen-width from termcap");
if ((so_gunk = tgetnum ("sg")) == -1)
so_gunk = 0; /* Default value */
so_str = tmp_ptr;
if (tgetstr ("so", &tmp_ptr) == NULL)
so_str = "";
se_str = tmp_ptr;
if (tgetstr ("se", &tmp_ptr) == NULL)
se_str = "";
}
/*-------------------------------------------------------------------------*/
/* BEGIN M002 */
/***************************************************************************\
|* *|
|* The function flush_keybord() checks to see whether there are any *|
|* keystrokes waiting to be read, and if so reads them and throws *|
|* them away. Surprising how complex this needs to be, but the only *|
|* alternative I can think of is to ioctl() stdin to be non-blocking, *|
|* read() until it returns 0, then ioctl() it back, which is scarcely *|
|* an improvement. *|
|* *|
|* Update: Thu May 16 15:22:41 PDT 1991 ([email protected]) *|
|* Rewritten as a one line ioctl() call - which is much more portable, *|
|* we hope. *|
|* *|
\***************************************************************************/
void flush_keyboard ()
{
#ifdef SYSV
/* M001 - We can do a luverly ioctl() for this */
(void) ioctl (fileno(stdin), TCFLSH, 0);
#else
/* We have to do it the hard way on SunOS 4.1.1 -- and all BSD's? */
fd_set fds; /* Will contain only stdin */
struct timeval tmout; /* Will contain a zero timeout (poll) */
static char buffer[LINELEN]; /* Will be used as the buffer to read(2) */
int status; /* Contains select(2)'s return value */
while (1) {
FD_ZERO (&fds);
FD_SET (0, &fds);
tmout.tv_sec = 0L;
tmout.tv_usec = 0L;
if (((status = select (1, &fds, (fd_set*) NULL, (fd_set*) NULL,
&tmout)) == -1) && (errno != EINTR))
die (LE_SELECT, "select(2) failed in flush_keybord()");
if ((status == 0) || (status == -1))
break; /* Can only be -1 if errno == EINTR */
(void) read (0, buffer, LINELEN);
}
#endif /*SYSV*/
}
/*-------------------------------------------------------------------------*/
#ifndef SYSV /* implies BSD... */
# ifdef NO_USLEEP_SYSCALL
/*
* Use this if your on BSD and don't have usleep() - mostly for
* non Sun's.
*/
void usleep (us)
unsigned us;
{
struct timeval tmout;
tmout.tv_sec = us / 1000000;
tmout.tv_usec = us % 1000000;
(void) select (0, (fd_set *)0, (fd_set *)0, (fd_set *)0, tmout);
}
# endif /* NO_USLEEP_SYSCALL */
#else /* SYSV */
/*
* If you don't have nap - you might try coding this using
* poll(). I'll put tentaive code in,
* although it didn't seem to work too well for me.
* Another solution would be to use curses napms() call,
* although I haven't bothered trying this one.
*/
void usleep (us)
unsigned us;
{
# ifndef NO_NAP_SYSCALL
(void) nap (us / 1000);
# else
(void) poll ((struct pollfd *)0, 0, us/1000);
# endif /* NO_NAP_SYSCALL */
}
/*
* Assumptions: Only ever select with nfds == 0, or rfd == 0, or
* rfd on file descriptor 0 (stdin).
* Timeout NULL implies blocking select.
*/
int sysv_select (nfds, rfd, wfd, xfd, tmout)
int nfds;
fd_set *rfd, *wfd, *xfd;
struct timeval *tmout;
{
struct pollfd pollrfd;
int ret, time;
unsigned long pollnfds = ((nfds == 0 || rfd == 0) ? 0L : 1L);
if (tmout)
time = (tmout->tv_sec * 1000) + (tmout->tv_usec / 1000);
else
time = -1;
pollrfd.fd = 0;
pollrfd.events = POLLIN;
pollrfd.revents = 0;
ret = poll (&pollrfd, pollnfds, time);
if (pollrfd.revents & POLLIN)
FD_SET (0,rfd);
return (ret);
}
#endif /* SYSV */
/* END M002 */
/*--------------------------------------------------------------------------*/