xref: /openbsd/usr.bin/less/main.c (revision 998de4a5)
1 /*
2  * Copyright (C) 1984-2012  Mark Nudelman
3  * Modified for use with illumos by Garrett D'Amore.
4  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information, see the README file.
10  */
11 
12 /*
13  * Entry point, initialization, miscellaneous routines.
14  */
15 
16 #include <sys/types.h>
17 
18 #include <libgen.h>
19 #include <stdarg.h>
20 
21 #include "less.h"
22 
23 char	*every_first_cmd = NULL;
24 int	new_file;
25 int	is_tty;
26 IFILE	curr_ifile = NULL;
27 IFILE	old_ifile = NULL;
28 struct scrpos initial_scrpos;
29 int	any_display = FALSE;
30 off_t	start_attnpos = -1;
31 off_t	end_attnpos = -1;
32 int	wscroll;
33 
34 static char	*progname;
35 
36 int	quitting;
37 int	secure;
38 int	dohelp;
39 
40 int logfile = -1;
41 int force_logfile = FALSE;
42 char *namelogfile = NULL;
43 char *editor;
44 char *editproto;
45 
46 extern char	*tags;
47 extern char	*tagoption;
48 extern int	jump_sline;
49 extern int	less_is_more;
50 extern int	missing_cap;
51 extern int	know_dumb;
52 extern int	quit_if_one_screen;
53 extern int	quit_at_eof;
54 extern int	pr_type;
55 extern int	hilite_search;
56 extern int	use_lessopen;
57 extern int	no_init;
58 extern int	top_scroll;
59 extern int	errmsgs;
60 
61 
62 /*
63  * Entry point.
64  */
65 int
66 main(int argc, char *argv[])
67 {
68 	IFILE ifile;
69 	char *s;
70 
71 	progname = basename(argv[0]);
72 	argv++;
73 	argc--;
74 
75 	/*
76 	 * If the name of the executable program is "more",
77 	 * act like LESS_IS_MORE is set.  We have to set this as early
78 	 * as possible for POSIX.
79 	 */
80 	if (strcmp(progname, "more") == 0)
81 		less_is_more = 1;
82 	else {
83 		s = lgetenv("LESS_IS_MORE");
84 		if (s != NULL && *s != '\0')
85 			less_is_more = 1;
86 	}
87 
88 	secure = 0;
89 	s = lgetenv("LESSSECURE");
90 	if (s != NULL && *s != '\0')
91 		secure = 1;
92 
93 	if (secure) {
94 		if (pledge("stdio rpath wpath tty", NULL) == -1) {
95 			perror("pledge");
96 			exit(1);
97 		}
98 	} else {
99 		if (pledge("stdio rpath wpath cpath fattr proc exec tty", NULL) == -1) {
100 			perror("pledge");
101 			exit(1);
102 		}
103 	}
104 
105 	/*
106 	 * Process command line arguments and LESS environment arguments.
107 	 * Command line arguments override environment arguments.
108 	 */
109 	is_tty = isatty(1);
110 	get_term();
111 	init_cmds();
112 	init_charset();
113 	init_line();
114 	init_cmdhist();
115 	init_option();
116 	init_search();
117 
118 
119 	init_prompt();
120 
121 	if (less_is_more) {
122 		/* this is specified by XPG */
123 		quit_at_eof = OPT_ON;
124 
125 		/* more users don't like the warning */
126 		know_dumb = OPT_ON;
127 
128 		/* default prompt is medium */
129 		pr_type = OPT_ON;
130 
131 		/* do not hilight search terms */
132 		hilite_search = OPT_OFF;
133 
134 		/* do not use LESSOPEN */
135 		use_lessopen = OPT_OFF;
136 
137 		/* do not set init strings to terminal */
138 		no_init = OPT_ON;
139 
140 		/* repaint from top of screen */
141 		top_scroll = OPT_OFF;
142 	}
143 
144 	s = lgetenv(less_is_more ? "MORE" : "LESS");
145 	if (s != NULL)
146 		scan_option(estrdup(s));
147 
148 #define	isoptstring(s)	(((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
149 	while (argc > 0 && (isoptstring(*argv) || isoptpending())) {
150 		s = *argv++;
151 		argc--;
152 		if (strcmp(s, "--") == 0)
153 			break;
154 		scan_option(s);
155 	}
156 #undef isoptstring
157 
158 	if (isoptpending()) {
159 		/*
160 		 * Last command line option was a flag requiring a
161 		 * following string, but there was no following string.
162 		 */
163 		nopendopt();
164 		quit(QUIT_OK);
165 	}
166 
167 	if (errmsgs) {
168 		quit(QUIT_ERROR);
169 	}
170 	if (less_is_more && quit_at_eof == OPT_ONPLUS) {
171 		extern int no_init;
172 		no_init = OPT_ON;
173 	}
174 	if (less_is_more && pr_type == OPT_ONPLUS) {
175 		extern int quiet;
176 		quiet = VERY_QUIET;
177 	}
178 
179 	editor = lgetenv("VISUAL");
180 	if (editor == NULL || *editor == '\0') {
181 		editor = lgetenv("EDITOR");
182 		if (editor == NULL || *editor == '\0')
183 			editor = EDIT_PGM;
184 	}
185 	editproto = lgetenv("LESSEDIT");
186 	if (editproto == NULL || *editproto == '\0')
187 		editproto = "%E ?lm+%lm. %f";
188 
189 	/*
190 	 * Call get_ifile with all the command line filenames
191 	 * to "register" them with the ifile system.
192 	 */
193 	ifile = NULL;
194 	if (dohelp)
195 		ifile = get_ifile(helpfile(), ifile);
196 	while (argc-- > 0) {
197 		char *filename;
198 		filename = shell_quote(*argv);
199 		if (filename == NULL)
200 			filename = *argv;
201 		argv++;
202 		(void) get_ifile(filename, ifile);
203 		ifile = prev_ifile(NULL);
204 		free(filename);
205 	}
206 	/*
207 	 * Set up terminal, etc.
208 	 */
209 	if (!is_tty) {
210 		/*
211 		 * Output is not a tty.
212 		 * Just copy the input file(s) to output.
213 		 */
214 		if (nifile() == 0) {
215 			if (edit_stdin() == 0)
216 				cat_file();
217 		} else if (edit_first() == 0) {
218 			do {
219 				cat_file();
220 			} while (edit_next(1) == 0);
221 		}
222 		quit(QUIT_OK);
223 	}
224 
225 	if (missing_cap && !know_dumb)
226 		error("WARNING: terminal is not fully functional", NULL);
227 	init_mark();
228 	open_getchr();
229 
230 	if (secure)
231 		if (pledge("stdio rpath tty", NULL) == -1) {
232 			perror("pledge");
233 			exit(1);
234 		}
235 
236 	raw_mode(1);
237 	init_signals(1);
238 
239 	/*
240 	 * Select the first file to examine.
241 	 */
242 	if (tagoption != NULL || strcmp(tags, "-") == 0) {
243 		/*
244 		 * A -t option was given.
245 		 * Verify that no filenames were also given.
246 		 * Edit the file selected by the "tags" search,
247 		 * and search for the proper line in the file.
248 		 */
249 		if (nifile() > 0) {
250 			error("No filenames allowed with -t option", NULL);
251 			quit(QUIT_ERROR);
252 		}
253 		findtag(tagoption);
254 		if (edit_tagfile())  /* Edit file which contains the tag */
255 			quit(QUIT_ERROR);
256 		/*
257 		 * Search for the line which contains the tag.
258 		 * Set up initial_scrpos so we display that line.
259 		 */
260 		initial_scrpos.pos = tagsearch();
261 		if (initial_scrpos.pos == -1)
262 			quit(QUIT_ERROR);
263 		initial_scrpos.ln = jump_sline;
264 	} else if (nifile() == 0) {
265 		if (edit_stdin())  /* Edit standard input */
266 			quit(QUIT_ERROR);
267 	} else {
268 		if (edit_first())  /* Edit first valid file in cmd line */
269 			quit(QUIT_ERROR);
270 	}
271 
272 	init();
273 	commands();
274 	quit(QUIT_OK);
275 	return (0);
276 }
277 
278 /*
279  * Allocate memory.
280  * Like calloc(), but never returns an error (NULL).
281  */
282 void *
283 ecalloc(int count, unsigned int size)
284 {
285 	void *p;
286 
287 	p = calloc(count, size);
288 	if (p != NULL)
289 		return (p);
290 	error("Cannot allocate memory", NULL);
291 	quit(QUIT_ERROR);
292 	return (NULL);
293 }
294 
295 char *
296 easprintf(const char *fmt, ...)
297 {
298 	char *p = NULL;
299 	int rv;
300 	va_list ap;
301 
302 	va_start(ap, fmt);
303 	rv = vasprintf(&p, fmt, ap);
304 	va_end(ap);
305 
306 	if (p == NULL || rv < 0) {
307 		error("Cannot allocate memory", NULL);
308 		quit(QUIT_ERROR);
309 	}
310 	return (p);
311 }
312 
313 char *
314 estrdup(const char *str)
315 {
316 	char *n;
317 
318 	n = strdup(str);
319 	if (n == NULL) {
320 		error("Cannot allocate memory", NULL);
321 		quit(QUIT_ERROR);
322 	}
323 	return (n);
324 }
325 
326 /*
327  * Skip leading spaces in a string.
328  */
329 char *
330 skipsp(char *s)
331 {
332 	while (*s == ' ' || *s == '\t')
333 		s++;
334 	return (s);
335 }
336 
337 /*
338  * See how many characters of two strings are identical.
339  * If uppercase is true, the first string must begin with an uppercase
340  * character; the remainder of the first string may be either case.
341  */
342 int
343 sprefix(char *ps, char *s, int uppercase)
344 {
345 	int c;
346 	int sc;
347 	int len = 0;
348 
349 	for (; *s != '\0';  s++, ps++) {
350 		c = *ps;
351 		if (uppercase) {
352 			if (len == 0 && islower(c))
353 				return (-1);
354 			c = tolower(c);
355 		}
356 		sc = *s;
357 		if (len > 0)
358 			sc = tolower(sc);
359 		if (c != sc)
360 			break;
361 		len++;
362 	}
363 	return (len);
364 }
365 
366 /*
367  * Exit the program.
368  */
369 void
370 quit(int status)
371 {
372 	static int save_status;
373 
374 	/*
375 	 * Put cursor at bottom left corner, clear the line,
376 	 * reset the terminal modes, and exit.
377 	 */
378 	if (status < 0)
379 		status = save_status;
380 	else
381 		save_status = status;
382 	quitting = 1;
383 	edit(NULL);
384 	if (!secure)
385 		save_cmdhist();
386 	if (any_display && is_tty)
387 		clear_bot();
388 	deinit();
389 	flush(1);
390 	raw_mode(0);
391 	exit(status);
392 }
393 
394 char *
395 helpfile(void)
396 {
397 	return (less_is_more ? HELPDIR "/more.help" : HELPDIR "/less.help");
398 }
399