github.com/hoop33/elvish@v0.0.0-20160801152013-6d25485beab4/edit/ls_colors.go (about) 1 package edit 2 3 import ( 4 "os" 5 "path" 6 "strings" 7 "syscall" 8 ) 9 10 // Color files based on their various features. 11 // 12 // This is a reverse-engineered implementation of the parsing and 13 // interpretation of the LS_COLORS environmental variable used by GNU 14 // coreutils. 15 16 type fileFeature int 17 18 const ( 19 featureInvalid fileFeature = iota 20 21 featureOrphanedSymlink 22 featureSymlink 23 24 featureMultiHardLink 25 26 featureNamedPipe 27 featureSocket 28 featureDoor 29 featureBlockDevice 30 featureCharDevice 31 32 featureWorldWritableStickyDirectory 33 featureWorldWritableDirectory 34 featureStickyDirectory 35 featureDirectory 36 37 featureCapability 38 39 featureSetuid 40 featureSetgid 41 featureExecutable 42 43 featureRegular 44 ) 45 46 var featureForName = map[string]fileFeature{ 47 "rs": featureRegular, 48 "di": featureDirectory, 49 "ln": featureSymlink, 50 "mh": featureMultiHardLink, 51 "pi": featureNamedPipe, 52 "so": featureSocket, 53 "do": featureDoor, 54 "bd": featureBlockDevice, 55 "cd": featureCharDevice, 56 "or": featureOrphanedSymlink, 57 "su": featureSetuid, 58 "sg": featureSetgid, 59 "ca": featureCapability, 60 "tw": featureWorldWritableStickyDirectory, 61 "ow": featureWorldWritableDirectory, 62 "st": featureStickyDirectory, 63 "ex": featureExecutable, 64 } 65 66 type lsColor struct { 67 styleForFeature map[fileFeature]string 68 styleForExt map[string]string 69 } 70 71 const defaultLsColorString = `rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:` 72 73 var defaultLsColor *lsColor 74 75 // parseLsColor parses a string in the LS_COLORS format into lsColor. Erroneous 76 // fields are silently ignored. 77 func parseLsColor(s string) *lsColor { 78 lc := &lsColor{make(map[fileFeature]string), make(map[string]string)} 79 for _, spec := range strings.Split(s, ":") { 80 words := strings.Split(spec, "=") 81 if len(words) != 2 { 82 continue 83 } 84 key, value := words[0], words[1] 85 if strings.HasPrefix(key, "*.") { 86 lc.styleForExt[key[2:]] = value 87 } else { 88 feature, ok := featureForName[key] 89 if !ok { 90 continue 91 } 92 lc.styleForFeature[feature] = value 93 } 94 } 95 return lc 96 } 97 98 func is(u, p uint32) bool { 99 return u&p == p 100 } 101 102 // Weirdly, permission masks for group and other are missing on platforms other 103 // than linux, darwin and netbsd. So we replicate some of them here. 104 const ( 105 S_IWOTH = 0x2 // Writable by other 106 S_IXGRP = 0x8 // Executable by group 107 S_IXOTH = 0x1 // Executable by other 108 ) 109 110 func determineFeature(fname string, mh bool) (fileFeature, error) { 111 var stat syscall.Stat_t 112 err := syscall.Lstat(fname, &stat) 113 if err != nil { 114 return 0, err 115 } 116 117 // The type of syscall.Stat_t.Mode is uint32 on Linux and uint16 on Mac 118 m := (uint32)(stat.Mode) 119 120 // Symlink and OrphanedSymlink has highest precedence 121 if is(m, syscall.S_IFLNK) { 122 _, err := os.Stat(fname) 123 if err != nil { 124 return featureOrphanedSymlink, nil 125 } 126 return featureSymlink, nil 127 } 128 129 // featureMultiHardLink 130 if mh && stat.Nlink > 1 { 131 return featureMultiHardLink, nil 132 } 133 134 // type bits features 135 switch { 136 case is(m, syscall.S_IFIFO): 137 return featureNamedPipe, nil 138 case is(m, syscall.S_IFSOCK): 139 return featureSocket, nil 140 /* 141 case m | syscall.S_IFDOOR != 0: 142 return featureDoor, nil 143 */ 144 case is(m, syscall.S_IFBLK): 145 return featureBlockDevice, nil 146 case is(m, syscall.S_IFCHR): 147 return featureCharDevice, nil 148 case is(m, syscall.S_IFDIR): 149 // Perm bits features for directory 150 switch { 151 case is(m, S_IWOTH|syscall.S_ISVTX): 152 return featureWorldWritableStickyDirectory, nil 153 case is(m, S_IWOTH): 154 return featureWorldWritableDirectory, nil 155 case is(m, syscall.S_ISVTX): 156 return featureStickyDirectory, nil 157 default: 158 return featureDirectory, nil 159 } 160 } 161 162 // TODO(xiaq): Support featureCapacity 163 164 // Perm bits features for regular files 165 switch { 166 case is(m, syscall.S_ISUID): 167 return featureSetuid, nil 168 case is(m, syscall.S_ISGID): 169 return featureSetgid, nil 170 case m&(syscall.S_IXUSR|S_IXGRP|S_IXOTH) != 0: 171 return featureExecutable, nil 172 } 173 174 // Check extension 175 return featureRegular, nil 176 } 177 178 func (lc *lsColor) getStyle(fname string) string { 179 mh := strings.Trim(lc.styleForFeature[featureMultiHardLink], "0") != "" 180 // TODO Handle error from determineFeature 181 feature, _ := determineFeature(fname, mh) 182 if feature == featureRegular { 183 if ext := path.Ext(fname); ext != "" { 184 if style, ok := lc.styleForExt[ext]; ok { 185 return style 186 } 187 } 188 } 189 return lc.styleForFeature[feature] 190 } 191 192 func init() { 193 defaultLsColor = parseLsColor(defaultLsColorString) 194 }