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