From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: 1425896314-10941-1-git-send-email-pclouds@gmail.com,
"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>,
"Junio C Hamano" <gitster@pobox.com>
Subject: [PATCH 01/25] ls_colors.c: add $LS_COLORS parsing code
Date: Mon, 6 Apr 2015 20:52:10 +0700 [thread overview]
Message-ID: <1428328354-14897-2-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1428328354-14897-1-git-send-email-pclouds@gmail.com>
Reusing color settings from $LS_COLORS could give a native look and
feel on file coloring.
This code is basically from coreutils.git [1], rewritten to fit Git.
As this is from GNU ls, the environment variable CLICOLOR is not
tested. It is to be decided later whether we should ignore $LS_COLORS
if $CLICOLOR is not set on Mac or FreeBSD.
[1] commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 file
src/ls.c. This is the last GPL-2 commit before coreutils turns to
GPL-3.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
Makefile | 1 +
color.h | 8 ++
ls_colors.c (new) | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 407 insertions(+)
create mode 100644 ls_colors.c
diff --git a/Makefile b/Makefile
index 827006b..459121d 100644
--- a/Makefile
+++ b/Makefile
@@ -703,6 +703,7 @@ LIB_OBJS += list-objects.o
LIB_OBJS += ll-merge.o
LIB_OBJS += lockfile.o
LIB_OBJS += log-tree.o
+LIB_OBJS += ls_colors.o
LIB_OBJS += mailmap.o
LIB_OBJS += match-trees.o
LIB_OBJS += merge.o
diff --git a/color.h b/color.h
index f5beab1..3eaa5bd 100644
--- a/color.h
+++ b/color.h
@@ -45,6 +45,12 @@ struct strbuf;
#define GIT_COLOR_BG_MAGENTA "\033[45m"
#define GIT_COLOR_BG_CYAN "\033[46m"
+#define GIT_COLOR_WHITE_ON_RED "\033[37;41m"
+#define GIT_COLOR_WHITE_ON_BLUE "\033[37;44m"
+#define GIT_COLOR_BLACK_ON_YELLOW "\033[30;43m"
+#define GIT_COLOR_BLUE_ON_GREEN "\033[34;42m"
+#define GIT_COLOR_BLACK_ON_GREEN "\033[30;42m"
+
/* A special value meaning "no color selected" */
#define GIT_COLOR_NIL "NIL"
@@ -87,4 +93,6 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb);
int color_is_nil(const char *color);
+void parse_ls_color(void);
+
#endif /* COLOR_H */
diff --git a/ls_colors.c b/ls_colors.c
new file mode 100644
index 0000000..e743315
--- /dev/null
+++ b/ls_colors.c
@@ -0,0 +1,398 @@
+#include "cache.h"
+#include "color.h"
+
+enum color_ls {
+ LS_LC, /* left, unused */
+ LS_RC, /* right, unused */
+ LS_EC, /* end color, unused */
+ LS_RS, /* reset */
+ LS_NO, /* normal */
+ LS_FL, /* file, default */
+ LS_DI, /* directory */
+ LS_LN, /* symlink */
+
+ LS_PI, /* pipe */
+ LS_SO, /* socket */
+ LS_BD, /* block device */
+ LS_CD, /* char device */
+ LS_MI, /* missing file */
+ LS_OR, /* orphaned symlink */
+ LS_EX, /* executable */
+ LS_DO, /* Solaris door */
+
+ LS_SU, /* setuid */
+ LS_SG, /* setgid */
+ LS_ST, /* sticky */
+ LS_OW, /* other-writable */
+ LS_TW, /* ow with sticky */
+ LS_CA, /* cap */
+ LS_MH, /* multi hardlink */
+ LS_CL, /* clear end of line */
+
+ MAX_LS
+};
+
+static char ls_colors[MAX_LS][COLOR_MAXLEN] = {
+ "",
+ "",
+ "",
+ GIT_COLOR_RESET,
+ GIT_COLOR_NORMAL,
+ GIT_COLOR_NORMAL,
+ GIT_COLOR_BOLD_BLUE,
+ GIT_COLOR_BOLD_CYAN,
+
+ GIT_COLOR_YELLOW,
+ GIT_COLOR_BOLD_MAGENTA,
+ GIT_COLOR_BOLD_YELLOW,
+ GIT_COLOR_BOLD_YELLOW,
+ GIT_COLOR_NORMAL,
+ GIT_COLOR_NORMAL,
+ GIT_COLOR_BOLD_GREEN,
+ GIT_COLOR_BOLD_MAGENTA,
+
+ GIT_COLOR_WHITE_ON_RED,
+ GIT_COLOR_BLACK_ON_YELLOW,
+ GIT_COLOR_WHITE_ON_BLUE,
+ GIT_COLOR_BLUE_ON_GREEN,
+ GIT_COLOR_BLACK_ON_GREEN,
+ "",
+ "",
+ ""
+};
+
+static const char *const indicator_name[] = {
+ "lc", "rc", "ec", "rs", "no", "fi", "di", "ln",
+ "pi", "so", "bd", "cd", "mi", "or", "ex", "do",
+ "su", "sg", "st", "ow", "tw", "ca", "mh", "cl",
+ NULL
+};
+
+struct bin_str {
+ size_t len; /* Number of bytes */
+ const char *string; /* Pointer to the same */
+};
+
+struct color_ext_type {
+ struct bin_str ext; /* The extension we're looking for */
+ struct bin_str seq; /* The sequence to output when we do */
+ struct color_ext_type *next; /* Next in list */
+};
+
+static struct color_ext_type *color_ext_list;
+
+/*
+ * When true, in a color listing, color each symlink name according to the
+ * type of file it points to. Otherwise, color them according to the `ln'
+ * directive in LS_COLORS. Dangling (orphan) symlinks are treated specially,
+ * regardless. This is set when `ln=target' appears in LS_COLORS.
+ */
+static int color_symlink_as_referent;
+
+/*
+ * 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. Set *OUTPUT_COUNT to the number of bytes output. Return
+ * true if successful.
+ *
+ * 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,
+ size_t *output_count)
+{
+ char num; /* For numerical codes */
+ size_t 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 = '\a';
+ 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();
+ }
+ }
+
+ *dest = q;
+ *src = p;
+ *output_count = count;
+
+ return state != ST_ERROR;
+}
+
+void parse_ls_color(void)
+{
+ const char *p; /* Pointer to character being parsed */
+ char *buf; /* color_buf buffer pointer */
+ int state; /* State of parser */
+ int ind_no; /* Indicator number */
+ char label[3]; /* Indicator label */
+ struct color_ext_type *ext; /* Extension we are working on */
+ static char *color_buf;
+ char *start;
+ size_t len;
+
+ if ((p = getenv("LS_COLORS")) == NULL || *p == '\0')
+ return;
+
+ ext = NULL;
+ strcpy(label, "??");
+
+ /*
+ * This is an overly conservative estimate, but any possible
+ * LS_COLORS string will *not* generate a color_buf longer
+ * than itself, so it is a safe way of allocating a buffer in
+ * advance.
+ */
+ buf = color_buf = xstrdup(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).
+ */
+
+ ext = xmalloc(sizeof(*ext));
+ ext->next = color_ext_list;
+ color_ext_list = ext;
+
+ ++p;
+ ext->ext.string = buf;
+
+ state = (get_funky_string(&buf, &p, 1, &ext->ext.len)
+ ? 4 : -1);
+ break;
+
+ case '\0':
+ state = 0; /* Done! */
+ break;
+
+ default: /* Assume it is file type label */
+ label[0] = *(p++);
+ state = 2;
+ break;
+ }
+ break;
+
+ case 2: /* Second label character */
+ if (*p) {
+ label[1] = *(p++);
+ state = 3;
+ } else
+ state = -1; /* Error */
+ break;
+
+ case 3: /* Equal sign after indicator label */
+ state = -1; /* Assume failure... */
+ if (*(p++) != '=')
+ break;
+ for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no) {
+ if (!strcmp(label, indicator_name[ind_no])) {
+ start = buf;
+ if (get_funky_string(&buf, &p, 0, &len))
+ state = 1;
+ else
+ state = -1;
+ break;
+ }
+ }
+ if (state == -1)
+ error(_("unrecognized prefix: %s"), label);
+ else if (ind_no == LS_LN && len == 6 &&
+ starts_with(start, "target"))
+ color_symlink_as_referent = 1;
+ else
+ sprintf(ls_colors[ind_no], "\033[%.*sm",
+ (int)len, start);
+ break;
+
+ case 4: /* Equal sign after *.ext */
+ if (*(p++) == '=') {
+ ext->seq.string = buf;
+ state = (get_funky_string(&buf, &p, 0, &ext->seq.len)
+ ? 1 : -1);
+ } else
+ state = -1;
+ break;
+ }
+ }
+
+ if (!strcmp(ls_colors[LS_LN], "target"))
+ color_symlink_as_referent = 1;
+}
--
2.3.0.rc1.137.g477eb31
next prev parent reply other threads:[~2015-04-06 13:52 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-06 13:52 [PATCH v2 00/25] list-files redesign Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` Nguyễn Thái Ngọc Duy [this message]
2015-04-06 13:52 ` [PATCH 02/25] ls_colors.c: parse color.ls.* from config file Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 03/25] ls_colors.c: add a function to color a file name Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 04/25] ls_colors.c: highlight submodules like directories Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 05/25] list-files: command skeleton Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 06/25] list-files: show paths relative to cwd Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 07/25] list-files: add tag to each entry, filter duplicate tags Nguyễn Thái Ngọc Duy
2015-04-06 21:32 ` Eric Sunshine
2015-04-06 13:52 ` [PATCH 08/25] list-files: add --[no-]column, -C and -1 Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 09/25] list-files: add --max-depth, -R and default to --max-depth=0 Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 10/25] list-files: show directories as well as files Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 11/25] list-files: add --color Nguyễn Thái Ngọc Duy
2015-04-06 21:33 ` Eric Sunshine
2015-04-06 13:52 ` [PATCH 12/25] list-files: add -F/--classify Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 13/25] list-files: new indicator '&' for submodules when -F is used Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 14/25] list-files: add --cached and --others Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 15/25] list-files: add --ignored Nguyễn Thái Ngọc Duy
2015-04-06 21:34 ` Eric Sunshine
2015-04-06 13:52 ` [PATCH 16/25] list-files: add --unmerged Nguyễn Thái Ngọc Duy
2015-04-06 21:34 ` Eric Sunshine
2015-04-06 13:52 ` [PATCH 17/25] list-files: add file modification options -[admADM] Nguyễn Thái Ngọc Duy
2015-04-06 21:34 ` Eric Sunshine
2015-04-06 13:52 ` [PATCH 18/25] list-files: delete redundant cached entries Nguyễn Thái Ngọc Duy
2015-04-06 21:35 ` Eric Sunshine
2015-04-08 2:39 ` Junio C Hamano
2015-04-06 13:52 ` [PATCH 19/25] list-files: make alias 'ls' default to 'list-files' Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 20/25] list-files: preload index Nguyễn Thái Ngọc Duy
2015-04-06 21:35 ` Eric Sunshine
2015-04-06 13:52 ` [PATCH 21/25] list-files: reduce match_pathspec calls in matched() Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 22/25] list-files: only do diff that is actually useful Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 23/25] pathspec: move getenv() code out of prefix_pathspec() Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 24/25] list-files: make :(glob) pathspec default Nguyễn Thái Ngọc Duy
2015-04-06 13:52 ` [PATCH 25/25] list-files: documentation Nguyễn Thái Ngọc Duy
2015-04-06 21:37 ` Eric Sunshine
2015-04-06 13:58 ` [PATCH v2 00/25] list-files redesign Duy Nguyen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1428328354-14897-2-git-send-email-pclouds@gmail.com \
--to=pclouds@gmail.com \
--cc=1425896314-10941-1-git-send-email-pclouds@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).