160812Sps/* $FreeBSD: stable/10/contrib/less/main.c 330571 2018-03-07 06:39:00Z delphij $ */
2238730Sdelphij/*
3330571Sdelphij * Copyright (C) 1984-2017  Mark Nudelman
4238730Sdelphij *
5238730Sdelphij * You may distribute under the terms of either the GNU General Public
6238730Sdelphij * License or the Less License, as specified in the README file.
7238730Sdelphij *
8238730Sdelphij * For more information, see the README file.
9238730Sdelphij */
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
57330571Sdelphijpublic int  line_count;
58221715Sdelphijextern int	less_is_more;
5960786Spsextern int	missing_cap;
6060786Spsextern int	know_dumb;
61171009Sdelphijextern int	no_init;
62170259Sdelphijextern int	pr_type;
63330571Sdelphijextern int	quit_if_one_screen;
6460786Sps
6560786Sps
6660786Sps/*
6760786Sps * Entry point.
6860786Sps */
6960786Spsint
7060786Spsmain(argc, argv)
7160786Sps	int argc;
7260786Sps	char *argv[];
7360786Sps{
7460786Sps	IFILE ifile;
7560786Sps	char *s;
7660812Sps	extern char *__progname;
7760786Sps
7860786Sps#ifdef __EMX__
7960786Sps	_response(&argc, &argv);
8060786Sps	_wildcard(&argc, &argv);
8160786Sps#endif
8260786Sps
8360786Sps	progname = *argv++;
8460786Sps	argc--;
8560786Sps
8660786Sps	secure = 0;
8760786Sps	s = lgetenv("LESSSECURE");
8860786Sps	if (s != NULL && *s != '\0')
8960786Sps		secure = 1;
9060786Sps
9160786Sps#ifdef WIN32
9260786Sps	if (getenv("HOME") == NULL)
9360786Sps	{
9460786Sps		/*
9560786Sps		 * If there is no HOME environment variable,
9660786Sps		 * try the concatenation of HOMEDRIVE + HOMEPATH.
9760786Sps		 */
9860786Sps		char *drive = getenv("HOMEDRIVE");
9960786Sps		char *path  = getenv("HOMEPATH");
10060786Sps		if (drive != NULL && path != NULL)
10160786Sps		{
10260786Sps			char *env = (char *) ecalloc(strlen(drive) +
10360786Sps					strlen(path) + 6, sizeof(char));
10460786Sps			strcpy(env, "HOME=");
10560786Sps			strcat(env, drive);
10660786Sps			strcat(env, path);
10760786Sps			putenv(env);
10860786Sps		}
10960786Sps	}
11089022Sps	GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char));
11160786Sps#endif /* WIN32 */
11260786Sps
11360786Sps	/*
11460786Sps	 * Process command line arguments and LESS environment arguments.
11560786Sps	 * Command line arguments override environment arguments.
11660786Sps	 */
11760786Sps	is_tty = isatty(1);
118330571Sdelphij	init_cmds();
11960786Sps	get_term();
120330571Sdelphij	expand_cmd_tables();
12160786Sps	init_charset();
12260786Sps	init_line();
123161478Sdelphij	init_cmdhist();
12460786Sps	init_option();
125195941Sdelphij	init_search();
126170259Sdelphij
127170259Sdelphij	/*
128170259Sdelphij	 * If the name of the executable program is "more",
129170259Sdelphij	 * act like LESS_IS_MORE is set.
130170259Sdelphij	 */
131170259Sdelphij	for (s = progname + strlen(progname);  s > progname;  s--)
132170259Sdelphij	{
133170259Sdelphij		if (s[-1] == PATHNAME_SEP[0])
134170259Sdelphij			break;
13560812Sps	}
136170259Sdelphij	if (strcmp(s, "more") == 0)
137170259Sdelphij		less_is_more = 1;
138170259Sdelphij
139170259Sdelphij	init_prompt();
140170259Sdelphij
141170812Sdelphij	if (less_is_more)
142170812Sdelphij		scan_option("-fG");
143170812Sdelphij
144170259Sdelphij	s = lgetenv(less_is_more ? "MORE" : "LESS");
14560786Sps	if (s != NULL)
14660786Sps		scan_option(save(s));
14760786Sps
148170963Sdelphij#define	isoptstring(s)	less_is_more   ? (((s)[0] == '-') && (s)[1] != '\0') : \
149170963Sdelphij			(((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
15060786Sps	while (argc > 0 && (isoptstring(*argv) || isoptpending()))
15160786Sps	{
15260786Sps		s = *argv++;
15360786Sps		argc--;
15460786Sps		if (strcmp(s, "--") == 0)
15560786Sps			break;
15660786Sps		scan_option(s);
15760786Sps	}
15860786Sps#undef isoptstring
15960786Sps
16060786Sps	if (isoptpending())
16160786Sps	{
16260786Sps		/*
16360786Sps		 * Last command line option was a flag requiring a
16460786Sps		 * following string, but there was no following string.
16560786Sps		 */
16660786Sps		nopendopt();
16760786Sps		quit(QUIT_OK);
16860786Sps	}
16960786Sps
170171817Sdelphij	if (less_is_more)
171171817Sdelphij		no_init = 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	{
19589022Sps#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC)
19660786Sps		/*
19760786Sps		 * Because the "shell" doesn't expand filename patterns,
19860786Sps		 * treat each argument as a filename pattern rather than
19960786Sps		 * a single filename.
20060786Sps		 * Expand the pattern and iterate over the expanded list.
20160786Sps		 */
20260786Sps		struct textlist tlist;
203330571Sdelphij		char *filename;
20460786Sps		char *gfilename;
205330571Sdelphij		char *qfilename;
20660786Sps
20760786Sps		gfilename = lglob(*argv++);
20860786Sps		init_textlist(&tlist, gfilename);
20960786Sps		filename = NULL;
21060786Sps		while ((filename = forw_textlist(&tlist, filename)) != NULL)
211128348Stjr		{
212330571Sdelphij			qfilename = shell_unquote(filename);
213330571Sdelphij			(void) get_ifile(qfilename, ifile);
214330571Sdelphij			free(qfilename);
215128348Stjr			ifile = prev_ifile(NULL_IFILE);
216128348Stjr		}
21760786Sps		free(gfilename);
21860786Sps#else
219330571Sdelphij		(void) get_ifile(*argv++, ifile);
220128348Stjr		ifile = prev_ifile(NULL_IFILE);
22160786Sps#endif
22260786Sps	}
22360786Sps	/*
22460786Sps	 * Set up terminal, etc.
22560786Sps	 */
22660786Sps	if (!is_tty)
22760786Sps	{
22860786Sps		/*
22960786Sps		 * Output is not a tty.
23060786Sps		 * Just copy the input file(s) to output.
23160786Sps		 */
23260786Sps		SET_BINARY(1);
23360786Sps		if (nifile() == 0)
23460786Sps		{
23560786Sps			if (edit_stdin() == 0)
23660786Sps				cat_file();
23760786Sps		} else if (edit_first() == 0)
23860786Sps		{
23960786Sps			do {
24060786Sps				cat_file();
24160786Sps			} while (edit_next(1) == 0);
24260786Sps		}
24360786Sps		quit(QUIT_OK);
24460786Sps	}
24560786Sps
246172045Sdelphij	if (missing_cap && !know_dumb && !less_is_more)
24760786Sps		error("WARNING: terminal is not fully functional", NULL_PARG);
24860786Sps	init_mark();
249128348Stjr	open_getchr();
25060786Sps	raw_mode(1);
25160786Sps	init_signals(1);
25260786Sps
25360786Sps	/*
25460786Sps	 * Select the first file to examine.
25560786Sps	 */
25660786Sps#if TAGS
25789022Sps	if (tagoption != NULL || strcmp(tags, "-") == 0)
25860786Sps	{
25960786Sps		/*
26060786Sps		 * A -t option was given.
26160786Sps		 * Verify that no filenames were also given.
26260786Sps		 * Edit the file selected by the "tags" search,
26360786Sps		 * and search for the proper line in the file.
26460786Sps		 */
26560786Sps		if (nifile() > 0)
26660786Sps		{
26760786Sps			error("No filenames allowed with -t option", NULL_PARG);
26860786Sps			quit(QUIT_ERROR);
26960786Sps		}
27060786Sps		findtag(tagoption);
27160786Sps		if (edit_tagfile())  /* Edit file which contains the tag */
27260786Sps			quit(QUIT_ERROR);
27360786Sps		/*
27460786Sps		 * Search for the line which contains the tag.
27560786Sps		 * Set up initial_scrpos so we display that line.
27660786Sps		 */
27760786Sps		initial_scrpos.pos = tagsearch();
27860786Sps		if (initial_scrpos.pos == NULL_POSITION)
27960786Sps			quit(QUIT_ERROR);
28060786Sps		initial_scrpos.ln = jump_sline;
28160786Sps	} else
28260786Sps#endif
28360786Sps	if (nifile() == 0)
28460786Sps	{
28560786Sps		if (edit_stdin())  /* Edit standard input */
28660786Sps			quit(QUIT_ERROR);
287330571Sdelphij		if (quit_if_one_screen)
288330571Sdelphij			line_count = get_line_count();
28960786Sps	} else
29060786Sps	{
29160786Sps		if (edit_first())  /* Edit first valid file in cmd line */
29260786Sps			quit(QUIT_ERROR);
293330571Sdelphij		if (quit_if_one_screen)
294330571Sdelphij		{
295330571Sdelphij			if (nifile() == 1)
296330571Sdelphij				line_count = get_line_count();
297330571Sdelphij			else /* If more than one file, -F can not be used */
298330571Sdelphij				quit_if_one_screen = FALSE;
299330571Sdelphij		}
30060786Sps	}
30160786Sps
30260786Sps	init();
30360786Sps	commands();
30460786Sps	quit(QUIT_OK);
30560786Sps	/*NOTREACHED*/
306128348Stjr	return (0);
30760786Sps}
30860786Sps
30960786Sps/*
31060786Sps * Copy a string to a "safe" place
31160786Sps * (that is, to a buffer allocated by calloc).
31260786Sps */
31360786Sps	public char *
31460786Spssave(s)
315330571Sdelphij	constant char *s;
31660786Sps{
317330571Sdelphij	char *p;
31860786Sps
31960786Sps	p = (char *) ecalloc(strlen(s)+1, sizeof(char));
32060786Sps	strcpy(p, s);
32160786Sps	return (p);
32260786Sps}
32360786Sps
32460786Sps/*
32560786Sps * Allocate memory.
32660786Sps * Like calloc(), but never returns an error (NULL).
32760786Sps */
32860786Sps	public VOID_POINTER
32960786Spsecalloc(count, size)
33060786Sps	int count;
33160786Sps	unsigned int size;
33260786Sps{
333330571Sdelphij	VOID_POINTER p;
33460786Sps
33560786Sps	p = (VOID_POINTER) calloc(count, size);
33660786Sps	if (p != NULL)
33760786Sps		return (p);
33860786Sps	error("Cannot allocate memory", NULL_PARG);
33960786Sps	quit(QUIT_ERROR);
34060786Sps	/*NOTREACHED*/
341128348Stjr	return (NULL);
34260786Sps}
34360786Sps
34460786Sps/*
34560786Sps * Skip leading spaces in a string.
34660786Sps */
34760786Sps	public char *
34860786Spsskipsp(s)
349330571Sdelphij	char *s;
35060786Sps{
35160786Sps	while (*s == ' ' || *s == '\t')
35260786Sps		s++;
35360786Sps	return (s);
35460786Sps}
35560786Sps
35660786Sps/*
35760786Sps * See how many characters of two strings are identical.
35860786Sps * If uppercase is true, the first string must begin with an uppercase
35960786Sps * character; the remainder of the first string may be either case.
36060786Sps */
36160786Sps	public int
36260786Spssprefix(ps, s, uppercase)
36360786Sps	char *ps;
36460786Sps	char *s;
36560786Sps	int uppercase;
36660786Sps{
367330571Sdelphij	int c;
368330571Sdelphij	int sc;
369330571Sdelphij	int len = 0;
37060786Sps
37160786Sps	for ( ;  *s != '\0';  s++, ps++)
37260786Sps	{
37360786Sps		c = *ps;
37460786Sps		if (uppercase)
37560786Sps		{
376161478Sdelphij			if (len == 0 && ASCII_IS_LOWER(c))
37760786Sps				return (-1);
378161478Sdelphij			if (ASCII_IS_UPPER(c))
379161478Sdelphij				c = ASCII_TO_LOWER(c);
38060786Sps		}
38160786Sps		sc = *s;
382161478Sdelphij		if (len > 0 && ASCII_IS_UPPER(sc))
383161478Sdelphij			sc = ASCII_TO_LOWER(sc);
38460786Sps		if (c != sc)
38560786Sps			break;
38660786Sps		len++;
38760786Sps	}
38860786Sps	return (len);
38960786Sps}
39060786Sps
39160786Sps/*
39260786Sps * Exit the program.
39360786Sps */
39460786Sps	public void
39560786Spsquit(status)
39660786Sps	int status;
39760786Sps{
39860786Sps	static int save_status;
39960786Sps
40060786Sps	/*
40160786Sps	 * Put cursor at bottom left corner, clear the line,
40260786Sps	 * reset the terminal modes, and exit.
40360786Sps	 */
40460786Sps	if (status < 0)
40560786Sps		status = save_status;
40660786Sps	else
40760786Sps		save_status = status;
40860786Sps	quitting = 1;
40960786Sps	edit((char*)NULL);
410161478Sdelphij	save_cmdhist();
41160786Sps	if (any_display && is_tty)
41260786Sps		clear_bot();
41360786Sps	deinit();
41460786Sps	flush();
41560786Sps	raw_mode(0);
41660786Sps#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
41760786Sps	/*
41860786Sps	 * If we don't close 2, we get some garbage from
41960786Sps	 * 2's buffer when it flushes automatically.
42060786Sps	 * I cannot track this one down  RB
42160786Sps	 * The same bug shows up if we use ^C^C to abort.
42260786Sps	 */
42360786Sps	close(2);
42460786Sps#endif
425221715Sdelphij#ifdef WIN32
42689022Sps	SetConsoleTitle(consoleTitle);
42789022Sps#endif
42860786Sps	close_getchr();
42960786Sps	exit(status);
43060786Sps}
431