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