1238730Sdelphij/*
2330571Sdelphij * Copyright (C) 1984-2017  Mark Nudelman
3238730Sdelphij *
4238730Sdelphij * You may distribute under the terms of either the GNU General Public
5238730Sdelphij * License or the Less License, as specified in the README file.
6238730Sdelphij *
7238730Sdelphij * For more information, see the README file.
8238730Sdelphij */
960786Sps
1060786Sps
1160786Sps#include "less.h"
12330571Sdelphij#include "position.h"
13173682Sdelphij#if HAVE_STAT
14173682Sdelphij#include <sys/stat.h>
15173682Sdelphij#endif
16330571Sdelphij#if OS2
17330571Sdelphij#include <signal.h>
18330571Sdelphij#endif
1960786Sps
2060786Spspublic int fd0 = 0;
2160786Sps
2260786Spsextern int new_file;
2360786Spsextern int errmsgs;
2460786Spsextern int cbufs;
2560786Spsextern char *every_first_cmd;
2660786Spsextern int any_display;
2760786Spsextern int force_open;
2860786Spsextern int is_tty;
2960786Spsextern int sigs;
3060786Spsextern IFILE curr_ifile;
3160786Spsextern IFILE old_ifile;
3260786Spsextern struct scrpos initial_scrpos;
33330571Sdelphijextern void *ml_examine;
3460786Sps#if SPACES_IN_FILENAMES
3560786Spsextern char openquote;
3660786Spsextern char closequote;
3760786Sps#endif
3860786Sps
3960786Sps#if LOGFILE
4060786Spsextern int logfile;
4160786Spsextern int force_logfile;
4260786Spsextern char *namelogfile;
4360786Sps#endif
4460786Sps
45173682Sdelphij#if HAVE_STAT_INO
46173682Sdelphijpublic dev_t curr_dev;
47173682Sdelphijpublic ino_t curr_ino;
48173682Sdelphij#endif
49173682Sdelphij
5060786Sps
5160786Sps/*
5260786Sps * Textlist functions deal with a list of words separated by spaces.
5360786Sps * init_textlist sets up a textlist structure.
5460786Sps * forw_textlist uses that structure to iterate thru the list of
5560786Sps * words, returning each one as a standard null-terminated string.
5660786Sps * back_textlist does the same, but runs thru the list backwards.
5760786Sps */
5860786Sps	public void
5960786Spsinit_textlist(tlist, str)
6060786Sps	struct textlist *tlist;
6160786Sps	char *str;
6260786Sps{
6360786Sps	char *s;
6460786Sps#if SPACES_IN_FILENAMES
65128345Stjr	int meta_quoted = 0;
66128345Stjr	int delim_quoted = 0;
67128345Stjr	char *esc = get_meta_escape();
68294286Sdelphij	int esclen = (int) strlen(esc);
6960786Sps#endif
7060786Sps
7160786Sps	tlist->string = skipsp(str);
7260786Sps	tlist->endstring = tlist->string + strlen(tlist->string);
7360786Sps	for (s = str;  s < tlist->endstring;  s++)
7460786Sps	{
7560786Sps#if SPACES_IN_FILENAMES
76128345Stjr		if (meta_quoted)
77128345Stjr		{
78128345Stjr			meta_quoted = 0;
79128345Stjr		} else if (esclen > 0 && s + esclen < tlist->endstring &&
80128345Stjr		           strncmp(s, esc, esclen) == 0)
81128345Stjr		{
82128345Stjr			meta_quoted = 1;
83128345Stjr			s += esclen - 1;
84128345Stjr		} else if (delim_quoted)
85128345Stjr		{
86128345Stjr			if (*s == closequote)
87128345Stjr				delim_quoted = 0;
88128345Stjr		} else /* (!delim_quoted) */
89128345Stjr		{
90128345Stjr			if (*s == openquote)
91128345Stjr				delim_quoted = 1;
92128345Stjr			else if (*s == ' ')
93128345Stjr				*s = '\0';
94128345Stjr		}
9560786Sps#else
9660786Sps		if (*s == ' ')
9760786Sps			*s = '\0';
9860786Sps#endif
9960786Sps	}
10060786Sps}
10160786Sps
10260786Sps	public char *
10360786Spsforw_textlist(tlist, prev)
10460786Sps	struct textlist *tlist;
10560786Sps	char *prev;
10660786Sps{
10760786Sps	char *s;
10860786Sps
10960786Sps	/*
11060786Sps	 * prev == NULL means return the first word in the list.
11160786Sps	 * Otherwise, return the word after "prev".
11260786Sps	 */
11360786Sps	if (prev == NULL)
11460786Sps		s = tlist->string;
11560786Sps	else
11660786Sps		s = prev + strlen(prev);
11760786Sps	if (s >= tlist->endstring)
11860786Sps		return (NULL);
11960786Sps	while (*s == '\0')
12060786Sps		s++;
12160786Sps	if (s >= tlist->endstring)
12260786Sps		return (NULL);
12360786Sps	return (s);
12460786Sps}
12560786Sps
12660786Sps	public char *
12760786Spsback_textlist(tlist, prev)
12860786Sps	struct textlist *tlist;
12960786Sps	char *prev;
13060786Sps{
13160786Sps	char *s;
13260786Sps
13360786Sps	/*
13460786Sps	 * prev == NULL means return the last word in the list.
13560786Sps	 * Otherwise, return the word before "prev".
13660786Sps	 */
13760786Sps	if (prev == NULL)
13860786Sps		s = tlist->endstring;
13960786Sps	else if (prev <= tlist->string)
14060786Sps		return (NULL);
14160786Sps	else
14260786Sps		s = prev - 1;
14360786Sps	while (*s == '\0')
14460786Sps		s--;
14560786Sps	if (s <= tlist->string)
14660786Sps		return (NULL);
14760786Sps	while (s[-1] != '\0' && s > tlist->string)
14860786Sps		s--;
14960786Sps	return (s);
15060786Sps}
15160786Sps
15260786Sps/*
153330571Sdelphij * Close a pipe opened via popen.
154330571Sdelphij */
155330571Sdelphij	static void
156330571Sdelphijclose_pipe(FILE *pipefd)
157330571Sdelphij{
158330571Sdelphij	if (pipefd == NULL)
159330571Sdelphij		return;
160330571Sdelphij#if OS2
161330571Sdelphij	/*
162330571Sdelphij	 * The pclose function of OS/2 emx sometimes fails.
163330571Sdelphij	 * Send SIGINT to the piped process before closing it.
164330571Sdelphij	 */
165330571Sdelphij	kill(pipefd->_pid, SIGINT);
166330571Sdelphij#endif
167330571Sdelphij	pclose(pipefd);
168330571Sdelphij}
169330571Sdelphij
170330571Sdelphij/*
17160786Sps * Close the current input file.
17260786Sps */
17360786Sps	static void
17460786Spsclose_file()
17560786Sps{
17660786Sps	struct scrpos scrpos;
177330571Sdelphij	int chflags;
178330571Sdelphij	FILE *altpipe;
179330571Sdelphij	char *altfilename;
18060786Sps
18160786Sps	if (curr_ifile == NULL_IFILE)
18260786Sps		return;
18360786Sps
18460786Sps	/*
18560786Sps	 * Save the current position so that we can return to
18660786Sps	 * the same position if we edit this file again.
18760786Sps	 */
188330571Sdelphij	get_scrpos(&scrpos, TOP);
18960786Sps	if (scrpos.pos != NULL_POSITION)
19060786Sps	{
19160786Sps		store_pos(curr_ifile, &scrpos);
19260786Sps		lastmark();
19360786Sps	}
19460786Sps	/*
19560786Sps	 * Close the file descriptor, unless it is a pipe.
19660786Sps	 */
197330571Sdelphij	chflags = ch_getflags();
19860786Sps	ch_close();
19960786Sps	/*
20060786Sps	 * If we opened a file using an alternate name,
20160786Sps	 * do special stuff to close it.
20260786Sps	 */
203330571Sdelphij	altfilename = get_altfilename(curr_ifile);
204330571Sdelphij	if (altfilename != NULL)
20560786Sps	{
206330571Sdelphij		altpipe = get_altpipe(curr_ifile);
207330571Sdelphij		if (altpipe != NULL && !(chflags & CH_KEEPOPEN))
208330571Sdelphij		{
209330571Sdelphij			close_pipe(altpipe);
210330571Sdelphij			set_altpipe(curr_ifile, NULL);
211330571Sdelphij		}
212330571Sdelphij		close_altfile(altfilename, get_filename(curr_ifile));
213330571Sdelphij		set_altfilename(curr_ifile, NULL);
21460786Sps	}
21560786Sps	curr_ifile = NULL_IFILE;
216173682Sdelphij#if HAVE_STAT_INO
217173682Sdelphij	curr_ino = curr_dev = 0;
218173682Sdelphij#endif
21960786Sps}
22060786Sps
22160786Sps/*
22260786Sps * Edit a new file (given its name).
22360786Sps * Filename == "-" means standard input.
22460786Sps * Filename == NULL means just close the current file.
22560786Sps */
22660786Sps	public int
22760786Spsedit(filename)
22860786Sps	char *filename;
22960786Sps{
23060786Sps	if (filename == NULL)
23160786Sps		return (edit_ifile(NULL_IFILE));
23260786Sps	return (edit_ifile(get_ifile(filename, curr_ifile)));
23360786Sps}
23460786Sps
23560786Sps/*
23660786Sps * Edit a new file (given its IFILE).
23760786Sps * ifile == NULL means just close the current file.
23860786Sps */
23960786Sps	public int
24060786Spsedit_ifile(ifile)
24160786Sps	IFILE ifile;
24260786Sps{
24360786Sps	int f;
24460786Sps	int answer;
24560786Sps	int no_display;
24660786Sps	int chflags;
24760786Sps	char *filename;
24860786Sps	char *open_filename;
24960786Sps	char *alt_filename;
250330571Sdelphij	void *altpipe;
25160786Sps	IFILE was_curr_ifile;
25260786Sps	PARG parg;
25360786Sps
25460786Sps	if (ifile == curr_ifile)
25560786Sps	{
25660786Sps		/*
25760786Sps		 * Already have the correct file open.
25860786Sps		 */
25960786Sps		return (0);
26060786Sps	}
26160786Sps
26260786Sps	/*
26360786Sps	 * We must close the currently open file now.
26460786Sps	 * This is necessary to make the open_altfile/close_altfile pairs
26560786Sps	 * nest properly (or rather to avoid nesting at all).
26660786Sps	 * {{ Some stupid implementations of popen() mess up if you do:
26760786Sps	 *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
26860786Sps	 */
26960786Sps#if LOGFILE
27060786Sps	end_logfile();
27160786Sps#endif
27260786Sps	was_curr_ifile = save_curr_ifile();
27360786Sps	if (curr_ifile != NULL_IFILE)
27460786Sps	{
27560786Sps		chflags = ch_getflags();
27660786Sps		close_file();
27760786Sps		if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
27860786Sps		{
27960786Sps			/*
28060786Sps			 * Don't keep the help file in the ifile list.
28160786Sps			 */
28260786Sps			del_ifile(was_curr_ifile);
28360786Sps			was_curr_ifile = old_ifile;
28460786Sps		}
28560786Sps	}
28660786Sps
28760786Sps	if (ifile == NULL_IFILE)
28860786Sps	{
28960786Sps		/*
29060786Sps		 * No new file to open.
29160786Sps		 * (Don't set old_ifile, because if you call edit_ifile(NULL),
29260786Sps		 *  you're supposed to have saved curr_ifile yourself,
29360786Sps		 *  and you'll restore it if necessary.)
29460786Sps		 */
29560786Sps		unsave_ifile(was_curr_ifile);
29660786Sps		return (0);
29760786Sps	}
29860786Sps
299128345Stjr	filename = save(get_filename(ifile));
300330571Sdelphij
30160786Sps	/*
30260786Sps	 * See if LESSOPEN specifies an "alternate" file to open.
30360786Sps	 */
304330571Sdelphij	altpipe = get_altpipe(ifile);
305330571Sdelphij	if (altpipe != NULL)
30660786Sps	{
30760786Sps		/*
308330571Sdelphij		 * File is already open.
309330571Sdelphij		 * chflags and f are not used by ch_init if ifile has
310330571Sdelphij		 * filestate which should be the case if we're here.
311330571Sdelphij		 * Set them here to avoid uninitialized variable warnings.
31260786Sps		 */
313330571Sdelphij		chflags = 0;
314330571Sdelphij		f = -1;
315330571Sdelphij		alt_filename = get_altfilename(ifile);
316330571Sdelphij		open_filename = (alt_filename != NULL) ? alt_filename : filename;
317330571Sdelphij	} else
31860786Sps	{
319330571Sdelphij		if (strcmp(filename, FAKE_HELPFILE) == 0 ||
320330571Sdelphij			 strcmp(filename, FAKE_EMPTYFILE) == 0)
321330571Sdelphij			alt_filename = NULL;
322330571Sdelphij		else
323330571Sdelphij			alt_filename = open_altfile(filename, &f, &altpipe);
324330571Sdelphij
325330571Sdelphij		open_filename = (alt_filename != NULL) ? alt_filename : filename;
326330571Sdelphij
327330571Sdelphij		chflags = 0;
328330571Sdelphij		if (altpipe != NULL)
329330571Sdelphij		{
330330571Sdelphij			/*
331330571Sdelphij			 * The alternate "file" is actually a pipe.
332330571Sdelphij			 * f has already been set to the file descriptor of the pipe
333330571Sdelphij			 * in the call to open_altfile above.
334330571Sdelphij			 * Keep the file descriptor open because it was opened
335330571Sdelphij			 * via popen(), and pclose() wants to close it.
336330571Sdelphij			 */
337330571Sdelphij			chflags |= CH_POPENED;
338330571Sdelphij			if (strcmp(filename, "-") == 0)
339330571Sdelphij				chflags |= CH_KEEPOPEN;
340330571Sdelphij		} else if (strcmp(filename, "-") == 0)
341330571Sdelphij		{
342330571Sdelphij			/*
343330571Sdelphij			 * Use standard input.
344330571Sdelphij			 * Keep the file descriptor open because we can't reopen it.
345330571Sdelphij			 */
346330571Sdelphij			f = fd0;
347330571Sdelphij			chflags |= CH_KEEPOPEN;
348330571Sdelphij			/*
349330571Sdelphij			 * Must switch stdin to BINARY mode.
350330571Sdelphij			 */
351330571Sdelphij			SET_BINARY(f);
35260786Sps#if MSDOS_COMPILER==DJGPPC
353330571Sdelphij			/*
354330571Sdelphij			 * Setting stdin to binary by default causes
355330571Sdelphij			 * Ctrl-C to not raise SIGINT.  We must undo
356330571Sdelphij			 * that side-effect.
357330571Sdelphij			 */
358330571Sdelphij			__djgpp_set_ctrl_c(1);
35960786Sps#endif
360330571Sdelphij		} else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
36160786Sps		{
362330571Sdelphij			f = -1;
363330571Sdelphij			chflags |= CH_NODATA;
364330571Sdelphij		} else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
365161475Sdelphij		{
366330571Sdelphij			f = -1;
367330571Sdelphij			chflags |= CH_HELPFILE;
368330571Sdelphij		} else if ((parg.p_string = bad_file(open_filename)) != NULL)
369330571Sdelphij		{
370161475Sdelphij			/*
371330571Sdelphij			 * It looks like a bad file.  Don't try to open it.
372161475Sdelphij			 */
373330571Sdelphij			error("%s", &parg);
374330571Sdelphij			free(parg.p_string);
375330571Sdelphij			err1:
376330571Sdelphij			if (alt_filename != NULL)
377330571Sdelphij			{
378330571Sdelphij				close_pipe(altpipe);
379330571Sdelphij				close_altfile(alt_filename, filename);
380330571Sdelphij				free(alt_filename);
381330571Sdelphij			}
382330571Sdelphij			del_ifile(ifile);
383330571Sdelphij			free(filename);
384330571Sdelphij			/*
385330571Sdelphij			 * Re-open the current file.
386330571Sdelphij			 */
387330571Sdelphij			if (was_curr_ifile == ifile)
388330571Sdelphij			{
389330571Sdelphij				/*
390330571Sdelphij				 * Whoops.  The "current" ifile is the one we just deleted.
391330571Sdelphij				 * Just give up.
392330571Sdelphij				 */
393330571Sdelphij				quit(QUIT_ERROR);
394330571Sdelphij			}
395330571Sdelphij			reedit_ifile(was_curr_ifile);
396330571Sdelphij			return (1);
397330571Sdelphij		} else if ((f = open(open_filename, OPEN_READ)) < 0)
39860786Sps		{
39960786Sps			/*
400330571Sdelphij			 * Got an error trying to open it.
40160786Sps			 */
402330571Sdelphij			parg.p_string = errno_message(filename);
403330571Sdelphij			error("%s", &parg);
404330571Sdelphij			free(parg.p_string);
405330571Sdelphij				goto err1;
406330571Sdelphij		} else
407330571Sdelphij		{
408330571Sdelphij			chflags |= CH_CANSEEK;
409330571Sdelphij			if (!force_open && !opened(ifile) && bin_file(f))
41060786Sps			{
411330571Sdelphij				/*
412330571Sdelphij				 * Looks like a binary file.
413330571Sdelphij				 * Ask user if we should proceed.
414330571Sdelphij				 */
415330571Sdelphij				parg.p_string = filename;
416330571Sdelphij				answer = query("\"%s\" may be a binary file.  See it anyway? ",
417330571Sdelphij					&parg);
418330571Sdelphij				if (answer != 'y' && answer != 'Y')
419330571Sdelphij				{
420330571Sdelphij					close(f);
421330571Sdelphij					goto err1;
422330571Sdelphij				}
42360786Sps			}
42460786Sps		}
42560786Sps	}
42660786Sps
42760786Sps	/*
42860786Sps	 * Get the new ifile.
42960786Sps	 * Get the saved position for the file.
43060786Sps	 */
43160786Sps	if (was_curr_ifile != NULL_IFILE)
43260786Sps	{
43360786Sps		old_ifile = was_curr_ifile;
43460786Sps		unsave_ifile(was_curr_ifile);
43560786Sps	}
43660786Sps	curr_ifile = ifile;
437330571Sdelphij	set_altfilename(curr_ifile, alt_filename);
438330571Sdelphij	set_altpipe(curr_ifile, altpipe);
43960786Sps	set_open(curr_ifile); /* File has been opened */
44060786Sps	get_pos(curr_ifile, &initial_scrpos);
44160786Sps	new_file = TRUE;
44260786Sps	ch_init(f, chflags);
44360786Sps
44460786Sps	if (!(chflags & CH_HELPFILE))
44560786Sps	{
44660786Sps#if LOGFILE
44760786Sps		if (namelogfile != NULL && is_tty)
44860786Sps			use_logfile(namelogfile);
44960786Sps#endif
450173682Sdelphij#if HAVE_STAT_INO
451173682Sdelphij		/* Remember the i-number and device of the opened file. */
452330571Sdelphij		if (strcmp(open_filename, "-") != 0)
453173682Sdelphij		{
454173682Sdelphij			struct stat statbuf;
455330571Sdelphij			int r = stat(open_filename, &statbuf);
456173682Sdelphij			if (r == 0)
457173682Sdelphij			{
458173682Sdelphij				curr_ino = statbuf.st_ino;
459173682Sdelphij				curr_dev = statbuf.st_dev;
460173682Sdelphij			}
461173682Sdelphij		}
462173682Sdelphij#endif
46360786Sps		if (every_first_cmd != NULL)
464294286Sdelphij		{
465294286Sdelphij			ungetcc(CHAR_END_COMMAND);
46660786Sps			ungetsc(every_first_cmd);
467294286Sdelphij		}
46860786Sps	}
46960786Sps
47060786Sps	no_display = !any_display;
47160786Sps	flush();
47260786Sps	any_display = TRUE;
47360786Sps
47460786Sps	if (is_tty)
47560786Sps	{
47660786Sps		/*
47760786Sps		 * Output is to a real tty.
47860786Sps		 */
47960786Sps
48060786Sps		/*
48160786Sps		 * Indicate there is nothing displayed yet.
48260786Sps		 */
48360786Sps		pos_clear();
48460786Sps		clr_linenum();
48560786Sps#if HILITE_SEARCH
48660786Sps		clr_hilite();
48760786Sps#endif
488294286Sdelphij		if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE))
489294286Sdelphij			cmd_addhist(ml_examine, filename, 1);
49060786Sps		if (no_display && errmsgs > 0)
49160786Sps		{
49260786Sps			/*
49360786Sps			 * We displayed some messages on error output
49460786Sps			 * (file descriptor 2; see error() function).
49560786Sps			 * Before erasing the screen contents,
49660786Sps			 * display the file name and wait for a keystroke.
49760786Sps			 */
49860786Sps			parg.p_string = filename;
49960786Sps			error("%s", &parg);
50060786Sps		}
50160786Sps	}
50260786Sps	free(filename);
50360786Sps	return (0);
50460786Sps}
50560786Sps
50660786Sps/*
50760786Sps * Edit a space-separated list of files.
50860786Sps * For each filename in the list, enter it into the ifile list.
50960786Sps * Then edit the first one.
51060786Sps */
51160786Sps	public int
51260786Spsedit_list(filelist)
51360786Sps	char *filelist;
51460786Sps{
51560786Sps	IFILE save_ifile;
51660786Sps	char *good_filename;
51760786Sps	char *filename;
51860786Sps	char *gfilelist;
51960786Sps	char *gfilename;
520330571Sdelphij	char *qfilename;
52160786Sps	struct textlist tl_files;
52260786Sps	struct textlist tl_gfiles;
52360786Sps
52460786Sps	save_ifile = save_curr_ifile();
52560786Sps	good_filename = NULL;
52660786Sps
52760786Sps	/*
52860786Sps	 * Run thru each filename in the list.
52960786Sps	 * Try to glob the filename.
53060786Sps	 * If it doesn't expand, just try to open the filename.
53160786Sps	 * If it does expand, try to open each name in that list.
53260786Sps	 */
53360786Sps	init_textlist(&tl_files, filelist);
53460786Sps	filename = NULL;
53560786Sps	while ((filename = forw_textlist(&tl_files, filename)) != NULL)
53660786Sps	{
53760786Sps		gfilelist = lglob(filename);
53860786Sps		init_textlist(&tl_gfiles, gfilelist);
53960786Sps		gfilename = NULL;
54060786Sps		while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
54160786Sps		{
542330571Sdelphij			qfilename = shell_unquote(gfilename);
543330571Sdelphij			if (edit(qfilename) == 0 && good_filename == NULL)
54460786Sps				good_filename = get_filename(curr_ifile);
545330571Sdelphij			free(qfilename);
54660786Sps		}
54760786Sps		free(gfilelist);
54860786Sps	}
54960786Sps	/*
55060786Sps	 * Edit the first valid filename in the list.
55160786Sps	 */
55260786Sps	if (good_filename == NULL)
55360786Sps	{
55460786Sps		unsave_ifile(save_ifile);
55560786Sps		return (1);
55660786Sps	}
55760786Sps	if (get_ifile(good_filename, curr_ifile) == curr_ifile)
55860786Sps	{
55960786Sps		/*
56060786Sps		 * Trying to edit the current file; don't reopen it.
56160786Sps		 */
56260786Sps		unsave_ifile(save_ifile);
56360786Sps		return (0);
56460786Sps	}
56560786Sps	reedit_ifile(save_ifile);
56660786Sps	return (edit(good_filename));
56760786Sps}
56860786Sps
56960786Sps/*
57060786Sps * Edit the first file in the command line (ifile) list.
57160786Sps */
57260786Sps	public int
57360786Spsedit_first()
57460786Sps{
57560786Sps	curr_ifile = NULL_IFILE;
57660786Sps	return (edit_next(1));
57760786Sps}
57860786Sps
57960786Sps/*
58060786Sps * Edit the last file in the command line (ifile) list.
58160786Sps */
58260786Sps	public int
58360786Spsedit_last()
58460786Sps{
58560786Sps	curr_ifile = NULL_IFILE;
58660786Sps	return (edit_prev(1));
58760786Sps}
58860786Sps
58960786Sps
59060786Sps/*
591161475Sdelphij * Edit the n-th next or previous file in the command line (ifile) list.
59260786Sps */
59360786Sps	static int
59460786Spsedit_istep(h, n, dir)
59560786Sps	IFILE h;
59660786Sps	int n;
59760786Sps	int dir;
59860786Sps{
59960786Sps	IFILE next;
60060786Sps
60160786Sps	/*
60260786Sps	 * Skip n filenames, then try to edit each filename.
60360786Sps	 */
60460786Sps	for (;;)
60560786Sps	{
60660786Sps		next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
60760786Sps		if (--n < 0)
60860786Sps		{
60960786Sps			if (edit_ifile(h) == 0)
61060786Sps				break;
61160786Sps		}
61260786Sps		if (next == NULL_IFILE)
61360786Sps		{
61460786Sps			/*
61560786Sps			 * Reached end of the ifile list.
61660786Sps			 */
61760786Sps			return (1);
61860786Sps		}
61960786Sps		if (ABORT_SIGS())
62060786Sps		{
62160786Sps			/*
62260786Sps			 * Interrupt breaks out, if we're in a long
62360786Sps			 * list of files that can't be opened.
62460786Sps			 */
62560786Sps			return (1);
62660786Sps		}
62760786Sps		h = next;
62860786Sps	}
62960786Sps	/*
63060786Sps	 * Found a file that we can edit.
63160786Sps	 */
63260786Sps	return (0);
63360786Sps}
63460786Sps
63560786Sps	static int
63660786Spsedit_inext(h, n)
63760786Sps	IFILE h;
63860786Sps	int n;
63960786Sps{
640161475Sdelphij	return (edit_istep(h, n, +1));
64160786Sps}
64260786Sps
64360786Sps	public int
64460786Spsedit_next(n)
64560786Sps	int n;
64660786Sps{
647161475Sdelphij	return edit_istep(curr_ifile, n, +1);
64860786Sps}
64960786Sps
65060786Sps	static int
65160786Spsedit_iprev(h, n)
65260786Sps	IFILE h;
65360786Sps	int n;
65460786Sps{
65560786Sps	return (edit_istep(h, n, -1));
65660786Sps}
65760786Sps
65860786Sps	public int
65960786Spsedit_prev(n)
66060786Sps	int n;
66160786Sps{
66260786Sps	return edit_istep(curr_ifile, n, -1);
66360786Sps}
66460786Sps
66560786Sps/*
66660786Sps * Edit a specific file in the command line (ifile) list.
66760786Sps */
66860786Sps	public int
66960786Spsedit_index(n)
67060786Sps	int n;
67160786Sps{
67260786Sps	IFILE h;
67360786Sps
67460786Sps	h = NULL_IFILE;
67560786Sps	do
67660786Sps	{
67760786Sps		if ((h = next_ifile(h)) == NULL_IFILE)
67860786Sps		{
67960786Sps			/*
68060786Sps			 * Reached end of the list without finding it.
68160786Sps			 */
68260786Sps			return (1);
68360786Sps		}
68460786Sps	} while (get_index(h) != n);
68560786Sps
68660786Sps	return (edit_ifile(h));
68760786Sps}
68860786Sps
68960786Sps	public IFILE
69060786Spssave_curr_ifile()
69160786Sps{
69260786Sps	if (curr_ifile != NULL_IFILE)
69360786Sps		hold_ifile(curr_ifile, 1);
69460786Sps	return (curr_ifile);
69560786Sps}
69660786Sps
69760786Sps	public void
69860786Spsunsave_ifile(save_ifile)
69960786Sps	IFILE save_ifile;
70060786Sps{
70160786Sps	if (save_ifile != NULL_IFILE)
70260786Sps		hold_ifile(save_ifile, -1);
70360786Sps}
70460786Sps
70560786Sps/*
70660786Sps * Reedit the ifile which was previously open.
70760786Sps */
70860786Sps	public void
70960786Spsreedit_ifile(save_ifile)
71060786Sps	IFILE save_ifile;
71160786Sps{
71260786Sps	IFILE next;
71360786Sps	IFILE prev;
71460786Sps
71560786Sps	/*
71660786Sps	 * Try to reopen the ifile.
71760786Sps	 * Note that opening it may fail (maybe the file was removed),
71860786Sps	 * in which case the ifile will be deleted from the list.
71960786Sps	 * So save the next and prev ifiles first.
72060786Sps	 */
72160786Sps	unsave_ifile(save_ifile);
72260786Sps	next = next_ifile(save_ifile);
72360786Sps	prev = prev_ifile(save_ifile);
72460786Sps	if (edit_ifile(save_ifile) == 0)
72560786Sps		return;
72660786Sps	/*
72760786Sps	 * If can't reopen it, open the next input file in the list.
72860786Sps	 */
72960786Sps	if (next != NULL_IFILE && edit_inext(next, 0) == 0)
73060786Sps		return;
73160786Sps	/*
73260786Sps	 * If can't open THAT one, open the previous input file in the list.
73360786Sps	 */
73460786Sps	if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
73560786Sps		return;
73660786Sps	/*
73760786Sps	 * If can't even open that, we're stuck.  Just quit.
73860786Sps	 */
73960786Sps	quit(QUIT_ERROR);
74060786Sps}
74160786Sps
742173682Sdelphij	public void
743173682Sdelphijreopen_curr_ifile()
744173682Sdelphij{
745173682Sdelphij	IFILE save_ifile = save_curr_ifile();
746173682Sdelphij	close_file();
747173682Sdelphij	reedit_ifile(save_ifile);
748173682Sdelphij}
749173682Sdelphij
75060786Sps/*
75160786Sps * Edit standard input.
75260786Sps */
75360786Sps	public int
75460786Spsedit_stdin()
75560786Sps{
75660786Sps	if (isatty(fd0))
75760786Sps	{
75860786Sps		error("Missing filename (\"less --help\" for help)", NULL_PARG);
75960786Sps		quit(QUIT_OK);
76060786Sps	}
76160786Sps	return (edit("-"));
76260786Sps}
76360786Sps
76460786Sps/*
76560786Sps * Copy a file directly to standard output.
76660786Sps * Used if standard output is not a tty.
76760786Sps */
76860786Sps	public void
76960786Spscat_file()
77060786Sps{
771330571Sdelphij	int c;
77260786Sps
77360786Sps	while ((c = ch_forw_get()) != EOI)
77460786Sps		putchr(c);
77560786Sps	flush();
77660786Sps}
77760786Sps
77860786Sps#if LOGFILE
77960786Sps
78060786Sps/*
78160786Sps * If the user asked for a log file and our input file
78260786Sps * is standard input, create the log file.
78360786Sps * We take care not to blindly overwrite an existing file.
78460786Sps */
78560786Sps	public void
78660786Spsuse_logfile(filename)
78760786Sps	char *filename;
78860786Sps{
789330571Sdelphij	int exists;
790330571Sdelphij	int answer;
79160786Sps	PARG parg;
79260786Sps
79360786Sps	if (ch_getflags() & CH_CANSEEK)
79460786Sps		/*
79560786Sps		 * Can't currently use a log file on a file that can seek.
79660786Sps		 */
79760786Sps		return;
79860786Sps
79960786Sps	/*
80060786Sps	 * {{ We could use access() here. }}
80160786Sps	 */
80260786Sps	exists = open(filename, OPEN_READ);
803294286Sdelphij	if (exists >= 0)
804294286Sdelphij		close(exists);
80560786Sps	exists = (exists >= 0);
80660786Sps
80760786Sps	/*
80860786Sps	 * Decide whether to overwrite the log file or append to it.
80960786Sps	 * If it doesn't exist we "overwrite" it.
81060786Sps	 */
81160786Sps	if (!exists || force_logfile)
81260786Sps	{
81360786Sps		/*
81460786Sps		 * Overwrite (or create) the log file.
81560786Sps		 */
81660786Sps		answer = 'O';
81760786Sps	} else
81860786Sps	{
81960786Sps		/*
82060786Sps		 * Ask user what to do.
82160786Sps		 */
82260786Sps		parg.p_string = filename;
82360786Sps		answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
82460786Sps	}
82560786Sps
82660786Spsloop:
82760786Sps	switch (answer)
82860786Sps	{
82960786Sps	case 'O': case 'o':
83060786Sps		/*
83160786Sps		 * Overwrite: create the file.
83260786Sps		 */
83360786Sps		logfile = creat(filename, 0644);
83460786Sps		break;
83560786Sps	case 'A': case 'a':
83660786Sps		/*
83760786Sps		 * Append: open the file and seek to the end.
83860786Sps		 */
83960786Sps		logfile = open(filename, OPEN_APPEND);
840173682Sdelphij		if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
84160786Sps		{
84260786Sps			close(logfile);
84360786Sps			logfile = -1;
84460786Sps		}
84560786Sps		break;
84660786Sps	case 'D': case 'd':
84760786Sps		/*
84860786Sps		 * Don't do anything.
84960786Sps		 */
85060786Sps		free(filename);
85160786Sps		return;
85260786Sps	case 'q':
85360786Sps		quit(QUIT_OK);
85460786Sps		/*NOTREACHED*/
85560786Sps	default:
85660786Sps		/*
85760786Sps		 * Eh?
85860786Sps		 */
85960786Sps		answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
86060786Sps		goto loop;
86160786Sps	}
86260786Sps
86360786Sps	if (logfile < 0)
86460786Sps	{
86560786Sps		/*
86660786Sps		 * Error in opening logfile.
86760786Sps		 */
86860786Sps		parg.p_string = filename;
86960786Sps		error("Cannot write to \"%s\"", &parg);
87060786Sps		return;
87160786Sps	}
87260786Sps	SET_BINARY(logfile);
87360786Sps}
87460786Sps
87560786Sps#endif
876