1 /*
2 * Copyright (C) 1984-2017 Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11 /*
12 * Operating system dependent routines.
13 *
14 * Most of the stuff in here is based on Unix, but an attempt
15 * has been made to make things work on other operating systems.
16 * This will sometimes result in a loss of functionality, unless
17 * someone rewrites code specifically for the new operating system.
18 *
19 * The makefile provides defines to decide whether various
20 * Unix features are present.
21 */
22
23 #include "less.h"
24 #include <signal.h>
25 #include <setjmp.h>
26 #if HAVE_TIME_H
27 #include <time.h>
28 #endif
29 #if HAVE_ERRNO_H
30 #include <errno.h>
31 #endif
32 #if HAVE_VALUES_H
33 #include <values.h>
34 #endif
35
36 /*
37 * BSD setjmp() saves (and longjmp() restores) the signal mask.
38 * This costs a system call or two per setjmp(), so if possible we clear the
39 * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
40 * On other systems, setjmp() doesn't affect the signal mask and so
41 * _setjmp() does not exist; we just use setjmp().
42 */
43 #if HAVE__SETJMP && HAVE_SIGSETMASK
44 #define SET_JUMP _setjmp
45 #define LONG_JUMP _longjmp
46 #else
47 #define SET_JUMP setjmp
48 #define LONG_JUMP longjmp
49 #endif
50
51 public int reading;
52
53 static jmp_buf read_label;
54
55 extern int sigs;
56
57 /*
58 * Like read() system call, but is deliberately interruptible.
59 * A call to intread() from a signal handler will interrupt
60 * any pending iread().
61 */
62 public int
iread(fd,buf,len)63 iread(fd, buf, len)
64 int fd;
65 unsigned char *buf;
66 unsigned int len;
67 {
68 int n;
69
70 start:
71 #if MSDOS_COMPILER==WIN32C
72 if (ABORT_SIGS())
73 return (READ_INTR);
74 #else
75 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
76 if (kbhit())
77 {
78 int c;
79
80 c = getch();
81 if (c == '\003')
82 return (READ_INTR);
83 ungetch(c);
84 }
85 #endif
86 #endif
87 if (SET_JUMP(read_label))
88 {
89 /*
90 * We jumped here from intread.
91 */
92 reading = 0;
93 #if HAVE_SIGPROCMASK
94 {
95 sigset_t mask;
96 sigemptyset(&mask);
97 sigprocmask(SIG_SETMASK, &mask, NULL);
98 }
99 #else
100 #if HAVE_SIGSETMASK
101 sigsetmask(0);
102 #else
103 #ifdef _OSK
104 sigmask(~0);
105 #endif
106 #endif
107 #endif
108 return (READ_INTR);
109 }
110
111 flush();
112 reading = 1;
113 #if MSDOS_COMPILER==DJGPPC
114 if (isatty(fd))
115 {
116 /*
117 * Don't try reading from a TTY until a character is
118 * available, because that makes some background programs
119 * believe DOS is busy in a way that prevents those
120 * programs from working while "less" waits.
121 */
122 fd_set readfds;
123
124 FD_ZERO(&readfds);
125 FD_SET(fd, &readfds);
126 if (select(fd+1, &readfds, 0, 0, 0) == -1)
127 return (-1);
128 }
129 #endif
130 n = read(fd, buf, len);
131 #if 1
132 /*
133 * This is a kludge to workaround a problem on some systems
134 * where terminating a remote tty connection causes read() to
135 * start returning 0 forever, instead of -1.
136 */
137 {
138 extern int ignore_eoi;
139 if (!ignore_eoi)
140 {
141 static int consecutive_nulls = 0;
142 if (n == 0)
143 consecutive_nulls++;
144 else
145 consecutive_nulls = 0;
146 if (consecutive_nulls > 20)
147 quit(QUIT_ERROR);
148 }
149 }
150 #endif
151 reading = 0;
152 if (n < 0)
153 {
154 #if HAVE_ERRNO
155 /*
156 * Certain values of errno indicate we should just retry the read.
157 */
158 #if MUST_DEFINE_ERRNO
159 extern int errno;
160 #endif
161 #ifdef EINTR
162 if (errno == EINTR)
163 goto start;
164 #endif
165 #ifdef EAGAIN
166 if (errno == EAGAIN)
167 goto start;
168 #endif
169 #endif
170 return (-1);
171 }
172 return (n);
173 }
174
175 /*
176 * Interrupt a pending iread().
177 */
178 public void
intread()179 intread()
180 {
181 LONG_JUMP(read_label, 1);
182 }
183
184 /*
185 * Return the current time.
186 */
187 #if HAVE_TIME
188 public time_type
get_time()189 get_time()
190 {
191 time_type t;
192
193 time(&t);
194 return (t);
195 }
196 #endif
197
198
199 #if !HAVE_STRERROR
200 /*
201 * Local version of strerror, if not available from the system.
202 */
203 static char *
strerror(err)204 strerror(err)
205 int err;
206 {
207 #if HAVE_SYS_ERRLIST
208 static char buf[16];
209 extern char *sys_errlist[];
210 extern int sys_nerr;
211
212 if (err < sys_nerr)
213 return sys_errlist[err];
214 sprintf(buf, "Error %d", err);
215 return buf;
216 #else
217 return ("cannot open");
218 #endif
219 }
220 #endif
221
222 /*
223 * errno_message: Return an error message based on the value of "errno".
224 */
225 public char *
errno_message(filename)226 errno_message(filename)
227 char *filename;
228 {
229 char *p;
230 char *m;
231 int len;
232 #if HAVE_ERRNO
233 #if MUST_DEFINE_ERRNO
234 extern int errno;
235 #endif
236 p = strerror(errno);
237 #else
238 p = "cannot open";
239 #endif
240 len = (int) (strlen(filename) + strlen(p) + 3);
241 m = (char *) ecalloc(len, sizeof(char));
242 SNPRINTF2(m, len, "%s: %s", filename, p);
243 return (m);
244 }
245
246 /* #define HAVE_FLOAT 0 */
247
248 static POSITION
muldiv(val,num,den)249 muldiv(val, num, den)
250 POSITION val, num, den;
251 {
252 #if HAVE_FLOAT
253 double v = (((double) val) * num) / den;
254 return ((POSITION) (v + 0.5));
255 #else
256 POSITION v = ((POSITION) val) * num;
257
258 if (v / num == val)
259 /* No overflow */
260 return (POSITION) (v / den);
261 else
262 /* Above calculation overflows;
263 * use a method that is less precise but won't overflow. */
264 return (POSITION) (val / (den / num));
265 #endif
266 }
267
268 /*
269 * Return the ratio of two POSITIONS, as a percentage.
270 * {{ Assumes a POSITION is a long int. }}
271 */
272 public int
percentage(num,den)273 percentage(num, den)
274 POSITION num;
275 POSITION den;
276 {
277 return (int) muldiv(num, (POSITION) 100, den);
278 }
279
280 /*
281 * Return the specified percentage of a POSITION.
282 */
283 public POSITION
percent_pos(pos,percent,fraction)284 percent_pos(pos, percent, fraction)
285 POSITION pos;
286 int percent;
287 long fraction;
288 {
289 /* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */
290 POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100);
291
292 if (perden == 0)
293 return (0);
294 return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM);
295 }
296
297 #if !HAVE_STRCHR
298 /*
299 * strchr is used by regexp.c.
300 */
301 char *
strchr(s,c)302 strchr(s, c)
303 char *s;
304 int c;
305 {
306 for ( ; *s != '\0'; s++)
307 if (*s == c)
308 return (s);
309 if (c == '\0')
310 return (s);
311 return (NULL);
312 }
313 #endif
314
315 #if !HAVE_MEMCPY
316 VOID_POINTER
memcpy(dst,src,len)317 memcpy(dst, src, len)
318 VOID_POINTER dst;
319 VOID_POINTER src;
320 int len;
321 {
322 char *dstp = (char *) dst;
323 char *srcp = (char *) src;
324 int i;
325
326 for (i = 0; i < len; i++)
327 dstp[i] = srcp[i];
328 return (dst);
329 }
330 #endif
331
332 #ifdef _OSK_MWC32
333
334 /*
335 * This implements an ANSI-style intercept setup for Microware C 3.2
336 */
337 public int
os9_signal(type,handler)338 os9_signal(type, handler)
339 int type;
340 RETSIGTYPE (*handler)();
341 {
342 intercept(handler);
343 }
344
345 #include <sgstat.h>
346
347 int
isatty(f)348 isatty(f)
349 int f;
350 {
351 struct sgbuf sgbuf;
352
353 if (_gs_opt(f, &sgbuf) < 0)
354 return -1;
355 return (sgbuf.sg_class == 0);
356 }
357
358 #endif
359