xref: /openbsd-src/usr.bin/less/main.c (revision 62a742911104f98b9185b2c6b6007d9b1c36396c)
1 /*
2  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice in the documentation and/or other materials provided with
12  *    the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 
28 /*
29  * Entry point, initialization, miscellaneous routines.
30  */
31 
32 #include "less.h"
33 #include "position.h"
34 
35 public char *	every_first_cmd = NULL;
36 public int	new_file;
37 public int	is_tty;
38 public IFILE	curr_ifile = NULL_IFILE;
39 public IFILE	old_ifile = NULL_IFILE;
40 public struct scrpos initial_scrpos;
41 public int	any_display = FALSE;
42 public int	wscroll;
43 public char *	progname;
44 public int	quitting;
45 public int	more_mode = 0;
46 
47 extern int	quit_at_eof;
48 extern int	cbufs;
49 extern int	errmsgs;
50 extern int	screen_trashed;
51 extern int	force_open;
52 
53 #if LOGFILE
54 public int	logfile = -1;
55 public int	force_logfile = FALSE;
56 public char *	namelogfile = NULL;
57 #endif
58 
59 #if EDITOR
60 public char *	editor;
61 public char *	editproto;
62 #endif
63 
64 #if TAGS
65 extern char *	tagfile;
66 extern char *	tagoption;
67 extern int	jump_sline;
68 #endif
69 
70 
71 
72 /*
73  * Entry point.
74  */
75 int
76 main(argc, argv)
77 	int argc;
78 	char *argv[];
79 {
80 	IFILE ifile;
81 	extern char *__progname;
82 
83 #ifdef __EMX__
84 	_response(&argc, &argv);
85 	_wildcard(&argc, &argv);
86 #endif
87 
88 	progname = *argv++;
89 
90 	/*
91 	 * Process command line arguments and LESS environment arguments.
92 	 * Command line arguments override environment arguments.
93 	 */
94 	if (strcmp(__progname, "more") == 0)
95 		more_mode = 1;
96 
97 	get_term();
98 	init_cmds();
99 	init_prompt();
100 	init_charset();
101 	init_option();
102 
103 	if (more_mode) {
104 		scan_option("-E");
105 		scan_option("-m");
106 		scan_option("-G");
107 		scan_option(getenv("MORE"));
108 	} else
109 		scan_option(getenv("LESS"));
110 
111 #if GNU_OPTIONS
112 	/*
113 	 * Special case for "less --help" and "less --version".
114 	 */
115 	if (argc == 2)
116 	{
117 		if (strcmp(argv[0], "--help") == 0)
118 			scan_option("-?");
119 		if (strcmp(argv[0], "--version") == 0)
120 			scan_option("-V");
121 	}
122 #endif
123 #define	isoptstring(s)	(((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
124 	while (--argc > 0 && (isoptstring(argv[0]) || isoptpending())) {
125 		if (strcmp(argv[0], "--") == 0) {
126 			argv++;
127 			argc--;
128 			break;
129 		}
130 		scan_option(*argv++);
131 	}
132 #undef isoptstring
133 
134 	if (isoptpending())
135 	{
136 		/*
137 		 * Last command line option was a flag requiring a
138 		 * following string, but there was no following string.
139 		 */
140 		nopendopt();
141 		quit(QUIT_OK);
142 	}
143 
144 #if EDITOR
145 	editor = getenv("VISUAL");
146 	if (editor == NULL || *editor == '\0')
147 	{
148 		editor = getenv("EDITOR");
149 		if (editor == NULL || *editor == '\0')
150 			editor = EDIT_PGM;
151 	}
152 	editproto = getenv("LESSEDIT");
153 	if (editproto == NULL || *editproto == '\0')
154 		editproto = "%E ?lm+%lm. %f";
155 #endif
156 
157 	/*
158 	 * Call get_ifile with all the command line filenames
159 	 * to "register" them with the ifile system.
160 	 */
161 	ifile = NULL_IFILE;
162 	while (--argc >= 0)
163 	{
164 #if MSOFTC || OS2
165 		/*
166 		 * Because the "shell" doesn't expand filename patterns,
167 		 * treat each argument as a filename pattern rather than
168 		 * a single filename.
169 		 * Expand the pattern and iterate over the expanded list.
170 		 */
171 		struct textlist tlist;
172 		char *gfilename;
173 		char *filename;
174 
175 		gfilename = glob(*argv++);
176 		init_textlist(&tlist, gfilename);
177 		filename = NULL;
178 		while ((filename = forw_textlist(&tlist, filename)) != NULL)
179 			ifile = get_ifile(filename, ifile);
180 		free(gfilename);
181 #else
182 		ifile = get_ifile(*argv++, ifile);
183 #endif
184 	}
185 	/*
186 	 * Set up terminal, etc.
187 	 */
188 	is_tty = isatty(1);
189 	if (!is_tty)
190 	{
191 		/*
192 		 * Output is not a tty.
193 		 * Just copy the input file(s) to output.
194 		 */
195 		if (nifile() == 0)
196 		{
197 			if (edit_stdin() == 0)
198 				cat_file();
199 		} else if (edit_first() == 0)
200 		{
201 			do {
202 				cat_file();
203 			} while (edit_next(1) == 0);
204 		}
205 		quit(QUIT_OK);
206 	}
207 
208 	init_mark();
209 	raw_mode(1);
210 	open_getchr();
211 	init_signals(1);
212 
213 	/*
214 	 * Select the first file to examine.
215 	 */
216 #if TAGS
217 	if (tagoption != NULL)
218 	{
219 		/*
220 		 * A -t option was given.
221 		 * Verify that no filenames were also given.
222 		 * Edit the file selected by the "tags" search,
223 		 * and search for the proper line in the file.
224 		 */
225 		if (nifile() > 0)
226 		{
227 			error("No filenames allowed with -t option", NULL_PARG);
228 			quit(QUIT_ERROR);
229 		}
230 		findtag(tagoption);
231 		if (tagfile == NULL)
232 			quit(QUIT_ERROR);
233 		if (edit(tagfile))  /* Edit file which contains the tag */
234 			quit(QUIT_ERROR);
235 		/*
236 		 * Search for the line which contains the tag.
237 		 * Set up initial_scrpos so we display that line.
238 		 */
239 		initial_scrpos.pos = tagsearch();
240 		if (initial_scrpos.pos == NULL_POSITION)
241 			quit(QUIT_ERROR);
242 		initial_scrpos.ln = jump_sline;
243 	} else
244 #endif
245 	if (nifile() == 0)
246 	{
247 		if (edit_stdin())  /* Edit standard input */
248 			quit(QUIT_ERROR);
249 	} else
250 	{
251 		if (edit_first())  /* Edit first valid file in cmd line */
252 			quit(QUIT_ERROR);
253 	}
254 
255 	init();
256 	commands();
257 	quit(QUIT_OK);
258 	/*NOTREACHED*/
259 }
260 
261 /*
262  * Copy a string, truncating to the specified length if necessary.
263  * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
264  */
265 	public void
266 strtcpy(to, from, len)
267 	char *to;
268 	char *from;
269 	unsigned int len;
270 {
271 	strncpy(to, from, len);
272 	to[len-1] = '\0';
273 }
274 
275 /*
276  * Copy a string to a "safe" place
277  * (that is, to a buffer allocated by calloc).
278  */
279 	public char *
280 save(s)
281 	char *s;
282 {
283 	register char *p;
284 
285 	p = (char *) ecalloc(strlen(s)+1, sizeof(char));
286 	strcpy(p, s);
287 	return (p);
288 }
289 
290 /*
291  * Allocate memory.
292  * Like calloc(), but never returns an error (NULL).
293  */
294 	public VOID_POINTER
295 ecalloc(count, size)
296 	int count;
297 	unsigned int size;
298 {
299 	register VOID_POINTER p;
300 
301 	p = (VOID_POINTER) calloc(count, size);
302 	if (p != NULL)
303 		return (p);
304 	error("Cannot allocate memory", NULL_PARG);
305 	quit(QUIT_ERROR);
306 	/*NOTREACHED*/
307 }
308 
309 /*
310  * Skip leading spaces in a string.
311  */
312 	public char *
313 skipsp(s)
314 	register char *s;
315 {
316 	while (*s == ' ' || *s == '\t')
317 		s++;
318 	return (s);
319 }
320 
321 /*
322  * Exit the program.
323  */
324 	public void
325 quit(status)
326 	int status;
327 {
328 	static int save_status;
329 
330 	/*
331 	 * Put cursor at bottom left corner, clear the line,
332 	 * reset the terminal modes, and exit.
333 	 */
334 	if (status < 0)
335 		status = save_status;
336 	else
337 		save_status = status;
338 	quitting = 1;
339 	edit((char*)NULL);
340 	if (is_tty && any_display)
341 		clear_bot();
342 	deinit();
343 	flush();
344 	raw_mode(0);
345 #if MSOFTC
346 	/*
347 	 * If we don't close 2, we get some garbage from
348 	 * 2's buffer when it flushes automatically.
349 	 * I cannot track this one down  RB
350 	 * The same bug shows up if we use ^C^C to abort.
351 	 */
352 	close(2);
353 #endif
354 	exit(status);
355 }
356