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