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