2 * Copyright (C) 1984-2012 Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
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.
20 extern char *every_first_cmd
;
21 extern int any_display
;
22 extern int force_open
;
24 extern volatile sig_atomic_t sigs
;
25 extern IFILE curr_ifile
;
26 extern IFILE old_ifile
;
27 extern struct scrpos initial_scrpos
;
28 extern void *ml_examine
;
29 extern char openquote
;
30 extern char closequote
;
31 extern int less_is_more
;
33 extern int force_logfile
;
34 extern char *namelogfile
;
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.
51 init_textlist(struct textlist
*tlist
, char *str
)
56 char *esc
= get_meta_escape();
57 int esclen
= strlen(esc
);
59 tlist
->string
= skipsp(str
);
60 tlist
->endstring
= tlist
->string
+ strlen(tlist
->string
);
61 for (s
= str
; s
< tlist
->endstring
; s
++) {
64 } else if (esclen
> 0 && s
+ esclen
< tlist
->endstring
&&
65 strncmp(s
, esc
, esclen
) == 0) {
68 } else if (delim_quoted
) {
71 } else /* (!delim_quoted) */ {
81 forw_textlist(struct textlist
*tlist
, char *prev
)
86 * prev == NULL means return the first word in the list.
87 * Otherwise, return the word after "prev".
92 s
= prev
+ strlen(prev
);
93 if (s
>= tlist
->endstring
)
97 if (s
>= tlist
->endstring
)
103 back_textlist(struct textlist
*tlist
, char *prev
)
108 * prev == NULL means return the last word in the list.
109 * Otherwise, return the word before "prev".
112 s
= tlist
->endstring
;
113 else if (prev
<= tlist
->string
)
119 if (s
<= tlist
->string
)
121 while (s
[-1] != '\0' && s
> tlist
->string
)
127 * Close the current input file.
132 struct scrpos scrpos
;
134 if (curr_ifile
== NULL
)
138 * Save the current position so that we can return to
139 * the same position if we edit this file again.
142 if (scrpos
.pos
!= -1) {
143 store_pos(curr_ifile
, &scrpos
);
147 * Close the file descriptor, unless it is a pipe.
151 * If we opened a file using an alternate name,
152 * do special stuff to close it.
154 if (curr_altfilename
!= NULL
) {
155 close_altfile(curr_altfilename
, get_filename(curr_ifile
),
157 free(curr_altfilename
);
158 curr_altfilename
= NULL
;
161 curr_ino
= curr_dev
= 0;
165 * Edit a new file (given its name).
166 * Filename == "-" means standard input.
167 * Filename == NULL means just close the current file.
172 if (filename
== NULL
)
173 return (edit_ifile(NULL
));
174 return (edit_ifile(get_ifile(filename
, curr_ifile
)));
178 * Edit a new file (given its IFILE).
179 * ifile == NULL means just close the current file.
182 edit_ifile(IFILE ifile
)
190 char *qopen_filename
;
193 IFILE was_curr_ifile
;
196 if (ifile
== curr_ifile
) {
198 * Already have the correct file open.
204 * We must close the currently open file now.
205 * This is necessary to make the open_altfile/close_altfile pairs
206 * nest properly (or rather to avoid nesting at all).
207 * {{ Some stupid implementations of popen() mess up if you do:
208 * fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
211 was_curr_ifile
= save_curr_ifile();
212 if (curr_ifile
!= NULL
) {
213 chflags
= ch_getflags();
215 if ((chflags
& CH_HELPFILE
) &&
216 held_ifile(was_curr_ifile
) <= 1) {
218 * Don't keep the help file in the ifile list.
220 del_ifile(was_curr_ifile
);
221 was_curr_ifile
= old_ifile
;
227 * No new file to open.
228 * (Don't set old_ifile, because if you call edit_ifile(NULL),
229 * you're supposed to have saved curr_ifile yourself,
230 * and you'll restore it if necessary.)
232 unsave_ifile(was_curr_ifile
);
236 filename
= estrdup(get_filename(ifile
));
238 * See if LESSOPEN specifies an "alternate" file to open.
241 alt_filename
= open_altfile(filename
, &f
, &alt_pipe
);
242 open_filename
= (alt_filename
!= NULL
) ? alt_filename
: filename
;
243 qopen_filename
= shell_unquote(open_filename
);
246 if (strcmp(open_filename
, helpfile()) == 0)
247 chflags
|= CH_HELPFILE
;
248 if (alt_pipe
!= NULL
) {
250 * The alternate "file" is actually a pipe.
251 * f has already been set to the file descriptor of the pipe
252 * in the call to open_altfile above.
253 * Keep the file descriptor open because it was opened
254 * via popen(), and pclose() wants to close it.
256 chflags
|= CH_POPENED
;
257 } else if (strcmp(open_filename
, "-") == 0) {
259 * Use standard input.
260 * Keep the file descriptor open because we can't reopen it.
263 chflags
|= CH_KEEPOPEN
;
264 } else if (strcmp(open_filename
, FAKE_EMPTYFILE
) == 0) {
266 chflags
|= CH_NODATA
;
267 } else if ((parg
.p_string
= bad_file(open_filename
)) != NULL
) {
269 * It looks like a bad file. Don't try to open it.
274 if (alt_filename
!= NULL
) {
275 close_altfile(alt_filename
, filename
, alt_pipe
);
279 free(qopen_filename
);
282 * Re-open the current file.
284 if (was_curr_ifile
== ifile
) {
286 * Whoops. The "current" ifile is the one we just
287 * deleted. Just give up.
291 reedit_ifile(was_curr_ifile
);
293 } else if ((f
= open(qopen_filename
, O_RDONLY
)) < 0) {
295 * Got an error trying to open it.
297 parg
.p_string
= errno_message(filename
);
302 chflags
|= CH_CANSEEK
;
303 if (!force_open
&& !opened(ifile
) && bin_file(f
)) {
305 * Looks like a binary file.
306 * Ask user if we should proceed.
308 parg
.p_string
= filename
;
309 answer
= query("\"%s\" may be a binary file. "
310 "See it anyway? ", &parg
);
311 if (answer
!= 'y' && answer
!= 'Y') {
320 * Get the saved position for the file.
322 if (was_curr_ifile
!= NULL
) {
323 old_ifile
= was_curr_ifile
;
324 unsave_ifile(was_curr_ifile
);
327 curr_altfilename
= alt_filename
;
328 curr_altpipe
= alt_pipe
;
329 set_open(curr_ifile
); /* File has been opened */
330 get_pos(curr_ifile
, &initial_scrpos
);
334 if (!(chflags
& CH_HELPFILE
)) {
338 if (namelogfile
!= NULL
&& is_tty
)
339 use_logfile(namelogfile
);
340 /* Remember the i-number and device of opened file. */
341 r
= stat(qopen_filename
, &statbuf
);
343 curr_ino
= statbuf
.st_ino
;
344 curr_dev
= statbuf
.st_dev
;
346 if (every_first_cmd
!= NULL
)
347 ungetsc(every_first_cmd
);
349 free(qopen_filename
);
350 no_display
= !any_display
;
356 * Output is to a real tty.
360 * Indicate there is nothing displayed yet.
365 cmd_addhist(ml_examine
, filename
);
366 if (no_display
&& errmsgs
> 0) {
368 * We displayed some messages on error output
369 * (file descriptor 2; see error() function).
370 * Before erasing the screen contents,
371 * display the file name and wait for a keystroke.
373 parg
.p_string
= filename
;
382 * Edit a space-separated list of files.
383 * For each filename in the list, enter it into the ifile list.
384 * Then edit the first one.
387 edit_list(char *filelist
)
394 struct textlist tl_files
;
395 struct textlist tl_gfiles
;
397 save_ifile
= save_curr_ifile();
398 good_filename
= NULL
;
401 * Run thru each filename in the list.
402 * Try to glob the filename.
403 * If it doesn't expand, just try to open the filename.
404 * If it does expand, try to open each name in that list.
406 init_textlist(&tl_files
, filelist
);
408 while ((filename
= forw_textlist(&tl_files
, filename
)) != NULL
) {
409 gfilelist
= lglob(filename
);
410 init_textlist(&tl_gfiles
, gfilelist
);
412 while ((gfilename
= forw_textlist(&tl_gfiles
, gfilename
)) !=
414 if (edit(gfilename
) == 0 && good_filename
== NULL
)
415 good_filename
= get_filename(curr_ifile
);
420 * Edit the first valid filename in the list.
422 if (good_filename
== NULL
) {
423 unsave_ifile(save_ifile
);
426 if (get_ifile(good_filename
, curr_ifile
) == curr_ifile
) {
428 * Trying to edit the current file; don't reopen it.
430 unsave_ifile(save_ifile
);
433 reedit_ifile(save_ifile
);
434 return (edit(good_filename
));
438 * Edit the first file in the command line (ifile) list.
444 return (edit_next(1));
448 * Edit the last file in the command line (ifile) list.
454 return (edit_prev(1));
459 * Edit the n-th next or previous file in the command line (ifile) list.
462 edit_istep(IFILE h
, int n
, int dir
)
467 * Skip n filenames, then try to edit each filename.
470 next
= (dir
> 0) ? next_ifile(h
) : prev_ifile(h
);
472 if (edit_ifile(h
) == 0)
477 * Reached end of the ifile list.
483 * Interrupt breaks out, if we're in a long
484 * list of files that can't be opened.
491 * Found a file that we can edit.
497 edit_inext(IFILE h
, int n
)
499 return (edit_istep(h
, n
, +1));
505 return (edit_istep(curr_ifile
, n
, +1));
509 edit_iprev(IFILE h
, int n
)
511 return (edit_istep(h
, n
, -1));
517 return (edit_istep(curr_ifile
, n
, -1));
521 * Edit a specific file in the command line (ifile) list.
530 if ((h
= next_ifile(h
)) == NULL
) {
532 * Reached end of the list without finding it.
536 } while (get_index(h
) != n
);
538 return (edit_ifile(h
));
542 save_curr_ifile(void)
544 if (curr_ifile
!= NULL
)
545 hold_ifile(curr_ifile
, 1);
550 unsave_ifile(IFILE save_ifile
)
552 if (save_ifile
!= NULL
)
553 hold_ifile(save_ifile
, -1);
557 * Reedit the ifile which was previously open.
560 reedit_ifile(IFILE save_ifile
)
566 * Try to reopen the ifile.
567 * Note that opening it may fail (maybe the file was removed),
568 * in which case the ifile will be deleted from the list.
569 * So save the next and prev ifiles first.
571 unsave_ifile(save_ifile
);
572 next
= next_ifile(save_ifile
);
573 prev
= prev_ifile(save_ifile
);
574 if (edit_ifile(save_ifile
) == 0)
577 * If can't reopen it, open the next input file in the list.
579 if (next
!= NULL
&& edit_inext(next
, 0) == 0)
582 * If can't open THAT one, open the previous input file in the list.
584 if (prev
!= NULL
&& edit_iprev(prev
, 0) == 0)
587 * If can't even open that, we're stuck. Just quit.
593 reopen_curr_ifile(void)
595 IFILE save_ifile
= save_curr_ifile();
597 reedit_ifile(save_ifile
);
601 * Edit standard input.
608 error("Missing filename (\"more -h\" for help)",
611 error("Missing filename (\"less --help\" for help)",
620 * Copy a file directly to standard output.
621 * Used if standard output is not a tty.
628 while ((c
= ch_forw_get()) != EOI
)
634 * If the user asked for a log file and our input file
635 * is standard input, create the log file.
636 * We take care not to blindly overwrite an existing file.
639 use_logfile(char *filename
)
645 if (ch_getflags() & CH_CANSEEK
)
647 * Can't currently use a log file on a file that can seek.
652 * {{ We could use access() here. }}
654 filename
= shell_unquote(filename
);
655 exists
= open(filename
, O_RDONLY
);
657 exists
= (exists
>= 0);
660 * Decide whether to overwrite the log file or append to it.
661 * If it doesn't exist we "overwrite" it.
663 if (!exists
|| force_logfile
) {
665 * Overwrite (or create) the log file.
670 * Ask user what to do.
672 parg
.p_string
= filename
;
673 answer
= query("Warning: \"%s\" exists; "
674 "Overwrite, Append or Don't log? ", &parg
);
681 * Overwrite: create the file.
683 logfile
= open(filename
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
687 * Append: open the file and seek to the end.
689 logfile
= open(filename
, O_WRONLY
| O_APPEND
);
690 if (lseek(logfile
, (off_t
)0, SEEK_END
) == (off_t
)-1) {
707 answer
= query("Overwrite, Append, or Don't log? "
708 "(Type \"O\", \"A\", \"D\" or \"q\") ", NULL
);
714 * Error in opening logfile.
716 parg
.p_string
= filename
;
717 error("Cannot write to \"%s\"", &parg
);