From 22291402d329a49ab8ab4a272344cab902b2ab3b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 20 Jun 2015 00:04:59 -0400 Subject: Add initial support for colorized output. --- color.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 color.c (limited to 'color.c') 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 * + * * + * 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 +#include +#include +#include +#include + +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); +} -- cgit v1.2.3