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