Blame src/DirColors.cc

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