xref: /dragonfly/contrib/less/edit.c (revision b2ced873)
11133e27eSPeter Avalos /*
2*320d7c8aSAaron LI  * Copyright (C) 1984-2023  Mark Nudelman
31133e27eSPeter Avalos  *
41133e27eSPeter Avalos  * You may distribute under the terms of either the GNU General Public
51133e27eSPeter Avalos  * License or the Less License, as specified in the README file.
61133e27eSPeter Avalos  *
7e639dc31SJohn Marino  * For more information, see the README file.
81133e27eSPeter Avalos  */
91133e27eSPeter Avalos 
101133e27eSPeter Avalos 
111133e27eSPeter Avalos #include "less.h"
1202d62a0fSDaniel Fojt #include "position.h"
131133e27eSPeter Avalos #if HAVE_STAT
141133e27eSPeter Avalos #include <sys/stat.h>
151133e27eSPeter Avalos #endif
16*320d7c8aSAaron LI #if HAVE_SYS_WAIT_H
17*320d7c8aSAaron LI #include <sys/wait.h>
18*320d7c8aSAaron LI #endif
1902d62a0fSDaniel Fojt #include <signal.h>
201133e27eSPeter Avalos 
211133e27eSPeter Avalos public int fd0 = 0;
221133e27eSPeter Avalos 
231133e27eSPeter Avalos extern int new_file;
241133e27eSPeter Avalos extern int cbufs;
251133e27eSPeter Avalos extern char *every_first_cmd;
261133e27eSPeter Avalos extern int force_open;
271133e27eSPeter Avalos extern int is_tty;
281133e27eSPeter Avalos extern int sigs;
290c7ad07eSAntonio Huete Jimenez extern int hshift;
300c7ad07eSAntonio Huete Jimenez extern int want_filesize;
310c7ad07eSAntonio Huete Jimenez extern int consecutive_nulls;
32*320d7c8aSAaron LI extern int modelines;
33*320d7c8aSAaron LI extern int show_preproc_error;
341133e27eSPeter Avalos extern IFILE curr_ifile;
351133e27eSPeter Avalos extern IFILE old_ifile;
361133e27eSPeter Avalos extern struct scrpos initial_scrpos;
3702d62a0fSDaniel Fojt extern void *ml_examine;
381133e27eSPeter Avalos #if SPACES_IN_FILENAMES
391133e27eSPeter Avalos extern char openquote;
401133e27eSPeter Avalos extern char closequote;
411133e27eSPeter Avalos #endif
421133e27eSPeter Avalos 
431133e27eSPeter Avalos #if LOGFILE
441133e27eSPeter Avalos extern int logfile;
451133e27eSPeter Avalos extern int force_logfile;
461133e27eSPeter Avalos extern char *namelogfile;
471133e27eSPeter Avalos #endif
481133e27eSPeter Avalos 
491133e27eSPeter Avalos #if HAVE_STAT_INO
501133e27eSPeter Avalos public dev_t curr_dev;
511133e27eSPeter Avalos public ino_t curr_ino;
521133e27eSPeter Avalos #endif
531133e27eSPeter Avalos 
541133e27eSPeter Avalos /*
551133e27eSPeter Avalos  * Textlist functions deal with a list of words separated by spaces.
561133e27eSPeter Avalos  * init_textlist sets up a textlist structure.
571133e27eSPeter Avalos  * forw_textlist uses that structure to iterate thru the list of
581133e27eSPeter Avalos  * words, returning each one as a standard null-terminated string.
591133e27eSPeter Avalos  * back_textlist does the same, but runs thru the list backwards.
601133e27eSPeter Avalos  */
init_textlist(struct textlist * tlist,char * str)61*320d7c8aSAaron LI public void init_textlist(struct textlist *tlist, char *str)
621133e27eSPeter Avalos {
631133e27eSPeter Avalos 	char *s;
641133e27eSPeter Avalos #if SPACES_IN_FILENAMES
651133e27eSPeter Avalos 	int meta_quoted = 0;
661133e27eSPeter Avalos 	int delim_quoted = 0;
671133e27eSPeter Avalos 	char *esc = get_meta_escape();
68fa0be7c5SJohn Marino 	int esclen = (int) strlen(esc);
691133e27eSPeter Avalos #endif
701133e27eSPeter Avalos 
711133e27eSPeter Avalos 	tlist->string = skipsp(str);
721133e27eSPeter Avalos 	tlist->endstring = tlist->string + strlen(tlist->string);
731133e27eSPeter Avalos 	for (s = str;  s < tlist->endstring;  s++)
741133e27eSPeter Avalos 	{
751133e27eSPeter Avalos #if SPACES_IN_FILENAMES
761133e27eSPeter Avalos 		if (meta_quoted)
771133e27eSPeter Avalos 		{
781133e27eSPeter Avalos 			meta_quoted = 0;
791133e27eSPeter Avalos 		} else if (esclen > 0 && s + esclen < tlist->endstring &&
801133e27eSPeter Avalos 		           strncmp(s, esc, esclen) == 0)
811133e27eSPeter Avalos 		{
821133e27eSPeter Avalos 			meta_quoted = 1;
831133e27eSPeter Avalos 			s += esclen - 1;
841133e27eSPeter Avalos 		} else if (delim_quoted)
851133e27eSPeter Avalos 		{
861133e27eSPeter Avalos 			if (*s == closequote)
871133e27eSPeter Avalos 				delim_quoted = 0;
881133e27eSPeter Avalos 		} else /* (!delim_quoted) */
891133e27eSPeter Avalos 		{
901133e27eSPeter Avalos 			if (*s == openquote)
911133e27eSPeter Avalos 				delim_quoted = 1;
921133e27eSPeter Avalos 			else if (*s == ' ')
931133e27eSPeter Avalos 				*s = '\0';
941133e27eSPeter Avalos 		}
951133e27eSPeter Avalos #else
961133e27eSPeter Avalos 		if (*s == ' ')
971133e27eSPeter Avalos 			*s = '\0';
981133e27eSPeter Avalos #endif
991133e27eSPeter Avalos 	}
1001133e27eSPeter Avalos }
1011133e27eSPeter Avalos 
forw_textlist(struct textlist * tlist,char * prev)102*320d7c8aSAaron LI public char * forw_textlist(struct textlist *tlist, char *prev)
1031133e27eSPeter Avalos {
1041133e27eSPeter Avalos 	char *s;
1051133e27eSPeter Avalos 
1061133e27eSPeter Avalos 	/*
1071133e27eSPeter Avalos 	 * prev == NULL means return the first word in the list.
1081133e27eSPeter Avalos 	 * Otherwise, return the word after "prev".
1091133e27eSPeter Avalos 	 */
1101133e27eSPeter Avalos 	if (prev == NULL)
1111133e27eSPeter Avalos 		s = tlist->string;
1121133e27eSPeter Avalos 	else
1131133e27eSPeter Avalos 		s = prev + strlen(prev);
1141133e27eSPeter Avalos 	if (s >= tlist->endstring)
1151133e27eSPeter Avalos 		return (NULL);
1161133e27eSPeter Avalos 	while (*s == '\0')
1171133e27eSPeter Avalos 		s++;
1181133e27eSPeter Avalos 	if (s >= tlist->endstring)
1191133e27eSPeter Avalos 		return (NULL);
1201133e27eSPeter Avalos 	return (s);
1211133e27eSPeter Avalos }
1221133e27eSPeter Avalos 
back_textlist(struct textlist * tlist,char * prev)123*320d7c8aSAaron LI public char * back_textlist(struct textlist *tlist, char *prev)
1241133e27eSPeter Avalos {
1251133e27eSPeter Avalos 	char *s;
1261133e27eSPeter Avalos 
1271133e27eSPeter Avalos 	/*
1281133e27eSPeter Avalos 	 * prev == NULL means return the last word in the list.
1291133e27eSPeter Avalos 	 * Otherwise, return the word before "prev".
1301133e27eSPeter Avalos 	 */
1311133e27eSPeter Avalos 	if (prev == NULL)
1321133e27eSPeter Avalos 		s = tlist->endstring;
1331133e27eSPeter Avalos 	else if (prev <= tlist->string)
1341133e27eSPeter Avalos 		return (NULL);
1351133e27eSPeter Avalos 	else
1361133e27eSPeter Avalos 		s = prev - 1;
1371133e27eSPeter Avalos 	while (*s == '\0')
1381133e27eSPeter Avalos 		s--;
1391133e27eSPeter Avalos 	if (s <= tlist->string)
1401133e27eSPeter Avalos 		return (NULL);
1411133e27eSPeter Avalos 	while (s[-1] != '\0' && s > tlist->string)
1421133e27eSPeter Avalos 		s--;
1431133e27eSPeter Avalos 	return (s);
1441133e27eSPeter Avalos }
1451133e27eSPeter Avalos 
1461133e27eSPeter Avalos /*
147*320d7c8aSAaron LI  * Parse a single option setting in a modeline.
148*320d7c8aSAaron LI  */
modeline_option(char * str,int opt_len)149*320d7c8aSAaron LI static void modeline_option(char *str, int opt_len)
150*320d7c8aSAaron LI {
151*320d7c8aSAaron LI 	struct mloption { char *opt_name; void (*opt_func)(char*,int); };
152*320d7c8aSAaron LI 	struct mloption options[] = {
153*320d7c8aSAaron LI 		{ "ts=",         set_tabs },
154*320d7c8aSAaron LI 		{ "tabstop=",    set_tabs },
155*320d7c8aSAaron LI 		{ NULL, NULL }
156*320d7c8aSAaron LI 	};
157*320d7c8aSAaron LI 	struct mloption *opt;
158*320d7c8aSAaron LI 	for (opt = options;  opt->opt_name != NULL;  opt++)
159*320d7c8aSAaron LI 	{
160*320d7c8aSAaron LI 		int name_len = strlen(opt->opt_name);
161*320d7c8aSAaron LI 		if (opt_len > name_len && strncmp(str, opt->opt_name, name_len) == 0)
162*320d7c8aSAaron LI 		{
163*320d7c8aSAaron LI 			(*opt->opt_func)(str + name_len, opt_len - name_len);
164*320d7c8aSAaron LI 			break;
165*320d7c8aSAaron LI 		}
166*320d7c8aSAaron LI 	}
167*320d7c8aSAaron LI }
168*320d7c8aSAaron LI 
169*320d7c8aSAaron LI /*
170*320d7c8aSAaron LI  * String length, terminated by option separator (space or colon).
171*320d7c8aSAaron LI  * Space/colon can be escaped with backspace.
172*320d7c8aSAaron LI  */
modeline_option_len(char * str)173*320d7c8aSAaron LI static int modeline_option_len(char *str)
174*320d7c8aSAaron LI {
175*320d7c8aSAaron LI 	int esc = FALSE;
176*320d7c8aSAaron LI 	char *s;
177*320d7c8aSAaron LI 	for (s = str;  *s != '\0';  s++)
178*320d7c8aSAaron LI 	{
179*320d7c8aSAaron LI 		if (esc)
180*320d7c8aSAaron LI 			esc = FALSE;
181*320d7c8aSAaron LI 		else if (*s == '\\')
182*320d7c8aSAaron LI 			esc = TRUE;
183*320d7c8aSAaron LI 		else if (*s == ' ' || *s == ':') /* separator */
184*320d7c8aSAaron LI 			break;
185*320d7c8aSAaron LI 	}
186*320d7c8aSAaron LI 	return (s - str);
187*320d7c8aSAaron LI }
188*320d7c8aSAaron LI 
189*320d7c8aSAaron LI /*
190*320d7c8aSAaron LI  * Parse colon- or space-separated option settings in a modeline.
191*320d7c8aSAaron LI  */
modeline_options(char * str,char end_char)192*320d7c8aSAaron LI static void modeline_options(char *str, char end_char)
193*320d7c8aSAaron LI {
194*320d7c8aSAaron LI 	for (;;)
195*320d7c8aSAaron LI 	{
196*320d7c8aSAaron LI 		int opt_len;
197*320d7c8aSAaron LI 		str = skipsp(str);
198*320d7c8aSAaron LI 		if (*str == '\0' || *str == end_char)
199*320d7c8aSAaron LI 			break;
200*320d7c8aSAaron LI 		opt_len = modeline_option_len(str);
201*320d7c8aSAaron LI 		modeline_option(str, opt_len);
202*320d7c8aSAaron LI 		str += opt_len;
203*320d7c8aSAaron LI 		if (*str != '\0')
204*320d7c8aSAaron LI 			str += 1; /* skip past the separator */
205*320d7c8aSAaron LI 	}
206*320d7c8aSAaron LI }
207*320d7c8aSAaron LI 
208*320d7c8aSAaron LI /*
209*320d7c8aSAaron LI  * See if there is a modeline string in a line.
210*320d7c8aSAaron LI  */
check_modeline(char * line)211*320d7c8aSAaron LI static void check_modeline(char *line)
212*320d7c8aSAaron LI {
213*320d7c8aSAaron LI #if HAVE_STRSTR
214*320d7c8aSAaron LI 	static char *pgms[] = { "less:", "vim:", "vi:", "ex:", NULL };
215*320d7c8aSAaron LI 	char **pgm;
216*320d7c8aSAaron LI 	for (pgm = pgms;  *pgm != NULL;  ++pgm)
217*320d7c8aSAaron LI 	{
218*320d7c8aSAaron LI 		char *pline = line;
219*320d7c8aSAaron LI 		for (;;)
220*320d7c8aSAaron LI 		{
221*320d7c8aSAaron LI 			char *str;
222*320d7c8aSAaron LI 			pline = strstr(pline, *pgm);
223*320d7c8aSAaron LI 			if (pline == NULL) /* pgm is not in this line */
224*320d7c8aSAaron LI 				break;
225*320d7c8aSAaron LI 			str = skipsp(pline + strlen(*pgm));
226*320d7c8aSAaron LI 			if (pline == line || pline[-1] == ' ')
227*320d7c8aSAaron LI 			{
228*320d7c8aSAaron LI 				if (strncmp(str, "set ", 4) == 0)
229*320d7c8aSAaron LI 					modeline_options(str+4, ':');
230*320d7c8aSAaron LI 				else if (pgm != &pgms[0]) /* "less:" requires "set" */
231*320d7c8aSAaron LI 					modeline_options(str, '\0');
232*320d7c8aSAaron LI 				break;
233*320d7c8aSAaron LI 			}
234*320d7c8aSAaron LI 			/* Continue searching the rest of the line. */
235*320d7c8aSAaron LI 			pline = str;
236*320d7c8aSAaron LI 		}
237*320d7c8aSAaron LI 	}
238*320d7c8aSAaron LI #endif /* HAVE_STRSTR */
239*320d7c8aSAaron LI }
240*320d7c8aSAaron LI 
241*320d7c8aSAaron LI /*
242*320d7c8aSAaron LI  * Read lines from start of file and check if any are modelines.
243*320d7c8aSAaron LI  */
check_modelines(void)244*320d7c8aSAaron LI static void check_modelines(void)
245*320d7c8aSAaron LI {
246*320d7c8aSAaron LI 	POSITION pos = ch_zero();
247*320d7c8aSAaron LI 	int i;
248*320d7c8aSAaron LI 	for (i = 0;  i < modelines;  i++)
249*320d7c8aSAaron LI 	{
250*320d7c8aSAaron LI 		char *line;
251*320d7c8aSAaron LI 		int line_len;
252*320d7c8aSAaron LI 		if (ABORT_SIGS())
253*320d7c8aSAaron LI 			return;
254*320d7c8aSAaron LI 		pos = forw_raw_line(pos, &line, &line_len);
255*320d7c8aSAaron LI 		if (pos == NULL_POSITION)
256*320d7c8aSAaron LI 			break;
257*320d7c8aSAaron LI 		check_modeline(line);
258*320d7c8aSAaron LI 	}
259*320d7c8aSAaron LI }
260*320d7c8aSAaron LI 
261*320d7c8aSAaron LI /*
26202d62a0fSDaniel Fojt  * Close a pipe opened via popen.
26302d62a0fSDaniel Fojt  */
close_pipe(FILE * pipefd)264*320d7c8aSAaron LI static void close_pipe(FILE *pipefd)
26502d62a0fSDaniel Fojt {
266*320d7c8aSAaron LI 	int status;
267*320d7c8aSAaron LI 	PARG parg;
268*320d7c8aSAaron LI 
26902d62a0fSDaniel Fojt 	if (pipefd == NULL)
27002d62a0fSDaniel Fojt 		return;
27102d62a0fSDaniel Fojt #if OS2
27202d62a0fSDaniel Fojt 	/*
27302d62a0fSDaniel Fojt 	 * The pclose function of OS/2 emx sometimes fails.
27402d62a0fSDaniel Fojt 	 * Send SIGINT to the piped process before closing it.
27502d62a0fSDaniel Fojt 	 */
27602d62a0fSDaniel Fojt 	kill(pipefd->_pid, SIGINT);
27702d62a0fSDaniel Fojt #endif
278*320d7c8aSAaron LI 	status = pclose(pipefd);
279*320d7c8aSAaron LI 	if (status == -1)
280*320d7c8aSAaron LI 	{
281*320d7c8aSAaron LI 		/* An internal error in 'less', not a preprocessor error.  */
282*320d7c8aSAaron LI 		parg.p_string = errno_message("pclose");
283*320d7c8aSAaron LI 		error("%s", &parg);
284*320d7c8aSAaron LI 		free(parg.p_string);
285*320d7c8aSAaron LI 		return;
286*320d7c8aSAaron LI 	}
287*320d7c8aSAaron LI 	if (!show_preproc_error)
288*320d7c8aSAaron LI 		return;
289*320d7c8aSAaron LI #if defined WIFEXITED && defined WEXITSTATUS
290*320d7c8aSAaron LI 	if (WIFEXITED(status))
291*320d7c8aSAaron LI 	{
292*320d7c8aSAaron LI 		int s = WEXITSTATUS(status);
293*320d7c8aSAaron LI 		if (s != 0)
294*320d7c8aSAaron LI 		{
295*320d7c8aSAaron LI 			parg.p_int = s;
296*320d7c8aSAaron LI 			error("Input preprocessor failed (status %d)", &parg);
297*320d7c8aSAaron LI 		}
298*320d7c8aSAaron LI 		return;
299*320d7c8aSAaron LI 	}
300*320d7c8aSAaron LI #endif
301*320d7c8aSAaron LI #if defined WIFSIGNALED && defined WTERMSIG && HAVE_STRSIGNAL
302*320d7c8aSAaron LI 	if (WIFSIGNALED(status))
303*320d7c8aSAaron LI 	{
304*320d7c8aSAaron LI 		int sig = WTERMSIG(status);
305*320d7c8aSAaron LI 		if (sig != SIGPIPE || ch_length() != NULL_POSITION)
306*320d7c8aSAaron LI 		{
307*320d7c8aSAaron LI 			parg.p_string = signal_message(sig);
308*320d7c8aSAaron LI 			error("Input preprocessor terminated: %s", &parg);
309*320d7c8aSAaron LI 		}
310*320d7c8aSAaron LI 		return;
311*320d7c8aSAaron LI 	}
312*320d7c8aSAaron LI #endif
313*320d7c8aSAaron LI 	if (status != 0)
314*320d7c8aSAaron LI 	{
315*320d7c8aSAaron LI 		parg.p_int = status;
316*320d7c8aSAaron LI 		error("Input preprocessor exited with status %x", &parg);
317*320d7c8aSAaron LI 	}
318*320d7c8aSAaron LI }
319*320d7c8aSAaron LI 
320*320d7c8aSAaron LI /*
321*320d7c8aSAaron LI  * Drain and close an input pipe if needed.
322*320d7c8aSAaron LI  */
close_altpipe(IFILE ifile)323*320d7c8aSAaron LI public void close_altpipe(IFILE ifile)
324*320d7c8aSAaron LI {
325*320d7c8aSAaron LI 	FILE *altpipe = get_altpipe(ifile);
326*320d7c8aSAaron LI 	if (altpipe != NULL && !(ch_getflags() & CH_KEEPOPEN))
327*320d7c8aSAaron LI 	{
328*320d7c8aSAaron LI 		close_pipe(altpipe);
329*320d7c8aSAaron LI 		set_altpipe(ifile, NULL);
330*320d7c8aSAaron LI 	}
331*320d7c8aSAaron LI }
332*320d7c8aSAaron LI 
333*320d7c8aSAaron LI /*
334*320d7c8aSAaron LI  * Check for error status from the current altpipe.
335*320d7c8aSAaron LI  * May or may not close the pipe.
336*320d7c8aSAaron LI  */
check_altpipe_error(void)337*320d7c8aSAaron LI public void check_altpipe_error(void)
338*320d7c8aSAaron LI {
339*320d7c8aSAaron LI 	if (!show_preproc_error)
340*320d7c8aSAaron LI 		return;
341*320d7c8aSAaron LI 	if (curr_ifile != NULL_IFILE && get_altfilename(curr_ifile) != NULL)
342*320d7c8aSAaron LI 		close_altpipe(curr_ifile);
34302d62a0fSDaniel Fojt }
34402d62a0fSDaniel Fojt 
34502d62a0fSDaniel Fojt /*
3461133e27eSPeter Avalos  * Close the current input file.
3471133e27eSPeter Avalos  */
close_file(void)348*320d7c8aSAaron LI static void close_file(void)
3491133e27eSPeter Avalos {
3501133e27eSPeter Avalos 	struct scrpos scrpos;
35102d62a0fSDaniel Fojt 	char *altfilename;
3521133e27eSPeter Avalos 
3531133e27eSPeter Avalos 	if (curr_ifile == NULL_IFILE)
3541133e27eSPeter Avalos 		return;
3551133e27eSPeter Avalos 
3561133e27eSPeter Avalos 	/*
3571133e27eSPeter Avalos 	 * Save the current position so that we can return to
3581133e27eSPeter Avalos 	 * the same position if we edit this file again.
3591133e27eSPeter Avalos 	 */
36002d62a0fSDaniel Fojt 	get_scrpos(&scrpos, TOP);
3611133e27eSPeter Avalos 	if (scrpos.pos != NULL_POSITION)
3621133e27eSPeter Avalos 	{
3631133e27eSPeter Avalos 		store_pos(curr_ifile, &scrpos);
3641133e27eSPeter Avalos 		lastmark();
3651133e27eSPeter Avalos 	}
3661133e27eSPeter Avalos 	/*
3671133e27eSPeter Avalos 	 * Close the file descriptor, unless it is a pipe.
3681133e27eSPeter Avalos 	 */
3691133e27eSPeter Avalos 	ch_close();
3701133e27eSPeter Avalos 	/*
3711133e27eSPeter Avalos 	 * If we opened a file using an alternate name,
3721133e27eSPeter Avalos 	 * do special stuff to close it.
3731133e27eSPeter Avalos 	 */
37402d62a0fSDaniel Fojt 	altfilename = get_altfilename(curr_ifile);
37502d62a0fSDaniel Fojt 	if (altfilename != NULL)
3761133e27eSPeter Avalos 	{
377*320d7c8aSAaron LI 		close_altpipe(curr_ifile);
37802d62a0fSDaniel Fojt 		close_altfile(altfilename, get_filename(curr_ifile));
37902d62a0fSDaniel Fojt 		set_altfilename(curr_ifile, NULL);
3801133e27eSPeter Avalos 	}
3811133e27eSPeter Avalos 	curr_ifile = NULL_IFILE;
3821133e27eSPeter Avalos #if HAVE_STAT_INO
3831133e27eSPeter Avalos 	curr_ino = curr_dev = 0;
3841133e27eSPeter Avalos #endif
3851133e27eSPeter Avalos }
3861133e27eSPeter Avalos 
3871133e27eSPeter Avalos /*
3881133e27eSPeter Avalos  * Edit a new file (given its name).
3891133e27eSPeter Avalos  * Filename == "-" means standard input.
3901133e27eSPeter Avalos  * Filename == NULL means just close the current file.
3911133e27eSPeter Avalos  */
edit(char * filename)392*320d7c8aSAaron LI public int edit(char *filename)
3931133e27eSPeter Avalos {
3941133e27eSPeter Avalos 	if (filename == NULL)
3951133e27eSPeter Avalos 		return (edit_ifile(NULL_IFILE));
3961133e27eSPeter Avalos 	return (edit_ifile(get_ifile(filename, curr_ifile)));
3971133e27eSPeter Avalos }
3981133e27eSPeter Avalos 
3991133e27eSPeter Avalos /*
400*320d7c8aSAaron LI  * Clean up what edit_ifile did before error return.
401*320d7c8aSAaron LI  */
edit_error(char * filename,char * alt_filename,void * altpipe,IFILE ifile,IFILE was_curr_ifile)402*320d7c8aSAaron LI static int edit_error(char *filename, char *alt_filename, void *altpipe, IFILE ifile, IFILE was_curr_ifile)
403*320d7c8aSAaron LI {
404*320d7c8aSAaron LI 	if (alt_filename != NULL)
405*320d7c8aSAaron LI 	{
406*320d7c8aSAaron LI 		close_pipe(altpipe);
407*320d7c8aSAaron LI 		close_altfile(alt_filename, filename);
408*320d7c8aSAaron LI 		free(alt_filename);
409*320d7c8aSAaron LI 	}
410*320d7c8aSAaron LI 	del_ifile(ifile);
411*320d7c8aSAaron LI 	free(filename);
412*320d7c8aSAaron LI 	/*
413*320d7c8aSAaron LI 	 * Re-open the current file.
414*320d7c8aSAaron LI 	 */
415*320d7c8aSAaron LI 	if (was_curr_ifile == ifile)
416*320d7c8aSAaron LI 	{
417*320d7c8aSAaron LI 		/*
418*320d7c8aSAaron LI 		 * Whoops.  The "current" ifile is the one we just deleted.
419*320d7c8aSAaron LI 		 * Just give up.
420*320d7c8aSAaron LI 		 */
421*320d7c8aSAaron LI 		quit(QUIT_ERROR);
422*320d7c8aSAaron LI 	}
423*320d7c8aSAaron LI 	reedit_ifile(was_curr_ifile);
424*320d7c8aSAaron LI 	return (1);
425*320d7c8aSAaron LI }
426*320d7c8aSAaron LI 
427*320d7c8aSAaron LI /*
4281133e27eSPeter Avalos  * Edit a new file (given its IFILE).
4291133e27eSPeter Avalos  * ifile == NULL means just close the current file.
4301133e27eSPeter Avalos  */
edit_ifile(IFILE ifile)431*320d7c8aSAaron LI public int edit_ifile(IFILE ifile)
4321133e27eSPeter Avalos {
4331133e27eSPeter Avalos 	int f;
4341133e27eSPeter Avalos 	int answer;
4351133e27eSPeter Avalos 	int chflags;
4361133e27eSPeter Avalos 	char *filename;
4371133e27eSPeter Avalos 	char *open_filename;
4381133e27eSPeter Avalos 	char *alt_filename;
43902d62a0fSDaniel Fojt 	void *altpipe;
4401133e27eSPeter Avalos 	IFILE was_curr_ifile;
4411133e27eSPeter Avalos 	PARG parg;
4421133e27eSPeter Avalos 
4431133e27eSPeter Avalos 	if (ifile == curr_ifile)
4441133e27eSPeter Avalos 	{
4451133e27eSPeter Avalos 		/*
4461133e27eSPeter Avalos 		 * Already have the correct file open.
4471133e27eSPeter Avalos 		 */
4481133e27eSPeter Avalos 		return (0);
4491133e27eSPeter Avalos 	}
4501133e27eSPeter Avalos 
4511133e27eSPeter Avalos 	/*
4521133e27eSPeter Avalos 	 * We must close the currently open file now.
4531133e27eSPeter Avalos 	 * This is necessary to make the open_altfile/close_altfile pairs
4541133e27eSPeter Avalos 	 * nest properly (or rather to avoid nesting at all).
4551133e27eSPeter Avalos 	 * {{ Some stupid implementations of popen() mess up if you do:
4561133e27eSPeter Avalos 	 *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
4571133e27eSPeter Avalos 	 */
4581133e27eSPeter Avalos #if LOGFILE
4591133e27eSPeter Avalos 	end_logfile();
4601133e27eSPeter Avalos #endif
4611133e27eSPeter Avalos 	was_curr_ifile = save_curr_ifile();
4621133e27eSPeter Avalos 	if (curr_ifile != NULL_IFILE)
4631133e27eSPeter Avalos 	{
4641133e27eSPeter Avalos 		chflags = ch_getflags();
4651133e27eSPeter Avalos 		close_file();
4661133e27eSPeter Avalos 		if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
4671133e27eSPeter Avalos 		{
4681133e27eSPeter Avalos 			/*
4691133e27eSPeter Avalos 			 * Don't keep the help file in the ifile list.
4701133e27eSPeter Avalos 			 */
4711133e27eSPeter Avalos 			del_ifile(was_curr_ifile);
4721133e27eSPeter Avalos 			was_curr_ifile = old_ifile;
4731133e27eSPeter Avalos 		}
4741133e27eSPeter Avalos 	}
4751133e27eSPeter Avalos 
4761133e27eSPeter Avalos 	if (ifile == NULL_IFILE)
4771133e27eSPeter Avalos 	{
4781133e27eSPeter Avalos 		/*
4791133e27eSPeter Avalos 		 * No new file to open.
4801133e27eSPeter Avalos 		 * (Don't set old_ifile, because if you call edit_ifile(NULL),
4811133e27eSPeter Avalos 		 *  you're supposed to have saved curr_ifile yourself,
4821133e27eSPeter Avalos 		 *  and you'll restore it if necessary.)
4831133e27eSPeter Avalos 		 */
4841133e27eSPeter Avalos 		unsave_ifile(was_curr_ifile);
4851133e27eSPeter Avalos 		return (0);
4861133e27eSPeter Avalos 	}
4871133e27eSPeter Avalos 
4881133e27eSPeter Avalos 	filename = save(get_filename(ifile));
48902d62a0fSDaniel Fojt 
4901133e27eSPeter Avalos 	/*
4911133e27eSPeter Avalos 	 * See if LESSOPEN specifies an "alternate" file to open.
4921133e27eSPeter Avalos 	 */
49302d62a0fSDaniel Fojt 	altpipe = get_altpipe(ifile);
49402d62a0fSDaniel Fojt 	if (altpipe != NULL)
49502d62a0fSDaniel Fojt 	{
49602d62a0fSDaniel Fojt 		/*
49702d62a0fSDaniel Fojt 		 * File is already open.
49802d62a0fSDaniel Fojt 		 * chflags and f are not used by ch_init if ifile has
49902d62a0fSDaniel Fojt 		 * filestate which should be the case if we're here.
50002d62a0fSDaniel Fojt 		 * Set them here to avoid uninitialized variable warnings.
50102d62a0fSDaniel Fojt 		 */
50202d62a0fSDaniel Fojt 		chflags = 0;
50302d62a0fSDaniel Fojt 		f = -1;
50402d62a0fSDaniel Fojt 		alt_filename = get_altfilename(ifile);
5051133e27eSPeter Avalos 		open_filename = (alt_filename != NULL) ? alt_filename : filename;
50602d62a0fSDaniel Fojt 	} else
50702d62a0fSDaniel Fojt 	{
50802d62a0fSDaniel Fojt 		if (strcmp(filename, FAKE_HELPFILE) == 0 ||
50902d62a0fSDaniel Fojt 			 strcmp(filename, FAKE_EMPTYFILE) == 0)
51002d62a0fSDaniel Fojt 			alt_filename = NULL;
51102d62a0fSDaniel Fojt 		else
51202d62a0fSDaniel Fojt 			alt_filename = open_altfile(filename, &f, &altpipe);
51302d62a0fSDaniel Fojt 
51402d62a0fSDaniel Fojt 		open_filename = (alt_filename != NULL) ? alt_filename : filename;
5151133e27eSPeter Avalos 
5161133e27eSPeter Avalos 		chflags = 0;
51702d62a0fSDaniel Fojt 		if (altpipe != NULL)
5181133e27eSPeter Avalos 		{
5191133e27eSPeter Avalos 			/*
5201133e27eSPeter Avalos 			 * The alternate "file" is actually a pipe.
5211133e27eSPeter Avalos 			 * f has already been set to the file descriptor of the pipe
5221133e27eSPeter Avalos 			 * in the call to open_altfile above.
5231133e27eSPeter Avalos 			 * Keep the file descriptor open because it was opened
5241133e27eSPeter Avalos 			 * via popen(), and pclose() wants to close it.
5251133e27eSPeter Avalos 			 */
5261133e27eSPeter Avalos 			chflags |= CH_POPENED;
52702d62a0fSDaniel Fojt 			if (strcmp(filename, "-") == 0)
52802d62a0fSDaniel Fojt 				chflags |= CH_KEEPOPEN;
52902d62a0fSDaniel Fojt 		} else if (strcmp(filename, "-") == 0)
5301133e27eSPeter Avalos 		{
5311133e27eSPeter Avalos 			/*
5321133e27eSPeter Avalos 			 * Use standard input.
5331133e27eSPeter Avalos 			 * Keep the file descriptor open because we can't reopen it.
5341133e27eSPeter Avalos 			 */
5351133e27eSPeter Avalos 			f = fd0;
5361133e27eSPeter Avalos 			chflags |= CH_KEEPOPEN;
5371133e27eSPeter Avalos 			/*
5381133e27eSPeter Avalos 			 * Must switch stdin to BINARY mode.
5391133e27eSPeter Avalos 			 */
5401133e27eSPeter Avalos 			SET_BINARY(f);
5411133e27eSPeter Avalos #if MSDOS_COMPILER==DJGPPC
5421133e27eSPeter Avalos 			/*
5431133e27eSPeter Avalos 			 * Setting stdin to binary by default causes
5441133e27eSPeter Avalos 			 * Ctrl-C to not raise SIGINT.  We must undo
5451133e27eSPeter Avalos 			 * that side-effect.
5461133e27eSPeter Avalos 			 */
5471133e27eSPeter Avalos 			__djgpp_set_ctrl_c(1);
5481133e27eSPeter Avalos #endif
549e639dc31SJohn Marino 		} else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
550e639dc31SJohn Marino 		{
551e639dc31SJohn Marino 			f = -1;
552e639dc31SJohn Marino 			chflags |= CH_NODATA;
5531133e27eSPeter Avalos 		} else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
5541133e27eSPeter Avalos 		{
5551133e27eSPeter Avalos 			f = -1;
5561133e27eSPeter Avalos 			chflags |= CH_HELPFILE;
5571133e27eSPeter Avalos 		} else if ((parg.p_string = bad_file(open_filename)) != NULL)
5581133e27eSPeter Avalos 		{
5591133e27eSPeter Avalos 			/*
5601133e27eSPeter Avalos 			 * It looks like a bad file.  Don't try to open it.
5611133e27eSPeter Avalos 			 */
5621133e27eSPeter Avalos 			error("%s", &parg);
5631133e27eSPeter Avalos 			free(parg.p_string);
564*320d7c8aSAaron LI 			return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
56502d62a0fSDaniel Fojt 		} else if ((f = open(open_filename, OPEN_READ)) < 0)
5661133e27eSPeter Avalos 		{
5671133e27eSPeter Avalos 			/*
5681133e27eSPeter Avalos 			 * Got an error trying to open it.
5691133e27eSPeter Avalos 			 */
5701133e27eSPeter Avalos 			parg.p_string = errno_message(filename);
5711133e27eSPeter Avalos 			error("%s", &parg);
5721133e27eSPeter Avalos 			free(parg.p_string);
573*320d7c8aSAaron LI 			return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
5741133e27eSPeter Avalos 		} else
5751133e27eSPeter Avalos 		{
5761133e27eSPeter Avalos 			chflags |= CH_CANSEEK;
5771133e27eSPeter Avalos 			if (!force_open && !opened(ifile) && bin_file(f))
5781133e27eSPeter Avalos 			{
5791133e27eSPeter Avalos 				/*
5801133e27eSPeter Avalos 				 * Looks like a binary file.
5811133e27eSPeter Avalos 				 * Ask user if we should proceed.
5821133e27eSPeter Avalos 				 */
5831133e27eSPeter Avalos 				parg.p_string = filename;
5841133e27eSPeter Avalos 				answer = query("\"%s\" may be a binary file.  See it anyway? ",
5851133e27eSPeter Avalos 					&parg);
5861133e27eSPeter Avalos 				if (answer != 'y' && answer != 'Y')
5871133e27eSPeter Avalos 				{
5881133e27eSPeter Avalos 					close(f);
589*320d7c8aSAaron LI 					return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
5901133e27eSPeter Avalos 				}
5911133e27eSPeter Avalos 			}
5921133e27eSPeter Avalos 		}
59302d62a0fSDaniel Fojt 	}
594*320d7c8aSAaron LI 	if (!force_open && f >= 0 && isatty(f))
595*320d7c8aSAaron LI 	{
596*320d7c8aSAaron LI 		PARG parg;
597*320d7c8aSAaron LI 		parg.p_string = filename;
598*320d7c8aSAaron LI 		error("%s is a terminal (use -f to open it)", &parg);
599*320d7c8aSAaron LI 		return edit_error(filename, alt_filename, altpipe, ifile, was_curr_ifile);
600*320d7c8aSAaron LI 	}
6011133e27eSPeter Avalos 
6021133e27eSPeter Avalos 	/*
6031133e27eSPeter Avalos 	 * Get the new ifile.
6041133e27eSPeter Avalos 	 * Get the saved position for the file.
6051133e27eSPeter Avalos 	 */
6061133e27eSPeter Avalos 	if (was_curr_ifile != NULL_IFILE)
6071133e27eSPeter Avalos 	{
6081133e27eSPeter Avalos 		old_ifile = was_curr_ifile;
6091133e27eSPeter Avalos 		unsave_ifile(was_curr_ifile);
6101133e27eSPeter Avalos 	}
6111133e27eSPeter Avalos 	curr_ifile = ifile;
61202d62a0fSDaniel Fojt 	set_altfilename(curr_ifile, alt_filename);
61302d62a0fSDaniel Fojt 	set_altpipe(curr_ifile, altpipe);
6141133e27eSPeter Avalos 	set_open(curr_ifile); /* File has been opened */
6151133e27eSPeter Avalos 	get_pos(curr_ifile, &initial_scrpos);
6161133e27eSPeter Avalos 	new_file = TRUE;
6171133e27eSPeter Avalos 	ch_init(f, chflags);
6180c7ad07eSAntonio Huete Jimenez 	consecutive_nulls = 0;
619*320d7c8aSAaron LI 	check_modelines();
6201133e27eSPeter Avalos 
6211133e27eSPeter Avalos 	if (!(chflags & CH_HELPFILE))
6221133e27eSPeter Avalos 	{
6231133e27eSPeter Avalos #if LOGFILE
6241133e27eSPeter Avalos 		if (namelogfile != NULL && is_tty)
6251133e27eSPeter Avalos 			use_logfile(namelogfile);
6261133e27eSPeter Avalos #endif
6271133e27eSPeter Avalos #if HAVE_STAT_INO
6281133e27eSPeter Avalos 		/* Remember the i-number and device of the opened file. */
62902d62a0fSDaniel Fojt 		if (strcmp(open_filename, "-") != 0)
6301133e27eSPeter Avalos 		{
6311133e27eSPeter Avalos 			struct stat statbuf;
63202d62a0fSDaniel Fojt 			int r = stat(open_filename, &statbuf);
6331133e27eSPeter Avalos 			if (r == 0)
6341133e27eSPeter Avalos 			{
6351133e27eSPeter Avalos 				curr_ino = statbuf.st_ino;
6361133e27eSPeter Avalos 				curr_dev = statbuf.st_dev;
6371133e27eSPeter Avalos 			}
6381133e27eSPeter Avalos 		}
6391133e27eSPeter Avalos #endif
6401133e27eSPeter Avalos 		if (every_first_cmd != NULL)
641fa0be7c5SJohn Marino 		{
6421133e27eSPeter Avalos 			ungetsc(every_first_cmd);
6430c7ad07eSAntonio Huete Jimenez 			ungetcc_back(CHAR_END_COMMAND);
6441133e27eSPeter Avalos 		}
645fa0be7c5SJohn Marino 	}
6461133e27eSPeter Avalos 
6471133e27eSPeter Avalos 	flush();
6481133e27eSPeter Avalos 
6491133e27eSPeter Avalos 	if (is_tty)
6501133e27eSPeter Avalos 	{
6511133e27eSPeter Avalos 		/*
6521133e27eSPeter Avalos 		 * Output is to a real tty.
6531133e27eSPeter Avalos 		 */
6541133e27eSPeter Avalos 
6551133e27eSPeter Avalos 		/*
6561133e27eSPeter Avalos 		 * Indicate there is nothing displayed yet.
6571133e27eSPeter Avalos 		 */
6581133e27eSPeter Avalos 		pos_clear();
6591133e27eSPeter Avalos 		clr_linenum();
6601133e27eSPeter Avalos #if HILITE_SEARCH
6611133e27eSPeter Avalos 		clr_hilite();
6621133e27eSPeter Avalos #endif
6630c7ad07eSAntonio Huete Jimenez 		hshift = 0;
6649b760066SJohn Marino 		if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE))
66502d62a0fSDaniel Fojt 		{
66602d62a0fSDaniel Fojt 			char *qfilename = shell_quote(filename);
66702d62a0fSDaniel Fojt 			cmd_addhist(ml_examine, qfilename, 1);
66802d62a0fSDaniel Fojt 			free(qfilename);
66902d62a0fSDaniel Fojt 		}
6700c7ad07eSAntonio Huete Jimenez 		if (want_filesize)
6710c7ad07eSAntonio Huete Jimenez 			scan_eof();
6721133e27eSPeter Avalos 	}
6731133e27eSPeter Avalos 	free(filename);
6741133e27eSPeter Avalos 	return (0);
6751133e27eSPeter Avalos }
6761133e27eSPeter Avalos 
6771133e27eSPeter Avalos /*
6781133e27eSPeter Avalos  * Edit a space-separated list of files.
6791133e27eSPeter Avalos  * For each filename in the list, enter it into the ifile list.
6801133e27eSPeter Avalos  * Then edit the first one.
6811133e27eSPeter Avalos  */
edit_list(char * filelist)682*320d7c8aSAaron LI public int edit_list(char *filelist)
6831133e27eSPeter Avalos {
6841133e27eSPeter Avalos 	IFILE save_ifile;
6851133e27eSPeter Avalos 	char *good_filename;
6861133e27eSPeter Avalos 	char *filename;
6871133e27eSPeter Avalos 	char *gfilelist;
6881133e27eSPeter Avalos 	char *gfilename;
68902d62a0fSDaniel Fojt 	char *qfilename;
6901133e27eSPeter Avalos 	struct textlist tl_files;
6911133e27eSPeter Avalos 	struct textlist tl_gfiles;
6921133e27eSPeter Avalos 
6931133e27eSPeter Avalos 	save_ifile = save_curr_ifile();
6941133e27eSPeter Avalos 	good_filename = NULL;
6951133e27eSPeter Avalos 
6961133e27eSPeter Avalos 	/*
6971133e27eSPeter Avalos 	 * Run thru each filename in the list.
6981133e27eSPeter Avalos 	 * Try to glob the filename.
6991133e27eSPeter Avalos 	 * If it doesn't expand, just try to open the filename.
7001133e27eSPeter Avalos 	 * If it does expand, try to open each name in that list.
7011133e27eSPeter Avalos 	 */
7021133e27eSPeter Avalos 	init_textlist(&tl_files, filelist);
7031133e27eSPeter Avalos 	filename = NULL;
7041133e27eSPeter Avalos 	while ((filename = forw_textlist(&tl_files, filename)) != NULL)
7051133e27eSPeter Avalos 	{
7061133e27eSPeter Avalos 		gfilelist = lglob(filename);
7071133e27eSPeter Avalos 		init_textlist(&tl_gfiles, gfilelist);
7081133e27eSPeter Avalos 		gfilename = NULL;
7091133e27eSPeter Avalos 		while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
7101133e27eSPeter Avalos 		{
71102d62a0fSDaniel Fojt 			qfilename = shell_unquote(gfilename);
71202d62a0fSDaniel Fojt 			if (edit(qfilename) == 0 && good_filename == NULL)
7131133e27eSPeter Avalos 				good_filename = get_filename(curr_ifile);
71402d62a0fSDaniel Fojt 			free(qfilename);
7151133e27eSPeter Avalos 		}
7161133e27eSPeter Avalos 		free(gfilelist);
7171133e27eSPeter Avalos 	}
7181133e27eSPeter Avalos 	/*
7191133e27eSPeter Avalos 	 * Edit the first valid filename in the list.
7201133e27eSPeter Avalos 	 */
7211133e27eSPeter Avalos 	if (good_filename == NULL)
7221133e27eSPeter Avalos 	{
7231133e27eSPeter Avalos 		unsave_ifile(save_ifile);
7241133e27eSPeter Avalos 		return (1);
7251133e27eSPeter Avalos 	}
7261133e27eSPeter Avalos 	if (get_ifile(good_filename, curr_ifile) == curr_ifile)
7271133e27eSPeter Avalos 	{
7281133e27eSPeter Avalos 		/*
7291133e27eSPeter Avalos 		 * Trying to edit the current file; don't reopen it.
7301133e27eSPeter Avalos 		 */
7311133e27eSPeter Avalos 		unsave_ifile(save_ifile);
7321133e27eSPeter Avalos 		return (0);
7331133e27eSPeter Avalos 	}
7341133e27eSPeter Avalos 	reedit_ifile(save_ifile);
7351133e27eSPeter Avalos 	return (edit(good_filename));
7361133e27eSPeter Avalos }
7371133e27eSPeter Avalos 
7381133e27eSPeter Avalos /*
7391133e27eSPeter Avalos  * Edit the first file in the command line (ifile) list.
7401133e27eSPeter Avalos  */
edit_first(void)741*320d7c8aSAaron LI public int edit_first(void)
7421133e27eSPeter Avalos {
74302d62a0fSDaniel Fojt 	if (nifile() == 0)
74402d62a0fSDaniel Fojt 		return (edit_stdin());
7451133e27eSPeter Avalos 	curr_ifile = NULL_IFILE;
7461133e27eSPeter Avalos 	return (edit_next(1));
7471133e27eSPeter Avalos }
7481133e27eSPeter Avalos 
7491133e27eSPeter Avalos /*
7501133e27eSPeter Avalos  * Edit the last file in the command line (ifile) list.
7511133e27eSPeter Avalos  */
edit_last(void)752*320d7c8aSAaron LI public int edit_last(void)
7531133e27eSPeter Avalos {
7541133e27eSPeter Avalos 	curr_ifile = NULL_IFILE;
7551133e27eSPeter Avalos 	return (edit_prev(1));
7561133e27eSPeter Avalos }
7571133e27eSPeter Avalos 
7581133e27eSPeter Avalos 
7591133e27eSPeter Avalos /*
7601133e27eSPeter Avalos  * Edit the n-th next or previous file in the command line (ifile) list.
7611133e27eSPeter Avalos  */
edit_istep(IFILE h,int n,int dir)762*320d7c8aSAaron LI static int edit_istep(IFILE h, int n, int dir)
7631133e27eSPeter Avalos {
7641133e27eSPeter Avalos 	IFILE next;
7651133e27eSPeter Avalos 
7661133e27eSPeter Avalos 	/*
7671133e27eSPeter Avalos 	 * Skip n filenames, then try to edit each filename.
7681133e27eSPeter Avalos 	 */
7691133e27eSPeter Avalos 	for (;;)
7701133e27eSPeter Avalos 	{
7711133e27eSPeter Avalos 		next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
7721133e27eSPeter Avalos 		if (--n < 0)
7731133e27eSPeter Avalos 		{
7741133e27eSPeter Avalos 			if (edit_ifile(h) == 0)
7751133e27eSPeter Avalos 				break;
7761133e27eSPeter Avalos 		}
7771133e27eSPeter Avalos 		if (next == NULL_IFILE)
7781133e27eSPeter Avalos 		{
7791133e27eSPeter Avalos 			/*
7801133e27eSPeter Avalos 			 * Reached end of the ifile list.
7811133e27eSPeter Avalos 			 */
7821133e27eSPeter Avalos 			return (1);
7831133e27eSPeter Avalos 		}
7841133e27eSPeter Avalos 		if (ABORT_SIGS())
7851133e27eSPeter Avalos 		{
7861133e27eSPeter Avalos 			/*
7871133e27eSPeter Avalos 			 * Interrupt breaks out, if we're in a long
7881133e27eSPeter Avalos 			 * list of files that can't be opened.
7891133e27eSPeter Avalos 			 */
7901133e27eSPeter Avalos 			return (1);
7911133e27eSPeter Avalos 		}
7921133e27eSPeter Avalos 		h = next;
7931133e27eSPeter Avalos 	}
7941133e27eSPeter Avalos 	/*
7951133e27eSPeter Avalos 	 * Found a file that we can edit.
7961133e27eSPeter Avalos 	 */
7971133e27eSPeter Avalos 	return (0);
7981133e27eSPeter Avalos }
7991133e27eSPeter Avalos 
edit_inext(IFILE h,int n)800*320d7c8aSAaron LI static int edit_inext(IFILE h, int n)
8011133e27eSPeter Avalos {
8021133e27eSPeter Avalos 	return (edit_istep(h, n, +1));
8031133e27eSPeter Avalos }
8041133e27eSPeter Avalos 
edit_next(int n)805*320d7c8aSAaron LI public int edit_next(int n)
8061133e27eSPeter Avalos {
8071133e27eSPeter Avalos 	return edit_istep(curr_ifile, n, +1);
8081133e27eSPeter Avalos }
8091133e27eSPeter Avalos 
edit_iprev(IFILE h,int n)810*320d7c8aSAaron LI static int edit_iprev(IFILE h, int n)
8111133e27eSPeter Avalos {
8121133e27eSPeter Avalos 	return (edit_istep(h, n, -1));
8131133e27eSPeter Avalos }
8141133e27eSPeter Avalos 
edit_prev(int n)815*320d7c8aSAaron LI public int edit_prev(int n)
8161133e27eSPeter Avalos {
8171133e27eSPeter Avalos 	return edit_istep(curr_ifile, n, -1);
8181133e27eSPeter Avalos }
8191133e27eSPeter Avalos 
8201133e27eSPeter Avalos /*
8211133e27eSPeter Avalos  * Edit a specific file in the command line (ifile) list.
8221133e27eSPeter Avalos  */
edit_index(int n)823*320d7c8aSAaron LI public int edit_index(int n)
8241133e27eSPeter Avalos {
8251133e27eSPeter Avalos 	IFILE h;
8261133e27eSPeter Avalos 
8271133e27eSPeter Avalos 	h = NULL_IFILE;
8281133e27eSPeter Avalos 	do
8291133e27eSPeter Avalos 	{
8301133e27eSPeter Avalos 		if ((h = next_ifile(h)) == NULL_IFILE)
8311133e27eSPeter Avalos 		{
8321133e27eSPeter Avalos 			/*
8331133e27eSPeter Avalos 			 * Reached end of the list without finding it.
8341133e27eSPeter Avalos 			 */
8351133e27eSPeter Avalos 			return (1);
8361133e27eSPeter Avalos 		}
8371133e27eSPeter Avalos 	} while (get_index(h) != n);
8381133e27eSPeter Avalos 
8391133e27eSPeter Avalos 	return (edit_ifile(h));
8401133e27eSPeter Avalos }
8411133e27eSPeter Avalos 
save_curr_ifile(void)842*320d7c8aSAaron LI public IFILE save_curr_ifile(void)
8431133e27eSPeter Avalos {
8441133e27eSPeter Avalos 	if (curr_ifile != NULL_IFILE)
8451133e27eSPeter Avalos 		hold_ifile(curr_ifile, 1);
8461133e27eSPeter Avalos 	return (curr_ifile);
8471133e27eSPeter Avalos }
8481133e27eSPeter Avalos 
unsave_ifile(IFILE save_ifile)849*320d7c8aSAaron LI public void unsave_ifile(IFILE save_ifile)
8501133e27eSPeter Avalos {
8511133e27eSPeter Avalos 	if (save_ifile != NULL_IFILE)
8521133e27eSPeter Avalos 		hold_ifile(save_ifile, -1);
8531133e27eSPeter Avalos }
8541133e27eSPeter Avalos 
8551133e27eSPeter Avalos /*
8561133e27eSPeter Avalos  * Reedit the ifile which was previously open.
8571133e27eSPeter Avalos  */
reedit_ifile(IFILE save_ifile)858*320d7c8aSAaron LI public void reedit_ifile(IFILE save_ifile)
8591133e27eSPeter Avalos {
8601133e27eSPeter Avalos 	IFILE next;
8611133e27eSPeter Avalos 	IFILE prev;
8621133e27eSPeter Avalos 
8631133e27eSPeter Avalos 	/*
8641133e27eSPeter Avalos 	 * Try to reopen the ifile.
8651133e27eSPeter Avalos 	 * Note that opening it may fail (maybe the file was removed),
8661133e27eSPeter Avalos 	 * in which case the ifile will be deleted from the list.
8671133e27eSPeter Avalos 	 * So save the next and prev ifiles first.
8681133e27eSPeter Avalos 	 */
8691133e27eSPeter Avalos 	unsave_ifile(save_ifile);
8701133e27eSPeter Avalos 	next = next_ifile(save_ifile);
8711133e27eSPeter Avalos 	prev = prev_ifile(save_ifile);
8721133e27eSPeter Avalos 	if (edit_ifile(save_ifile) == 0)
8731133e27eSPeter Avalos 		return;
8741133e27eSPeter Avalos 	/*
8751133e27eSPeter Avalos 	 * If can't reopen it, open the next input file in the list.
8761133e27eSPeter Avalos 	 */
8771133e27eSPeter Avalos 	if (next != NULL_IFILE && edit_inext(next, 0) == 0)
8781133e27eSPeter Avalos 		return;
8791133e27eSPeter Avalos 	/*
8801133e27eSPeter Avalos 	 * If can't open THAT one, open the previous input file in the list.
8811133e27eSPeter Avalos 	 */
8821133e27eSPeter Avalos 	if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
8831133e27eSPeter Avalos 		return;
8841133e27eSPeter Avalos 	/*
8851133e27eSPeter Avalos 	 * If can't even open that, we're stuck.  Just quit.
8861133e27eSPeter Avalos 	 */
8871133e27eSPeter Avalos 	quit(QUIT_ERROR);
8881133e27eSPeter Avalos }
8891133e27eSPeter Avalos 
reopen_curr_ifile(void)890*320d7c8aSAaron LI public void reopen_curr_ifile(void)
8911133e27eSPeter Avalos {
8921133e27eSPeter Avalos 	IFILE save_ifile = save_curr_ifile();
8931133e27eSPeter Avalos 	close_file();
8941133e27eSPeter Avalos 	reedit_ifile(save_ifile);
8951133e27eSPeter Avalos }
8961133e27eSPeter Avalos 
8971133e27eSPeter Avalos /*
8981133e27eSPeter Avalos  * Edit standard input.
8991133e27eSPeter Avalos  */
edit_stdin(void)900*320d7c8aSAaron LI public int edit_stdin(void)
9011133e27eSPeter Avalos {
9021133e27eSPeter Avalos 	if (isatty(fd0))
9031133e27eSPeter Avalos 	{
9041133e27eSPeter Avalos 		error("Missing filename (\"less --help\" for help)", NULL_PARG);
9051133e27eSPeter Avalos 		quit(QUIT_OK);
9061133e27eSPeter Avalos 	}
9071133e27eSPeter Avalos 	return (edit("-"));
9081133e27eSPeter Avalos }
9091133e27eSPeter Avalos 
9101133e27eSPeter Avalos /*
9111133e27eSPeter Avalos  * Copy a file directly to standard output.
9121133e27eSPeter Avalos  * Used if standard output is not a tty.
9131133e27eSPeter Avalos  */
cat_file(void)914*320d7c8aSAaron LI public void cat_file(void)
9151133e27eSPeter Avalos {
91602d62a0fSDaniel Fojt 	int c;
9171133e27eSPeter Avalos 
9181133e27eSPeter Avalos 	while ((c = ch_forw_get()) != EOI)
9191133e27eSPeter Avalos 		putchr(c);
9201133e27eSPeter Avalos 	flush();
9211133e27eSPeter Avalos }
9221133e27eSPeter Avalos 
9231133e27eSPeter Avalos #if LOGFILE
9241133e27eSPeter Avalos 
9250c7ad07eSAntonio Huete Jimenez #define OVERWRITE_OPTIONS "Overwrite, Append, Don't log, or Quit?"
9260c7ad07eSAntonio Huete Jimenez 
9271133e27eSPeter Avalos /*
9281133e27eSPeter Avalos  * If the user asked for a log file and our input file
9291133e27eSPeter Avalos  * is standard input, create the log file.
9301133e27eSPeter Avalos  * We take care not to blindly overwrite an existing file.
9311133e27eSPeter Avalos  */
use_logfile(char * filename)932*320d7c8aSAaron LI public void use_logfile(char *filename)
9331133e27eSPeter Avalos {
93402d62a0fSDaniel Fojt 	int exists;
93502d62a0fSDaniel Fojt 	int answer;
9361133e27eSPeter Avalos 	PARG parg;
9371133e27eSPeter Avalos 
9381133e27eSPeter Avalos 	if (ch_getflags() & CH_CANSEEK)
9391133e27eSPeter Avalos 		/*
9401133e27eSPeter Avalos 		 * Can't currently use a log file on a file that can seek.
9411133e27eSPeter Avalos 		 */
9421133e27eSPeter Avalos 		return;
9431133e27eSPeter Avalos 
9441133e27eSPeter Avalos 	/*
9451133e27eSPeter Avalos 	 * {{ We could use access() here. }}
9461133e27eSPeter Avalos 	 */
9471133e27eSPeter Avalos 	exists = open(filename, OPEN_READ);
948fa0be7c5SJohn Marino 	if (exists >= 0)
9491133e27eSPeter Avalos 		close(exists);
9501133e27eSPeter Avalos 	exists = (exists >= 0);
9511133e27eSPeter Avalos 
9521133e27eSPeter Avalos 	/*
9531133e27eSPeter Avalos 	 * Decide whether to overwrite the log file or append to it.
9541133e27eSPeter Avalos 	 * If it doesn't exist we "overwrite" it.
9551133e27eSPeter Avalos 	 */
9561133e27eSPeter Avalos 	if (!exists || force_logfile)
9571133e27eSPeter Avalos 	{
9581133e27eSPeter Avalos 		/*
9591133e27eSPeter Avalos 		 * Overwrite (or create) the log file.
9601133e27eSPeter Avalos 		 */
9611133e27eSPeter Avalos 		answer = 'O';
9621133e27eSPeter Avalos 	} else
9631133e27eSPeter Avalos 	{
9641133e27eSPeter Avalos 		/*
9651133e27eSPeter Avalos 		 * Ask user what to do.
9661133e27eSPeter Avalos 		 */
9671133e27eSPeter Avalos 		parg.p_string = filename;
9680c7ad07eSAntonio Huete Jimenez 		answer = query("Warning: \"%s\" exists; "OVERWRITE_OPTIONS" ", &parg);
9691133e27eSPeter Avalos 	}
9701133e27eSPeter Avalos 
9711133e27eSPeter Avalos loop:
9721133e27eSPeter Avalos 	switch (answer)
9731133e27eSPeter Avalos 	{
9741133e27eSPeter Avalos 	case 'O': case 'o':
9751133e27eSPeter Avalos 		/*
9761133e27eSPeter Avalos 		 * Overwrite: create the file.
9771133e27eSPeter Avalos 		 */
978*320d7c8aSAaron LI 		logfile = creat(filename, CREAT_RW);
9791133e27eSPeter Avalos 		break;
9801133e27eSPeter Avalos 	case 'A': case 'a':
9811133e27eSPeter Avalos 		/*
9821133e27eSPeter Avalos 		 * Append: open the file and seek to the end.
9831133e27eSPeter Avalos 		 */
9841133e27eSPeter Avalos 		logfile = open(filename, OPEN_APPEND);
9851133e27eSPeter Avalos 		if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
9861133e27eSPeter Avalos 		{
9871133e27eSPeter Avalos 			close(logfile);
9881133e27eSPeter Avalos 			logfile = -1;
9891133e27eSPeter Avalos 		}
9901133e27eSPeter Avalos 		break;
9911133e27eSPeter Avalos 	case 'D': case 'd':
9921133e27eSPeter Avalos 		/*
9931133e27eSPeter Avalos 		 * Don't do anything.
9941133e27eSPeter Avalos 		 */
9951133e27eSPeter Avalos 		return;
9961133e27eSPeter Avalos 	default:
9971133e27eSPeter Avalos 		/*
9981133e27eSPeter Avalos 		 * Eh?
9991133e27eSPeter Avalos 		 */
10000c7ad07eSAntonio Huete Jimenez 
10010c7ad07eSAntonio Huete Jimenez 		answer = query(OVERWRITE_OPTIONS" (Type \"O\", \"A\", \"D\" or \"Q\") ", NULL_PARG);
10021133e27eSPeter Avalos 		goto loop;
10031133e27eSPeter Avalos 	}
10041133e27eSPeter Avalos 
10051133e27eSPeter Avalos 	if (logfile < 0)
10061133e27eSPeter Avalos 	{
10071133e27eSPeter Avalos 		/*
10081133e27eSPeter Avalos 		 * Error in opening logfile.
10091133e27eSPeter Avalos 		 */
10101133e27eSPeter Avalos 		parg.p_string = filename;
10111133e27eSPeter Avalos 		error("Cannot write to \"%s\"", &parg);
10121133e27eSPeter Avalos 		return;
10131133e27eSPeter Avalos 	}
10141133e27eSPeter Avalos 	SET_BINARY(logfile);
10151133e27eSPeter Avalos }
10161133e27eSPeter Avalos 
10171133e27eSPeter Avalos #endif
1018