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