/* * lftp - file transfer program * * Copyright (c) 1996-2015 by Alexander V. Lukyanov (lav@yars.free.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "DirColors.h" #include "ResMgr.h" #include "FileSet.h" #include "buffer.h" DirColors *DirColors::instance; const char DirColors::resource[]="color:dir-colors"; /* Parse a string as part of the LS_COLORS variable; this may involve decoding all kinds of escape characters. If equals_end is set an unescaped equal sign ends the string, otherwise only a : or \0 does. Returns the number of characters output, or -1 on failure. The resulting string is *not* null-terminated, but may contain embedded nulls. Note that both dest and src are char **; on return they point to the first free byte after the array and the character that ended the input string, respectively. */ static int get_funky_string (char **dest, const char **src, int equals_end) { int num; /* For numerical codes */ int count; /* Something to count with */ enum { ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR } state; const char *p; char *q; p = *src; /* We don't want to double-indirect */ q = *dest; /* the whole darn time. */ count = 0; /* No characters counted in yet. */ num = 0; state = ST_GND; /* Start in ground state. */ while (state < ST_END) { switch (state) { case ST_GND: /* Ground state (no escapes) */ switch (*p) { case ':': case '\0': state = ST_END; /* End of string */ break; case '\\': state = ST_BACKSLASH; /* Backslash scape sequence */ ++p; break; case '^': state = ST_CARET; /* Caret escape */ ++p; break; case '=': if (equals_end) { state = ST_END; /* End */ break; } /* else fall through */ default: *(q++) = *(p++); ++count; break; } break; case ST_BACKSLASH: /* Backslash escaped character */ switch (*p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': state = ST_OCTAL; /* Octal sequence */ num = *p - '0'; break; case 'x': case 'X': state = ST_HEX; /* Hex sequence */ num = 0; break; case 'a': /* Bell */ num = 7; /* Not all C compilers know what \a means */ break; case 'b': /* Backspace */ num = '\b'; break; case 'e': /* Escape */ num = 27; break; case 'f': /* Form feed */ num = '\f'; break; case 'n': /* Newline */ num = '\n'; break; case 'r': /* Carriage return */ num = '\r'; break; case 't': /* Tab */ num = '\t'; break; case 'v': /* Vtab */ num = '\v'; break; case '?': /* Delete */ num = 127; break; case '_': /* Space */ num = ' '; break; case '\0': /* End of string */ state = ST_ERROR; /* Error! */ break; default: /* Escaped character like \ ^ : = */ num = *p; break; } if (state == ST_BACKSLASH) { *(q++) = num; ++count; state = ST_GND; } ++p; break; case ST_OCTAL: /* Octal sequence */ if (*p < '0' || *p > '7') { *(q++) = num; ++count; state = ST_GND; } else num = (num << 3) + (*(p++) - '0'); break; case ST_HEX: /* Hex sequence */ switch (*p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': num = (num << 4) + (*(p++) - '0'); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': num = (num << 4) + (*(p++) - 'a') + 10; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': num = (num << 4) + (*(p++) - 'A') + 10; break; default: *(q++) = num; ++count; state = ST_GND; break; } break; case ST_CARET: /* Caret escape */ state = ST_GND; /* Should be the next state... */ if (*p >= '@' && *p <= '~') { *(q++) = *(p++) & 037; ++count; } else if (*p == '?') { *(q++) = 127; ++count; } else state = ST_ERROR; break; default: abort (); } } *(q++) = 0; *dest = q; *src = p; if(state == ST_ERROR) return -1; return count; } void DirColors::Parse(const char *p) { Empty(); Add(".lc", "\033["); Add(".rc", "m"); Add(".no", ""); Add(".fi", ""); Add(".di", ""); Add(".ln", ""); if(!p) return; char *buf; /* color_buf buffer pointer */ int state; /* State of parser */ char label[4]; /* Indicator label */ const char *ext; label[0] = '.'; label[3] = 0; ext = NULL; buf = alloca_strdup (p); state = 1; while (state > 0) { switch (state) { case 1: /* First label character */ switch (*p) { case ':': ++p; break; case '*': /* Allocate new extension block and add to head of linked list (this way a later definition will override an earlier one, which can be useful for having terminal-specific defs override global). */ ++p; /* next should be . */ if(*p++ != '.') { state = -1; break; } ext = buf; state = get_funky_string (&buf, &p, 1) < 0 ? -1 : 4; break; case '\0': state = 0; /* Done! */ break; default: /* Assume it is file type label */ label[1] = *(p++); state = 2; break; } break; case 2: /* Second label character */ if (*p) { label[2] = *(p++); state = 3; } else state = -1; /* Error */ break; case 3: /* Equal sign after indicator label */ state = -1; /* Assume failure... */ if (*(p++) == '=')/* It *should* be... */ { const char *b = buf; state = get_funky_string (&buf, &p, 0) < 0 ? -1 : 1; Add(label, b); } break; case 4: /* Equal sign after *.ext */ if (*(p++) == '=') { const char *b = buf; state = get_funky_string (&buf, &p, 0) < 0 ? -1 : 1; Add(ext, b); } else state = -1; break; } } // if (color_indicator[C_LINK].len == 6 // && !strncmp (color_indicator[C_LINK].string, "target", 6)) // color_symlink_as_referent = 1; if(!Lookup(".ec")) { const char *no=Lookup(".no"); const char *lc=Lookup(".lc"); const char *rc=Lookup(".rc"); Add(".ec",xstring::cat(lc,no,rc,NULL)); } } DirColors::DirColors() { Reconfig(resource); } const char *DirColors::GetColor(const char *name,int type) { const char *ret=0; if(type==FileInfo::DIRECTORY) { ret=Lookup(".di"); if(ret) return ret; } else if(type==FileInfo::SYMLINK) { ret=Lookup(".ln"); if(ret) return ret; } else if(type==FileInfo::NORMAL) ret=Lookup(".fi"); const char *ext = strrchr(name, '.'); if(ext && *++ext) { const char *l=Lookup(ext); if(l) return l; } return ret?ret:""; } const char *DirColors::GetColor(const FileInfo *fi) { return GetColor(fi->name,fi->defined&fi->TYPE?fi->filetype:-1); } void DirColors::PutColored(const Ref& buf,const char *name,int type) { const char *color=GetColor(name,type); const char *lc=Lookup(".lc"); const char *rc=Lookup(".rc"); if(!color || !*color || !lc || !rc) { buf->Put(name); return; } buf->Put(lc); buf->Put(color); buf->Put(rc); buf->Put(name); PutReset(buf); } void DirColors::PutReset(const Ref& buf) { const char *reset=Lookup(".ec"); buf->Put(reset); } void DirColors::Reconfig(const char *name) { if(!xstrcmp(name,resource)) Parse(ResMgr::Query(resource,0)); }