xref: /freebsd/contrib/less/edit.c (revision 464501a8)
1a5f0fb15SPaul Saab /*
27f074f9cSXin LI  * Copyright (C) 1984-2007  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  *
7a5f0fb15SPaul Saab  * For more information about less, or for information on how to
8a5f0fb15SPaul Saab  * contact the author, see the README file.
9a5f0fb15SPaul Saab  */
10a5f0fb15SPaul Saab 
11a5f0fb15SPaul Saab 
12a5f0fb15SPaul Saab #include "less.h"
13464501a8SXin LI #if HAVE_STAT
14464501a8SXin LI #include <sys/stat.h>
15464501a8SXin LI #endif
16a5f0fb15SPaul Saab 
17a5f0fb15SPaul Saab public int fd0 = 0;
18a5f0fb15SPaul Saab 
19a5f0fb15SPaul Saab extern int new_file;
20a5f0fb15SPaul Saab extern int errmsgs;
21a5f0fb15SPaul Saab extern int cbufs;
22a5f0fb15SPaul Saab extern char *every_first_cmd;
23a5f0fb15SPaul Saab extern int any_display;
24a5f0fb15SPaul Saab extern int force_open;
25a5f0fb15SPaul Saab extern int is_tty;
26a5f0fb15SPaul Saab extern int sigs;
27a5f0fb15SPaul Saab extern IFILE curr_ifile;
28a5f0fb15SPaul Saab extern IFILE old_ifile;
29a5f0fb15SPaul Saab extern struct scrpos initial_scrpos;
30a5f0fb15SPaul Saab extern void constant *ml_examine;
31a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
32a5f0fb15SPaul Saab extern char openquote;
33a5f0fb15SPaul Saab extern char closequote;
34a5f0fb15SPaul Saab #endif
35a5f0fb15SPaul Saab 
36a5f0fb15SPaul Saab #if LOGFILE
37a5f0fb15SPaul Saab extern int logfile;
38a5f0fb15SPaul Saab extern int force_logfile;
39a5f0fb15SPaul Saab extern char *namelogfile;
40a5f0fb15SPaul Saab #endif
41a5f0fb15SPaul Saab 
42464501a8SXin LI #if HAVE_STAT_INO
43464501a8SXin LI public dev_t curr_dev;
44464501a8SXin LI public ino_t curr_ino;
45464501a8SXin LI #endif
46464501a8SXin LI 
47a5f0fb15SPaul Saab char *curr_altfilename = NULL;
48a5f0fb15SPaul Saab static void *curr_altpipe;
49a5f0fb15SPaul Saab 
50a5f0fb15SPaul Saab 
51a5f0fb15SPaul Saab /*
52a5f0fb15SPaul Saab  * Textlist functions deal with a list of words separated by spaces.
53a5f0fb15SPaul Saab  * init_textlist sets up a textlist structure.
54a5f0fb15SPaul Saab  * forw_textlist uses that structure to iterate thru the list of
55a5f0fb15SPaul Saab  * words, returning each one as a standard null-terminated string.
56a5f0fb15SPaul Saab  * back_textlist does the same, but runs thru the list backwards.
57a5f0fb15SPaul Saab  */
58a5f0fb15SPaul Saab 	public void
59a5f0fb15SPaul Saab init_textlist(tlist, str)
60a5f0fb15SPaul Saab 	struct textlist *tlist;
61a5f0fb15SPaul Saab 	char *str;
62a5f0fb15SPaul Saab {
63a5f0fb15SPaul Saab 	char *s;
64a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
65000ba3e8STim J. Robbins 	int meta_quoted = 0;
66000ba3e8STim J. Robbins 	int delim_quoted = 0;
67000ba3e8STim J. Robbins 	char *esc = get_meta_escape();
68000ba3e8STim J. Robbins 	int esclen = strlen(esc);
69a5f0fb15SPaul Saab #endif
70a5f0fb15SPaul Saab 
71a5f0fb15SPaul Saab 	tlist->string = skipsp(str);
72a5f0fb15SPaul Saab 	tlist->endstring = tlist->string + strlen(tlist->string);
73a5f0fb15SPaul Saab 	for (s = str;  s < tlist->endstring;  s++)
74a5f0fb15SPaul Saab 	{
75a5f0fb15SPaul Saab #if SPACES_IN_FILENAMES
76000ba3e8STim J. Robbins 		if (meta_quoted)
77000ba3e8STim J. Robbins 		{
78000ba3e8STim J. Robbins 			meta_quoted = 0;
79000ba3e8STim J. Robbins 		} else if (esclen > 0 && s + esclen < tlist->endstring &&
80000ba3e8STim J. Robbins 		           strncmp(s, esc, esclen) == 0)
81000ba3e8STim J. Robbins 		{
82000ba3e8STim J. Robbins 			meta_quoted = 1;
83000ba3e8STim J. Robbins 			s += esclen - 1;
84000ba3e8STim J. Robbins 		} else if (delim_quoted)
85000ba3e8STim J. Robbins 		{
86000ba3e8STim J. Robbins 			if (*s == closequote)
87000ba3e8STim J. Robbins 				delim_quoted = 0;
88000ba3e8STim J. Robbins 		} else /* (!delim_quoted) */
89000ba3e8STim J. Robbins 		{
90000ba3e8STim J. Robbins 			if (*s == openquote)
91000ba3e8STim J. Robbins 				delim_quoted = 1;
92000ba3e8STim J. Robbins 			else if (*s == ' ')
93a5f0fb15SPaul Saab 				*s = '\0';
94000ba3e8STim J. Robbins 		}
95a5f0fb15SPaul Saab #else
96a5f0fb15SPaul Saab 		if (*s == ' ')
97a5f0fb15SPaul Saab 			*s = '\0';
98a5f0fb15SPaul Saab #endif
99a5f0fb15SPaul Saab 	}
100a5f0fb15SPaul Saab }
101a5f0fb15SPaul Saab 
102a5f0fb15SPaul Saab 	public char *
103a5f0fb15SPaul Saab forw_textlist(tlist, prev)
104a5f0fb15SPaul Saab 	struct textlist *tlist;
105a5f0fb15SPaul Saab 	char *prev;
106a5f0fb15SPaul Saab {
107a5f0fb15SPaul Saab 	char *s;
108a5f0fb15SPaul Saab 
109a5f0fb15SPaul Saab 	/*
110a5f0fb15SPaul Saab 	 * prev == NULL means return the first word in the list.
111a5f0fb15SPaul Saab 	 * Otherwise, return the word after "prev".
112a5f0fb15SPaul Saab 	 */
113a5f0fb15SPaul Saab 	if (prev == NULL)
114a5f0fb15SPaul Saab 		s = tlist->string;
115a5f0fb15SPaul Saab 	else
116a5f0fb15SPaul Saab 		s = prev + strlen(prev);
117a5f0fb15SPaul Saab 	if (s >= tlist->endstring)
118a5f0fb15SPaul Saab 		return (NULL);
119a5f0fb15SPaul Saab 	while (*s == '\0')
120a5f0fb15SPaul Saab 		s++;
121a5f0fb15SPaul Saab 	if (s >= tlist->endstring)
122a5f0fb15SPaul Saab 		return (NULL);
123a5f0fb15SPaul Saab 	return (s);
124a5f0fb15SPaul Saab }
125a5f0fb15SPaul Saab 
126a5f0fb15SPaul Saab 	public char *
127a5f0fb15SPaul Saab back_textlist(tlist, prev)
128a5f0fb15SPaul Saab 	struct textlist *tlist;
129a5f0fb15SPaul Saab 	char *prev;
130a5f0fb15SPaul Saab {
131a5f0fb15SPaul Saab 	char *s;
132a5f0fb15SPaul Saab 
133a5f0fb15SPaul Saab 	/*
134a5f0fb15SPaul Saab 	 * prev == NULL means return the last word in the list.
135a5f0fb15SPaul Saab 	 * Otherwise, return the word before "prev".
136a5f0fb15SPaul Saab 	 */
137a5f0fb15SPaul Saab 	if (prev == NULL)
138a5f0fb15SPaul Saab 		s = tlist->endstring;
139a5f0fb15SPaul Saab 	else if (prev <= tlist->string)
140a5f0fb15SPaul Saab 		return (NULL);
141a5f0fb15SPaul Saab 	else
142a5f0fb15SPaul Saab 		s = prev - 1;
143a5f0fb15SPaul Saab 	while (*s == '\0')
144a5f0fb15SPaul Saab 		s--;
145a5f0fb15SPaul Saab 	if (s <= tlist->string)
146a5f0fb15SPaul Saab 		return (NULL);
147a5f0fb15SPaul Saab 	while (s[-1] != '\0' && s > tlist->string)
148a5f0fb15SPaul Saab 		s--;
149a5f0fb15SPaul Saab 	return (s);
150a5f0fb15SPaul Saab }
151a5f0fb15SPaul Saab 
152a5f0fb15SPaul Saab /*
153a5f0fb15SPaul Saab  * Close the current input file.
154a5f0fb15SPaul Saab  */
155a5f0fb15SPaul Saab 	static void
156a5f0fb15SPaul Saab close_file()
157a5f0fb15SPaul Saab {
158a5f0fb15SPaul Saab 	struct scrpos scrpos;
159a5f0fb15SPaul Saab 
160a5f0fb15SPaul Saab 	if (curr_ifile == NULL_IFILE)
161a5f0fb15SPaul Saab 		return;
162a5f0fb15SPaul Saab 
163a5f0fb15SPaul Saab 	/*
164a5f0fb15SPaul Saab 	 * Save the current position so that we can return to
165a5f0fb15SPaul Saab 	 * the same position if we edit this file again.
166a5f0fb15SPaul Saab 	 */
167a5f0fb15SPaul Saab 	get_scrpos(&scrpos);
168a5f0fb15SPaul Saab 	if (scrpos.pos != NULL_POSITION)
169a5f0fb15SPaul Saab 	{
170a5f0fb15SPaul Saab 		store_pos(curr_ifile, &scrpos);
171a5f0fb15SPaul Saab 		lastmark();
172a5f0fb15SPaul Saab 	}
173a5f0fb15SPaul Saab 	/*
174a5f0fb15SPaul Saab 	 * Close the file descriptor, unless it is a pipe.
175a5f0fb15SPaul Saab 	 */
176a5f0fb15SPaul Saab 	ch_close();
177a5f0fb15SPaul Saab 	/*
178a5f0fb15SPaul Saab 	 * If we opened a file using an alternate name,
179a5f0fb15SPaul Saab 	 * do special stuff to close it.
180a5f0fb15SPaul Saab 	 */
181a5f0fb15SPaul Saab 	if (curr_altfilename != NULL)
182a5f0fb15SPaul Saab 	{
183000ba3e8STim J. Robbins 		close_altfile(curr_altfilename, get_filename(curr_ifile),
184000ba3e8STim J. Robbins 				curr_altpipe);
185a5f0fb15SPaul Saab 		free(curr_altfilename);
186a5f0fb15SPaul Saab 		curr_altfilename = NULL;
187a5f0fb15SPaul Saab 	}
188a5f0fb15SPaul Saab 	curr_ifile = NULL_IFILE;
189464501a8SXin LI #if HAVE_STAT_INO
190464501a8SXin LI 	curr_ino = curr_dev = 0;
191464501a8SXin LI #endif
192a5f0fb15SPaul Saab }
193a5f0fb15SPaul Saab 
194a5f0fb15SPaul Saab /*
195a5f0fb15SPaul Saab  * Edit a new file (given its name).
196a5f0fb15SPaul Saab  * Filename == "-" means standard input.
197a5f0fb15SPaul Saab  * Filename == NULL means just close the current file.
198a5f0fb15SPaul Saab  */
199a5f0fb15SPaul Saab 	public int
200a5f0fb15SPaul Saab edit(filename)
201a5f0fb15SPaul Saab 	char *filename;
202a5f0fb15SPaul Saab {
203a5f0fb15SPaul Saab 	if (filename == NULL)
204a5f0fb15SPaul Saab 		return (edit_ifile(NULL_IFILE));
205a5f0fb15SPaul Saab 	return (edit_ifile(get_ifile(filename, curr_ifile)));
206a5f0fb15SPaul Saab }
207a5f0fb15SPaul Saab 
208a5f0fb15SPaul Saab /*
209a5f0fb15SPaul Saab  * Edit a new file (given its IFILE).
210a5f0fb15SPaul Saab  * ifile == NULL means just close the current file.
211a5f0fb15SPaul Saab  */
212a5f0fb15SPaul Saab 	public int
213a5f0fb15SPaul Saab edit_ifile(ifile)
214a5f0fb15SPaul Saab 	IFILE ifile;
215a5f0fb15SPaul Saab {
216a5f0fb15SPaul Saab 	int f;
217a5f0fb15SPaul Saab 	int answer;
218a5f0fb15SPaul Saab 	int no_display;
219a5f0fb15SPaul Saab 	int chflags;
220a5f0fb15SPaul Saab 	char *filename;
221a5f0fb15SPaul Saab 	char *open_filename;
222000ba3e8STim J. Robbins 	char *qopen_filename;
223a5f0fb15SPaul Saab 	char *alt_filename;
224a5f0fb15SPaul Saab 	void *alt_pipe;
225a5f0fb15SPaul Saab 	IFILE was_curr_ifile;
226a5f0fb15SPaul Saab 	PARG parg;
227a5f0fb15SPaul Saab 
228a5f0fb15SPaul Saab 	if (ifile == curr_ifile)
229a5f0fb15SPaul Saab 	{
230a5f0fb15SPaul Saab 		/*
231a5f0fb15SPaul Saab 		 * Already have the correct file open.
232a5f0fb15SPaul Saab 		 */
233a5f0fb15SPaul Saab 		return (0);
234a5f0fb15SPaul Saab 	}
235a5f0fb15SPaul Saab 
236a5f0fb15SPaul Saab 	/*
237a5f0fb15SPaul Saab 	 * We must close the currently open file now.
238a5f0fb15SPaul Saab 	 * This is necessary to make the open_altfile/close_altfile pairs
239a5f0fb15SPaul Saab 	 * nest properly (or rather to avoid nesting at all).
240a5f0fb15SPaul Saab 	 * {{ Some stupid implementations of popen() mess up if you do:
241a5f0fb15SPaul Saab 	 *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
242a5f0fb15SPaul Saab 	 */
243a5f0fb15SPaul Saab #if LOGFILE
244a5f0fb15SPaul Saab 	end_logfile();
245a5f0fb15SPaul Saab #endif
246a5f0fb15SPaul Saab 	was_curr_ifile = save_curr_ifile();
247a5f0fb15SPaul Saab 	if (curr_ifile != NULL_IFILE)
248a5f0fb15SPaul Saab 	{
249a5f0fb15SPaul Saab 		chflags = ch_getflags();
250a5f0fb15SPaul Saab 		close_file();
251a5f0fb15SPaul Saab 		if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
252a5f0fb15SPaul Saab 		{
253a5f0fb15SPaul Saab 			/*
254a5f0fb15SPaul Saab 			 * Don't keep the help file in the ifile list.
255a5f0fb15SPaul Saab 			 */
256a5f0fb15SPaul Saab 			del_ifile(was_curr_ifile);
257a5f0fb15SPaul Saab 			was_curr_ifile = old_ifile;
258a5f0fb15SPaul Saab 		}
259a5f0fb15SPaul Saab 	}
260a5f0fb15SPaul Saab 
261a5f0fb15SPaul Saab 	if (ifile == NULL_IFILE)
262a5f0fb15SPaul Saab 	{
263a5f0fb15SPaul Saab 		/*
264a5f0fb15SPaul Saab 		 * No new file to open.
265a5f0fb15SPaul Saab 		 * (Don't set old_ifile, because if you call edit_ifile(NULL),
266a5f0fb15SPaul Saab 		 *  you're supposed to have saved curr_ifile yourself,
267a5f0fb15SPaul Saab 		 *  and you'll restore it if necessary.)
268a5f0fb15SPaul Saab 		 */
269a5f0fb15SPaul Saab 		unsave_ifile(was_curr_ifile);
270a5f0fb15SPaul Saab 		return (0);
271a5f0fb15SPaul Saab 	}
272a5f0fb15SPaul Saab 
273000ba3e8STim J. Robbins 	filename = save(get_filename(ifile));
274a5f0fb15SPaul Saab 	/*
275a5f0fb15SPaul Saab 	 * See if LESSOPEN specifies an "alternate" file to open.
276a5f0fb15SPaul Saab 	 */
277a5f0fb15SPaul Saab 	alt_pipe = NULL;
278a5f0fb15SPaul Saab 	alt_filename = open_altfile(filename, &f, &alt_pipe);
279a5f0fb15SPaul Saab 	open_filename = (alt_filename != NULL) ? alt_filename : filename;
280000ba3e8STim J. Robbins 	qopen_filename = shell_unquote(open_filename);
281a5f0fb15SPaul Saab 
282a5f0fb15SPaul Saab 	chflags = 0;
283a5f0fb15SPaul Saab 	if (alt_pipe != NULL)
284a5f0fb15SPaul Saab 	{
285a5f0fb15SPaul Saab 		/*
286a5f0fb15SPaul Saab 		 * The alternate "file" is actually a pipe.
287a5f0fb15SPaul Saab 		 * f has already been set to the file descriptor of the pipe
288a5f0fb15SPaul Saab 		 * in the call to open_altfile above.
289a5f0fb15SPaul Saab 		 * Keep the file descriptor open because it was opened
290a5f0fb15SPaul Saab 		 * via popen(), and pclose() wants to close it.
291a5f0fb15SPaul Saab 		 */
292a5f0fb15SPaul Saab 		chflags |= CH_POPENED;
293a5f0fb15SPaul Saab 	} else if (strcmp(open_filename, "-") == 0)
294a5f0fb15SPaul Saab 	{
295a5f0fb15SPaul Saab 		/*
296a5f0fb15SPaul Saab 		 * Use standard input.
297a5f0fb15SPaul Saab 		 * Keep the file descriptor open because we can't reopen it.
298a5f0fb15SPaul Saab 		 */
299a5f0fb15SPaul Saab 		f = fd0;
300a5f0fb15SPaul Saab 		chflags |= CH_KEEPOPEN;
301a5f0fb15SPaul Saab 		/*
302a5f0fb15SPaul Saab 		 * Must switch stdin to BINARY mode.
303a5f0fb15SPaul Saab 		 */
304a5f0fb15SPaul Saab 		SET_BINARY(f);
305a5f0fb15SPaul Saab #if MSDOS_COMPILER==DJGPPC
306a5f0fb15SPaul Saab 		/*
307a5f0fb15SPaul Saab 		 * Setting stdin to binary by default causes
308a5f0fb15SPaul Saab 		 * Ctrl-C to not raise SIGINT.  We must undo
309a5f0fb15SPaul Saab 		 * that side-effect.
310a5f0fb15SPaul Saab 		 */
311a5f0fb15SPaul Saab 		__djgpp_set_ctrl_c(1);
312a5f0fb15SPaul Saab #endif
313a5f0fb15SPaul Saab 	} else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
314a5f0fb15SPaul Saab 	{
315a5f0fb15SPaul Saab 		f = -1;
316a5f0fb15SPaul Saab 		chflags |= CH_HELPFILE;
317a5f0fb15SPaul Saab 	} else if ((parg.p_string = bad_file(open_filename)) != NULL)
318a5f0fb15SPaul Saab 	{
319a5f0fb15SPaul Saab 		/*
320a5f0fb15SPaul Saab 		 * It looks like a bad file.  Don't try to open it.
321a5f0fb15SPaul Saab 		 */
322a5f0fb15SPaul Saab 		error("%s", &parg);
323a5f0fb15SPaul Saab 		free(parg.p_string);
324a5f0fb15SPaul Saab 	    err1:
325a5f0fb15SPaul Saab 		if (alt_filename != NULL)
326a5f0fb15SPaul Saab 		{
327a5f0fb15SPaul Saab 			close_altfile(alt_filename, filename, alt_pipe);
328a5f0fb15SPaul Saab 			free(alt_filename);
329a5f0fb15SPaul Saab 		}
330a5f0fb15SPaul Saab 		del_ifile(ifile);
331000ba3e8STim J. Robbins 		free(qopen_filename);
332a5f0fb15SPaul Saab 		free(filename);
333a5f0fb15SPaul Saab 		/*
334a5f0fb15SPaul Saab 		 * Re-open the current file.
335a5f0fb15SPaul Saab 		 */
3366dcb072bSXin LI 		if (was_curr_ifile == ifile)
3376dcb072bSXin LI 		{
3386dcb072bSXin LI 			/*
3396dcb072bSXin LI 			 * Whoops.  The "current" ifile is the one we just deleted.
3406dcb072bSXin LI 			 * Just give up.
3416dcb072bSXin LI 			 */
3426dcb072bSXin LI 			quit(QUIT_ERROR);
3436dcb072bSXin LI 		}
344a5f0fb15SPaul Saab 		reedit_ifile(was_curr_ifile);
345a5f0fb15SPaul Saab 		return (1);
346000ba3e8STim J. Robbins 	} else if ((f = open(qopen_filename, OPEN_READ)) < 0)
347a5f0fb15SPaul Saab 	{
348a5f0fb15SPaul Saab 		/*
349a5f0fb15SPaul Saab 		 * Got an error trying to open it.
350a5f0fb15SPaul Saab 		 */
351a5f0fb15SPaul Saab 		parg.p_string = errno_message(filename);
352a5f0fb15SPaul Saab 		error("%s", &parg);
353a5f0fb15SPaul Saab 		free(parg.p_string);
354a5f0fb15SPaul Saab 	    	goto err1;
355a5f0fb15SPaul Saab 	} else
356a5f0fb15SPaul Saab 	{
357a5f0fb15SPaul Saab 		chflags |= CH_CANSEEK;
358a5f0fb15SPaul Saab 		if (!force_open && !opened(ifile) && bin_file(f))
359a5f0fb15SPaul Saab 		{
360a5f0fb15SPaul Saab 			/*
361a5f0fb15SPaul Saab 			 * Looks like a binary file.
362a5f0fb15SPaul Saab 			 * Ask user if we should proceed.
363a5f0fb15SPaul Saab 			 */
364a5f0fb15SPaul Saab 			parg.p_string = filename;
365a5f0fb15SPaul Saab 			answer = query("\"%s\" may be a binary file.  See it anyway? ",
366a5f0fb15SPaul Saab 				&parg);
367a5f0fb15SPaul Saab 			if (answer != 'y' && answer != 'Y')
368a5f0fb15SPaul Saab 			{
369a5f0fb15SPaul Saab 				close(f);
370a5f0fb15SPaul Saab 				goto err1;
371a5f0fb15SPaul Saab 			}
372a5f0fb15SPaul Saab 		}
373a5f0fb15SPaul Saab 	}
374a5f0fb15SPaul Saab 
375a5f0fb15SPaul Saab 	/*
376a5f0fb15SPaul Saab 	 * Get the new ifile.
377a5f0fb15SPaul Saab 	 * Get the saved position for the file.
378a5f0fb15SPaul Saab 	 */
379a5f0fb15SPaul Saab 	if (was_curr_ifile != NULL_IFILE)
380a5f0fb15SPaul Saab 	{
381a5f0fb15SPaul Saab 		old_ifile = was_curr_ifile;
382a5f0fb15SPaul Saab 		unsave_ifile(was_curr_ifile);
383a5f0fb15SPaul Saab 	}
384a5f0fb15SPaul Saab 	curr_ifile = ifile;
385a5f0fb15SPaul Saab 	curr_altfilename = alt_filename;
386a5f0fb15SPaul Saab 	curr_altpipe = alt_pipe;
387a5f0fb15SPaul Saab 	set_open(curr_ifile); /* File has been opened */
388a5f0fb15SPaul Saab 	get_pos(curr_ifile, &initial_scrpos);
389a5f0fb15SPaul Saab 	new_file = TRUE;
390a5f0fb15SPaul Saab 	ch_init(f, chflags);
391a5f0fb15SPaul Saab 
392a5f0fb15SPaul Saab 	if (!(chflags & CH_HELPFILE))
393a5f0fb15SPaul Saab 	{
394a5f0fb15SPaul Saab #if LOGFILE
395a5f0fb15SPaul Saab 		if (namelogfile != NULL && is_tty)
396a5f0fb15SPaul Saab 			use_logfile(namelogfile);
397a5f0fb15SPaul Saab #endif
398464501a8SXin LI #if HAVE_STAT_INO
399464501a8SXin LI 		/* Remember the i-number and device of the opened file. */
400464501a8SXin LI 		{
401464501a8SXin LI 			struct stat statbuf;
402464501a8SXin LI 			int r = stat(qopen_filename, &statbuf);
403464501a8SXin LI 			if (r == 0)
404464501a8SXin LI 			{
405464501a8SXin LI 				curr_ino = statbuf.st_ino;
406464501a8SXin LI 				curr_dev = statbuf.st_dev;
407464501a8SXin LI 			}
408464501a8SXin LI 		}
409464501a8SXin LI #endif
410a5f0fb15SPaul Saab 		if (every_first_cmd != NULL)
411a5f0fb15SPaul Saab 			ungetsc(every_first_cmd);
412a5f0fb15SPaul Saab 	}
413a5f0fb15SPaul Saab 
414464501a8SXin LI 	free(qopen_filename);
415a5f0fb15SPaul Saab 	no_display = !any_display;
416a5f0fb15SPaul Saab 	flush();
417a5f0fb15SPaul Saab 	any_display = TRUE;
418a5f0fb15SPaul Saab 
419a5f0fb15SPaul Saab 	if (is_tty)
420a5f0fb15SPaul Saab 	{
421a5f0fb15SPaul Saab 		/*
422a5f0fb15SPaul Saab 		 * Output is to a real tty.
423a5f0fb15SPaul Saab 		 */
424a5f0fb15SPaul Saab 
425a5f0fb15SPaul Saab 		/*
426a5f0fb15SPaul Saab 		 * Indicate there is nothing displayed yet.
427a5f0fb15SPaul Saab 		 */
428a5f0fb15SPaul Saab 		pos_clear();
429a5f0fb15SPaul Saab 		clr_linenum();
430a5f0fb15SPaul Saab #if HILITE_SEARCH
431a5f0fb15SPaul Saab 		clr_hilite();
432a5f0fb15SPaul Saab #endif
433a5f0fb15SPaul Saab 		cmd_addhist(ml_examine, filename);
434a5f0fb15SPaul Saab 		if (no_display && errmsgs > 0)
435a5f0fb15SPaul Saab 		{
436a5f0fb15SPaul Saab 			/*
437a5f0fb15SPaul Saab 			 * We displayed some messages on error output
438a5f0fb15SPaul Saab 			 * (file descriptor 2; see error() function).
439a5f0fb15SPaul Saab 			 * Before erasing the screen contents,
440a5f0fb15SPaul Saab 			 * display the file name and wait for a keystroke.
441a5f0fb15SPaul Saab 			 */
442a5f0fb15SPaul Saab 			parg.p_string = filename;
443a5f0fb15SPaul Saab 			error("%s", &parg);
444a5f0fb15SPaul Saab 		}
445a5f0fb15SPaul Saab 	}
446a5f0fb15SPaul Saab 	free(filename);
447a5f0fb15SPaul Saab 	return (0);
448a5f0fb15SPaul Saab }
449a5f0fb15SPaul Saab 
450a5f0fb15SPaul Saab /*
451a5f0fb15SPaul Saab  * Edit a space-separated list of files.
452a5f0fb15SPaul Saab  * For each filename in the list, enter it into the ifile list.
453a5f0fb15SPaul Saab  * Then edit the first one.
454a5f0fb15SPaul Saab  */
455a5f0fb15SPaul Saab 	public int
456a5f0fb15SPaul Saab edit_list(filelist)
457a5f0fb15SPaul Saab 	char *filelist;
458a5f0fb15SPaul Saab {
459a5f0fb15SPaul Saab 	IFILE save_ifile;
460a5f0fb15SPaul Saab 	char *good_filename;
461a5f0fb15SPaul Saab 	char *filename;
462a5f0fb15SPaul Saab 	char *gfilelist;
463a5f0fb15SPaul Saab 	char *gfilename;
464a5f0fb15SPaul Saab 	struct textlist tl_files;
465a5f0fb15SPaul Saab 	struct textlist tl_gfiles;
466a5f0fb15SPaul Saab 
467a5f0fb15SPaul Saab 	save_ifile = save_curr_ifile();
468a5f0fb15SPaul Saab 	good_filename = NULL;
469a5f0fb15SPaul Saab 
470a5f0fb15SPaul Saab 	/*
471a5f0fb15SPaul Saab 	 * Run thru each filename in the list.
472a5f0fb15SPaul Saab 	 * Try to glob the filename.
473a5f0fb15SPaul Saab 	 * If it doesn't expand, just try to open the filename.
474a5f0fb15SPaul Saab 	 * If it does expand, try to open each name in that list.
475a5f0fb15SPaul Saab 	 */
476a5f0fb15SPaul Saab 	init_textlist(&tl_files, filelist);
477a5f0fb15SPaul Saab 	filename = NULL;
478a5f0fb15SPaul Saab 	while ((filename = forw_textlist(&tl_files, filename)) != NULL)
479a5f0fb15SPaul Saab 	{
480a5f0fb15SPaul Saab 		gfilelist = lglob(filename);
481a5f0fb15SPaul Saab 		init_textlist(&tl_gfiles, gfilelist);
482a5f0fb15SPaul Saab 		gfilename = NULL;
483a5f0fb15SPaul Saab 		while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
484a5f0fb15SPaul Saab 		{
485a5f0fb15SPaul Saab 			if (edit(gfilename) == 0 && good_filename == NULL)
486a5f0fb15SPaul Saab 				good_filename = get_filename(curr_ifile);
487a5f0fb15SPaul Saab 		}
488a5f0fb15SPaul Saab 		free(gfilelist);
489a5f0fb15SPaul Saab 	}
490a5f0fb15SPaul Saab 	/*
491a5f0fb15SPaul Saab 	 * Edit the first valid filename in the list.
492a5f0fb15SPaul Saab 	 */
493a5f0fb15SPaul Saab 	if (good_filename == NULL)
494a5f0fb15SPaul Saab 	{
495a5f0fb15SPaul Saab 		unsave_ifile(save_ifile);
496a5f0fb15SPaul Saab 		return (1);
497a5f0fb15SPaul Saab 	}
498a5f0fb15SPaul Saab 	if (get_ifile(good_filename, curr_ifile) == curr_ifile)
499a5f0fb15SPaul Saab 	{
500a5f0fb15SPaul Saab 		/*
501a5f0fb15SPaul Saab 		 * Trying to edit the current file; don't reopen it.
502a5f0fb15SPaul Saab 		 */
503a5f0fb15SPaul Saab 		unsave_ifile(save_ifile);
504a5f0fb15SPaul Saab 		return (0);
505a5f0fb15SPaul Saab 	}
506a5f0fb15SPaul Saab 	reedit_ifile(save_ifile);
507a5f0fb15SPaul Saab 	return (edit(good_filename));
508a5f0fb15SPaul Saab }
509a5f0fb15SPaul Saab 
510a5f0fb15SPaul Saab /*
511a5f0fb15SPaul Saab  * Edit the first file in the command line (ifile) list.
512a5f0fb15SPaul Saab  */
513a5f0fb15SPaul Saab 	public int
514a5f0fb15SPaul Saab edit_first()
515a5f0fb15SPaul Saab {
516a5f0fb15SPaul Saab 	curr_ifile = NULL_IFILE;
517a5f0fb15SPaul Saab 	return (edit_next(1));
518a5f0fb15SPaul Saab }
519a5f0fb15SPaul Saab 
520a5f0fb15SPaul Saab /*
521a5f0fb15SPaul Saab  * Edit the last file in the command line (ifile) list.
522a5f0fb15SPaul Saab  */
523a5f0fb15SPaul Saab 	public int
524a5f0fb15SPaul Saab edit_last()
525a5f0fb15SPaul Saab {
526a5f0fb15SPaul Saab 	curr_ifile = NULL_IFILE;
527a5f0fb15SPaul Saab 	return (edit_prev(1));
528a5f0fb15SPaul Saab }
529a5f0fb15SPaul Saab 
530a5f0fb15SPaul Saab 
531a5f0fb15SPaul Saab /*
5326dcb072bSXin LI  * Edit the n-th next or previous file in the command line (ifile) list.
533a5f0fb15SPaul Saab  */
534a5f0fb15SPaul Saab 	static int
535a5f0fb15SPaul Saab edit_istep(h, n, dir)
536a5f0fb15SPaul Saab 	IFILE h;
537a5f0fb15SPaul Saab 	int n;
538a5f0fb15SPaul Saab 	int dir;
539a5f0fb15SPaul Saab {
540a5f0fb15SPaul Saab 	IFILE next;
541a5f0fb15SPaul Saab 
542a5f0fb15SPaul Saab 	/*
543a5f0fb15SPaul Saab 	 * Skip n filenames, then try to edit each filename.
544a5f0fb15SPaul Saab 	 */
545a5f0fb15SPaul Saab 	for (;;)
546a5f0fb15SPaul Saab 	{
547a5f0fb15SPaul Saab 		next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
548a5f0fb15SPaul Saab 		if (--n < 0)
549a5f0fb15SPaul Saab 		{
550a5f0fb15SPaul Saab 			if (edit_ifile(h) == 0)
551a5f0fb15SPaul Saab 				break;
552a5f0fb15SPaul Saab 		}
553a5f0fb15SPaul Saab 		if (next == NULL_IFILE)
554a5f0fb15SPaul Saab 		{
555a5f0fb15SPaul Saab 			/*
556a5f0fb15SPaul Saab 			 * Reached end of the ifile list.
557a5f0fb15SPaul Saab 			 */
558a5f0fb15SPaul Saab 			return (1);
559a5f0fb15SPaul Saab 		}
560a5f0fb15SPaul Saab 		if (ABORT_SIGS())
561a5f0fb15SPaul Saab 		{
562a5f0fb15SPaul Saab 			/*
563a5f0fb15SPaul Saab 			 * Interrupt breaks out, if we're in a long
564a5f0fb15SPaul Saab 			 * list of files that can't be opened.
565a5f0fb15SPaul Saab 			 */
566a5f0fb15SPaul Saab 			return (1);
567a5f0fb15SPaul Saab 		}
568a5f0fb15SPaul Saab 		h = next;
569a5f0fb15SPaul Saab 	}
570a5f0fb15SPaul Saab 	/*
571a5f0fb15SPaul Saab 	 * Found a file that we can edit.
572a5f0fb15SPaul Saab 	 */
573a5f0fb15SPaul Saab 	return (0);
574a5f0fb15SPaul Saab }
575a5f0fb15SPaul Saab 
576a5f0fb15SPaul Saab 	static int
577a5f0fb15SPaul Saab edit_inext(h, n)
578a5f0fb15SPaul Saab 	IFILE h;
579a5f0fb15SPaul Saab 	int n;
580a5f0fb15SPaul Saab {
5816dcb072bSXin LI 	return (edit_istep(h, n, +1));
582a5f0fb15SPaul Saab }
583a5f0fb15SPaul Saab 
584a5f0fb15SPaul Saab 	public int
585a5f0fb15SPaul Saab edit_next(n)
586a5f0fb15SPaul Saab 	int n;
587a5f0fb15SPaul Saab {
5886dcb072bSXin LI 	return edit_istep(curr_ifile, n, +1);
589a5f0fb15SPaul Saab }
590a5f0fb15SPaul Saab 
591a5f0fb15SPaul Saab 	static int
592a5f0fb15SPaul Saab edit_iprev(h, n)
593a5f0fb15SPaul Saab 	IFILE h;
594a5f0fb15SPaul Saab 	int n;
595a5f0fb15SPaul Saab {
596a5f0fb15SPaul Saab 	return (edit_istep(h, n, -1));
597a5f0fb15SPaul Saab }
598a5f0fb15SPaul Saab 
599a5f0fb15SPaul Saab 	public int
600a5f0fb15SPaul Saab edit_prev(n)
601a5f0fb15SPaul Saab 	int n;
602a5f0fb15SPaul Saab {
603a5f0fb15SPaul Saab 	return edit_istep(curr_ifile, n, -1);
604a5f0fb15SPaul Saab }
605a5f0fb15SPaul Saab 
606a5f0fb15SPaul Saab /*
607a5f0fb15SPaul Saab  * Edit a specific file in the command line (ifile) list.
608a5f0fb15SPaul Saab  */
609a5f0fb15SPaul Saab 	public int
610a5f0fb15SPaul Saab edit_index(n)
611a5f0fb15SPaul Saab 	int n;
612a5f0fb15SPaul Saab {
613a5f0fb15SPaul Saab 	IFILE h;
614a5f0fb15SPaul Saab 
615a5f0fb15SPaul Saab 	h = NULL_IFILE;
616a5f0fb15SPaul Saab 	do
617a5f0fb15SPaul Saab 	{
618a5f0fb15SPaul Saab 		if ((h = next_ifile(h)) == NULL_IFILE)
619a5f0fb15SPaul Saab 		{
620a5f0fb15SPaul Saab 			/*
621a5f0fb15SPaul Saab 			 * Reached end of the list without finding it.
622a5f0fb15SPaul Saab 			 */
623a5f0fb15SPaul Saab 			return (1);
624a5f0fb15SPaul Saab 		}
625a5f0fb15SPaul Saab 	} while (get_index(h) != n);
626a5f0fb15SPaul Saab 
627a5f0fb15SPaul Saab 	return (edit_ifile(h));
628a5f0fb15SPaul Saab }
629a5f0fb15SPaul Saab 
630a5f0fb15SPaul Saab 	public IFILE
631a5f0fb15SPaul Saab save_curr_ifile()
632a5f0fb15SPaul Saab {
633a5f0fb15SPaul Saab 	if (curr_ifile != NULL_IFILE)
634a5f0fb15SPaul Saab 		hold_ifile(curr_ifile, 1);
635a5f0fb15SPaul Saab 	return (curr_ifile);
636a5f0fb15SPaul Saab }
637a5f0fb15SPaul Saab 
638a5f0fb15SPaul Saab 	public void
639a5f0fb15SPaul Saab unsave_ifile(save_ifile)
640a5f0fb15SPaul Saab 	IFILE save_ifile;
641a5f0fb15SPaul Saab {
642a5f0fb15SPaul Saab 	if (save_ifile != NULL_IFILE)
643a5f0fb15SPaul Saab 		hold_ifile(save_ifile, -1);
644a5f0fb15SPaul Saab }
645a5f0fb15SPaul Saab 
646a5f0fb15SPaul Saab /*
647a5f0fb15SPaul Saab  * Reedit the ifile which was previously open.
648a5f0fb15SPaul Saab  */
649a5f0fb15SPaul Saab 	public void
650a5f0fb15SPaul Saab reedit_ifile(save_ifile)
651a5f0fb15SPaul Saab 	IFILE save_ifile;
652a5f0fb15SPaul Saab {
653a5f0fb15SPaul Saab 	IFILE next;
654a5f0fb15SPaul Saab 	IFILE prev;
655a5f0fb15SPaul Saab 
656a5f0fb15SPaul Saab 	/*
657a5f0fb15SPaul Saab 	 * Try to reopen the ifile.
658a5f0fb15SPaul Saab 	 * Note that opening it may fail (maybe the file was removed),
659a5f0fb15SPaul Saab 	 * in which case the ifile will be deleted from the list.
660a5f0fb15SPaul Saab 	 * So save the next and prev ifiles first.
661a5f0fb15SPaul Saab 	 */
662a5f0fb15SPaul Saab 	unsave_ifile(save_ifile);
663a5f0fb15SPaul Saab 	next = next_ifile(save_ifile);
664a5f0fb15SPaul Saab 	prev = prev_ifile(save_ifile);
665a5f0fb15SPaul Saab 	if (edit_ifile(save_ifile) == 0)
666a5f0fb15SPaul Saab 		return;
667a5f0fb15SPaul Saab 	/*
668a5f0fb15SPaul Saab 	 * If can't reopen it, open the next input file in the list.
669a5f0fb15SPaul Saab 	 */
670a5f0fb15SPaul Saab 	if (next != NULL_IFILE && edit_inext(next, 0) == 0)
671a5f0fb15SPaul Saab 		return;
672a5f0fb15SPaul Saab 	/*
673a5f0fb15SPaul Saab 	 * If can't open THAT one, open the previous input file in the list.
674a5f0fb15SPaul Saab 	 */
675a5f0fb15SPaul Saab 	if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
676a5f0fb15SPaul Saab 		return;
677a5f0fb15SPaul Saab 	/*
678a5f0fb15SPaul Saab 	 * If can't even open that, we're stuck.  Just quit.
679a5f0fb15SPaul Saab 	 */
680a5f0fb15SPaul Saab 	quit(QUIT_ERROR);
681a5f0fb15SPaul Saab }
682a5f0fb15SPaul Saab 
683464501a8SXin LI 	public void
684464501a8SXin LI reopen_curr_ifile()
685464501a8SXin LI {
686464501a8SXin LI 	IFILE save_ifile = save_curr_ifile();
687464501a8SXin LI 	close_file();
688464501a8SXin LI 	reedit_ifile(save_ifile);
689464501a8SXin LI }
690464501a8SXin LI 
691a5f0fb15SPaul Saab /*
692a5f0fb15SPaul Saab  * Edit standard input.
693a5f0fb15SPaul Saab  */
694a5f0fb15SPaul Saab 	public int
695a5f0fb15SPaul Saab edit_stdin()
696a5f0fb15SPaul Saab {
697a5f0fb15SPaul Saab 	if (isatty(fd0))
698a5f0fb15SPaul Saab 	{
699a5f0fb15SPaul Saab 		error("Missing filename (\"less --help\" for help)", NULL_PARG);
700a5f0fb15SPaul Saab 		quit(QUIT_OK);
701a5f0fb15SPaul Saab 	}
702a5f0fb15SPaul Saab 	return (edit("-"));
703a5f0fb15SPaul Saab }
704a5f0fb15SPaul Saab 
705a5f0fb15SPaul Saab /*
706a5f0fb15SPaul Saab  * Copy a file directly to standard output.
707a5f0fb15SPaul Saab  * Used if standard output is not a tty.
708a5f0fb15SPaul Saab  */
709a5f0fb15SPaul Saab 	public void
710a5f0fb15SPaul Saab cat_file()
711a5f0fb15SPaul Saab {
712a5f0fb15SPaul Saab 	register int c;
713a5f0fb15SPaul Saab 
714a5f0fb15SPaul Saab 	while ((c = ch_forw_get()) != EOI)
715a5f0fb15SPaul Saab 		putchr(c);
716a5f0fb15SPaul Saab 	flush();
717a5f0fb15SPaul Saab }
718a5f0fb15SPaul Saab 
719a5f0fb15SPaul Saab #if LOGFILE
720a5f0fb15SPaul Saab 
721a5f0fb15SPaul Saab /*
722a5f0fb15SPaul Saab  * If the user asked for a log file and our input file
723a5f0fb15SPaul Saab  * is standard input, create the log file.
724a5f0fb15SPaul Saab  * We take care not to blindly overwrite an existing file.
725a5f0fb15SPaul Saab  */
726a5f0fb15SPaul Saab 	public void
727a5f0fb15SPaul Saab use_logfile(filename)
728a5f0fb15SPaul Saab 	char *filename;
729a5f0fb15SPaul Saab {
730a5f0fb15SPaul Saab 	register int exists;
731a5f0fb15SPaul Saab 	register int answer;
732a5f0fb15SPaul Saab 	PARG parg;
733a5f0fb15SPaul Saab 
734a5f0fb15SPaul Saab 	if (ch_getflags() & CH_CANSEEK)
735a5f0fb15SPaul Saab 		/*
736a5f0fb15SPaul Saab 		 * Can't currently use a log file on a file that can seek.
737a5f0fb15SPaul Saab 		 */
738a5f0fb15SPaul Saab 		return;
739a5f0fb15SPaul Saab 
740a5f0fb15SPaul Saab 	/*
741a5f0fb15SPaul Saab 	 * {{ We could use access() here. }}
742a5f0fb15SPaul Saab 	 */
743000ba3e8STim J. Robbins 	filename = shell_unquote(filename);
744a5f0fb15SPaul Saab 	exists = open(filename, OPEN_READ);
745a5f0fb15SPaul Saab 	close(exists);
746a5f0fb15SPaul Saab 	exists = (exists >= 0);
747a5f0fb15SPaul Saab 
748a5f0fb15SPaul Saab 	/*
749a5f0fb15SPaul Saab 	 * Decide whether to overwrite the log file or append to it.
750a5f0fb15SPaul Saab 	 * If it doesn't exist we "overwrite" it.
751a5f0fb15SPaul Saab 	 */
752a5f0fb15SPaul Saab 	if (!exists || force_logfile)
753a5f0fb15SPaul Saab 	{
754a5f0fb15SPaul Saab 		/*
755a5f0fb15SPaul Saab 		 * Overwrite (or create) the log file.
756a5f0fb15SPaul Saab 		 */
757a5f0fb15SPaul Saab 		answer = 'O';
758a5f0fb15SPaul Saab 	} else
759a5f0fb15SPaul Saab 	{
760a5f0fb15SPaul Saab 		/*
761a5f0fb15SPaul Saab 		 * Ask user what to do.
762a5f0fb15SPaul Saab 		 */
763a5f0fb15SPaul Saab 		parg.p_string = filename;
764a5f0fb15SPaul Saab 		answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
765a5f0fb15SPaul Saab 	}
766a5f0fb15SPaul Saab 
767a5f0fb15SPaul Saab loop:
768a5f0fb15SPaul Saab 	switch (answer)
769a5f0fb15SPaul Saab 	{
770a5f0fb15SPaul Saab 	case 'O': case 'o':
771a5f0fb15SPaul Saab 		/*
772a5f0fb15SPaul Saab 		 * Overwrite: create the file.
773a5f0fb15SPaul Saab 		 */
774a5f0fb15SPaul Saab 		logfile = creat(filename, 0644);
775a5f0fb15SPaul Saab 		break;
776a5f0fb15SPaul Saab 	case 'A': case 'a':
777a5f0fb15SPaul Saab 		/*
778a5f0fb15SPaul Saab 		 * Append: open the file and seek to the end.
779a5f0fb15SPaul Saab 		 */
780a5f0fb15SPaul Saab 		logfile = open(filename, OPEN_APPEND);
781464501a8SXin LI 		if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
782a5f0fb15SPaul Saab 		{
783a5f0fb15SPaul Saab 			close(logfile);
784a5f0fb15SPaul Saab 			logfile = -1;
785a5f0fb15SPaul Saab 		}
786a5f0fb15SPaul Saab 		break;
787a5f0fb15SPaul Saab 	case 'D': case 'd':
788a5f0fb15SPaul Saab 		/*
789a5f0fb15SPaul Saab 		 * Don't do anything.
790a5f0fb15SPaul Saab 		 */
791a5f0fb15SPaul Saab 		free(filename);
792a5f0fb15SPaul Saab 		return;
793a5f0fb15SPaul Saab 	case 'q':
794a5f0fb15SPaul Saab 		quit(QUIT_OK);
795a5f0fb15SPaul Saab 		/*NOTREACHED*/
796a5f0fb15SPaul Saab 	default:
797a5f0fb15SPaul Saab 		/*
798a5f0fb15SPaul Saab 		 * Eh?
799a5f0fb15SPaul Saab 		 */
800a5f0fb15SPaul Saab 		answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
801a5f0fb15SPaul Saab 		goto loop;
802a5f0fb15SPaul Saab 	}
803a5f0fb15SPaul Saab 
804a5f0fb15SPaul Saab 	if (logfile < 0)
805a5f0fb15SPaul Saab 	{
806a5f0fb15SPaul Saab 		/*
807a5f0fb15SPaul Saab 		 * Error in opening logfile.
808a5f0fb15SPaul Saab 		 */
809a5f0fb15SPaul Saab 		parg.p_string = filename;
810a5f0fb15SPaul Saab 		error("Cannot write to \"%s\"", &parg);
811a5f0fb15SPaul Saab 		free(filename);
812a5f0fb15SPaul Saab 		return;
813a5f0fb15SPaul Saab 	}
814a5f0fb15SPaul Saab 	free(filename);
815a5f0fb15SPaul Saab 	SET_BINARY(logfile);
816a5f0fb15SPaul Saab }
817a5f0fb15SPaul Saab 
818a5f0fb15SPaul Saab #endif
819