summaryrefslogtreecommitdiffstats
path: root/color.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2015-06-20 00:04:59 -0400
committerTavian Barnes <tavianator@tavianator.com>2015-06-20 00:04:59 -0400
commit22291402d329a49ab8ab4a272344cab902b2ab3b (patch)
treeb1337c8e1dbfe6ae1b9d79e2b16af210562c2e17 /color.c
parentad628c395cc45add3e3a87f21250cbd0df1d860e (diff)
downloadbfs-22291402d329a49ab8ab4a272344cab902b2ab3b.tar.xz
Add initial support for colorized output.
Diffstat (limited to 'color.c')
-rw-r--r--color.c281
1 files changed, 281 insertions, 0 deletions
diff --git a/color.c b/color.c
new file mode 100644
index 0000000..09dfc75
--- /dev/null
+++ b/color.c
@@ -0,0 +1,281 @@
+/*********************************************************************
+ * bfs *
+ * Copyright (C) 2015 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This program is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+#include "color.h"
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct color_table {
+ const char *reset;
+ const char *normal;
+ const char *file;
+ const char *dir;
+ const char *link;
+ const char *multi_hard;
+ const char *pipe;
+ const char *door;
+ const char *block;
+ const char *chardev;
+ const char *orphan;
+ const char *socket;
+ const char *setuid;
+ const char *setgid;
+ const char *capable;
+ const char *sticky_ow;
+ const char *ow;
+ const char *sticky;
+ const char *exec;
+};
+
+color_table *parse_colors(char *ls_colors) {
+ color_table *colors = malloc(sizeof(color_table));
+ if (!colors) {
+ goto done;
+ }
+
+ // Defaults generated by dircolors --print-database
+ colors->reset = "0";
+ colors->normal = NULL;
+ colors->file = NULL;
+ colors->dir = "01;34";
+ colors->link = "01;36";
+ colors->multi_hard = "00";
+ colors->pipe = "40;33";
+ colors->socket = "01;35";
+ colors->door = "01;35";
+ colors->block = "40;33;01";
+ colors->chardev = "40;33;01";
+ colors->orphan = "40;31;01";
+ colors->setuid = "37;41";
+ colors->setgid = "30;43";
+ colors->capable = "30;41";
+ colors->sticky_ow = "30;42";
+ colors->ow = "34;42";
+ colors->sticky = "37;44";
+ colors->exec = "01;32";
+
+ if (!ls_colors) {
+ goto done;
+ }
+
+ char *start = ls_colors;
+ char *end;
+ for (end = strchr(start, ':'); *start && end; start = end + 1, end = strchr(start, ':')) {
+ char *equals = strchr(start, '=');
+ if (!equals) {
+ continue;
+ }
+
+ *equals = '\0';
+ *end = '\0';
+
+ const char *key = start;
+ const char *value = equals + 1;
+
+ switch (key[0]) {
+ case 'b':
+ if (strcmp(key, "bd") == 0) {
+ colors->block = value;
+ }
+ break;
+
+ case 'c':
+ if (strcmp(key, "ca") == 0) {
+ colors->capable = value;
+ } else if (strcmp(key, "cd") == 0) {
+ colors->chardev = value;
+ }
+ break;
+
+ case 'd':
+ if (strcmp(key, "di") == 0) {
+ colors->dir = value;
+ } else if (strcmp(key, "do") == 0) {
+ colors->socket = value;
+ }
+ break;
+
+ case 'e':
+ if (strcmp(key, "ex") == 0) {
+ colors->exec = value;
+ }
+ break;
+
+ case 'f':
+ if (strcmp(key, "fi") == 0) {
+ colors->file = value;
+ }
+ break;
+
+ case 'l':
+ if (strcmp(key, "ln") == 0) {
+ colors->link = value;
+ }
+ break;
+
+ case 'n':
+ if (strcmp(key, "no") == 0) {
+ colors->normal = value;
+ }
+ break;
+
+ case 'o':
+ if (strcmp(key, "or") == 0) {
+ colors->orphan = value;
+ } else if (strcmp(key, "ow") == 0) {
+ colors->ow = value;
+ }
+ break;
+
+ case 'p':
+ if (strcmp(key, "pi") == 0) {
+ colors->pipe = value;
+ }
+ break;
+
+ case 'r':
+ if (strcmp(key, "rs") == 0) {
+ colors->reset = value;
+ }
+ break;
+
+ case 's':
+ if (strcmp(key, "sg") == 0) {
+ colors->setgid = value;
+ } else if (strcmp(key, "so") == 0) {
+ colors->socket = value;
+ } else if (strcmp(key, "st") == 0) {
+ colors->sticky = value;
+ } else if (strcmp(key, "su") == 0) {
+ colors->setuid = value;
+ }
+ break;
+
+ case 't':
+ if (strcmp(key, "tw") == 0) {
+ colors->sticky_ow = value;
+ }
+ break;
+ }
+
+ // TODO: Handle file extensions
+ }
+
+done:
+ return colors;
+}
+
+static const char *file_color(const color_table *colors, const char *filename, const struct stat *sb) {
+ if (!sb) {
+ return colors->orphan;
+ }
+
+ const char *color = NULL;
+
+ switch (sb->st_mode & S_IFMT) {
+ case S_IFREG:
+ if (sb->st_mode & S_ISUID) {
+ color = colors->setuid;
+ } else if (sb->st_mode & S_ISGID) {
+ color = colors->setgid;
+ } else if (sb->st_mode & 0111) {
+ color = colors->exec;
+ }
+
+ if (!color) {
+ color = colors->file;
+ }
+
+ break;
+
+ case S_IFDIR:
+ if (sb->st_mode & S_ISVTX) {
+ if (sb->st_mode & S_IWOTH) {
+ color = colors->sticky_ow;
+ } else {
+ color = colors->sticky;
+ }
+ } else if (sb->st_mode & S_IWOTH) {
+ color = colors->ow;
+ }
+
+ if (!color) {
+ color = colors->dir;
+ }
+
+ break;
+
+ case S_IFLNK:
+ color = colors->link;
+ break;
+ case S_IFBLK:
+ color = colors->block;
+ break;
+ case S_IFCHR:
+ color = colors->chardev;
+ break;
+ case S_IFIFO:
+ color = colors->pipe;
+ break;
+ case S_IFSOCK:
+ color = colors->socket;
+ break;
+ }
+
+ if (!color) {
+ color = colors->normal;
+ }
+
+ return color;
+}
+
+static void printf_color(const color_table *colors, const char *color, const char *format, ...) {
+ if (color) {
+ printf("\033[%sm", color);
+ }
+
+ va_list args;
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+
+ if (color) {
+ printf("\033[%sm", colors->reset);
+ }
+}
+
+void pretty_print(const color_table *colors, const char *fpath, const struct stat *sb) {
+ if (!colors) {
+ printf("%s\n", fpath);
+ return;
+ }
+
+ const char *filename = strrchr(fpath, '/');
+ if (filename) {
+ ++filename;
+ } else {
+ filename = fpath + strlen(fpath);
+ }
+
+ int dirlen = filename - fpath;
+ printf_color(colors, colors->dir, "%.*s", dirlen, fpath);
+
+ const char *color = file_color(colors, filename, sb);
+ printf_color(colors, color, "%s", filename);
+ printf("\n");
+}
+
+void free_colors(color_table *colors) {
+ free(colors);
+}