| 1 | /* `dir', `vdir' and `ls' directory listing programs for GNU. |
| 2 | |
| 3 | Modified by Chet Ramey for Readline. |
| 4 | |
| 5 | Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012, 2015, 2017 |
| 6 | Free Software Foundation, Inc. |
| 7 | |
| 8 | This program is free software: you can redistribute it and/or modify |
| 9 | it under the terms of the GNU General Public License as published by |
| 10 | the Free Software Foundation, either version 3 of the License, or |
| 11 | (at your option) any later version. |
| 12 | |
| 13 | This program is distributed in the hope that it will be useful, |
| 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | GNU General Public License for more details. |
| 17 | |
| 18 | You should have received a copy of the GNU General Public License |
| 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| 20 | |
| 21 | /* Written by Richard Stallman and David MacKenzie. */ |
| 22 | |
| 23 | /* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis |
| 24 | Flaherty <dennisf@denix.elk.miles.com> based on original patches by |
| 25 | Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */ |
| 26 | |
| 27 | #define READLINE_LIBRARY |
| 28 | |
| 29 | #if defined (HAVE_CONFIG_H) |
| 30 | # include <config.h> |
| 31 | #endif |
| 32 | |
| 33 | #include "rlconf.h" |
| 34 | |
| 35 | #include <stdio.h> |
| 36 | |
| 37 | #include "posixstat.h" // stat related macros (S_ISREG, ...) |
| 38 | #include <fcntl.h> // S_ISUID |
| 39 | |
| 40 | #ifndef S_ISDIR |
| 41 | # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) |
| 42 | #endif |
| 43 | |
| 44 | // strlen() |
| 45 | #if defined (HAVE_STRING_H) |
| 46 | # include <string.h> |
| 47 | #else /* !HAVE_STRING_H */ |
| 48 | # include <strings.h> |
| 49 | #endif /* !HAVE_STRING_H */ |
| 50 | |
| 51 | // abort() |
| 52 | #if defined (HAVE_STDLIB_H) |
| 53 | # include <stdlib.h> |
| 54 | #else |
| 55 | # include "ansi_stdlib.h" |
| 56 | #endif /* HAVE_STDLIB_H */ |
| 57 | |
| 58 | #include "readline.h" |
| 59 | #include "rldefs.h" |
| 60 | |
| 61 | #ifdef COLOR_SUPPORT |
| 62 | |
| 63 | #include "xmalloc.h" |
| 64 | #include "colors.h" |
| 65 | |
| 66 | static bool is_colored (enum indicator_no type); |
| 67 | static void restore_default_color (void); |
| 68 | |
| 69 | COLOR_EXT_TYPE *_rl_color_ext_list = 0; |
| 70 | |
| 71 | /* Output a color indicator (which may contain nulls). */ |
| 72 | void |
| 73 | _rl_put_indicator (const struct bin_str *ind) |
| 74 | { |
| 75 | fwrite (ind->string, ind->len, 1, rl_outstream); |
| 76 | } |
| 77 | |
| 78 | static bool |
| 79 | is_colored (enum indicator_no colored_filetype) |
| 80 | { |
| 81 | size_t len = _rl_color_indicator[colored_filetype].len; |
| 82 | char const *s = _rl_color_indicator[colored_filetype].string; |
| 83 | return ! (len == 0 |
| 84 | || (len == 1 && strncmp (s, "0", 1) == 0) |
| 85 | || (len == 2 && strncmp (s, "00", 2) == 0)); |
| 86 | } |
| 87 | |
| 88 | static void |
| 89 | restore_default_color (void) |
| 90 | { |
| 91 | _rl_put_indicator (&_rl_color_indicator[C_LEFT]); |
| 92 | _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); |
| 93 | } |
| 94 | |
| 95 | void |
| 96 | _rl_set_normal_color (void) |
| 97 | { |
| 98 | if (is_colored (C_NORM)) |
| 99 | { |
| 100 | _rl_put_indicator (&_rl_color_indicator[C_LEFT]); |
| 101 | _rl_put_indicator (&_rl_color_indicator[C_NORM]); |
| 102 | _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | bool |
| 107 | _rl_print_prefix_color (void) |
| 108 | { |
| 109 | struct bin_str *s; |
| 110 | |
| 111 | /* What do we want to use for the prefix? Let's try cyan first, see colors.h */ |
| 112 | s = &_rl_color_indicator[C_PREFIX]; |
| 113 | if (s->string != NULL) |
| 114 | { |
| 115 | if (is_colored (C_NORM)) |
| 116 | restore_default_color (); |
| 117 | _rl_put_indicator (&_rl_color_indicator[C_LEFT]); |
| 118 | _rl_put_indicator (s); |
| 119 | _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); |
| 120 | return 0; |
| 121 | } |
| 122 | else |
| 123 | return 1; |
| 124 | } |
| 125 | |
| 126 | /* Returns whether any color sequence was printed. */ |
| 127 | bool |
| 128 | _rl_print_color_indicator (const char *f) |
| 129 | { |
| 130 | enum indicator_no colored_filetype; |
| 131 | COLOR_EXT_TYPE *ext; /* Color extension */ |
| 132 | size_t len; /* Length of name */ |
| 133 | |
| 134 | const char* name; |
| 135 | char *filename; |
| 136 | struct stat astat, linkstat; |
| 137 | mode_t mode; |
| 138 | int linkok; /* 1 == ok, 0 == dangling symlink, -1 == missing */ |
| 139 | int stat_ok; |
| 140 | |
| 141 | name = f; |
| 142 | |
| 143 | /* This should already have undergone tilde expansion */ |
| 144 | filename = 0; |
| 145 | if (rl_filename_stat_hook) |
| 146 | { |
| 147 | filename = savestring (f); |
| 148 | (*rl_filename_stat_hook) (&filename); |
| 149 | name = filename; |
| 150 | } |
| 151 | |
| 152 | #if defined (HAVE_LSTAT) |
| 153 | stat_ok = lstat(name, &astat); |
| 154 | #else |
| 155 | stat_ok = stat(name, &astat); |
| 156 | #endif |
| 157 | if (stat_ok == 0) |
| 158 | { |
| 159 | mode = astat.st_mode; |
| 160 | #if defined (HAVE_LSTAT) |
| 161 | if (S_ISLNK (mode)) |
| 162 | { |
| 163 | linkok = stat (name, &linkstat) == 0; |
| 164 | if (linkok && strncmp (_rl_color_indicator[C_LINK].string, "target", 6) == 0) |
| 165 | mode = linkstat.st_mode; |
| 166 | } |
| 167 | else |
| 168 | #endif |
| 169 | linkok = 1; |
| 170 | } |
| 171 | else |
| 172 | linkok = -1; |
| 173 | |
| 174 | /* Is this a nonexistent file? If so, linkok == -1. */ |
| 175 | |
| 176 | if (linkok == -1 && _rl_color_indicator[C_MISSING].string != NULL) |
| 177 | colored_filetype = C_MISSING; |
| 178 | else if (linkok == 0 && _rl_color_indicator[C_ORPHAN].string != NULL) |
| 179 | colored_filetype = C_ORPHAN; /* dangling symlink */ |
| 180 | else if(stat_ok != 0) |
| 181 | { |
| 182 | static enum indicator_no filetype_indicator[] = FILETYPE_INDICATORS; |
| 183 | colored_filetype = filetype_indicator[normal]; //f->filetype]; |
| 184 | } |
| 185 | else |
| 186 | { |
| 187 | if (S_ISREG (mode)) |
| 188 | { |
| 189 | colored_filetype = C_FILE; |
| 190 | |
| 191 | #if defined (S_ISUID) |
| 192 | if ((mode & S_ISUID) != 0 && is_colored (C_SETUID)) |
| 193 | colored_filetype = C_SETUID; |
| 194 | else |
| 195 | #endif |
| 196 | #if defined (S_ISGID) |
| 197 | if ((mode & S_ISGID) != 0 && is_colored (C_SETGID)) |
| 198 | colored_filetype = C_SETGID; |
| 199 | else |
| 200 | #endif |
| 201 | if (is_colored (C_CAP) && 0) //f->has_capability) |
| 202 | colored_filetype = C_CAP; |
| 203 | else if ((mode & S_IXUGO) != 0 && is_colored (C_EXEC)) |
| 204 | colored_filetype = C_EXEC; |
| 205 | else if ((1 < astat.st_nlink) && is_colored (C_MULTIHARDLINK)) |
| 206 | colored_filetype = C_MULTIHARDLINK; |
| 207 | } |
| 208 | else if (S_ISDIR (mode)) |
| 209 | { |
| 210 | colored_filetype = C_DIR; |
| 211 | |
| 212 | #if defined (S_ISVTX) |
| 213 | if ((mode & S_ISVTX) && (mode & S_IWOTH) |
| 214 | && is_colored (C_STICKY_OTHER_WRITABLE)) |
| 215 | colored_filetype = C_STICKY_OTHER_WRITABLE; |
| 216 | else |
| 217 | #endif |
| 218 | if ((mode & S_IWOTH) != 0 && is_colored (C_OTHER_WRITABLE)) |
| 219 | colored_filetype = C_OTHER_WRITABLE; |
| 220 | #if defined (S_ISVTX) |
| 221 | else if ((mode & S_ISVTX) != 0 && is_colored (C_STICKY)) |
| 222 | colored_filetype = C_STICKY; |
| 223 | #endif |
| 224 | } |
| 225 | #if defined (S_ISLNK) |
| 226 | else if (S_ISLNK (mode)) |
| 227 | colored_filetype = C_LINK; |
| 228 | #endif |
| 229 | else if (S_ISFIFO (mode)) |
| 230 | colored_filetype = C_FIFO; |
| 231 | #if defined (S_ISSOCK) |
| 232 | else if (S_ISSOCK (mode)) |
| 233 | colored_filetype = C_SOCK; |
| 234 | #endif |
| 235 | else if (S_ISBLK (mode)) |
| 236 | colored_filetype = C_BLK; |
| 237 | else if (S_ISCHR (mode)) |
| 238 | colored_filetype = C_CHR; |
| 239 | else |
| 240 | { |
| 241 | /* Classify a file of some other type as C_ORPHAN. */ |
| 242 | colored_filetype = C_ORPHAN; |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | /* Check the file's suffix only if still classified as C_FILE. */ |
| 247 | ext = NULL; |
| 248 | if (colored_filetype == C_FILE) |
| 249 | { |
| 250 | /* Test if NAME has a recognized suffix. */ |
| 251 | len = strlen (name); |
| 252 | name += len; /* Pointer to final \0. */ |
| 253 | for (ext = _rl_color_ext_list; ext != NULL; ext = ext->next) |
| 254 | { |
| 255 | if (ext->ext.len <= len |
| 256 | && strncmp (name - ext->ext.len, ext->ext.string, |
| 257 | ext->ext.len) == 0) |
| 258 | break; |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | free (filename); /* NULL or savestring return value */ |
| 263 | |
| 264 | { |
| 265 | const struct bin_str *const s |
| 266 | = ext ? &(ext->seq) : &_rl_color_indicator[colored_filetype]; |
| 267 | if (s->string != NULL) |
| 268 | { |
| 269 | /* Need to reset so not dealing with attribute combinations */ |
| 270 | if (is_colored (C_NORM)) |
| 271 | restore_default_color (); |
| 272 | _rl_put_indicator (&_rl_color_indicator[C_LEFT]); |
| 273 | _rl_put_indicator (s); |
| 274 | _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); |
| 275 | return 0; |
| 276 | } |
| 277 | else |
| 278 | return 1; |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | void |
| 283 | _rl_prep_non_filename_text (void) |
| 284 | { |
| 285 | if (_rl_color_indicator[C_END].string != NULL) |
| 286 | _rl_put_indicator (&_rl_color_indicator[C_END]); |
| 287 | else |
| 288 | { |
| 289 | _rl_put_indicator (&_rl_color_indicator[C_LEFT]); |
| 290 | _rl_put_indicator (&_rl_color_indicator[C_RESET]); |
| 291 | _rl_put_indicator (&_rl_color_indicator[C_RIGHT]); |
| 292 | } |
| 293 | } |
| 294 | #endif /* COLOR_SUPPORT */ |