Merge branch 'vendor/DHCPCD'
[dragonfly.git] / contrib / less / edit.c
blob82a79b134c3e486cf881012d1af0916c285673ee
1 /*
2 * Copyright (C) 1984-2019 Mark Nudelman
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
7 * For more information, see the README file.
8 */
11 #include "less.h"
12 #include "position.h"
13 #if HAVE_STAT
14 #include <sys/stat.h>
15 #endif
16 #if OS2
17 #include <signal.h>
18 #endif
20 public int fd0 = 0;
22 extern int new_file;
23 extern int errmsgs;
24 extern int cbufs;
25 extern char *every_first_cmd;
26 extern int any_display;
27 extern int force_open;
28 extern int is_tty;
29 extern int sigs;
30 extern IFILE curr_ifile;
31 extern IFILE old_ifile;
32 extern struct scrpos initial_scrpos;
33 extern void *ml_examine;
34 #if SPACES_IN_FILENAMES
35 extern char openquote;
36 extern char closequote;
37 #endif
39 #if LOGFILE
40 extern int logfile;
41 extern int force_logfile;
42 extern char *namelogfile;
43 #endif
45 #if HAVE_STAT_INO
46 public dev_t curr_dev;
47 public ino_t curr_ino;
48 #endif
52 * Textlist functions deal with a list of words separated by spaces.
53 * init_textlist sets up a textlist structure.
54 * forw_textlist uses that structure to iterate thru the list of
55 * words, returning each one as a standard null-terminated string.
56 * back_textlist does the same, but runs thru the list backwards.
58 public void
59 init_textlist(tlist, str)
60 struct textlist *tlist;
61 char *str;
63 char *s;
64 #if SPACES_IN_FILENAMES
65 int meta_quoted = 0;
66 int delim_quoted = 0;
67 char *esc = get_meta_escape();
68 int esclen = (int) strlen(esc);
69 #endif
71 tlist->string = skipsp(str);
72 tlist->endstring = tlist->string + strlen(tlist->string);
73 for (s = str; s < tlist->endstring; s++)
75 #if SPACES_IN_FILENAMES
76 if (meta_quoted)
78 meta_quoted = 0;
79 } else if (esclen > 0 && s + esclen < tlist->endstring &&
80 strncmp(s, esc, esclen) == 0)
82 meta_quoted = 1;
83 s += esclen - 1;
84 } else if (delim_quoted)
86 if (*s == closequote)
87 delim_quoted = 0;
88 } else /* (!delim_quoted) */
90 if (*s == openquote)
91 delim_quoted = 1;
92 else if (*s == ' ')
93 *s = '\0';
95 #else
96 if (*s == ' ')
97 *s = '\0';
98 #endif
102 public char *
103 forw_textlist(tlist, prev)
104 struct textlist *tlist;
105 char *prev;
107 char *s;
110 * prev == NULL means return the first word in the list.
111 * Otherwise, return the word after "prev".
113 if (prev == NULL)
114 s = tlist->string;
115 else
116 s = prev + strlen(prev);
117 if (s >= tlist->endstring)
118 return (NULL);
119 while (*s == '\0')
120 s++;
121 if (s >= tlist->endstring)
122 return (NULL);
123 return (s);
126 public char *
127 back_textlist(tlist, prev)
128 struct textlist *tlist;
129 char *prev;
131 char *s;
134 * prev == NULL means return the last word in the list.
135 * Otherwise, return the word before "prev".
137 if (prev == NULL)
138 s = tlist->endstring;
139 else if (prev <= tlist->string)
140 return (NULL);
141 else
142 s = prev - 1;
143 while (*s == '\0')
144 s--;
145 if (s <= tlist->string)
146 return (NULL);
147 while (s[-1] != '\0' && s > tlist->string)
148 s--;
149 return (s);
153 * Close a pipe opened via popen.
155 static void
156 close_pipe(FILE *pipefd)
158 if (pipefd == NULL)
159 return;
160 #if OS2
162 * The pclose function of OS/2 emx sometimes fails.
163 * Send SIGINT to the piped process before closing it.
165 kill(pipefd->_pid, SIGINT);
166 #endif
167 pclose(pipefd);
171 * Close the current input file.
173 static void
174 close_file(VOID_PARAM)
176 struct scrpos scrpos;
177 int chflags;
178 FILE *altpipe;
179 char *altfilename;
181 if (curr_ifile == NULL_IFILE)
182 return;
185 * Save the current position so that we can return to
186 * the same position if we edit this file again.
188 get_scrpos(&scrpos, TOP);
189 if (scrpos.pos != NULL_POSITION)
191 store_pos(curr_ifile, &scrpos);
192 lastmark();
195 * Close the file descriptor, unless it is a pipe.
197 chflags = ch_getflags();
198 ch_close();
200 * If we opened a file using an alternate name,
201 * do special stuff to close it.
203 altfilename = get_altfilename(curr_ifile);
204 if (altfilename != NULL)
206 altpipe = get_altpipe(curr_ifile);
207 if (altpipe != NULL && !(chflags & CH_KEEPOPEN))
209 close_pipe(altpipe);
210 set_altpipe(curr_ifile, NULL);
212 close_altfile(altfilename, get_filename(curr_ifile));
213 set_altfilename(curr_ifile, NULL);
215 curr_ifile = NULL_IFILE;
216 #if HAVE_STAT_INO
217 curr_ino = curr_dev = 0;
218 #endif
222 * Edit a new file (given its name).
223 * Filename == "-" means standard input.
224 * Filename == NULL means just close the current file.
226 public int
227 edit(filename)
228 char *filename;
230 if (filename == NULL)
231 return (edit_ifile(NULL_IFILE));
232 return (edit_ifile(get_ifile(filename, curr_ifile)));
236 * Edit a new file (given its IFILE).
237 * ifile == NULL means just close the current file.
239 public int
240 edit_ifile(ifile)
241 IFILE ifile;
243 int f;
244 int answer;
245 int no_display;
246 int chflags;
247 char *filename;
248 char *open_filename;
249 char *alt_filename;
250 void *altpipe;
251 IFILE was_curr_ifile;
252 PARG parg;
254 if (ifile == curr_ifile)
257 * Already have the correct file open.
259 return (0);
263 * We must close the currently open file now.
264 * This is necessary to make the open_altfile/close_altfile pairs
265 * nest properly (or rather to avoid nesting at all).
266 * {{ Some stupid implementations of popen() mess up if you do:
267 * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
269 #if LOGFILE
270 end_logfile();
271 #endif
272 was_curr_ifile = save_curr_ifile();
273 if (curr_ifile != NULL_IFILE)
275 chflags = ch_getflags();
276 close_file();
277 if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
280 * Don't keep the help file in the ifile list.
282 del_ifile(was_curr_ifile);
283 was_curr_ifile = old_ifile;
287 if (ifile == NULL_IFILE)
290 * No new file to open.
291 * (Don't set old_ifile, because if you call edit_ifile(NULL),
292 * you're supposed to have saved curr_ifile yourself,
293 * and you'll restore it if necessary.)
295 unsave_ifile(was_curr_ifile);
296 return (0);
299 filename = save(get_filename(ifile));
302 * See if LESSOPEN specifies an "alternate" file to open.
304 altpipe = get_altpipe(ifile);
305 if (altpipe != NULL)
308 * File is already open.
309 * chflags and f are not used by ch_init if ifile has
310 * filestate which should be the case if we're here.
311 * Set them here to avoid uninitialized variable warnings.
313 chflags = 0;
314 f = -1;
315 alt_filename = get_altfilename(ifile);
316 open_filename = (alt_filename != NULL) ? alt_filename : filename;
317 } else
319 if (strcmp(filename, FAKE_HELPFILE) == 0 ||
320 strcmp(filename, FAKE_EMPTYFILE) == 0)
321 alt_filename = NULL;
322 else
323 alt_filename = open_altfile(filename, &f, &altpipe);
325 open_filename = (alt_filename != NULL) ? alt_filename : filename;
327 chflags = 0;
328 if (altpipe != NULL)
331 * The alternate "file" is actually a pipe.
332 * f has already been set to the file descriptor of the pipe
333 * in the call to open_altfile above.
334 * Keep the file descriptor open because it was opened
335 * via popen(), and pclose() wants to close it.
337 chflags |= CH_POPENED;
338 if (strcmp(filename, "-") == 0)
339 chflags |= CH_KEEPOPEN;
340 } else if (strcmp(filename, "-") == 0)
343 * Use standard input.
344 * Keep the file descriptor open because we can't reopen it.
346 f = fd0;
347 chflags |= CH_KEEPOPEN;
349 * Must switch stdin to BINARY mode.
351 SET_BINARY(f);
352 #if MSDOS_COMPILER==DJGPPC
354 * Setting stdin to binary by default causes
355 * Ctrl-C to not raise SIGINT. We must undo
356 * that side-effect.
358 __djgpp_set_ctrl_c(1);
359 #endif
360 } else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
362 f = -1;
363 chflags |= CH_NODATA;
364 } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
366 f = -1;
367 chflags |= CH_HELPFILE;
368 } else if ((parg.p_string = bad_file(open_filename)) != NULL)
371 * It looks like a bad file. Don't try to open it.
373 error("%s", &parg);
374 free(parg.p_string);
375 err1:
376 if (alt_filename != NULL)
378 close_pipe(altpipe);
379 close_altfile(alt_filename, filename);
380 free(alt_filename);
382 del_ifile(ifile);
383 free(filename);
385 * Re-open the current file.
387 if (was_curr_ifile == ifile)
390 * Whoops. The "current" ifile is the one we just deleted.
391 * Just give up.
393 quit(QUIT_ERROR);
395 reedit_ifile(was_curr_ifile);
396 return (1);
397 } else if ((f = open(open_filename, OPEN_READ)) < 0)
400 * Got an error trying to open it.
402 parg.p_string = errno_message(filename);
403 error("%s", &parg);
404 free(parg.p_string);
405 goto err1;
406 } else
408 chflags |= CH_CANSEEK;
409 if (!force_open && !opened(ifile) && bin_file(f))
412 * Looks like a binary file.
413 * Ask user if we should proceed.
415 parg.p_string = filename;
416 answer = query("\"%s\" may be a binary file. See it anyway? ",
417 &parg);
418 if (answer != 'y' && answer != 'Y')
420 close(f);
421 goto err1;
428 * Get the new ifile.
429 * Get the saved position for the file.
431 if (was_curr_ifile != NULL_IFILE)
433 old_ifile = was_curr_ifile;
434 unsave_ifile(was_curr_ifile);
436 curr_ifile = ifile;
437 set_altfilename(curr_ifile, alt_filename);
438 set_altpipe(curr_ifile, altpipe);
439 set_open(curr_ifile); /* File has been opened */
440 get_pos(curr_ifile, &initial_scrpos);
441 new_file = TRUE;
442 ch_init(f, chflags);
444 if (!(chflags & CH_HELPFILE))
446 #if LOGFILE
447 if (namelogfile != NULL && is_tty)
448 use_logfile(namelogfile);
449 #endif
450 #if HAVE_STAT_INO
451 /* Remember the i-number and device of the opened file. */
452 if (strcmp(open_filename, "-") != 0)
454 struct stat statbuf;
455 int r = stat(open_filename, &statbuf);
456 if (r == 0)
458 curr_ino = statbuf.st_ino;
459 curr_dev = statbuf.st_dev;
462 #endif
463 if (every_first_cmd != NULL)
465 ungetcc(CHAR_END_COMMAND);
466 ungetsc(every_first_cmd);
470 no_display = !any_display;
471 flush();
472 any_display = TRUE;
474 if (is_tty)
477 * Output is to a real tty.
481 * Indicate there is nothing displayed yet.
483 pos_clear();
484 clr_linenum();
485 #if HILITE_SEARCH
486 clr_hilite();
487 #endif
488 if (strcmp(filename, FAKE_HELPFILE) && strcmp(filename, FAKE_EMPTYFILE))
490 char *qfilename = shell_quote(filename);
491 cmd_addhist(ml_examine, qfilename, 1);
492 free(qfilename);
495 if (no_display && errmsgs > 0)
498 * We displayed some messages on error output
499 * (file descriptor 2; see error() function).
500 * Before erasing the screen contents,
501 * display the file name and wait for a keystroke.
503 parg.p_string = filename;
504 error("%s", &parg);
507 free(filename);
508 return (0);
512 * Edit a space-separated list of files.
513 * For each filename in the list, enter it into the ifile list.
514 * Then edit the first one.
516 public int
517 edit_list(filelist)
518 char *filelist;
520 IFILE save_ifile;
521 char *good_filename;
522 char *filename;
523 char *gfilelist;
524 char *gfilename;
525 char *qfilename;
526 struct textlist tl_files;
527 struct textlist tl_gfiles;
529 save_ifile = save_curr_ifile();
530 good_filename = NULL;
533 * Run thru each filename in the list.
534 * Try to glob the filename.
535 * If it doesn't expand, just try to open the filename.
536 * If it does expand, try to open each name in that list.
538 init_textlist(&tl_files, filelist);
539 filename = NULL;
540 while ((filename = forw_textlist(&tl_files, filename)) != NULL)
542 gfilelist = lglob(filename);
543 init_textlist(&tl_gfiles, gfilelist);
544 gfilename = NULL;
545 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
547 qfilename = shell_unquote(gfilename);
548 if (edit(qfilename) == 0 && good_filename == NULL)
549 good_filename = get_filename(curr_ifile);
550 free(qfilename);
552 free(gfilelist);
555 * Edit the first valid filename in the list.
557 if (good_filename == NULL)
559 unsave_ifile(save_ifile);
560 return (1);
562 if (get_ifile(good_filename, curr_ifile) == curr_ifile)
565 * Trying to edit the current file; don't reopen it.
567 unsave_ifile(save_ifile);
568 return (0);
570 reedit_ifile(save_ifile);
571 return (edit(good_filename));
575 * Edit the first file in the command line (ifile) list.
577 public int
578 edit_first(VOID_PARAM)
580 if (nifile() == 0)
581 return (edit_stdin());
582 curr_ifile = NULL_IFILE;
583 return (edit_next(1));
587 * Edit the last file in the command line (ifile) list.
589 public int
590 edit_last(VOID_PARAM)
592 curr_ifile = NULL_IFILE;
593 return (edit_prev(1));
598 * Edit the n-th next or previous file in the command line (ifile) list.
600 static int
601 edit_istep(h, n, dir)
602 IFILE h;
603 int n;
604 int dir;
606 IFILE next;
609 * Skip n filenames, then try to edit each filename.
611 for (;;)
613 next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
614 if (--n < 0)
616 if (edit_ifile(h) == 0)
617 break;
619 if (next == NULL_IFILE)
622 * Reached end of the ifile list.
624 return (1);
626 if (ABORT_SIGS())
629 * Interrupt breaks out, if we're in a long
630 * list of files that can't be opened.
632 return (1);
634 h = next;
637 * Found a file that we can edit.
639 return (0);
642 static int
643 edit_inext(h, n)
644 IFILE h;
645 int n;
647 return (edit_istep(h, n, +1));
650 public int
651 edit_next(n)
652 int n;
654 return edit_istep(curr_ifile, n, +1);
657 static int
658 edit_iprev(h, n)
659 IFILE h;
660 int n;
662 return (edit_istep(h, n, -1));
665 public int
666 edit_prev(n)
667 int n;
669 return edit_istep(curr_ifile, n, -1);
673 * Edit a specific file in the command line (ifile) list.
675 public int
676 edit_index(n)
677 int n;
679 IFILE h;
681 h = NULL_IFILE;
684 if ((h = next_ifile(h)) == NULL_IFILE)
687 * Reached end of the list without finding it.
689 return (1);
691 } while (get_index(h) != n);
693 return (edit_ifile(h));
696 public IFILE
697 save_curr_ifile(VOID_PARAM)
699 if (curr_ifile != NULL_IFILE)
700 hold_ifile(curr_ifile, 1);
701 return (curr_ifile);
704 public void
705 unsave_ifile(save_ifile)
706 IFILE save_ifile;
708 if (save_ifile != NULL_IFILE)
709 hold_ifile(save_ifile, -1);
713 * Reedit the ifile which was previously open.
715 public void
716 reedit_ifile(save_ifile)
717 IFILE save_ifile;
719 IFILE next;
720 IFILE prev;
723 * Try to reopen the ifile.
724 * Note that opening it may fail (maybe the file was removed),
725 * in which case the ifile will be deleted from the list.
726 * So save the next and prev ifiles first.
728 unsave_ifile(save_ifile);
729 next = next_ifile(save_ifile);
730 prev = prev_ifile(save_ifile);
731 if (edit_ifile(save_ifile) == 0)
732 return;
734 * If can't reopen it, open the next input file in the list.
736 if (next != NULL_IFILE && edit_inext(next, 0) == 0)
737 return;
739 * If can't open THAT one, open the previous input file in the list.
741 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
742 return;
744 * If can't even open that, we're stuck. Just quit.
746 quit(QUIT_ERROR);
749 public void
750 reopen_curr_ifile(VOID_PARAM)
752 IFILE save_ifile = save_curr_ifile();
753 close_file();
754 reedit_ifile(save_ifile);
758 * Edit standard input.
760 public int
761 edit_stdin(VOID_PARAM)
763 if (isatty(fd0))
765 error("Missing filename (\"less --help\" for help)", NULL_PARG);
766 quit(QUIT_OK);
768 return (edit("-"));
772 * Copy a file directly to standard output.
773 * Used if standard output is not a tty.
775 public void
776 cat_file(VOID_PARAM)
778 int c;
780 while ((c = ch_forw_get()) != EOI)
781 putchr(c);
782 flush();
785 #if LOGFILE
788 * If the user asked for a log file and our input file
789 * is standard input, create the log file.
790 * We take care not to blindly overwrite an existing file.
792 public void
793 use_logfile(filename)
794 char *filename;
796 int exists;
797 int answer;
798 PARG parg;
800 if (ch_getflags() & CH_CANSEEK)
802 * Can't currently use a log file on a file that can seek.
804 return;
807 * {{ We could use access() here. }}
809 exists = open(filename, OPEN_READ);
810 if (exists >= 0)
811 close(exists);
812 exists = (exists >= 0);
815 * Decide whether to overwrite the log file or append to it.
816 * If it doesn't exist we "overwrite" it.
818 if (!exists || force_logfile)
821 * Overwrite (or create) the log file.
823 answer = 'O';
824 } else
827 * Ask user what to do.
829 parg.p_string = filename;
830 answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
833 loop:
834 switch (answer)
836 case 'O': case 'o':
838 * Overwrite: create the file.
840 logfile = creat(filename, 0644);
841 break;
842 case 'A': case 'a':
844 * Append: open the file and seek to the end.
846 logfile = open(filename, OPEN_APPEND);
847 if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
849 close(logfile);
850 logfile = -1;
852 break;
853 case 'D': case 'd':
855 * Don't do anything.
857 free(filename);
858 return;
859 case 'q':
860 quit(QUIT_OK);
861 /*NOTREACHED*/
862 default:
864 * Eh?
866 answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
867 goto loop;
870 if (logfile < 0)
873 * Error in opening logfile.
875 parg.p_string = filename;
876 error("Cannot write to \"%s\"", &parg);
877 return;
879 SET_BINARY(logfile);
882 #endif