Drop main() prototype. Syncs with NetBSD-8
[minix.git] / external / bsd / less / dist / edit.c
blobef6b3a7a3240ebf24d8a2cfd8f327b3f1638b48c
1 /* $NetBSD: edit.c,v 1.4 2013/09/04 19:44:21 tron Exp $ */
3 /*
4 * Copyright (C) 1984-2012 Mark Nudelman
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
9 * For more information, see the README file.
13 #include "less.h"
14 #if HAVE_STAT
15 #include <sys/stat.h>
16 #endif
18 public int fd0 = 0;
20 extern int new_file;
21 extern int errmsgs;
22 extern int cbufs;
23 extern char *every_first_cmd;
24 extern int any_display;
25 extern int force_open;
26 extern int is_tty;
27 extern int sigs;
28 extern IFILE curr_ifile;
29 extern IFILE old_ifile;
30 extern struct scrpos initial_scrpos;
31 extern void *constant ml_examine;
32 #if SPACES_IN_FILENAMES
33 extern char openquote;
34 extern char closequote;
35 #endif
37 #if LOGFILE
38 extern int logfile;
39 extern int force_logfile;
40 extern char *namelogfile;
41 #endif
43 #if HAVE_STAT_INO
44 public dev_t curr_dev;
45 public ino_t curr_ino;
46 #endif
48 char *curr_altfilename = NULL;
49 static void *curr_altpipe;
52 static void close_file __P((void));
53 static int edit_istep __P((IFILE, int, int));
54 static int edit_inext __P((IFILE, int));
55 static int edit_iprev __P((IFILE, int));
58 * Textlist functions deal with a list of words separated by spaces.
59 * init_textlist sets up a textlist structure.
60 * forw_textlist uses that structure to iterate thru the list of
61 * words, returning each one as a standard null-terminated string.
62 * back_textlist does the same, but runs thru the list backwards.
64 public void
65 init_textlist(tlist, str)
66 struct textlist *tlist;
67 char *str;
69 char *s;
70 #if SPACES_IN_FILENAMES
71 int meta_quoted = 0;
72 int delim_quoted = 0;
73 char *esc = get_meta_escape();
74 int esclen = strlen(esc);
75 #endif
77 tlist->string = skipsp(str);
78 tlist->endstring = tlist->string + strlen(tlist->string);
79 for (s = str; s < tlist->endstring; s++)
81 #if SPACES_IN_FILENAMES
82 if (meta_quoted)
84 meta_quoted = 0;
85 } else if (esclen > 0 && s + esclen < tlist->endstring &&
86 strncmp(s, esc, esclen) == 0)
88 meta_quoted = 1;
89 s += esclen - 1;
90 } else if (delim_quoted)
92 if (*s == closequote)
93 delim_quoted = 0;
94 } else /* (!delim_quoted) */
96 if (*s == openquote)
97 delim_quoted = 1;
98 else if (*s == ' ')
99 *s = '\0';
101 #else
102 if (*s == ' ')
103 *s = '\0';
104 #endif
108 public char *
109 forw_textlist(tlist, prev)
110 struct textlist *tlist;
111 char *prev;
113 char *s;
116 * prev == NULL means return the first word in the list.
117 * Otherwise, return the word after "prev".
119 if (prev == NULL)
120 s = tlist->string;
121 else
122 s = prev + strlen(prev);
123 if (s >= tlist->endstring)
124 return (NULL);
125 while (*s == '\0')
126 s++;
127 if (s >= tlist->endstring)
128 return (NULL);
129 return (s);
132 public char *
133 back_textlist(tlist, prev)
134 struct textlist *tlist;
135 char *prev;
137 char *s;
140 * prev == NULL means return the last word in the list.
141 * Otherwise, return the word before "prev".
143 if (prev == NULL)
144 s = tlist->endstring;
145 else if (prev <= tlist->string)
146 return (NULL);
147 else
148 s = prev - 1;
149 while (*s == '\0')
150 s--;
151 if (s <= tlist->string)
152 return (NULL);
153 while (s[-1] != '\0' && s > tlist->string)
154 s--;
155 return (s);
159 * Close the current input file.
161 static void
162 close_file()
164 struct scrpos scrpos;
166 if (curr_ifile == NULL_IFILE)
167 return;
170 * Save the current position so that we can return to
171 * the same position if we edit this file again.
173 get_scrpos(&scrpos);
174 if (scrpos.pos != NULL_POSITION)
176 store_pos(curr_ifile, &scrpos);
177 lastmark();
180 * Close the file descriptor, unless it is a pipe.
182 ch_close();
184 * If we opened a file using an alternate name,
185 * do special stuff to close it.
187 if (curr_altfilename != NULL)
189 close_altfile(curr_altfilename, get_filename(curr_ifile),
190 curr_altpipe);
191 free(curr_altfilename);
192 curr_altfilename = NULL;
194 curr_ifile = NULL_IFILE;
195 #if HAVE_STAT_INO
196 curr_ino = curr_dev = 0;
197 #endif
201 * Edit a new file (given its name).
202 * Filename == "-" means standard input.
203 * Filename == NULL means just close the current file.
205 public int
206 edit(filename)
207 char *filename;
209 if (filename == NULL)
210 return (edit_ifile(NULL_IFILE));
211 return (edit_ifile(get_ifile(filename, curr_ifile)));
215 * Edit a new file (given its IFILE).
216 * ifile == NULL means just close the current file.
218 public int
219 edit_ifile(ifile)
220 IFILE ifile;
222 int f;
223 int answer;
224 int no_display;
225 int chflags;
226 char *filename;
227 char *open_filename;
228 char *qopen_filename;
229 char *alt_filename;
230 void *alt_pipe;
231 IFILE was_curr_ifile;
232 PARG parg;
234 if (ifile == curr_ifile)
237 * Already have the correct file open.
239 return (0);
243 * We must close the currently open file now.
244 * This is necessary to make the open_altfile/close_altfile pairs
245 * nest properly (or rather to avoid nesting at all).
246 * {{ Some stupid implementations of popen() mess up if you do:
247 * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
249 #if LOGFILE
250 end_logfile();
251 #endif
252 was_curr_ifile = save_curr_ifile();
253 if (curr_ifile != NULL_IFILE)
255 chflags = ch_getflags();
256 close_file();
257 if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
260 * Don't keep the help file in the ifile list.
262 del_ifile(was_curr_ifile);
263 was_curr_ifile = old_ifile;
267 if (ifile == NULL_IFILE)
270 * No new file to open.
271 * (Don't set old_ifile, because if you call edit_ifile(NULL),
272 * you're supposed to have saved curr_ifile yourself,
273 * and you'll restore it if necessary.)
275 unsave_ifile(was_curr_ifile);
276 return (0);
279 filename = save(get_filename(ifile));
281 * See if LESSOPEN specifies an "alternate" file to open.
283 alt_pipe = NULL;
284 alt_filename = open_altfile(filename, &f, &alt_pipe);
285 open_filename = (alt_filename != NULL) ? alt_filename : filename;
286 qopen_filename = shell_unquote(open_filename);
288 chflags = 0;
289 if (alt_pipe != NULL)
292 * The alternate "file" is actually a pipe.
293 * f has already been set to the file descriptor of the pipe
294 * in the call to open_altfile above.
295 * Keep the file descriptor open because it was opened
296 * via popen(), and pclose() wants to close it.
298 chflags |= CH_POPENED;
299 } else if (strcmp(open_filename, "-") == 0)
302 * Use standard input.
303 * Keep the file descriptor open because we can't reopen it.
305 f = fd0;
306 chflags |= CH_KEEPOPEN;
308 * Must switch stdin to BINARY mode.
310 SET_BINARY(f);
311 #if MSDOS_COMPILER==DJGPPC
313 * Setting stdin to binary by default causes
314 * Ctrl-C to not raise SIGINT. We must undo
315 * that side-effect.
317 __djgpp_set_ctrl_c(1);
318 #endif
319 } else if (strcmp(open_filename, FAKE_EMPTYFILE) == 0)
321 f = -1;
322 chflags |= CH_NODATA;
323 } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
325 f = -1;
326 chflags |= CH_HELPFILE;
327 } else if ((parg.p_string = bad_file(open_filename)) != NULL)
330 * It looks like a bad file. Don't try to open it.
332 error("%s", &parg);
333 free((void *)parg.p_string);
334 err1:
335 if (alt_filename != NULL)
337 close_altfile(alt_filename, filename, alt_pipe);
338 free(alt_filename);
340 del_ifile(ifile);
341 free(qopen_filename);
342 free(filename);
344 * Re-open the current file.
346 if (was_curr_ifile == ifile)
349 * Whoops. The "current" ifile is the one we just deleted.
350 * Just give up.
352 quit(QUIT_ERROR);
354 reedit_ifile(was_curr_ifile);
355 return (1);
356 } else if ((f = open(qopen_filename, OPEN_READ)) < 0)
359 * Got an error trying to open it.
361 parg.p_string = errno_message(filename);
362 error("%s", &parg);
363 free((void *)parg.p_string);
364 goto err1;
365 } else
367 chflags |= CH_CANSEEK;
368 if (!force_open && !opened(ifile) && bin_file(f))
371 * Looks like a binary file.
372 * Ask user if we should proceed.
374 parg.p_string = filename;
375 answer = query("\"%s\" may be a binary file. See it anyway? ",
376 &parg);
377 if (answer != 'y' && answer != 'Y')
379 close(f);
380 goto err1;
386 * Get the new ifile.
387 * Get the saved position for the file.
389 if (was_curr_ifile != NULL_IFILE)
391 old_ifile = was_curr_ifile;
392 unsave_ifile(was_curr_ifile);
394 curr_ifile = ifile;
395 curr_altfilename = alt_filename;
396 curr_altpipe = alt_pipe;
397 set_open(curr_ifile); /* File has been opened */
398 get_pos(curr_ifile, &initial_scrpos);
399 new_file = TRUE;
400 ch_init(f, chflags);
402 if (!(chflags & CH_HELPFILE))
404 #if LOGFILE
405 if (namelogfile != NULL && is_tty)
406 use_logfile(namelogfile);
407 #endif
408 #if HAVE_STAT_INO
409 /* Remember the i-number and device of the opened file. */
411 struct stat statbuf;
412 int r = stat(qopen_filename, &statbuf);
413 if (r == 0)
415 curr_ino = statbuf.st_ino;
416 curr_dev = statbuf.st_dev;
419 #endif
420 if (every_first_cmd != NULL)
421 ungetsc(every_first_cmd);
424 free(qopen_filename);
425 no_display = !any_display;
426 flush();
427 any_display = TRUE;
429 if (is_tty)
432 * Output is to a real tty.
436 * Indicate there is nothing displayed yet.
438 pos_clear();
439 clr_linenum();
440 #if HILITE_SEARCH
441 clr_hilite();
442 #endif
443 cmd_addhist(ml_examine, filename);
444 if (no_display && errmsgs > 0)
447 * We displayed some messages on error output
448 * (file descriptor 2; see error() function).
449 * Before erasing the screen contents,
450 * display the file name and wait for a keystroke.
452 parg.p_string = filename;
453 error("%s", &parg);
456 free(filename);
457 return (0);
461 * Edit a space-separated list of files.
462 * For each filename in the list, enter it into the ifile list.
463 * Then edit the first one.
465 public int
466 edit_list(filelist)
467 char *filelist;
469 IFILE save_ifile;
470 char *good_filename;
471 char *filename;
472 char *gfilelist;
473 char *gfilename;
474 struct textlist tl_files;
475 struct textlist tl_gfiles;
477 save_ifile = save_curr_ifile();
478 good_filename = NULL;
481 * Run thru each filename in the list.
482 * Try to glob the filename.
483 * If it doesn't expand, just try to open the filename.
484 * If it does expand, try to open each name in that list.
486 init_textlist(&tl_files, filelist);
487 filename = NULL;
488 while ((filename = forw_textlist(&tl_files, filename)) != NULL)
490 gfilelist = lglob(filename);
491 init_textlist(&tl_gfiles, gfilelist);
492 gfilename = NULL;
493 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
495 if (edit(gfilename) == 0 && good_filename == NULL)
496 good_filename = get_filename(curr_ifile);
498 free(gfilelist);
501 * Edit the first valid filename in the list.
503 if (good_filename == NULL)
505 unsave_ifile(save_ifile);
506 return (1);
508 if (get_ifile(good_filename, curr_ifile) == curr_ifile)
511 * Trying to edit the current file; don't reopen it.
513 unsave_ifile(save_ifile);
514 return (0);
516 reedit_ifile(save_ifile);
517 return (edit(good_filename));
521 * Edit the first file in the command line (ifile) list.
523 public int
524 edit_first()
526 curr_ifile = NULL_IFILE;
527 return (edit_next(1));
531 * Edit the last file in the command line (ifile) list.
533 public int
534 edit_last()
536 curr_ifile = NULL_IFILE;
537 return (edit_prev(1));
542 * Edit the n-th next or previous file in the command line (ifile) list.
544 static int
545 edit_istep(h, n, dir)
546 IFILE h;
547 int n;
548 int dir;
550 IFILE next;
553 * Skip n filenames, then try to edit each filename.
555 for (;;)
557 next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
558 if (--n < 0)
560 if (edit_ifile(h) == 0)
561 break;
563 if (next == NULL_IFILE)
566 * Reached end of the ifile list.
568 return (1);
570 if (ABORT_SIGS())
573 * Interrupt breaks out, if we're in a long
574 * list of files that can't be opened.
576 return (1);
578 h = next;
581 * Found a file that we can edit.
583 return (0);
586 static int
587 edit_inext(h, n)
588 IFILE h;
589 int n;
591 return (edit_istep(h, n, +1));
594 public int
595 edit_next(n)
596 int n;
598 return edit_istep(curr_ifile, n, +1);
601 static int
602 edit_iprev(h, n)
603 IFILE h;
604 int n;
606 return (edit_istep(h, n, -1));
609 public int
610 edit_prev(n)
611 int n;
613 return edit_istep(curr_ifile, n, -1);
617 * Edit a specific file in the command line (ifile) list.
619 public int
620 edit_index(n)
621 int n;
623 IFILE h;
625 h = NULL_IFILE;
628 if ((h = next_ifile(h)) == NULL_IFILE)
631 * Reached end of the list without finding it.
633 return (1);
635 } while (get_index(h) != n);
637 return (edit_ifile(h));
640 public IFILE
641 save_curr_ifile()
643 if (curr_ifile != NULL_IFILE)
644 hold_ifile(curr_ifile, 1);
645 return (curr_ifile);
648 public void
649 unsave_ifile(save_ifile)
650 IFILE save_ifile;
652 if (save_ifile != NULL_IFILE)
653 hold_ifile(save_ifile, -1);
657 * Reedit the ifile which was previously open.
659 public void
660 reedit_ifile(save_ifile)
661 IFILE save_ifile;
663 IFILE next;
664 IFILE prev;
667 * Try to reopen the ifile.
668 * Note that opening it may fail (maybe the file was removed),
669 * in which case the ifile will be deleted from the list.
670 * So save the next and prev ifiles first.
672 unsave_ifile(save_ifile);
673 next = next_ifile(save_ifile);
674 prev = prev_ifile(save_ifile);
675 if (edit_ifile(save_ifile) == 0)
676 return;
678 * If can't reopen it, open the next input file in the list.
680 if (next != NULL_IFILE && edit_inext(next, 0) == 0)
681 return;
683 * If can't open THAT one, open the previous input file in the list.
685 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
686 return;
688 * If can't even open that, we're stuck. Just quit.
690 quit(QUIT_ERROR);
693 public void
694 reopen_curr_ifile()
696 IFILE save_ifile = save_curr_ifile();
697 close_file();
698 reedit_ifile(save_ifile);
702 * Edit standard input.
704 public int
705 edit_stdin()
707 if (isatty(fd0))
709 error("Missing filename (\"less --help\" for help)", NULL_PARG);
710 quit(QUIT_OK);
712 return (edit("-"));
716 * Copy a file directly to standard output.
717 * Used if standard output is not a tty.
719 public void
720 cat_file()
722 register int c;
724 while ((c = ch_forw_get()) != EOI)
725 putchr(c);
726 flush();
729 #if LOGFILE
732 * If the user asked for a log file and our input file
733 * is standard input, create the log file.
734 * We take care not to blindly overwrite an existing file.
736 public void
737 use_logfile(filename)
738 char *filename;
740 register int exists;
741 register int answer;
742 PARG parg;
744 if (ch_getflags() & CH_CANSEEK)
746 * Can't currently use a log file on a file that can seek.
748 return;
751 * {{ We could use access() here. }}
753 filename = shell_unquote(filename);
754 exists = open(filename, OPEN_READ);
755 if (exists >= 0)
756 close(exists);
757 exists = (exists >= 0);
760 * Decide whether to overwrite the log file or append to it.
761 * If it doesn't exist we "overwrite" it.
763 if (!exists || force_logfile)
766 * Overwrite (or create) the log file.
768 answer = 'O';
769 } else
772 * Ask user what to do.
774 parg.p_string = filename;
775 answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
778 loop:
779 switch (answer)
781 case 'O': case 'o':
783 * Overwrite: create the file.
785 logfile = creat(filename, 0644);
786 break;
787 case 'A': case 'a':
789 * Append: open the file and seek to the end.
791 logfile = open(filename, OPEN_APPEND);
792 if (lseek(logfile, (off_t)0, SEEK_END) == BAD_LSEEK)
794 close(logfile);
795 logfile = -1;
797 break;
798 case 'D': case 'd':
800 * Don't do anything.
802 free(filename);
803 return;
804 case 'q':
805 quit(QUIT_OK);
806 /*NOTREACHED*/
807 default:
809 * Eh?
811 answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
812 goto loop;
815 if (logfile < 0)
818 * Error in opening logfile.
820 parg.p_string = filename;
821 error("Cannot write to \"%s\"", &parg);
822 free(filename);
823 return;
825 free(filename);
826 SET_BINARY(logfile);
829 #endif