160812Sps/* $FreeBSD$ */
260786Sps/*
3240121Sdelphij * Copyright (C) 1984-2012  Mark Nudelman
460786Sps *
560786Sps * You may distribute under the terms of either the GNU General Public
660786Sps * License or the Less License, as specified in the README file.
760786Sps *
8240121Sdelphij * For more information, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * Entry point, initialization, miscellaneous routines.
1460786Sps */
1560786Sps
1660786Sps#include "less.h"
1789022Sps#if MSDOS_COMPILER==WIN32C
1889022Sps#include <windows.h>
1989022Sps#endif
2060786Sps
2160786Spspublic char *	every_first_cmd = NULL;
2260786Spspublic int	new_file;
2360786Spspublic int	is_tty;
2460786Spspublic IFILE	curr_ifile = NULL_IFILE;
2560786Spspublic IFILE	old_ifile = NULL_IFILE;
2660786Spspublic struct scrpos initial_scrpos;
2760786Spspublic int	any_display = FALSE;
2860786Spspublic POSITION	start_attnpos = NULL_POSITION;
2960786Spspublic POSITION	end_attnpos = NULL_POSITION;
3060786Spspublic int	wscroll;
3160786Spspublic char *	progname;
3260786Spspublic int	quitting;
3360786Spspublic int	secure;
3460786Spspublic int	dohelp;
3560786Sps
3660786Sps#if LOGFILE
3760786Spspublic int	logfile = -1;
3860786Spspublic int	force_logfile = FALSE;
3960786Spspublic char *	namelogfile = NULL;
4060786Sps#endif
4160786Sps
4260786Sps#if EDITOR
4360786Spspublic char *	editor;
4460786Spspublic char *	editproto;
4560786Sps#endif
4660786Sps
4760786Sps#if TAGS
4889022Spsextern char *	tags;
4960786Spsextern char *	tagoption;
5060786Spsextern int	jump_sline;
5160786Sps#endif
5260786Sps
5389022Sps#ifdef WIN32
5489022Spsstatic char consoleTitle[256];
5589022Sps#endif
5689022Sps
57221715Sdelphijextern int	less_is_more;
5860786Spsextern int	missing_cap;
5960786Spsextern int	know_dumb;
60170259Sdelphijextern int	quit_if_one_screen;
61171009Sdelphijextern int	no_init;
62170259Sdelphijextern int	pr_type;
6360786Sps
6460786Sps
6560786Sps/*
6660786Sps * Entry point.
6760786Sps */
6860786Spsint
6960786Spsmain(argc, argv)
7060786Sps	int argc;
7160786Sps	char *argv[];
7260786Sps{
7360786Sps	IFILE ifile;
7460786Sps	char *s;
7560812Sps	extern char *__progname;
7660786Sps
7760786Sps#ifdef __EMX__
7860786Sps	_response(&argc, &argv);
7960786Sps	_wildcard(&argc, &argv);
8060786Sps#endif
8160786Sps
8260786Sps	progname = *argv++;
8360786Sps	argc--;
8460786Sps
8560786Sps	secure = 0;
8660786Sps	s = lgetenv("LESSSECURE");
8760786Sps	if (s != NULL && *s != '\0')
8860786Sps		secure = 1;
8960786Sps
9060786Sps#ifdef WIN32
9160786Sps	if (getenv("HOME") == NULL)
9260786Sps	{
9360786Sps		/*
9460786Sps		 * If there is no HOME environment variable,
9560786Sps		 * try the concatenation of HOMEDRIVE + HOMEPATH.
9660786Sps		 */
9760786Sps		char *drive = getenv("HOMEDRIVE");
9860786Sps		char *path  = getenv("HOMEPATH");
9960786Sps		if (drive != NULL && path != NULL)
10060786Sps		{
10160786Sps			char *env = (char *) ecalloc(strlen(drive) +
10260786Sps					strlen(path) + 6, sizeof(char));
10360786Sps			strcpy(env, "HOME=");
10460786Sps			strcat(env, drive);
10560786Sps			strcat(env, path);
10660786Sps			putenv(env);
10760786Sps		}
10860786Sps	}
10989022Sps	GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char));
11060786Sps#endif /* WIN32 */
11160786Sps
11260786Sps	/*
11360786Sps	 * Process command line arguments and LESS environment arguments.
11460786Sps	 * Command line arguments override environment arguments.
11560786Sps	 */
11660786Sps	is_tty = isatty(1);
11760786Sps	get_term();
11860786Sps	init_cmds();
11960786Sps	init_charset();
12060786Sps	init_line();
121161478Sdelphij	init_cmdhist();
12260786Sps	init_option();
123195941Sdelphij	init_search();
124170259Sdelphij
125170259Sdelphij	/*
126170259Sdelphij	 * If the name of the executable program is "more",
127170259Sdelphij	 * act like LESS_IS_MORE is set.
128170259Sdelphij	 */
129170259Sdelphij	for (s = progname + strlen(progname);  s > progname;  s--)
130170259Sdelphij	{
131170259Sdelphij		if (s[-1] == PATHNAME_SEP[0])
132170259Sdelphij			break;
13360812Sps	}
134170259Sdelphij	if (strcmp(s, "more") == 0)
135170259Sdelphij		less_is_more = 1;
136170259Sdelphij
137170259Sdelphij	init_prompt();
138170259Sdelphij
139170812Sdelphij	if (less_is_more)
140170812Sdelphij		scan_option("-fG");
141170812Sdelphij
142170259Sdelphij	s = lgetenv(less_is_more ? "MORE" : "LESS");
14360786Sps	if (s != NULL)
14460786Sps		scan_option(save(s));
14560786Sps
146170963Sdelphij#define	isoptstring(s)	less_is_more   ? (((s)[0] == '-') && (s)[1] != '\0') : \
147170963Sdelphij			(((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
14860786Sps	while (argc > 0 && (isoptstring(*argv) || isoptpending()))
14960786Sps	{
15060786Sps		s = *argv++;
15160786Sps		argc--;
15260786Sps		if (strcmp(s, "--") == 0)
15360786Sps			break;
15460786Sps		scan_option(s);
15560786Sps	}
15660786Sps#undef isoptstring
15760786Sps
15860786Sps	if (isoptpending())
15960786Sps	{
16060786Sps		/*
16160786Sps		 * Last command line option was a flag requiring a
16260786Sps		 * following string, but there was no following string.
16360786Sps		 */
16460786Sps		nopendopt();
16560786Sps		quit(QUIT_OK);
16660786Sps	}
16760786Sps
168171817Sdelphij	if (less_is_more)
169171817Sdelphij		no_init = TRUE;
170171817Sdelphij	if (less_is_more && get_quit_at_eof())
171171817Sdelphij		quit_if_one_screen = TRUE;
172170259Sdelphij
17360786Sps#if EDITOR
17460786Sps	editor = lgetenv("VISUAL");
17560786Sps	if (editor == NULL || *editor == '\0')
17660786Sps	{
17760786Sps		editor = lgetenv("EDITOR");
17860786Sps		if (editor == NULL || *editor == '\0')
17960786Sps			editor = EDIT_PGM;
18060786Sps	}
18160786Sps	editproto = lgetenv("LESSEDIT");
18260786Sps	if (editproto == NULL || *editproto == '\0')
18360786Sps		editproto = "%E ?lm+%lm. %f";
18460786Sps#endif
18560786Sps
18660786Sps	/*
18760786Sps	 * Call get_ifile with all the command line filenames
18860786Sps	 * to "register" them with the ifile system.
18960786Sps	 */
19060786Sps	ifile = NULL_IFILE;
19160786Sps	if (dohelp)
19260786Sps		ifile = get_ifile(FAKE_HELPFILE, ifile);
19360786Sps	while (argc-- > 0)
19460786Sps	{
195128348Stjr		char *filename;
19689022Sps#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC)
19760786Sps		/*
19860786Sps		 * Because the "shell" doesn't expand filename patterns,
19960786Sps		 * treat each argument as a filename pattern rather than
20060786Sps		 * a single filename.
20160786Sps		 * Expand the pattern and iterate over the expanded list.
20260786Sps		 */
20360786Sps		struct textlist tlist;
20460786Sps		char *gfilename;
20560786Sps
20660786Sps		gfilename = lglob(*argv++);
20760786Sps		init_textlist(&tlist, gfilename);
20860786Sps		filename = NULL;
20960786Sps		while ((filename = forw_textlist(&tlist, filename)) != NULL)
210128348Stjr		{
211128348Stjr			(void) get_ifile(filename, ifile);
212128348Stjr			ifile = prev_ifile(NULL_IFILE);
213128348Stjr		}
21460786Sps		free(gfilename);
21560786Sps#else
216128348Stjr		filename = shell_quote(*argv);
217128348Stjr		if (filename == NULL)
218128348Stjr			filename = *argv;
219128348Stjr		argv++;
220128348Stjr		(void) get_ifile(filename, ifile);
221128348Stjr		ifile = prev_ifile(NULL_IFILE);
222240121Sdelphij		free(filename);
22360786Sps#endif
22460786Sps	}
22560786Sps	/*
22660786Sps	 * Set up terminal, etc.
22760786Sps	 */
22860786Sps	if (!is_tty)
22960786Sps	{
23060786Sps		/*
23160786Sps		 * Output is not a tty.
23260786Sps		 * Just copy the input file(s) to output.
23360786Sps		 */
23460786Sps		SET_BINARY(1);
23560786Sps		if (nifile() == 0)
23660786Sps		{
23760786Sps			if (edit_stdin() == 0)
23860786Sps				cat_file();
23960786Sps		} else if (edit_first() == 0)
24060786Sps		{
24160786Sps			do {
24260786Sps				cat_file();
24360786Sps			} while (edit_next(1) == 0);
24460786Sps		}
24560786Sps		quit(QUIT_OK);
24660786Sps	}
24760786Sps
248172045Sdelphij	if (missing_cap && !know_dumb && !less_is_more)
24960786Sps		error("WARNING: terminal is not fully functional", NULL_PARG);
25060786Sps	init_mark();
251128348Stjr	open_getchr();
25260786Sps	raw_mode(1);
25360786Sps	init_signals(1);
25460786Sps
25560786Sps	/*
25660786Sps	 * Select the first file to examine.
25760786Sps	 */
25860786Sps#if TAGS
25989022Sps	if (tagoption != NULL || strcmp(tags, "-") == 0)
26060786Sps	{
26160786Sps		/*
26260786Sps		 * A -t option was given.
26360786Sps		 * Verify that no filenames were also given.
26460786Sps		 * Edit the file selected by the "tags" search,
26560786Sps		 * and search for the proper line in the file.
26660786Sps		 */
26760786Sps		if (nifile() > 0)
26860786Sps		{
26960786Sps			error("No filenames allowed with -t option", NULL_PARG);
27060786Sps			quit(QUIT_ERROR);
27160786Sps		}
27260786Sps		findtag(tagoption);
27360786Sps		if (edit_tagfile())  /* Edit file which contains the tag */
27460786Sps			quit(QUIT_ERROR);
27560786Sps		/*
27660786Sps		 * Search for the line which contains the tag.
27760786Sps		 * Set up initial_scrpos so we display that line.
27860786Sps		 */
27960786Sps		initial_scrpos.pos = tagsearch();
28060786Sps		if (initial_scrpos.pos == NULL_POSITION)
28160786Sps			quit(QUIT_ERROR);
28260786Sps		initial_scrpos.ln = jump_sline;
28360786Sps	} else
28460786Sps#endif
28560786Sps	if (nifile() == 0)
28660786Sps	{
28760786Sps		if (edit_stdin())  /* Edit standard input */
28860786Sps			quit(QUIT_ERROR);
28960786Sps	} else
29060786Sps	{
29160786Sps		if (edit_first())  /* Edit first valid file in cmd line */
29260786Sps			quit(QUIT_ERROR);
29360786Sps	}
29460786Sps
29560786Sps	init();
29660786Sps	commands();
29760786Sps	quit(QUIT_OK);
29860786Sps	/*NOTREACHED*/
299128348Stjr	return (0);
30060786Sps}
30160786Sps
30260786Sps/*
30360786Sps * Copy a string to a "safe" place
30460786Sps * (that is, to a buffer allocated by calloc).
30560786Sps */
30660786Sps	public char *
30760786Spssave(s)
30860786Sps	char *s;
30960786Sps{
31060786Sps	register char *p;
31160786Sps
31260786Sps	p = (char *) ecalloc(strlen(s)+1, sizeof(char));
31360786Sps	strcpy(p, s);
31460786Sps	return (p);
31560786Sps}
31660786Sps
31760786Sps/*
31860786Sps * Allocate memory.
31960786Sps * Like calloc(), but never returns an error (NULL).
32060786Sps */
32160786Sps	public VOID_POINTER
32260786Spsecalloc(count, size)
32360786Sps	int count;
32460786Sps	unsigned int size;
32560786Sps{
32660786Sps	register VOID_POINTER p;
32760786Sps
32860786Sps	p = (VOID_POINTER) calloc(count, size);
32960786Sps	if (p != NULL)
33060786Sps		return (p);
33160786Sps	error("Cannot allocate memory", NULL_PARG);
33260786Sps	quit(QUIT_ERROR);
33360786Sps	/*NOTREACHED*/
334128348Stjr	return (NULL);
33560786Sps}
33660786Sps
33760786Sps/*
33860786Sps * Skip leading spaces in a string.
33960786Sps */
34060786Sps	public char *
34160786Spsskipsp(s)
34260786Sps	register char *s;
34360786Sps{
34460786Sps	while (*s == ' ' || *s == '\t')
34560786Sps		s++;
34660786Sps	return (s);
34760786Sps}
34860786Sps
34960786Sps/*
35060786Sps * See how many characters of two strings are identical.
35160786Sps * If uppercase is true, the first string must begin with an uppercase
35260786Sps * character; the remainder of the first string may be either case.
35360786Sps */
35460786Sps	public int
35560786Spssprefix(ps, s, uppercase)
35660786Sps	char *ps;
35760786Sps	char *s;
35860786Sps	int uppercase;
35960786Sps{
36060786Sps	register int c;
36160786Sps	register int sc;
36260786Sps	register int len = 0;
36360786Sps
36460786Sps	for ( ;  *s != '\0';  s++, ps++)
36560786Sps	{
36660786Sps		c = *ps;
36760786Sps		if (uppercase)
36860786Sps		{
369161478Sdelphij			if (len == 0 && ASCII_IS_LOWER(c))
37060786Sps				return (-1);
371161478Sdelphij			if (ASCII_IS_UPPER(c))
372161478Sdelphij				c = ASCII_TO_LOWER(c);
37360786Sps		}
37460786Sps		sc = *s;
375161478Sdelphij		if (len > 0 && ASCII_IS_UPPER(sc))
376161478Sdelphij			sc = ASCII_TO_LOWER(sc);
37760786Sps		if (c != sc)
37860786Sps			break;
37960786Sps		len++;
38060786Sps	}
38160786Sps	return (len);
38260786Sps}
38360786Sps
38460786Sps/*
38560786Sps * Exit the program.
38660786Sps */
38760786Sps	public void
38860786Spsquit(status)
38960786Sps	int status;
39060786Sps{
39160786Sps	static int save_status;
39260786Sps
39360786Sps	/*
39460786Sps	 * Put cursor at bottom left corner, clear the line,
39560786Sps	 * reset the terminal modes, and exit.
39660786Sps	 */
39760786Sps	if (status < 0)
39860786Sps		status = save_status;
39960786Sps	else
40060786Sps		save_status = status;
40160786Sps	quitting = 1;
40260786Sps	edit((char*)NULL);
403161478Sdelphij	save_cmdhist();
40460786Sps	if (any_display && is_tty)
40560786Sps		clear_bot();
40660786Sps	deinit();
40760786Sps	flush();
40860786Sps	raw_mode(0);
40960786Sps#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
41060786Sps	/*
41160786Sps	 * If we don't close 2, we get some garbage from
41260786Sps	 * 2's buffer when it flushes automatically.
41360786Sps	 * I cannot track this one down  RB
41460786Sps	 * The same bug shows up if we use ^C^C to abort.
41560786Sps	 */
41660786Sps	close(2);
41760786Sps#endif
418221715Sdelphij#ifdef WIN32
41989022Sps	SetConsoleTitle(consoleTitle);
42089022Sps#endif
42160786Sps	close_getchr();
42260786Sps	exit(status);
42360786Sps}
424