1 /*
2  * Entry point, initialization, miscellaneous routines.
3  */
4 
5 #include "less.h"
6 #include "position.h"
7 
8 public int	ispipe;
9 public char *	first_cmd;
10 public char *	every_first_cmd;
11 public int	new_file;
12 public int	is_tty;
13 public char 	*current_file;
14 public char 	*previous_file;
15 public POSITION	prev_pos;
16 public int	any_display;
17 public int	scroll;
18 public int	ac;
19 public char **	av;
20 public int 	curr_ac;
21 public int	quitting;
22 
23 extern int	file;
24 extern int	quit_at_eof;
25 extern int	cbufs;
26 extern int	errmsgs;
27 
28 #if LOGFILE
29 public int	logfile = -1;
30 public int	force_logfile = 0;
31 public char *	namelogfile = NULL;
32 #endif
33 
34 #if EDITOR
35 public char *	editor;
36 #endif
37 
38 #if TAGS
39 extern char *	tagfile;
40 extern char *	tagpattern;
41 extern int	tagoption;
42 #endif
43 
44 
45 /*
46  * Edit a new file.
47  * Filename "-" means standard input.
48  * No filename means the "current" file, from the command line.
49  */
50 	public void
edit(filename)51 edit(filename)
52 	register char *filename;
53 {
54 	register int f;
55 	register char *m;
56 	POSITION initial_pos;
57 	char message[100];
58 	static int didpipe;
59 
60 	initial_pos = NULL_POSITION;
61 	if (filename == NULL || *filename == '\0')
62 	{
63 		if (curr_ac >= ac)
64 		{
65 			error("No current file");
66 			return;
67 		}
68 		filename = save(av[curr_ac]);
69 	} else if (strcmp(filename, "#") == 0)
70 	{
71 		if (*previous_file == '\0')
72 		{
73 			error("no previous file");
74 			return;
75 		}
76 		filename = save(previous_file);
77 		initial_pos = prev_pos;
78 	} else
79 		filename = save(filename);
80 
81 	if (strcmp(filename, "-") == 0)
82 	{
83 		/*
84 		 * Use standard input.
85 		 */
86 		if (didpipe)
87 		{
88 			error("Can view standard input only once");
89 			return;
90 		}
91 		f = 0;
92 	} else if ((m = bad_file(filename, message, sizeof(message))) != NULL)
93 	{
94 		error(m);
95 		free(filename);
96 		return;
97 	} else if ((f = open(filename, 0)) < 0)
98 	{
99 		error(errno_message(filename, message, sizeof(message)));
100 		free(filename);
101 		return;
102 	}
103 
104 	if (isatty(f))
105 	{
106 		/*
107 		 * Not really necessary to call this an error,
108 		 * but if the control terminal (for commands)
109 		 * and the input file (for data) are the same,
110 		 * we get weird results at best.
111 		 */
112 		error("Can't take input from a terminal");
113 		if (f > 0)
114 			close(f);
115 		free(filename);
116 		return;
117 	}
118 
119 #if LOGFILE
120 	if (f == 0 && namelogfile != NULL && is_tty)
121 		use_logfile();
122 #endif
123 
124 	/*
125 	 * We are now committed to using the new file.
126 	 * Close the current input file and set up to use the new one.
127 	 */
128 	if (file > 0)
129 		close(file);
130 	new_file = 1;
131 	if (previous_file != NULL)
132 		free(previous_file);
133 	previous_file = current_file;
134 	current_file = filename;
135 	prev_pos = position(TOP);
136 	ispipe = (f == 0);
137 	if (ispipe)
138 		didpipe = 1;
139 	file = f;
140 	ch_init(cbufs, 0);
141 	init_mark();
142 
143 	if (every_first_cmd != NULL)
144 		first_cmd = every_first_cmd;
145 
146 	if (is_tty)
147 	{
148 		int no_display = !any_display;
149 		any_display = 1;
150 		if (no_display && errmsgs > 0)
151 		{
152 			/*
153 			 * We displayed some messages on error output
154 			 * (file descriptor 2; see error() function).
155 			 * Before erasing the screen contents,
156 			 * display the file name and wait for a keystroke.
157 			 */
158 			error(filename);
159 		}
160 		/*
161 		 * Indicate there is nothing displayed yet.
162 		 */
163 		pos_clear();
164 		if (initial_pos != NULL_POSITION)
165 			jump_loc(initial_pos);
166 		clr_linenum();
167 	}
168 }
169 
170 /*
171  * Edit the next file in the command line list.
172  */
173 	public void
next_file(n)174 next_file(n)
175 	int n;
176 {
177 	if (curr_ac + n >= ac)
178 	{
179 		if (quit_at_eof)
180 			quit();
181 		error("No (N-th) next file");
182 	} else
183 		edit(av[curr_ac += n]);
184 }
185 
186 /*
187  * Edit the previous file in the command line list.
188  */
189 	public void
prev_file(n)190 prev_file(n)
191 	int n;
192 {
193 	if (curr_ac - n < 0)
194 		error("No (N-th) previous file");
195 	else
196 		edit(av[curr_ac -= n]);
197 }
198 
199 /*
200  * Copy a file directly to standard output.
201  * Used if standard output is not a tty.
202  */
203 	static void
cat_file()204 cat_file()
205 {
206 	register int c;
207 
208 	while ((c = ch_forw_get()) != EOI)
209 		putchr(c);
210 	flush();
211 }
212 
213 #if LOGFILE
214 
use_logfile()215 use_logfile()
216 {
217 	int exists;
218 	int answer;
219 	char message[100];
220 
221 	/*
222 	 * If he asked for a log file and we have opened standard input,
223 	 * create the log file.
224 	 * We take care not to blindly overwrite an existing file.
225 	 */
226 	end_logfile();
227 
228 	/*
229 	 * {{ We could use access() here. }}
230 	 */
231 	exists = open(namelogfile, 0);
232 	close(exists);
233 	exists = (exists >= 0);
234 
235 	if (exists && !force_logfile)
236 	{
237 		static char w[] = "WARNING: log file exists: ";
238 		strcpy(message, w);
239 		strtcpy(message+sizeof(w)-1, namelogfile,
240 			sizeof(message)-sizeof(w));
241 		error(message);
242 		answer = 'X';	/* Ask the user what to do */
243 	} else
244 		answer = 'O';	/* Create the log file */
245 
246 loop:
247 	switch (answer)
248 	{
249 	case 'O': case 'o':
250 		logfile = creat(namelogfile, 0644);
251 		break;
252 	case 'A': case 'a':
253 		logfile = open(namelogfile, 1);
254 		if (lseek(logfile, (offset_t)0, 2) < 0)
255 		{
256 			close(logfile);
257 			logfile = -1;
258 		}
259 		break;
260 	case 'D': case 'd':
261 		answer = 0;	/* Don't print an error message */
262 		break;
263 	case 'q':
264 		quit();
265 	default:
266 		putstr("\n  Overwrite, Append, or Don't log? ");
267 		answer = getchr();
268 		putstr("\n");
269 		flush();
270 		goto loop;
271 	}
272 
273 	if (logfile < 0 && answer != 0)
274 	{
275 		sprintf(message, "Cannot write to \"%s\"",
276 			namelogfile);
277 		error(message);
278 	}
279 }
280 
281 #endif
282 
283 /*
284  * Entry point.
285  */
main(argc,argv)286 main(argc, argv)
287 	int argc;
288 	char *argv[];
289 {
290 	char *getenv();
291 
292 
293 	/*
294 	 * Process command line arguments and LESS environment arguments.
295 	 * Command line arguments override environment arguments.
296 	 */
297 	init_prompt();
298 	init_option();
299 	scan_option(getenv("LESS"));
300 	argv++;
301 	while ( (--argc > 0) &&
302 		(argv[0][0] == '-' || argv[0][0] == '+') &&
303 		argv[0][1] != '\0')
304 		scan_option(*argv++);
305 
306 #if EDITOR
307 	editor = getenv("EDITOR");
308 	if (editor == NULL || *editor == '\0')
309 		editor = EDIT_PGM;
310 #endif
311 
312 	/*
313 	 * Set up list of files to be examined.
314 	 */
315 	ac = argc;
316 	av = argv;
317 	curr_ac = 0;
318 
319 	/*
320 	 * Set up terminal, etc.
321 	 */
322 	is_tty = isatty(1);
323 	if (!is_tty)
324 	{
325 		/*
326 		 * Output is not a tty.
327 		 * Just copy the input file(s) to output.
328 		 */
329 		if (ac < 1)
330 		{
331 			edit("-");
332 			cat_file();
333 		} else
334 		{
335 			do
336 			{
337 				edit((char *)NULL);
338 				if (file >= 0)
339 					cat_file();
340 			} while (++curr_ac < ac);
341 		}
342 		exit(0);
343 	}
344 
345 	raw_mode(1);
346 	get_term();
347 	open_getchr();
348 	init();
349 	init_cmd();
350 
351 	init_signals(1);
352 
353 	/*
354 	 * Select the first file to examine.
355 	 */
356 #if TAGS
357 	if (tagoption)
358 	{
359 		/*
360 		 * A -t option was given.
361 		 * Verify that no filenames were also given.
362 		 * Edit the file selected by the "tags" search,
363 		 * and search for the proper line in the file.
364 		 */
365 		if (ac > 0)
366 		{
367 			error("No filenames allowed with -t option");
368 			quit();
369 		}
370 		if (tagfile == NULL)
371 			quit();
372 		edit(tagfile);
373 		if (file < 0)
374 			quit();
375 		if (tagsearch())
376 			quit();
377 	} else
378 #endif
379 	if (ac < 1)
380 		edit("-");	/* Standard input */
381 	else
382 	{
383 		/*
384 		 * Try all the files named as command arguments.
385 		 * We are simply looking for one which can be
386 		 * opened without error.
387 		 */
388 		do
389 		{
390 			edit((char *)NULL);
391 		} while (file < 0 && ++curr_ac < ac);
392 	}
393 
394 	if (file >= 0)
395 		commands();
396 	quit();
397 	/*NOTREACHED*/
398 }
399 
400 /*
401  * Copy a string, truncating to the specified length if necessary.
402  * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
403  */
404 	public void
strtcpy(to,from,len)405 strtcpy(to, from, len)
406 	char *to;
407 	char *from;
408 	unsigned int len;
409 {
410 	strncpy(to, from, len);
411 	to[len-1] = '\0';
412 }
413 
414 /*
415  * Copy a string to a "safe" place
416  * (that is, to a buffer allocated by calloc).
417  */
418 	public char *
save(s)419 save(s)
420 	char *s;
421 {
422 	register char *p;
423 
424 	p = calloc(strlen(s)+1, sizeof(char));
425 	if (p == NULL)
426 	{
427 		error("cannot allocate memory");
428 		quit();
429 	}
430 	strcpy(p, s);
431 	return (p);
432 }
433 
434 /*
435  * Exit the program.
436  */
437 	public void
quit()438 quit()
439 {
440 	/*
441 	 * Put cursor at bottom left corner, clear the line,
442 	 * reset the terminal modes, and exit.
443 	 */
444 	quitting = 1;
445 #if LOGFILE
446 	end_logfile();
447 #endif
448 	lower_left();
449 	clear_eol();
450 	deinit();
451 	flush();
452 	raw_mode(0);
453 	exit(0);
454 }
455