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