Blame src/DirColors.cc

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