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.
13 * Routines to mess around with filenames (and files).
14 * Much of this is very OS dependent.
16 * Modified for illumos/POSIX -- it uses native glob(3C) rather than
17 * popen to a shell to perform the expansion.
27 extern int force_open
;
29 extern int use_lessopen
;
32 extern IFILE curr_ifile
;
33 extern IFILE old_ifile
;
34 extern char openquote
;
35 extern char closequote
;
38 * Remove quotes around a filename.
41 shell_unquote(char *str
)
46 name
= p
= ecalloc(strlen(str
)+1, sizeof (char));
47 if (*str
== openquote
) {
49 while (*str
!= '\0') {
50 if (*str
== closequote
) {
51 if (str
[1] != closequote
)
58 char *esc
= get_meta_escape();
59 int esclen
= strlen(esc
);
60 while (*str
!= '\0') {
61 if (esclen
> 0 && strncmp(str
, esc
, esclen
) == 0)
71 * Get the shell's escape character.
78 s
= lgetenv("LESSMETAESCAPE");
85 * Get the characters which the shell considers to be "metacharacters".
90 static char *mchars
= NULL
;
93 mchars
= lgetenv("LESSMETACHARS");
95 mchars
= DEF_METACHARS
;
101 * Is this a shell metacharacter?
106 return (strchr(metachars(), c
) != NULL
);
110 * Insert a backslash before each metacharacter in a string.
113 shell_quote(const char *s
)
119 char *esc
= get_meta_escape();
120 int esclen
= strlen(esc
);
125 * Determine how big a string we need to allocate.
127 len
= 1; /* Trailing null byte */
128 for (p
= s
; *p
!= '\0'; p
++) {
130 if (*p
== openquote
|| *p
== closequote
)
135 * We've got a metachar, but this shell
136 * doesn't support escape chars. Use quotes.
141 * Allow space for the escape char.
148 * Allocate and construct the new string.
151 /* We can't quote a string that contains quotes. */
154 newstr
= easprintf("%c%s%c", openquote
, s
, closequote
);
156 newstr
= r
= ecalloc(len
, sizeof (char));
160 * Add the escape char.
162 (void) strlcpy(r
, esc
, newstr
+ len
- p
);
173 * Return a pathname that points to a specified file in a specified directory.
174 * Return NULL if the file does not exist in the directory.
177 dirfile(const char *dirname
, const char *filename
)
183 if (dirname
== NULL
|| *dirname
== '\0')
186 * Construct the full pathname.
188 pathname
= easprintf("%s/%s", dirname
, filename
);
190 * Make sure the file exists.
192 qpathname
= shell_unquote(pathname
);
193 f
= open(qpathname
, O_RDONLY
);
205 * Return the full pathname of the given file in the "home directory".
208 homefile(char *filename
)
210 return (dirfile(lgetenv("HOME"), filename
));
214 * Expand a string, substituting any "%" with the current filename,
215 * and any "#" with the previous filename.
216 * But a string of N "%"s is just replaced with N-1 "%"s.
217 * Likewise for a string of N "#"s.
218 * {{ This is a lot of work just to support % and #. }}
228 #define fchar_ifile(c) \
229 ((c) == '%' ? curr_ifile : (c) == '#' ? old_ifile : NULL)
232 * Make one pass to see how big a buffer we
233 * need to allocate for the expanded string.
236 for (fr
= s
; *fr
!= '\0'; fr
++) {
240 if (fr
> s
&& fr
[-1] == *fr
) {
242 * Second (or later) char in a string
243 * of identical chars. Treat as normal.
246 } else if (fr
[1] != *fr
) {
248 * Single char (not repeated). Treat specially.
250 ifile
= fchar_ifile(*fr
);
254 n
+= strlen(get_filename(ifile
));
257 * Else it is the first char in a string of
258 * identical chars. Just discard it.
267 e
= ecalloc(n
+1, sizeof (char));
270 * Now copy the string, expanding any "%" or "#".
273 for (fr
= s
; *fr
!= '\0'; fr
++) {
277 if (fr
> s
&& fr
[-1] == *fr
) {
279 } else if (fr
[1] != *fr
) {
280 ifile
= fchar_ifile(*fr
);
284 (void) strlcpy(to
, get_filename(ifile
),
300 * Return a blank-separated list of filenames which "complete"
312 * Complete the filename "s" by globbing "s*".
314 fpat
= easprintf("%s*", s
);
317 s
= shell_unquote(qs
);
318 if (strcmp(s
, fpat
) == 0) {
320 * The filename didn't expand.
331 * Try to determine if a file is "binary".
332 * This is just a guess, and we need not try too hard to make it accurate.
345 if (lseek(f
, (off_t
)0, SEEK_SET
) == (off_t
)-1)
347 n
= read(f
, data
, sizeof (data
));
349 for (p
= data
; p
< pend
; ) {
350 LWCHAR c
= step_char(&p
, +1, pend
);
351 if (ctldisp
== OPT_ONPLUS
&& IS_CSI_START(c
)) {
353 c
= step_char(&p
, +1, pend
);
354 } while (p
< pend
&& is_ansi_middle(c
));
355 } else if (binary_char(c
))
359 * Call it a binary file if there are more than 5 binary characters
360 * in the first 256 bytes of the file.
362 return (bin_count
> 5);
366 * Read a string from a file.
367 * Return a pointer to the string in memory.
378 * Make a guess about how many chars in the string
379 * and allocate a buffer to hold it.
382 buf
= ecalloc(len
, sizeof (char));
383 for (p
= buf
; ; p
++) {
384 if ((ch
= getc(fd
)) == '\n' || ch
== EOF
)
386 if (p
>= buf
+ len
-1) {
388 * The string is too big to fit in the buffer we have.
389 * Allocate a new buffer, twice as big.
393 p
= ecalloc(len
, sizeof (char));
394 strlcpy(p
, buf
, len
);
397 p
= buf
+ strlen(buf
);
406 * Execute a shell command.
407 * Return a pointer to a pipe connected to the shell command's standard output.
416 shell
= lgetenv("SHELL");
417 if (shell
!= NULL
&& *shell
!= '\0') {
422 * Read the output of <$SHELL -c cmd>.
423 * Escape any metacharacters in the command.
425 esccmd
= shell_quote(cmd
);
426 if (esccmd
== NULL
) {
427 fd
= popen(cmd
, "r");
429 scmd
= easprintf("%s -c %s", shell
, esccmd
);
431 fd
= popen(scmd
, "r");
435 fd
= popen(cmd
, "r");
438 * Redirection in `popen' might have messed with the
439 * standard devices. Restore binary input mode.
445 * Expand a filename, doing any system-specific metacharacter substitutions.
448 lglob(char *filename
)
458 ofilename
= fexpand(filename
);
461 filename
= shell_unquote(ofilename
);
464 * The globbing function returns a list of names.
473 if (glob(filename
, GLOB_TILDE
| GLOB_LIMIT
, NULL
, &list
) != 0) {
477 length
= 1; /* Room for trailing null byte */
478 for (i
= 0; i
< list
.gl_pathc
; i
++) {
479 p
= list
.gl_pathv
[i
];
480 qfilename
= shell_quote(p
);
481 if (qfilename
!= NULL
) {
482 length
+= strlen(qfilename
) + 1;
486 gfilename
= ecalloc(length
, sizeof (char));
487 for (i
= 0; i
< list
.gl_pathc
; i
++) {
488 p
= list
.gl_pathv
[i
];
489 qfilename
= shell_quote(p
);
490 if (qfilename
!= NULL
) {
492 (void) strlcat(gfilename
, " ", length
);
494 (void) strlcat(gfilename
, qfilename
, length
);
505 * Expand LESSOPEN or LESSCLOSE. Returns a newly allocated string
506 * on success, NULL otherwise.
509 expand_pct_s(const char *fmt
, ...)
514 const char *f
[3]; /* max expansions + 1 for NULL */
518 for (n
= 0; n
< ((sizeof (f
)/sizeof (f
[0])) - 1); n
++) {
519 f
[n
] = (const char *)va_arg(ap
, const char *);
525 f
[n
] = NULL
; /* terminate list */
527 len
= strlen(fmt
) + 1;
528 for (n
= 0; f
[n
] != NULL
; n
++) {
529 len
+= strlen(f
[n
]); /* technically could -2 for "%s" */
531 r
= ecalloc(len
, sizeof (char));
533 for (n
= 0, d
= r
; *fmt
!= 0; ) {
539 /* Permit embedded "%%" */
551 (void) strlcpy(d
, f
[n
++], r
+ len
- d
);
566 * See if we should open a "replacement file"
567 * instead of the file we're about to open.
570 open_altfile(char *filename
, int *pf
, void **pfd
)
577 if (!use_lessopen
|| secure
)
580 if ((lessopen
= lgetenv("LESSOPEN")) == NULL
)
582 while (*lessopen
== '|') {
584 * If LESSOPEN starts with a |, it indicates
585 * a "pipe preprocessor".
590 if (*lessopen
== '-') {
592 * Lessopen preprocessor will accept "-" as a filename.
596 if (strcmp(filename
, "-") == 0)
600 if ((cmd
= expand_pct_s(lessopen
, filename
, NULL
)) == NULL
) {
601 error("Invalid LESSOPEN variable", NULL
);
608 * Cannot create the pipe.
617 * Read one char to see if the pipe will produce any data.
618 * If it does, push the char back on the pipe.
621 if (read(f
, &c
, 1) != 1) {
624 * If more than 1 pipe char was specified,
625 * the exit status tells whether the file itself
626 * is empty, or if there is no alt file.
627 * If only one pipe char, just assume no alt file.
629 int status
= pclose(fd
);
630 if (returnfd
> 1 && status
== 0) {
633 return (estrdup(FAKE_EMPTYFILE
));
640 return (estrdup("-"));
646 * Pipe is empty. This means there is no alt file.
653 * Close a replacement file.
656 close_altfile(char *altfilename
, char *filename
, void *pipefd
)
664 if (pipefd
!= NULL
) {
665 pclose((FILE *)pipefd
);
667 if ((lessclose
= lgetenv("LESSCLOSE")) == NULL
)
669 cmd
= expand_pct_s(lessclose
, filename
, altfilename
, NULL
);
671 error("Invalid LESSCLOSE variable", NULL
);
681 * Is the specified file a directory?
684 is_dir(char *filename
)
690 filename
= shell_unquote(filename
);
692 r
= stat(filename
, &statbuf
);
693 isdir
= (r
>= 0 && S_ISDIR(statbuf
.st_mode
));
699 * Returns NULL if the file can be opened and
700 * is an ordinary file, otherwise an error message
701 * (if it cannot be opened or is a directory, etc.)
704 bad_file(char *filename
)
708 filename
= shell_unquote(filename
);
709 if (!force_open
&& is_dir(filename
)) {
710 m
= easprintf("%s is a directory", filename
);
715 r
= stat(filename
, &statbuf
);
717 m
= errno_message(filename
);
718 } else if (force_open
) {
720 } else if (!S_ISREG(statbuf
.st_mode
)) {
721 m
= easprintf("%s is not a regular file (use -f to "
722 "see it)", filename
);
730 * Return the size of a file, as cheaply as possible.
737 if (fstat(f
, &statbuf
) >= 0)
738 return (statbuf
.st_size
);
743 * Return last component of a pathname.
746 last_component(char *name
)
750 for (slash
= name
+ strlen(name
); slash
> name
; ) {