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