1 /* $NetBSD: os.c,v 1.9 2016/01/26 16:04:12 christos Exp $ */
2
3 /*
4 * Copyright (c) 1988 Mark Nudelman
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)os.c 8.1 (Berkeley) 6/6/93";
37 #else
38 __RCSID("$NetBSD: os.c,v 1.9 2016/01/26 16:04:12 christos Exp $");
39 #endif
40 #endif /* not lint */
41
42 /*
43 * Operating system dependent routines.
44 *
45 * Most of the stuff in here is based on Unix, but an attempt
46 * has been made to make things work on other operating systems.
47 * This will sometimes result in a loss of functionality, unless
48 * someone rewrites code specifically for the new operating system.
49 *
50 * The makefile provides defines to decide whether various
51 * Unix features are present.
52 */
53
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 #include <sys/file.h>
57 #include <signal.h>
58 #include <setjmp.h>
59 #include <stdio.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <errno.h>
64
65 #include "less.h"
66 #include "extern.h"
67 #include "pathnames.h"
68
69 int reading;
70
71 static jmp_buf read_label;
72
73 /*
74 * Pass the specified command to a shell to be executed.
75 * Like plain "system()", but handles resetting terminal modes, etc.
76 */
77 void
lsystem(cmd)78 lsystem(cmd)
79 char *cmd;
80 {
81 int inp;
82 char cmdbuf[256];
83 char *shell;
84
85 /*
86 * Print the command which is to be executed,
87 * unless the command starts with a "-".
88 */
89 if (cmd[0] == '-')
90 cmd++;
91 else
92 {
93 lower_left();
94 clear_eol();
95 putstr("!");
96 putstr(cmd);
97 putstr("\n");
98 }
99
100 /*
101 * De-initialize the terminal and take out of raw mode.
102 */
103 deinit();
104 flush();
105 raw_mode(0);
106
107 /*
108 * Restore signals to their defaults.
109 */
110 init_signals(0);
111
112 /*
113 * Force standard input to be the terminal, "/dev/tty",
114 * even if less's standard input is coming from a pipe.
115 */
116 inp = dup(0);
117 (void)close(0);
118 if (open(_PATH_TTY, O_RDONLY, 0) < 0)
119 (void)dup(inp);
120
121 /*
122 * Pass the command to the system to be executed.
123 * If we have a SHELL environment variable, use
124 * <$SHELL -c "command"> instead of just <command>.
125 * If the command is empty, just invoke a shell.
126 */
127 if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
128 {
129 if (*cmd == '\0')
130 cmd = shell;
131 else
132 {
133 (void)snprintf(cmdbuf, sizeof(cmdbuf), "%s -c \"%s\"",
134 shell, cmd);
135 cmd = cmdbuf;
136 }
137 }
138 if (*cmd == '\0')
139 cmd = "sh";
140
141 (void)system(cmd);
142
143 /*
144 * Restore standard input, reset signals, raw mode, etc.
145 */
146 (void)close(0);
147 (void)dup(inp);
148 (void)close(inp);
149
150 init_signals(1);
151 raw_mode(1);
152 init();
153 screen_trashed = 1;
154 #if defined(SIGWINCH) || defined(SIGWIND)
155 /*
156 * Since we were ignoring window change signals while we executed
157 * the system command, we must assume the window changed.
158 */
159 winch(SIGWINCH);
160 #endif
161 }
162
163 /*
164 * Like read() system call, but is deliberately interruptable.
165 * A call to intread() from a signal handler will interrupt
166 * any pending iread().
167 */
168 int
iread(fd,buf,len)169 iread(fd, buf, len)
170 int fd;
171 char *buf;
172 int len;
173 {
174 int n;
175
176 if (setjmp(read_label))
177 /*
178 * We jumped here from intread.
179 */
180 return (READ_INTR);
181
182 flush();
183 reading = 1;
184 n = read(fd, buf, len);
185 reading = 0;
186 if (n < 0)
187 return (-1);
188 return (n);
189 }
190
191 void
intread()192 intread()
193 {
194 (void)sigsetmask(0L);
195 longjmp(read_label, 1);
196 }
197
198 /*
199 * Expand a filename, substituting any environment variables, etc.
200 * The implementation of this is necessarily very operating system
201 * dependent. This implementation is unabashedly only for Unix systems.
202 */
203 char *
glob(filename)204 glob(filename)
205 char *filename;
206 {
207 FILE *f;
208 char *p;
209 int ch;
210 char *cmd;
211 static char buffer[MAXPATHLEN];
212
213 if (filename[0] == '#')
214 return (filename);
215
216 /*
217 * We get the shell to expand the filename for us by passing
218 * an "echo" command to the shell and reading its output.
219 */
220 p = getenv("SHELL");
221 if (p == NULL || *p == '\0')
222 {
223 /*
224 * Read the output of <echo filename>.
225 */
226 asprintf(&cmd, "echo \"%s\"", filename);
227 if (cmd == NULL)
228 return (filename);
229 } else
230 {
231 /*
232 * Read the output of <$SHELL -c "echo filename">.
233 */
234 asprintf(&cmd, "%s -c \"echo %s\"", p, filename);
235 if (cmd == NULL)
236 return (filename);
237 }
238
239 if ((f = popen(cmd, "r")) == NULL) {
240 free(cmd);
241 return (filename);
242 }
243 free(cmd);
244
245 for (p = buffer; p < &buffer[sizeof(buffer)-1]; p++)
246 {
247 if ((ch = getc(f)) == '\n' || ch == EOF)
248 break;
249 *p = ch;
250 }
251 *p = '\0';
252 (void)pclose(f);
253 return(buffer);
254 }
255
256 char *
bad_file(filename,message,len)257 bad_file(filename, message, len)
258 char *filename, *message;
259 u_int len;
260 {
261 struct stat statbuf;
262
263 if (stat(filename, &statbuf) < 0) {
264 (void)snprintf(message, len, "%s: %s", filename,
265 strerror(errno));
266 return(message);
267 }
268 if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
269 static char is_dir[] = " is a directory";
270
271 strtcpy(message, filename, (int)(len-sizeof(is_dir)-1));
272 (void)strlcat(message, is_dir, len);
273 return(message);
274 }
275 return((char *)NULL);
276 }
277
278 /*
279 * Copy a string, truncating to the specified length if necessary.
280 * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
281 */
282 void
strtcpy(to,from,len)283 strtcpy(to, from, len)
284 char *to, *from;
285 int len;
286 {
287 (void)strncpy(to, from, (int)len);
288 to[len-1] = '\0';
289 }
290
291