main.c revision 171817
1/* $FreeBSD: head/contrib/less/main.c 171817 2007-08-13 16:15:26Z delphij $ */
2/*
3 * Copyright (C) 1984-2007  Mark Nudelman
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Less License, as specified in the README file.
7 *
8 * For more information about less, or for information on how to
9 * contact the author, see the README file.
10 */
11
12
13/*
14 * Entry point, initialization, miscellaneous routines.
15 */
16
17#include "less.h"
18#if MSDOS_COMPILER==WIN32C
19#include <windows.h>
20#endif
21
22public char *	every_first_cmd = NULL;
23public int	new_file;
24public int	is_tty;
25public IFILE	curr_ifile = NULL_IFILE;
26public IFILE	old_ifile = NULL_IFILE;
27public struct scrpos initial_scrpos;
28public int	any_display = FALSE;
29public POSITION	start_attnpos = NULL_POSITION;
30public POSITION	end_attnpos = NULL_POSITION;
31public int	wscroll;
32public char *	progname;
33public int	quitting;
34public int	secure;
35public int	dohelp;
36public int	less_is_more;
37
38#if LOGFILE
39public int	logfile = -1;
40public int	force_logfile = FALSE;
41public char *	namelogfile = NULL;
42#endif
43
44#if EDITOR
45public char *	editor;
46public char *	editproto;
47#endif
48
49#if TAGS
50extern char *	tags;
51extern char *	tagoption;
52extern int	jump_sline;
53#endif
54
55#ifdef WIN32
56static char consoleTitle[256];
57#endif
58
59extern int	missing_cap;
60extern int	know_dumb;
61extern int	quit_if_one_screen;
62extern int	no_init;
63extern int	pr_type;
64
65
66/*
67 * Entry point.
68 */
69int
70main(argc, argv)
71	int argc;
72	char *argv[];
73{
74	IFILE ifile;
75	char *s;
76	extern char *__progname;
77
78#ifdef __EMX__
79	_response(&argc, &argv);
80	_wildcard(&argc, &argv);
81#endif
82
83	progname = *argv++;
84	argc--;
85
86	secure = 0;
87	s = lgetenv("LESSSECURE");
88	if (s != NULL && *s != '\0')
89		secure = 1;
90
91#ifdef WIN32
92	if (getenv("HOME") == NULL)
93	{
94		/*
95		 * If there is no HOME environment variable,
96		 * try the concatenation of HOMEDRIVE + HOMEPATH.
97		 */
98		char *drive = getenv("HOMEDRIVE");
99		char *path  = getenv("HOMEPATH");
100		if (drive != NULL && path != NULL)
101		{
102			char *env = (char *) ecalloc(strlen(drive) +
103					strlen(path) + 6, sizeof(char));
104			strcpy(env, "HOME=");
105			strcat(env, drive);
106			strcat(env, path);
107			putenv(env);
108		}
109	}
110	GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char));
111#endif /* WIN32 */
112
113	/*
114	 * Process command line arguments and LESS environment arguments.
115	 * Command line arguments override environment arguments.
116	 */
117	is_tty = isatty(1);
118	get_term();
119	init_cmds();
120	init_charset();
121	init_line();
122	init_cmdhist();
123	init_option();
124
125	/*
126	 * If the name of the executable program is "more",
127	 * act like LESS_IS_MORE is set.
128	 */
129	for (s = progname + strlen(progname);  s > progname;  s--)
130	{
131		if (s[-1] == PATHNAME_SEP[0])
132			break;
133	}
134	if (strcmp(s, "more") == 0)
135		less_is_more = 1;
136
137	init_prompt();
138
139	if (less_is_more)
140		scan_option("-fG");
141
142	s = lgetenv(less_is_more ? "MORE" : "LESS");
143	if (s != NULL)
144		scan_option(save(s));
145
146#define	isoptstring(s)	less_is_more   ? (((s)[0] == '-') && (s)[1] != '\0') : \
147			(((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
148	while (argc > 0 && (isoptstring(*argv) || isoptpending()))
149	{
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		/*
161		 * Last command line option was a flag requiring a
162		 * following string, but there was no following string.
163		 */
164		nopendopt();
165		quit(QUIT_OK);
166	}
167
168	if (less_is_more)
169		no_init = TRUE;
170	if (less_is_more && get_quit_at_eof())
171		quit_if_one_screen = TRUE;
172
173#if EDITOR
174	editor = lgetenv("VISUAL");
175	if (editor == NULL || *editor == '\0')
176	{
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#endif
185
186	/*
187	 * Call get_ifile with all the command line filenames
188	 * to "register" them with the ifile system.
189	 */
190	ifile = NULL_IFILE;
191	if (dohelp)
192		ifile = get_ifile(FAKE_HELPFILE, ifile);
193	while (argc-- > 0)
194	{
195		char *filename;
196#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC)
197		/*
198		 * Because the "shell" doesn't expand filename patterns,
199		 * treat each argument as a filename pattern rather than
200		 * a single filename.
201		 * Expand the pattern and iterate over the expanded list.
202		 */
203		struct textlist tlist;
204		char *gfilename;
205
206		gfilename = lglob(*argv++);
207		init_textlist(&tlist, gfilename);
208		filename = NULL;
209		while ((filename = forw_textlist(&tlist, filename)) != NULL)
210		{
211			(void) get_ifile(filename, ifile);
212			ifile = prev_ifile(NULL_IFILE);
213		}
214		free(gfilename);
215#else
216		filename = shell_quote(*argv);
217		if (filename == NULL)
218			filename = *argv;
219		argv++;
220		(void) get_ifile(filename, ifile);
221		ifile = prev_ifile(NULL_IFILE);
222#endif
223	}
224	/*
225	 * Set up terminal, etc.
226	 */
227	if (!is_tty)
228	{
229		/*
230		 * Output is not a tty.
231		 * Just copy the input file(s) to output.
232		 */
233		SET_BINARY(1);
234		if (nifile() == 0)
235		{
236			if (edit_stdin() == 0)
237				cat_file();
238		} else if (edit_first() == 0)
239		{
240			do {
241				cat_file();
242			} while (edit_next(1) == 0);
243		}
244		quit(QUIT_OK);
245	}
246
247	if (missing_cap && !know_dumb)
248		error("WARNING: terminal is not fully functional", NULL_PARG);
249	init_mark();
250	open_getchr();
251	raw_mode(1);
252	init_signals(1);
253
254	/*
255	 * Select the first file to examine.
256	 */
257#if TAGS
258	if (tagoption != NULL || strcmp(tags, "-") == 0)
259	{
260		/*
261		 * A -t option was given.
262		 * Verify that no filenames were also given.
263		 * Edit the file selected by the "tags" search,
264		 * and search for the proper line in the file.
265		 */
266		if (nifile() > 0)
267		{
268			error("No filenames allowed with -t option", NULL_PARG);
269			quit(QUIT_ERROR);
270		}
271		findtag(tagoption);
272		if (edit_tagfile())  /* Edit file which contains the tag */
273			quit(QUIT_ERROR);
274		/*
275		 * Search for the line which contains the tag.
276		 * Set up initial_scrpos so we display that line.
277		 */
278		initial_scrpos.pos = tagsearch();
279		if (initial_scrpos.pos == NULL_POSITION)
280			quit(QUIT_ERROR);
281		initial_scrpos.ln = jump_sline;
282	} else
283#endif
284	if (nifile() == 0)
285	{
286		if (edit_stdin())  /* Edit standard input */
287			quit(QUIT_ERROR);
288	} else
289	{
290		if (edit_first())  /* Edit first valid file in cmd line */
291			quit(QUIT_ERROR);
292	}
293
294	init();
295	commands();
296	quit(QUIT_OK);
297	/*NOTREACHED*/
298	return (0);
299}
300
301/*
302 * Copy a string to a "safe" place
303 * (that is, to a buffer allocated by calloc).
304 */
305	public char *
306save(s)
307	char *s;
308{
309	register char *p;
310
311	p = (char *) ecalloc(strlen(s)+1, sizeof(char));
312	strcpy(p, s);
313	return (p);
314}
315
316/*
317 * Allocate memory.
318 * Like calloc(), but never returns an error (NULL).
319 */
320	public VOID_POINTER
321ecalloc(count, size)
322	int count;
323	unsigned int size;
324{
325	register VOID_POINTER p;
326
327	p = (VOID_POINTER) calloc(count, size);
328	if (p != NULL)
329		return (p);
330	error("Cannot allocate memory", NULL_PARG);
331	quit(QUIT_ERROR);
332	/*NOTREACHED*/
333	return (NULL);
334}
335
336/*
337 * Skip leading spaces in a string.
338 */
339	public char *
340skipsp(s)
341	register char *s;
342{
343	while (*s == ' ' || *s == '\t')
344		s++;
345	return (s);
346}
347
348/*
349 * See how many characters of two strings are identical.
350 * If uppercase is true, the first string must begin with an uppercase
351 * character; the remainder of the first string may be either case.
352 */
353	public int
354sprefix(ps, s, uppercase)
355	char *ps;
356	char *s;
357	int uppercase;
358{
359	register int c;
360	register int sc;
361	register int len = 0;
362
363	for ( ;  *s != '\0';  s++, ps++)
364	{
365		c = *ps;
366		if (uppercase)
367		{
368			if (len == 0 && ASCII_IS_LOWER(c))
369				return (-1);
370			if (ASCII_IS_UPPER(c))
371				c = ASCII_TO_LOWER(c);
372		}
373		sc = *s;
374		if (len > 0 && ASCII_IS_UPPER(sc))
375			sc = ASCII_TO_LOWER(sc);
376		if (c != sc)
377			break;
378		len++;
379	}
380	return (len);
381}
382
383/*
384 * Exit the program.
385 */
386	public void
387quit(status)
388	int status;
389{
390	static int save_status;
391
392	/*
393	 * Put cursor at bottom left corner, clear the line,
394	 * reset the terminal modes, and exit.
395	 */
396	if (status < 0)
397		status = save_status;
398	else
399		save_status = status;
400	quitting = 1;
401	edit((char*)NULL);
402	save_cmdhist();
403	if (any_display && is_tty)
404		clear_bot();
405	deinit();
406	flush();
407	raw_mode(0);
408#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
409	/*
410	 * If we don't close 2, we get some garbage from
411	 * 2's buffer when it flushes automatically.
412	 * I cannot track this one down  RB
413	 * The same bug shows up if we use ^C^C to abort.
414	 */
415	close(2);
416#endif
417#if WIN32
418	SetConsoleTitle(consoleTitle);
419#endif
420	close_getchr();
421	exit(status);
422}
423