xref: /openbsd-src/usr.bin/less/main.c (revision a4afd6dad3fba28f80e70208181c06c482259988)
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 		scan_option(*argv++);
126 #undef isoptstring
127 
128 	if (isoptpending())
129 	{
130 		/*
131 		 * Last command line option was a flag requiring a
132 		 * following string, but there was no following string.
133 		 */
134 		nopendopt();
135 		quit(QUIT_OK);
136 	}
137 
138 #if EDITOR
139 	editor = getenv("VISUAL");
140 	if (editor == NULL || *editor == '\0')
141 	{
142 		editor = getenv("EDITOR");
143 		if (editor == NULL || *editor == '\0')
144 			editor = EDIT_PGM;
145 	}
146 	editproto = getenv("LESSEDIT");
147 	if (editproto == NULL || *editproto == '\0')
148 		editproto = "%E ?lm+%lm. %f";
149 #endif
150 
151 	/*
152 	 * Call get_ifile with all the command line filenames
153 	 * to "register" them with the ifile system.
154 	 */
155 	ifile = NULL_IFILE;
156 	while (--argc >= 0)
157 	{
158 #if MSOFTC || OS2
159 		/*
160 		 * Because the "shell" doesn't expand filename patterns,
161 		 * treat each argument as a filename pattern rather than
162 		 * a single filename.
163 		 * Expand the pattern and iterate over the expanded list.
164 		 */
165 		struct textlist tlist;
166 		char *gfilename;
167 		char *filename;
168 
169 		gfilename = glob(*argv++);
170 		init_textlist(&tlist, gfilename);
171 		filename = NULL;
172 		while ((filename = forw_textlist(&tlist, filename)) != NULL)
173 			ifile = get_ifile(filename, ifile);
174 		free(gfilename);
175 #else
176 		ifile = get_ifile(*argv++, ifile);
177 #endif
178 	}
179 	/*
180 	 * Set up terminal, etc.
181 	 */
182 	is_tty = isatty(1);
183 	if (!is_tty)
184 	{
185 		/*
186 		 * Output is not a tty.
187 		 * Just copy the input file(s) to output.
188 		 */
189 		if (nifile() == 0)
190 		{
191 			if (edit_stdin() == 0)
192 				cat_file();
193 		} else if (edit_first() == 0)
194 		{
195 			do {
196 				cat_file();
197 			} while (edit_next(1) == 0);
198 		}
199 		quit(QUIT_OK);
200 	}
201 
202 	init_mark();
203 	raw_mode(1);
204 	open_getchr();
205 	init_signals(1);
206 
207 	/*
208 	 * Select the first file to examine.
209 	 */
210 #if TAGS
211 	if (tagoption != NULL)
212 	{
213 		/*
214 		 * A -t option was given.
215 		 * Verify that no filenames were also given.
216 		 * Edit the file selected by the "tags" search,
217 		 * and search for the proper line in the file.
218 		 */
219 		if (nifile() > 0)
220 		{
221 			error("No filenames allowed with -t option", NULL_PARG);
222 			quit(QUIT_ERROR);
223 		}
224 		findtag(tagoption);
225 		if (tagfile == NULL)
226 			quit(QUIT_ERROR);
227 		if (edit(tagfile))  /* Edit file which contains the tag */
228 			quit(QUIT_ERROR);
229 		/*
230 		 * Search for the line which contains the tag.
231 		 * Set up initial_scrpos so we display that line.
232 		 */
233 		initial_scrpos.pos = tagsearch();
234 		if (initial_scrpos.pos == NULL_POSITION)
235 			quit(QUIT_ERROR);
236 		initial_scrpos.ln = jump_sline;
237 	} else
238 #endif
239 	if (nifile() == 0)
240 	{
241 		if (edit_stdin())  /* Edit standard input */
242 			quit(QUIT_ERROR);
243 	} else
244 	{
245 		if (edit_first())  /* Edit first valid file in cmd line */
246 			quit(QUIT_ERROR);
247 	}
248 
249 	init();
250 	commands();
251 	quit(QUIT_OK);
252 	/*NOTREACHED*/
253 }
254 
255 /*
256  * Copy a string, truncating to the specified length if necessary.
257  * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
258  */
259 	public void
260 strtcpy(to, from, len)
261 	char *to;
262 	char *from;
263 	unsigned int len;
264 {
265 	strncpy(to, from, len);
266 	to[len-1] = '\0';
267 }
268 
269 /*
270  * Copy a string to a "safe" place
271  * (that is, to a buffer allocated by calloc).
272  */
273 	public char *
274 save(s)
275 	char *s;
276 {
277 	register char *p;
278 
279 	p = (char *) ecalloc(strlen(s)+1, sizeof(char));
280 	strcpy(p, s);
281 	return (p);
282 }
283 
284 /*
285  * Allocate memory.
286  * Like calloc(), but never returns an error (NULL).
287  */
288 	public VOID_POINTER
289 ecalloc(count, size)
290 	int count;
291 	unsigned int size;
292 {
293 	register VOID_POINTER p;
294 
295 	p = (VOID_POINTER) calloc(count, size);
296 	if (p != NULL)
297 		return (p);
298 	error("Cannot allocate memory", NULL_PARG);
299 	quit(QUIT_ERROR);
300 	/*NOTREACHED*/
301 }
302 
303 /*
304  * Skip leading spaces in a string.
305  */
306 	public char *
307 skipsp(s)
308 	register char *s;
309 {
310 	while (*s == ' ' || *s == '\t')
311 		s++;
312 	return (s);
313 }
314 
315 /*
316  * Exit the program.
317  */
318 	public void
319 quit(status)
320 	int status;
321 {
322 	static int save_status;
323 
324 	/*
325 	 * Put cursor at bottom left corner, clear the line,
326 	 * reset the terminal modes, and exit.
327 	 */
328 	if (status < 0)
329 		status = save_status;
330 	else
331 		save_status = status;
332 	quitting = 1;
333 	edit((char*)NULL);
334 	if (is_tty && any_display)
335 		clear_bot();
336 	deinit();
337 	flush();
338 	raw_mode(0);
339 #if MSOFTC
340 	/*
341 	 * If we don't close 2, we get some garbage from
342 	 * 2's buffer when it flushes automatically.
343 	 * I cannot track this one down  RB
344 	 * The same bug shows up if we use ^C^C to abort.
345 	 */
346 	close(2);
347 #endif
348 	exit(status);
349 }
350