xref: /netbsd-src/distrib/utils/more/os.c (revision d066f94a02df74c74bcfd4c685488c1b96af7edc)
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