xref: /freebsd/contrib/less/edit.c (revision f80a33ea)
1a5f0fb15SPaul Saab /*
2d713e089SXin LI  * Copyright (C) 1984-2023  Mark Nudelman
3a5f0fb15SPaul Saab  *
4a5f0fb15SPaul Saab  * You may distribute under the terms of either the GNU General Public
5a5f0fb15SPaul Saab  * License or the Less License, as specified in the README file.
6a5f0fb15SPaul Saab  *
796e55cc7SXin LI  * For more information, see the README file.
8a5f0fb15SPaul Saab  */
9a5f0fb15SPaul Saab 
10a5f0fb15SPaul Saab 
11a5f0fb15SPaul Saab #include "less.h"
12b2ea2440SXin LI #include "position.h"
13464501a8SXin LI #if HAVE_STAT
14464501a8SXin LI #include <sys/stat.h>
15464501a8SXin LI #endif
16d713e089SXin LI #if HAVE_SYS_WAIT_H
17d713e089SXin LI #include <sys/wait.h>
18b2ea2440SXin LI #endif
19d713e089SXin LI #include <signal.h>
20a5f0fb15SPaul Saab 
21a5f0fb15SPaul Saab public int fd0 = 0;
22a5f0fb15SPaul Saab 
23a5f0fb15SPaul Saab extern int new_file;
24a5f0fb15SPaul Saab extern int cbufs;
25a5f0fb15SPaul Saab extern char *every_first_cmd;
26a5f0fb15SPaul Saab extern int force_open;
27a5f0fb15SPaul Saab extern int is_tty;
28a5f0fb15SPaul Saab extern int sigs;
292235c7feSXin LI extern int hshift;
3030a1828cSXin LI extern int want_filesize;
3195270f73SXin LI extern int consecutive_nulls;
32d713e089SXin LI extern int modelines;
33d713e089SXin LI extern int show_preproc_error;
34a5f0fb15SPaul Saab extern IFILE curr_ifile;
35a5f0fb15SPaul Saab extern IFILE old_ifile;
36a5f0fb15SPaul Saab extern struct scrpos initial_scrpos;
37f6b74a7dSXin LI extern void *ml_examine;
38a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
39a5f0fb15SPaul Saab extern char openquote;
40a5f0fb15SPaul Saab extern char closequote;
41a5f0fb15SPaul Saab #endif
42a5f0fb15SPaul Saab 
43a5f0fb15SPaul Saab #if LOGFILE
44a5f0fb15SPaul Saab extern int logfile;
45a5f0fb15SPaul Saab extern int force_logfile;
46a5f0fb15SPaul Saab extern char *namelogfile;
47a5f0fb15SPaul Saab #endif
48a5f0fb15SPaul Saab 
49464501a8SXin LI #if HAVE_STAT_INO
50464501a8SXin LI public dev_t curr_dev;
51464501a8SXin LI public ino_t curr_ino;
52464501a8SXin LI #endif
53464501a8SXin LI 
54a5f0fb15SPaul Saab /*
55a5f0fb15SPaul Saab  * Textlist functions deal with a list of words separated by spaces.
56a5f0fb15SPaul Saab  * init_textlist sets up a textlist structure.
57a5f0fb15SPaul Saab  * forw_textlist uses that structure to iterate thru the list of
58a5f0fb15SPaul Saab  * words, returning each one as a standard null-terminated string.
59a5f0fb15SPaul Saab  * back_textlist does the same, but runs thru the list backwards.
60a5f0fb15SPaul Saab  */
init_textlist(struct textlist * tlist,char * str)61d713e089SXin LI public void init_textlist(struct textlist *tlist, char *str)
62a5f0fb15SPaul Saab {
63a5f0fb15SPaul Saab 	char *s;
64a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
65000ba3e8STim J. Robbins 	int meta_quoted = 0;
66000ba3e8STim J. Robbins 	int delim_quoted = 0;
67000ba3e8STim J. Robbins 	char *esc = get_meta_escape();
68a15691bfSXin LI 	int esclen = (int) strlen(esc);
69a5f0fb15SPaul Saab #endif
70a5f0fb15SPaul Saab 
71a5f0fb15SPaul Saab 	tlist->string = skipsp(str);
72a5f0fb15SPaul Saab 	tlist->endstring = tlist->string + strlen(tlist->string);
73a5f0fb15SPaul Saab 	for (s = str;  s < tlist->endstring;  s++)
74a5f0fb15SPaul Saab 	{
75a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
76000ba3e8STim J. Robbins 		if (meta_quoted)
77000ba3e8STim J. Robbins 		{
78000ba3e8STim J. Robbins 			meta_quoted = 0;
79000ba3e8STim J. Robbins 		} else if (esclen > 0 && s + esclen < tlist->endstring &&
80000ba3e8STim J. Robbins 		           strncmp(s, esc, esclen) == 0)
81000ba3e8STim J. Robbins 		{
82000ba3e8STim J. Robbins 			meta_quoted = 1;
83000ba3e8STim J. Robbins 			s += esclen - 1;
84000ba3e8STim J. Robbins 		} else if (delim_quoted)
85000ba3e8STim J. Robbins 		{
86000ba3e8STim J. Robbins 			if (*s == closequote)
87000ba3e8STim J. Robbins 				delim_quoted = 0;
88000ba3e8STim J. Robbins 		} else /* (!delim_quoted) */
89000ba3e8STim J. Robbins 		{
90000ba3e8STim J. Robbins 			if (*s == openquote)
91000ba3e8STim J. Robbins 				delim_quoted = 1;
92000ba3e8STim J. Robbins 			else if (*s == ' ')
93a5f0fb15SPaul Saab 				*s = '\0';
94000ba3e8STim J. Robbins 		}
95a5f0fb15SPaul Saab #else
96a5f0fb15SPaul Saab 		if (*s == ' ')
97a5f0fb15SPaul Saab 			*s = '\0';
98a5f0fb15SPaul Saab #endif
99a5f0fb15SPaul Saab 	}
100a5f0fb15SPaul Saab }
101a5f0fb15SPaul Saab 
forw_textlist(struct textlist * tlist,char * prev)102d713e089SXin LI public char * forw_textlist(struct textlist *tlist, char *prev)
103a5f0fb15SPaul Saab {
104a5f0fb15SPaul Saab 	char *s;
105a5f0fb15SPaul Saab 
106a5f0fb15SPaul Saab 	/*
107a5f0fb15SPaul Saab 	 * prev == NULL means return the first word in the list.
108a5f0fb15SPaul Saab 	 * Otherwise, return the word after "prev".
109a5f0fb15SPaul Saab 	 */
110a5f0fb15SPaul Saab 	if (prev == NULL)
111a5f0fb15SPaul Saab 		s = tlist->string;
112a5f0fb15SPaul Saab 	else
113a5f0fb15SPaul Saab 		s = prev + strlen(prev);
114a5f0fb15SPaul Saab 	if (s >= tlist->endstring)
115a5f0fb15SPaul Saab 		return (NULL);
116a5f0fb15SPaul Saab 	while (*s == '\0')
117a5f0fb15SPaul Saab 		s++;
118a5f0fb15SPaul Saab 	if (s >= tlist->endstring)
119a5f0fb15SPaul Saab 		return (NULL);
120a5f0fb15SPaul Saab 	return (s);
121a5f0fb15SPaul Saab }
122a5f0fb15SPaul Saab 
back_textlist(struct textlist * tlist,char * prev)123d713e089SXin LI public char * back_textlist(struct textlist *tlist, char *prev)
124a5f0fb15SPaul Saab {
125a5f0fb15SPaul Saab 	char *s;
126a5f0fb15SPaul Saab 
127a5f0fb15SPaul Saab 	/*
128a5f0fb15SPaul Saab 	 * prev == NULL means return the last word in the list.
129a5f0fb15SPaul Saab 	 * Otherwise, return the word before "prev".
130a5f0fb15SPaul Saab 	 */
131a5f0fb15SPaul Saab 	if (prev == NULL)
132a5f0fb15SPaul Saab 		s = tlist->endstring;
133a5f0fb15SPaul Saab 	else if (prev <= tlist->string)
134a5f0fb15SPaul Saab 		return (NULL);
135a5f0fb15SPaul Saab 	else
136a5f0fb15SPaul Saab 		s = prev - 1;
137a5f0fb15SPaul Saab 	while (*s == '\0')
138a5f0fb15SPaul Saab 		s--;
139a5f0fb15SPaul Saab 	if (s <= tlist->string)
140a5f0fb15SPaul Saab 		return (NULL);
141a5f0fb15SPaul Saab 	while (s[-1] != '\0' && s > tlist->string)
142a5f0fb15SPaul Saab 		s--;
143a5f0fb15SPaul Saab 	return (s);
144a5f0fb15SPaul Saab }
145a5f0fb15SPaul Saab 
146a5f0fb15SPaul Saab /*
147d713e089SXin LI  * Parse a single option setting in a modeline.
148d713e089SXin LI  */
modeline_option(char * str,int opt_len)149d713e089SXin LI static void modeline_option(char *str, int opt_len)
150d713e089SXin LI {
151d713e089SXin LI 	struct mloption { char *opt_name; void (*opt_func)(char*,int); };
152d713e089SXin LI 	struct mloption options[] = {
153d713e089SXin LI 		{ "ts=",         set_tabs },
154d713e089SXin LI 		{ "tabstop=",    set_tabs },
155d713e089SXin LI 		{ NULL, NULL }
156d713e089SXin LI 	};
157d713e089SXin LI 	struct mloption *opt;
158d713e089SXin LI 	for (opt = options;  opt->opt_name != NULL;  opt++)
159d713e089SXin LI 	{
160d713e089SXin LI 		int name_len = strlen(opt->opt_name);
161d713e089SXin LI 		if (opt_len > name_len && strncmp(str, opt->opt_name, name_len) == 0)
162d713e089SXin LI 		{
163d713e089SXin LI 			(*opt->opt_func)(str + name_len, opt_len - name_len);
164d713e089SXin LI 			break;
165d713e089SXin LI 		}
166d713e089SXin LI 	}
167d713e089SXin LI }
168d713e089SXin LI 
169d713e089SXin LI /*
170d713e089SXin LI  * String length, terminated by option separator (space or colon).
171d713e089SXin LI  * Space/colon can be escaped with backspace.
172d713e089SXin LI  */
modeline_option_len(char * str)173d713e089SXin LI static int modeline_option_len(char *str)
174d713e089SXin LI {
175d713e089SXin LI 	int esc = FALSE;
176d713e089SXin LI 	char *s;
177d713e089SXin LI 	for (s = str;  *s != '\0';  s++)
178d713e089SXin LI 	{
179d713e089SXin LI 		if (esc)
180d713e089SXin LI 			esc = FALSE;
181d713e089SXin LI 		else if (*s == '\\')
182d713e089SXin LI 			esc = TRUE;
183d713e089SXin LI 		else if (*s == ' ' || *s == ':') /* separator */
184d713e089SXin LI 			break;
185d713e089SXin LI 	}
186d713e089SXin LI 	return (s - str);
187d713e089SXin LI }
188d713e089SXin LI 
189d713e089SXin LI /*
190d713e089SXin LI  * Parse colon- or space-separated option settings in a modeline.
191d713e089SXin LI  */
modeline_options(char * str,char end_char)192d713e089SXin LI static void modeline_options(char *str, char end_char)
193d713e089SXin LI {
194d713e089SXin LI 	for (;;)
195d713e089SXin LI 	{
196d713e089SXin LI 		int opt_len;
197d713e089SXin LI 		str = skipsp(str);
198d713e089SXin LI 		if (*str == '\0' || *str == end_char)
199d713e089SXin LI 			break;
200d713e089SXin LI 		opt_len = modeline_option_len(str);
201d713e089SXin LI 		modeline_option(str, opt_len);
202d713e089SXin LI 		str += opt_len;
203d713e089SXin LI 		if (*str != '\0')
204d713e089SXin LI 			str += 1; /* skip past the separator */
205d713e089SXin LI 	}
206d713e089SXin LI }
207d713e089SXin LI 
208d713e089SXin LI /*
209d713e089SXin LI  * See if there is a modeline string in a line.
210d713e089SXin LI  */
check_modeline(char * line)211d713e089SXin LI static void check_modeline(char *line)
212d713e089SXin LI {
213d713e089SXin LI #if HAVE_STRSTR
214d713e089SXin LI 	static char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL };
215d713e089SXin LI 	char **pgm;
216d713e089SXin LI 	for (pgm = pgms;  *pgm != NULL;  ++pgm)
217d713e089SXin LI 	{
218d713e089SXin LI 		char *pline = line;
219d713e089SXin LI 		for (;;)
220d713e089SXin LI 		{
221d713e089SXin LI 			char *str;
222d713e089SXin LI 			pline = strstr(pline, *pgm);
223d713e089SXin LI 			if (pline == NULL) /* pgm is not in this line */
224d713e089SXin LI 				break;
225d713e089SXin LI 			str = skipsp(pline + strlen(*pgm));
226d713e089SXin LI 			if (pline == line || pline[-1] == ' ')
227d713e089SXin LI 			{
228d713e089SXin LI 				if (strncmp(str, "set ", 4) == 0)
229d713e089SXin LI 					modeline_options(str+4, ':');
230d713e089SXin LI 				else if (pgm != &pgms[0]) /* "less:" requires "set" */
231d713e089SXin LI 					modeline_options(str, '\0');
232d713e089SXin LI 				break;
233d713e089SXin LI 			}
234d713e089SXin LI 			/* Continue searching the rest of the line. */
235d713e089SXin LI 			pline = str;
236d713e089SXin LI 		}
237d713e089SXin LI 	}
238d713e089SXin LI #endif /* HAVE_STRSTR */
239d713e089SXin LI }
240d713e089SXin LI 
241d713e089SXin LI /*
242d713e089SXin LI  * Read lines from start of file and check if any are modelines.
243d713e089SXin LI  */
check_modelines(void)244d713e089SXin LI static void check_modelines(void)
245d713e089SXin LI {
246d713e089SXin LI 	POSITION pos = ch_zero();
247d713e089SXin LI 	int i;
248d713e089SXin LI 	for (i = 0;  i < modelines;  i++)
249d713e089SXin LI 	{
250d713e089SXin LI 		char *line;
251d713e089SXin LI 		int line_len;
252d713e089SXin LI 		if (ABORT_SIGS())
253d713e089SXin LI 			return;
254d713e089SXin LI 		pos = forw_raw_line(pos, &line, &line_len);
255d713e089SXin LI 		if (pos == NULL_POSITION)
256d713e089SXin LI 			break;
257d713e089SXin LI 		check_modeline(line);
258d713e089SXin LI 	}
259d713e089SXin LI }
260d713e089SXin LI 
261d713e089SXin LI /*
262b2ea2440SXin LI  * Close a pipe opened via popen.
263b2ea2440SXin LI  */
close_pipe(FILE * pipefd)264d713e089SXin LI static void close_pipe(FILE *pipefd)
265b2ea2440SXin LI {
266d713e089SXin LI 	int status;
267d713e089SXin LI 	PARG parg;
268d713e089SXin LI 
269b2ea2440SXin LI 	if (pipefd == NULL)
270b2ea2440SXin LI 		return;
271b2ea2440SXin LI #if OS2
272b2ea2440SXin LI 	/*
273b2ea2440SXin LI 	 * The pclose function of OS/2 emx sometimes fails.
274b2ea2440SXin LI 	 * Send SIGINT to the piped process before closing it.
275b2ea2440SXin LI 	 */
276b2ea2440SXin LI 	kill(pipefd->_pid, SIGINT);
277b2ea2440SXin LI #endif
278d713e089SXin LI 	status = pclose(pipefd);
279d713e089SXin LI 	if (status == -1)
280d713e089SXin LI 	{
281d713e089SXin LI 		/* An internal error in 'less', not a preprocessor error.  */
282d713e089SXin LI 		parg.p_string = errno_message("pclose");
283d713e089SXin LI 		error("%s", &parg);
284d713e089SXin LI 		free(parg.p_string);
285d713e089SXin LI 		return;
286d713e089SXin LI 	}
287d713e089SXin LI 	if (!show_preproc_error)
288d713e089SXin LI 		return;
289d713e089SXin LI #if defined WIFEXITED && defined WEXITSTATUS
290d713e089SXin LI 	if (WIFEXITED(status))
291d713e089SXin LI 	{
292d713e089SXin LI 		int s = WEXITSTATUS(status);
293d713e089SXin LI 		if (s != 0)
294d713e089SXin LI 		{
295d713e089SXin LI 			parg.p_int = s;
296d713e089SXin LI 			error("Input preprocessor failed (status %d)", &parg);
297d713e089SXin LI 		}
298d713e089SXin LI 		return;
299d713e089SXin LI 	}
300d713e089SXin LI #endif
301d713e089SXin LI #if defined WIFSIGNALED && defined WTERMSIG && HAVE_STRSIGNAL
302d713e089SXin LI 	if (WIFSIGNALED(status))
303d713e089SXin LI 	{
304d713e089SXin LI 		int sig = WTERMSIG(status);
305d713e089SXin LI 		if (sig != SIGPIPE || ch_length() != NULL_POSITION)
306d713e089SXin LI 		{
307d713e089SXin LI 			parg.p_string = signal_message(sig);
308d713e089SXin LI 			error("Input preprocessor terminated: %s", &parg);
309d713e089SXin LI 		}
310d713e089SXin LI 		return;
311d713e089SXin LI 	}
312d713e089SXin LI #endif
313d713e089SXin LI 	if (status != 0)
314d713e089SXin LI 	{
315d713e089SXin LI 		parg.p_int = status;
316d713e089SXin LI 		error("Input preprocessor exited with status %x", &parg);
317d713e089SXin LI 	}
318d713e089SXin LI }
319d713e089SXin LI 
320d713e089SXin LI /*
321d713e089SXin LI  * Drain and close an input pipe if needed.
322d713e089SXin LI  */
close_altpipe(IFILE ifile)323d713e089SXin LI public void close_altpipe(IFILE ifile)
324d713e089SXin LI {
325d713e089SXin LI 	FILE *altpipe = get_altpipe(ifile);
326d713e089SXin LI 	if (altpipe != NULL && !(ch_getflags() & CH_KEEPOPEN))
327d713e089SXin LI 	{
328d713e089SXin LI 		close_pipe(altpipe);
329d713e089SXin LI 		set_altpipe(ifile, NULL);
330d713e089SXin LI 	}
331d713e089SXin LI }
332d713e089SXin LI 
333d713e089SXin LI /*
334d713e089SXin LI  * Check for error status from the current altpipe.
335d713e089SXin LI  * May or may not close the pipe.
336d713e089SXin LI  */
check_altpipe_error(void)337d713e089SXin LI public void check_altpipe_error(void)
338d713e089SXin LI {
339d713e089SXin LI 	if (!show_preproc_error)
340d713e089SXin LI 		return;
341d713e089SXin LI 	if (curr_ifile != NULL_IFILE && get_altfilename(curr_ifile) != NULL)
342d713e089SXin LI 		close_altpipe(curr_ifile);
343b2ea2440SXin LI }
344b2ea2440SXin LI 
345b2ea2440SXin LI /*
346a5f0fb15SPaul Saab  * Close the current input file.
347a5f0fb15SPaul Saab  */
close_file(void)348d713e089SXin LI static void close_file(void)
349a5f0fb15SPaul Saab {
350a5f0fb15SPaul Saab 	struct scrpos scrpos;
351b2ea2440SXin LI 	char *altfilename;
352a5f0fb15SPaul Saab 
353a5f0fb15SPaul Saab 	if (curr_ifile == NULL_IFILE)
354a5f0fb15SPaul Saab 		return;
355a5f0fb15SPaul Saab 
356a5f0fb15SPaul Saab 	/*
357a5f0fb15SPaul Saab 	 * Save the current position so that we can return to
358a5f0fb15SPaul Saab 	 * the same position if we edit this file again.
359a5f0fb15SPaul Saab 	 */
360b2ea2440SXin LI 	get_scrpos(&scrpos, TOP);
361a5f0fb15SPaul Saab 	if (scrpos.pos != NULL_POSITION)
362a5f0fb15SPaul Saab 	{
363a5f0fb15SPaul Saab 		store_pos(curr_ifile, &scrpos);
364a5f0fb15SPaul Saab 		lastmark();
365a5f0fb15SPaul Saab 	}
366a5f0fb15SPaul Saab 	/*
367a5f0fb15SPaul Saab 	 * Close the file descriptor, unless it is a pipe.
368a5f0fb15SPaul Saab 	 */
369a5f0fb15SPaul Saab 	ch_close();
370a5f0fb15SPaul Saab 	/*
371a5f0fb15SPaul Saab 	 * If we opened a file using an alternate name,
372a5f0fb15SPaul Saab 	 * do special stuff to close it.
373a5f0fb15SPaul Saab 	 */
374b2ea2440SXin LI 	altfilename = get_altfilename(curr_ifile);
375b2ea2440SXin LI 	if (altfilename != NULL)
376a5f0fb15SPaul Saab 	{
377d713e089SXin LI 		close_altpipe(curr_ifile);
378b2ea2440SXin LI 		close_altfile(altfilename, get_filename(curr_ifile));
379b2ea2440SXin LI 		set_altfilename(curr_ifile, NULL);
380a5f0fb15SPaul Saab 	}
381a5f0fb15SPaul Saab 	curr_ifile = NULL_IFILE;
382464501a8SXin LI #if HAVE_STAT_INO
383464501a8SXin LI 	curr_ino = curr_dev = 0;
384464501a8SXin LI #endif
385a5f0fb15SPaul Saab }
386a5f0fb15SPaul Saab 
387a5f0fb15SPaul Saab /*
388a5f0fb15SPaul Saab  * Edit a new file (given its name).
389a5f0fb15SPaul Saab  * Filename == "-" means standard input.
390a5f0fb15SPaul Saab  * Filename == NULL means just close the current file.
391a5f0fb15SPaul Saab  */
edit(char * filename)392d713e089SXin LI public int edit(char *filename)
393a5f0fb15SPaul Saab {
394a5f0fb15SPaul Saab 	if (filename == NULL)
395a5f0fb15SPaul Saab 		return (edit_ifile(NULL_IFILE));
396a5f0fb15SPaul Saab 	return (edit_ifile(get_ifile(filename, curr_ifile)));
397a5f0fb15SPaul Saab }
398a5f0fb15SPaul Saab 
399a5f0fb15SPaul Saab /*
400d713e089SXin LI  * Clean up what edit_ifile did before error return.
401d713e089SXin LI  */
edit_error(char * filename,char * alt_filename,void * altpipe,IFILE ifile,IFILE was_curr_ifile)402d713e089SXin LI static int edit_error(char *filename, char *alt_filename, void *altpipe, IFILE ifile, IFILE was_curr_ifile)
403d713e089SXin LI {
404d713e089SXin LI 	if (alt_filename != NULL)
405d713e089SXin LI 	{
406d713e089SXin LI 		close_pipe(altpipe);
407d713e089SXin LI 		close_altfile(alt_filename, filename);
408d713e089SXin LI 		free(alt_filename);
409d713e089SXin LI 	}
410d713e089SXin LI 	del_ifile(ifile);
411d713e089SXin LI 	free(filename);
412d713e089SXin LI 	/*
413d713e089SXin LI 	 * Re-open the current file.
414d713e089SXin LI 	 */
415d713e089SXin LI 	if (was_curr_ifile == ifile)
416d713e089SXin LI 	{
417d713e089SXin LI 		/*
418d713e089SXin LI 		 * Whoops.  The "current" ifile is the one we just deleted.
419d713e089SXin LI 		 * Just give up.
420d713e089SXin LI 		 */
421d713e089SXin LI 		quit(QUIT_ERROR);
422d713e089SXin LI 	}
423d713e089SXin LI 	reedit_ifile(was_curr_ifile);
424d713e089SXin LI 	return (1);
425d713e089SXin LI }
426d713e089SXin LI 
427d713e089SXin LI /*
428a5f0fb15SPaul Saab  * Edit a new file (given its IFILE).
429a5f0fb15SPaul Saab  * ifile == NULL means just close the current file.
430a5f0fb15SPaul Saab  */
edit_ifile(IFILE ifile)431d713e089SXin LI public int edit_ifile(IFILE ifile)
432a5f0fb15SPaul Saab {
433a5f0fb15SPaul Saab 	int f;
434a5f0fb15SPaul Saab 	int answer;
435a5f0fb15SPaul Saab 	int chflags;
436a5f0fb15SPaul Saab 	char *filename;
437a5f0fb15SPaul Saab 	char *open_filename;
438a5f0fb15SPaul Saab 	char *alt_filename;
439b2ea2440SXin LI 	void *altpipe;
440a5f0fb15SPaul Saab 	IFILE was_curr_ifile;
441a5f0fb15SPaul Saab 	PARG parg;
442a5f0fb15SPaul Saab 
443a5f0fb15SPaul Saab 	if (ifile == curr_ifile)
444a5f0fb15SPaul Saab 	{
445a5f0fb15SPaul Saab 		/*
446a5f0fb15SPaul Saab 		 * Already have the correct file open.
447a5f0fb15SPaul Saab 		 */
448a5f0fb15SPaul Saab 		return (0);
449a5f0fb15SPaul Saab 	}
450a5f0fb15SPaul Saab 
451a5f0fb15SPaul Saab 	/*
452a5f0fb15SPaul Saab 	 * We must close the currently open file now.
453a5f0fb15SPaul Saab 	 * This is necessary to make the open_altfile/close_altfile pairs
454a5f0fb15SPaul Saab 	 * nest properly (or rather to avoid nesting at all).
455a5f0fb15SPaul Saab 	 * {{ Some stupid implementations of popen() mess up if you do:
456a5f0fb15SPaul Saab 	 *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
457a5f0fb15SPaul Saab 	 */
458a5f0fb15SPaul Saab #if LOGFILE
459a5f0fb15SPaul Saab 	end_logfile();
460a5f0fb15SPaul Saab #endif
461a5f0fb15SPaul Saab 	was_curr_ifile = save_curr_ifile();
462a5f0fb15SPaul Saab 	if (curr_ifile != NULL_IFILE)
463a5f0fb15SPaul Saab 	{
464a5f0fb15SPaul Saab 		chflags = ch_getflags();
465a5f0fb15SPaul Saab 		close_file();
466a5f0fb15SPaul Saab 		if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
467a5f0fb15SPaul Saab 		{
468a5f0fb15SPaul Saab 			/*
469a5f0fb15SPaul Saab 			 * Don't keep the help file in the ifile list.
470a5f0fb15SPaul Saab 			 */
471a5f0fb15SPaul Saab 			del_ifile(was_curr_ifile);
472a5f0fb15SPaul Saab 			was_curr_ifile = old_ifile;
473a5f0fb15SPaul Saab 		}
474a5f0fb15SPaul Saab 	}
475a5f0fb15SPaul Saab 
476a5f0fb15SPaul Saab 	if (ifile == NULL_IFILE)
477a5f0fb15SPaul Saab 	{
478a5f0fb15SPaul Saab 		/*
479a5f0fb15SPaul Saab 		 * No new file to open.
480a5f0fb15SPaul Saab 		 * (Don't set old_ifile, because if you call edit_ifile(NULL),
481a5f0fb15SPaul Saab 		 *  you're supposed to have saved curr_ifile yourself,
482a5f0fb15SPaul Saab 		 *  and you'll restore it if necessary.)
483a5f0fb15SPaul Saab 		 */
484a5f0fb15SPaul Saab 		unsave_ifile(was_curr_ifile);
485a5f0fb15SPaul Saab 		return (0);
486a5f0fb15SPaul Saab 	}
487a5f0fb15SPaul Saab 
488000ba3e8STim J. Robbins 	filename = save(get_filename(ifile));
489b2ea2440SXin LI 
490a5f0fb15SPaul Saab 	/*
491a5f0fb15SPaul Saab 	 * See if LESSOPEN specifies an "alternate" file to open.
492a5f0fb15SPaul Saab 	 */
493b2ea2440SXin LI 	altpipe = get_altpipe(ifile);
494b2ea2440SXin LI 	if (altpipe != NULL)
495b2ea2440SXin LI 	{
496b2ea2440SXin LI 		/*
497b2ea2440SXin LI 		 * File is already open.
498b2ea2440SXin LI 		 * chflags and f are not used by ch_init if ifile has
499b2ea2440SXin LI 		 * filestate which should be the case if we're here.
500b2ea2440SXin LI 		 * Set them here to avoid uninitialized variable warnings.
501b2ea2440SXin LI 		 */
502b2ea2440SXin LI 		chflags = 0;
503b2ea2440SXin LI 		f = -1;
504b2ea2440SXin LI 		alt_filename = get_altfilename(ifile);
505a5f0fb15SPaul Saab 		open_filename = (alt_filename != NULL) ? alt_filename : filename;
506b2ea2440SXin LI 	} else
507b2ea2440SXin LI 	{
508b2ea2440SXin LI 		if (strcmp(filename, FAKE_HELPFILE) == 0 ||
509b2ea2440SXin LI 			 strcmp(filename, FAKE_EMPTYFILE) == 0)
510b2ea2440SXin LI 			alt_filename = NULL;
511b2ea2440SXin LI 		else
512b2ea2440SXin LI 			alt_filename = open_altfile(filename, &f, &altpipe);
513b2ea2440SXin LI 
514b2ea2440SXin LI 		open_filename = (alt_filename != NULL) ? alt_filename : filename;
515a5f0fb15SPaul Saab 
516a5f0fb15SPaul Saab 		chflags = 0;
517b2ea2440SXin LI 		if (altpipe != NULL)
518a5f0fb15SPaul Saab 		{
519a5f0fb15SPaul Saab 			/*
520a5f0fb15SPaul Saab 			 * The alternate "file" is actually a pipe.
521a5f0fb15SPaul Saab 			 * f has already been set to the file descriptor of the pipe
522a5f0fb15SPaul Saab 			 * in the call to open_altfile above.
523a5f0fb15SPaul Saab 			 * Keep the file descriptor open because it was opened
524a5f0fb15SPaul Saab 			 * via popen(), and pclose() wants to close it.
525a5f0fb15SPaul Saab 			 */
526a5f0fb15SPaul Saab 			chflags |= CH_POPENED;
527b2ea2440SXin LI 			if (strcmp(filename, "-") == 0)
528b2ea2440SXin LI 				chflags |= CH_KEEPOPEN;
529b2ea2440SXin LI 		} else if (strcmp(filename, "-") == 0)
530a5f0fb15SPaul Saab 		{
531a5f0fb15SPaul Saab 			/*
532a5f0fb15SPaul Saab 			 * Use standard input.
533a5f0fb15SPaul Saab 			 * Keep the file descriptor open because we can't reopen it.
534a5f0fb15SPaul Saab 			 */
535a5f0fb15SPaul Saab 			f = fd0;
536a5f0fb15SPaul Saab 			chflags |= CH_KEEPOPEN;
537a5f0fb15SPaul Saab 			/*
538a5f0fb15SPaul Saab 			 * Must switch stdin to BINARY mode.
539a5f0fb15SPaul Saab 			 */
540a5f0fb15SPaul Saab 			SET_BINARY(f);
541a5f0fb15SPaul Saab #if MSDOS_COMPILER==DJGPPC
542a5f0fb15SPaul Saab 			/*
543a5f0fb15SPaul Saab 			 * Setting stdin to binary by default causes
544a5f0fb15SPaul Saab 			 * Ctrl-C to not raise SIGINT.  We must undo
545a5f0fb15SPaul Saab 			 * that side-effect.
546a5f0fb15SPaul Saab 			 */
547a5f0fb15SPaul Saab 			__djgpp_set_ctrl_c(1);
548a5f0fb15SPaul Saab #endif
54996e55cc7SXin LI 		} else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
55096e55cc7SXin LI 		{
55196e55cc7SXin LI 			f = -1;
55296e55cc7SXin LI 			chflags |= CH_NODATA;
553a5f0fb15SPaul Saab 		} else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
554a5f0fb15SPaul Saab 		{
555a5f0fb15SPaul Saab 			f = -1;
556a5f0fb15SPaul Saab 			chflags |= CH_HELPFILE;
557a5f0fb15SPaul Saab 		} else if ((parg.p_string = bad_file(open_filename)) != NULL)
558a5f0fb15SPaul Saab 		{
559a5f0fb15SPaul Saab 			/*
560a5f0fb15SPaul Saab 			 * It looks like a bad file.  Don't try to open it.
561a5f0fb15SPaul Saab 			 */
562a5f0fb15SPaul Saab 			error("%s", &parg);
563a5f0fb15SPaul Saab 			free(parg.p_string);
564d713e089SXin LI 			return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
565b2ea2440SXin LI 		} else if ((f = open(open_filename, OPEN_READ)) < 0)
566a5f0fb15SPaul Saab 		{
567a5f0fb15SPaul Saab 			/*
568a5f0fb15SPaul Saab 			 * Got an error trying to open it.
569a5f0fb15SPaul Saab 			 */
570a5f0fb15SPaul Saab 			parg.p_string = errno_message(filename);
571a5f0fb15SPaul Saab 			error("%s", &parg);
572a5f0fb15SPaul Saab 			free(parg.p_string);
573d713e089SXin LI 			return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
574a5f0fb15SPaul Saab 		} else
575a5f0fb15SPaul Saab 		{
576a5f0fb15SPaul Saab 			chflags |= CH_CANSEEK;
577a5f0fb15SPaul Saab 			if (!force_open && !opened(ifile) && bin_file(f))
578a5f0fb15SPaul Saab 			{
579a5f0fb15SPaul Saab 				/*
580a5f0fb15SPaul Saab 				 * Looks like a binary file.
581a5f0fb15SPaul Saab 				 * Ask user if we should proceed.
582a5f0fb15SPaul Saab 				 */
583a5f0fb15SPaul Saab 				parg.p_string = filename;
584a5f0fb15SPaul Saab 				answer = query("\"%s\" may be a binary file.  See it anyway? ",
585a5f0fb15SPaul Saab 					&parg);
586a5f0fb15SPaul Saab 				if (answer != 'y' && answer != 'Y')
587a5f0fb15SPaul Saab 				{
588a5f0fb15SPaul Saab 					close(f);
589d713e089SXin LI 					return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
590a5f0fb15SPaul Saab 				}
591a5f0fb15SPaul Saab 			}
592a5f0fb15SPaul Saab 		}
593b2ea2440SXin LI 	}
594d713e089SXin LI 	if (!force_open && f >= 0 && isatty(f))
595d713e089SXin LI 	{
596d713e089SXin LI 		PARG parg;
597d713e089SXin LI 		parg.p_string = filename;
598d713e089SXin LI 		error("%s is a terminal (use -f to open it)", &parg);
599d713e089SXin LI 		return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
600d713e089SXin LI 	}
601a5f0fb15SPaul Saab 
602a5f0fb15SPaul Saab 	/*
603a5f0fb15SPaul Saab 	 * Get the new ifile.
604a5f0fb15SPaul Saab 	 * Get the saved position for the file.
605a5f0fb15SPaul Saab 	 */
606a5f0fb15SPaul Saab 	if (was_curr_ifile != NULL_IFILE)
607a5f0fb15SPaul Saab 	{
608a5f0fb15SPaul Saab 		old_ifile = was_curr_ifile;
609a5f0fb15SPaul Saab 		unsave_ifile(was_curr_ifile);
610a5f0fb15SPaul Saab 	}
611a5f0fb15SPaul Saab 	curr_ifile = ifile;
612b2ea2440SXin LI 	set_altfilename(curr_ifile, alt_filename);
613b2ea2440SXin LI 	set_altpipe(curr_ifile, altpipe);
614a5f0fb15SPaul Saab 	set_open(curr_ifile); /* File has been opened */
615a5f0fb15SPaul Saab 	get_pos(curr_ifile, &initial_scrpos);
616a5f0fb15SPaul Saab 	new_file = TRUE;
617a5f0fb15SPaul Saab 	ch_init(f, chflags);
61895270f73SXin LI 	consecutive_nulls = 0;
619d713e089SXin LI 	check_modelines();
620a5f0fb15SPaul Saab 
621a5f0fb15SPaul Saab 	if (!(chflags & CH_HELPFILE))
622a5f0fb15SPaul Saab 	{
623a5f0fb15SPaul Saab #if LOGFILE
624a5f0fb15SPaul Saab 		if (namelogfile != NULL && is_tty)
625a5f0fb15SPaul Saab 			use_logfile(namelogfile);
626a5f0fb15SPaul Saab #endif
627464501a8SXin LI #if HAVE_STAT_INO
628464501a8SXin LI 		/* Remember the i-number and device of the opened file. */
629b2ea2440SXin LI 		if (strcmp(open_filename, "-") != 0)
630464501a8SXin LI 		{
631464501a8SXin LI 			struct stat statbuf;
632b2ea2440SXin LI 			int r = stat(open_filename, &statbuf);
633464501a8SXin LI 			if (r == 0)
634464501a8SXin LI 			{
635464501a8SXin LI 				curr_ino = statbuf.st_ino;
636464501a8SXin LI 				curr_dev = statbuf.st_dev;
637464501a8SXin LI 			}
638464501a8SXin LI 		}
639464501a8SXin LI #endif
640a5f0fb15SPaul Saab 		if (every_first_cmd != NULL)
641a15691bfSXin LI 		{
642a5f0fb15SPaul Saab 			ungetsc(every_first_cmd);
6432235c7feSXin LI 			ungetcc_back(CHAR_END_COMMAND);
644a5f0fb15SPaul Saab 		}
645a15691bfSXin LI 	}
646a5f0fb15SPaul Saab 
647a5f0fb15SPaul Saab 	flush();
648a5f0fb15SPaul Saab 
649a5f0fb15SPaul Saab 	if (is_tty)
650a5f0fb15SPaul Saab 	{
651a5f0fb15SPaul Saab 		/*
652a5f0fb15SPaul Saab 		 * Output is to a real tty.
653a5f0fb15SPaul Saab 		 */
654a5f0fb15SPaul Saab 
655a5f0fb15SPaul Saab 		/*
656a5f0fb15SPaul Saab 		 * Indicate there is nothing displayed yet.
657a5f0fb15SPaul Saab 		 */
658a5f0fb15SPaul Saab 		pos_clear();
659a5f0fb15SPaul Saab 		clr_linenum();
660a5f0fb15SPaul Saab #if HILITE_SEARCH
661a5f0fb15SPaul Saab 		clr_hilite();
662a5f0fb15SPaul Saab #endif
6632235c7feSXin LI 		hshift = 0;
664a15691bfSXin LI 		if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE))
665b7780dbeSXin LI 		{
666b7780dbeSXin LI 			char *qfilename = shell_quote(filename);
667b7780dbeSXin LI 			cmd_addhist(ml_examine, qfilename, 1);
668b7780dbeSXin LI 			free(qfilename);
669b7780dbeSXin LI 		}
67030a1828cSXin LI 		if (want_filesize)
67130a1828cSXin LI 			scan_eof();
672a5f0fb15SPaul Saab 	}
673a5f0fb15SPaul Saab 	free(filename);
674a5f0fb15SPaul Saab 	return (0);
675a5f0fb15SPaul Saab }
676a5f0fb15SPaul Saab 
677a5f0fb15SPaul Saab /*
678a5f0fb15SPaul Saab  * Edit a space-separated list of files.
679a5f0fb15SPaul Saab  * For each filename in the list, enter it into the ifile list.
680a5f0fb15SPaul Saab  * Then edit the first one.
681a5f0fb15SPaul Saab  */
edit_list(char * filelist)682d713e089SXin LI public int edit_list(char *filelist)
683a5f0fb15SPaul Saab {
684a5f0fb15SPaul Saab 	IFILE save_ifile;
685a5f0fb15SPaul Saab 	char *good_filename;
686a5f0fb15SPaul Saab 	char *filename;
687a5f0fb15SPaul Saab 	char *gfilelist;
688a5f0fb15SPaul Saab 	char *gfilename;
689b2ea2440SXin LI 	char *qfilename;
690a5f0fb15SPaul Saab 	struct textlist tl_files;
691a5f0fb15SPaul Saab 	struct textlist tl_gfiles;
692a5f0fb15SPaul Saab 
693a5f0fb15SPaul Saab 	save_ifile = save_curr_ifile();
694a5f0fb15SPaul Saab 	good_filename = NULL;
695a5f0fb15SPaul Saab 
696a5f0fb15SPaul Saab 	/*
697a5f0fb15SPaul Saab 	 * Run thru each filename in the list.
698a5f0fb15SPaul Saab 	 * Try to glob the filename.
699a5f0fb15SPaul Saab 	 * If it doesn't expand, just try to open the filename.
700a5f0fb15SPaul Saab 	 * If it does expand, try to open each name in that list.
701a5f0fb15SPaul Saab 	 */
702a5f0fb15SPaul Saab 	init_textlist(&tl_files, filelist);
703a5f0fb15SPaul Saab 	filename = NULL;
704a5f0fb15SPaul Saab 	while ((filename = forw_textlist(&tl_files, filename)) != NULL)
705a5f0fb15SPaul Saab 	{
706a5f0fb15SPaul Saab 		gfilelist = lglob(filename);
707a5f0fb15SPaul Saab 		init_textlist(&tl_gfiles, gfilelist);
708a5f0fb15SPaul Saab 		gfilename = NULL;
709a5f0fb15SPaul Saab 		while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
710a5f0fb15SPaul Saab 		{
711b2ea2440SXin LI 			qfilename = shell_unquote(gfilename);
712b2ea2440SXin LI 			if (edit(qfilename) == 0 && good_filename == NULL)
713a5f0fb15SPaul Saab 				good_filename = get_filename(curr_ifile);
714b2ea2440SXin LI 			free(qfilename);
715a5f0fb15SPaul Saab 		}
716a5f0fb15SPaul Saab 		free(gfilelist);
717a5f0fb15SPaul Saab 	}
718a5f0fb15SPaul Saab 	/*
719a5f0fb15SPaul Saab 	 * Edit the first valid filename in the list.
720a5f0fb15SPaul Saab 	 */
721a5f0fb15SPaul Saab 	if (good_filename == NULL)
722a5f0fb15SPaul Saab 	{
723a5f0fb15SPaul Saab 		unsave_ifile(save_ifile);
724a5f0fb15SPaul Saab 		return (1);
725a5f0fb15SPaul Saab 	}
726a5f0fb15SPaul Saab 	if (get_ifile(good_filename, curr_ifile) == curr_ifile)
727a5f0fb15SPaul Saab 	{
728a5f0fb15SPaul Saab 		/*
729a5f0fb15SPaul Saab 		 * Trying to edit the current file; don't reopen it.
730a5f0fb15SPaul Saab 		 */
731a5f0fb15SPaul Saab 		unsave_ifile(save_ifile);
732a5f0fb15SPaul Saab 		return (0);
733a5f0fb15SPaul Saab 	}
734a5f0fb15SPaul Saab 	reedit_ifile(save_ifile);
735a5f0fb15SPaul Saab 	return (edit(good_filename));
736a5f0fb15SPaul Saab }
737a5f0fb15SPaul Saab 
738a5f0fb15SPaul Saab /*
739a5f0fb15SPaul Saab  * Edit the first file in the command line (ifile) list.
740a5f0fb15SPaul Saab  */
edit_first(void)741d713e089SXin LI public int edit_first(void)
742a5f0fb15SPaul Saab {
743b7780dbeSXin LI 	if (nifile() == 0)
744b7780dbeSXin LI 		return (edit_stdin());
745a5f0fb15SPaul Saab 	curr_ifile = NULL_IFILE;
746a5f0fb15SPaul Saab 	return (edit_next(1));
747a5f0fb15SPaul Saab }
748a5f0fb15SPaul Saab 
749a5f0fb15SPaul Saab /*
750a5f0fb15SPaul Saab  * Edit the last file in the command line (ifile) list.
751a5f0fb15SPaul Saab  */
edit_last(void)752d713e089SXin LI public int edit_last(void)
753a5f0fb15SPaul Saab {
754a5f0fb15SPaul Saab 	curr_ifile = NULL_IFILE;
755a5f0fb15SPaul Saab 	return (edit_prev(1));
756a5f0fb15SPaul Saab }
757a5f0fb15SPaul Saab 
758a5f0fb15SPaul Saab 
759a5f0fb15SPaul Saab /*
7606dcb072bSXin LI  * Edit the n-th next or previous file in the command line (ifile) list.
761a5f0fb15SPaul Saab  */
edit_istep(IFILE h,int n,int dir)762d713e089SXin LI static int edit_istep(IFILE h, int n, int dir)
763a5f0fb15SPaul Saab {
764a5f0fb15SPaul Saab 	IFILE next;
765a5f0fb15SPaul Saab 
766a5f0fb15SPaul Saab 	/*
767a5f0fb15SPaul Saab 	 * Skip n filenames, then try to edit each filename.
768a5f0fb15SPaul Saab 	 */
769a5f0fb15SPaul Saab 	for (;;)
770a5f0fb15SPaul Saab 	{
771a5f0fb15SPaul Saab 		next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
772a5f0fb15SPaul Saab 		if (--n < 0)
773a5f0fb15SPaul Saab 		{
774a5f0fb15SPaul Saab 			if (edit_ifile(h) == 0)
775a5f0fb15SPaul Saab 				break;
776a5f0fb15SPaul Saab 		}
777a5f0fb15SPaul Saab 		if (next == NULL_IFILE)
778a5f0fb15SPaul Saab 		{
779a5f0fb15SPaul Saab 			/*
780a5f0fb15SPaul Saab 			 * Reached end of the ifile list.
781a5f0fb15SPaul Saab 			 */
782a5f0fb15SPaul Saab 			return (1);
783a5f0fb15SPaul Saab 		}
784a5f0fb15SPaul Saab 		if (ABORT_SIGS())
785a5f0fb15SPaul Saab 		{
786a5f0fb15SPaul Saab 			/*
787a5f0fb15SPaul Saab 			 * Interrupt breaks out, if we're in a long
788a5f0fb15SPaul Saab 			 * list of files that can't be opened.
789a5f0fb15SPaul Saab 			 */
790a5f0fb15SPaul Saab 			return (1);
791a5f0fb15SPaul Saab 		}
792a5f0fb15SPaul Saab 		h = next;
793a5f0fb15SPaul Saab 	}
794a5f0fb15SPaul Saab 	/*
795a5f0fb15SPaul Saab 	 * Found a file that we can edit.
796a5f0fb15SPaul Saab 	 */
797a5f0fb15SPaul Saab 	return (0);
798a5f0fb15SPaul Saab }
799a5f0fb15SPaul Saab 
edit_inext(IFILE h,int n)800d713e089SXin LI static int edit_inext(IFILE h, int n)
801a5f0fb15SPaul Saab {
8026dcb072bSXin LI 	return (edit_istep(h, n, +1));
803a5f0fb15SPaul Saab }
804a5f0fb15SPaul Saab 
edit_next(int n)805d713e089SXin LI public int edit_next(int n)
806a5f0fb15SPaul Saab {
8076dcb072bSXin LI 	return edit_istep(curr_ifile, n, +1);
808a5f0fb15SPaul Saab }
809a5f0fb15SPaul Saab 
edit_iprev(IFILE h,int n)810d713e089SXin LI static int edit_iprev(IFILE h, int n)
811a5f0fb15SPaul Saab {
812a5f0fb15SPaul Saab 	return (edit_istep(h, n, -1));
813a5f0fb15SPaul Saab }
814a5f0fb15SPaul Saab 
edit_prev(int n)815d713e089SXin LI public int edit_prev(int n)
816a5f0fb15SPaul Saab {
817a5f0fb15SPaul Saab 	return edit_istep(curr_ifile, n, -1);
818a5f0fb15SPaul Saab }
819a5f0fb15SPaul Saab 
820a5f0fb15SPaul Saab /*
821a5f0fb15SPaul Saab  * Edit a specific file in the command line (ifile) list.
822a5f0fb15SPaul Saab  */
edit_index(int n)823d713e089SXin LI public int edit_index(int n)
824a5f0fb15SPaul Saab {
825a5f0fb15SPaul Saab 	IFILE h;
826a5f0fb15SPaul Saab 
827a5f0fb15SPaul Saab 	h = NULL_IFILE;
828a5f0fb15SPaul Saab 	do
829a5f0fb15SPaul Saab 	{
830a5f0fb15SPaul Saab 		if ((h = next_ifile(h)) == NULL_IFILE)
831a5f0fb15SPaul Saab 		{
832a5f0fb15SPaul Saab 			/*
833a5f0fb15SPaul Saab 			 * Reached end of the list without finding it.
834a5f0fb15SPaul Saab 			 */
835a5f0fb15SPaul Saab 			return (1);
836a5f0fb15SPaul Saab 		}
837a5f0fb15SPaul Saab 	} while (get_index(h) != n);
838a5f0fb15SPaul Saab 
839a5f0fb15SPaul Saab 	return (edit_ifile(h));
840a5f0fb15SPaul Saab }
841a5f0fb15SPaul Saab 
save_curr_ifile(void)842d713e089SXin LI public IFILE save_curr_ifile(void)
843a5f0fb15SPaul Saab {
844a5f0fb15SPaul Saab 	if (curr_ifile != NULL_IFILE)
845a5f0fb15SPaul Saab 		hold_ifile(curr_ifile, 1);
846a5f0fb15SPaul Saab 	return (curr_ifile);
847a5f0fb15SPaul Saab }
848a5f0fb15SPaul Saab 
unsave_ifile(IFILE save_ifile)849d713e089SXin LI public void unsave_ifile(IFILE save_ifile)
850a5f0fb15SPaul Saab {
851a5f0fb15SPaul Saab 	if (save_ifile != NULL_IFILE)
852a5f0fb15SPaul Saab 		hold_ifile(save_ifile, -1);
853a5f0fb15SPaul Saab }
854a5f0fb15SPaul Saab 
855a5f0fb15SPaul Saab /*
856a5f0fb15SPaul Saab  * Reedit the ifile which was previously open.
857a5f0fb15SPaul Saab  */
reedit_ifile(IFILE save_ifile)858d713e089SXin LI public void reedit_ifile(IFILE save_ifile)
859a5f0fb15SPaul Saab {
860a5f0fb15SPaul Saab 	IFILE next;
861a5f0fb15SPaul Saab 	IFILE prev;
862a5f0fb15SPaul Saab 
863a5f0fb15SPaul Saab 	/*
864a5f0fb15SPaul Saab 	 * Try to reopen the ifile.
865a5f0fb15SPaul Saab 	 * Note that opening it may fail (maybe the file was removed),
866a5f0fb15SPaul Saab 	 * in which case the ifile will be deleted from the list.
867a5f0fb15SPaul Saab 	 * So save the next and prev ifiles first.
868a5f0fb15SPaul Saab 	 */
869a5f0fb15SPaul Saab 	unsave_ifile(save_ifile);
870a5f0fb15SPaul Saab 	next = next_ifile(save_ifile);
871a5f0fb15SPaul Saab 	prev = prev_ifile(save_ifile);
872a5f0fb15SPaul Saab 	if (edit_ifile(save_ifile) == 0)
873a5f0fb15SPaul Saab 		return;
874a5f0fb15SPaul Saab 	/*
875a5f0fb15SPaul Saab 	 * If can't reopen it, open the next input file in the list.
876a5f0fb15SPaul Saab 	 */
877a5f0fb15SPaul Saab 	if (next != NULL_IFILE && edit_inext(next, 0) == 0)
878a5f0fb15SPaul Saab 		return;
879a5f0fb15SPaul Saab 	/*
880a5f0fb15SPaul Saab 	 * If can't open THAT one, open the previous input file in the list.
881a5f0fb15SPaul Saab 	 */
882a5f0fb15SPaul Saab 	if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
883a5f0fb15SPaul Saab 		return;
884a5f0fb15SPaul Saab 	/*
885a5f0fb15SPaul Saab 	 * If can't even open that, we're stuck.  Just quit.
886a5f0fb15SPaul Saab 	 */
887a5f0fb15SPaul Saab 	quit(QUIT_ERROR);
888a5f0fb15SPaul Saab }
889a5f0fb15SPaul Saab 
reopen_curr_ifile(void)890d713e089SXin LI public void reopen_curr_ifile(void)
891464501a8SXin LI {
892464501a8SXin LI 	IFILE save_ifile = save_curr_ifile();
893464501a8SXin LI 	close_file();
894464501a8SXin LI 	reedit_ifile(save_ifile);
895464501a8SXin LI }
896464501a8SXin LI 
897a5f0fb15SPaul Saab /*
898a5f0fb15SPaul Saab  * Edit standard input.
899a5f0fb15SPaul Saab  */
edit_stdin(void)900d713e089SXin LI public int edit_stdin(void)
901a5f0fb15SPaul Saab {
902a5f0fb15SPaul Saab 	if (isatty(fd0))
903a5f0fb15SPaul Saab 	{
904a5f0fb15SPaul Saab 		error("Missing filename (\"less --help\" for help)", NULL_PARG);
905a5f0fb15SPaul Saab 		quit(QUIT_OK);
906a5f0fb15SPaul Saab 	}
907a5f0fb15SPaul Saab 	return (edit("-"));
908a5f0fb15SPaul Saab }
909a5f0fb15SPaul Saab 
910a5f0fb15SPaul Saab /*
911a5f0fb15SPaul Saab  * Copy a file directly to standard output.
912a5f0fb15SPaul Saab  * Used if standard output is not a tty.
913a5f0fb15SPaul Saab  */
cat_file(void)914d713e089SXin LI public void cat_file(void)
915a5f0fb15SPaul Saab {
9161ea31627SRobert Watson 	int c;
917a5f0fb15SPaul Saab 
918a5f0fb15SPaul Saab 	while ((c = ch_forw_get()) != EOI)
919a5f0fb15SPaul Saab 		putchr(c);
920a5f0fb15SPaul Saab 	flush();
921a5f0fb15SPaul Saab }
922a5f0fb15SPaul Saab 
923a5f0fb15SPaul Saab #if LOGFILE
924a5f0fb15SPaul Saab 
9252235c7feSXin LI #define OVERWRITE_OPTIONS "Overwrite, Append, Don't log, or Quit?"
9262235c7feSXin LI 
927a5f0fb15SPaul Saab /*
928a5f0fb15SPaul Saab  * If the user asked for a log file and our input file
929a5f0fb15SPaul Saab  * is standard input, create the log file.
930a5f0fb15SPaul Saab  * We take care not to blindly overwrite an existing file.
931a5f0fb15SPaul Saab  */
use_logfile(char * filename)932d713e089SXin LI public void use_logfile(char *filename)
933a5f0fb15SPaul Saab {
9341ea31627SRobert Watson 	int exists;
9351ea31627SRobert Watson 	int answer;
936a5f0fb15SPaul Saab 	PARG parg;
937a5f0fb15SPaul Saab 
938a5f0fb15SPaul Saab 	if (ch_getflags() & CH_CANSEEK)
939a5f0fb15SPaul Saab 		/*
940a5f0fb15SPaul Saab 		 * Can't currently use a log file on a file that can seek.
941a5f0fb15SPaul Saab 		 */
942a5f0fb15SPaul Saab 		return;
943a5f0fb15SPaul Saab 
944a5f0fb15SPaul Saab 	/*
945a5f0fb15SPaul Saab 	 * {{ We could use access() here. }}
946a5f0fb15SPaul Saab 	 */
947a5f0fb15SPaul Saab 	exists = open(filename, OPEN_READ);
948a15691bfSXin LI 	if (exists >= 0)
949a5f0fb15SPaul Saab 		close(exists);
950a5f0fb15SPaul Saab 	exists = (exists >= 0);
951a5f0fb15SPaul Saab 
952a5f0fb15SPaul Saab 	/*
953a5f0fb15SPaul Saab 	 * Decide whether to overwrite the log file or append to it.
954a5f0fb15SPaul Saab 	 * If it doesn't exist we "overwrite" it.
955a5f0fb15SPaul Saab 	 */
956a5f0fb15SPaul Saab 	if (!exists || force_logfile)
957a5f0fb15SPaul Saab 	{
958a5f0fb15SPaul Saab 		/*
959a5f0fb15SPaul Saab 		 * Overwrite (or create) the log file.
960a5f0fb15SPaul Saab 		 */
961a5f0fb15SPaul Saab 		answer = 'O';
962a5f0fb15SPaul Saab 	} else
963a5f0fb15SPaul Saab 	{
964a5f0fb15SPaul Saab 		/*
965a5f0fb15SPaul Saab 		 * Ask user what to do.
966a5f0fb15SPaul Saab 		 */
967a5f0fb15SPaul Saab 		parg.p_string = filename;
9682235c7feSXin LI 		answer = query("Warning: \"%s\" exists; "OVERWRITE_OPTIONS" ", &parg);
969a5f0fb15SPaul Saab 	}
970a5f0fb15SPaul Saab 
971a5f0fb15SPaul Saab loop:
972a5f0fb15SPaul Saab 	switch (answer)
973a5f0fb15SPaul Saab 	{
974a5f0fb15SPaul Saab 	case 'O': case 'o':
975a5f0fb15SPaul Saab 		/*
976a5f0fb15SPaul Saab 		 * Overwrite: create the file.
977a5f0fb15SPaul Saab 		 */
978f80a33eaSXin LI 		logfile = creat(filename, CREAT_RW);
979a5f0fb15SPaul Saab 		break;
980a5f0fb15SPaul Saab 	case 'A': case 'a':
981a5f0fb15SPaul Saab 		/*
982a5f0fb15SPaul Saab 		 * Append: open the file and seek to the end.
983a5f0fb15SPaul Saab 		 */
984a5f0fb15SPaul Saab 		logfile = open(filename, OPEN_APPEND);
985464501a8SXin LI 		if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
986a5f0fb15SPaul Saab 		{
987a5f0fb15SPaul Saab 			close(logfile);
988a5f0fb15SPaul Saab 			logfile = -1;
989a5f0fb15SPaul Saab 		}
990a5f0fb15SPaul Saab 		break;
991a5f0fb15SPaul Saab 	case 'D': case 'd':
992a5f0fb15SPaul Saab 		/*
993a5f0fb15SPaul Saab 		 * Don't do anything.
994a5f0fb15SPaul Saab 		 */
995a5f0fb15SPaul Saab 		return;
996a5f0fb15SPaul Saab 	default:
997a5f0fb15SPaul Saab 		/*
998a5f0fb15SPaul Saab 		 * Eh?
999a5f0fb15SPaul Saab 		 */
10002235c7feSXin LI 
10012235c7feSXin LI 		answer = query(OVERWRITE_OPTIONS" (Type \"O\", \"A\", \"D\" or \"Q\") ", NULL_PARG);
1002a5f0fb15SPaul Saab 		goto loop;
1003a5f0fb15SPaul Saab 	}
1004a5f0fb15SPaul Saab 
1005a5f0fb15SPaul Saab 	if (logfile < 0)
1006a5f0fb15SPaul Saab 	{
1007a5f0fb15SPaul Saab 		/*
1008a5f0fb15SPaul Saab 		 * Error in opening logfile.
1009a5f0fb15SPaul Saab 		 */
1010a5f0fb15SPaul Saab 		parg.p_string = filename;
1011a5f0fb15SPaul Saab 		error("Cannot write to \"%s\"", &parg);
1012a5f0fb15SPaul Saab 		return;
1013a5f0fb15SPaul Saab 	}
1014a5f0fb15SPaul Saab 	SET_BINARY(logfile);
1015a5f0fb15SPaul Saab }
1016a5f0fb15SPaul Saab 
1017a5f0fb15SPaul Saab #endif
1018