Add our READMEs.
[dragonfly/vkernel-mp.git] / contrib / less-403 / edit.c
blob908f29e18d28eb7bbbec6d19eff0442d13e419dd
1 /*
2 * Copyright (C) 1984-2007 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 about less, or for information on how to
8 * contact the author, see the README file.
9 */
12 #include "less.h"
14 public int fd0 = 0;
16 extern int new_file;
17 extern int errmsgs;
18 extern int cbufs;
19 extern char *every_first_cmd;
20 extern int any_display;
21 extern int force_open;
22 extern int is_tty;
23 extern int sigs;
24 extern IFILE curr_ifile;
25 extern IFILE old_ifile;
26 extern struct scrpos initial_scrpos;
27 extern void constant *ml_examine;
28 #if SPACES_IN_FILENAMES
29 extern char openquote;
30 extern char closequote;
31 #endif
33 #if LOGFILE
34 extern int logfile;
35 extern int force_logfile;
36 extern char *namelogfile;
37 #endif
39 char *curr_altfilename = NULL;
40 static void *curr_altpipe;
44 * Textlist functions deal with a list of words separated by spaces.
45 * init_textlist sets up a textlist structure.
46 * forw_textlist uses that structure to iterate thru the list of
47 * words, returning each one as a standard null-terminated string.
48 * back_textlist does the same, but runs thru the list backwards.
50 public void
51 init_textlist(tlist, str)
52 struct textlist *tlist;
53 char *str;
55 char *s;
56 #if SPACES_IN_FILENAMES
57 int meta_quoted = 0;
58 int delim_quoted = 0;
59 char *esc = get_meta_escape();
60 int esclen = strlen(esc);
61 #endif
63 tlist->string = skipsp(str);
64 tlist->endstring = tlist->string + strlen(tlist->string);
65 for (s = str; s < tlist->endstring; s++)
67 #if SPACES_IN_FILENAMES
68 if (meta_quoted)
70 meta_quoted = 0;
71 } else if (esclen > 0 && s + esclen < tlist->endstring &&
72 strncmp(s, esc, esclen) == 0)
74 meta_quoted = 1;
75 s += esclen - 1;
76 } else if (delim_quoted)
78 if (*s == closequote)
79 delim_quoted = 0;
80 } else /* (!delim_quoted) */
82 if (*s == openquote)
83 delim_quoted = 1;
84 else if (*s == ' ')
85 *s = '\0';
87 #else
88 if (*s == ' ')
89 *s = '\0';
90 #endif
94 public char *
95 forw_textlist(tlist, prev)
96 struct textlist *tlist;
97 char *prev;
99 char *s;
102 * prev == NULL means return the first word in the list.
103 * Otherwise, return the word after "prev".
105 if (prev == NULL)
106 s = tlist->string;
107 else
108 s = prev + strlen(prev);
109 if (s >= tlist->endstring)
110 return (NULL);
111 while (*s == '\0')
112 s++;
113 if (s >= tlist->endstring)
114 return (NULL);
115 return (s);
118 public char *
119 back_textlist(tlist, prev)
120 struct textlist *tlist;
121 char *prev;
123 char *s;
126 * prev == NULL means return the last word in the list.
127 * Otherwise, return the word before "prev".
129 if (prev == NULL)
130 s = tlist->endstring;
131 else if (prev <= tlist->string)
132 return (NULL);
133 else
134 s = prev - 1;
135 while (*s == '\0')
136 s--;
137 if (s <= tlist->string)
138 return (NULL);
139 while (s[-1] != '\0' && s > tlist->string)
140 s--;
141 return (s);
145 * Close the current input file.
147 static void
148 close_file()
150 struct scrpos scrpos;
152 if (curr_ifile == NULL_IFILE)
153 return;
156 * Save the current position so that we can return to
157 * the same position if we edit this file again.
159 get_scrpos(&scrpos);
160 if (scrpos.pos != NULL_POSITION)
162 store_pos(curr_ifile, &scrpos);
163 lastmark();
166 * Close the file descriptor, unless it is a pipe.
168 ch_close();
170 * If we opened a file using an alternate name,
171 * do special stuff to close it.
173 if (curr_altfilename != NULL)
175 close_altfile(curr_altfilename, get_filename(curr_ifile),
176 curr_altpipe);
177 free(curr_altfilename);
178 curr_altfilename = NULL;
180 curr_ifile = NULL_IFILE;
184 * Edit a new file (given its name).
185 * Filename == "-" means standard input.
186 * Filename == NULL means just close the current file.
188 public int
189 edit(filename)
190 char *filename;
192 if (filename == NULL)
193 return (edit_ifile(NULL_IFILE));
194 return (edit_ifile(get_ifile(filename, curr_ifile)));
198 * Edit a new file (given its IFILE).
199 * ifile == NULL means just close the current file.
201 public int
202 edit_ifile(ifile)
203 IFILE ifile;
205 int f;
206 int answer;
207 int no_display;
208 int chflags;
209 char *filename;
210 char *open_filename;
211 char *qopen_filename;
212 char *alt_filename;
213 void *alt_pipe;
214 IFILE was_curr_ifile;
215 PARG parg;
217 if (ifile == curr_ifile)
220 * Already have the correct file open.
222 return (0);
226 * We must close the currently open file now.
227 * This is necessary to make the open_altfile/close_altfile pairs
228 * nest properly (or rather to avoid nesting at all).
229 * {{ Some stupid implementations of popen() mess up if you do:
230 * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
232 #if LOGFILE
233 end_logfile();
234 #endif
235 was_curr_ifile = save_curr_ifile();
236 if (curr_ifile != NULL_IFILE)
238 chflags = ch_getflags();
239 close_file();
240 if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
243 * Don't keep the help file in the ifile list.
245 del_ifile(was_curr_ifile);
246 was_curr_ifile = old_ifile;
250 if (ifile == NULL_IFILE)
253 * No new file to open.
254 * (Don't set old_ifile, because if you call edit_ifile(NULL),
255 * you're supposed to have saved curr_ifile yourself,
256 * and you'll restore it if necessary.)
258 unsave_ifile(was_curr_ifile);
259 return (0);
262 filename = save(get_filename(ifile));
264 * See if LESSOPEN specifies an "alternate" file to open.
266 alt_pipe = NULL;
267 alt_filename = open_altfile(filename, &f, &alt_pipe);
268 open_filename = (alt_filename != NULL) ? alt_filename : filename;
269 qopen_filename = shell_unquote(open_filename);
271 chflags = 0;
272 if (alt_pipe != NULL)
275 * The alternate "file" is actually a pipe.
276 * f has already been set to the file descriptor of the pipe
277 * in the call to open_altfile above.
278 * Keep the file descriptor open because it was opened
279 * via popen(), and pclose() wants to close it.
281 chflags |= CH_POPENED;
282 } else if (strcmp(open_filename, "-") == 0)
285 * Use standard input.
286 * Keep the file descriptor open because we can't reopen it.
288 f = fd0;
289 chflags |= CH_KEEPOPEN;
291 * Must switch stdin to BINARY mode.
293 SET_BINARY(f);
294 #if MSDOS_COMPILER==DJGPPC
296 * Setting stdin to binary by default causes
297 * Ctrl-C to not raise SIGINT. We must undo
298 * that side-effect.
300 __djgpp_set_ctrl_c(1);
301 #endif
302 } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
304 f = -1;
305 chflags |= CH_HELPFILE;
306 } else if ((parg.p_string = bad_file(open_filename)) != NULL)
309 * It looks like a bad file. Don't try to open it.
311 error("%s", &parg);
312 free(parg.p_string);
313 err1:
314 if (alt_filename != NULL)
316 close_altfile(alt_filename, filename, alt_pipe);
317 free(alt_filename);
319 del_ifile(ifile);
320 free(qopen_filename);
321 free(filename);
323 * Re-open the current file.
325 if (was_curr_ifile == ifile)
328 * Whoops. The "current" ifile is the one we just deleted.
329 * Just give up.
331 quit(QUIT_ERROR);
333 reedit_ifile(was_curr_ifile);
334 return (1);
335 } else if ((f = open(qopen_filename, OPEN_READ)) < 0)
338 * Got an error trying to open it.
340 parg.p_string = errno_message(filename);
341 error("%s", &parg);
342 free(parg.p_string);
343 goto err1;
344 } else
346 chflags |= CH_CANSEEK;
347 if (!force_open && !opened(ifile) && bin_file(f))
350 * Looks like a binary file.
351 * Ask user if we should proceed.
353 parg.p_string = filename;
354 answer = query("\"%s\" may be a binary file. See it anyway? ",
355 &parg);
356 if (answer != 'y' && answer != 'Y')
358 close(f);
359 goto err1;
363 free(qopen_filename);
366 * Get the new ifile.
367 * Get the saved position for the file.
369 if (was_curr_ifile != NULL_IFILE)
371 old_ifile = was_curr_ifile;
372 unsave_ifile(was_curr_ifile);
374 curr_ifile = ifile;
375 curr_altfilename = alt_filename;
376 curr_altpipe = alt_pipe;
377 set_open(curr_ifile); /* File has been opened */
378 get_pos(curr_ifile, &initial_scrpos);
379 new_file = TRUE;
380 ch_init(f, chflags);
382 if (!(chflags & CH_HELPFILE))
384 #if LOGFILE
385 if (namelogfile != NULL && is_tty)
386 use_logfile(namelogfile);
387 #endif
388 if (every_first_cmd != NULL)
389 ungetsc(every_first_cmd);
392 no_display = !any_display;
393 flush();
394 any_display = TRUE;
396 if (is_tty)
399 * Output is to a real tty.
403 * Indicate there is nothing displayed yet.
405 pos_clear();
406 clr_linenum();
407 #if HILITE_SEARCH
408 clr_hilite();
409 #endif
410 cmd_addhist(ml_examine, filename);
411 if (no_display && errmsgs > 0)
414 * We displayed some messages on error output
415 * (file descriptor 2; see error() function).
416 * Before erasing the screen contents,
417 * display the file name and wait for a keystroke.
419 parg.p_string = filename;
420 error("%s", &parg);
423 free(filename);
424 return (0);
428 * Edit a space-separated list of files.
429 * For each filename in the list, enter it into the ifile list.
430 * Then edit the first one.
432 public int
433 edit_list(filelist)
434 char *filelist;
436 IFILE save_ifile;
437 char *good_filename;
438 char *filename;
439 char *gfilelist;
440 char *gfilename;
441 struct textlist tl_files;
442 struct textlist tl_gfiles;
444 save_ifile = save_curr_ifile();
445 good_filename = NULL;
448 * Run thru each filename in the list.
449 * Try to glob the filename.
450 * If it doesn't expand, just try to open the filename.
451 * If it does expand, try to open each name in that list.
453 init_textlist(&tl_files, filelist);
454 filename = NULL;
455 while ((filename = forw_textlist(&tl_files, filename)) != NULL)
457 gfilelist = lglob(filename);
458 init_textlist(&tl_gfiles, gfilelist);
459 gfilename = NULL;
460 while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
462 if (edit(gfilename) == 0 && good_filename == NULL)
463 good_filename = get_filename(curr_ifile);
465 free(gfilelist);
468 * Edit the first valid filename in the list.
470 if (good_filename == NULL)
472 unsave_ifile(save_ifile);
473 return (1);
475 if (get_ifile(good_filename, curr_ifile) == curr_ifile)
478 * Trying to edit the current file; don't reopen it.
480 unsave_ifile(save_ifile);
481 return (0);
483 reedit_ifile(save_ifile);
484 return (edit(good_filename));
488 * Edit the first file in the command line (ifile) list.
490 public int
491 edit_first()
493 curr_ifile = NULL_IFILE;
494 return (edit_next(1));
498 * Edit the last file in the command line (ifile) list.
500 public int
501 edit_last()
503 curr_ifile = NULL_IFILE;
504 return (edit_prev(1));
509 * Edit the n-th next or previous file in the command line (ifile) list.
511 static int
512 edit_istep(h, n, dir)
513 IFILE h;
514 int n;
515 int dir;
517 IFILE next;
520 * Skip n filenames, then try to edit each filename.
522 for (;;)
524 next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
525 if (--n < 0)
527 if (edit_ifile(h) == 0)
528 break;
530 if (next == NULL_IFILE)
533 * Reached end of the ifile list.
535 return (1);
537 if (ABORT_SIGS())
540 * Interrupt breaks out, if we're in a long
541 * list of files that can't be opened.
543 return (1);
545 h = next;
548 * Found a file that we can edit.
550 return (0);
553 static int
554 edit_inext(h, n)
555 IFILE h;
556 int n;
558 return (edit_istep(h, n, +1));
561 public int
562 edit_next(n)
563 int n;
565 return edit_istep(curr_ifile, n, +1);
568 static int
569 edit_iprev(h, n)
570 IFILE h;
571 int n;
573 return (edit_istep(h, n, -1));
576 public int
577 edit_prev(n)
578 int n;
580 return edit_istep(curr_ifile, n, -1);
584 * Edit a specific file in the command line (ifile) list.
586 public int
587 edit_index(n)
588 int n;
590 IFILE h;
592 h = NULL_IFILE;
595 if ((h = next_ifile(h)) == NULL_IFILE)
598 * Reached end of the list without finding it.
600 return (1);
602 } while (get_index(h) != n);
604 return (edit_ifile(h));
607 public IFILE
608 save_curr_ifile()
610 if (curr_ifile != NULL_IFILE)
611 hold_ifile(curr_ifile, 1);
612 return (curr_ifile);
615 public void
616 unsave_ifile(save_ifile)
617 IFILE save_ifile;
619 if (save_ifile != NULL_IFILE)
620 hold_ifile(save_ifile, -1);
624 * Reedit the ifile which was previously open.
626 public void
627 reedit_ifile(save_ifile)
628 IFILE save_ifile;
630 IFILE next;
631 IFILE prev;
634 * Try to reopen the ifile.
635 * Note that opening it may fail (maybe the file was removed),
636 * in which case the ifile will be deleted from the list.
637 * So save the next and prev ifiles first.
639 unsave_ifile(save_ifile);
640 next = next_ifile(save_ifile);
641 prev = prev_ifile(save_ifile);
642 if (edit_ifile(save_ifile) == 0)
643 return;
645 * If can't reopen it, open the next input file in the list.
647 if (next != NULL_IFILE && edit_inext(next, 0) == 0)
648 return;
650 * If can't open THAT one, open the previous input file in the list.
652 if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
653 return;
655 * If can't even open that, we're stuck. Just quit.
657 quit(QUIT_ERROR);
661 * Edit standard input.
663 public int
664 edit_stdin()
666 if (isatty(fd0))
668 error("Missing filename (\"less --help\" for help)", NULL_PARG);
669 quit(QUIT_OK);
671 return (edit("-"));
675 * Copy a file directly to standard output.
676 * Used if standard output is not a tty.
678 public void
679 cat_file()
681 register int c;
683 while ((c = ch_forw_get()) != EOI)
684 putchr(c);
685 flush();
688 #if LOGFILE
691 * If the user asked for a log file and our input file
692 * is standard input, create the log file.
693 * We take care not to blindly overwrite an existing file.
695 public void
696 use_logfile(filename)
697 char *filename;
699 register int exists;
700 register int answer;
701 PARG parg;
703 if (ch_getflags() & CH_CANSEEK)
705 * Can't currently use a log file on a file that can seek.
707 return;
710 * {{ We could use access() here. }}
712 filename = shell_unquote(filename);
713 exists = open(filename, OPEN_READ);
714 close(exists);
715 exists = (exists >= 0);
718 * Decide whether to overwrite the log file or append to it.
719 * If it doesn't exist we "overwrite" it.
721 if (!exists || force_logfile)
724 * Overwrite (or create) the log file.
726 answer = 'O';
727 } else
730 * Ask user what to do.
732 parg.p_string = filename;
733 answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
736 loop:
737 switch (answer)
739 case 'O': case 'o':
741 * Overwrite: create the file.
743 logfile = creat(filename, 0644);
744 break;
745 case 'A': case 'a':
747 * Append: open the file and seek to the end.
749 logfile = open(filename, OPEN_APPEND);
750 if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
752 close(logfile);
753 logfile = -1;
755 break;
756 case 'D': case 'd':
758 * Don't do anything.
760 free(filename);
761 return;
762 case 'q':
763 quit(QUIT_OK);
764 /*NOTREACHED*/
765 default:
767 * Eh?
769 answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
770 goto loop;
773 if (logfile < 0)
776 * Error in opening logfile.
778 parg.p_string = filename;
779 error("Cannot write to \"%s\"", &parg);
780 free(filename);
781 return;
783 free(filename);
784 SET_BINARY(logfile);
787 #endif