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